| /******************************************************************************* |
| * Copyright (c) 2014-2015 BSI Business Systems Integration AG. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * BSI Business Systems Integration AG - initial API and implementation |
| ******************************************************************************/ |
| scout.Desktop = function() { |
| scout.Desktop.parent.call(this); |
| |
| this.desktopStyle = scout.Desktop.DisplayStyle.DEFAULT; |
| this.benchVisible = true; |
| this.headerVisible = true; |
| this.navigationVisible = true; |
| this.navigationHandleVisible = true; |
| this.menus = []; |
| this.addOns = []; |
| this.dialogs = []; |
| this.views = []; |
| this.viewButtons = []; |
| this.messageBoxes = []; |
| this.fileChoosers = []; |
| this.navigation; |
| this.header; |
| this.bench; |
| this.splitter; |
| this.formController; |
| this.messageBoxController; |
| this.fileChooserController; |
| this.initialFormRendering = false; |
| this.offline = false; |
| this.notifications = []; |
| this.inBackground = false; |
| this.geolocationServiceAvailable = scout.device.supportsGeolocation(); |
| this.openUriHandler; |
| |
| this._addWidgetProperties(['activeForm', 'viewButtons', 'menus', 'views', 'selectedViewTabs', 'dialogs', 'outline', 'messageBoxes', 'notifications', 'fileChoosers', 'addOns', 'keyStrokes']); |
| |
| // event listeners |
| this._benchActiveViewChangedHandler = this._onBenchActivateViewChanged.bind(this); |
| }; |
| scout.inherits(scout.Desktop, scout.Widget); |
| |
| scout.Desktop.DisplayStyle = { |
| DEFAULT: 'default', |
| BENCH: 'bench', |
| COMPACT: 'compact' |
| }; |
| |
| scout.Desktop.UriAction = { |
| DOWNLOAD: 'download', |
| OPEN: 'open', |
| NEW_WINDOW: 'newWindow', |
| SAME_WINDOW: 'sameWindow' |
| }; |
| |
| scout.Desktop.prototype._init = function(model) { |
| scout.Desktop.parent.prototype._init.call(this, model); |
| this.formController = scout.create('DesktopFormController', { |
| displayParent: this, |
| session: this.session |
| }); |
| |
| this.messageBoxController = new scout.MessageBoxController(this, this.session); |
| this.fileChooserController = new scout.FileChooserController(this, this.session); |
| this._resizeHandler = this.onResize.bind(this); |
| this._popstateHandler = this.onPopstate.bind(this); |
| this.updateSplitterVisibility(); |
| this.resolveTextKeys(['title']); |
| this._setViews(this.views); |
| this._setViewButtons(this.viewButtons); |
| this._setMenus(this.menus); |
| this._setKeyStrokes(this.keyStrokes); |
| this._setBenchLayoutData(this.benchLayoutData); |
| this.openUriHandler = scout.create('OpenUriHandler', { |
| session: this.session |
| }); |
| |
| // Note: session and desktop are tightly coupled. Because a lot of widgets want to register |
| // a listener on the desktop in their init phase, they access the desktop by calling 'this.session.desktop' |
| // that's why we need this instance as early as possible. When that happens they access a desktop which is |
| // not yet fully initialized. But anyway, it's already possible to attach a listener, for instance. |
| // Because of this line of code here, we don't have to set the variable in App.js, after the desktop has been |
| // created. Also note that Scout Java uses a different pattern to solve the same problem, there a VirtualDesktop |
| // is used during initialization. When initialization is done, all registered listeners on the virtual desktop |
| // are copied to the real desktop instance. |
| this.session.desktop = this; |
| }; |
| |
| /** |
| * @override |
| */ |
| scout.Desktop.prototype._createKeyStrokeContext = function() { |
| return new scout.KeyStrokeContext(); |
| }; |
| |
| /** |
| * @override |
| */ |
| scout.Desktop.prototype._initKeyStrokeContext = function() { |
| scout.Desktop.parent.prototype._initKeyStrokeContext.call(this); |
| |
| this.keyStrokeContext.invokeAcceptInputOnActiveValueField = true; |
| // Keystroke on the top-level DOM element which works as a catch-all when the busy indicator is active |
| this.keyStrokeContext.registerKeyStroke(new scout.DesktopKeyStroke(this.session)); |
| this.keyStrokeContext.registerKeyStroke(new scout.DisableBrowserTabSwitchingKeyStroke(this)); |
| }; |
| |
| scout.Desktop.prototype._onBenchActivateViewChanged = function(event) { |
| if (this.initialFormRendering) { |
| return; |
| } |
| var view = event.view; |
| if (view instanceof scout.Form && this.bench.outlineContent !== view && !view.detailForm) { |
| // Notify model that this form is active (only for regular views, not detail forms) |
| this._setFormActivated(view); |
| } |
| }; |
| |
| scout.Desktop.prototype._render = function() { |
| this.$container = this.$parent; |
| this.$container.addClass('desktop'); |
| this.htmlComp = scout.HtmlComponent.install(this.$container, this.session); |
| this.htmlComp.setLayout(this._createLayout()); |
| |
| // Desktop elements are added before this separator, all overlays are opened after (dialogs, popups, tooltips etc.) |
| this.$overlaySeparator = this.$container.appendDiv('overlay-separator').setVisible(false); |
| |
| this._renderNavigationVisible(); |
| this._renderHeaderVisible(); |
| this._renderBenchVisible(); |
| this._renderTitle(); |
| this._renderLogoUrl(); |
| this._renderSplitterVisible(); |
| this._renderInBackground(); |
| this._renderDisplayStyle(); |
| this._renderNavigationHandleVisible(); |
| this._renderNotifications(); |
| this.addOns.forEach(function(addOn) { |
| addOn.render(); |
| }, this); |
| |
| this.$container.window() |
| .on('resize', this._resizeHandler) |
| .on('popstate', this._popstateHandler); |
| |
| // prevent general drag and drop, dropping a file anywhere in the application must not open this file in browser |
| this._setupDragAndDrop(); |
| |
| this._disableContextMenu(); |
| }; |
| |
| scout.Desktop.prototype._remove = function() { |
| this.formController.remove(); |
| this.messageBoxController.remove(); |
| this.fileChooserController.remove(); |
| this.$container.window() |
| .off('resize', this._resizeHandler) |
| .off('popstate', this._popstateHandler); |
| scout.Desktop.parent.prototype._remove.call(this); |
| }; |
| |
| scout.Desktop.prototype._postRender = function() { |
| scout.Desktop.parent.prototype._postRender.call(this); |
| |
| // Render attached forms, message boxes and file choosers. |
| this.initialFormRendering = true; |
| this._renderDisplayChildsOfOutline(); |
| this.formController.render(); |
| this.messageBoxController.render(); |
| this.fileChooserController.render(); |
| this.initialFormRendering = false; |
| }; |
| |
| scout.Desktop.prototype._renderDisplayStyle = function() { |
| var DisplayStyle = scout.Desktop.DisplayStyle, |
| isCompact = this.displayStyle === DisplayStyle.COMPACT; |
| |
| if (this.header) { |
| this.header.setToolBoxVisible(!isCompact); |
| this.header.animateRemoval = isCompact; |
| } |
| if (this.navigation) { |
| this.navigation.setToolBoxVisible(isCompact); |
| this.navigation.htmlComp.layoutData.fullWidth = isCompact; |
| } |
| if (this.bench) { |
| this.bench.setOutlineContentVisible(!isCompact); |
| } |
| if (this.outline) { |
| this.outline.setCompact(isCompact); |
| this.outline.setEmbedDetailContent(isCompact); |
| } |
| |
| this.invalidateLayoutTree(); |
| }; |
| |
| scout.Desktop.prototype._createLayout = function() { |
| return new scout.DesktopLayout(this); |
| }; |
| |
| /** |
| * Displays attached forms, message boxes and file choosers. |
| * Outline does not need to be rendered to show the child elements, it needs to be active (necessary if navigation is invisible) |
| */ |
| scout.Desktop.prototype._renderDisplayChildsOfOutline = function() { |
| if (!this.outline) { |
| return; |
| } |
| this.outline.formController.render(); |
| this.outline.messageBoxController.render(); |
| this.outline.fileChooserController.render(); |
| |
| if (this.outline.selectedViewTabs) { |
| this.outline.selectedViewTabs.forEach(function(selectedView) { |
| this.formController._activateView(selectedView); |
| }.bind(this)); |
| } |
| }; |
| |
| scout.Desktop.prototype._removeDisplayChildsOfOutline = function() { |
| if (!this.outline) { |
| return; |
| } |
| this.outline.formController.remove(); |
| this.outline.messageBoxController.remove(); |
| this.outline.fileChooserController.remove(); |
| }; |
| |
| scout.Desktop.prototype._renderTitle = function() { |
| var title = this.title; |
| if (title === undefined || title === null) { |
| return; |
| } |
| var $scoutDivs = $('div.scout'); |
| if ($scoutDivs.length <= 1) { // only set document title in non-portlet case |
| $scoutDivs.document(true).title = title; |
| } |
| }; |
| |
| scout.Desktop.prototype._renderActiveForm = function() { |
| // NOP -> is handled in _setFormActivated when ui changes active form or if model changes form in _onFormShow/_onFormActivate |
| }; |
| |
| scout.Desktop.prototype._renderBench = function() { |
| if (this.bench) { |
| return; |
| } |
| this.bench = scout.create('DesktopBench', { |
| parent: this, |
| animateRemoval: true, |
| headerTabArea: this.header ? this.header.tabArea : undefined, |
| outlineContentVisible: this.displayStyle !== scout.Desktop.DisplayStyle.COMPACT |
| }); |
| this.bench.on('viewActivate', this._benchActiveViewChangedHandler); |
| this.bench.render(); |
| this.bench.$container.insertBefore(this.$overlaySeparator); |
| this.invalidateLayoutTree(); |
| }; |
| |
| scout.Desktop.prototype._removeBench = function() { |
| if (!this.bench) { |
| return; |
| } |
| this.bench.off('viewActivate', this._benchActiveViewChangedHandler); |
| this.bench.on('destroy', function() { |
| this.bench = null; |
| this.invalidateLayoutTree(); |
| }.bind(this)); |
| this.bench.destroy(); |
| }; |
| |
| scout.Desktop.prototype._renderBenchVisible = function() { |
| this.animateLayoutChange = this.rendered; |
| if (this.benchVisible) { |
| this._renderBench(); |
| this._renderInBackground(); |
| } else { |
| this._removeBench(); |
| } |
| }; |
| |
| scout.Desktop.prototype._renderNavigation = function() { |
| if (this.navigation) { |
| return; |
| } |
| this.navigation = scout.create('DesktopNavigation', { |
| parent: this, |
| outline: this.outline, |
| toolBoxVisible: this.displayStyle === scout.Desktop.DisplayStyle.COMPACT, |
| layoutData: { |
| fullWidth: this.displayStyle === scout.Desktop.DisplayStyle.COMPACT |
| } |
| }); |
| this.navigation.render(); |
| this.navigation.$container.prependTo(this.$container); |
| this.invalidateLayoutTree(); |
| }; |
| |
| scout.Desktop.prototype._removeNavigation = function() { |
| if (!this.navigation) { |
| return; |
| } |
| this.navigation.destroy(); |
| this.navigation = null; |
| this.invalidateLayoutTree(); |
| }; |
| |
| scout.Desktop.prototype._renderNavigationVisible = function() { |
| this.animateLayoutChange = this.rendered; |
| if (this.navigationVisible) { |
| this._renderNavigation(); |
| } else { |
| if (!this.animateLayoutChange) { |
| this._removeNavigation(); |
| } else { |
| // re layout to trigger animation |
| this.invalidateLayoutTree(); |
| } |
| } |
| }; |
| |
| scout.Desktop.prototype._renderHeader = function() { |
| if (this.header) { |
| return; |
| } |
| this.header = scout.create('DesktopHeader', { |
| parent: this, |
| animateRemoval: this.displayStyle === scout.Desktop.DisplayStyle.COMPACT, |
| toolBoxVisible: this.displayStyle !== scout.Desktop.DisplayStyle.COMPACT |
| }); |
| this.header.render(); |
| this.header.$container.insertBefore(this.$overlaySeparator); |
| this.invalidateLayoutTree(); |
| }; |
| |
| scout.Desktop.prototype._removeHeader = function() { |
| if (!this.header) { |
| return; |
| } |
| this.header.on('destroy', function() { |
| this.invalidateLayoutTree(); |
| this.header = null; |
| }.bind(this)); |
| this.header.destroy(); |
| }; |
| |
| scout.Desktop.prototype._renderHeaderVisible = function() { |
| if (this.headerVisible) { |
| this._renderHeader(); |
| } else { |
| this._removeHeader(); |
| } |
| }; |
| |
| scout.Desktop.prototype._renderLogoUrl = function() { |
| if (this.header) { |
| this.header.setLogoUrl(this.logoUrl); |
| } |
| }; |
| |
| scout.Desktop.prototype._renderSplitterVisible = function() { |
| if (this.splitterVisible) { |
| this._renderSplitter(); |
| } else { |
| this._removeSplitter(); |
| } |
| }; |
| |
| scout.Desktop.prototype._renderSplitter = function() { |
| if (this.splitter || !this.navigation) { |
| return; |
| } |
| this.splitter = scout.create('Splitter', { |
| parent: this, |
| $anchor: this.navigation.$container, |
| $root: this.$container |
| }); |
| this.splitter.render(); |
| this.splitter.$container.insertBefore(this.$overlaySeparator); |
| this.splitter.on('move', this._onSplitterMove.bind(this)); |
| this.splitter.on('moveEnd', this._onSplitterMoveEnd.bind(this)); |
| this.splitter.on('positionChange', this._onSplitterPositionChange.bind(this)); |
| this.updateSplitterPosition(); |
| }; |
| |
| scout.Desktop.prototype._removeSplitter = function() { |
| if (!this.splitter) { |
| return; |
| } |
| this.splitter.destroy(); |
| this.splitter = null; |
| }; |
| |
| scout.Desktop.prototype._renderInBackground = function() { |
| if (this.bench) { |
| this.bench.$container.toggleClass('drop-shadow', this.inBackground); |
| } |
| }; |
| |
| scout.Desktop.prototype._renderBrowserHistoryEntry = function() { |
| if (!scout.device.supportsHistoryApi()) { |
| return; |
| } |
| var myWindow = this.$container.window(true), |
| history = this.browserHistoryEntry; |
| myWindow.history.pushState({ |
| deepLinkPath: history.deepLinkPath |
| }, history.title, history.path); |
| }; |
| |
| scout.Desktop.prototype._setupDragAndDrop = function() { |
| var dragEnterOrOver = function(event) { |
| event.stopPropagation(); |
| event.preventDefault(); |
| // change cursor to forbidden (no dropping allowed) |
| event.originalEvent.dataTransfer.dropEffect = 'none'; |
| }; |
| |
| this.$container.on('dragenter', dragEnterOrOver); |
| this.$container.on('dragover', dragEnterOrOver); |
| this.$container.on('drop', function(event) { |
| event.stopPropagation(); |
| event.preventDefault(); |
| }); |
| }; |
| |
| scout.Desktop.prototype.updateSplitterVisibility = function() { |
| // Splitter should only be visible if navigation and bench are visible, but never in compact mode (to prevent unnecessary splitter rendering) |
| this.setSplitterVisible(this.navigationVisible && this.benchVisible && this.displayStyle !== scout.Desktop.DisplayStyle.COMPACT); |
| }; |
| |
| scout.Desktop.prototype.setSplitterVisible = function(visible) { |
| this.setProperty('splitterVisible', visible); |
| }; |
| |
| scout.Desktop.prototype.updateSplitterPosition = function() { |
| if (!this.splitter) { |
| return; |
| } |
| var storedSplitterPosition = this.cacheSplitterPosition && this._loadCachedSplitterPosition(); |
| if (storedSplitterPosition) { |
| // Restore splitter position |
| var splitterPosition = parseInt(storedSplitterPosition, 10); |
| this.splitter.setPosition(splitterPosition); |
| this.invalidateLayoutTree(); |
| } else { |
| // Set initial splitter position (default defined by css) |
| this.splitter.setPosition(); |
| this.invalidateLayoutTree(); |
| } |
| }; |
| |
| scout.Desktop.prototype._disableContextMenu = function() { |
| // Switch off browser's default context menu for the entire scout desktop (except input fields) |
| this.$container.on('contextmenu', function(event) { |
| if (event.target.nodeName !== 'INPUT' && event.target.nodeName !== 'TEXTAREA' && !event.target.isContentEditable) { |
| event.preventDefault(); |
| } |
| }); |
| }; |
| |
| scout.Desktop.prototype.setOutline = function(outline) { |
| if (this.outline === outline) { |
| return; |
| } |
| try { |
| if (this.bench) { |
| this.bench.setChanging(true); |
| } |
| if (this.rendered) { |
| this._removeDisplayChildsOfOutline(); |
| } |
| |
| this.outline = outline; |
| this._setOutlineActivated(); |
| if (this.navigation) { |
| this.navigation.setOutline(this.outline); |
| } |
| // call render after triggering event so glasspane rendering taking place can refer to the current outline content |
| this.trigger('outlineChange'); |
| |
| if (this.rendered) { |
| this._renderDisplayChildsOfOutline(); |
| this._renderDisplayStyle(); |
| } |
| } finally { |
| if (this.bench) { |
| this.bench.setChanging(false); |
| } |
| } |
| }; |
| |
| scout.Desktop.prototype._setViews = function(views) { |
| if (views) { |
| views.forEach(function(view) { |
| view.setDisplayParent(this); |
| }.bind(this)); |
| } |
| this._setProperty('views', views); |
| }; |
| |
| scout.Desktop.prototype._setViewButtons = function(viewButtons) { |
| this.updateKeyStrokes(viewButtons, this.viewButtons); |
| this._setProperty('viewButtons', viewButtons); |
| }; |
| |
| scout.Desktop.prototype.setMenus = function(menus) { |
| if (this.header) { |
| this.header.setMenus(menus); |
| } |
| }; |
| |
| scout.Desktop.prototype._setMenus = function(menus) { |
| this.updateKeyStrokes(menus, this.menus); |
| this._setProperty('menus', menus); |
| }; |
| |
| scout.Desktop.prototype._setKeyStrokes = function(keyStrokes) { |
| this.updateKeyStrokes(keyStrokes, this.keyStrokes); |
| this._setProperty('keyStrokes', keyStrokes); |
| }; |
| |
| scout.Desktop.prototype.setNavigationHandleVisible = function(visible) { |
| this.setProperty('navigationHandleVisible', visible); |
| }; |
| |
| scout.Desktop.prototype._renderNavigationHandleVisible = function() { |
| this.$container.toggleClass('has-navigation-handle', this.navigationHandleVisible); |
| }; |
| |
| scout.Desktop.prototype.setNavigationVisible = function(visible) { |
| this.setProperty('navigationVisible', visible); |
| this.updateSplitterVisibility(); |
| }; |
| |
| scout.Desktop.prototype.setBenchVisible = function(visible) { |
| this.setProperty('benchVisible', visible); |
| this.updateSplitterVisibility(); |
| }; |
| |
| scout.Desktop.prototype.setHeaderVisible = function(visible) { |
| this.setProperty('headerVisible', visible); |
| }; |
| |
| scout.Desktop.prototype._setBenchLayoutData = function(layoutData) { |
| layoutData = scout.BenchColumnLayoutData.ensure(layoutData); |
| this._setProperty('benchLayoutData', layoutData); |
| }; |
| |
| scout.Desktop.prototype._setInBackground = function(inBackground) { |
| this._setProperty('inBackground', inBackground); |
| }; |
| |
| scout.Desktop.prototype.outlineDisplayStyle = function() { |
| if (this.outline) { |
| return this.outline.displayStyle; |
| } |
| }; |
| |
| scout.Desktop.prototype.shrinkNavigation = function() { |
| if (this.outline.toggleBreadcrumbStyleEnabled && this.navigationVisible && |
| this.outlineDisplayStyle() === scout.Tree.DisplayStyle.DEFAULT) { |
| this.outline.setDisplayStyle(scout.Tree.DisplayStyle.BREADCRUMB); |
| } else { |
| this.setNavigationVisible(false); |
| } |
| }; |
| |
| scout.Desktop.prototype.enlargeNavigation = function() { |
| if (this.navigationVisible && this.outlineDisplayStyle() === scout.Tree.DisplayStyle.BREADCRUMB) { |
| this.outline.setDisplayStyle(scout.Tree.DisplayStyle.DEFAULT); |
| } else { |
| this.setNavigationVisible(true); |
| // Layout immediately to have view tabs positioned correctly before animation starts |
| this.validateLayoutTree(); |
| } |
| }; |
| |
| scout.Desktop.prototype.switchToBench = function() { |
| this.setHeaderVisible(true); |
| this.setBenchVisible(true); |
| this.setNavigationVisible(false); |
| }; |
| |
| scout.Desktop.prototype.switchToNavigation = function() { |
| this.setNavigationVisible(true); |
| this.setHeaderVisible(false); |
| this.setBenchVisible(false); |
| }; |
| |
| scout.Desktop.prototype.revalidateHeaderLayout = function() { |
| if (this.header) { |
| this.header.revalidateLayout(); |
| } |
| }; |
| |
| scout.Desktop.prototype.goOffline = function() { |
| if (this.offline) { |
| return; |
| } |
| this.offline = true; |
| this._removeOfflineNotification(); |
| this._offlineNotification = scout.create('DesktopNotification:Offline', { |
| parent: this |
| }); |
| this._offlineNotification.show(); |
| }; |
| |
| scout.Desktop.prototype.goOnline = function() { |
| this._removeOfflineNotification(); |
| }; |
| |
| scout.Desktop.prototype._removeOfflineNotification = function() { |
| if (this._offlineNotification) { |
| setTimeout(this.removeNotification.bind(this, this._offlineNotification), 3000); |
| this._offlineNotification = null; |
| } |
| }; |
| |
| scout.Desktop.prototype.addNotification = function(notification) { |
| if (!notification) { |
| return; |
| } |
| this.notifications.push(notification); |
| if (this.rendered) { |
| this._renderNotification(notification); |
| } |
| }; |
| |
| scout.Desktop.prototype._renderNotification = function(notification) { |
| if (this.$notifications) { |
| // Bring to front |
| this.$notifications.appendTo(this.$container); |
| } else { |
| this.$notifications = this.$container.appendDiv('desktop-notifications'); |
| } |
| notification.fadeIn(this.$notifications); |
| if (notification.duration > 0) { |
| notification.removeTimeout = setTimeout(notification.hide.bind(notification), notification.duration); |
| notification.one('remove', function(){ |
| this.removeNotification(notification); |
| }.bind(this)); |
| } |
| }; |
| |
| scout.Desktop.prototype._renderNotifications = function() { |
| this.notifications.forEach(function(notification) { |
| this._renderNotification(notification); |
| }.bind(this)); |
| }; |
| |
| /** |
| * Removes the given notification. |
| * @param notification Either an instance of scout.DesktopNavigation or a String containing an ID of a notification instance. |
| */ |
| scout.Desktop.prototype.removeNotification = function(notification) { |
| if (typeof notification === 'string') { |
| var notificationId = notification; |
| notification = scout.arrays.find(this.notifications, function(n) { |
| return notificationId === n.id; |
| }); |
| } |
| if (!notification) { |
| return; |
| } |
| if (notification.removeTimeout) { |
| clearTimeout(notification.removeTimeout); |
| } |
| scout.arrays.remove(this.notifications, notification); |
| if (!this.rendered) { |
| return; |
| } |
| if (this.$notifications) { |
| notification.fadeOut(); |
| notification.one('remove', this._onNotificationRemove.bind(this, notification)); |
| } |
| }; |
| |
| /** |
| * Destroys every popup which is a descendant of the given widget. |
| */ |
| scout.Desktop.prototype.destroyPopupsFor = function(widget) { |
| this.$container.children('.popup').each(function(i, elem) { |
| var $popup = $(elem), |
| popup = scout.Widget.getWidgetFor($popup); |
| |
| if (widget.has(popup)) { |
| popup.destroy(); |
| } |
| }); |
| }; |
| |
| scout.Desktop.prototype.openUri = function(uri, action) { |
| if (!this.rendered) { |
| this._postRenderActions.push(this.openUri.bind(this, uri, action)); |
| return; |
| } |
| this.openUriHandler.openUri(uri, action); |
| }; |
| |
| scout.Desktop.prototype.bringOutlineToFront = function() { |
| if (!this.rendered) { |
| this._postRenderActions.push(this.bringOutlineToFront.bind(this)); |
| return; |
| } |
| |
| if (!this.inBackground || this.displayStyle === scout.Desktop.DisplayStyle.BENCH) { |
| return; |
| } |
| |
| this._setInBackground(false); |
| this._setOutlineActivated(); |
| |
| if (this.navigationVisible) { |
| this.navigation.bringToFront(); |
| } |
| if (this.benchVisible) { |
| this.bench.bringToFront(); |
| } |
| if (this.headerVisible) { |
| this.header.bringToFront(); |
| } |
| |
| this._renderInBackground(); |
| }; |
| |
| scout.Desktop.prototype.sendOutlineToBack = function() { |
| if (this.inBackground) { |
| return; |
| } |
| this._setInBackground(true); |
| if (this.navigationVisible) { |
| this.navigation.sendToBack(); |
| } |
| if (this.benchVisible) { |
| this.bench.sendToBack(); |
| } |
| if (this.headerVisible) { |
| this.header.sendToBack(); |
| } |
| this._renderInBackground(); |
| }; |
| |
| /** |
| * === Method required for objects that act as 'displayParent' === |
| * |
| * Returns 'true' if the Desktop is currently accessible to the user. |
| */ |
| scout.Desktop.prototype.inFront = function() { |
| return true; // Desktop is always available to the user. |
| }; |
| |
| /** |
| * === Method required for objects that act as 'displayParent' === |
| * |
| * Returns the DOM elements to paint a glassPanes over, once a modal Form, message-box, file-chooser or wait-dialog is showed with the Desktop as its 'displayParent'. |
| */ |
| scout.Desktop.prototype._glassPaneTargets = function(element) { |
| // Do not return $container, because this is the parent of all forms and message boxes. Otherwise, no form could gain focus, even the form requested desktop modality. |
| var $glassPaneTargets = this.$container |
| .children() |
| .not('.splitter') // exclude splitter to be locked |
| .not('.desktop-notifications') // exclude notification box like 'connection interrupted' to be locked |
| .not('.overlay-separator'); // exclude overlay separator (marker element) |
| |
| if (element && element.$container) { |
| $glassPaneTargets = $glassPaneTargets.not(element.$container); |
| } |
| |
| var glassPaneTargets; |
| if (element instanceof scout.Form && element.displayHint === scout.Form.DisplayHint.VIEW) { |
| $glassPaneTargets = $glassPaneTargets |
| .not('.desktop-bench') |
| .not('.desktop-header'); |
| |
| if (this.header && this.header.toolBox && this.header.toolBox.$container) { |
| $glassPaneTargets.push(this.header.toolBox.$container); |
| } |
| |
| glassPaneTargets = $.makeArray($glassPaneTargets); |
| scout.arrays.pushAll(glassPaneTargets, this._getBenchGlassPaneTargetsForView(element)); |
| } else { |
| glassPaneTargets = $.makeArray($glassPaneTargets); |
| } |
| |
| // When a popup-window is opened its container must also be added to the result |
| this._pushPopupWindowGlassPaneTargets(glassPaneTargets, element); |
| |
| return glassPaneTargets; |
| }; |
| |
| /** |
| * This 'deferred' object is used because popup windows are not immediately usable when they're opened. |
| * That's why we must render the glass-pane of a popup window later. Which means, at the point in time |
| * when its $container is created and ready for usage. To avoid race conditions we must also wait until |
| * the glass pane renderer is ready. Only when both conditions are fullfilled, we can render the glass |
| * pane. |
| */ |
| scout.Desktop.prototype._deferredGlassPaneTarget = function(popupWindow) { |
| var deferred = new scout.DeferredGlassPaneTarget(); |
| popupWindow.one('init', function() { |
| deferred.ready([popupWindow.$container]); |
| }); |
| return deferred; |
| }; |
| |
| scout.Desktop.prototype._getBenchGlassPaneTargetsForView = function(view) { |
| var $glassPanes = []; |
| |
| $glassPanes = $glassPanes.concat(this._getTabGlassPaneTargetsForView(view, this.header)); |
| |
| if (this.bench) { |
| this.bench.visibleTabBoxes().forEach(function(tabBox) { |
| if (!tabBox.rendered) { |
| return; |
| } |
| if (tabBox.hasView(view)) { |
| $glassPanes = $glassPanes.concat(this._getTabGlassPaneTargetsForView(view, tabBox)); |
| } else { |
| $glassPanes.push(tabBox.$container); |
| } |
| }, this); |
| } |
| return $glassPanes; |
| }; |
| |
| scout.Desktop.prototype._getTabGlassPaneTargetsForView = function(view, tabBox) { |
| var $glassPanes = []; |
| if (tabBox && tabBox.tabArea) { |
| tabBox.tabArea.tabs.forEach(function(tab) { |
| if (tab.view !== view) { |
| $glassPanes.push(tab.$container); |
| // Workaround for javascript not being able to prevent hover event propagation: |
| // In case of tabs, the hover selector is defined on the element that is the direct parent |
| // of the glass pane. Under these circumstances, the hover style isn't be prevented by the glass pane. |
| tab.$container.addClass('no-hover'); |
| } |
| }); |
| } |
| return $glassPanes; |
| }; |
| |
| scout.Desktop.prototype._pushPopupWindowGlassPaneTargets = function(glassPaneTargets, element) { |
| this.formController._popupWindows.forEach(function(popupWindow) { |
| glassPaneTargets.push(popupWindow.initialized ? |
| popupWindow.$container[0] : this._deferredGlassPaneTarget(popupWindow)); |
| }, this); |
| }; |
| |
| scout.Desktop.prototype.showForm = function(form, position) { |
| var displayParent = form.displayParent || this; |
| form.setDisplayParent(displayParent); |
| |
| this._setFormActivated(form); |
| // register listener to recover active form when child dialog is removed |
| displayParent.formController.registerAndRender(form, position, true); |
| }; |
| |
| scout.Desktop.prototype.hideForm = function(form) { |
| if (!form.displayParent) { |
| // showForm has probably never been called -> nothing to do here |
| // May happen if form.close() is called immediately after form.open() without waiting for the open promise to resolve |
| // Hint: it is not possible to check whether the form is rendered and then return (which would be the obvious thing to do). |
| // Reason: Forms in popup windows are removed before getting closed, see DesktopFormController._onPopupWindowUnload |
| return; |
| } |
| |
| if (this.displayStyle === scout.Desktop.DisplayStyle.COMPACT && form.isView() && this.benchVisible) { |
| var openViews = this.bench.getViews().slice(); |
| scout.arrays.remove(openViews, form); |
| if (openViews.length === 0) { |
| // Hide bench and show navigation if this is the last view to be hidden |
| this.switchToNavigation(); |
| } |
| } |
| form.displayParent.formController.unregisterAndRemove(form); |
| if (this.benchVisible && this.bench.getViews().length === 0) { |
| this.bringOutlineToFront(); |
| } |
| }; |
| |
| scout.Desktop.prototype.activateForm = function(form) { |
| var displayParent = form.displayParent || this; |
| displayParent.formController.activateForm(form); |
| this._setFormActivated(form); |
| |
| // If the form has a modal child dialog, this dialog needs to be activated as well. |
| form.dialogs.forEach(function(dialog) { |
| if (dialog.modal) { |
| this.activateForm(dialog); |
| } |
| }, this); |
| }; |
| |
| scout.Desktop.prototype._setOutlineActivated = function() { |
| this._setFormActivated(); |
| if (this.outline) { |
| this.outline.activateCurrentPage(); |
| } |
| }; |
| |
| scout.Desktop.prototype._setFormActivated = function(form) { |
| // If desktop is in rendering process the can not set a new active form. instead the active form from the model is set selected. |
| if (!this.rendered || this.initialFormRendering) { |
| return; |
| } |
| if (this.activeForm === form) { |
| return; |
| } |
| |
| this.activeForm = form; |
| |
| this.triggerFormActivate(form); |
| }; |
| |
| scout.Desktop.prototype.triggerFormActivate = function(form) { |
| this.trigger('formActivate', { |
| form: form |
| }); |
| }; |
| |
| /** |
| * Called when the animation triggered by animationLayoutChange is complete (e.g. navigation or bench got visible/invisible) |
| */ |
| scout.Desktop.prototype.onLayoutAnimationComplete = function() { |
| if (!this.headerVisible) { |
| this._removeHeader(); |
| } |
| if (!this.navigationVisible) { |
| this._removeNavigation(); |
| } |
| if (!this.benchVisible) { |
| this._removeBench(); |
| } |
| this.trigger('animationEnd'); |
| this.animateLayoutChange = false; |
| }; |
| |
| scout.Desktop.prototype.onResize = function(event) { |
| this.revalidateLayoutTree(); |
| }; |
| |
| scout.Desktop.prototype.onPopstate = function(event) { |
| var historyState = event.originalEvent.state; |
| if (historyState && historyState.deepLinkPath) { |
| this.trigger('historyEntryActivate', historyState); |
| } |
| }; |
| |
| scout.Desktop.prototype._onSplitterMove = function(event) { |
| // disallow a position greater than 50% |
| this.resizing = true; |
| var max = Math.floor(this.$container.outerWidth(true) / 2); |
| if (event.position > max) { |
| event.setPosition(max); |
| } |
| }; |
| |
| scout.Desktop.prototype._onSplitterPositionChange = function(event) { |
| // No need to revalidate while layouting (desktop layout sets the splitter position and would trigger a relayout) |
| if (!this.htmlComp.layouting) { |
| this.revalidateLayout(); |
| } |
| }; |
| |
| scout.Desktop.prototype._onSplitterMoveEnd = function(event) { |
| var splitterPosition = event.position; |
| |
| // Store size |
| if (this.cacheSplitterPosition) { |
| this._storeCachedSplitterPosition(this.splitter.position); |
| } |
| |
| // Check if splitter is smaller than min size |
| if (splitterPosition < scout.DesktopNavigation.BREADCRUMB_STYLE_WIDTH) { |
| // Set width of navigation to BREADCRUMB_STYLE_WIDTH, using an animation. |
| // While animating, update the desktop layout. |
| // At the end of the animation, update the desktop layout, and store the splitter position. |
| this.navigation.$container.animate({ |
| width: scout.DesktopNavigation.BREADCRUMB_STYLE_WIDTH |
| }, { |
| progress: function() { |
| this.resizing = true; |
| this.splitter.setPosition(); |
| this.revalidateLayout(); |
| this.resizing = false; // progress seems to be called after complete again -> layout requires flag to be properly set |
| }.bind(this), |
| complete: function() { |
| this.resizing = true; |
| this.splitter.setPosition(); |
| // Store size |
| this._storeCachedSplitterPosition(this.splitter.position); |
| this.revalidateLayout(); |
| this.resizing = false; |
| }.bind(this) |
| }); |
| } else { |
| this.resizing = false; |
| } |
| }; |
| |
| scout.Desktop.prototype._loadCachedSplitterPosition = function() { |
| return scout.webstorage.getItem(sessionStorage, 'scout:desktopSplitterPosition') || |
| scout.webstorage.getItem(localStorage, 'scout:desktopSplitterPosition:' + window.location.pathname); |
| }; |
| |
| scout.Desktop.prototype._storeCachedSplitterPosition = function(splitterPosition) { |
| scout.webstorage.setItem(sessionStorage, 'scout:desktopSplitterPosition', splitterPosition); |
| scout.webstorage.setItem(localStorage, 'scout:desktopSplitterPosition:' + window.location.pathname, splitterPosition); |
| }; |
| |
| scout.Desktop.prototype._onNotificationRemove = function(notification) { |
| if (this.notifications.length === 0 && this.$notifications) { |
| this.$notifications.remove(); |
| this.$notifications = null; |
| } |
| }; |
| |
| scout.Desktop.prototype.onReconnecting = function() { |
| if (!this.offline) { |
| return; |
| } |
| this._offlineNotification.reconnect(); |
| }; |
| |
| scout.Desktop.prototype.onReconnectingSucceeded = function() { |
| if (!this.offline) { |
| return; |
| } |
| this.offline = false; |
| this._offlineNotification.reconnectSucceeded(); |
| this._removeOfflineNotification(); |
| }; |
| |
| scout.Desktop.prototype.onReconnectingFailed = function() { |
| if (!this.offline) { |
| return; |
| } |
| this._offlineNotification.reconnectFailed(); |
| }; |
| |
| scout.Desktop.prototype.dataChange = function(dataType) { |
| this.events.trigger('dataChange', dataType); |
| }; |