/** * Controller controls process of changing sections and provides API for doing it programmatically. * * Will dispatch these events through EventBus: * * **runtime.before_section_changed** * * **runtime.section_changed** * * @see MF.runtime.applications.authenticationaware.controller.Workbench * * @author Sergei Lissovski <sergei.lissovski@modera.net> */ Ext.define('MF.runtime.applications.WorkbenchController', { extend: 'Ext.app.Controller', requires: [ 'MF.Util' ], /** * @private {MF.runtime.Section} currentlyLoadedSection */ // override init: function() { this.control({ 'component[runtimerole=workbench]': { changesection: this.onSectionChange } }); this.getContainer().get('root_execution_context').on('statechanged', this.onExecutionContextStateChanged, this); this.callParent(arguments); }, // private onExecutionContextStateChanged: function(ec, diff, oldValues, newValues) { // if section is changed if (oldValues.section != newValues.section) { this.changeSection(newValues.section, newValues.params, Ext.emptyFn, true); return false; } }, // private getUi: function() { return Ext.ComponentQuery.query('component[runtimerole=workbench]')[0]; }, // private getContainer: function() { return this.application.getContainer(); }, /** * @private * @param {MF.runtime.Section} section * @param {String} sectionName This will be published to execution context * @param {Function} sectionActivatedCallback Optional callback. If given then it will be invoked when section has been fully * initialized and user can start interacting with it * @param {Object} params (optional) */ activateSection: function(section, sectionName, params, sectionActivatedCallback) { var me = this; var eventBus = this.getContainer().get('event_bus'), workbench = this.getContainer().get('workbench'), executionContext = this.getContainer().get('root_execution_context'); if (false !== eventBus.fireEvent('runtime.before_section_changed', me, me.currentlyLoadedSection, section)) { var previouslyLoadedSectionController = me.currentlyLoadedSection; me.getUi().showSectionBeingChangedIndicator(function() { section.activate(workbench, function(onSectionUiCreatedCallback) { executionContext.setSectionName(sectionName); // this will result in having all Activity parameters reset workbench.getActivitiesManager().bootstrap(params, function() { me.currentlyLoadedSection = section; me.getUi().hideSectionBeingChangedIndicator(function() { me.fireEvent('runtime.section_changed', me, previouslyLoadedSectionController, section); if (Ext.isFunction(onSectionUiCreatedCallback)) { onSectionUiCreatedCallback(me.getUi()); } if (Ext.isFunction(sectionActivatedCallback)) { sectionActivatedCallback(section); } // Usually this will update navigation, like highlighting a newly loaded section me.getUi().sectionChanged(MF.Util.resolveOwningSectionName(sectionName), params, sectionName); }); }); }); }); } }, /** * @private * @param {Object} section */ onSectionChange: function(section) { if (!section.hasOwnProperty('controller')) { throw this.$className + ".onSectionChange(section): section parameter doesn't have 'controller' property!"; } this.changeSection(section.id, section.metadata['activation_params'] || null); }, /** * Returns a currently loaded section. * * @return {MF.runtime.Section} */ getCurrentlyLoadedSection: function() { return this.currentlyLoadedSection; }, /** * Do not rely on this method existence, it is not public API and may be gone during refactoring. * * @internal * @private * @param {MF.runtime.Section} section */ setCurrentlyLoadedSection: function(section) { this.currentlyLoadedSection = section; }, /** * Allows to change a section. * * Parameter `handleSectionNotFound` has been added in v0.5.0. * * @param {String} sectionName A short section name * @param {Object} params Optional. Parameters to activate a new section with * @param {Function} sectionChangedCallback Optional. Callback to invoke when section is changed, the callback will * receive an argument indicating if a section has been successfully changed. * If something went wrong then usually an exception is thrown, but you have * a chance to avoid having an exception thrown if a section is not found * by using a `handleSectionNotFound` parameter of this method. * @param {Boolean} handleSectionNotFound Optional. Defaults to FALSE. If set to TRUE and given section is not found * then a nice message will be presented to a user instead of throwing an * exception which can be seen only in the developers console. If you have * provided `sectionChangedCallback` and section could not have been loaded, * then the callback will receive FALSE. */ changeSection: function(sectionName, params, sectionChangedCallback, handleSectionNotFound) { var me = this; console.debug( '%s.changeSection(sectionName="%s", params=%s, sectionChangeCallback): Changing section to "%s", params: %s', this.$className, sectionName, Ext.encode(params), sectionName, Ext.encode(params) ); params = params || {}; var workbench = this.getContainer().get('workbench'), ec = this.getRootExecutionContext(); var sectionController = null; if (handleSectionNotFound) { sectionController = workbench.getSection(sectionName); if (!sectionController) { // It is not an ideal solution because previously loaded section is still loaded as such, // but at the same by not "deactivating" currently loaded section things like SharedActivitiesProvider // will still work properly (see below how "deactivate" method is used) // See MF.runtime.applications.authenticationaware.controller.Viewport.initializeWorkbenchAndShowActivities() // method, while we here simply show an error message, the "initializeWorkbenchAndShowActivities" creates // a dummy "section" and loads it (otherwise SharedActivitiesProvider) doesn't work, here we can't // really easily implement this approach because when a section is activated using "activateSection" // method the latter will automatically switch the UI from displaying an error to a container // that is used to present Activity's UI this.getUi().showSectionNotFoundError(sectionName); // We could have updated then UI to remove highlighting from the menu here using // me.getUi().sectionChanged(), but we will not do that because technically speaking current // section is still loaded into runtime (see a comment above) sectionChangedCallback(false); return; } } else { sectionController = workbench.getSectionOrDie(sectionName); } ec.startTransaction(); var onSectionActivated = function() { ec.commitTransaction(); if (Ext.isFunction(sectionChangedCallback)) { sectionChangedCallback(true); } }; // if there's already active section we will at first deactivate it if (this.currentlyLoadedSection) { this.currentlyLoadedSection.deactivate(function() { me.activateSection(sectionController, sectionName, params, onSectionActivated); }); } else { this.activateSection(sectionController, sectionName, params, onSectionActivated); } }, // private getRootExecutionContext: function() { return this.getContainer().get('root_execution_context'); }, halt: function() { return this.getRootExecutionContext().removeListener('statechanged', this.onExecutionContextStateChanged, this); } });