define([
'module',
'core/js/api/ListenerBuilder',
'core/js/api/utils/RequireUtils',
'core/js/api/utils/StringUtils',
'core.ui.dt/js/api/GlobalNavigation',
'layout.dt/js/LayoutGenerator',
'pages/js/api/Navigation',
'viewmodel/js/api/context/ContextualData'
], function(
module,
ListenerBuilder,
RequireUtils,
StringUtils,
GlobalNavigation,
LayoutGenerator,
BaseNavigation,
ContextualData) {
'use strict';
/**
* Navigation module provides design-time related navigation APIs
* for ABCS applications.
*
* <p><strong>Most parts of the Navigation APIs are publicly available
* inside the {@link module:api/js/Pages Pages} module.</strong> This module should be
* used for design-time work only - i.e. obtaining of the active page ({@link pages.dt/js/api/Page Page}
* object is known to the ABCS design-time only).</p>
*
* @AbcsExtension stable
* @version 16.3.5
*
* @private
* @singleton
* @exports pages.dt/js/api/Navigation
* @constructor
*
* @see {@link module:api/js/Pages Pages} module for the list of available public Navigation APIs
* @see {@link pages.dt/js/api/PageUtils PageUtils} module holding helper methods for {@link pages.dt/js/api/Page Pages}
*/
var Navigation = function() {
AbcsLib.checkThis(this);
AbcsLib.checkSingleton(Navigation);
BaseNavigation.apply(this, arguments);
this.pages = undefined;
this.activePage = undefined;
this.lastState = undefined;
this._initializedListeners = false;
this._pageNavigationPromise = Promise.resolve();
};
AbcsLib.extend(Navigation, BaseNavigation);
Navigation._LOGGER = Logger.get(module.id);
/**
* Get the active (currently shown) {@link pages.dt/js/api/Page page}.
*
* <p>Unlike other public navigation methods within {@link module:api/js/Pages public Navigation API},
* this method provides instances of the design-time specific object {@link pages.dt/js/api/Page Page}.
* <br>{@link pages.dt/js/api/Page Page} can be used for seeking {@link viewmodel/js/api/Archetype Archetypes},
* page's display name, its {@link pages.dt/js/api/PageType type} or also its
* {@link pages.dt/js/api/View views}.</p>
*
* @AbcsExtension stable
* @version 16.3.5
*
* @returns {pages.dt/js/api/Page|undefined} active page in the Page Designer, undefined if Page Designer is not visible
*
* @see {@link pages.dt/js/api/Page Page} class which defines the page structure
* @see {@link pages.dt/js/api/PageUtils PageUtils} module with helper methods for {@link pages.dt/js/api/Page Page}s
*
* @example <caption>Example of getting active Page object information.</caption>
* // getting the active page instance
* var activePage = navigation.getActivePage();
* // printing page's information into the console
* console.log('Active page information: ' +
* 'id' + page.getId() +
* 'displayName=' + page.getDisplayName() +
* 'pageType=' + page.getType());
*/
Navigation.prototype.getActivePage = function() {
AbcsLib.checkParameterCount(arguments, 0);
this._checkInitialised();
return this.activePage;
};
/**
* Get ID of the active (shown) page or undefined in case Page Designer is not active.
*
* @return {String|undefined} active page's ID, otherwise undefined
*/
Navigation.prototype.getActivePageId = function() {
AbcsLib.checkParameterCount(arguments, 0);
this._checkInitialised();
var activePage = this.getActivePage();
return activePage ? activePage.getId() : undefined;
};
/**
* Get Display name of the active (shown) page.
*
* @return {String|undefined} active page's display name
*/
Navigation.prototype.getActivePageDisplayName = function() {
AbcsLib.checkParameterCount(arguments, 0);
this._checkInitialised();
var activePage = this.getActivePage();
return activePage ? activePage.getDisplayName() : undefined;
};
/**
* Get ID of the home (default) page.
*
* @return {String} home page's ID
*/
Navigation.prototype.getHomePageId = function() {
this._checkInitialised();
return this.pages.getHomepageId();
};
/**
* Navigates application to the given page ID.
*
* @param {String} pageId page's ID to navigate to
* @param {ContextualData} contextualData optional contextual data of the navigation
*/
Navigation.prototype.navigateToPage = function(pageId, contextualData) {
this._checkInitialised();
var self = this;
// serialization of the navigation requests
return self._pageNavigationPromise = self._pageNavigationPromise.then(function() {
return self._deactivateActivePage();
}).then(function() {
return RequireUtils.require(['core.dt/js/Router']);
}).then(function(Router) {
if (self.pages.getPage(pageId) === undefined) {
throw new Error(AbcsLib.i18n('pagesDt.api.navigation.pageNotExistsError', pageId));
}
return Router.navigateToPage(pageId, contextualData).then(function(pageModel) {
self.__focusAppContent();
return pageModel;
});
}).catch(function(error) {
var message = AbcsLib.i18n('pagesDt.api.navigation.navigationFailedError', error ? ('<br>' + error) : '');
Abcs.UI().showNotification(Abcs.UI().Notification.create({
message: StringUtils.format(message, pageId),
level: Abcs.UI().Notification.Level.ERROR
}));
});
};
/**
* Navigates application back to particular page ID. Difference is that upon
* returning to a page it is assumed that page was already initialized.
*
* @param {String} pageId page's ID to return to
* @param {ContextualData} contextualData optional contextual data of the navigation
*/
Navigation.prototype.returnToPage = function(pageId, contextualData) {
this._checkInitialised();
var self = this;
return self._pageNavigationPromise = self._pageNavigationPromise.then(function() {
return self._deactivateActivePage();
}).then(function() {
return RequireUtils.require(['core.dt/js/Router']);
}).then(function(Router) {
return Router.returnToPage(pageId, contextualData);
});
};
/**
* Navigates application to the homepage.
*/
Navigation.prototype.navigateHome = function() {
this._checkInitialised();
return this.navigateToPage(this.getHomePageId());
};
/**
* Refreshes the active page of the application.
*/
Navigation.prototype.refreshActivePage = function() {
this._checkInitialised();
var activePage = this.getActivePage();
if (activePage) {
activePage.refresh();
}
};
/**
* Sets the active (shown) page.
*
* @param {Page} page page to set as active
* @param {ContextualData} contextualData navigation contextual data
* @param {Boolean} returningToPreviousPage flag whether we are returning back to previous page (using back, cancel etc.)
* @return {Promise} returns promise of the page being activated
* @fires pages.dt/js/api/Navigation#EVENT_ACTIVE_PAGE_SET
*/
Navigation.prototype.setActivePage = function(page, contextualData, returningToPreviousPage) {
AbcsLib.checkDefined(page, 'page');
this._checkInitialised();
//stores the last activation state
var lastStateContextualData = contextualData;
if (returningToPreviousPage) {
lastStateContextualData = page.getViewModel().getContextualData();
}
this.lastState = new Navigation.LastState(this.activePage, page, lastStateContextualData);
return this._setActivePage(page, contextualData, returningToPreviousPage);
};
/**
* Switch the active page to the provided one.
* It calls it with the same parameters as were used originally.
*
* @param {Page|undefined} pageToSwitch page instance to switch to
*/
Navigation.prototype.switchActivePage = function(pageToSwitch) {
return this._setActivePage(
pageToSwitch,
this.lastState ? this.lastState.getContextualData() : undefined);
};
Navigation.prototype._setActivePage = function(page, contextualData, returningToPreviousPage) {
this._checkInitialised();
this.activePage = page;
//create an empty page context if not provider by the caller
contextualData = contextualData || new ContextualData();
if (page !== undefined) {
//turn on/off Modal Page support within layout setup
LayoutGenerator.applyModalPage(page.isModalPage());
var promise = page.activated(contextualData, returningToPreviousPage);
this.fireEvent(this.EVENT_ACTIVE_PAGE_SET, page.getId(), this.lastState && this.lastState.getPreviousPage() ? this.lastState.getPreviousPage().getId() : undefined);
Navigation._LOGGER.debug('Set active page \'' + page.getId() + '\' with context: ' + JSON.stringify(contextualData));
return promise;
} else {
Navigation._LOGGER.debug('Set active page set to undefined.');
return Promise.resolve();
}
};
/**
* Sets the active (shown) page for the given page ID.
*
* @param {string} pageId ID of the page to be set as active
* @param {ContextualData} contextualData navigation contextual data
* @param {Boolean} returningToPreviousPage flag whether we are returning back to previous page (using back, cancel etc.)
*/
Navigation.prototype.setActivePageId = function(pageId, contextualData, returningToPreviousPage) {
AbcsLib.checkDefined(pageId, 'pageId');
this._checkInitialised();
return this.setActivePage(this.pages.getPage(pageId), contextualData, returningToPreviousPage);
};
/**
* Gets last state of the navigation object.
*/
Navigation.prototype.getLastState = function() {
return this.lastState;
};
/**
* Calls deactivation of the page and fires appropriate event.
*
* @returns {Promise} promise to be fulfiled once the deactivation is complete
*/
Navigation.prototype._deactivateActivePage = function() {
this._checkInitialised();
var self = this;
var page = self.getActivePage();
if (page !== undefined) {
return page.deactivated().then(function() {
self.fireEvent(self.EVENT_PAGE_DEACTIVATED, page.getId(), page.getViewModel());
Navigation._LOGGER.debug('Deactivating active page \'' + page.getId() + '\'.');
return self._setActivePage(undefined);
}).catch(function(error) {
Navigation._LOGGER.debug('Page deactivation failure: \'' + error + '\'.');
throw error;
});
} else {
Navigation._LOGGER.debug('No active page to be deactivated found.');
return Promise.resolve();
}
};
/**
* <b>To be called from Pages only!</b>
*/
Navigation.prototype.__initialize = function(pages) {
var self = this;
self.pages = pages;
if (!self._initializedListeners) {
self._initializedListeners = true;
// init GlobalNavigation listening
GlobalNavigation.getInstance().addListener(new ListenerBuilder((GlobalNavigation.Events.ACTIVE_SECTION_SET), function(activeSection, previousSection) {
self._handleGlobalNavigationEvent(activeSection, previousSection);
}).build());
AbcsLib.Translations.addListener(new ListenerBuilder(AbcsLib.Translations.EVENTS.CHANGED_EXTERNALLY, function () {
var activePage = self.getActivePage();
if (activePage) {
activePage.refresh();
}
}).build());
}
};
Navigation.prototype.__focusAppContent = function() {
Navigation._superClass.__focusAppContent.apply(this, arguments);
var mode = this.pages.getMode();
var $elementToScroll = mode && mode.isRuntime() ? $(window) : $('#dt-application-canvas');
$elementToScroll.scrollTop(0);
};
/**
* Checks that it's initialized from Pages module.
*/
Navigation.prototype._checkInitialised = function() {
if (!this.isInitialised()) {
throw new Error('No Pages.setContext() has been called first!');
}
};
/**
* Gets a flag whether the Navigation module is initialized for the application or not.
*/
Navigation.prototype.isInitialised = function() {
return this.pages !== undefined;
};
Navigation.prototype._handleGlobalNavigationEvent = function(activeSection, previousSection) {
var self = this;
if (GlobalNavigation.isManagementSection(activeSection)) {
// switching to other application
self._setActivePage(undefined);
self.lastState = undefined;
} else if (GlobalNavigation.isAppDesignerSection(activeSection)) {
if (!previousSection) {
// no-op, activation after the browser refresh waits for the page route initialization - BUFP-10046
} else if (GlobalNavigation.isManagementSection(previousSection)) {
// switching to newly opened application
self.setActivePageId(self.getHomePageId());
} else if (GlobalNavigation.isAppDesignerSection(previousSection)) {
// switching designer - preview, no-op
} else {
// switching from other management section back to designer
if (self.lastState && self.lastState.getActivePage()) {
var activePage = self.lastState.getActivePage();
RequireUtils.require(['core.dt/js/Router']).then(function(Router) {
Router.updateRouter({
doNotDispatchSignal: true,
replaceLatestState: true,
pageId: activePage.getId()
});
self.switchActivePage(activePage);
});
} else {
self.setActivePageId(self.getHomePageId());
}
}
} else if (GlobalNavigation.isAppSetupSection(activeSection)) {
// switching between application setup panels
self.switchActivePage(undefined);
}
};
Navigation.LastState = function(previousPage, activePage, contextualData) {
this.previousPage = previousPage;
this.activePage = activePage;
this.contextualData = contextualData;
};
Navigation.LastState.prototype.getPreviousPage = function() {
return this.previousPage;
};
Navigation.LastState.prototype.getActivePage = function() {
return this.activePage;
};
Navigation.LastState.prototype.getContextualData = function() {
return this.contextualData;
};
return AbcsLib.initSingleton(Navigation);
});