UNPKG

@eclipse-scout/core

Version:
186 lines (161 loc) 7.31 kB
/* * Copyright (c) 2010, 2024 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 * which is available at https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 */ import {arrays, Desktop, DesktopFormControllerModel, Dimension, Event, Form, FormController, InitModelOf, PopupBlockerHandler, PopupWindow, scout} from '../index'; import $ from 'jquery'; export class DesktopFormController extends FormController implements DesktopFormControllerModel { declare model: DesktopFormControllerModel; declare displayParent: Desktop; desktop: Desktop; popupWindows: PopupWindow[]; protected _documentPopupWindowReadyHandler: (event: JQuery.TriggeredEvent, data: PopupWindowReadyData) => void; constructor(model: InitModelOf<DesktopFormController>) { super(model); this.desktop = model.displayParent; this.popupWindows = []; this._documentPopupWindowReadyHandler = this._onDocumentPopupWindowReady.bind(this); // must use a document-event, since when popup-window is reloading it does // only know the opener of its own window (and nothing about Scout). $(document).on('popupWindowReady', this._documentPopupWindowReadyHandler); } override render() { super.render(); let activeForm = this.desktop.activeForm; if (activeForm) { activeForm.activate(); } else { this.desktop.bringOutlineToFront(); } } protected override _renderViews() { super._renderViews(); if (this.desktop.selectedViewTabs) { this.desktop.selectedViewTabs.forEach(selectedView => this._activateView(selectedView)); } // ensure in all view stacks the last view is activated if (this.desktop.bench) { this.desktop.bench.postRender(); } } override isFormShown(form: Form): boolean { if (form.isPopupWindow()) { return this.popupWindows.some(popup => popup.form.id === form.id); } return super.isFormShown(form); } protected override _renderPopupWindow(form: Form, position?: number) { if (form.blockRendering) { return; } let windowSpecs: string, resizeToPrefSize: boolean; // flag used to resize browser-window later (see PopupWindow.js) let bounds = form.readCacheBounds(); if (bounds) { windowSpecs = 'left=' + bounds.x + ',top=' + bounds.y + ',width=' + bounds.width + ',height=' + bounds.height; resizeToPrefSize = false; } else { let $mainDocument = $(document), documentSize = new Dimension($mainDocument.width(), $mainDocument.height()); windowSpecs = 'left=0,top=0,width=' + documentSize.width + ',height=' + documentSize.height; resizeToPrefSize = true; } // Note: Chrome does not allow to position a popup outside the primary monitor (Firefox does) // So the popup will always appear on the primary monitor even if we have stored the correct // bounds to position the popup on the secondary monitor! // See: https://developer.mozilla.org/en-US/docs/Web/API/Window/open#Position_and_size_features windowSpecs += ',location=no,toolbar=no,menubar=no,resizable=yes'; let popupBlockerHandler = scout.create(PopupBlockerHandler, {session: this.session, preserveOpener: true /* no external untrusted URI: Can keep the opener for callback. */}); // form ID in URL is required for 'reload window' support let url = 'popup-window.html?formId=' + form.id; // use '_blank' as window-name so browser-windows are never reused popupBlockerHandler.openWindow(url, '_blank', windowSpecs, popup => { this._addPopupWindow(popup, form, resizeToPrefSize); }); } protected _addPopupWindow(newWindow: Window, form: Form, resizeToPrefSize: boolean) { let popupWindow = new PopupWindow(newWindow, form); popupWindow.resizeToPrefSize = resizeToPrefSize; popupWindow.on('popupWindowUnload', this._onPopupWindowUnload.bind(this)); this.popupWindows.push(popupWindow); $.log.isDebugEnabled() && $.log.debug('Opened new popup window for form ID ' + form.id); } protected _onDocumentPopupWindowReady(event: JQuery.TriggeredEvent, data: PopupWindowReadyData) { $.log.isDebugEnabled() && $.log.debug('(FormController#_onDocumentPopupWindowReady) data=' + data); let popupWindow: PopupWindow; if (data.formId) { // reload (existing popup window) let formId = data.formId; $.log.isDebugEnabled() && $.log.debug('Popup window for form ID ' + formId + ' has been reloaded'); for (let i = 0; i < this.popupWindows.length; i++) { popupWindow = this.popupWindows[i]; if (popupWindow.form.id === formId) { break; } } if (!popupWindow) { throw new Error('Couldn\'t find popupWindow reference while popup window was reloaded'); } } else if (data.popupWindow) { // open new child window popupWindow = data.popupWindow; } else { // error assertion throw new Error('Neither property \'formId\' nor \'popupWindow\' exists on data parameter'); } popupWindow._onReady(); } protected _onPopupWindowUnload(event: Event<PopupWindow>) { let popupWindow = event.source; let form = popupWindow.form; $.log.isDebugEnabled() && $.log.debug('Popup window for form ID ' + form.id + ' is unloaded - don\'t know if its closed or reloaded yet'); // this remove() is important: when a popup-window in IE is closed, all references to a HTMLDivElement become // invalid. Every call or read on such invalid objects will cause an Error. Even though the DOM element // is invalid, the JQuery object which references the DOM element is still alive and occupies memory. That's // why we must remove JQuery objects _before_ the popup-window is closed finally. form.remove(); // must do this with setTimeout because at this point window is always still open // Note: timeout with 0 milliseconds will not work setTimeout(() => { // Check if popup is closed (when the unload event was triggered by page reload it will still be open) if (popupWindow.isClosed()) { $.log.isDebugEnabled() && $.log.debug('Popup window for form ID ' + form.id + ' has been closed'); form.close(); } }, 250); } /** * We only close browser windows here, since during an unload event, we cannot send * anything with an HTTP request anyway. So we cannot inform the server that it * should "kill" the forms - instead we simply render the popupWindows and forms * again when the page has been reloaded. */ closePopupWindows() { this.popupWindows.forEach(popupWindow => this._removePopupWindow(popupWindow.form)); this.popupWindows = []; } protected override _removePopupWindow(form: Form) { let popupWindow = form.popupWindow; if (popupWindow) { delete form.popupWindow; arrays.remove(this.popupWindows, popupWindow); } if (form.rendered) { form.remove(); popupWindow?.close(); } } dispose() { $(document).off('popupWindowReady', this._documentPopupWindowReadyHandler); } } export interface PopupWindowReadyData { window: Window; popupWindow?: PopupWindow; formId?: string; }