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

rwt.widgets.base.BasicText.js Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright: 2004, 2017 1&1 Internet AG, Germany, http://www.1und1.de,
 *                       and EclipseSource
 *
 * 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
 ******************************************************************************/

rwt.qx.Class.define( "rwt.widgets.base.BasicText", {

  extend : rwt.widgets.base.Terminator,

  construct : function( value ) {
    this.base( arguments );
    if( value != null ) {
      this.setValue( value );
    }
    this.initWidth();
    this.initHeight();
    this.initTabIndex();
    this._selectionStart = 0;
    this._selectionLength = 0;
    this.__oninput = rwt.util.Functions.bind( this._oninput, this );
    this.addEventListener( "blur", this._onblur );
    this.addEventListener( "keydown", this._onkeydown );
    this.addEventListener( "keypress", this._onkeypress );
    this.addEventListener( "keyup", this._onkeyup, this );
    this.addEventListener( "mousedown", this._onMouseDownUp, this );
    this.addEventListener( "mouseup", this._onMouseDownUp, this );
    this._typed = null;
    this._selectionNeedsUpdate = false;
    this._applyBrowserFixes();
    this._inputOverflow = "hidden";
  },

  destruct : function() {
    if( this._inputElement != null ) {
      this._inputElement.removeEventListener( "input", this.__oninput, false );
    }
    this._inputElement = null;
    this.__font = null;
    if( this._checkTimer ) {
      this._checkTimer.dispose();
      this._checkTimer = null;
    }
  },

  events: {
    "input" : "rwt.event.DataEvent"
  },

  properties : {

    allowStretchX : { refine : true, init : true },
    allowStretchY : { refine : true, init : false },
    appearance : { refine : true, init : "text-field" },
    tabIndex : { refine : true, init : 1 },
    hideFocus : { refine : true, init : true },
    width : { refine : true, init : "auto" },
    height : { refine : true, init : "auto" },
    selectable : { refine : true, init : true },

    value : {
      init : "",
      nullable : true,
      event : "changeValue",
      apply : "_applyValue",
      dispose : true // in the case we use i18n text here
    },

    textAlign : {
      check : [ "left", "center", "right", "justify" ],
      nullable : true,
      themeable : true,
      apply : "_applyTextAlign"
    },

    maxLength : {
      check : "Integer",
      apply : "_applyMaxLength",
      nullable : true
    },

    readOnly : {
      check : "Boolean",
      apply : "_applyReadOnly",
      init : false,
      event : "changeReadOnly"
    }

  },

  members : {
    _inputTag : "input",
    _inputType : "text",
    _inputElement : null,

    /////////
    // API

    setSelection : function( selection ) {
      this._selectionStart = selection[ 0 ];
      this._selectionLength = selection[ 1 ] - selection[ 0 ];
      this._renderSelection();
    },

    getSelection : function() {
      return [ this._selectionStart, this._selectionStart + this._selectionLength ];
    },

    getComputedSelection : function() {
      var start = this._getSelectionStart();
      var length = this._getSelectionLength();
      return [ start, start + length ];
    },

    getComputedValue : function() {
      var result;
      if( this.isCreated() ) {
        result = this._inputElement.value;
      } else {
        result = this.getValue();
      }
      return result;
    },

    getInputElement : function() {
      if( !this._inputElement && !this.isDisposed() ) {
        this._inputElement = document.createElement( this._inputTag );
        if( this._inputType ) {
          this._inputElement.type = this._inputType;
        }
        this._inputElement.style.position = "absolute";
        this._inputElement.autoComplete = "off";
        this._inputElement.setAttribute( "autoComplete", "off" );
      }
      return this._inputElement || null;
    },

    /////////////////////
    // selection handling

    _renderSelection : function() {
      // setting selection here might de-select all other selections, so only render if focused
      if( this.isCreated() && ( this.getFocused() || this.getParent().getFocused() ) ) {
        this._setSelectionStart( this._selectionStart );
        this._setSelectionLength( this._selectionLength );
        this._selectionNeedsUpdate = false;
      }
    },

    _detectSelectionChange : function() {
      if( this._isCreated ) {
        var start = this._getSelectionStart();
        var length = this._getSelectionLength();
        if( typeof start === "undefined" ) {
          start = 0;
        }
        if( typeof length === "undefined" ) {
          length = 0;
        }
        if( this._selectionStart !== start || this._selectionLength !== length ) {
          this._handleSelectionChange( start, length );
        }
      }
    },

    _handleSelectionChange : function( start, length ) {
      this._selectionStart = start;
      this._selectionLength = length;
      this.dispatchSimpleEvent( "selectionChanged" );
    },

    _setSelectionStart : function( vStart ) {
      this._visualPropertyCheck();
      // the try catch blocks are neccesary because FireFox raises an exception
      // if the property "selectionStart" is read while the element or one of
      // its parent elements is invisible
      // https://bugzilla.mozilla.org/show_bug.cgi?id=329354
      try {
        if( this._inputElement.selectionStart !== vStart ) {
          this._inputElement.selectionStart = vStart;
        }
      } catch(ex ) {
        // do nothing
      }
    },

    _getSelectionStart : function() {
      this._visualPropertyCheck();
      try {
        if( this.isValidString( this._inputElement.value ) ) {
          return this._inputElement.selectionStart;
        } else {
          return 0;
        }
      } catch( ex ) {
        return 0;
      }
    },

    _setSelectionLength : function( length ) {
      this._visualPropertyCheck();
      try {
        if( this.isValidString( this._inputElement.value ) ) {
          var end = this._inputElement.selectionStart + length;
          if( this._inputElement.selectionEnd != end ) {
            this._inputElement.selectionEnd = end;
          }
        }
      } catch( ex ) {
        // do nothing
      }
    },

    _getSelectionLength : function() {
      this._visualPropertyCheck();
      try {
        return this._inputElement.selectionEnd - this._inputElement.selectionStart;
      } catch( ex ) {
        return 0;
      }
    },

    selectAll : function() {
      this._visualPropertyCheck();
      if( this.getValue() != null ) {
        this._setSelectionStart( 0 );
        this._setSelectionLength( this._inputElement.value.length );
      }
      // to be sure we get the element selected
      this._inputElement.select();
      // RAP [if] focus() leads to error in IE if the _inputElement is disabled or not visible.
      // 277444: JavaScript error in IE when using setSelection on a ComboViewer with setEnabled
      // is false
      // https://bugs.eclipse.org/bugs/show_bug.cgi?id=277444
      // 280420: [Combo] JavaScript error in IE when using setSelection on an invisible Combo
      // https://bugs.eclipse.org/bugs/show_bug.cgi?id=280420
      if( this.isEnabled() && this.isSeeable() ) {
        this._inputElement.focus();
      }
      this._detectSelectionChange();
    },

    ////////////
    // rendering

    _applyElement : function( value, old ) {
      this.base( arguments, value, old );
      if( value ) {
        var inputElement = this.getInputElement();
        inputElement.disabled = this.getEnabled() === false;
        inputElement.readOnly = this.getReadOnly();
        inputElement.value = this.getValue() != null ? this.getValue().toString() : "";
        if( this.getMaxLength() != null ) {
          inputElement.maxLength = this.getMaxLength();
        }
        inputElement.style.padding = 0;
        inputElement.style.margin = 0;
        inputElement.style.border = "0 none";
        inputElement.style.background = "transparent";
        // See Bug 419676: [Text] Input element of Text field may not be vertically centered in IE
        if( rwt.client.Client.isTrident() ) {
          inputElement.style.verticalAlign = "top";
        }
        inputElement.style.overflow = this._inputOverflow;
        inputElement.style.outline = "none";
        inputElement.style.resize = "none";
        inputElement.style.WebkitAppearance = "none";
        inputElement.style.MozAppearance = "none";
        this._renderFont();
        this._renderTextColor();
        this._renderTextAlign();
        this._renderCursor();
        this._renderTextShadow();
        this._textInit();
        this._getTargetNode().appendChild( this._inputElement );
      }
    },

    _textInit : function() {
      this._inputElement.addEventListener( "input", this.__oninput, false );
      this._applyBrowserFixesOnCreate();
    },

    _postApply : function() {
      this._syncFieldWidth();
      this._syncFieldHeight();
    },

    _changeInnerWidth : function() {
      this._syncFieldWidth();
    },

    _changeInnerHeight : function() {
      this._syncFieldHeight();
      this._centerFieldVertically();
    },

    _syncFieldWidth : function() {
      this._inputElement.style.width = Math.max( 2, this.getInnerWidth() ) + "px";
    },

    _syncFieldHeight : function() {
      if( this._inputTag !== "input" ) {
        this._inputElement.style.height = this.getInnerHeight() + "px";
      }
    },

    _applyCursor : function() {
      if( this.isCreated() ) {
        this._renderCursor();
      }
    },

    _renderCursor : function() {
      var value = this.getCursor();
      if( value ) {
        this._inputElement.style.cursor = value;
      } else {
        this._inputElement.style.cursor = "";
      }
    },

    _applyTextAlign : function() {
      if( this._inputElement ) {
        this._renderTextAlign();
      }
    },

    _renderTextAlign : function() {
      var textAlign = this.getTextAlign();
      if( this.getDirection() === "rtl" ) {
        if( textAlign === "left" ) {
          textAlign = "right";
        } else if( textAlign === "right" ) {
          textAlign = "left";
        }
      }
      this._inputElement.style.textAlign = textAlign || "";
    },

    _applyEnabled : function( value, old ) {
      if( this.isCreated() ) {
        this._inputElement.disabled = value === false;
      }
      return this.base( arguments, value, old );
    },

    _applyValue : function() {
      this._renderValue();
      this._detectSelectionChange();
    },

    _renderValue : function() {
      this._inValueProperty = true;
      var value = this.getValue();
      if( this.isCreated() ) {
        if (value === null) {
          value = "";
        }
        if( this._inputElement.value !== value ) {
          this._inputElement.value = value;
        }
      }
      delete this._inValueProperty;
    },

    _applyMaxLength : function( value ) {
      if( this._inputElement ) {
        this._inputElement.maxLength = value == null ? "" : value;
      }
    },

    _applyReadOnly : function( value ) {
      if( this._inputElement ) {
        this._inputElement.readOnly = value;
      }
      this.toggleState( "readonly", value );
    },

    _renderTextColor : function() {
      if( this.isCreated() ) {
        var color = this.getEnabled() ? this.getTextColor() : this.__theme$textColor;
        if( this._textColor !== color ) {
          this._textColor = color;
          rwt.html.Style.setStyleProperty( this._inputElement, "color", color || "" );
        }
      }
    },

    _applyFont : function( value ) {
      this._styleFont( value );
    },

    _styleFont : function( value ) {
      this.__font = value;
      this._renderFont();
    },

    _renderFont : function() {
      if( this.isCreated() ) {
        if( this.__font != null ) {
          this.__font.renderElement( this._inputElement );
        } else {
          rwt.html.Font.resetElement( this._inputElement );
        }
      }
    },

    _applyTextShadow : function( value ) {
      this.__textShadow = value;
      if( this._inputElement ) {
        this._renderTextShadow();
      }
    },

    _renderTextShadow : function() {
      rwt.html.Style.setTextShadow( this._inputElement, this.__textShadow );
    },

    _visualizeFocus : function() {
      this.base( arguments );
      if( !rwt.widgets.util.FocusHandler.blockFocus ) {
        try {
          this._inputElement.focus();
        } catch( ex ) {
          // ignore
        }
      }
    },

    _visualizeBlur : function() {
      this.base( arguments );
      try {
        this._inputElement.blur();
      } catch( ex ) {
        // ignore
      }
    },

    _afterAppear : function() {
      this.base( arguments );
      this._centerFieldVertically();
      this._renderSelection();
    },


    _centerFieldVertically : function() {
      if( this._inputTag === "input" && this._inputElement ) {
        var innerHeight = this.getInnerHeight();
        var inputElementHeight = this._inputElement.offsetHeight;
        if( inputElementHeight !== 0 ) {
          var top = ( innerHeight - inputElementHeight ) / 2;
          if( top < 0 ) {
            top = 0;
          }
          this._inputElement.style.top = Math.floor( top ) + "px";
        }
      }
    },

    ////////////////
    // event handler

    _oninput : function() {
      try {
        var doit = true;
        if( this.hasEventListeners( "input" ) ) {
          doit = this.dispatchEvent( new rwt.event.DataEvent( "input", this._typed ), true );
        }
        if( doit ) {
          // at least webkit does sometiems fire "input" before the selection is updated
          rwt.client.Timer.once( this._updateValueProperty, this, 0 );
        } else if( rwt.client.Client.isWebkit() || rwt.client.Client.isBlink() ) {
          // some browser set new selection after input event, ignoring all changes before that
          rwt.client.Timer.once( this._renderSelection, this, 0 );
          this._selectionNeedsUpdate = true;
        }
      } catch( ex ) {
        rwt.runtime.ErrorHandler.processJavaScriptError( ex );
      }
    },

    _updateValueProperty : function() {
      if( !this.isDisposed() ) {
        this.setValue( this.getComputedValue().toString() );
      }
    },

    _ontabfocus : function() {
      this.selectAll();
    },

    _applyFocused : function( newValue, oldValue ) {
      this.base( arguments, newValue, oldValue );
      if( newValue && !rwt.widgets.util.FocusHandler.mouseFocus ) {
        this._renderSelection();
      }
    },

    _onblur : function() {
      // RAP workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=201080
      if( this.getParent() != null ) {
        this._setSelectionLength( 0 );
      }
    },

    // [rst] Catch backspace in readonly text fields to prevent browser default
    // action (which commonly triggers a history step back)
    // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=178320
    _onkeydown : function( e ) {
      if( e.getKeyIdentifier() == "Backspace" && this.getReadOnly() ) {
        e.preventDefault();
      }
      this._detectSelectionChange();
      this._typed = null;
    },

    // [if] Stops keypress propagation
    // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=335779
    _onkeypress : function( e ) {
      if( e.getKeyIdentifier() !== "Tab" ) {
        e.stopPropagation();
      }
      if( this._selectionNeedsUpdate ) {
        this._renderSelection();
      }
      this._detectSelectionChange();
      this._typed = String.fromCharCode( e.getCharCode() );
    },

    _onkeyup : function() {
      if( this._selectionNeedsUpdate ) {
        this._renderSelection();
      }
      this._detectSelectionChange();
      this._typed = null;
    },

    _onMouseDownUp : function() {
      this._detectSelectionChange();
      this._typed = null;
    },

    /////////////////
    // browser quirks

    _applyBrowserFixes : rwt.util.Variant.select( "qx.client", {
      "default" : function() {},
      "trident" : function() {
        // See Bug 372193 - Text widget: Modify Event not fired for Backspace key in IE
        this._checkTimer = new rwt.client.Timer( 0 );
        this._checkTimer.addEventListener( "interval", this._checkValueChanged, this );
        // For delete, backspace, CTRL+X, etc:
        this.addEventListener( "keypress", this._checkTimer.start, this._checkTimer );
        this.addEventListener( "keyup", this._checkTimer.start, this._checkTimer );
        // For context menu: (might not catch the change instantly
        this.addEventListener( "mousemove", this._checkValueChanged, this );
        this.addEventListener( "mouseout", this._checkValueChanged, this );
        // Backup for all other cases (e.g. menubar):
        this.addEventListener( "blur", this._checkValueChanged, this );
      }
    } ),

    _checkValueChanged : function() {
      this._checkTimer.stop();
      var newValue = this.getComputedValue();
      var oldValue = this.getValue();
      if( newValue !== oldValue ) {
        this._oninput();
      }
    },

    _applyBrowserFixesOnCreate : rwt.util.Variant.select( "qx.client", {
      "default" : function() {},
      "webkit|blink" : function() {
        this.addEventListener( "keydown", this._preventEnter, this );
        this.addEventListener( "keypress", this._preventEnter, this );
        this.addEventListener( "keyup", this._preventEnter, this );
        this._applyIOSFixes();
      }
    } ),

    _applyIOSFixes : function() {
      if( rwt.client.Client.getPlatform() === "ios" ) {
        var onfocus = rwt.util.Functions.bind( function() {
          var control = rwt.remote.WidgetManager.getInstance().findControl( this );
          control.getFocusRoot().setFocusedChild( control );
        }, this );
        this._inputElement.addEventListener( "focus", onfocus, false );
        this.addEventListener( "dispose", function() {
          if( this._inputElement != null ) {
            this._inputElement.removeEventListener( "focus", onfocus, false );
          }
        } );
      }
    },

    _preventEnter : function( event ) {
      if( event.getKeyIdentifier() === "Enter" ) {
        event.preventDefault();
      }
    },

    /////////
    // helper

    isValidString : function( v ) {
      return typeof v === "string" && v !== "";
    }
  }

} );




© 2015 - 2025 Weber Informatics LLC | Privacy Policy