/*
* Copyright (c) 2014 Ahomé Innovation Technologies. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.ait.toolkit.sencha.ext.client.ui;
import com.ait.toolkit.core.client.JsoHelper;
import com.ait.toolkit.sencha.ext.client.core.Component;
import com.ait.toolkit.sencha.ext.client.events.form.KeyDownHandler;
import com.ait.toolkit.sencha.ext.client.events.form.KeyPressHandler;
import com.ait.toolkit.sencha.ext.client.events.form.KeyUpHandler;
import com.ait.toolkit.sencha.ext.client.field.FieldBase;
import com.ait.toolkit.sencha.shared.client.core.RegExp;
import com.ait.toolkit.sencha.shared.client.dom.ExtElement;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.editor.client.IsEditor;
import com.google.gwt.editor.client.LeafValueEditor;
import com.google.gwt.editor.client.adapters.TakesValueEditor;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.ui.HasValue;
/**
* A basic text field. Can be used as a direct replacement for traditional text
* inputs, or as the base class for more sophisticated input controls (like
* TextArea and ComboBox). Has support for empty-field placeholder values (see
* emptyText).
*/
public class TextField extends FieldBase implements HasValue<String>,
IsEditor<LeafValueEditor<String>> {
private LeafValueEditor<String> editor;
private static JavaScriptObject configPrototype;
private static native void init()/*-{
var c = new $wnd.Ext.form.field.Text();
@com.ait.toolkit.sencha.ext.client.ui.TextField::configPrototype = c.initialConfig;
}-*/;
protected JavaScriptObject getConfigPrototype() {
return configPrototype;
}
public String getXType() {
return "textfield";
}
/**
* Create a new TextField.
*/
public TextField() {}
protected TextField( JavaScriptObject jsObj ) {
super( jsObj );
}
protected native JavaScriptObject create( JavaScriptObject jsObj ) /*-{
return new $wnd.Ext.form.field.Text(jsObj);
}-*/;
/**
* Automatically grows the field to accomodate the width of the text up to
* the maximum field width allowed. This only takes effect if grow = true,
* and fires the autosize event.
*/
public native void autoSize() /*-{
var field = this.@com.ait.toolkit.sencha.ext.client.core.Component::getOrCreateJsObj()();
field.autoSize();
}-*/;
/**
* Selects text in this field.
*/
public native void selectText() /*-{
var field = this.@com.ait.toolkit.sencha.ext.client.core.Component::getOrCreateJsObj()();
field.selectText();
}-*/;
/**
* Selects text in this field.
*
* @param start
* the index where the selection should start
* @param end
* the index where the selection should end
*/
public native void selectText( int start, int end ) /*-{
var field = this.@com.ait.toolkit.sencha.ext.client.core.Component::getOrCreateJsObj()();
field.selectText(start, end);
}-*/;
/**
* Validates a value according to the field's validation rules and marks the
* field as invalid if the validation fails.
*
* @param value
* the value to valdiate
* @return true if valid
*/
public native boolean validateValue( String value ) /*-{
var field = this.@com.ait.toolkit.sencha.ext.client.core.Component::getOrCreateJsObj()();
return field.validateValue(value);
}-*/;
/**
* Returns the value of the text field.
*
* @return the text field value
*/
public String getText() {
return getValueAsString();
}
// --- config properties ---
/**
* False to validate that the value length > 0 (defaults to true).
*
* @param allowBlank
* false to disallow blank
*/
public void setAllowBlank( boolean allowBlank ) {
setAttribute( "allowBlank", allowBlank, true, true );
}
/**
* Specify false to automatically trim the value before validating the
* whether the value is blank. Setting this to false automatically sets
* allowBlank to false.
* <p>
* Defaults to: true
*/
public void setAllowOnlyWhiteSpace( boolean allowBlank ) {
setAttribute( "allowOnlyWhiteSpace", allowBlank, true, true );
}
/**
* Error text to display if the allow blank validation fails (defaults to
* "This field is required").
*
* @param blankText
* error message for blank field
*/
public void setBlankText( String blankText ) {
setAttribute( "blankText", blankText, true, true );
}
/**
* True to disable input keystroke filtering (defaults to false).
*
* @param disableKeyFilter
* true to disable
* @throws IllegalStateException
* this property cannot be changed after the Component has been
* rendered
*/
public void setDisableKeyFilter( boolean disableKeyFilter )
throws IllegalStateException {
setAttribute( "disableKeyFilter", disableKeyFilter, true );
}
/**
* The CSS class to apply to an empty field to style the emptyText (defaults
* to 'x-form-empty-field'). This class is automatically added and removed
* as needed depending on the current field value.
*
* @param emptyClass
* the empty field CSS class
* @throws IllegalStateException
* this property cannot be changed after the Component has been
* rendered
*/
public void setEmptyClass( String emptyClass ) throws IllegalStateException {
setAttribute( "emptyClass", emptyClass, true );
}
/**
* The default text to display in an empty field (defaults to null).
*
* @param emptyText
* the empty field text
*/
public void setEmptyText( String emptyText ) {
setAttribute( "emptyText", emptyText, true, true );
}
/**
* The default text to display in an empty field (defaults to null).
*
* @param emptyText
* the empty field text
*/
public void setEnableKeyEvents( boolean value ) {
setAttribute( "enableKeyEvents", value, true );
}
/**
* True to set the maxLength property on the underlying input field.
* Defaults to false
* <p>
*/
public void setEnforceMaxLength( boolean value ) {
setAttribute( "enforceMaxLength", value, true, true );
}
/**
* True if this field should automatically grow and shrink to its content.
*
* @param grow
* true to allow grow
* @throws IllegalStateException
* this property cannot be changed after the Component has been
* rendered
*/
public void setGrow( boolean grow ) throws IllegalStateException {
setAttribute( "grow", grow, true, true );
}
/**
* A string that will be appended to the field's current value for the
* purposes of calculating the target field size. Only used when the grow
* config is true. Defaults to a single capital "W" (the widest character in
* common fonts) to leave enough space for the next typed character and
* avoid the field value shifting before the width is adjusted.
* <p>
* Defaults to: 'W'
*
* @throws IllegalStateException
* this property cannot be changed after the Component has been
* rendered
*/
public void setGrowAppend( String value ) throws IllegalStateException {
setAttribute( "grow", value, true, true );
}
/**
* The maximum width to allow when grow = true (defaults to 800).
*
* @param growMax
* the max width
* @throws IllegalStateException
* this property cannot be changed after the Component has been
* rendered
*/
public void setGrowMax( int growMax ) throws IllegalStateException {
setAttribute( "growMax", growMax, true );
}
/**
* The minimum width to allow when grow = true (defaults to 30).
*
* @param growMin
* the minimum width
* @throws IllegalStateException
* this property cannot be changed after the Component has been
* rendered
*/
public void setGrowMin( int growMin ) throws IllegalStateException {
setAttribute( "growMin", growMin, true );
}
/**
* An input mask regular expression that will be used to filter keystrokes
* that don't match (defaults to null).
*
* <br>
* <br>
* <b>Note:</b> This property cannot be changed after the Component has been
* rendered.
*
* @param maskRe
* the mask regular expression
*/
public void setMaskRe( String maskRe ) {
RegExp reg = new RegExp( maskRe );
setAttribute( "maskRe", reg.getJsObj(), true );
}
public void setMaskRe( RegExp reg ) {
setAttribute( "maskRe", reg.getJsObj(), true );
}
/**
* A JavaScript RegExp object used to strip unwanted content from the value
* during input. If stripCharsRe is specified, every character sequence
* matching stripCharsRe will be removed.
*
* @param maskRe
* the mask regular expression
*/
public void setStripCharsRe( String maskRe ) {
RegExp reg = new RegExp( maskRe );
setAttribute( "stripCharsRe", reg.getJsObj(), true );
}
public void setStripCharsRe( RegExp reg ) {
setAttribute( "stripCharsRe", reg.getJsObj(), true );
}
public void setMaxLength( int maxLength ) {
setAttribute( "maxLength", maxLength, true, true );
}
/**
* Error text to display if the maximum length validation fails. (defaults
* to "The maximum length for this field is {maxLength}")
*
* @param maxLengthText
* the max lenght error text
*/
public void setMaxLengthText( String maxLengthText ) {
setAttribute( "maxLengthText", maxLengthText, true, true );
}
/**
* Minimum input field length required (defaults to 0).
*
* @param minLength
* the min length
*/
public void setMinLength( int minLength ) {
setAttribute( "minLength", minLength, true, true );
}
/**
* Error text to display if the minimum length validation fails. (defaults
* to "The minimum length for this field is {minLength}")
*
* @param minLengthText
* the min length error text
*/
public void setMinLengthText( String minLengthText ) {
setAttribute( "minLengthText", minLengthText, true, true );
}
/**
* The CSS class to apply to a required field, i.e. a field where allowBlank
* is false.
* <p>
* Defaults to: 'x-form-required-field'
*/
public void setRequireCls( String value ) {
setAttribute( "requireCls", value, true, true );
}
/**
* An initial value for the 'size' attribute on the text input element. This
* is only used if the field has no configured width and is not given a
* width by its container's layout. Defaults to 20.
* <p>
* Defaults to: 20
*/
public void setSize( double value ) {
setAttribute( "size", value, true );
}
/**
* Set true if field is a password field.
*
* @param password
* true if passowrd field
* @throws IllegalStateException
* this property cannot be changed after the Component has been
* rendered
*/
public void setPassword( boolean password ) throws IllegalStateException {
if( password )
setInputType( "password" );
}
/**
* A Regular Expressionto be tested against the field value during
* validation (defaults to null). If available, this regex will be evaluated
* only after the basic validators all return true, and will be passed the
* current field value. If the test fails, the field will be marked invalid
* using regexText.
*
*
* @param regex
* the regular expression
*/
public void setRegex( String regex ) {
setRegex( isCreated() ? getJsObj() : config, regex );
}
private native void setRegex( JavaScriptObject config, String regex ) /*-{
var config = this.@com.ait.toolkit.sencha.ext.client.core.Component::config;
config['regex'] = new $wnd.RegExp(regex);
}-*/;
/**
* The error text to display if regex is used and the test fails during
* validation (defaults to "").
*
* <br>
* <br>
* <b>Note:</b> This property cannot be changed after the Component has been
* rendered.
*
* @param regexText
* the regexp text
*/
public void setRegexText( String regexText ) {
setAttribute( "regexText", regexText, true, true );
}
/**
* True to automatically select any existing field text when the field
* receives input focus (defaults to false).
*
* @param selectOnFocus
* true to select text on focus
* @throws IllegalStateException
* this property cannot be changed after the Component has been
* rendered
*/
public void setSelectOnFocus( boolean selectOnFocus )
throws IllegalStateException {
setAttribute( "selectOnFocus", selectOnFocus, true );
}
/**
* A validation type name as defined in {@link VTypes} (defaults to null).
*
* @param vtype
* the validation type
*/
public void setVtype( VTypes vtype ) {
setVtype( vtype.getVType() );
}
/**
* A validation type name as defined in {@link VTypes} (defaults to null).
*
* @param vtype
* the validation type
*/
public void setVtype( String vtype ) {
setAttribute( "vtype", vtype, true );
}
/**
* The validation type text if the validation specified by
* {@link #setVtype(VTypes)} fails.
*
* @param vtypeText
* the vtype
*/
public void setVtypeText( String vtypeText ) {
setAttribute( "vtypeText", vtypeText, true );
}
/**
* This method return the position of the caret as a int array. Two values
* are provided: start of highlight and end of highlight. If there is
* nothing highlighted, the two values are the same.
*
* @return the position of the caret: start and end position
*/
public int[] getCaretPosition() {
int retval[] = new int[2];
JavaScriptObject jsob = getCaretPositionJs();
retval[0] = JsoHelper.getAttributeAsInt( jsob, "start" );
retval[1] = JsoHelper.getAttributeAsInt( jsob, "end" );
return retval;
}
private native JavaScriptObject getCaretPositionJs() /*-{
var field = this.@com.ait.toolkit.sencha.ext.client.core.Component::getOrCreateJsObj()();
var el = $doc.getElementById(field.getId());
var result = {
start : 0,
end : 0
};
// IE Support
if ($doc.selection) {
el.focus();
if (el.type === 'text') // textbox
{
var range = $doc.selection.createRange();
var r2 = range.duplicate();
result.start = 0 - r2.moveStart('character', -100000);
result.end = result.start + range.text.length;
} else // textarea
{
if (el.value.charCodeAt(el.value.length - 1) < 14) {
el.value = el.value.replace(/\034/g, '')
+ String.fromCharCode(28);
}
var oRng = $doc.selection.createRange();
var oRng2 = oRng.duplicate();
oRng2.moveToElementText(el);
oRng2.setEndPoint('StartToEnd', oRng);
result.end = el.value.length - oRng2.text.length;
oRng2.setEndPoint('StartToStart', oRng);
result.start = el.value.length - oRng2.text.length;
}
}
// Firefox support
else if (el.selectionStart || el.selectionStart == '0') {
result.start = el.selectionStart;
result.end = el.selectionEnd;
}
return result;
}-*/;
/**
* This method sets the caret position
*
* @param caretPos
* the caret position
*/
public native void setCaretPosition( int caretStart, int numToSelect ) /*-{
var field = this.@com.ait.toolkit.sencha.ext.client.core.Component::getOrCreateJsObj()();
var el = $doc.getElementById(field.getId());
if ($doc.selection) // IE
{
var range = $doc.selection.createRange();
if (el.type == 'text') // textbox
{
range.moveStart('character', -el.value.length);
range.moveEnd('character', -el.value.length);
range.moveStart('character', caretStart);
range.moveEnd('character', numToSelect);
range.select();
} else // textarea
{
var sel = el.createTextRange();
sel.collapse(true);
sel.moveStart("character", caretStart);
sel.moveEnd("character", numToSelect);
sel.select();
}
} else if (el.selectionStart || el.selectionStart == '0') // Firefox
{
el.setSelectionRange(caretStart, caretStart + numToSelect);
}
}-*/;
public native void insertAtCaret( String text ) /*-{
var field = this.@com.ait.toolkit.sencha.ext.client.core.Component::getOrCreateJsObj()();
var el = $doc.getElementById(field.getId());
if ($doc.selection) {
el.focus();
var orig = el.value.replace(/\r\n/g, "\n");
var range = $doc.selection.createRange();
if (range.parentElement() != el) {
return false;
}
range.text = text;
var actual, tmp;
actual = tmp = el.value.replace(/\r\n/g, "\n");
for (var diff = 0; diff < orig.length; diff++) {
if (orig.charAt(diff) != actual.charAt(diff))
break;
}
var macthTxt;
if ((text == "\\") || (text == "(") || (text == ")")
|| (text == "*") || (text == "?") || (text == "+")
|| (text == ".") || (text == "[") || (text == "]")) {
macthTxt = "\\" + text;
} else {
macthTxt = text;
}
for (var index = 0, start = 0; tmp.match(macthTxt)
&& (tmp = tmp.replace(macthTxt, "")) && index <= diff; index = start
+ text.length) {
start = actual.indexOf(text, index);
}
} else if (el.selectionStart) {
var start = el.selectionStart;
var end = el.selectionEnd;
el.value = el.value.substr(0, start) + text
+ el.value.substr(end, el.value.length);
}
if (start != null) {
var textend = start + text.length;
this.@com.ait.toolkit.sencha.ext.client.ui.TextField::setCaretPosition(II)(textend,textend);
} else {
el.value += text;
}
}-*/;
public native ExtElement getInputElement()/*-{
var component = this.@com.ait.toolkit.sencha.ext.client.core.Component::getOrCreateJsObj()();
var obj = component.inputEl;
if (!obj) {
return null;
}
return @com.ait.toolkit.sencha.shared.client.dom.ExtElement::new(Lcom/google/gwt/core/client/JavaScriptObject;)(obj);
}-*/;
/**
* Creates a new TextField from the given component
*
* @param component
* , the component to cast from
* @return, a new TextField from the component
*
*/
public static TextField cast( Component component ) {
return new TextField( component.getOrCreateJsObj() );
}
private native void _setValue( String value ) /*-{
var field = this.@com.ait.toolkit.sencha.ext.client.core.Component::getOrCreateJsObj()();
field.setValue(value);
}-*/;
private native String _getValue() /*-{
var cb = this.@com.ait.toolkit.sencha.ext.client.core.Component::getOrCreateJsObj()();
var val = cb.getValue();
if (!val)
return null;
return val === '' ? null : val.toString();
}-*/;
@Override
public HandlerRegistration addValueChangeHandler(
ValueChangeHandler<String> handler ) {
return null;
}
@Override
public LeafValueEditor<String> asEditor() {
if( editor == null ) {
editor = TakesValueEditor.of( this );
}
return editor;
}
@Override
public String getValue() {
return _getValue();
}
@Override
public void setValue( String value ) {
_setValue( value );
}
@Override
public void setValue( String value, boolean fireEvents ) {
_setValue( value );
}
/**
* Fires when the autoSize function is triggered and the field is resized
* according to the grow/growMin/growMax configs as a result. This event
* provides a hook for the developer to apply additional logic at runtime to
* resize the field if needed.
*
* @param handler
* , the handler that will handle the event
*/
public native HandlerRegistration addAutoSizeHandler(
com.ait.toolkit.sencha.ext.client.events.form.AutoSizeHandler handler )/*-{
var component = this.@com.ait.toolkit.sencha.ext.client.core.Component::getOrCreateJsObj()();
var fn = function(t, width, e) {
var tf = @com.ait.toolkit.sencha.ext.client.ui.TextField::new(Lcom/google/gwt/core/client/JavaScriptObject;)(t);
var event = @com.ait.toolkit.sencha.ext.client.events.form.AutoSizeEvent::new(Lcom/ait/toolkit/sencha/ext/client/ui/TextField;DLcom/google/gwt/core/client/JavaScriptObject;)(tf,width,e);
handler.@com.ait.toolkit.sencha.ext.client.events.form.AutoSizeHandler::onAutoSize(Lcom/ait/toolkit/sencha/ext/client/events/form/AutoSizeEvent;)(event);
};
var eventName = @com.ait.toolkit.sencha.ext.client.events.form.AutoSizeEvent::EVENT_NAME;
component.addListener(eventName, fn);
var toReturn = @com.ait.toolkit.sencha.ext.client.events.HandlerRegistration::new(Lcom/ait/toolkit/sencha/ext/client/core/Component;Ljava/lang/String;Lcom/google/gwt/core/client/JavaScriptObject;)(this,eventName,fn);
return toReturn;
}-*/;
/**
* Keyup input field event. This event only fires if enableKeyEvents is set
* to true.
*
* @param handler
* , the handler that will handle the event
*/
public native HandlerRegistration AddKeyUpHandler( KeyUpHandler handler )/*-{
var component = this.@com.ait.toolkit.sencha.ext.client.core.Component::getOrCreateJsObj()();
var fn = function(t, e) {
var tf = @com.ait.toolkit.sencha.ext.client.ui.TextField::new(Lcom/google/gwt/core/client/JavaScriptObject;)(t);
var event = @com.ait.toolkit.sencha.ext.client.events.form.KeyUpEvent::new(Lcom/ait/toolkit/sencha/ext/client/ui/TextField;Lcom/google/gwt/core/client/JavaScriptObject;)(tf,e);
handler.@com.ait.toolkit.sencha.ext.client.events.form.KeyUpHandler::onKeyUp(Lcom/ait/toolkit/sencha/ext/client/events/form/KeyUpEvent;)(event);
};
var eventName = @com.ait.toolkit.sencha.ext.client.events.form.KeyUpEvent::EVENT_NAME;
component.addListener(eventName, fn);
var toReturn = @com.ait.toolkit.sencha.ext.client.events.HandlerRegistration::new(Lcom/ait/toolkit/sencha/ext/client/core/Component;Ljava/lang/String;Lcom/google/gwt/core/client/JavaScriptObject;)(this,eventName,fn);
return toReturn;
}-*/;
/**
* Keypress input field event. This event only fires if enableKeyEvents is
* set to true.
*
* @param handler
* , the handler that will handle the event
*/
public native HandlerRegistration AddKeyPressHandler( KeyPressHandler handler )/*-{
var component = this.@com.ait.toolkit.sencha.ext.client.core.Component::getOrCreateJsObj()();
var fn = function(t, e) {
var tf = @com.ait.toolkit.sencha.ext.client.ui.TextField::new(Lcom/google/gwt/core/client/JavaScriptObject;)(t);
var event = @com.ait.toolkit.sencha.ext.client.events.form.KeyPressEvent::new(Lcom/ait/toolkit/sencha/ext/client/ui/TextField;Lcom/google/gwt/core/client/JavaScriptObject;)(tf,e);
handler.@com.ait.toolkit.sencha.ext.client.events.form.KeyPressHandler::onKeyPress(Lcom/ait/toolkit/sencha/ext/client/events/form/KeyPressEvent;)(event);
};
var eventName = @com.ait.toolkit.sencha.ext.client.events.form.KeyPressEvent::EVENT_NAME;
component.addListener(eventName, fn);
var toReturn = @com.ait.toolkit.sencha.ext.client.events.HandlerRegistration::new(Lcom/ait/toolkit/sencha/ext/client/core/Component;Ljava/lang/String;Lcom/google/gwt/core/client/JavaScriptObject;)(this,eventName,fn);
return toReturn;
}-*/;
/**
* Keydown input field event. This event only fires if enableKeyEvents is
* set to true.
*
* @param handler
* , the handler that will handle the event
*/
public native HandlerRegistration AddKeyDownHandler( KeyDownHandler handler )/*-{
var component = this.@com.ait.toolkit.sencha.ext.client.core.Component::getOrCreateJsObj()();
var fn = function(t, e) {
var tf = @com.ait.toolkit.sencha.ext.client.ui.TextField::new(Lcom/google/gwt/core/client/JavaScriptObject;)(t);
var event = @com.ait.toolkit.sencha.ext.client.events.form.KeyDownEvent::new(Lcom/ait/toolkit/sencha/ext/client/ui/TextField;Lcom/google/gwt/core/client/JavaScriptObject;)(tf,e);
handler.@com.ait.toolkit.sencha.ext.client.events.form.KeyDownHandler::onKeyDown(Lcom/ait/toolkit/sencha/ext/client/events/form/KeyDownEvent;)(event);
};
var eventName = @com.ait.toolkit.sencha.ext.client.events.form.KeyDownEvent::EVENT_NAME;
component.addListener(eventName, fn);
var toReturn = @com.ait.toolkit.sencha.ext.client.events.HandlerRegistration::new(Lcom/ait/toolkit/sencha/ext/client/core/Component;Ljava/lang/String;Lcom/google/gwt/core/client/JavaScriptObject;)(this,eventName,fn);
return toReturn;
}-*/;
}