view class doc
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713/// FOURJS_START_COPYRIGHT(D,2021)
/// Property of Four Js*
/// (c) Copyright Four Js 2021, 2022. All Rights Reserved.
/// * Trademark of Four Js Development Tools Europe Ltd
///   in the United States and elsewhere
///
/// This file can be modified by licensees according to the
/// product manual.
/// FOURJS_END_COPYRIGHT

"use strict";

modulum("VMSessionNavigationManager", ["EventListener"],
  function(context, cls) {
    const Wevents = context.constants.widgetEvents;
    /**
     * session mode tooling when tabbed container is activated
     * @class VMSessionNavigationManager
     * @memberOf classes
     * @extends classes.EventListener
     * @publicdoc Base
     */
    cls.VMSessionNavigationManager = context.oo.Class(cls.EventListener, function($super) {
      return /** @lends classes.VMSessionNavigationManager.prototype */ {
        __name: "VMSessionNavigationManager",
        /** @type {classes.VMSession} */
        _session: null,

        /** @type {classes.VMApplication} */
        _currentApplication: null,

        /** @type {classes.SessionSidebarWidget} */
        _sidebarWidget: null,

        /** @type {classes.ChromeBarTitleWidget} */
        _chromeBarTitleWidget: null,

        /** @type {classes.ChromeBarWidget} */
        _currentChromeBarWidget: null,
        _stackCurrentApplication: null,
        /**
         * @type {Map<classes.VMApplication, classes.SessionSidebarApplicationItemWidget>}
         */
        _applicationItems: null,

        /**
         * @type {Map<classes.WindowNode, classes.SessionSidebarWindowItemWidget>}
         */
        _windowItems: null,

        /**
         * @type {WeakMap<classes.WindowNode|classes.VMApplication, {nameHandle:HandleRegistration, iconHandle:HandleRegistration}>}
         */
        _handles: null,

        /** @type {Map<string, classes.SessionSidebarApplicationStackItemWidget>} */
        _applicationStackRootWidgets: null,

        /** @type {Map<string, classes.SessionSidebarApplicationStackListWidget>} */
        _applicationStackWidgets: null,

        /** @type {Map<string, Array<string>>} */
        _applicationStacks: null,

        /** @type {Map<string, Array<classes.WindowNode>>} */
        _windowStacks: null,

        /** @type {Map<string, string>} */
        _rootStackLookup: null,

        /** @type {Map<string, classes.VMApplication>} */
        _applicationLookupByProcId: null,

        /** @type {Map<string, string>} */
        _applicationParentLookup: null,

        /** @type {WeakMap<classes.VMApplication, string>} */
        _applicationProcIds: null,

        _currentStackItem: null,

        /** @type {Map<string, Object>} */
        _lastActiveWindow: null,

        /**
         * @inheritDoc
         * @constructs
         * @param {classes.VMSession} session session
         */
        constructor: function(session) {
          $super.constructor.call(this);
          this._session = session;
          this._sidebarWidget = cls.WidgetFactory.createWidget("SessionSidebar", {
            appHash: gbc.systemAppId
          });

          this._chromeBarTitleWidget = cls.WidgetFactory.createWidget("ChromeBarTitle", {
            appHash: gbc.systemAppId
          });

          this._chromeBarTitleWidget.onDropDownBeforeOpen(() => {
            const appId = this._applicationProcIds.get(this._currentApplication),
              rootProcId = this._rootStackLookup.get(appId),
              dropDownContent = this._applicationStackWidgets.get(rootProcId);
            this._chromeBarTitleWidget.setDropDownContent(dropDownContent);
          });

          this._chromeBarTitleWidget.onDropDownClose(() => {
            this._chromeBarTitleWidget.setDropDownContent(null);
          });

          this._applicationItems = new Map();
          this._windowItems = new Map();
          this._handles = new WeakMap();

          this._stackCurrentApplication = new Map();
          this._applicationStackWidgets = new Map();
          this._applicationStackRootWidgets = new Map();
          this._applicationStacks = new Map();
          this._windowStacks = new Map();
          this._rootStackLookup = new Map();

          this._applicationLookupByProcId = new Map();
          this._applicationParentLookup = new Map();

          this._applicationProcIds = new WeakMap();
          this._lastActiveWindow = new Map();
        },

        /**
         * @override
         */
        destroy: function() {
          this._applicationItems = null;
          this._windowItems = null;
          this._handles = null;

          for (const rootWidget of this._applicationStackRootWidgets.values()) {
            this._sidebarWidget.removeChildWidget(rootWidget);
          }

          this._applicationStackRootWidgets = null;
          this._applicationStackWidgets = null;
          this._applicationStacks = null;
          this._rootStackLookup = null;

          this._applicationLookupByProcId = null;
          this._applicationParentLookup = null;

          this._sidebarWidget.destroy();
          this._sidebarWidget = null;
          this._chromeBarTitleWidget.destroy();
          this._chromeBarTitleWidget = null;

          this._session = null;
          this._lastActiveWindow.clear();
          this._lastActiveWindow = null;

          $super.destroy.call(this);
        },

        /**
         * Get the owning session
         * @returns {classes.VMSession}
         */
        getSession: function() {
          return this._session;
        },

        /**
         *
         * @returns {classes.ChromeBarTitleWidget}
         */
        getChromeBarTitleWidget: function() {
          return this._chromeBarTitleWidget;
        },

        /**
         * @returns {classes.SessionSidebarWidget}
         */
        getWidget: function() {
          return this._sidebarWidget;
        },

        /**
         *
         * @param {classes.WindowNode} windowNode
         */
        setCurrentWindow: function(windowNode) {
          const app = windowNode.getApplication(),
            appId = this._applicationProcIds.get(app);
          this._currentApplication = app;
          this._currentWindow = windowNode;
          const rootProcId = this._rootStackLookup.get(appId);
          this._stackCurrentApplication.set(rootProcId, appId);
          if (windowNode.getTitle()) {
            windowNode.setTitle(windowNode.getTitle()); // force title sync if any
          }
        },

        getCurrentWindow: function() {
          return this._currentWindow;
        },

        /**
         *
         * @param {classes.VMApplication} application
         * @returns {?classes.WidgetBase}
         */
        addApplication: function(application) {
          if (this._applicationItems.has(application)) {
            console.warn("Application already added");
            return;
          }
          const applicationSidebarWidget = cls.WidgetFactory.createWidget("SessionSidebarApplicationItem",
            application.getUI().getWidget().getBuildParameters());
          this._applicationItems.set(application, applicationSidebarWidget);
          this._handles.set(application, {
            nameHandle: application.when(Wevents.titleChanged, (evt, src, title) => {
              this.syncHost();
              applicationSidebarWidget.setApplicationName(title);
              const appInfo = application.applicationInfo.connectionInfo,
                rootWidget = this._applicationStackRootWidgets.get(appInfo && appInfo.procId);
              if (rootWidget) {
                rootWidget.setApplicationTitle(title);
              }
            }),
            iconHandle: application.when(Wevents.iconChanged, (evt, src, icon) => {
              this.syncHost();
              applicationSidebarWidget.setApplicationIcon(icon);
              const appInfo = application.applicationInfo.connectionInfo,
                rootWidget = this._applicationStackRootWidgets.get(appInfo && appInfo.procId);
              if (rootWidget) {
                rootWidget.setIcon(icon);
              }
            }),
            clickHandle: applicationSidebarWidget.when(Wevents.click, () => {
              const ui = application.getUI();
              ui.syncCurrentWindow();
              if (gbc.StoredSettingsService.isSideBarVisible()) {
                context.HostLeftSidebarService.hideSidebar();
              }
            })
          });

          application.dvm.onOrdersManaged(() => {
            applicationSidebarWidget.setApplicationName(application.getTitle());
            applicationSidebarWidget.setApplicationIcon(application.getImage());
          }, true);

          gbc.HostService.updateDisplay(); // re-align everything in window

          return applicationSidebarWidget;
        },

        updateApplicationInformation: function(application) {
          const appInfo = application.applicationInfo.connectionInfo;
          let procId = null,
            procIdParent = null,
            procIdWaiting = null;
          if (appInfo) {
            procId = appInfo.procId;
            procIdParent = appInfo.procIdParent || null;
            procIdWaiting = Boolean(appInfo.procIdWaiting);
            this._applicationLookupByProcId.set(procId, application);
            this._applicationParentLookup.set(procId, procIdParent);
            this._applicationProcIds.set(application, procId);
          }
          if (procId) {
            if (!procIdParent || !procIdWaiting || !this._rootStackLookup.get(procIdParent)) {
              if (!this._applicationStackRootWidgets.has(procId)) {
                const rootWidget = cls.WidgetFactory.createWidget("SessionSidebarApplicationStackItem", {
                  ...(application.getUI().getWidget().getBuildParameters()),
                  appHash: gbc.systemAppId
                });
                rootWidget.setApplicationTitle(application.getTitle());
                rootWidget.setIcon(application.getImage());
                rootWidget.when(context.constants.widgetEvents.click, () => {
                  let app = this._applicationLookupByProcId.get(this._stackCurrentApplication.get(procId));
                  if (!app) {
                    const stack = this._applicationStacks.get(procId);
                    app = this._applicationLookupByProcId.get(stack[stack.length - 1]);
                  }
                  const ui = app.getUI();
                  ui.syncCurrentWindow();

                  // Hide left topmenu if any
                  context.HostLeftSidebarService.showTopMenu(false, true); // hide + fast
                  context.TopmenuService.syncTopMenus(app.applicationHash);

                  if (gbc.StoredSettingsService.isSideBarVisible()) {
                    context.HostLeftSidebarService.hideSidebar();
                  }
                });
                const listWidget = cls.WidgetFactory.createWidget("SessionSidebarApplicationStackList", {
                  ...(application.getUI().getWidget().getBuildParameters()),
                  appHash: gbc.systemAppId
                });
                this._applicationStackRootWidgets.set(procId, rootWidget);
                this._applicationStackWidgets.set(procId, listWidget);
                this._applicationStacks.set(procId, [procId]);
                this._windowStacks.set(procId, []);
                this._rootStackLookup.set(procId, procId);
                listWidget.addChildWidget(this._applicationItems.get(application));
                this._sidebarWidget.addChildWidget(rootWidget);
                this.emit(context.constants.VMSessionNavigationManagerEvents.addSessionSidebarApplicationStackItem,
                  application, rootWidget);

              }
            } else {
              const rootProcId = this._rootStackLookup.get(procIdParent),
                stack = this._applicationStacks.get(rootProcId),
                listWidget = this._applicationStackWidgets.get(rootProcId);
              stack.push(procId);
              listWidget.addChildWidget(this._applicationItems.get(application));
              this._rootStackLookup.set(procId, rootProcId);
            }
            context.HostLeftSidebarService.updateApplicationCount(1);
          }
        },

        removeApplication: function(application) {
          context.HostLeftSidebarService.updateApplicationCount(-1);

          const {
            nameHandle,
            iconHandle,
            clickHandle
          } = this._handles.get(application);
          nameHandle();
          iconHandle();
          clickHandle();
          this._handles.delete(application);
          const applicationSidebarWidget = this._applicationItems.get(application),
            parentWidget = applicationSidebarWidget.getParentWidget();

          if (parentWidget) {
            parentWidget.removeChildWidget(applicationSidebarWidget);
          }
          applicationSidebarWidget.destroy();
          this._applicationItems.delete(application);

          if (this._applicationProcIds.has(application)) {
            const procId = this._applicationProcIds.get(application);
            this._applicationProcIds.delete(application);
            this._applicationLookupByProcId.delete(procId);
          }
          const appInfo = application.applicationInfo.connectionInfo,
            procId = appInfo.procId,
            rootProcId = this._rootStackLookup.get(procId),
            stack = this._applicationStacks.get(rootProcId);
          stack.remove(procId);
          if (!stack.length) {
            this._applicationStacks.delete(rootProcId);
            this._sidebarWidget.removeChildWidget(this._applicationStackRootWidgets.get(rootProcId));
            if (this._currentStackItem === this._applicationStackRootWidgets.get(rootProcId)) {
              this._currentStackItem = null;
            }

            this.emit(context.constants.VMSessionNavigationManagerEvents.removeSessionSidebarApplicationStackItem,
              application, this._applicationStackRootWidgets.get(rootProcId));

            this._applicationStackRootWidgets.get(rootProcId).destroy();
            this._applicationStackRootWidgets.delete(rootProcId);
            [...this._rootStackLookup.keys()].filter(k => this._rootStackLookup[k] === rootProcId)
              .forEach(k => {
                this._rootStackLookup.delete(k);
                this._applicationParentLookup.delete(k);
              });
            this._sidebarWidget.removeChildWidget(this._applicationStackWidgets.get(rootProcId));
          }
        },

        setApplicationProcessing: function(application, processing) {
          this._applicationItems.get(application).setProcessing(processing);
        },

        freezeApplication: function(application) {
          const item = this._applicationItems.get(application);
          if (item) {
            item.freeze();
          }
        },

        unfreezeApplication: function(application) {
          const item = this._applicationItems.get(application);
          if (item) {
            item.unfreeze();
          }
        },

        /**
         *
         * @param {classes.WindowNode} windowNode
         * @returns {?classes.WidgetBase}
         */
        addWindow: function(windowNode) {
          if (this._windowItems.has(windowNode)) {
            console.warn("Window already added");
            return;
          }

          const application = windowNode.getApplication();
          windowNode.whenControllerCreated(() => {
            const windowSidebarWidget = cls.WidgetFactory.createWidget("SessionSidebarWindowItem",
              windowNode.getWidget().getBuildParameters());
            this._applicationItems.get(application).addChildWidget(windowSidebarWidget);
            this._windowItems.set(windowNode, windowSidebarWidget);
            const windowList = this._windowStacks.get(this._getRootProcId(application));
            windowList.add(windowNode);
            windowSidebarWidget.setWindowName(windowNode.attribute("text") || windowNode.attribute("name"));
            windowSidebarWidget.setWindowIcon(windowNode.getWidget().getImage());
            this._handles.set(windowNode, {
              nameHandle: windowNode.when(Wevents.titleChanged, (evt, src, title) => {
                this.syncHost();
                windowSidebarWidget.setWindowName(title);
              }),
              iconHandle: windowNode.when(Wevents.iconChanged, (evt, src, icon) => {
                this.syncHost();
                windowSidebarWidget.setWindowIcon(icon);
              }),
              clickHandle: windowSidebarWidget.when(Wevents.click, () => {
                const ui = application.getUI();
                const uiWidget = ui.getWidget();
                ui.syncCurrentWindow();
                ui.setCurrentWindow(windowNode.getId());

                var infoLastWindow = this._lastActiveWindow.get(this.getRootWaitingApplication(application).getProcId());

                if (ui !== infoLastWindow.ui || windowNode.getId() !== infoLastWindow.windowId) {
                  uiWidget.domAttributesMutator(() => uiWidget.addClass("inactiveWindow"));
                } else {
                  uiWidget.domAttributesMutator(() => uiWidget.removeClass("inactiveWindow"));
                }

                if (gbc.StoredSettingsService.isSideBarVisible()) {
                  context.HostLeftSidebarService.hideSidebar();
                }
              })
            });

            this.emit(context.constants.VMSessionNavigationManagerEvents.addSessionSidebarWindowItem,
              application, windowSidebarWidget);

            return windowSidebarWidget;
          }, true);
        },
        /**
         *
         * @param {classes.WindowNode} windowNode
         */
        removeWindow: function(windowNode) {
          const {
            nameHandle,
            iconHandle,
            clickHandle
          } = this._handles.get(windowNode);
          nameHandle();
          iconHandle();
          clickHandle();
          this._handles.delete(windowNode);
          const windowSidebarWidget = this._windowItems.get(windowNode);
          const application = windowNode.getApplication();

          this.emit(context.constants.VMSessionNavigationManagerEvents.removeSessionSidebarWindowItem,
            application, windowSidebarWidget);

          this._applicationItems.get(windowNode.getApplication()).removeChildWidget(windowSidebarWidget);
          windowSidebarWidget.destroy();
          this._windowItems.delete(windowNode);

          const windowList = this._windowStacks.get(this._getRootProcId(application));

          windowList.remove(windowNode);
        },

        /**
         * @param {classes.WindowNode} windowNode
         * @param {boolean} isNextModal
         */
        freezeWindow: function(windowNode, isNextModal) {
          const windowWidget = windowNode && windowNode.getWidget();
          if (windowWidget) {
            windowWidget.freeze(isNextModal);
          }
          if (windowNode) {
            this._windowItems.get(windowNode).setFrozen(true);
          }
        },

        unfreezeWindow: function(windowNode) {
          const windowWidget = windowNode && windowNode.getWidget();
          if (windowWidget) {
            windowWidget.unfreeze();
          }
          if (windowNode) {
            this._windowItems.get(windowNode).setFrozen(false);
          }
          //Sync top menu if any
          context.TopmenuService.syncTopMenus(windowWidget.getApplicationIdentifier());
        },

        /**
         *
         * @param {classes.WindowNode} currentWindowNode
         */
        updateItemsStatuses: function(currentWindowNode) {
          if (currentWindowNode && !currentWindowNode.isModal()) {
            const currentApp = currentWindowNode && currentWindowNode.getApplication(),
              currentSession = currentApp && currentApp.getSession(),
              rootProcId = this._rootStackLookup.get(this._applicationProcIds.get(currentApp)),
              applicationStackItemWidget = this._applicationStackRootWidgets.get(rootProcId);

            if (this._session) {
              if (currentWindowNode) {
                if (currentSession && (!currentSession.isInTabbedContainerMode() || currentSession.getHostApplication() === currentApp)) {
                  const chromeBarWidget = currentApp && currentApp.getChromeBar();
                  //this._chromeBarTitleWidget.setApplicationTitle(currentApp && currentApp.getTitle());
                  const windowList = this._windowStacks.get(this._getRootProcId(currentApp));
                  const stackItem = this._applicationStackRootWidgets.get(this._getRootProcId(currentApp));
                  this._chromeBarTitleWidget.setListingVisible(windowList.length > 1);
                  this._chromeBarTitleWidget.setWindowTitle(currentWindowNode.getTitle(), currentApp);
                  this._chromeBarTitleWidget.setIcon(currentWindowNode.getIcon());
                  if (this._currentChromeBarWidget !== chromeBarWidget) {
                    if (this._currentChromeBarWidget) {
                      this._currentChromeBarWidget.setTitle();
                      this._currentChromeBarWidget = null;
                    }
                    if (chromeBarWidget) {
                      chromeBarWidget.setTitle(this._chromeBarTitleWidget, currentApp);
                      this._currentChromeBarWidget = chromeBarWidget;
                    }
                  }
                }
                if (this._currentStackItem) {
                  this._currentStackItem.setVisible(false);
                  this._currentStackItem = null;
                }
                if (applicationStackItemWidget) {
                  this._currentStackItem = applicationStackItemWidget;
                  applicationStackItemWidget.setVisible(true);
                }
              }
              this._session.getApplications().forEach(app => {
                const appItem = this._applicationItems.get(app);
                if (appItem) {
                  appItem.toggleClass("activeWindow", app === currentApp);
                  app.model.getNodesByTag("Window").forEach(win => {
                    const winItem = this._windowItems.get(win);
                    if (winItem) {
                      winItem.toggleClass("activeWindow", app.getVMWindow() === win);
                      winItem.toggleClass("visibleWindow", win === currentWindowNode);
                    }
                  });
                }
              });
            }
          }
        },
        /**
         *
         * @param {classes.VMApplication} application
         * @private
         */
        _getRootProcId: function(application) {
          return this._rootStackLookup.get(this._applicationProcIds.get(application));
        },

        syncHost: function() {
          this.updateItemsStatuses(context.HostService._currentWindowNode);
        },

        /**
         * Get the application widget list
         * @return {Map<string, classes.SessionSidebarApplicationStackItemWidget>}
         */
        getApplicationWidgetList: function() {
          return this._applicationStackRootWidgets;
        },

        /**
         * Get the application by is procId
         * @param procId
         * @return {classes.VMApplication}
         */
        getApplicationByProcId: function(procId) {
          return this._applicationLookupByProcId.get(procId);
        },

        /**
         * Get the window widget list
         * @return {Map<classes.WindowNode, classes.SessionSidebarWindowItemWidget>}
         */
        getWindowWidgetList: function() {
          return this._windowItems;
        },

        /**
         * get the root waiting application for application procId
         * @param {classes.VMApplication} procId
         * @return {classes.VMApplication|null}
         */
        getRootWaitingApplication: function(application) {
          let app = application;
          let appInfo = application.applicationInfo.connectionInfo;
          let procId = appInfo.procId;

          do {
            if (!appInfo.procIdWaiting || !appInfo.procIdParent) {
              return app;
            }

            procId = appInfo.procIdParent;
            app = this._applicationLookupByProcId.get(procId);
            appInfo = app && app.applicationInfo.connectionInfo;
          } while (app && appInfo);

          return null;
        },

        /**
         * Get parent application
         * @param {classes.VMApplication} application
         * @return {null|classes.VMApplication}
         */
        getParentApplication: function(application) {
          let parentProcId = application.applicationInfo.connectionInfo.procIdParent;

          if (parentProcId) {
            return this._applicationLookupByProcId.get(parentProcId);
          }

          return null;
        },

        /**
         * Get the child application
         * @param {classes.VMApplication} application
         * @return {null|classes.VMApplication}
         */
        getChildApplication: function(application) {
          let appProcId = application.getProcId();

          for (let [procId, app] of this._applicationLookupByProcId.entries()) {
            if (app.applicationInfo.connectionInfo.procIdParent === appProcId) {
              return app;
            }
          }

          return null;
        },

        /**
         * Save the last VMApplication and active window id
         * @param {classes.VMApplication} lastApplication
         * @param {number} lastActiveWindowId
         */
        setLastActiveWindow: function(application, lastActiveWindowId) {
          let rootApp = this.getRootWaitingApplication(application);

          if (!rootApp) {
            return;
          }

          this._lastActiveWindow.set(rootApp.getProcId(), {
            ui: application.getUI(),
            windowId: lastActiveWindowId
          });

          let windowNode = application.model.getNode(lastActiveWindowId);
          let windowText = windowNode.attribute("text");

          if (!windowText) {
            windowText = windowNode.attribute("name");
          }
        },

        /**
         * Go back to the last active window id
         * @param {classes.VMApplication} application
         */
        goBackToLastActiveWindow: function(application) {
          var procId = this.getRootWaitingApplication(application || this.getSession().getCurrentApplication()).getProcId();
          var info = this._lastActiveWindow.get(procId);

          info.ui.syncCurrentWindow();
          info.ui.setCurrentWindow(info.windowId);

          const uiWidget = info.ui.getWidget();
          uiWidget.domAttributesMutator(() => uiWidget.removeClass("inactiveWindow"));

          if (gbc.StoredSettingsService.isSideBarVisible()) {
            context.HostLeftSidebarService.hideSidebar();
          }
        },

        /**
         * Get all the applications
         * @return {classes.VMApplication[]}
         */
        getApplications: function() {
          var res = [];

          this._applicationLookupByProcId.forEach((value) => {
            res.push(value);
          });

          return res;
        }
      };
    });
  });