All Downloads are FREE. Search and download functionalities are using the official Maven repository.

rwt.event.EventHandler.js Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (c) 2004, 2020 1&1 Internet AG, Germany, http://www.1und1.de,
 *                          EclipseSource, and others.
 *
 * 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:
 *    1&1 Internet AG and others - original API and implementation
 *    EclipseSource - adaptation for the Eclipse Remote Application Platform
 ******************************************************************************/

namespace( "rwt.event" );


rwt.event.EventHandler = {

  _filter : {},
  _allowContextMenu : rwt.util.Functions.returnFalse,
  _captureWidget : null,
  _focusRoot : null,
  // state storage:
  _focused : false,
  _blockKeyEvents : false,

  ///////////////////
  // Public functions

  init : function() {
    var functionUtil = rwt.util.Functions;
    this.__onmouseevent = functionUtil.bind( this._onmouseevent, this );
    this.__ondragevent = functionUtil.bind( this._ondragevent, this );
    this.__onselectevent = functionUtil.bind( this._onselectevent, this );
    this.__onwindowblur = functionUtil.bind( this._onwindowblur, this );
    this.__onwindowfocus = functionUtil.bind( this._onwindowfocus, this );
    this.__onwindowresize = functionUtil.bind( this._onwindowresize, this );
    this.__onKeyEvent = rwt.util.Functions.bind( this._onKeyEvent, this );
    rwt.event.EventHandlerUtil.applyBrowserFixes();
  },

  cleanUp : function() {
    delete this.__onmouseevent;
    delete this.__ondragevent;
    delete this.__onselectevent;
    delete this.__onwindowblur;
    delete this.__onwindowfocus;
    delete this.__onwindowresize;
    delete this.__onKeyEvent;
    delete this._lastMouseDownDomTarget;
    delete this._lastMouseDownDispatchTarget;
    rwt.event.EventHandlerUtil.cleanUp();
  },

  attachEvents : function() {
    this.attachEventTypes( this._mouseEventTypes, this.__onmouseevent );
    this.attachEventTypes( this._dragEventTypes, this.__ondragevent );
    this.attachEventTypes( this._keyEventTypes, this.__onKeyEvent );
    window.addEventListener( "blur", this.__onwindowblur, false );
    window.addEventListener( "focus", this.__onwindowfocus, false  );
    window.addEventListener( "resize", this.__onwindowresize, false  );
    document.body.onselect = this.__onselectevent;
    document.onselectstart = this.__onselectevent;
    document.onselectionchange = this.__onselectevent;
  },

  detachEvents : function() {
    this.detachEventTypes( this._mouseEventTypes, this.__onmouseevent);
    this.detachEventTypes( this._dragEventTypes, this.__ondragevent);
    this.detachEventTypes( this._keyEventTypes, this.__onKeyEvent );
    window.removeEventListener( "blur", this.__onwindowblur, false );
    window.removeEventListener( "focus", this.__onwindowfocus, false );
    window.removeEventListener( "resize", this.__onwindowresize, false );
    document.body.onselect = null;
    document.onselectstart = null;
    document.onselectionchange = null;
  },

  setCaptureWidget : function( widget ) {
    if( this._captureWidget !== widget ) {
      if( this._captureWidget !== null ) {
        this._captureWidget.setCapture( false );
      }
      this._captureWidget = widget;
      if( widget != null ) {
        widget.setCapture( true );
      }
    }
  },

  getCaptureWidget : function() {
    return this._captureWidget && this._captureWidget.getEnabled() ? this._captureWidget : null;
  },

  setFocusRoot : function( widget ) {
    if( widget !== this._focusRoot ) {
      if( this._focusRoot !== null ) {
        this._focusRoot.setFocusedChild( null );
      }
      this._focusRoot = widget;
      if( widget !== null && widget.getFocusedChild() === null ) {
        widget.setFocusedChild( widget );
      }
    }
  },

  getFocusRoot : function() {
    return this._focusRoot;
  },

  /**
   * Sets a callback-function to decide if the native context-
   * menu is displayed. It will be called on DOM-events of the type
   * "contextmenu". The target-Widget of the event will be given as
   * the first argument, the dom-target as the second.
   * It must return a boolean. Null is not allowed.
   *
   */
  setAllowContextMenu : function( func ) {
    this._allowContextMenu = func;
  },

  setBlockKeyEvents : function( value ) {
    this._blockKeyEvents = value;
  },

  getBlockKeyEvents : function() {
    return this._blockKeyEvents;
  },

  getMenuManager : function() {
    return rwt.widgets.util.MenuManager.getInstance();
  },

  setMouseEventFilter : function( filter, context ) {
    if( filter ) {
      this._filter[ "mouseevent" ] = [ filter, context ];
    } else {
      delete this._filter[ "mouseevent" ];
    }
  },

  getMouseEventFilter : function() {
    return this._filter[ "mouseevent" ];
  },

  setMouseDomEventFilter : function( filter, context ) {
    this._filter[ "dommouseevent" ] = [ filter, context ];
  },

  setKeyEventFilter : function( filter, context ) {
    // TODO [tb] : Unify behavior and API for EventFilter, only use event
    // create API for order of filter
    this._filter[ "keyevent" ] = [ filter, context ];
  },

  setKeyDomEventFilter : function( filter, context ) {
    this._filter[ "domkeyevent" ] = [ filter, context ];
  },

  //////////////
  // KEY EVENTS:

  _onKeyEvent : function( event ) {
    try {
      var EventHandlerUtil = rwt.event.EventHandlerUtil;
      var keyCode = EventHandlerUtil.getKeyCode( event );
      var charCode = EventHandlerUtil.getCharCode( event );
      if( this._blockKeyEvents ) {
        if( EventHandlerUtil.shouldBlock( event.type, keyCode, charCode, event ) ) {
          EventHandlerUtil.stopDomEvent( event );
        }
        return;
      }
      if( typeof this._filter[ "domkeyevent" ] !== "undefined" ) {
        var context = this._filter[ "domkeyevent" ][ 1 ];
        var func = this._filter[ "domkeyevent" ][ 0 ];
        func.call( context, event.type, keyCode, charCode, event );
      }
      var pseudoTypes = EventHandlerUtil.getEventPseudoTypes( event, keyCode, charCode );
      var keyUpCodes =  EventHandlerUtil.mustRestoreKeyup( keyCode, pseudoTypes );
      for( var i = 0; i < keyUpCodes.length; i++ ) {
        this._onkeyevent_post( event, "keyup", keyUpCodes[ i ], 0 );
        EventHandlerUtil.clearStuckKey( keyUpCodes[ i ] );
      }
      for( var i = 0; i < pseudoTypes.length; i++ ) {
        this._onkeyevent_post( event, pseudoTypes[ i ], keyCode, charCode );
      }
      if( EventHandlerUtil.mustRestoreKeypress( event, pseudoTypes ) ) {
        this._onkeyevent_post( event, "keypress", keyCode, charCode );
      }
      EventHandlerUtil.saveData( event, keyCode, charCode );
    } catch( ex ) {
      rwt.runtime.ErrorHandler.processJavaScriptError( ex );
    }
  },

  _onkeyevent_post : function( vDomEvent, vType, vKeyCode, vCharCode ) {
    var process = true;
    if( typeof this._filter[ "keyevent" ] !== "undefined" ) {
      var context = this._filter[ "keyevent" ][ 1 ];
      var func = this._filter[ "keyevent" ][ 0 ];
      process = func.call( context, vType, vKeyCode, vCharCode, vDomEvent );
    }
    if( process ) {
      this._processKeyEvent( vDomEvent, vType, vKeyCode, vCharCode );
    }
  },

  _processKeyEvent : function( vDomEvent, vType, vKeyCode, vCharCode ) {
    var EventHandlerUtil = rwt.event.EventHandlerUtil;
    var keyIdentifier;
    if( !isNaN( vKeyCode ) && vKeyCode !== 0 ) {
      keyIdentifier = EventHandlerUtil.keyCodeToIdentifier( vKeyCode );
    } else {
      keyIdentifier = EventHandlerUtil.charCodeToIdentifier( vCharCode );
    }
    var vDomTarget = EventHandlerUtil.getDomTarget( vDomEvent );
    var vTarget = this._getKeyEventTarget();
    if( this.getCaptureWidget() ) {
      EventHandlerUtil.stopDomEvent( vDomEvent );
    }
    var vKeyEventObject = new rwt.event.KeyEvent( vType,
                                                      vDomEvent,
                                                      vDomTarget,
                                                      vTarget,
                                                      null,
                                                      vKeyCode,
                                                      vCharCode,
                                                      keyIdentifier );
    if( vTarget != null && vTarget.getEnabled() ) {
      switch( keyIdentifier ) {
        case "Escape":
        case "Tab":
          this.getMenuManager().update(vTarget, vType);
        break;
      }
      if( vDomEvent.ctrlKey && keyIdentifier == "A" ) {
        switch( vDomTarget.tagName.toLowerCase() ) {
          case "input":
          case "textarea":
          case "iframe":
            // selection allowed
          break;
          default:
           EventHandlerUtil.stopDomEvent(vDomEvent);
          break;
        }
      }
      vTarget.dispatchEvent( vKeyEventObject );
      if( rwt.qx.Class.isDefined("rwt.event.DragAndDropHandler") ) {
        rwt.event.DragAndDropHandler.getInstance().handleKeyEvent( vKeyEventObject );
      }
      rwt.widgets.util.ToolTipManager.getInstance().handleKeyEvent( vKeyEventObject );
    }
    vKeyEventObject.dispose();
  },

  ///////////////
  // MOUSE EVENTS

  _onmouseevent : function( event ) {
    try {
      var process = true;
      if( typeof this._filter[ "dommouseevent" ] !== "undefined" ) {
        var context = this._filter[ "dommouseevent" ][ 1 ];
        process = this._filter[ "dommouseevent" ][ 0 ].call( context, event );
      }
      if( process ) {
        this._processMouseEvent( event );
      }
    } catch( ex ) {
      rwt.runtime.ErrorHandler.processJavaScriptError( ex );
    }
  },

  // TODO [tb] : refactor to work like _onKeyEvent
  _processMouseEvent : function( vDomEvent ) {
    var EventHandlerUtil = rwt.event.EventHandlerUtil;
    var vDomTarget = EventHandlerUtil.getDomTarget( vDomEvent );
    var vType = vDomEvent.type;
    switch(vType) {
      case "wheel":
        vType = "mousewheel";
      break;
      case "click":
      case "dblclick":
        // ignore click or dblclick events with other then the left mouse button
        if( vDomEvent.which !== 1 ) {
          return;
        }
    }
    this._onmouseevent_post( vDomEvent, vType, vDomTarget );
  },

  _onmouseevent_post : function( vDomEvent, vType, vDomTarget ) {
    var eventConsumed = false;
    var EventHandlerUtil = rwt.event.EventHandlerUtil;
    var vCaptureTarget = this.getCaptureWidget();
    var vOriginalTarget = EventHandlerUtil.getOriginalTargetObject( vDomTarget );
    var vTarget = EventHandlerUtil.getTargetObject( null, vOriginalTarget, true );
    if( !vTarget ) {
      return;
    }
    var vDispatchTarget = vCaptureTarget ? vCaptureTarget : vTarget;
    var vFixClick = this._onmouseevent_click_fix( vDomTarget, vType, vDispatchTarget );
    if( vType == "contextmenu" ) {
      if( this._allowContextMenu( vOriginalTarget, vDomTarget ) ) {
        eventConsumed = true;
      } else {
        EventHandlerUtil.stopDomEvent( vDomEvent );
      }
    }
    if( vType == "mousedown" ) {
      EventHandlerUtil.handleFocusedChild( vDispatchTarget );
    }
    // handle related target object
    if( vType == "mouseover" || vType == "mouseout" ) {
      var vRelatedTarget = EventHandlerUtil.getRelatedTargetObjectFromEvent( vDomEvent );
      var elementEventType = vType == "mouseover" ? "elementOver" : "elementOut";
      this._fireElementHoverEvents( elementEventType,
                                    vDomEvent,
                                    vDomTarget,
                                    vTarget,
                                    vOriginalTarget,
                                    vRelatedTarget,
                                    vDispatchTarget );
      // Ignore events where the related target and
      // the real target are equal - from our sight
      if( vRelatedTarget == vTarget ) {
        return;
      }
    }
    var vEventObject = new rwt.event.MouseEvent( vType,
                                                     vDomEvent,
                                                     vDomTarget,
                                                     vTarget,
                                                     vOriginalTarget,
                                                     vRelatedTarget );
    // Store last Event in MouseEvent Constructor. Needed for Tooltips, ...
    if( vType !== "contextmenu" ) {
      rwt.event.MouseEvent.storeEventState( vEventObject );
    }
    if( !eventConsumed && !( vType === "mousewheel" && !vEventObject.getWheelDelta() ) ) {
      this._dispatchMouseEvent( vDispatchTarget, vEventObject );
      if( vDispatchTarget.getEnabled() ) {
        this._onmouseevent_special_post( vType,
                                         vTarget,
                                         vOriginalTarget,
                                         vDispatchTarget,
                                         vEventObject,
                                         vDomEvent );
      }
    } else if( vType == "mouseover" ) {
      var toolTipManager = rwt.widgets.util.ToolTipManager.getInstance();
      toolTipManager.handleMouseEvent( vEventObject );
    }
    vEventObject.dispose();
    rwt.widgets.base.Widget.flushGlobalQueues();
    // Fix Click (Gecko Bug, see above)
    if( vFixClick ) {
      this._onmouseevent_post( vDomEvent, "click", this._lastMouseDownDomTarget );
      this._lastMouseDownDomTarget = null;
      this._lastMouseDownDispatchTarget = null;
    }
  },

  _fireElementHoverEvents : function( type,
                                      domEvent,
                                      domTarget,
                                      target,
                                      originalTarget,
                                      relatedTarget,
                                      dispatchTarget )
  {
    if( dispatchTarget.getEnabled() ) {
      var eventObject = new rwt.event.MouseEvent( type,
                                                  domEvent,
                                                  domTarget,
                                                  target,
                                                  originalTarget,
                                                  relatedTarget );
      this._dispatchMouseEvent( dispatchTarget, eventObject );
    }
  },

  _dispatchMouseEvent : function( target, event ) {
    var process = true;
    if( typeof this._filter[ "mouseevent" ] !== "undefined" ) {
      var context = this._filter[ "mouseevent" ][ 1 ];
      process = this._filter[ "mouseevent" ][ 0 ].call( context, event );
    }
    if( process ) {
      target.dispatchEvent( event );
    }
  },

  _onmouseevent_special_post : function( vType,
                                         vTarget,
                                         vOriginalTarget,
                                         vDispatchTarget,
                                         vEventObject )
  {
    switch( vType ) {
      case "mousedown":
        rwt.widgets.util.PopupManager.getInstance().update( vTarget );
        this.getMenuManager().update( vTarget, vType );
        rwt.widgets.util.IframeManager.getInstance().handleMouseDown( vEventObject );
      break;
      case "mouseup":
        // Mouseup event should always hide, independed of target,
        //  so don't send a target
        this.getMenuManager().update( vTarget, vType );
        if( rwt.qx.Class.isDefined("rwt.widgets.util.IframeManager" ) ) {
          rwt.widgets.util.IframeManager.getInstance().handleMouseUp( vEventObject );
        }
      break;
    }
    rwt.widgets.util.ToolTipManager.getInstance().handleMouseEvent( vEventObject );
    this._ignoreWindowBlur = vType === "mousedown";
    if( rwt.qx.Class.isDefined( "rwt.event.DragAndDropHandler" ) && vTarget ) {
      rwt.event.DragAndDropHandler.getInstance().handleMouseEvent( vEventObject );
    }
  },

  _ondragevent : function( event ) {
    try {
      if( !event ) {
        event = window.event;
      }
      if( event.type === "dragstart" || event.type === "dragover" || event.type === "drop" ) {
        rwt.event.EventHandlerUtil.stopDomEvent( event );
      }
      if( ( event.type === "dragover" || event.type === "dragenter" ) && event.dataTransfer ) {
        event.dataTransfer.dropEffect = "none";  // has no effect in IE
      }
    } catch( ex ) {
      rwt.runtime.ErrorHandler.processJavaScriptError( ex );
    }
  },

  ////////////////
  // SELECT EVENTS

  _onselectevent : function( event ) {
    try {
      var EventHandlerUtil = rwt.event.EventHandlerUtil;
      var target = EventHandlerUtil.getOriginalTargetObjectFromEvent( event );
      while( target ) {
        if( target.getSelectable() != null ) {
          if( !target.getSelectable() ) {
            EventHandlerUtil.stopDomEvent( event );
          }
          break;
        }
        target = target.getParent();
      }
    } catch( ex ) {
      rwt.runtime.ErrorHandler.processJavaScriptError( ex );
    }
  },

  _onwindowblur : function( e ) {
    try {
      if( !this._focused || this._ignoreWindowBlur || e.originalTarget != window ) {
        return;
      }
      this._focused = false;
      this.setCaptureWidget( null );
      if( rwt.qx.Class.isDefined( "rwt.widgets.util.PopupManager" ) ) {
        rwt.widgets.util.PopupManager.getInstance().update();
      }
      this.getMenuManager().update();
      if( rwt.qx.Class.isDefined( "rwt.event.DragAndDropHandler" ) ) {
        rwt.event.DragAndDropHandler.getInstance().globalCancelDrag();
      }
      rwt.widgets.base.ClientDocument.getInstance().createDispatchEvent( "windowblur" );
    } catch( ex ) {
      rwt.runtime.ErrorHandler.processJavaScriptError( ex );
    }
  },

  _onwindowfocus : function() {
    try {
      if( this._focused ) {
        return;
      }
      this._focused = true;
      rwt.widgets.base.ClientDocument.getInstance().createDispatchEvent( "windowfocus" );
    } catch( ex ) {
      rwt.runtime.ErrorHandler.processJavaScriptError( ex );
    }
  },

  _onwindowresize : function() {
    try {
      var clientDocument = rwt.widgets.base.ClientDocument.getInstance();
      // Catch redundant resize events, fired for example by iPad:
      var oldWidth = clientDocument.getInnerWidth();
      var oldHeight = clientDocument.getInnerHeight();
      var width = clientDocument._computeInnerWidth();
      var height = clientDocument._computeInnerHeight();
      if( width !== oldWidth || height !== oldHeight ) {
        rwt.widgets.base.ClientDocument.getInstance().createDispatchEvent( "windowresize" );
      }
    } catch( ex ) {
      rwt.runtime.ErrorHandler.processJavaScriptError( ex );
    }
  },

  ///////////////
  // Helper-maps:

  _mouseEventTypes : [
    "mouseover",
    "mousemove",
    "mouseout",
    "mousedown",
    "mouseup",
    "click",
    "dblclick",
    "contextmenu",
    "onwheel" in document ? "wheel" : "mousewheel"
  ],

  _keyEventTypes : [ "keydown", "keypress", "keyup" ],

  _dragEventTypes : [ "dragstart", "drop", "dragover", "dragleave", "dragenter" ],

  ////////////////////
  // Helper-functions:

  _getKeyEventTarget : function() {
    var vFocusRoot = this.getFocusRoot();
    return this.getCaptureWidget() || ( vFocusRoot == null ? null : vFocusRoot.getActiveChild() );
  },

  attachEventTypes : function( vEventTypes, vFunctionPointer ) {
    try {
      // Gecko is a bit buggy to handle key events on document if
      // not previously focused. Internet Explorer has problems to use
      // 'window', so there we use the 'body' element
      var el = rwt.client.Client.isGecko() ? window : document.body;
      for( var i=0, l=vEventTypes.length; i




© 2015 - 2025 Weber Informatics LLC | Privacy Policy