"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var sp_component_base_1 = require("@microsoft/sp-component-base");
var sp_core_library_1 = require("@microsoft/sp-core-library");
var sp_diagnostics_1 = require("@microsoft/sp-diagnostics");
var sp_loader_1 = require("@microsoft/sp-loader");
var sp_telemetry_1 = require("@ms/sp-telemetry");
var BaseApplicationContext_1 = tslib_1.__importDefault(require("./BaseApplicationContext"));
var AadPlaceholderManager_1 = tslib_1.__importDefault(require("./frameworkPlaceholders/AadPlaceholderManager"));
var ApplicationLoadType_1 = require("./ApplicationLoadType");
var ApplicationManager_1 = tslib_1.__importDefault(require("./ApplicationManager"));
var Killswitches_1 = require("./common/Killswitches");
var SUITE_NAV_USE_SPO_BEHAVIOR_KILL_SWITCH = sp_core_library_1.Guid.parse('22F8084E-9DEB-4642-B63E-E70A7F87C998');
// Qos constants
var loadQosScenarioName = 'BaseApplication.load';
var renderQosScenarioName = 'BaseApplication.render';
var unloadQosScenarioName = 'BaseApplication.unload';
/**
 * This is the system base class for client-side applications.  It manages the overall
 * life cycle of your application, and is the first entry point for your code to start
 * executing when the page loads.  The two main events are onLoad() which occurs first,
 * and onRender() which occurs after the application manager has initialized the environment
 * and completed rendering the page chrome.
 *
 * @alpha
 */
var BaseApplication = /** @class */ (function (_super) {
    tslib_1.__extends(BaseApplication, _super);
    function BaseApplication() {
        var _this = _super.call(this) || this;
        _this._handleSecondFlush = function (data) {
            _this.context._updateSecondFlushData(data);
            _this.onLoad()
                .then(function () {
                _this._onRender();
            })
                .catch(function (e) {
                throw e;
            });
        };
        // tslint:disable-next-line:no-string-literal
        _this['__type'] = 'BaseApplication';
        var pageChromeProvider = _this._getPageChromeProvider();
        if (pageChromeProvider) {
            // When initialized, this will merge Fabric global settings to avoid run-time exception in Fabric component code.
            _this._themeManager = pageChromeProvider.initializeThemeManager(function () { return _this.context.serviceScope; });
        }
        if (sp_loader_1._SPLoaderFlights._isMinimalStartFlightEnabled()) {
            _this._onSecondFlushEvent = new sp_core_library_1.SPEvent('onSecondFlush');
            _this._onSecondFlushEvent.add(_this, function (data) {
                _this._secondFlushEventRaised = true;
                _this._secondFlushPreloadedData = data;
            });
        }
        return _this;
    }
    Object.defineProperty(BaseApplication.prototype, "domElement", {
        /**
         * Returns the DOM element where the application is expected to render its content.
         * The domElement will be undefined until the onRender() event occurs.
         *
         * @remarks
         * IMPORTANT: The application should not access DOM elements outside of this subtree,
         * as they are system-defined and may change over time.
         *
         * Throws an error if the domElement is undefined.
         */
        get: function () {
            sp_core_library_1.Validate.isNotNullOrUndefined(this.context, 'context');
            sp_core_library_1.Validate.isNotDisposed(this.context, 'context');
            sp_core_library_1.Validate.isNotNullOrUndefined(this.context.chrome, 'chrome');
            sp_core_library_1.Validate.isNotDisposed(this.context.chrome, 'chrome');
            return this.context.chrome.appDiv;
        },
        set: function (value) {
            throw new Error('The property cannot be assigned because it is read-only');
        },
        enumerable: false,
        configurable: true
    });
    /**
     * RESERVED FOR INTERNAL USAGE. This method is invoked automatically by the application manager.
     * The application code should not call it directly.
     *
     * @internal
     */
    BaseApplication.prototype._load = function (contextParameters) {
        var _this = this;
        var qosMonitor = new sp_diagnostics_1._QosMonitor(loadQosScenarioName);
        try {
            var applicationContext = this._getApplicationContext(contextParameters);
            this._initializeContext(applicationContext);
            var aadPlaceholderManager = new AadPlaceholderManager_1.default();
            aadPlaceholderManager.setUpTokenAcquistionFailurePlaceholder(this, this.context.serviceScope);
            var loadPromise = void 0;
            if (sp_loader_1._SPLoaderFlights._isMinimalStartFlightEnabled()) {
                loadPromise = this.onBeforeLoad().then(function () {
                    // Either the second flush was fast or this is an app-to-app transition
                    if (_this._secondFlushEventRaised) {
                        // If this is an app-to-app transition then the preloaded data is already up to date
                        if (_this.context.preloadedData.isFirstFlush) {
                            sp_diagnostics_1._EngagementLogger.logEvent('_load.SecondFlushBeforeFirstFlushFinished');
                            _this.context._updateSecondFlushData(_this._secondFlushPreloadedData);
                        }
                        _this._loadedDuringFirstFlush = true;
                        return _this.onLoad();
                    }
                });
            }
            else {
                loadPromise = this.onLoad();
            }
            return loadPromise
                .then(function () { return qosMonitor.writeSuccess(); })
                .catch(function (e) {
                qosMonitor.writeExpectedFailure('onLoadFailure', e);
                throw e;
            });
        }
        catch (error) {
            qosMonitor.writeUnexpectedFailure('SyncError', error);
            return Promise.reject(error);
        }
    };
    /**
     * RESERVED FOR INTERNAL USAGE. This method is invoked automatically by the application manager
     * to render the application.
     * The application code should not call it directly.
     *
     * This method is only called for apps that are not chromeless.
     *
     * @internal
     */
    BaseApplication.prototype._render = function () {
        var _a;
        var qosMonitor = new sp_diagnostics_1._QosMonitor(renderQosScenarioName);
        try {
            var pageChromeProvider = this._getPageChromeProvider();
            if (pageChromeProvider === undefined) {
                // Render should not be called on chromeless applications
                return;
            }
            // We load styles async by default, which is good for performance.
            // At this point in time we want to ensure that all styles in buffer are mounted before
            // rendering the app. ThemeProvider should not be defined since _loadTheme is called before _render
            (_a = this._themeManager) === null || _a === void 0 ? void 0 : _a.flushStyles();
            this.context.chrome.show();
            var isFullPageLoad = this.context.loadType === ApplicationLoadType_1.ApplicationLoadType.FullPageLoad;
            var suiteNavManager = pageChromeProvider === null || pageChromeProvider === void 0 ? void 0 : pageChromeProvider.initializeSuiteNavManager(this.context.serviceScope);
            if (suiteNavManager) {
                var config = this.suiteNavConfiguration();
                suiteNavManager.initialize(this.context.chrome.suiteNavDiv, isFullPageLoad);
                this.context.initializeSuiteNavManager(suiteNavManager);
                if (config && !config.isSuiteNavDisabled()) {
                    var useNewFlow = sp_core_library_1._SPFlight.isEnabled(1309 /* SPClientSuiteNavCommon */) && config.isSuiteNavLoadingDeferred();
                    if (useNewFlow) {
                        if (this.componentId === '1f019ae1-2de1-4f44-b723-00a6ec1d7445' /* SPHome app id */) {
                            sp_telemetry_1._PerformanceLogger.trackFlight(sp_telemetry_1._PerformanceFlight.CSISPHomeLazyLoadSearchPackage);
                        }
                        else {
                            // Every other sp-client app uses the SPPages flight
                            sp_telemetry_1._PerformanceLogger.trackFlight(sp_telemetry_1._PerformanceFlight.CSISPPagesLazyLoadSearchPackage);
                        }
                        suiteNavManager.loadSuiteNavNewFlow(config); // uses new suite nav flow
                    }
                    else {
                        suiteNavManager.loadSuiteNav(config);
                    }
                }
            }
            // We would have only loaded during the first flush if the second flush happened before a call to `_load`
            if (sp_loader_1._SPLoaderFlights._isMinimalStartFlightEnabled() && !this._loadedDuringFirstFlush) {
                this._onSecondFlushEvent.add(this, this._handleSecondFlush);
            }
            else {
                this._onRender();
            }
            qosMonitor.writeSuccess();
        }
        catch (error) {
            if (!Killswitches_1.Killswitches.throwExceptionFromCatchBlockKSActivated()) {
                qosMonitor.writeUnexpectedFailure('onRenderError', error);
                throw error;
            }
            else {
                qosMonitor.writeExpectedFailure('onRenderError', error);
            }
        }
    };
    /**
     * RESERVED FOR INTERNAL USAGE. This method is invoked by the application manager
     * to unload the application. The page chrome is still available at this point
     * The application code should not call it directly.
     *
     * @internal
     */
    BaseApplication.prototype._unload = function () {
        var qosMonitor = new sp_diagnostics_1._QosMonitor(unloadQosScenarioName);
        try {
            this.onUnload();
            qosMonitor.writeSuccess();
        }
        catch (error) {
            qosMonitor.writeExpectedFailure('onUnloadError', error);
        }
    };
    /**
     * RESERVED FOR INTERNAL USAGE. This method is invoked automatically by the application manager
     * to load the application specific theme.
     * The application code should not call it directly.
     *
     * @internal
     */
    BaseApplication.prototype._loadTheme = function () {
        var _a;
        if (!ApplicationManager_1.default._isThemelessApplication(this.componentId)) {
            (_a = this._themeManager) === null || _a === void 0 ? void 0 : _a.registerOnThemeChangeCallback();
            var themeProvider = this._getThemeProvider();
            themeProvider === null || themeProvider === void 0 ? void 0 : themeProvider.loadThemedStyles();
        }
    };
    /**
     * RESERVED FOR INTERNAL USAGE.
     * Internal API to dispose the application.
     * See onDispose for more details
     * override
     */
    BaseApplication.prototype.dispose = function () {
        var _a;
        (_a = this._themeManager) === null || _a === void 0 ? void 0 : _a.dispose();
        this.onDispose();
        _super.prototype.dispose.call(this);
    };
    /**
     * This virtual function returns the default configuration and can be configured by applications
     * when required.
     *
     * virtual
     */
    BaseApplication.prototype.suiteNavConfiguration = function () {
        var pageChromeProvider = this._getPageChromeProvider();
        if (pageChromeProvider) {
            return pageChromeProvider.initializeSuiteNavManagerConfiguration(this._getSuiteNavManagerConfigurationData());
        }
        return undefined;
    };
    /**
     * This virtual function returns the default application context and can be configured by applications
     * when required.
     *
     * virtual
     * @internal
     */
    BaseApplication.prototype._getApplicationContext = function (contextParameters) {
        return new BaseApplicationContext_1.default(contextParameters);
    };
    /**
     * This virtual function returns the default application theme provider and can be overridden by applications
     * when required.
     *
     * Because the SPThemeProvider is not needed for chromeless applications, by default, this initializes the
     * SPThemeProvider via the SPPageChromeProvider (sp-page-chrome is only loaded for apps with chromes)
     *
     * virtual
     *
     * @internal
     */
    BaseApplication.prototype._getThemeProvider = function () {
        var pageChromeProvider = this._getPageChromeProvider();
        if (pageChromeProvider) {
            return pageChromeProvider.initializeThemeProvider(this.context.serviceScope);
        }
        return undefined;
    };
    /**
     * Perform operations dependent only on `_IMinimalPreloadedData` while waiting for the second flush to provide
     * `_IPreloadedData`. Any operations dependent on `_IPreloadedData` should happen in `onLoad`.
     *
     * @internal
     * @virtual
     */
    BaseApplication.prototype.onBeforeLoad = function () {
        return Promise.resolve();
    };
    /**
     * This life cycle event occurs immediately after the application manager has loaded the application,
     * before the DOM is constructed.  Applications can use this event to load scripts
     * or start asynchronous operations that need to occur early in the lifecycle.
     * Inside the onLoad() event, applications may also modify the rendering of various
     * page chrome elements, for example by calling this.context.suiteNav.setComponentVisibility(false).
     * virtual
     */
    BaseApplication.prototype.onLoad = function () {
        // (implemented by subclass)
        return Promise.resolve();
    };
    /**
     * This lifecycle event occurs before hiding the application chrome. At this point the application still has
     * the DOM element available and can perform specific operations before the chrome is hidden from the view-port
     * virtual
     */
    BaseApplication.prototype.onUnload = function () {
        // (implemented by subclass)
    };
    /**
     * This lifecycle event occurs after the application manager has constructed the DOM for the page chrome.
     * At this time, the domElement property will be initialized, and the application can begin
     * rendering its own DOM elements.
     * virtual
     */
    BaseApplication.prototype.onRender = function () {
        // (implemented by subclass)
    };
    /**
     * This API is called at the end of the application lifecycle. It should be used to dispose any local
     * resources (i.e. DOM elements) that the application is holding onto.
     *
     * This API is expected to be called in scenarios like cross-application navigation
     * i.e. the host is transitioning from one application to another and disposes the application that is being
     * transitioned out.
     * virtual
     */
    BaseApplication.prototype.onDispose = function () {
        // (implemented by subclass)
    };
    /**
     * Provides the data necessary to construct an instance of SuiteNavManagerConfiguration
     * @internal
     */
    BaseApplication.prototype._getSuiteNavManagerConfigurationData = function () {
        var pageContext = this.context.pageContext;
        var webTemplateId;
        if (!sp_core_library_1._SPKillSwitch.isActivated(SUITE_NAV_USE_SPO_BEHAVIOR_KILL_SWITCH, '9/08/2017', 'SuiteNavUseSPOBehaviors')) {
            webTemplateId = pageContext.legacyPageContext.webTemplateId;
        }
        var disableSuiteNavSearchBox = pageContext.legacyPageContext.searchBoxInNavBar === 3 /* Hidden */;
        return {
            currentUICultureName: pageContext.cultureInfo.currentUICultureName,
            disableSuiteNav: this._shouldDisableSuiteNav(),
            preloadedData: this.context.preloadedData,
            settingsData: pageContext.legacyPageContext.MenuData
                ? pageContext.legacyPageContext.MenuData.SettingsData
                : undefined,
            signoutUrl: pageContext.legacyPageContext.MenuData
                ? pageContext.legacyPageContext.MenuData.SignOutUrl
                : undefined,
            siteClientTag: pageContext.legacyPageContext.siteClientTag,
            systemUserKey: pageContext.legacyPageContext.systemUserKey,
            userDisplayName: pageContext.user.displayName,
            webServerRelativeUrl: pageContext.web.serverRelativeUrl,
            webTemplateId: webTemplateId,
            disableSuiteNavSearchBox: disableSuiteNavSearchBox
        };
    };
    /**
     * Causes the navigator to navigate to a new page
     *
     * @remarks
     * This will be removed soon. Please use navigator from application context
     * to invoke public APIs
     */
    BaseApplication.prototype._navigate = function (url, props) {
        sp_core_library_1.Validate.isNonemptyString(url, 'url');
        return this.context.navigator.navigate(url, props);
    };
    /**
     * Allows the navigator to reinitialize SPFx components with `_IPreloadedData`.
     *
     * @remarks
     * This will be removed soon. Please use navigator from application context
     * to invoke public APIs
     *
     * Navigation is an async operation but calling this API ignores the promise.
     * This is used by List application to change the context, so the framework will send the appropriate events
     * for the context change, but it doesn't impact the application.
     */
    BaseApplication.prototype._navigateToPreloadedData = function (preloadedData) {
        sp_core_library_1.Validate.isNotNullOrUndefined(preloadedData, 'preloadedData');
        this.context.navigator.navigateToPreloadedData(preloadedData);
    };
    /**
     * Invalidates a cached resource by its URL. Any subsequent request for the resource
     * will be fetched from its origin and recached.
     *
     * @param url - URL to invalidate
     *
     * @remarks
     * This will be removed soon. Please use navigator from application context
     * to invoke public APIs
     */
    BaseApplication.prototype._invalidate = function (url) {
        sp_core_library_1.Validate.isNotNullOrUndefined(url, 'url');
        return this.context.navigator.invalidate(url);
    };
    /**
     * Raises an event for a layout change.
     */
    BaseApplication.prototype.raiseLayoutChangedEvent = function () {
        sp_core_library_1._SPEventManager.instance.raiseEvent(BaseApplication._layoutChangedEventName, {});
    };
    BaseApplication.prototype._onRender = function () {
        this.onRender();
        // After we render we can stop listening for a secondFlush
        sp_core_library_1._SPEventManager.instance.removeEvent('onSecondFlush');
    };
    BaseApplication.prototype._shouldDisableSuiteNav = function () {
        var urlQueryParams = new sp_core_library_1.UrlQueryParameterCollection(window.location.href);
        return (window.location.hostname === 'localhost' ||
            urlQueryParams.getValue('disableSuiteNav') === 'true' ||
            sp_core_library_1._BrowserUtilities.isEmbedded() ||
            !this.manifest.hasSuiteNav);
    };
    /**
     * SPEvent name for page navigation.
     * @internal
     */
    BaseApplication._navigatedEventName = 'application.navigatedEvent';
    /**
     * SPEvent name for layout changes.
     * @internal
     */
    BaseApplication._layoutChangedEventName = 'application.layoutChangedEvent';
    /**
     * SPEvent name for prefetched data availability.
     * @internal
     */
    BaseApplication._prefetchedDataEventName = 'application.prefetchedDataEvent';
    /**
     * SPEvent name for application pre in-place navigation.
     * @internal
     */
    BaseApplication._onBeforeNavigationEventName = 'application.onBeforeNavigationEventName';
    return BaseApplication;
}(sp_component_base_1.BaseComponent));
exports.default = BaseApplication;
//# sourceMappingURL=BaseApplication.js.map