view class doc
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702/// FOURJS_START_COPYRIGHT(D,2015)
/// Property of Four Js*
/// (c) Copyright Four Js 2015, 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('VMApplication', ['EventListener'],
  function(context, cls) {
    /**
     * Object that represents an application of a VM Session
     * @class VMApplication
     * @memberOf classes
     * @extends classes.EventListener
     * @publicdoc Base
     */
    cls.VMApplication = context.oo.Class(cls.EventListener, function($super) {
      return /** @lends classes.VMApplication.prototype */ {
        $static: /** @lends classes.VMApplication */ {
          styleListLoaded: "gStyleListLoaded"
        },
        __name: "VMApplication",
        /** General information for this application
         * @type classes.VMApplicationInfo
         */
        applicationInfo: null,
        procId: null,
        /** Indicator to know if the application is running or not */
        running: false,
        /** Indicator to know if the application is ending */
        ending: false,
        ended: false,
        /** Indicator to know if the application has protocol error */
        hasError: false,
        /** protocolInterface
         * @type classes.ProtocolInterface
         */
        protocolInterface: null,
        /** dvm management
         * @type classes.DVMApplicationService
         */
        dvm: null,
        /** model (aui) management
         * @type classes.AuiApplicationService
         */
        model: null,
        /** layout management
         * @type classes.LayoutApplicationService
         */
        layout: null,
        /** actions
         * @type classes.ActionApplicationService
         */
        action: null,
        /** file transfer management
         * @type classes.FileTransferApplicationService
         */
        filetransfer: null,
        /** type ahead management
         * @type classes.TypeAheadApplicationService
         */
        typeahead: null,
        /** keyboard management
         * @type classes.KeyboardApplicationService
         */
        keyboard: null,
        /** Focus management
         * @type classes.FocusApplicationService
         */
        focus: null,
        /** application ui
         * @type classes.UIApplicationService
         * */
        _ui: null,
        /**
         * @type {classes.VMSession}
         */
        _session: null,

        /** @type {Array} */
        styleAttributesChanged: null,

        /** @type {boolean} */
        styleListsChanged: false,

        /** @type {Object} */
        usedStyleAttributes: {},

        _currentlyProcessing: false,
        _processingDelayer: 0,

        _title: null,
        _icon: null,

        _currentWindow: null,
        /**
         *
         * @param {classes.VMApplicationInfo} info - application info
         * @param {classes.VMSession} session

         */
        constructor: function(info, session) {
          $super.constructor.call(this);
          this._session = session;
          this.applicationInfo = info;
          this.applicationHash = session.getApplicationIdentifier();
          if (!info.inNewWindow) {
            this._ui = cls.ApplicationServiceFactory.create('UI', this);
            this.dvm = cls.ApplicationServiceFactory.create('Dvm', this);
            this.model = cls.ApplicationServiceFactory.create('Model', this);
            this.layout = cls.ApplicationServiceFactory.create('Layout', this);
            this.action = cls.ApplicationServiceFactory.create('Action', this);
            this.filetransfer = cls.ApplicationServiceFactory.create('FileTransfer', this);
            this.typeahead = cls.ApplicationServiceFactory.create('TypeAhead', this);
            this.keyboard = cls.ApplicationServiceFactory.create('Keyboard', this);
            this.focus = cls.ApplicationServiceFactory.create('Focus', this);
            this.message = cls.ApplicationServiceFactory.create('Message', this);
            session.getNavigationManager().addApplication(this);
          }
          this.protocolInterface = cls.ApplicationServiceFactory.create(this._getProtocolInterface(info), this);

          this.styleAttributesChanged = [];
          context.WidgetService.registerVMApplication(this);
        },
        /**
         * Get the owning session
         * @returns {classes.VMSession} The owning session
         * @publicdoc
         */
        getSession: function() {
          return this._session;
        },

        waitForApplicationInNewWindow: function(onSuccess, onFailure) {
          this.protocolInterface.waitForApplicationInNewWindow(onSuccess, onFailure);
        },
        prepareEmergencyClose: function() {
          this._emergencyClose = cls.UANetwork._prepared.close(this);
          window.requestAnimationFrame(function() {
            try {
              if (window.opener && window.opener._emergencyClose) {
                window.opener._emergencyClose[context.uid].push(this._emergencyClose);
              }
            } catch (e) {
              if (e.name === "SecurityError") {
                context.LogService.networkProtocol.debug("GBC opened from a cross domain origin", e);
              }
            }
          }.bind(this));
        },
        _getProtocolInterface: function(info) {
          var result = "NoProtocolInterface";
          switch (info.mode) {
            case "direct":
              result = "DirectProtocolInterface";
              break;
            case "ua":
              result = "UAProtocolInterface";
              break;
            default:
              break;
          }
          return result;
        },
        /**
         * @inheritDoc
         */
        destroy: function() {
          if (!this._destroyed) { // TODO whe should not call destroy on a destroyed object
            this._ui.destroy();
            this.filetransfer.destroy();
            this._session.remove(this);
            this.model.destroy();

            this.applicationInfo = null;

            this._session = null;
            this._ui = null;
            this.dvm = null;
            this.model = null;
            this.layout = null;
            this.action = null;
            this.filetransfer = null;
            this.typeahead = null;
            this.keyboard = null;
            this.focus = null;
            this.message = null;
            this.protocolInterface = null;

            this._currentWindow = null;

            $super.destroy.call(this);

            context.WidgetService.unregisterVMApplication(this);
          }
        },

        start: function() {
          this.protocolInterface.start(this.applicationInfo);
        },
        stop: function(message) {
          if (!this.stopping) {
            this.stopping = true;
            if (!this.ended && this.applicationInfo) {
              if (message) {
                this.applicationInfo.ending = cls.ApplicationEnding.notok(message);
              }
              if (!this.applicationInfo.ending) {
                this.applicationInfo.ending = cls.ApplicationEnding.ok;
              }
              if (this.applicationInfo.urlParameters && this.applicationInfo.urlParameters.logPlayer) {
                this.applicationInfo.ending = cls.ApplicationEnding.logPlayer;
              }

              context.styler.bufferize();
              // TODO reorder, why we don't call this in destroy ??
              this.typeahead.destroy();
              this.model.remove();
              this.setEnding();
              this.action.destroy();
              this.layout.destroy();
              this.model.stop();
              this.dvm.destroy();
              this.protocolInterface.destroy();
              this.keyboard.destroy();
              this.focus.destroy();
              this.filetransfer.destroy();
              this.message.destroy();

              this.destroy();
              context.styler.flush();
              this.ended = true;
            }
          }
        },
        /**
         * Set status of application
         * @param {boolean} running Status
         */
        setRunning: function(running) {
          this.running = running;
          this._ui.setRunning(running);
        },

        /**
         * Set the error status at application's protocol error
         */
        setError: function() {
          this.hasError = true;
        },

        /**
         * Set the ending status at application's end
         */
        setEnding: function() {
          if (!this.ending && !this.ended && !this._destroyed) {
            this.ending = true;
            this.setIdle();
          }
        },

        /**
         * Returns this application's info.
         * @returns {classes.VMApplicationInfo}
         */
        info: function() {
          return this.applicationInfo;
        },
        /**
         * Get application instantiated node by its Aui Id
         * @param {number} id the node id
         * @returns {classes.NodeBase} the node, if found
         * @publicdoc
         */
        getNode: function(id) {
          return this.model && this.model.getNode(id);
        },
        uiNode: function() {
          return this.getNode(0);
        },

        /**
         * Get the VM Focused Node instance
         * @returns {classes.NodeBase} The VM focused node
         * @publicdoc
         */
        getFocusedVMNode: function() {
          var uiNode = this.uiNode();
          if (uiNode) {
            var id = uiNode.attribute("focus");
            return this.getNode(id);
          }
          return null;
        },

        /**
         * Get the VM Focused Node instance
         * or if the focused node is a table or a matrix get the current value node
         * @param {boolean} [inputModeOnly] - return value node only if is node is in INPUT mode
         * @returns {*|classes.NodeBase}
         */
        getFocusedVMNodeAndValue: function(inputModeOnly) {
          var focusedNode = this.getFocusedVMNode();
          if (focusedNode && focusedNode.getCurrentValueNode) {
            var currentValueNode = focusedNode.getCurrentValueNode(inputModeOnly);
            if (currentValueNode) {
              focusedNode = currentValueNode;
            }
          }
          return focusedNode;
        },

        newTask: function() {
          let session = context.BrowserWindowsService.getRootSession();
          if (session) {
            session.newTask();
          } else {
            this.protocolInterface.newTask();
          }
        },
        /**
         * Set the idle status to true
         */
        setIdle: function() {
          this._setProcessingStyle(false);
          this.dvm.setIdle(true);
          this.action.setInterruptablesActive(false);
        },

        /**
         * Bypass Idle Time excecution in case of GMA UR background service
         * @param {Boolean} bypass - true to bypass, false to get back to normal
         */
        bypassIdleTimer: function(bypass) {
          if (context.__wrapper.isGMA()) {
            this._bypassTimer = bypass;
          }
        },

        /**
         * Check if Idle Timer execution is bypassed
         * @return {Boolean} true if byPassed, false otherwise
         */
        isBypassedIdleTimer: function() {
          return this._bypassTimer;
        },

        /**
         * Set the processing status to true
         */
        setProcessing: function() {
          this._setProcessingStyle(true);
          this.dvm.setIdle(false);
          this.action.setInterruptablesActive(true);
        },

        _setProcessingStyleImpl: function(processing) {
          this._processingDelayer = 0;
          if (this._ui && this._ui.getWidget()) {
            this._session.getNavigationManager().setApplicationProcessing(this, processing);
            var windows = this.model.getNodesByTag("Window"),
              len = windows.length,
              i = 0;
            for (; i < len; i++) {
              if (windows[i] && windows[i]._setProcessingStyle) {
                windows[i]._setProcessingStyle(processing);
              }
            }
          }
        },

        _setProcessingStyle: function(processing) {
          if (!processing) {
            if (this._processingDelayer !== 0) {
              this._clearAnimationFrame(this._processingDelayer);
              this._processingDelayer = 0;
            }
            this._setProcessingStyleImpl(false);
          } else {
            if (this._processingDelayer === 0) {
              this._processingDelayer = this._registerAnimationFrame(this._setProcessingStyleImpl.bind(this, true));
            }
          }
        },
        /**
         * Check if the application is idle
         * @returns {boolean} true if idle, false otherwise
         */
        isIdle: function() {
          return this.dvm.idle;
        },

        /**
         * Check if the application is running
         * @returns {boolean} true if running, false otherwise
         */
        isProcessing: function() {
          return !this.dvm.idle;
        },
        /**
         * Send an Interrupt order
         */
        interrupt: function() {
          this.protocolInterface.interrupt();
        },
        close: function() {
          if (!this.ended && !this._destroyed) {
            if (this.ending) {
              this.destroy();
            } else {
              this.protocolInterface.close();
            }
          }
        },
        error: function() {
          this.setEnding();
        },
        /**
         * fail application gracefully,
         * @param ending ending message
         */
        fail: function(ending) {
          if (ending && this.applicationInfo) {
            this.applicationInfo.ending = cls.ApplicationEnding.notok(ending);
          }
          this._registerTimeout(function() {
            this.protocolInterface.stop(ending); // send destroy event to the VM
          }.bind(this));
        },
        /**
         * get active window
         * @returns {classes.WindowNode}
         */
        getVMWindow: function() {
          if (this.ending) {
            return null;
          }
          var uiNode = this.uiNode();
          var uiCurrentWindow = uiNode &&
            uiNode.isAttributeSetByVM("currentWindow") &&
            uiNode.attribute("currentWindow");
          if (uiCurrentWindow) { // we don't consider the value 0 as it is not a Window node
            return this.model.getNode(uiCurrentWindow);
          } else {
            return null;
          }
        },

        getActionApplicationService: function() {
          return this.action;
        },

        /**
         *
         * @param {classes.WidgetBase} widget
         */
        attachRootWidget: function(widget) {
          this._ui.getWidget().addChildWidget(widget);
        },
        /**
         *
         * @returns {classes.UIApplicationService}
         */
        getUI: function() {
          return this._ui;
        },

        /**
         * Get items from the chromebar or from the applicationHostWidget if legacy mode enabled
         * @param {String} name
         * @return {*}
         */
        getMenu: function(name) {
          var menu = this.getUI().getWidget().getUserInterfaceWidget().getChromeBarWidget();
          return menu.getGbcMenuItem(name);
        },

        /**
         * Get the Chromebar
         * @return {classes.ChromeBarWidget|null} the chromebar
         */
        getChromeBar: function() {
          const uiNode = this.model && this.model.getNode(0),
            uiWidget = uiNode && uiNode.getWidget();
          return uiWidget && uiWidget.getChromeBarWidget();
        },

        hasActiveKeyEventNode: function() {
          var uiNode = this.uiNode();
          if (uiNode) {
            var focusId = uiNode.attribute('focus');
            var focusedNode = this.getNode(focusId);
            if (['Table', 'Matrix', 'Menu', 'MenuAction', 'Dialog', 'Action'].indexOf(focusedNode.getTag()) !== -1) {
              var isActive = focusedNode.attribute('active') === 1;
              return isActive && (!focusedNode.isAttributePresent("dialogType") || focusedNode.attribute("dialogType").startsWith(
                "Display")); // if node is table and is in display or displayarray mode (only send keys to VM if not in edit mode)
            }
          }
          return false;
        },

        setTabbedContainerMode: function(activation, windowNode) {
          if (this._session && activation) {
            this._session.activateTabbedContainerMode(windowNode);
          }
        },

        /** Return the action node of active dialog according to vmKey param
         *
         * @param vmKey
         * @returns {*} vm event
         */
        getActiveDialogAction: function(vmKey) {

          var actionNode = null;
          var window = this.getVMWindow();
          var acceleratorName = null;
          var acceleratorName2 = null;
          var acceleratorName3 = null;
          var acceleratorName4 = null;

          if (window) { // search the action in the current dialog
            var dialog = window.getActiveDialog();
            if (dialog) {
              var actions = dialog.getChildren();
              for (var i = 0; i < actions.length; ++i) {
                var action = actions[i];
                var isActive = (action.attribute("active") !== 0);
                if (isActive) {
                  acceleratorName = action.attribute("acceleratorName");
                  acceleratorName2 = action.attribute("acceleratorName2");
                  acceleratorName3 = action.attribute("acceleratorName3");
                  acceleratorName4 = action.attribute("acceleratorName4");
                  if (acceleratorName && acceleratorName.toString().toLowerCase() === vmKey ||
                    acceleratorName2 && acceleratorName2.toString().toLowerCase() === vmKey ||
                    acceleratorName3 && acceleratorName3.toString().toLowerCase() === vmKey ||
                    acceleratorName4 && acceleratorName4.toString().toLowerCase() === vmKey) {
                    actionNode = action;
                  }
                }
              }
            }
          }
          return actionNode;
        },

        /** Return the action node of default action list according to vmKey param
         * @param vmKey
         * @param formNode - optional form node to focus research on. If not specified we look for action defaults in user interface
         * @returns {*} vm event
         */
        getDefaultAction: function(vmKey, formNode) {
          var acceleratorName = null;
          var acceleratorName2 = null;
          var acceleratorName3 = null;
          var acceleratorName4 = null;

          // search the action in the action default list
          var containerNode = formNode || this.uiNode();
          if (containerNode) {
            var actionDefaultList = containerNode.getFirstChild("ActionDefaultList");
            if (actionDefaultList) {
              var actionDefaults = actionDefaultList.getChildren();
              for (var i = 0; i < actionDefaults.length; ++i) {
                var actionDefault = actionDefaults[i];
                acceleratorName = actionDefault.attribute("acceleratorName");
                acceleratorName2 = actionDefault.attribute("acceleratorName2");
                acceleratorName3 = actionDefault.attribute("acceleratorName3");
                acceleratorName4 = actionDefault.attribute("acceleratorName4");
                if (acceleratorName && acceleratorName.toString().toLowerCase() === vmKey ||
                  acceleratorName2 && acceleratorName2.toString().toLowerCase() === vmKey ||
                  acceleratorName3 && acceleratorName3.toString().toLowerCase() === vmKey ||
                  acceleratorName4 && acceleratorName4.toString().toLowerCase() === vmKey) {
                  return actionDefault;
                }
              }
            }
          }
          return null;
        },

        /**
         * Return default action with provided name and optionally declared in form node
         * @param name
         * @param formNode - optional form node to focus research on. If not specified we look for action defaults in user interface
         * @returns {*} vm event
         */
        getDefaultActionForName: function(name, formNode) {
          var containerNode = formNode || this.uiNode();
          var actionDefaultList = containerNode.getFirstChild("ActionDefaultList");
          if (actionDefaultList) {
            var actionDefaults = actionDefaultList.getChildren();
            for (var i = 0; i < actionDefaults.length; ++i) {
              var actionDefault = actionDefaults[i];
              if (name === actionDefault.attribute("name")) {
                return actionDefault;
              }
            }
          }
          return null;
        },

        getTitle: function() {
          return this._title;
        },

        setTitle: function(title) {
          this._title = title;
          this.emit(context.constants.widgetEvents.titleChanged, title);
        },

        getImage: function() {
          return this._image;
        },

        setImage: function(image) {
          this._image = image;
          this.emit(context.constants.widgetEvents.iconChanged, image);
        },

        /**
         * transforms a resource path
         * GBC internal
         * DO NOT IMPLEMENT
         * @param {string} path raw path
         * @param {string} [nativePrefix] native prefix if any
         * @param {string} [browserPrefix] browser prefix if any
         * @return {string} the transformed resource path
         */
        wrapResourcePath: function(path, nativePrefix, browserPrefix) {
          path = "" + path; // force path to string if not
          // if path has a scheme, don't change it
          if (!path || /^(http[s]?|[s]?ftp|data|file|font)/i.test(path)) {
            return path;
          }
          var startPath = context.__wrapper.isNative() ?
            this.info().nativeResourcePrefix + (nativePrefix ? nativePrefix + "/" : "") :
            (browserPrefix ? browserPrefix + "/" : "");

          // In case there is a queryString, we should not encode it, keep it aside before adding it to returnPath
          const queryIndex = path && path.indexOf("?");
          let queryStr = "";
          if (queryIndex > 0) {
            queryStr = path.substring(queryIndex);
            path = path.replace(queryStr, "");
          }

          // Prevent Windows path like C:\foo\too.ttf
          if (context.__wrapper.isNative()) {
            // clean path
            path = encodeURIComponent(path).replace("%2F", "/");
          }

          return startPath + path + queryStr;
        },

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

        setCurrentWindow: function(windowNode) {
          this._currentWindow = windowNode;
        },

        /**
         * get the application procId
         * @return {String}
         */
        getProcId: function() {
          return this.applicationInfo.connectionInfo.procId;
        },

        /**
         * get the parent application procId
         * @return {String}
         */
        getParentProcId: function() {
          return this.applicationInfo.connectionInfo.procIdParent;
        },

        /**
         * Return true if we can accept events on this application
         * @return {boolean}
         */
        canProcessEvent: function() {
          var curSession = context.SessionService.getCurrent();
          var navigationManager = curSession && curSession.getNavigationManager();

          if (this.isProcessing() && navigationManager.getChildApplication(this)) {
            return false;
          }

          return true;
        }
      };
    });
  });