X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/ee06f37b0f6f6d94cd05a6ffae556660f7c4a2bc..c930e9176a5a85509c5b0230e2bff5c22a591432:/pkgs/pkg-forms-debug.js?ds=sidebyside diff --git a/pkgs/pkg-forms-debug.js b/pkgs/pkg-forms-debug.js new file mode 100644 index 00000000..e3776075 --- /dev/null +++ b/pkgs/pkg-forms-debug.js @@ -0,0 +1,7661 @@ +/*! + * Ext JS Library 3.0.0 + * Copyright(c) 2006-2009 Ext JS, LLC + * licensing@extjs.com + * http://www.extjs.com/license + */ +/** + * @class Ext.form.Field + * @extends Ext.BoxComponent + * Base class for form fields that provides default event handling, sizing, value handling and other functionality. + * @constructor + * Creates a new Field + * @param {Object} config Configuration options + * @xtype field + */ +Ext.form.Field = Ext.extend(Ext.BoxComponent, { + /** + * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password, file (defaults + * to "text"). The types "file" and "password" must be used to render those field types currently -- there are + * no separate Ext components for those. Note that if you use inputType:'file', {@link #emptyText} + * is not supported and should be avoided. + */ + /** + * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, + * not those which are built via applyTo (defaults to undefined). + */ + /** + * @cfg {Mixed} value A value to initialize this field with (defaults to undefined). + */ + /** + * @cfg {String} name The field's HTML name attribute (defaults to ""). + * Note: this property must be set if this field is to be automatically included with + * {@link Ext.form.BasicForm#submit form submit()}. + */ + /** + * @cfg {String} cls A custom CSS class to apply to the field's underlying element (defaults to ""). + */ + + /** + * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid") + */ + invalidClass : "x-form-invalid", + /** + * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided + * (defaults to "The value in this field is invalid") + */ + invalidText : "The value in this field is invalid", + /** + * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus") + */ + focusClass : "x-form-focus", + /** + * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable + automatic validation (defaults to "keyup"). + */ + validationEvent : "keyup", + /** + * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true). + */ + validateOnBlur : true, + /** + * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation + * is initiated (defaults to 250) + */ + validationDelay : 250, + /** + * @cfg {String/Object} autoCreate
A {@link Ext.DomHelper DomHelper} element spec, or true for a default + * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component. + * See {@link Ext.Component#autoEl autoEl} for details. Defaults to:
+ *{tag: "input", type: "text", size: "20", autocomplete: "off"}
+ */
+ defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
+ /**
+ * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
+ */
+ fieldClass : "x-form-field",
+ /**
+ * @cfg {String} msgTarget The location where error text should display. Should be one of the following values
+ * (defaults to 'qtip'):
+ *+Value Description +----------- ---------------------------------------------------------------------- +qtip Display a quick tip when the user hovers over the field +title Display a default browser title attribute popup +under Add a block div beneath the field containing the error text +side Add an error icon to the right of the field with a popup on hover +[element id] Add the error text directly to the innerHTML of the specified element ++ */ + msgTarget : 'qtip', + /** + * @cfg {String} msgFx Experimental The effect used when displaying a validation message under the field + * (defaults to 'normal'). + */ + msgFx : 'normal', + /** + * @cfg {Boolean} readOnly true to mark the field as readOnly in HTML + * (defaults to false). + *
Note: this only sets the element's readOnly DOM attribute.
+ * Setting readOnly=true
, for example, will not disable triggering a
+ * ComboBox or DateField; it gives you the option of forcing the user to choose
+ * via the trigger without typing in the text box. To hide the trigger use
+ * {@link Ext.form.TriggerField#hideTrigger hideTrigger}
.
Be aware that conformant with the HTML specification, + * disabled Fields will not be {@link Ext.form.BasicForm#submit submitted}.
+ */ + disabled : false, + + // private + isFormField : true, + + // private + hasFocus : false, + + // private + initComponent : function(){ + Ext.form.Field.superclass.initComponent.call(this); + this.addEvents( + /** + * @event focus + * Fires when this field receives input focus. + * @param {Ext.form.Field} this + */ + 'focus', + /** + * @event blur + * Fires when this field loses input focus. + * @param {Ext.form.Field} this + */ + 'blur', + /** + * @event specialkey + * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. + * To handle other keys see {@link Ext.Panel#keys} or {@link Ext.KeyMap}. + * You can check {@link Ext.EventObject#getKey} to determine which key was pressed. + * For example:
+var form = new Ext.form.FormPanel({
+ ...
+ items: [{
+ fieldLabel: 'Field 1',
+ name: 'field1',
+ allowBlank: false
+ },{
+ fieldLabel: 'Field 2',
+ name: 'field2',
+ listeners: {
+ specialkey: function(field, e){
+ // e.HOME, e.END, e.PAGE_UP, e.PAGE_DOWN,
+ // e.TAB, e.ESC, arrow keys: e.LEFT, e.RIGHT, e.UP, e.DOWN
+ if (e.{@link Ext.EventObject#getKey getKey()} == e.ENTER) {
+ var form = field.ownerCt.getForm();
+ form.submit();
+ }
+ }
+ }
+ }
+ ],
+ ...
+});
+ *
+ * @param {Ext.form.Field} this
+ * @param {Ext.EventObject} e The event object
+ */
+ 'specialkey',
+ /**
+ * @event change
+ * Fires just before the field blurs if the field value has changed.
+ * @param {Ext.form.Field} this
+ * @param {Mixed} newValue The new value
+ * @param {Mixed} oldValue The original value
+ */
+ 'change',
+ /**
+ * @event invalid
+ * Fires after the field has been marked as invalid.
+ * @param {Ext.form.Field} this
+ * @param {String} msg The validation message
+ */
+ 'invalid',
+ /**
+ * @event valid
+ * Fires after the field has been validated with no errors.
+ * @param {Ext.form.Field} this
+ */
+ 'valid'
+ );
+ },
+
+ /**
+ * Returns the {@link Ext.form.Field#name name} or {@link Ext.form.ComboBox#hiddenName hiddenName}
+ * attribute of the field if available.
+ * @return {String} name The field {@link Ext.form.Field#name name} or {@link Ext.form.ComboBox#hiddenName hiddenName}
+ */
+ getName: function(){
+ return this.rendered && this.el.dom.name ? this.el.dom.name : this.name || this.id || '';
+ },
+
+ // private
+ onRender : function(ct, position){
+ if(!this.el){
+ var cfg = this.getAutoCreate();
+
+ if(!cfg.name){
+ cfg.name = this.name || this.id;
+ }
+ if(this.inputType){
+ cfg.type = this.inputType;
+ }
+ this.autoEl = cfg;
+ }
+ Ext.form.Field.superclass.onRender.call(this, ct, position);
+
+ var type = this.el.dom.type;
+ if(type){
+ if(type == 'password'){
+ type = 'text';
+ }
+ this.el.addClass('x-form-'+type);
+ }
+ if(this.readOnly){
+ this.el.dom.readOnly = true;
+ }
+ if(this.tabIndex !== undefined){
+ this.el.dom.setAttribute('tabIndex', this.tabIndex);
+ }
+
+ this.el.addClass([this.fieldClass, this.cls]);
+ },
+
+ // private
+ getItemCt : function(){
+ return this.el.up('.x-form-item', 4);
+ },
+
+ // private
+ initValue : function(){
+ if(this.value !== undefined){
+ this.setValue(this.value);
+ }else if(!Ext.isEmpty(this.el.dom.value) && this.el.dom.value != this.emptyText){
+ this.setValue(this.el.dom.value);
+ }
+ /**
+ * The original value of the field as configured in the {@link #value} configuration, or
+ * as loaded by the last form load operation if the form's {@link Ext.form.BasicForm#trackResetOnLoad trackResetOnLoad}
+ * setting is true
.
+ * @type mixed
+ * @property originalValue
+ */
+ this.originalValue = this.getValue();
+ },
+
+ /**
+ * Returns true if the value of this Field has been changed from its original value. + * Will return false if the field is disabled or has not been rendered yet.
+ *Note that if the owning {@link Ext.form.BasicForm form} was configured with + * {@link Ext.form.BasicForm}.{@link Ext.form.BasicForm#trackResetOnLoad trackResetOnLoad} + * then the original value is updated when the values are loaded by + * {@link Ext.form.BasicForm}.{@link Ext.form.BasicForm#setValues setValues}.
+ * @return {Boolean} True if this field has been changed from its original value (and + * is not disabled), false otherwise. + */ + isDirty : function() { + if(this.disabled || !this.rendered) { + return false; + } + return String(this.getValue()) !== String(this.originalValue); + }, + + // private + afterRender : function(){ + Ext.form.Field.superclass.afterRender.call(this); + this.initEvents(); + this.initValue(); + }, + + // private + fireKey : function(e){ + if(e.isSpecialKey()){ + this.fireEvent("specialkey", this, e); + } + }, + + /** + * Resets the current field value to the originally loaded value and clears any validation messages. + * See {@link Ext.form.BasicForm}.{@link Ext.form.BasicForm#trackResetOnLoad trackResetOnLoad} + */ + reset : function(){ + this.setValue(this.originalValue); + this.clearInvalid(); + }, + + // private + initEvents : function(){ + this.mon(this.el, Ext.EventManager.useKeydown ? "keydown" : "keypress", this.fireKey, this); + this.mon(this.el, 'focus', this.onFocus, this); + + // fix weird FF/Win editor issue when changing OS window focus + var o = this.inEditor && Ext.isWindows && Ext.isGecko ? {buffer:10} : null; + this.mon(this.el, 'blur', this.onBlur, this, o); + }, + + // private + onFocus : function(){ + if(this.focusClass){ + this.el.addClass(this.focusClass); + } + if(!this.hasFocus){ + this.hasFocus = true; + this.startValue = this.getValue(); + this.fireEvent("focus", this); + } + }, + + // private + beforeBlur : Ext.emptyFn, + + // private + onBlur : function(){ + this.beforeBlur(); + if(this.focusClass){ + this.el.removeClass(this.focusClass); + } + this.hasFocus = false; + if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){ + this.validate(); + } + var v = this.getValue(); + if(String(v) !== String(this.startValue)){ + this.fireEvent('change', this, v, this.startValue); + } + this.fireEvent("blur", this); + }, + + /** + * Returns whether or not the field value is currently valid + * @param {Boolean} preventMark True to disable marking the field invalid + * @return {Boolean} True if the value is valid, else false + */ + isValid : function(preventMark){ + if(this.disabled){ + return true; + } + var restore = this.preventMark; + this.preventMark = preventMark === true; + var v = this.validateValue(this.processValue(this.getRawValue())); + this.preventMark = restore; + return v; + }, + + /** + * Validates the field value + * @return {Boolean} True if the value is valid, else false + */ + validate : function(){ + if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){ + this.clearInvalid(); + return true; + } + return false; + }, + + // protected - should be overridden by subclasses if necessary to prepare raw values for validation + processValue : function(value){ + return value; + }, + + // private + // Subclasses should provide the validation implementation by overriding this + validateValue : function(value){ + return true; + }, + + /** + * Mark this field as invalid, using {@link #msgTarget} to determine how to display the error and + * applying {@link #invalidClass} to the field's element. + * @param {String} msg (optional) The validation message (defaults to {@link #invalidText}) + */ + markInvalid : function(msg){ + if(!this.rendered || this.preventMark){ // not rendered + return; + } + msg = msg || this.invalidText; + + var mt = this.getMessageHandler(); + if(mt){ + mt.mark(this, msg); + }else if(this.msgTarget){ + this.el.addClass(this.invalidClass); + var t = Ext.getDom(this.msgTarget); + if(t){ + t.innerHTML = msg; + t.style.display = this.msgDisplay; + } + } + this.fireEvent('invalid', this, msg); + }, + + /** + * Clear any invalid styles/messages for this field + */ + clearInvalid : function(){ + if(!this.rendered || this.preventMark){ // not rendered + return; + } + this.el.removeClass(this.invalidClass); + var mt = this.getMessageHandler(); + if(mt){ + mt.clear(this); + }else if(this.msgTarget){ + this.el.removeClass(this.invalidClass); + var t = Ext.getDom(this.msgTarget); + if(t){ + t.innerHTML = ''; + t.style.display = 'none'; + } + } + this.fireEvent('valid', this); + }, + + // private + getMessageHandler : function(){ + return Ext.form.MessageTargets[this.msgTarget]; + }, + + // private + getErrorCt : function(){ + return this.el.findParent('.x-form-element', 5, true) || // use form element wrap if available + this.el.findParent('.x-form-field-wrap', 5, true); // else direct field wrap + }, + + // private + alignErrorIcon : function(){ + this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]); + }, + + /** + * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}. + * @return {Mixed} value The field value + */ + getRawValue : function(){ + var v = this.rendered ? this.el.getValue() : Ext.value(this.value, ''); + if(v === this.emptyText){ + v = ''; + } + return v; + }, + + /** + * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}. + * @return {Mixed} value The field value + */ + getValue : function(){ + if(!this.rendered) { + return this.value; + } + var v = this.el.getValue(); + if(v === this.emptyText || v === undefined){ + v = ''; + } + return v; + }, + + /** + * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}. + * @param {Mixed} value The value to set + * @return {Mixed} value The field value that is set + */ + setRawValue : function(v){ + return (this.el.dom.value = (Ext.isEmpty(v) ? '' : v)); + }, + + /** + * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}. + * @param {Mixed} value The value to set + * @return {Ext.form.Field} this + */ + setValue : function(v){ + this.value = v; + if(this.rendered){ + this.el.dom.value = (Ext.isEmpty(v) ? '' : v); + this.validate(); + } + return this; + }, + + // private, does not work for all fields + append : function(v){ + this.setValue([this.getValue(), v].join('')); + }, + + // private + adjustSize : function(w, h){ + var s = Ext.form.Field.superclass.adjustSize.call(this, w, h); + s.width = this.adjustWidth(this.el.dom.tagName, s.width); + if(this.offsetCt){ + var ct = this.getItemCt(); + s.width -= ct.getFrameWidth('lr'); + s.height -= ct.getFrameWidth('tb'); + } + return s; + }, + + // private + adjustWidth : function(tag, w){ + if(typeof w == 'number' && (Ext.isIE && (Ext.isIE6 || !Ext.isStrict)) && /input|textarea/i.test(tag) && !this.inEditor){ + return w - 3; + } + return w; + } + + /** + * @cfg {Boolean} autoWidth @hide + */ + /** + * @cfg {Boolean} autoHeight @hide + */ + + /** + * @cfg {String} autoEl @hide + */ +}); + + +Ext.form.MessageTargets = { + 'qtip' : { + mark: function(field, msg){ + field.el.addClass(field.invalidClass); + field.el.dom.qtip = msg; + field.el.dom.qclass = 'x-form-invalid-tip'; + if(Ext.QuickTips){ // fix for floating editors interacting with DND + Ext.QuickTips.enable(); + } + }, + clear: function(field){ + field.el.removeClass(field.invalidClass); + field.el.dom.qtip = ''; + } + }, + 'title' : { + mark: function(field, msg){ + field.el.addClass(field.invalidClass); + field.el.dom.title = msg; + }, + clear: function(field){ + field.el.dom.title = ''; + } + }, + 'under' : { + mark: function(field, msg){ + field.el.addClass(field.invalidClass); + if(!field.errorEl){ + var elp = field.getErrorCt(); + if(!elp){ // field has no container el + field.el.dom.title = msg; + return; + } + field.errorEl = elp.createChild({cls:'x-form-invalid-msg'}); + field.errorEl.setWidth(elp.getWidth(true)-20); + } + field.errorEl.update(msg); + Ext.form.Field.msgFx[field.msgFx].show(field.errorEl, field); + }, + clear: function(field){ + field.el.removeClass(field.invalidClass); + if(field.errorEl){ + Ext.form.Field.msgFx[field.msgFx].hide(field.errorEl, field); + }else{ + field.el.dom.title = ''; + } + } + }, + 'side' : { + mark: function(field, msg){ + field.el.addClass(field.invalidClass); + if(!field.errorIcon){ + var elp = field.getErrorCt(); + if(!elp){ // field has no container el + field.el.dom.title = msg; + return; + } + field.errorIcon = elp.createChild({cls:'x-form-invalid-icon'}); + } + field.alignErrorIcon(); + field.errorIcon.dom.qtip = msg; + field.errorIcon.dom.qclass = 'x-form-invalid-tip'; + field.errorIcon.show(); + field.on('resize', field.alignErrorIcon, field); + }, + clear: function(field){ + field.el.removeClass(field.invalidClass); + if(field.errorIcon){ + field.errorIcon.dom.qtip = ''; + field.errorIcon.hide(); + field.un('resize', field.alignErrorIcon, field); + }else{ + field.el.dom.title = ''; + } + } + } +}; + +// anything other than normal should be considered experimental +Ext.form.Field.msgFx = { + normal : { + show: function(msgEl, f){ + msgEl.setDisplayed('block'); + }, + + hide : function(msgEl, f){ + msgEl.setDisplayed(false).update(''); + } + }, + + slide : { + show: function(msgEl, f){ + msgEl.slideIn('t', {stopFx:true}); + }, + + hide : function(msgEl, f){ + msgEl.slideOut('t', {stopFx:true,useDisplay:true}); + } + }, + + slideRight : { + show: function(msgEl, f){ + msgEl.fixDisplay(); + msgEl.alignTo(f.el, 'tl-tr'); + msgEl.slideIn('l', {stopFx:true}); + }, + + hide : function(msgEl, f){ + msgEl.slideOut('l', {stopFx:true,useDisplay:true}); + } + } +}; +Ext.reg('field', Ext.form.Field); +/** + * @class Ext.form.TextField + * @extends Ext.form.Field + *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 {@link Ext.form.TextArea} + * and {@link Ext.form.ComboBox}).
+ *Validation
+ *Field validation is processed in a particular order. If validation fails at any particular + * step the validation routine halts.
+ *If a field is configured with a {@link Ext.form.TextField#validator validator}
function,
+ * it will be passed the current field value. The {@link Ext.form.TextField#validator validator}
+ * function is expected to return boolean true if the value is valid or return a string to
+ * represent the invalid message if invalid.
Basic validation is affected with the following configuration properties:
+ *+ * Validation Invalid Message + *+ *{@link Ext.form.TextField#allowBlank allowBlank} {@link Ext.form.TextField#emptyText emptyText}
+ *{@link Ext.form.TextField#minLength minLength} {@link Ext.form.TextField#minLengthText minLengthText}
+ *{@link Ext.form.TextField#maxLength maxLength} {@link Ext.form.TextField#maxLengthText maxLengthText}
+ *
Using VTypes offers a convenient way to reuse validation. If a field is configured with a
+ * {@link Ext.form.TextField#vtype vtype}
, the corresponding {@link Ext.form.VTypes VTypes}
+ * validation function will be used for validation. If invalid, either the field's
+ * {@link Ext.form.TextField#vtypeText vtypeText}
or the VTypes vtype Text property will be
+ * used for the invalid message. Keystrokes on the field will be filtered according to the VTypes
+ * vtype Mask property.
Each field may also specify a {@link Ext.form.TextField#regex regex}
test.
+ * The invalid message for this test is configured with
+ * {@link Ext.form.TextField#regexText regexText}
.
Validation behavior for each field can be configured:
{@link Ext.form.TextField#invalidText invalidText}
: the default validation message to
+ * show if any validation step above does not provide a message when invalid{@link Ext.form.TextField#maskRe maskRe}
: filter out keystrokes before any validation occurs{@link Ext.form.TextField#stripCharsRe stripCharsRe}
: filter characters after being typed in,
+ * but before being validated{@link Ext.form.Field#invalidClass invalidClass}
: alternate style when invalid{@link Ext.form.Field#validateOnBlur validateOnBlur}
,
+ * {@link Ext.form.Field#validationDelay validationDelay}
, and
+ * {@link Ext.form.Field#validationEvent validationEvent}
: modify how/when validation is triggered{@link #vtype}
currently set for this field (defaults to ''). Note:
+ * only applies if {@link #vtype}
is set, else ignored.
+ */
+ /**
+ * @cfg {RegExp} stripCharsRe A JavaScript RegExp object used to strip unwanted content from the value
+ * before validation (defaults to null).
+ */
+ /**
+ * @cfg {Boolean} grow true if this field should automatically grow and shrink to its content
+ * (defaults to false)
+ */
+ grow : false,
+ /**
+ * @cfg {Number} growMin The minimum width to allow when {@link #grow} = true
(defaults
+ * to 30)
+ */
+ growMin : 30,
+ /**
+ * @cfg {Number} growMax The maximum width to allow when {@link #grow} = true
(defaults
+ * to 800)
+ */
+ growMax : 800,
+ /**
+ * @cfg {String} vtype A validation type name as defined in {@link Ext.form.VTypes} (defaults to null)
+ */
+ vtype : null,
+ /**
+ * @cfg {RegExp} maskRe An input mask regular expression that will be used to filter keystrokes that do
+ * not match (defaults to null)
+ */
+ maskRe : null,
+ /**
+ * @cfg {Boolean} disableKeyFilter Specify true to disable input keystroke filtering (defaults
+ * to false)
+ */
+ disableKeyFilter : false,
+ /**
+ * @cfg {Boolean} allowBlank Specify false to validate that the value's length is > 0 (defaults to
+ * true)
+ */
+ allowBlank : true,
+ /**
+ * @cfg {Number} minLength Minimum input field length required (defaults to 0)
+ */
+ minLength : 0,
+ /**
+ * @cfg {Number} maxLength Maximum input field length allowed by validation (defaults to Number.MAX_VALUE).
+ * This behavior is intended to provide instant feedback to the user by improving usability to allow pasting
+ * and editing or overtyping and back tracking. To restrict the maximum number of characters that can be
+ * entered into the field use {@link Ext.form.Field#autoCreate autoCreate} to add
+ * any attributes you want to a field, for example:
+var myField = new Ext.form.NumberField({
+ id: 'mobile',
+ anchor:'90%',
+ fieldLabel: 'Mobile',
+ maxLength: 16, // for validation
+ autoCreate: {tag: 'input', type: 'text', size: '20', autocomplete: 'off', maxlength: '10'}
+});
+
+ */
+ maxLength : Number.MAX_VALUE,
+ /**
+ * @cfg {String} minLengthText Error text to display if the {@link #minLength minimum length}
+ * validation fails (defaults to 'The minimum length for this field is {minLength}')
+ */
+ minLengthText : 'The minimum length for this field is {0}',
+ /**
+ * @cfg {String} maxLengthText Error text to display if the {@link #maxLength maximum length}
+ * validation fails (defaults to 'The maximum length for this field is {maxLength}')
+ */
+ maxLengthText : 'The maximum length for this field is {0}',
+ /**
+ * @cfg {Boolean} selectOnFocus true to automatically select any existing field text when the field
+ * receives input focus (defaults to false)
+ */
+ selectOnFocus : false,
+ /**
+ * @cfg {String} blankText The error text to display if the {@link #allowBlank} validation
+ * fails (defaults to 'This field is required')
+ */
+ blankText : 'This field is required',
+ /**
+ * @cfg {Function} validator A custom validation function to be called during field validation
+ * (defaults to null). If specified, this function will be called first, allowing the
+ * developer to override the default validation process. This function will be passed the current
+ * field value and expected to return boolean true if the value is valid or a string
+ * error message if invalid.
+ */
+ validator : null,
+ /**
+ * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation
+ * (defaults to null). If the test fails, the field will be marked invalid using
+ * {@link #regexText}.
+ */
+ regex : null,
+ /**
+ * @cfg {String} regexText The error text to display if {@link #regex} is used and the
+ * test fails during validation (defaults to '')
+ */
+ regexText : '',
+ /**
+ * @cfg {String} emptyText The default text to place into an empty field (defaults to null).
+ * Note: that this value will be submitted to the server if this field is enabled and configured
+ * with a {@link #name}.
+ */
+ emptyText : null,
+ /**
+ * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText}
+ * (defaults to 'x-form-empty-field'). This class is automatically added and removed as needed
+ * depending on the current field value.
+ */
+ emptyClass : 'x-form-empty-field',
+
+ /**
+ * @cfg {Boolean} enableKeyEvents true to enable the proxying of key events for the HTML input
+ * field (defaults to false)
+ */
+
+ initComponent : function(){
+ Ext.form.TextField.superclass.initComponent.call(this);
+ this.addEvents(
+ /**
+ * @event autosize
+ * Fires when the {@link #autoSize} function is triggered. The field may or
+ * may not have actually changed size according to the default logic, but this event provides
+ * a hook for the developer to apply additional logic at runtime to resize the field if needed.
+ * @param {Ext.form.Field} this This text field
+ * @param {Number} width The new field width
+ */
+ 'autosize',
+
+ /**
+ * @event keydown
+ * Keydown input field event. This event only fires if {@link #enableKeyEvents}
+ * is set to true.
+ * @param {Ext.form.TextField} this This text field
+ * @param {Ext.EventObject} e
+ */
+ 'keydown',
+ /**
+ * @event keyup
+ * Keyup input field event. This event only fires if {@link #enableKeyEvents}
+ * is set to true.
+ * @param {Ext.form.TextField} this This text field
+ * @param {Ext.EventObject} e
+ */
+ 'keyup',
+ /**
+ * @event keypress
+ * Keypress input field event. This event only fires if {@link #enableKeyEvents}
+ * is set to true.
+ * @param {Ext.form.TextField} this This text field
+ * @param {Ext.EventObject} e
+ */
+ 'keypress'
+ );
+ },
+
+ // private
+ initEvents : function(){
+ Ext.form.TextField.superclass.initEvents.call(this);
+ if(this.validationEvent == 'keyup'){
+ this.validationTask = new Ext.util.DelayedTask(this.validate, this);
+ this.mon(this.el, 'keyup', this.filterValidation, this);
+ }
+ else if(this.validationEvent !== false){
+ this.mon(this.el, this.validationEvent, this.validate, this, {buffer: this.validationDelay});
+ }
+ if(this.selectOnFocus || this.emptyText){
+ this.on('focus', this.preFocus, this);
+
+ this.mon(this.el, 'mousedown', function(){
+ if(!this.hasFocus){
+ this.el.on('mouseup', function(e){
+ e.preventDefault();
+ }, this, {single:true});
+ }
+ }, this);
+
+ if(this.emptyText){
+ this.on('blur', this.postBlur, this);
+ this.applyEmptyText();
+ }
+ }
+ if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Ext.form.VTypes[this.vtype+'Mask']))){
+ this.mon(this.el, 'keypress', this.filterKeys, this);
+ }
+ if(this.grow){
+ this.mon(this.el, 'keyup', this.onKeyUpBuffered, this, {buffer: 50});
+ this.mon(this.el, 'click', this.autoSize, this);
+ }
+ if(this.enableKeyEvents){
+ this.mon(this.el, 'keyup', this.onKeyUp, this);
+ this.mon(this.el, 'keydown', this.onKeyDown, this);
+ this.mon(this.el, 'keypress', this.onKeyPress, this);
+ }
+ },
+
+ processValue : function(value){
+ if(this.stripCharsRe){
+ var newValue = value.replace(this.stripCharsRe, '');
+ if(newValue !== value){
+ this.setRawValue(newValue);
+ return newValue;
+ }
+ }
+ return value;
+ },
+
+ filterValidation : function(e){
+ if(!e.isNavKeyPress()){
+ this.validationTask.delay(this.validationDelay);
+ }
+ },
+
+ //private
+ onDisable: function(){
+ Ext.form.TextField.superclass.onDisable.call(this);
+ if(Ext.isIE){
+ this.el.dom.unselectable = 'on';
+ }
+ },
+
+ //private
+ onEnable: function(){
+ Ext.form.TextField.superclass.onEnable.call(this);
+ if(Ext.isIE){
+ this.el.dom.unselectable = '';
+ }
+ },
+
+ // private
+ onKeyUpBuffered : function(e){
+ if(!e.isNavKeyPress()){
+ this.autoSize();
+ }
+ },
+
+ // private
+ onKeyUp : function(e){
+ this.fireEvent('keyup', this, e);
+ },
+
+ // private
+ onKeyDown : function(e){
+ this.fireEvent('keydown', this, e);
+ },
+
+ // private
+ onKeyPress : function(e){
+ this.fireEvent('keypress', this, e);
+ },
+
+ /**
+ * Resets the current field value to the originally-loaded value and clears any validation messages.
+ * Also adds {@link #emptyText} and {@link #emptyClass} if the
+ * original value was blank.
+ */
+ reset : function(){
+ Ext.form.TextField.superclass.reset.call(this);
+ this.applyEmptyText();
+ },
+
+ applyEmptyText : function(){
+ if(this.rendered && this.emptyText && this.getRawValue().length < 1 && !this.hasFocus){
+ this.setRawValue(this.emptyText);
+ this.el.addClass(this.emptyClass);
+ }
+ },
+
+ // private
+ preFocus : function(){
+ var el = this.el;
+ if(this.emptyText){
+ if(el.dom.value == this.emptyText){
+ this.setRawValue('');
+ }
+ el.removeClass(this.emptyClass);
+ }
+ if(this.selectOnFocus){
+ (function(){
+ el.dom.select();
+ }).defer(this.inEditor && Ext.isIE ? 50 : 0);
+ }
+ },
+
+ // private
+ postBlur : function(){
+ this.applyEmptyText();
+ },
+
+ // private
+ filterKeys : function(e){
+ // special keys don't generate charCodes, so leave them alone
+ if(e.ctrlKey || e.isSpecialKey()){
+ return;
+ }
+
+ if(!this.maskRe.test(String.fromCharCode(e.getCharCode()))){
+ e.stopEvent();
+ }
+ },
+
+ setValue : function(v){
+ if(this.emptyText && this.el && !Ext.isEmpty(v)){
+ this.el.removeClass(this.emptyClass);
+ }
+ Ext.form.TextField.superclass.setValue.apply(this, arguments);
+ this.applyEmptyText();
+ this.autoSize();
+ return this;
+ },
+
+ /**
+ * Validates a value according to the field's validation rules and marks the field as invalid
+ * if the validation fails
+ * @param {Mixed} value The value to validate
+ * @return {Boolean} True if the value is valid, else false
+ */
+ validateValue : function(value){
+ if(Ext.isFunction(this.validator)){
+ var msg = this.validator(value);
+ if(msg !== true){
+ this.markInvalid(msg);
+ return false;
+ }
+ }
+ if(value.length < 1 || value === this.emptyText){ // if it's blank
+ if(this.allowBlank){
+ this.clearInvalid();
+ return true;
+ }else{
+ this.markInvalid(this.blankText);
+ return false;
+ }
+ }
+ if(value.length < this.minLength){
+ this.markInvalid(String.format(this.minLengthText, this.minLength));
+ return false;
+ }
+ if(value.length > this.maxLength){
+ this.markInvalid(String.format(this.maxLengthText, this.maxLength));
+ return false;
+ }
+ if(this.vtype){
+ var vt = Ext.form.VTypes;
+ if(!vt[this.vtype](value, this)){
+ this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
+ return false;
+ }
+ }
+ if(this.regex && !this.regex.test(value)){
+ this.markInvalid(this.regexText);
+ return false;
+ }
+ return true;
+ },
+
+ /**
+ * Selects text in this field
+ * @param {Number} start (optional) The index where the selection should start (defaults to 0)
+ * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
+ */
+ selectText : function(start, end){
+ var v = this.getRawValue();
+ var doFocus = false;
+ if(v.length > 0){
+ start = start === undefined ? 0 : start;
+ end = end === undefined ? v.length : end;
+ var d = this.el.dom;
+ if(d.setSelectionRange){
+ d.setSelectionRange(start, end);
+ }else if(d.createTextRange){
+ var range = d.createTextRange();
+ range.moveStart('character', start);
+ range.moveEnd('character', end-v.length);
+ range.select();
+ }
+ doFocus = Ext.isGecko || Ext.isOpera;
+ }else{
+ doFocus = true;
+ }
+ if(doFocus){
+ this.focus();
+ }
+ },
+
+ /**
+ * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
+ * This only takes effect if {@link #grow} = true, and fires the {@link #autosize} event.
+ */
+ autoSize : function(){
+ if(!this.grow || !this.rendered){
+ return;
+ }
+ if(!this.metrics){
+ this.metrics = Ext.util.TextMetrics.createInstance(this.el);
+ }
+ var el = this.el;
+ var v = el.dom.value;
+ var d = document.createElement('div');
+ d.appendChild(document.createTextNode(v));
+ v = d.innerHTML;
+ d = null;
+ Ext.removeNode(d);
+ v += ' ';
+ var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
+ this.el.setWidth(w);
+ this.fireEvent('autosize', this, w);
+ },
+
+ onDestroy: function(){
+ if(this.validationTask){
+ this.validationTask.cancel();
+ this.validationTask = null;
+ }
+ Ext.form.TextField.superclass.onDestroy.call(this);
+ }
+});
+Ext.reg('textfield', Ext.form.TextField);
+/**
+ * @class Ext.form.TriggerField
+ * @extends Ext.form.TextField
+ * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
+ * The trigger has no default action, so you must assign a function to implement the trigger click handler by
+ * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
+ * for which you can provide a custom implementation. For example:
+ *
+var trigger = new Ext.form.TriggerField();
+trigger.onTriggerClick = myTriggerFn;
+trigger.applyToMarkup('my-field');
+
+ *
+ * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
+ * {@link Ext.form.DateField} and {@link Ext.form.ComboBox} are perfect examples of this.
+ *
+ * @constructor
+ * Create a new TriggerField.
+ * @param {Object} config Configuration options (valid {@Ext.form.TextField} config options will also be applied
+ * to the base TextField)
+ * @xtype trigger
+ */
+Ext.form.TriggerField = Ext.extend(Ext.form.TextField, {
+ /**
+ * @cfg {String} triggerClass
+ * An additional CSS class used to style the trigger button. The trigger will always get the
+ * class 'x-form-trigger' by default and triggerClass will be appended if specified.
+ */
+ /**
+ * @cfg {Mixed} triggerConfig
+ * A {@link Ext.DomHelper DomHelper} config object specifying the structure of the + * trigger element for this Field. (Optional).
+ *Specify this when you need a customized element to act as the trigger button for a TriggerField.
+ *Note that when using this option, it is the developer's responsibility to ensure correct sizing, positioning + * and appearance of the trigger. Defaults to:
+ *{tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass}
+ */
+ /**
+ * @cfg {String/Object} autoCreate A {@link Ext.DomHelper DomHelper} element spec, or true for a default + * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component. + * See {@link Ext.Component#autoEl autoEl} for details. Defaults to:
+ *{tag: "input", type: "text", size: "16", autocomplete: "off"}
+ */
+ defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
+ /**
+ * @cfg {Boolean} hideTrigger true to hide the trigger element and display only the base
+ * text field (defaults to false)
+ */
+ hideTrigger:false,
+ /**
+ * @cfg {Boolean} editable false to prevent the user from typing text directly into the field,
+ * the field will only respond to a click on the trigger to set the value. (defaults to true)
+ */
+ editable: true,
+ /**
+ * @cfg {String} wrapFocusClass The class added to the to the wrap of the trigger element. Defaults to
+ * x-trigger-wrap-focus.
+ */
+ wrapFocusClass: 'x-trigger-wrap-focus',
+ /**
+ * @hide
+ * @method autoSize
+ */
+ autoSize: Ext.emptyFn,
+ // private
+ monitorTab : true,
+ // private
+ deferHeight : true,
+ // private
+ mimicing : false,
+
+ actionMode: 'wrap',
+
+ // private
+ onResize : function(w, h){
+ Ext.form.TriggerField.superclass.onResize.call(this, w, h);
+ if(typeof w == 'number'){
+ this.el.setWidth(this.adjustWidth('input', w - this.trigger.getWidth()));
+ }
+ this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
+ },
+
+ // private
+ adjustSize : Ext.BoxComponent.prototype.adjustSize,
+
+ // private
+ getResizeEl : function(){
+ return this.wrap;
+ },
+
+ // private
+ getPositionEl : function(){
+ return this.wrap;
+ },
+
+ // private
+ alignErrorIcon : function(){
+ if(this.wrap){
+ this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
+ }
+ },
+
+ // private
+ onRender : function(ct, position){
+ Ext.form.TriggerField.superclass.onRender.call(this, ct, position);
+
+ this.wrap = this.el.wrap({cls: 'x-form-field-wrap x-form-field-trigger-wrap'});
+ this.trigger = this.wrap.createChild(this.triggerConfig ||
+ {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
+ if(this.hideTrigger){
+ this.trigger.setDisplayed(false);
+ }
+ this.initTrigger();
+ if(!this.width){
+ this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
+ }
+ if(!this.editable){
+ this.editable = true;
+ this.setEditable(false);
+ }
+ },
+
+ afterRender : function(){
+ Ext.form.TriggerField.superclass.afterRender.call(this);
+ },
+
+ // private
+ initTrigger : function(){
+ this.mon(this.trigger, 'click', this.onTriggerClick, this, {preventDefault:true});
+ this.trigger.addClassOnOver('x-form-trigger-over');
+ this.trigger.addClassOnClick('x-form-trigger-click');
+ },
+
+ // private
+ onDestroy : function(){
+ Ext.destroy(this.trigger, this.wrap);
+ if (this.mimicing){
+ Ext.get(Ext.isIE ? document.body : document).un("mousedown", this.mimicBlur, this);
+ }
+ Ext.form.TriggerField.superclass.onDestroy.call(this);
+ },
+
+ // private
+ onFocus : function(){
+ Ext.form.TriggerField.superclass.onFocus.call(this);
+ if(!this.mimicing){
+ this.wrap.addClass(this.wrapFocusClass);
+ this.mimicing = true;
+ Ext.get(Ext.isIE ? document.body : document).on("mousedown", this.mimicBlur, this, {delay: 10});
+ if(this.monitorTab){
+ this.el.on('keydown', this.checkTab, this);
+ }
+ }
+ },
+
+ // private
+ checkTab : function(e){
+ if(e.getKey() == e.TAB){
+ this.triggerBlur();
+ }
+ },
+
+ // private
+ onBlur : function(){
+ // do nothing
+ },
+
+ // private
+ mimicBlur : function(e){
+ if(!this.wrap.contains(e.target) && this.validateBlur(e)){
+ this.triggerBlur();
+ }
+ },
+
+ // private
+ triggerBlur : function(){
+ this.mimicing = false;
+ Ext.get(Ext.isIE ? document.body : document).un("mousedown", this.mimicBlur, this);
+ if(this.monitorTab && this.el){
+ this.el.un("keydown", this.checkTab, this);
+ }
+ Ext.form.TriggerField.superclass.onBlur.call(this);
+ if(this.wrap){
+ this.wrap.removeClass(this.wrapFocusClass);
+ }
+ },
+
+ beforeBlur : Ext.emptyFn,
+
+ /**
+ * Allow or prevent the user from directly editing the field text. If false is passed,
+ * the user will only be able to modify the field using the trigger. This method
+ * is the runtime equivalent of setting the 'editable' config option at config time.
+ * @param {Boolean} value True to allow the user to directly edit the field text
+ */
+ setEditable : function(value){
+ if(value == this.editable){
+ return;
+ }
+ this.editable = value;
+ if(!value){
+ this.el.addClass('x-trigger-noedit').on('click', this.onTriggerClick, this).dom.setAttribute('readOnly', true);
+ }else{
+ this.el.removeClass('x-trigger-noedit').un('click', this.onTriggerClick, this).dom.removeAttribute('readOnly');
+ }
+ },
+
+ // private
+ // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
+ validateBlur : function(e){
+ return true;
+ },
+
+ /**
+ * The function that should handle the trigger's click event. This method does nothing by default
+ * until overridden by an implementing function. See Ext.form.ComboBox and Ext.form.DateField for
+ * sample implementations.
+ * @method
+ * @param {EventObject} e
+ */
+ onTriggerClick : Ext.emptyFn
+
+ /**
+ * @cfg {Boolean} grow @hide
+ */
+ /**
+ * @cfg {Number} growMin @hide
+ */
+ /**
+ * @cfg {Number} growMax @hide
+ */
+});
+
+/**
+ * @class Ext.form.TwinTriggerField
+ * @extends Ext.form.TriggerField
+ * TwinTriggerField is not a public class to be used directly. It is meant as an abstract base class
+ * to be extended by an implementing class. For an example of implementing this class, see the custom
+ * SearchField implementation here:
+ * http://extjs.com/deploy/ext/examples/form/custom.html
+ */
+Ext.form.TwinTriggerField = Ext.extend(Ext.form.TriggerField, {
+ /**
+ * @cfg {Mixed} triggerConfig
+ * A {@link Ext.DomHelper DomHelper} config object specifying the structure of the trigger elements + * for this Field. (Optional).
+ *Specify this when you need a customized element to contain the two trigger elements for this Field. + * Each trigger element must be marked by the CSS class x-form-trigger (also see + * {@link #trigger1Class} and {@link #trigger2Class}).
+ *Note that when using this option, it is the developer's responsibility to ensure correct sizing, + * positioning and appearance of the triggers.
+ */ + /** + * @cfg {String} trigger1Class + * An additional CSS class used to style the trigger button. The trigger will always get the + * class 'x-form-trigger' by default and triggerClass will be appended if specified. + */ + /** + * @cfg {String} trigger2Class + * An additional CSS class used to style the trigger button. The trigger will always get the + * class 'x-form-trigger' by default and triggerClass will be appended if specified. + */ + + initComponent : function(){ + Ext.form.TwinTriggerField.superclass.initComponent.call(this); + + this.triggerConfig = { + tag:'span', cls:'x-form-twin-triggers', cn:[ + {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class}, + {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class} + ]}; + }, + + getTrigger : function(index){ + return this.triggers[index]; + }, + + initTrigger : function(){ + var ts = this.trigger.select('.x-form-trigger', true); + this.wrap.setStyle('overflow', 'hidden'); + var triggerField = this; + ts.each(function(t, all, index){ + t.hide = function(){ + var w = triggerField.wrap.getWidth(); + this.dom.style.display = 'none'; + triggerField.el.setWidth(w-triggerField.trigger.getWidth()); + }; + t.show = function(){ + var w = triggerField.wrap.getWidth(); + this.dom.style.display = ''; + triggerField.el.setWidth(w-triggerField.trigger.getWidth()); + }; + var triggerIndex = 'Trigger'+(index+1); + + if(this['hide'+triggerIndex]){ + t.dom.style.display = 'none'; + } + this.mon(t, 'click', this['on'+triggerIndex+'Click'], this, {preventDefault:true}); + t.addClassOnOver('x-form-trigger-over'); + t.addClassOnClick('x-form-trigger-click'); + }, this); + this.triggers = ts.elements; + }, + + /** + * The function that should handle the trigger's click event. This method does nothing by default + * until overridden by an implementing function. See {@link Ext.form.TriggerField#onTriggerClick} + * for additional information. + * @method + * @param {EventObject} e + */ + onTrigger1Click : Ext.emptyFn, + /** + * The function that should handle the trigger's click event. This method does nothing by default + * until overridden by an implementing function. See {@link Ext.form.TriggerField#onTriggerClick} + * for additional information. + * @method + * @param {EventObject} e + */ + onTrigger2Click : Ext.emptyFn +}); +Ext.reg('trigger', Ext.form.TriggerField);/** + * @class Ext.form.TextArea + * @extends Ext.form.TextField + * Multiline text field. Can be used as a direct replacement for traditional textarea fields, plus adds + * support for auto-sizing. + * @constructor + * Creates a new TextArea + * @param {Object} config Configuration options + * @xtype textarea + */ +Ext.form.TextArea = Ext.extend(Ext.form.TextField, { + /** + * @cfg {Number} growMin The minimum height to allow when {@link Ext.form.TextField#grow grow}=true + * (defaults to 60) + */ + growMin : 60, + /** + * @cfg {Number} growMax The maximum height to allow when {@link Ext.form.TextField#grow grow}=true + * (defaults to 1000) + */ + growMax: 1000, + growAppend : ' \n ', + growPad : Ext.isWebKit ? -6 : 0, + + enterIsSpecial : false, + + /** + * @cfg {Boolean} preventScrollbars true to prevent scrollbars from appearing regardless of how much text is + * in the field (equivalent to setting overflow: hidden, defaults to false) + */ + preventScrollbars: false, + /** + * @cfg {String/Object} autoCreateA {@link Ext.DomHelper DomHelper} element spec, or true for a default + * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component. + * See {@link Ext.Component#autoEl autoEl} for details. Defaults to:
+ *{tag: "textarea", style: "width:100px;height:60px;", autocomplete: "off"}
+ */
+
+ // private
+ onRender : function(ct, position){
+ if(!this.el){
+ this.defaultAutoCreate = {
+ tag: "textarea",
+ style:"width:100px;height:60px;",
+ autocomplete: "off"
+ };
+ }
+ Ext.form.TextArea.superclass.onRender.call(this, ct, position);
+ if(this.grow){
+ this.textSizeEl = Ext.DomHelper.append(document.body, {
+ tag: "pre", cls: "x-form-grow-sizer"
+ });
+ if(this.preventScrollbars){
+ this.el.setStyle("overflow", "hidden");
+ }
+ this.el.setHeight(this.growMin);
+ }
+ },
+
+ onDestroy : function(){
+ Ext.destroy(this.textSizeEl);
+ Ext.form.TextArea.superclass.onDestroy.call(this);
+ },
+
+ fireKey : function(e){
+ if(e.isSpecialKey() && (this.enterIsSpecial || (e.getKey() != e.ENTER || e.hasModifier()))){
+ this.fireEvent("specialkey", this, e);
+ }
+ },
+
+ // private
+ onKeyUp : function(e){
+ if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
+ this.autoSize();
+ }
+ Ext.form.TextArea.superclass.onKeyUp.call(this, e);
+ },
+
+ /**
+ * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
+ * This only takes effect if grow = true, and fires the {@link #autosize} event if the height changes.
+ */
+ autoSize: function(){
+ if(!this.grow || !this.textSizeEl){
+ return;
+ }
+ var el = this.el;
+ var v = el.dom.value;
+ var ts = this.textSizeEl;
+ ts.innerHTML = '';
+ ts.appendChild(document.createTextNode(v));
+ v = ts.innerHTML;
+ Ext.fly(ts).setWidth(this.el.getWidth());
+ if(v.length < 1){
+ v = " ";
+ }else{
+ v += this.growAppend;
+ if(Ext.isIE){
+ v = v.replace(/\n/g, '
+// disable Sunday and Saturday:
+disabledDays: [0, 6]
+// disable weekdays:
+disabledDays: [1,2,3,4,5]
+ *
+ */
+ /**
+ * @cfg {Array} disabledDates
+ * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
+ * expression so they are very powerful. Some examples:
+// disable these exact dates:
+disabledDates: ["03/08/2003", "09/16/2003"]
+// disable these days for every year:
+disabledDates: ["03/08", "09/16"]
+// only match the beginning (useful if you are using short years):
+disabledDates: ["^03/08"]
+// disable every day in March 2006:
+disabledDates: ["03/../2006"]
+// disable every day in every March:
+disabledDates: ["^03"]
+ *
+ * Note that the format of the dates included in the array should exactly match the {@link #format} config.
+ * In order to support regular expressions, if you are using a {@link #format date format} that has "." in
+ * it, you will have to escape the dot when restricting dates. For example: ["03\\.08\\.03"].
+ */
+ /**
+ * @cfg {String/Object} autoCreate
+ * A {@link Ext.DomHelper DomHelper element specification object}, or true for the default element
+ * specification object:
+ * autoCreate: {tag: "input", type: "text", size: "10", autocomplete: "off"}
+ *
+ */
+
+ // private
+ defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
+
+ initComponent : function(){
+ Ext.form.DateField.superclass.initComponent.call(this);
+
+ this.addEvents(
+ /**
+ * @event select
+ * Fires when a date is selected via the date picker.
+ * @param {Ext.form.DateField} this
+ * @param {Date} date The date that was selected
+ */
+ 'select'
+ );
+
+ if(Ext.isString(this.minValue)){
+ this.minValue = this.parseDate(this.minValue);
+ }
+ if(Ext.isString(this.maxValue)){
+ this.maxValue = this.parseDate(this.maxValue);
+ }
+ this.disabledDatesRE = null;
+ this.initDisabledDays();
+ },
+
+ // private
+ initDisabledDays : function(){
+ if(this.disabledDates){
+ var dd = this.disabledDates,
+ len = dd.length - 1,
+ re = "(?:";
+
+ Ext.each(dd, function(d, i){
+ re += Ext.isDate(d) ? '^' + Ext.escapeRe(d.dateFormat(this.format)) + '$' : dd[i];
+ if(i != len){
+ re += '|';
+ }
+ }, this);
+ this.disabledDatesRE = new RegExp(re + ')');
+ }
+ },
+
+ /**
+ * Replaces any existing disabled dates with new values and refreshes the DatePicker.
+ * @param {Array} disabledDates An array of date strings (see the {@link #disabledDates} config
+ * for details on supported values) used to disable a pattern of dates.
+ */
+ setDisabledDates : function(dd){
+ this.disabledDates = dd;
+ this.initDisabledDays();
+ if(this.menu){
+ this.menu.picker.setDisabledDates(this.disabledDatesRE);
+ }
+ },
+
+ /**
+ * Replaces any existing disabled days (by index, 0-6) with new values and refreshes the DatePicker.
+ * @param {Array} disabledDays An array of disabled day indexes. See the {@link #disabledDays}
+ * config for details on supported values.
+ */
+ setDisabledDays : function(dd){
+ this.disabledDays = dd;
+ if(this.menu){
+ this.menu.picker.setDisabledDays(dd);
+ }
+ },
+
+ /**
+ * Replaces any existing {@link #minValue} with the new value and refreshes the DatePicker.
+ * @param {Date} value The minimum date that can be selected
+ */
+ setMinValue : function(dt){
+ this.minValue = (Ext.isString(dt) ? this.parseDate(dt) : dt);
+ if(this.menu){
+ this.menu.picker.setMinDate(this.minValue);
+ }
+ },
+
+ /**
+ * Replaces any existing {@link #maxValue} with the new value and refreshes the DatePicker.
+ * @param {Date} value The maximum date that can be selected
+ */
+ setMaxValue : function(dt){
+ this.maxValue = (Ext.isString(dt) ? this.parseDate(dt) : dt);
+ if(this.menu){
+ this.menu.picker.setMaxDate(this.maxValue);
+ }
+ },
+
+ // private
+ validateValue : function(value){
+ value = this.formatDate(value);
+ if(!Ext.form.DateField.superclass.validateValue.call(this, value)){
+ return false;
+ }
+ if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
+ return true;
+ }
+ var svalue = value;
+ value = this.parseDate(value);
+ if(!value){
+ this.markInvalid(String.format(this.invalidText, svalue, this.format));
+ return false;
+ }
+ var time = value.getTime();
+ if(this.minValue && time < this.minValue.getTime()){
+ this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
+ return false;
+ }
+ if(this.maxValue && time > this.maxValue.getTime()){
+ this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
+ return false;
+ }
+ if(this.disabledDays){
+ var day = value.getDay();
+ for(var i = 0; i < this.disabledDays.length; i++) {
+ if(day === this.disabledDays[i]){
+ this.markInvalid(this.disabledDaysText);
+ return false;
+ }
+ }
+ }
+ var fvalue = this.formatDate(value);
+ if(this.disabledDatesRE && this.disabledDatesRE.test(fvalue)){
+ this.markInvalid(String.format(this.disabledDatesText, fvalue));
+ return false;
+ }
+ return true;
+ },
+
+ // private
+ // Provides logic to override the default TriggerField.validateBlur which just returns true
+ validateBlur : function(){
+ return !this.menu || !this.menu.isVisible();
+ },
+
+ /**
+ * Returns the current date value of the date field.
+ * @return {Date} The date value
+ */
+ getValue : function(){
+ return this.parseDate(Ext.form.DateField.superclass.getValue.call(this)) || "";
+ },
+
+ /**
+ * Sets the value of the date field. You can pass a date object or any string that can be
+ * parsed into a valid date, using {@link #format} as the date format, according
+ * to the same rules as {@link Date#parseDate} (the default format used is "m/d/Y").
+ *
+//All of these calls set the same date value (May 4, 2006)
+
+//Pass a date object:
+var dt = new Date('5/4/2006');
+dateField.setValue(dt);
+
+//Pass a date string (default format):
+dateField.setValue('05/04/2006');
+
+//Pass a date string (custom format):
+dateField.format = 'Y-m-d';
+dateField.setValue('2006-05-04');
+
+ * @param {String/Date} date The date or valid date string
+ * @return {Ext.form.Field} this
+ */
+ setValue : function(date){
+ return Ext.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
+ },
+
+ // private
+ parseDate : function(value){
+ if(!value || Ext.isDate(value)){
+ return value;
+ }
+ var v = Date.parseDate(value, this.format);
+ if(!v && this.altFormats){
+ if(!this.altFormatsArray){
+ this.altFormatsArray = this.altFormats.split("|");
+ }
+ for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
+ v = Date.parseDate(value, this.altFormatsArray[i]);
+ }
+ }
+ return v;
+ },
+
+ // private
+ onDestroy : function(){
+ Ext.destroy(this.menu);
+ Ext.form.DateField.superclass.onDestroy.call(this);
+ },
+
+ // private
+ formatDate : function(date){
+ return Ext.isDate(date) ? date.dateFormat(this.format) : date;
+ },
+
+ /**
+ * @method onTriggerClick
+ * @hide
+ */
+ // private
+ // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
+ onTriggerClick : function(){
+ if(this.disabled){
+ return;
+ }
+ if(this.menu == null){
+ this.menu = new Ext.menu.DateMenu({
+ hideOnClick: false
+ });
+ }
+ this.onFocus();
+ Ext.apply(this.menu.picker, {
+ minDate : this.minValue,
+ maxDate : this.maxValue,
+ disabledDatesRE : this.disabledDatesRE,
+ disabledDatesText : this.disabledDatesText,
+ disabledDays : this.disabledDays,
+ disabledDaysText : this.disabledDaysText,
+ format : this.format,
+ showToday : this.showToday,
+ minText : String.format(this.minText, this.formatDate(this.minValue)),
+ maxText : String.format(this.maxText, this.formatDate(this.maxValue))
+ });
+ this.menu.picker.setValue(this.getValue() || new Date());
+ this.menu.show(this.el, "tl-bl?");
+ this.menuEvents('on');
+ },
+
+ //private
+ menuEvents: function(method){
+ this.menu[method]('select', this.onSelect, this);
+ this.menu[method]('hide', this.onMenuHide, this);
+ this.menu[method]('show', this.onFocus, this);
+ },
+
+ onSelect: function(m, d){
+ this.setValue(d);
+ this.fireEvent('select', this, d);
+ this.menu.hide();
+ },
+
+ onMenuHide: function(){
+ this.focus(false, 60);
+ this.menuEvents('un');
+ },
+
+ // private
+ beforeBlur : function(){
+ var v = this.parseDate(this.getRawValue());
+ if(v){
+ this.setValue(v);
+ }
+ }
+
+ /**
+ * @cfg {Boolean} grow @hide
+ */
+ /**
+ * @cfg {Number} growMin @hide
+ */
+ /**
+ * @cfg {Number} growMax @hide
+ */
+ /**
+ * @hide
+ * @method autoSize
+ */
+});
+Ext.reg('datefield', Ext.form.DateField);/**
+ * @class Ext.form.DisplayField
+ * @extends Ext.form.Field
+ * A display-only text field which is not validated and not submitted.
+ * @constructor
+ * Creates a new DisplayField.
+ * @param {Object} config Configuration options
+ * @xtype displayfield
+ */
+Ext.form.DisplayField = Ext.extend(Ext.form.Field, {
+ validationEvent : false,
+ validateOnBlur : false,
+ defaultAutoCreate : {tag: "div"},
+ /**
+ * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-display-field")
+ */
+ fieldClass : "x-form-display-field",
+ /**
+ * @cfg {Boolean} htmlEncode false to skip HTML-encoding the text when rendering it (defaults to
+ * false). This might be useful if you want to include tags in the field's innerHTML rather than
+ * rendering them as string literals per the default logic.
+ */
+ htmlEncode: false,
+
+ // private
+ initEvents : Ext.emptyFn,
+
+ isValid : function(){
+ return true;
+ },
+
+ validate : function(){
+ return true;
+ },
+
+ getRawValue : function(){
+ var v = this.rendered ? this.el.dom.innerHTML : Ext.value(this.value, '');
+ if(v === this.emptyText){
+ v = '';
+ }
+ if(this.htmlEncode){
+ v = Ext.util.Format.htmlDecode(v);
+ }
+ return v;
+ },
+
+ getValue : function(){
+ return this.getRawValue();
+ },
+
+ getName: function() {
+ return this.name;
+ },
+
+ setRawValue : function(v){
+ if(this.htmlEncode){
+ v = Ext.util.Format.htmlEncode(v);
+ }
+ return this.rendered ? (this.el.dom.innerHTML = (Ext.isEmpty(v) ? '' : v)) : (this.value = v);
+ },
+
+ setValue : function(v){
+ this.setRawValue(v);
+ return this;
+ }
+ /**
+ * @cfg {String} inputType
+ * @hide
+ */
+ /**
+ * @cfg {Boolean} disabled
+ * @hide
+ */
+ /**
+ * @cfg {Boolean} readOnly
+ * @hide
+ */
+ /**
+ * @cfg {Boolean} validateOnBlur
+ * @hide
+ */
+ /**
+ * @cfg {Number} validationDelay
+ * @hide
+ */
+ /**
+ * @cfg {String/Boolean} validationEvent
+ * @hide
+ */
+});
+
+Ext.reg('displayfield', Ext.form.DisplayField);
+/**
+ * @class Ext.form.ComboBox
+ * @extends Ext.form.TriggerField
+ * A combobox control with support for autocomplete, remote-loading, paging and many other features.
+ *A ComboBox works in a similar manner to a traditional HTML <select> field. The difference is + * that to submit the {@link #valueField}, you must specify a {@link #hiddenName} to create a hidden input + * field to hold the value of the valueField. The {@link #displayField} is shown in the text field + * which is named according to the {@link #name}.
+ *Events
+ *To do something when something in ComboBox is selected, configure the select event:
+var cb = new Ext.form.ComboBox({
+ // all of your config options
+ listeners:{
+ scope: yourScope,
+ 'select': yourFunction
+ }
+});
+
+// Alternatively, you can assign events after the object is created:
+var cb = new Ext.form.ComboBox(yourOptions);
+cb.on('select', yourFunction, yourScope);
+ *
+ *
+ * ComboBox in Grid
+ *If using a ComboBox in an {@link Ext.grid.EditorGridPanel Editor Grid} a {@link Ext.grid.Column#renderer renderer} + * will be needed to show the displayField when the editor is not active. Set up the renderer manually, or implement + * a reusable render, for example:
+// create reusable renderer
+Ext.util.Format.comboRenderer = function(combo){
+ return function(value){
+ var record = combo.findRecord(combo.{@link #valueField}, value);
+ return record ? record.get(combo.{@link #displayField}) : combo.{@link #valueNotFoundText};
+ }
+}
+
+// create the combo instance
+var combo = new Ext.form.ComboBox({
+ {@link #typeAhead}: true,
+ {@link #triggerAction}: 'all',
+ {@link #lazyRender}:true,
+ {@link #mode}: 'local',
+ {@link #store}: new Ext.data.ArrayStore({
+ id: 0,
+ fields: [
+ 'myId',
+ 'displayText'
+ ],
+ data: [[1, 'item1'], [2, 'item2']]
+ }),
+ {@link #valueField}: 'myId',
+ {@link #displayField}: 'displayText'
+});
+
+// snippet of column model used within grid
+var cm = new Ext.grid.ColumnModel([{
+ ...
+ },{
+ header: "Some Header",
+ dataIndex: 'whatever',
+ width: 130,
+ editor: combo, // specify reference to combo instance
+ renderer: Ext.util.Format.comboRenderer(combo) // pass combo instance to reusable renderer
+ },
+ ...
+]);
+ *
+ *
+ * Filtering
+ *A ComboBox {@link #doQuery uses filtering itself}, for information about filtering the ComboBox + * store manually see {@link #lastQuery}.
+ * @constructor + * Create a new ComboBox. + * @param {Object} config Configuration options + * @xtype combo + */ +Ext.form.ComboBox = Ext.extend(Ext.form.TriggerField, { + /** + * @cfg {Mixed} transform The id, DOM node or element of an existing HTML SELECT to convert to a ComboBox. + * Note that if you specify this and the combo is going to be in an {@link Ext.form.BasicForm} or + * {@link Ext.form.FormPanel}, you must also set {@link #lazyRender} = true. + */ + /** + * @cfg {Boolean} lazyRender true to prevent the ComboBox from rendering until requested + * (should always be used when rendering into an {@link Ext.Editor} (e.g. {@link Ext.grid.EditorGridPanel Grids}), + * defaults to false). + */ + /** + * @cfg {String/Object} autoCreateA {@link Ext.DomHelper DomHelper} element spec, or true for a default + * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component. + * See {@link Ext.Component#autoEl autoEl} for details. Defaults to:
+ *{tag: "input", type: "text", size: "24", autocomplete: "off"}
+ */
+ /**
+ * @cfg {Ext.data.Store/Array} store The data source to which this combo is bound (defaults to undefined).
+ * Acceptable values for this property are:
+ * See also {@link #mode}.
+ */ + /** + * @cfg {String} title If supplied, a header element is created containing this text and added into the top of + * the dropdown list (defaults to undefined, with no header element) + */ + + // private + defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"}, + /** + * @cfg {Number} listWidth The width (used as a parameter to {@link Ext.Element#setWidth}) of the dropdown + * list (defaults to the width of the ComboBox field). See also {@link #minListWidth} + */ + /** + * @cfg {String} displayField The underlying {@link Ext.data.Field#name data field name} to bind to this + * ComboBox (defaults to undefined if {@link #mode} = 'remote' or 'text' if + * {@link #transform transforming a select} a select). + *See also {@link #valueField}.
+ *Note: if using a ComboBox in an {@link Ext.grid.EditorGridPanel Editor Grid} a + * {@link Ext.grid.Column#renderer renderer} will be needed to show the displayField when the editor is not + * active.
+ */ + /** + * @cfg {String} valueField The underlying {@link Ext.data.Field#name data value name} to bind to this + * ComboBox (defaults to undefined if {@link #mode} = 'remote' or 'value' if + * {@link #transform transforming a select}). + *Note: use of a valueField requires the user to make a selection in order for a value to be + * mapped. See also {@link #hiddenName}, {@link #hiddenValue}, and {@link #displayField}.
+ */ + /** + * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the + * field's data value (defaults to the underlying DOM element's name). Required for the combo's value to automatically + * post during a form submission. See also {@link #valueField}. + *Note: the hidden field's id will also default to this name if {@link #hiddenId} is not specified. + * The ComboBox {@link Ext.Component#id id} and the {@link #hiddenId} should be different, since + * no two DOM nodes should share the same id. So, if the ComboBox {@link Ext.form.Field#name name} and + * hiddenName are the same, you should specify a unique {@link #hiddenId}.
+ */ + /** + * @cfg {String} hiddenId If {@link #hiddenName} is specified, hiddenId can also be provided + * to give the hidden field a unique id (defaults to the {@link #hiddenName}). The hiddenId + * and combo {@link Ext.Component#id id} should be different, since no two DOM + * nodes should share the same id. + */ + /** + * @cfg {String} hiddenValue Sets the initial value of the hidden field if {@link #hiddenName} is + * specified to contain the selected {@link #valueField}, from the Store. Defaults to the configured + * {@link Ext.form.Field#value value}. + */ + /** + * @cfg {String} listClass The CSS class to add to the predefined 'x-combo-list' class + * applied the dropdown list element (defaults to ''). + */ + listClass : '', + /** + * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list + * (defaults to 'x-combo-selected') + */ + selectedClass : 'x-combo-selected', + /** + * @cfg {String} listEmptyText The empty text to display in the data view if no items are found. + * (defaults to '') + */ + listEmptyText: '', + /** + * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always + * get the class 'x-form-trigger' and triggerClass will be appended if specified + * (defaults to 'x-form-arrow-trigger' which displays a downward arrow icon). + */ + triggerClass : 'x-form-arrow-trigger', + /** + * @cfg {Boolean/String} shadow true or "sides" for the default effect, "frame" for + * 4-way shadow, and "drop" for bottom-right + */ + shadow : 'sides', + /** + * @cfg {String} listAlign A valid anchor position value. See {@link Ext.Element#alignTo} for details + * on supported anchor positions (defaults to 'tl-bl?') + */ + listAlign : 'tl-bl?', + /** + * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown + * (defaults to 300) + */ + maxHeight : 300, + /** + * @cfg {Number} minHeight The minimum height in pixels of the dropdown list when the list is constrained by its + * distance to the viewport edges (defaults to 90) + */ + minHeight : 90, + /** + * @cfg {String} triggerAction The action to execute when the trigger is clicked. + *{@link #doQuery run the query} using the {@link Ext.form.Field#getRawValue raw value}.
{@link #doQuery run the query} specified by the {@link #allQuery} config option
See also {@link #queryParam}
.
Automatically loads the {@link #store} the first time the trigger + * is clicked. If you do not want the store to be automatically loaded the first time the trigger is + * clicked, set to 'local' and manually load the store. To force a requery of the store + * every time the trigger is clicked see {@link #lastQuery}.
ComboBox loads local data
+ *
+var combo = new Ext.form.ComboBox({
+ renderTo: document.body,
+ mode: 'local',
+ store: new Ext.data.ArrayStore({
+ id: 0,
+ fields: [
+ 'myId', // numeric value is the key
+ 'displayText'
+ ],
+ data: [[1, 'item1'], [2, 'item2']] // data is local
+ }),
+ valueField: 'myId',
+ displayField: 'displayText',
+ triggerAction: 'all'
+});
+ *
+var combo = new Ext.form.ComboBox({
+ ...
+ mode: 'remote',
+ ...
+ listeners: {
+ // delete the previous query in the beforequery event or set
+ // combo.lastQuery = null (this will reload the store the next time it expands)
+ beforequery: function(qe){
+ delete qe.combo.lastQuery;
+ }
+ }
+});
+ *
+ * To make sure the filter in the store is not cleared the first time the ComboBox trigger is used
+ * configure the combo with lastQuery=''. Example use:
+ *
+var combo = new Ext.form.ComboBox({
+ ...
+ mode: 'local',
+ triggerAction: 'all',
+ lastQuery: ''
+});
+ *
+ * @property lastQuery
+ * @type String
+ */
+
+ // private
+ initComponent : function(){
+ Ext.form.ComboBox.superclass.initComponent.call(this);
+ this.addEvents(
+ /**
+ * @event expand
+ * Fires when the dropdown list is expanded
+ * @param {Ext.form.ComboBox} combo This combo box
+ */
+ 'expand',
+ /**
+ * @event collapse
+ * Fires when the dropdown list is collapsed
+ * @param {Ext.form.ComboBox} combo This combo box
+ */
+ 'collapse',
+ /**
+ * @event beforeselect
+ * Fires before a list item is selected. Return false to cancel the selection.
+ * @param {Ext.form.ComboBox} combo This combo box
+ * @param {Ext.data.Record} record The data record returned from the underlying store
+ * @param {Number} index The index of the selected item in the dropdown list
+ */
+ 'beforeselect',
+ /**
+ * @event select
+ * Fires when a list item is selected
+ * @param {Ext.form.ComboBox} combo This combo box
+ * @param {Ext.data.Record} record The data record returned from the underlying store
+ * @param {Number} index The index of the selected item in the dropdown list
+ */
+ 'select',
+ /**
+ * @event beforequery
+ * Fires before all queries are processed. Return false to cancel the query or set the queryEvent's
+ * cancel property to true.
+ * @param {Object} queryEvent An object that has these properties:combo
: Ext.form.ComboBox query
: String forceAll
: Boolean cancel
: Boolean The template string, or {@link Ext.XTemplate} instance to + * use to display each item in the dropdown list. The dropdown list is displayed in a + * DataView. See {@link #view}.
+ *The default template string is:
+ '<tpl for="."><div class="x-combo-list-item">{' + this.displayField + '}</div></tpl>'
+ *
+ * Override the default value to create custom UI layouts for items in the list. + * For example:
+ '<tpl for="."><div ext:qtip="{state}. {nick}" class="x-combo-list-item">{state}</div></tpl>'
+ *
+ * The template must contain one or more substitution parameters using field + * names from the Combo's {@link #store Store}. In the example above an + *
ext:qtipattribute is added to display other fields from the Store. + *
To preserve the default visual look of list items, add the CSS class name + *
x-combo-list-itemto the template's container element. + *
Also see {@link #itemSelector} for additional details.
+ */ + this.tpl = 'A simple CSS selector (e.g. div.some-class or span:first-child) that will be + * used to determine what nodes the {@link #view Ext.DataView} which handles the dropdown + * display will be working with.
+ *Note: this setting is required if a custom XTemplate has been + * specified in {@link #tpl} which assigns a class other than
'x-combo-list-item'+ * to dropdown list items + */ + } + + /** + * The {@link Ext.DataView DataView} used to display the ComboBox's options. + * @type Ext.DataView + */ + this.view = new Ext.DataView({ + applyTo: this.innerList, + tpl: this.tpl, + singleSelect: true, + selectedClass: this.selectedClass, + itemSelector: this.itemSelector || '.' + cls + '-item', + emptyText: this.listEmptyText + }); + + this.mon(this.view, 'click', this.onViewClick, this); + + this.bindStore(this.store, true); + + if(this.resizable){ + this.resizer = new Ext.Resizable(this.list, { + pinned:true, handles:'se' + }); + this.mon(this.resizer, 'resize', function(r, w, h){ + this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight; + this.listWidth = w; + this.innerList.setWidth(w - this.list.getFrameWidth('lr')); + this.restrictHeight(); + }, this); + + this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px'); + } + } + }, + + /** + *
Returns the element used to house this ComboBox's pop-up list. Defaults to the document body.
+ * A custom implementation may be provided as a configuration option if the floating list needs to be rendered + * to a different Element. An example might be rendering the list inside a Menu so that clicking + * the list does not hide the Menu:
+var store = new Ext.data.ArrayStore({
+ autoDestroy: true,
+ fields: ['initials', 'fullname'],
+ data : [
+ ['FF', 'Fred Flintstone'],
+ ['BR', 'Barney Rubble']
+ ]
+});
+
+var combo = new Ext.form.ComboBox({
+ store: store,
+ displayField: 'fullname',
+ emptyText: 'Select a name...',
+ forceSelection: true,
+ getListParent: function() {
+ return this.el.up('.x-menu');
+ },
+ iconCls: 'no-icon', //use iconCls if placing within menu to shift to right side of menu
+ mode: 'local',
+ selectOnFocus: true,
+ triggerAction: 'all',
+ typeAhead: true,
+ width: 135
+});
+
+var menu = new Ext.menu.Menu({
+ id: 'mainMenu',
+ items: [
+ combo // A Field in a Menu
+ ]
+});
+
+ */
+ getListParent : function() {
+ return document.body;
+ },
+
+ /**
+ * Returns the store associated with this combo.
+ * @return {Ext.data.Store} The store
+ */
+ getStore : function(){
+ return this.store;
+ },
+
+ // private
+ bindStore : function(store, initial){
+ if(this.store && !initial){
+ this.store.un('beforeload', this.onBeforeLoad, this);
+ this.store.un('load', this.onLoad, this);
+ this.store.un('exception', this.collapse, this);
+ if(this.store !== store && this.store.autoDestroy){
+ this.store.destroy();
+ }
+ if(!store){
+ this.store = null;
+ if(this.view){
+ this.view.bindStore(null);
+ }
+ }
+ }
+ if(store){
+ if(!initial) {
+ this.lastQuery = null;
+ if(this.pageTb) {
+ this.pageTb.bindStore(store);
+ }
+ }
+
+ this.store = Ext.StoreMgr.lookup(store);
+ this.store.on({
+ scope: this,
+ beforeload: this.onBeforeLoad,
+ load: this.onLoad,
+ exception: this.collapse
+ });
+
+ if(this.view){
+ this.view.bindStore(store);
+ }
+ }
+ },
+
+ // private
+ initEvents : function(){
+ Ext.form.ComboBox.superclass.initEvents.call(this);
+
+ this.keyNav = new Ext.KeyNav(this.el, {
+ "up" : function(e){
+ this.inKeyMode = true;
+ this.selectPrev();
+ },
+
+ "down" : function(e){
+ if(!this.isExpanded()){
+ this.onTriggerClick();
+ }else{
+ this.inKeyMode = true;
+ this.selectNext();
+ }
+ },
+
+ "enter" : function(e){
+ this.onViewClick();
+ this.delayedCheck = true;
+ this.unsetDelayCheck.defer(10, this);
+ },
+
+ "esc" : function(e){
+ this.collapse();
+ },
+
+ "tab" : function(e){
+ this.onViewClick(false);
+ return true;
+ },
+
+ scope : this,
+
+ doRelay : function(foo, bar, hname){
+ if(hname == 'down' || this.scope.isExpanded()){
+ return Ext.KeyNav.prototype.doRelay.apply(this, arguments);
+ }
+ return true;
+ },
+
+ forceKeyDown : true
+ });
+ this.queryDelay = Math.max(this.queryDelay || 10,
+ this.mode == 'local' ? 10 : 250);
+ this.dqTask = new Ext.util.DelayedTask(this.initQuery, this);
+ if(this.typeAhead){
+ this.taTask = new Ext.util.DelayedTask(this.onTypeAhead, this);
+ }
+ if(this.editable !== false && !this.enableKeyEvents){
+ this.mon(this.el, 'keyup', this.onKeyUp, this);
+ }
+ },
+
+ // private
+ onDestroy : function(){
+ if (this.dqTask){
+ this.dqTask.cancel();
+ this.dqTask = null;
+ }
+ this.bindStore(null);
+ Ext.destroy(
+ this.resizer,
+ this.view,
+ this.pageTb,
+ this.list
+ );
+ Ext.form.ComboBox.superclass.onDestroy.call(this);
+ },
+
+ // private
+ unsetDelayCheck : function(){
+ delete this.delayedCheck;
+ },
+
+ // private
+ fireKey : function(e){
+ var fn = function(ev){
+ if (ev.isNavKeyPress() && !this.isExpanded() && !this.delayedCheck) {
+ this.fireEvent("specialkey", this, ev);
+ }
+ };
+ //For some reason I can't track down, the events fire in a different order in webkit.
+ //Need a slight delay here
+ if(this.inEditor && Ext.isWebKit && e.getKey() == e.TAB){
+ fn.defer(10, this, [new Ext.EventObjectImpl(e)]);
+ }else{
+ fn.call(this, e);
+ }
+ },
+
+ // private
+ onResize : function(w, h){
+ Ext.form.ComboBox.superclass.onResize.apply(this, arguments);
+ if(this.list && !Ext.isDefined(this.listWidth)){
+ var lw = Math.max(w, this.minListWidth);
+ this.list.setWidth(lw);
+ this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
+ }
+ },
+
+ // private
+ onEnable : function(){
+ Ext.form.ComboBox.superclass.onEnable.apply(this, arguments);
+ if(this.hiddenField){
+ this.hiddenField.disabled = false;
+ }
+ },
+
+ // private
+ onDisable : function(){
+ Ext.form.ComboBox.superclass.onDisable.apply(this, arguments);
+ if(this.hiddenField){
+ this.hiddenField.disabled = true;
+ }
+ },
+
+ // private
+ onBeforeLoad : function(){
+ if(!this.hasFocus){
+ return;
+ }
+ this.innerList.update(this.loadingText ?
+ '{@link #handler}
(if configured).
+ * @param {Boolean/String} checked The following values will check the checkbox:
+ * true, 'true', '1', or 'on'
. Any other value will uncheck the checkbox.
+ * @return {Ext.form.Field} this
+ */
+ setValue : function(v){
+ var checked = this.checked ;
+ this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
+ if(this.rendered){
+ this.el.dom.checked = this.checked;
+ this.el.dom.defaultChecked = this.checked;
+ }
+ if(checked != this.checked){
+ this.fireEvent('check', this, this.checked);
+ if(this.handler){
+ this.handler.call(this.scope || this, this, this.checked);
+ }
+ }
+ return this;
+ }
+});
+Ext.reg('checkbox', Ext.form.Checkbox);
+/**
+ * @class Ext.form.CheckboxGroup
+ * @extends Ext.form.Field
+ * A grouping container for {@link Ext.form.Checkbox} controls.
+ *Sample usage:
+ *
+var myCheckboxGroup = new Ext.form.CheckboxGroup({
+ id:'myGroup',
+ xtype: 'checkboxgroup',
+ fieldLabel: 'Single Column',
+ itemCls: 'x-check-group-alt',
+ // Put all controls in a single column with width 100%
+ columns: 1,
+ items: [
+ {boxLabel: 'Item 1', name: 'cb-col-1'},
+ {boxLabel: 'Item 2', name: 'cb-col-2', checked: true},
+ {boxLabel: 'Item 3', name: 'cb-col-3'}
+ ]
+});
+ *
+ * @constructor
+ * Creates a new CheckboxGroup
+ * @param {Object} config Configuration options
+ * @xtype checkboxgroup
+ */
+Ext.form.CheckboxGroup = Ext.extend(Ext.form.Field, {
+ /**
+ * @cfg {Array} items An Array of {@link Ext.form.Checkbox Checkbox}es or Checkbox config objects
+ * to arrange in the group.
+ */
+ /**
+ * @cfg {String/Number/Array} columns Specifies the number of columns to use when displaying grouped
+ * checkbox/radio controls using automatic layout. This config can take several types of values:
+ * The controls will be rendered one per column on one row and the width + * of each column will be evenly distributed based on the width of the overall field container. This is the default.
If you specific a number (e.g., 3) that number of columns will be + * created and the contained controls will be automatically distributed based on the value of {@link #vertical}.
You can also specify an array of column widths, mixing integer + * (fixed width) and float (percentage width) values as needed (e.g., [100, .25, .75]). Any integer values will + * be rendered first, then any float values will be calculated as a percentage of the remaining space. Float + * values do not have to add up to 1 (100%) although if you want the controls to take up the entire field + * container you should do so.
+// call with name and value
+myCheckboxGroup.setValue('cb-col-1', true);
+// call with an array of boolean values
+myCheckboxGroup.setValue([true, false, false]);
+// call with an object literal specifying item:value pairs
+myCheckboxGroup.setValue({
+ 'cb-col-2': false,
+ 'cb-col-3': true
+});
+// use comma separated string to set items with name to true (checked)
+myCheckboxGroup.setValue('cb-col-1,cb-col-3');
+ *
+ * See {@link Ext.form.Checkbox#setValue} for additional information.
+ * @param {Mixed} id The checkbox to check, or as described by example shown.
+ * @param {Boolean} value (optional) The value to set the item.
+ * @return {Ext.form.CheckboxGroup} this
+ */
+ setValue : function(id, value){
+ if(this.rendered){
+ if(arguments.length == 1){
+ if(Ext.isArray(id)){
+ //an array of boolean values
+ Ext.each(id, function(val, idx){
+ var item = this.items.itemAt(idx);
+ if(item){
+ item.setValue(val);
+ }
+ }, this);
+ }else if(Ext.isObject(id)){
+ //set of name/value pairs
+ for(var i in id){
+ var f = this.getBox(i);
+ if(f){
+ f.setValue(id[i]);
+ }
+ }
+ }else{
+ this.setValueForItem(id);
+ }
+ }else{
+ var f = this.getBox(id);
+ if(f){
+ f.setValue(value);
+ }
+ }
+ }else{
+ this.values = arguments;
+ }
+ return this;
+ },
+
+ // private
+ onDestroy: function(){
+ Ext.destroy(this.panel);
+ Ext.form.CheckboxGroup.superclass.onDestroy.call(this);
+
+ },
+
+ setValueForItem : function(val){
+ val = String(val).split(',');
+ this.eachItem(function(item){
+ if(val.indexOf(item.inputValue)> -1){
+ item.setValue(true);
+ }
+ });
+ },
+
+ // private
+ getBox : function(id){
+ var box = null;
+ this.eachItem(function(f){
+ if(id == f || f.dataIndex == id || f.id == id || f.getName() == id){
+ box = f;
+ return false;
+ }
+ });
+ return box;
+ },
+
+ /**
+ * Gets an array of the selected {@link Ext.form.Checkbox} in the group.
+ * @return {Array} An array of the selected checkboxes.
+ */
+ getValue : function(){
+ var out = [];
+ this.eachItem(function(item){
+ if(item.checked){
+ out.push(item);
+ }
+ });
+ return out;
+ },
+
+ // private
+ eachItem: function(fn){
+ if(this.items && this.items.each){
+ this.items.each(fn, this);
+ }
+ },
+
+ /**
+ * @cfg {String} name
+ * @hide
+ */
+ /**
+ * @method initValue
+ * @hide
+ */
+ initValue : Ext.emptyFn,
+ /**
+ * @method getValue
+ * @hide
+ */
+ getValue : Ext.emptyFn,
+ /**
+ * @method getRawValue
+ * @hide
+ */
+ getRawValue : Ext.emptyFn,
+
+ /**
+ * @method setRawValue
+ * @hide
+ */
+ setRawValue : Ext.emptyFn
+
+});
+
+Ext.reg('checkboxgroup', Ext.form.CheckboxGroup);
+/**
+ * @class Ext.form.Radio
+ * @extends Ext.form.Checkbox
+ * Single radio field. Same as Checkbox, but provided as a convenience for automatically setting the input type.
+ * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
+ * @constructor
+ * Creates a new Radio
+ * @param {Object} config Configuration options
+ * @xtype radio
+ */
+Ext.form.Radio = Ext.extend(Ext.form.Checkbox, {
+ inputType: 'radio',
+
+ /**
+ * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
+ * @method
+ */
+ markInvalid : Ext.emptyFn,
+ /**
+ * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
+ * @method
+ */
+ clearInvalid : Ext.emptyFn,
+
+ /**
+ * If this radio is part of a group, it will return the selected value
+ * @return {String}
+ */
+ getGroupValue : function(){
+ var p = this.el.up('form') || Ext.getBody();
+ var c = p.child('input[name='+this.el.dom.name+']:checked', true);
+ return c ? c.value : null;
+ },
+
+ // private
+ onClick : function(){
+ if(this.el.dom.checked != this.checked){
+ var els = this.getCheckEl().select('input[name=' + this.el.dom.name + ']');
+ els.each(function(el){
+ if(el.dom.id == this.id){
+ this.setValue(true);
+ }else{
+ Ext.getCmp(el.dom.id).setValue(false);
+ }
+ }, this);
+ }
+ },
+
+ /**
+ * Sets either the checked/unchecked status of this Radio, or, if a string value
+ * is passed, checks a sibling Radio of the same name whose value is the value specified.
+ * @param value {String/Boolean} Checked value, or the value of the sibling radio button to check.
+ * @return {Ext.form.Field} this
+ */
+ setValue : function(v){
+ if (typeof v == 'boolean') {
+ Ext.form.Radio.superclass.setValue.call(this, v);
+ } else {
+ var r = this.getCheckEl().child('input[name=' + this.el.dom.name + '][value=' + v + ']', true);
+ if(r){
+ Ext.getCmp(r.id).setValue(true);
+ }
+ }
+ return this;
+ },
+
+ // private
+ getCheckEl: function(){
+ if(this.inGroup){
+ return this.el.up('.x-form-radio-group')
+ }
+ return this.el.up('form') || Ext.getBody();
+ }
+});
+Ext.reg('radio', Ext.form.Radio);
+/**
+ * @class Ext.form.RadioGroup
+ * @extends Ext.form.CheckboxGroup
+ * A grouping container for {@link Ext.form.Radio} controls.
+ * @constructor
+ * Creates a new RadioGroup
+ * @param {Object} config Configuration options
+ * @xtype radiogroup
+ */
+Ext.form.RadioGroup = Ext.extend(Ext.form.CheckboxGroup, {
+ /**
+ * @cfg {Boolean} allowBlank True to allow every item in the group to be blank (defaults to true).
+ * If allowBlank = false and no items are selected at validation time, {@link @blankText} will
+ * be used as the error text.
+ */
+ allowBlank : true,
+ /**
+ * @cfg {String} blankText Error text to display if the {@link #allowBlank} validation fails
+ * (defaults to 'You must select one item in this group')
+ */
+ blankText : 'You must select one item in this group',
+
+ // private
+ defaultType : 'radio',
+
+ // private
+ groupCls : 'x-form-radio-group',
+
+ /**
+ * @event change
+ * Fires when the state of a child radio changes.
+ * @param {Ext.form.RadioGroup} this
+ * @param {Ext.form.Radio} checked The checked radio
+ */
+
+ /**
+ * Gets the selected {@link Ext.form.Radio} in the group, if it exists.
+ * @return {Ext.form.Radio} The selected radio.
+ */
+ getValue : function(){
+ var out = null;
+ this.eachItem(function(item){
+ if(item.checked){
+ out = item;
+ return false;
+ }
+ });
+ return out;
+ },
+
+ /**
+ * Sets the checked radio in the group.
+ * @param {String/Ext.form.Radio} id The radio to check.
+ * @param {Boolean} value The value to set the radio.
+ * @return {Ext.form.RadioGroup} this
+ */
+ setValue : function(id, value){
+ if(this.rendered){
+ if(arguments.length > 1){
+ var f = this.getBox(id);
+ if(f){
+ f.setValue(value);
+ if(f.checked){
+ this.eachItem(function(item){
+ if (item !== f){
+ item.setValue(false);
+ }
+ });
+ }
+ }
+ }else{
+ this.setValueForItem(id);
+ }
+ }else{
+ this.values = arguments;
+ }
+ return this;
+ },
+
+ // private
+ fireChecked : function(){
+ if(!this.checkTask){
+ this.checkTask = new Ext.util.DelayedTask(this.bufferChecked, this);
+ }
+ this.checkTask.delay(10);
+ },
+
+ // private
+ bufferChecked : function(){
+ var out = null;
+ this.eachItem(function(item){
+ if(item.checked){
+ out = item;
+ return false;
+ }
+ });
+ this.fireEvent('change', this, out);
+ },
+
+ onDestroy : function(){
+ if(this.checkTask){
+ this.checkTask.cancel();
+ this.checkTask = null;
+ }
+ Ext.form.RadioGroup.superclass.onDestroy.call(this);
+ }
+
+});
+
+Ext.reg('radiogroup', Ext.form.RadioGroup);
+/**
+ * @class Ext.form.Hidden
+ * @extends Ext.form.Field
+ * A basic hidden field for storing hidden values in forms that need to be passed in the form submit.
+ * @constructor
+ * Create a new Hidden field.
+ * @param {Object} config Configuration options
+ * @xtype hidden
+ */
+Ext.form.Hidden = Ext.extend(Ext.form.Field, {
+ // private
+ inputType : 'hidden',
+
+ // private
+ onRender : function(){
+ Ext.form.Hidden.superclass.onRender.apply(this, arguments);
+ },
+
+ // private
+ initEvents : function(){
+ this.originalValue = this.getValue();
+ },
+
+ // These are all private overrides
+ setSize : Ext.emptyFn,
+ setWidth : Ext.emptyFn,
+ setHeight : Ext.emptyFn,
+ setPosition : Ext.emptyFn,
+ setPagePosition : Ext.emptyFn,
+ markInvalid : Ext.emptyFn,
+ clearInvalid : Ext.emptyFn
+});
+Ext.reg('hidden', Ext.form.Hidden);/**
+ * @class Ext.form.BasicForm
+ * @extends Ext.util.Observable
+ * Encapsulates the DOM <form> element at the heart of the {@link Ext.form.FormPanel FormPanel} class, and provides + * input field management, validation, submission, and form loading services.
+ *By default, Ext Forms are submitted through Ajax, using an instance of {@link Ext.form.Action.Submit}. + * To enable normal browser submission of an Ext Form, use the {@link #standardSubmit} config option.
+ *File Uploads
+ *{@link #fileUpload File uploads} are not performed using Ajax submission, that + * is they are not performed using XMLHttpRequests. Instead the form is submitted in the standard + * manner with the DOM <form> element temporarily modified to have its + * target set to refer + * to a dynamically generated, hidden <iframe> which is inserted into the document + * but removed after the return data has been gathered.
+ *The server response is parsed by the browser to create the document for the IFRAME. If the + * server is using JSON to send the return object, then the + * Content-Type header + * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.
+ *Characters which are significant to an HTML parser must be sent as HTML entities, so encode + * "<" as "<", "&" as "&" etc.
+ *The response text is retrieved from the document, and a fake XMLHttpRequest object + * is created containing a responseText property in order to conform to the + * requirements of event handlers and callbacks.
+ *Be aware that file upload packets are sent with the content type multipart/form + * and some server technologies (notably JEE) may require some custom processing in order to + * retrieve parameter names and parameter values from the packet content.
+ * @constructor + * @param {Mixed} el The form element or its id + * @param {Object} config Configuration options + */ +Ext.form.BasicForm = function(el, config){ + Ext.apply(this, config); + if(Ext.isString(this.paramOrder)){ + this.paramOrder = this.paramOrder.split(/[\s,|]/); + } + /* + * @property items + * A {@link Ext.util.MixedCollection MixedCollection) containing all the Ext.form.Fields in this form. + * @type MixedCollection + */ + this.items = new Ext.util.MixedCollection(false, function(o){ + return o.itemId || o.id || (o.id = Ext.id()); + }); + this.addEvents( + /** + * @event beforeaction + * Fires before any action is performed. Return false to cancel the action. + * @param {Form} this + * @param {Action} action The {@link Ext.form.Action} to be performed + */ + 'beforeaction', + /** + * @event actionfailed + * Fires when an action fails. + * @param {Form} this + * @param {Action} action The {@link Ext.form.Action} that failed + */ + 'actionfailed', + /** + * @event actioncomplete + * Fires when an action is completed. + * @param {Form} this + * @param {Action} action The {@link Ext.form.Action} that completed + */ + 'actioncomplete' + ); + + if(el){ + this.initEl(el); + } + Ext.form.BasicForm.superclass.constructor.call(this); +}; + +Ext.extend(Ext.form.BasicForm, Ext.util.Observable, { + /** + * @cfg {String} method + * The request method to use (GET or POST) for form actions if one isn't supplied in the action options. + */ + /** + * @cfg {DataReader} reader + * An Ext.data.DataReader (e.g. {@link Ext.data.XmlReader}) to be used to read + * data when executing 'load' actions. This is optional as there is built-in + * support for processing JSON. For additional information on using an XMLReader + * see the example provided in examples/form/xml-form.html. + */ + /** + * @cfg {DataReader} errorReader + *An Ext.data.DataReader (e.g. {@link Ext.data.XmlReader}) to be used to + * read field error messages returned from 'submit' actions. This is optional + * as there is built-in support for processing JSON.
+ *The Records which provide messages for the invalid Fields must use the + * Field name (or id) as the Record ID, and must contain a field called 'msg' + * which contains the error message.
+ *The errorReader does not have to be a full-blown implementation of a + * DataReader. It simply needs to implement a read(xhr) function + * which returns an Array of Records in an object with the following + * structure:
+{
+ records: recordArray
+}
+
+ */
+ /**
+ * @cfg {String} url
+ * The URL to use for form actions if one isn't supplied in the
+ * {@link #doAction doAction} options
.
+ */
+ /**
+ * @cfg {Boolean} fileUpload
+ * Set to true if this form is a file upload.
+ * File uploads are not performed using normal 'Ajax' techniques, that is they are not + * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the + * DOM <form> element temporarily modified to have its + * target set to refer + * to a dynamically generated, hidden <iframe> which is inserted into the document + * but removed after the return data has been gathered.
+ *The server response is parsed by the browser to create the document for the IFRAME. If the + * server is using JSON to send the return object, then the + * Content-Type header + * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.
+ *Characters which are significant to an HTML parser must be sent as HTML entities, so encode + * "<" as "<", "&" as "&" etc.
+ *The response text is retrieved from the document, and a fake XMLHttpRequest object + * is created containing a responseText property in order to conform to the + * requirements of event handlers and callbacks.
+ *Be aware that file upload packets are sent with the content type multipart/form + * and some server technologies (notably JEE) may require some custom processing in order to + * retrieve parameter names and parameter values from the packet content.
+ */ + /** + * @cfg {Object} baseParams + *Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
+ *Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.
+ */ + /** + * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds). + */ + timeout: 30, + + /** + * @cfg {Object} api (Optional) If specified load and submit actions will be handled + * with {@link Ext.form.Action.DirectLoad} and {@link Ext.form.Action.DirectSubmit}. + * Methods which have been imported by Ext.Direct can be specified here to load and submit + * forms. + * Such as the following:
+api: {
+ load: App.ss.MyProfile.load,
+ submit: App.ss.MyProfile.submit
+}
+
+ * Load actions can use {@link #paramOrder}
or {@link #paramsAsHash}
+ * to customize how the load method is invoked.
+ * Submit actions will always use a standard form submit. The formHandler configuration must
+ * be set on the associated server-side method which has been imported by Ext.Direct
A list of params to be executed server side.
+ * Defaults to undefined. Only used for the {@link #api}
+ * load
configuration.
Specify the params in the order in which they must be executed on the + * server-side as either (1) an Array of String values, or (2) a String of params + * delimited by either whitespace, comma, or pipe. For example, + * any of the following would be acceptable:
+paramOrder: ['param1','param2','param3']
+paramOrder: 'param1 param2 param3'
+paramOrder: 'param1,param2,param3'
+paramOrder: 'param1|param2|param'
+
+ */
+ paramOrder: undefined,
+
+ /**
+ * @cfg {Boolean} paramsAsHash Only used for the {@link #api}
+ * load
configuration. Send parameters as a collection of named
+ * arguments (defaults to false). Providing a
+ * {@link #paramOrder} nullifies this configuration.
+ */
+ paramsAsHash: false,
+
+
+ // private
+ activeAction : null,
+
+ /**
+ * @cfg {Boolean} trackResetOnLoad If set to true, {@link #reset}() resets to the last loaded
+ * or {@link #setValues}() data instead of when the form was first created. Defaults to false.
+ */
+ trackResetOnLoad : false,
+
+ /**
+ * @cfg {Boolean} standardSubmit If set to true, standard HTML form submits are used instead of XHR (Ajax) style
+ * form submissions. (defaults to false)Note: When using standardSubmit, the options to {@link #submit} are ignored because Ext's + * Ajax infrastracture is bypassed. To pass extra parameters (baseParams and params), you will need to + * create hidden fields within the form.
+ *The url config option is also bypassed, so set the action as well:
+ *
+PANEL.getForm().getEl().dom.action = 'URL'
+ *
+ * An example encapsulating the above:
+ *
+new Ext.FormPanel({
+ standardSubmit: true,
+ baseParams: {
+ foo: 'bar'
+ },
+ url: 'myProcess.php',
+ items: [{
+ xtype: 'textfield',
+ name: 'userName'
+ }],
+ buttons: [{
+ text: 'Save',
+ handler: function(){
+ var O = this.ownerCt;
+ if (O.getForm().isValid()) {
+ if (O.url)
+ O.getForm().getEl().dom.action = O.url;
+ if (O.baseParams) {
+ for (i in O.baseParams) {
+ O.add({
+ xtype: 'hidden',
+ name: i,
+ value: O.baseParams[i]
+ })
+ }
+ O.doLayout();
+ }
+ O.getForm().submit();
+ }
+ }
+ }]
+});
+ *
+ */
+ /**
+ * By default wait messages are displayed with Ext.MessageBox.wait. You can target a specific
+ * element by passing it or its id or mask the form itself by passing in true.
+ * @type Mixed
+ * @property waitMsgTarget
+ */
+
+ // private
+ initEl : function(el){
+ this.el = Ext.get(el);
+ this.id = this.el.id || Ext.id();
+ if(!this.standardSubmit){
+ this.el.on('submit', this.onSubmit, this);
+ }
+ this.el.addClass('x-form');
+ },
+
+ /**
+ * Get the HTML form Element
+ * @return Ext.Element
+ */
+ getEl: function(){
+ return this.el;
+ },
+
+ // private
+ onSubmit : function(e){
+ e.stopEvent();
+ },
+
+ // private
+ destroy: function() {
+ this.items.each(function(f){
+ Ext.destroy(f);
+ });
+ if(this.el){
+ this.el.removeAllListeners();
+ this.el.remove();
+ }
+ this.purgeListeners();
+ },
+
+ /**
+ * Returns true if client-side validation on the form is successful.
+ * @return Boolean
+ */
+ isValid : function(){
+ var valid = true;
+ this.items.each(function(f){
+ if(!f.validate()){
+ valid = false;
+ }
+ });
+ return valid;
+ },
+
+ /**
+ * Returns true if any fields in this form have changed from their original values.
+ *Note that if this BasicForm was configured with {@link #trackResetOnLoad} then the + * Fields' original values are updated when the values are loaded by {@link #setValues} + * or {@link #loadRecord}.
+ * @return Boolean + */ + isDirty : function(){ + var dirty = false; + this.items.each(function(f){ + if(f.isDirty()){ + dirty = true; + return false; + } + }); + return dirty; + }, + + /** + * Performs a predefined action ({@link Ext.form.Action.Submit} or + * {@link Ext.form.Action.Load}) or a custom extension of {@link Ext.form.Action} + * to perform application-specific processing. + * @param {String/Object} actionName The name of the predefined action type, + * or instance of {@link Ext.form.Action} to perform. + * @param {Object} options (optional) The options to pass to the {@link Ext.form.Action}. + * All of the config options listed below are supported by both the + * {@link Ext.form.Action.Submit submit} and {@link Ext.form.Action.Load load} + * actions unless otherwise noted (custom actions could also accept + * other config options):The params to pass + * (defaults to the form's baseParams, or none if not defined)
+ *Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.
Note: this is ignored when using the {@link #standardSubmit} option.
+ *The following code:
+myFormPanel.getForm().submit({
+ clientValidation: true,
+ url: 'updateConsignment.php',
+ params: {
+ newStatus: 'delivered'
+ },
+ success: function(form, action) {
+ Ext.Msg.alert('Success', action.result.msg);
+ },
+ failure: function(form, action) {
+ switch (action.failureType) {
+ case Ext.form.Action.CLIENT_INVALID:
+ Ext.Msg.alert('Failure', 'Form fields may not be submitted with invalid values');
+ break;
+ case Ext.form.Action.CONNECT_FAILURE:
+ Ext.Msg.alert('Failure', 'Ajax communication failed');
+ break;
+ case Ext.form.Action.SERVER_INVALID:
+ Ext.Msg.alert('Failure', action.result.msg);
+ }
+ }
+});
+
+ * would process the following server response for a successful submission:
+{
+ "success":true, // note this is Boolean, not string
+ "msg":"Consignment updated"
+}
+
+ * and the following server response for a failed submission:
+{
+ "success":false, // note this is Boolean, not string
+ "msg":"You do not have permission to perform this operation"
+}
+
+ * @return {BasicForm} this
+ */
+ submit : function(options){
+ if(this.standardSubmit){
+ var v = this.isValid();
+ if(v){
+ this.el.dom.submit();
+ }
+ return v;
+ }
+ var submitAction = String.format('{0}submit', this.api ? 'direct' : '');
+ this.doAction(submitAction, options);
+ return this;
+ },
+
+ /**
+ * Shortcut to {@link #doAction do} a {@link Ext.form.Action.Load load action}.
+ * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
+ * @return {BasicForm} this
+ */
+ load : function(options){
+ var loadAction = String.format('{0}load', this.api ? 'direct' : '');
+ this.doAction(loadAction, options);
+ return this;
+ },
+
+ /**
+ * Persists the values in this form into the passed {@link Ext.data.Record} object in a beginEdit/endEdit block.
+ * @param {Record} record The record to edit
+ * @return {BasicForm} this
+ */
+ updateRecord : function(record){
+ record.beginEdit();
+ var fs = record.fields;
+ fs.each(function(f){
+ var field = this.findField(f.name);
+ if(field){
+ record.set(f.name, field.getValue());
+ }
+ }, this);
+ record.endEdit();
+ return this;
+ },
+
+ /**
+ * Loads an {@link Ext.data.Record} into this form by calling {@link #setValues} with the
+ * {@link Ext.data.Record#data record data}.
+ * See also {@link #trackResetOnLoad}.
+ * @param {Record} record The record to load
+ * @return {BasicForm} this
+ */
+ loadRecord : function(record){
+ this.setValues(record.data);
+ return this;
+ },
+
+ // private
+ beforeAction : function(action){
+ var o = action.options;
+ if(o.waitMsg){
+ if(this.waitMsgTarget === true){
+ this.el.mask(o.waitMsg, 'x-mask-loading');
+ }else if(this.waitMsgTarget){
+ this.waitMsgTarget = Ext.get(this.waitMsgTarget);
+ this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
+ }else{
+ Ext.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
+ }
+ }
+ },
+
+ // private
+ afterAction : function(action, success){
+ this.activeAction = null;
+ var o = action.options;
+ if(o.waitMsg){
+ if(this.waitMsgTarget === true){
+ this.el.unmask();
+ }else if(this.waitMsgTarget){
+ this.waitMsgTarget.unmask();
+ }else{
+ Ext.MessageBox.updateProgress(1);
+ Ext.MessageBox.hide();
+ }
+ }
+ if(success){
+ if(o.reset){
+ this.reset();
+ }
+ Ext.callback(o.success, o.scope, [this, action]);
+ this.fireEvent('actioncomplete', this, action);
+ }else{
+ Ext.callback(o.failure, o.scope, [this, action]);
+ this.fireEvent('actionfailed', this, action);
+ }
+ },
+
+ /**
+ * Find a {@link Ext.form.Field} in this form.
+ * @param {String} id The value to search for (specify either a {@link Ext.Component#id id},
+ * {@link Ext.grid.Column#dataIndex dataIndex}, {@link Ext.form.Field#getName name or hiddenName}).
+ * @return Field
+ */
+ findField : function(id){
+ var field = this.items.get(id);
+ if(!Ext.isObject(field)){
+ this.items.each(function(f){
+ if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
+ field = f;
+ return false;
+ }
+ });
+ }
+ return field || null;
+ },
+
+
+ /**
+ * Mark fields in this form invalid in bulk.
+ * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
+ * @return {BasicForm} this
+ */
+ markInvalid : function(errors){
+ if(Ext.isArray(errors)){
+ for(var i = 0, len = errors.length; i < len; i++){
+ var fieldError = errors[i];
+ var f = this.findField(fieldError.id);
+ if(f){
+ f.markInvalid(fieldError.msg);
+ }
+ }
+ }else{
+ var field, id;
+ for(id in errors){
+ if(!Ext.isFunction(errors[id]) && (field = this.findField(id))){
+ field.markInvalid(errors[id]);
+ }
+ }
+ }
+ return this;
+ },
+
+ /**
+ * Set values for fields in this form in bulk.
+ * @param {Array/Object} values Either an array in the form:
+[{id:'clientName', value:'Fred. Olsen Lines'},
+ {id:'portOfLoading', value:'FXT'},
+ {id:'portOfDischarge', value:'OSL'} ]
+ * or an object hash of the form:
+{
+ clientName: 'Fred. Olsen Lines',
+ portOfLoading: 'FXT',
+ portOfDischarge: 'OSL'
+}
+ * @return {BasicForm} this
+ */
+ setValues : function(values){
+ if(Ext.isArray(values)){ // array of objects
+ for(var i = 0, len = values.length; i < len; i++){
+ var v = values[i];
+ var f = this.findField(v.id);
+ if(f){
+ f.setValue(v.value);
+ if(this.trackResetOnLoad){
+ f.originalValue = f.getValue();
+ }
+ }
+ }
+ }else{ // object hash
+ var field, id;
+ for(id in values){
+ if(!Ext.isFunction(values[id]) && (field = this.findField(id))){
+ field.setValue(values[id]);
+ if(this.trackResetOnLoad){
+ field.originalValue = field.getValue();
+ }
+ }
+ }
+ }
+ return this;
+ },
+
+ /**
+ * Returns the fields in this form as an object with key/value pairs as they would be submitted using a standard form submit. + * If multiple fields exist with the same name they are returned as an array.
+ *Note: The values are collected from all enabled HTML input elements within the form, not from + * the Ext Field objects. This means that all returned values are Strings (or Arrays of Strings) and that the + * value can potentially be the emptyText of a field.
+ * @param {Boolean} asString (optional) Pass true to return the values as a string. (defaults to false, returning an Object) + * @return {String/Object} + */ + getValues : function(asString){ + var fs = Ext.lib.Ajax.serializeForm(this.el.dom); + if(asString === true){ + return fs; + } + return Ext.urlDecode(fs); + }, + + getFieldValues : function(){ + var o = {}; + this.items.each(function(f){ + o[f.getName()] = f.getValue(); + }); + return o; + }, + + /** + * Clears all invalid messages in this form. + * @return {BasicForm} this + */ + clearInvalid : function(){ + this.items.each(function(f){ + f.clearInvalid(); + }); + return this; + }, + + /** + * Resets this form. + * @return {BasicForm} this + */ + reset : function(){ + this.items.each(function(f){ + f.reset(); + }); + return this; + }, + + /** + * Add Ext.form Components to this form's Collection. This does not result in rendering of + * the passed Component, it just enables the form to validate Fields, and distribute values to + * Fields. + *You will not usually call this function. In order to be rendered, a Field must be added + * to a {@link Ext.Container Container}, usually an {@link Ext.form.FormPanel FormPanel}. + * The FormPanel to which the field is added takes care of adding the Field to the BasicForm's + * collection.
+ * @param {Field} field1 + * @param {Field} field2 (optional) + * @param {Field} etc (optional) + * @return {BasicForm} this + */ + add : function(){ + this.items.addAll(Array.prototype.slice.call(arguments, 0)); + return this; + }, + + + /** + * Removes a field from the items collection (does NOT remove its markup). + * @param {Field} field + * @return {BasicForm} this + */ + remove : function(field){ + this.items.remove(field); + return this; + }, + + /** + * Iterates through the {@link Ext.form.Field Field}s which have been {@link #add add}ed to this BasicForm, + * checks them for an id attribute, and calls {@link Ext.form.Field#applyToMarkup} on the existing dom element with that id. + * @return {BasicForm} this + */ + render : function(){ + this.items.each(function(f){ + if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists + f.applyToMarkup(f.id); + } + }); + return this; + }, + + /** + * Calls {@link Ext#apply} for all fields in this form with the passed object. + * @param {Object} values + * @return {BasicForm} this + */ + applyToFields : function(o){ + this.items.each(function(f){ + Ext.apply(f, o); + }); + return this; + }, + + /** + * Calls {@link Ext#applyIf} for all field in this form with the passed object. + * @param {Object} values + * @return {BasicForm} this + */ + applyIfToFields : function(o){ + this.items.each(function(f){ + Ext.applyIf(f, o); + }); + return this; + }, + + callFieldMethod : function(fnName, args){ + args = args || []; + this.items.each(function(f){ + if(Ext.isFunction(f[fnName])){ + f[fnName].apply(f, args); + } + }); + return this; + } +}); + +// back compat +Ext.BasicForm = Ext.form.BasicForm;/** + * @class Ext.form.FormPanel + * @extends Ext.Panel + *Standard form container.
+ * + *Layout
+ *By default, FormPanel is configured with layout:'form' to use an {@link Ext.layout.FormLayout} + * layout manager, which styles and renders fields and labels correctly. When nesting additional Containers + * within a FormPanel, you should ensure that any descendant Containers which host input Fields use the + * {@link Ext.layout.FormLayout} layout manager.
+ * + *BasicForm
+ *Although not listed as configuration options of FormPanel, the FormPanel class accepts all + * of the config options required to configure its internal {@link Ext.form.BasicForm} for: + *
Note: If subclassing FormPanel, any configuration options for the BasicForm must be applied to + * the initialConfig property of the FormPanel. Applying {@link Ext.form.BasicForm BasicForm} + * configuration settings to this will not affect the BasicForm's configuration.
+ * + *Form Validation
+ *For information on form validation see the following:
+ *Form Submission
+ *By default, Ext Forms are submitted through Ajax, using {@link Ext.form.Action}. To enable normal browser + * submission of the {@link Ext.form.BasicForm BasicForm} contained in this FormPanel, see the + * {@link Ext.form.BasicForm#standardSubmit standardSubmit} option.
+ * + * @constructor + * @param {Object} config Configuration options + * @xtype form + */ +Ext.FormPanel = Ext.extend(Ext.Panel, { + /** + * @cfg {String} formId (optional) The id of the FORM tag (defaults to an auto-generated id). + */ + /** + * @cfg {Boolean} hideLabels + *true to hide field labels by default (sets display:none). Defaults to + * false.
+ *Also see {@link Ext.Component}.{@link Ext.Component#hideLabel hideLabel}.
+ */
+ /**
+ * @cfg {Number} labelPad
+ * The default padding in pixels for field labels (defaults to 5). labelPad only
+ * applies if {@link #labelWidth} is also specified, otherwise it will be ignored.
+ */
+ /**
+ * @cfg {String} labelSeparator
+ * See {@link Ext.Component}.{@link Ext.Component#labelSeparator labelSeparator}
+ */
+ /**
+ * @cfg {Number} labelWidth The width of labels in pixels. This property cascades to child containers
+ * and can be overridden on any child container (e.g., a fieldset can specify a different labelWidth
+ * for its fields) (defaults to 100).
+ */
+ /**
+ * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
+ */
+ /**
+ * @cfg {Array} buttons
+ * An array of {@link Ext.Button}s or {@link Ext.Button} configs used to add buttons to the footer of this FormPanel.
+ *
Buttons in the footer of a FormPanel may be configured with the option formBind: true. This causes + * the form's {@link #monitorValid valid state monitor task} to enable/disable those Buttons depending on + * the form's valid/invalid state.
+ */ + + + /** + * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75). + */ + minButtonWidth : 75, + + /** + * @cfg {String} labelAlign The label alignment value used for the text-align specification + * for the container. Valid values are "left", "top" or "right" + * (defaults to "left"). This property cascades to child containers and can be + * overridden on any child container (e.g., a fieldset can specify a different labelAlign + * for its fields). + */ + labelAlign : 'left', + + /** + * @cfg {Boolean} monitorValid If true, the form monitors its valid state client-side and + * regularly fires the {@link #clientvalidation} event passing that state.When monitoring valid state, the FormPanel enables/disables any of its configured
+ * {@link #buttons} which have been configured with formBind: true
depending
+ * on whether the {@link Ext.form.BasicForm#isValid form is valid} or not. Defaults to false
+var form = new Ext.FormPanel({
+ title: 'Simple Form with FieldSets',
+ labelWidth: 75, // label settings here cascade unless overridden
+ url: 'save-form.php',
+ frame:true,
+ bodyStyle:'padding:5px 5px 0',
+ width: 700,
+ renderTo: document.body,
+ layout:'column', // arrange items in columns
+ defaults: { // defaults applied to items
+ layout: 'form',
+ border: false,
+ bodyStyle: 'padding:4px'
+ },
+ items: [{
+ // Fieldset in Column 1
+ xtype:'fieldset',
+ columnWidth: 0.5,
+ title: 'Fieldset 1',
+ collapsible: true,
+ autoHeight:true,
+ defaults: {
+ anchor: '-20' // leave room for error icon
+ },
+ defaultType: 'textfield',
+ items :[{
+ fieldLabel: 'Field 1'
+ }, {
+ fieldLabel: 'Field 2'
+ }, {
+ fieldLabel: 'Field 3'
+ }
+ ]
+ },{
+ // Fieldset in Column 2 - Panel inside
+ xtype:'fieldset',
+ title: 'Show Panel', // title, header, or checkboxToggle creates fieldset header
+ autoHeight:true,
+ columnWidth: 0.5,
+ checkboxToggle: true,
+ collapsed: true, // fieldset initially collapsed
+ layout:'anchor',
+ items :[{
+ xtype: 'panel',
+ anchor: '100%',
+ title: 'Panel inside a fieldset',
+ frame: true,
+ height: 100
+ }]
+ }]
+});
+ *
+ * @constructor
+ * @param {Object} config Configuration options
+ * @xtype fieldset
+ */
+Ext.form.FieldSet = Ext.extend(Ext.Panel, {
+ /**
+ * @cfg {Mixed} checkboxToggle true to render a checkbox into the fieldset frame just
+ * in front of the legend to expand/collapse the fieldset when the checkbox is toggled. (defaults
+ * to false).
+ * A {@link Ext.DomHelper DomHelper} element spec may also be specified to create the checkbox. + * If true is specified, the default DomHelper config object used to create the element + * is:
+ * {tag: 'input', type: 'checkbox', name: this.checkboxName || this.id+'-checkbox'}
+ *
+ */
+ /**
+ * @cfg {String} checkboxName The name to assign to the fieldset's checkbox if {@link #checkboxToggle} = true
+ * (defaults to '[checkbox id]-checkbox').
+ */
+ /**
+ * @cfg {Boolean} collapsible
+ * true to make the fieldset collapsible and have the expand/collapse toggle button automatically
+ * rendered into the legend element, false to keep the fieldset statically sized with no collapse
+ * button (defaults to false). Another option is to configure {@link #checkboxToggle}.
+ */
+ /**
+ * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
+ */
+ /**
+ * @cfg {String} itemCls A css class to apply to the x-form-item of fields (see
+ * {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl} for details).
+ * This property cascades to child containers.
+ */
+ /**
+ * @cfg {String} baseCls The base CSS class applied to the fieldset (defaults to 'x-fieldset').
+ */
+ baseCls : 'x-fieldset',
+ /**
+ * @cfg {String} layout The {@link Ext.Container#layout} to use inside the fieldset (defaults to 'form').
+ */
+ layout : 'form',
+ /**
+ * @cfg {Boolean} animCollapse
+ * true to animate the transition when the panel is collapsed, false to skip the
+ * animation (defaults to false).
+ */
+ animCollapse : false,
+
+ // private
+ onRender : function(ct, position){
+ if(!this.el){
+ this.el = document.createElement('fieldset');
+ this.el.id = this.id;
+ if (this.title || this.header || this.checkboxToggle) {
+ this.el.appendChild(document.createElement('legend')).className = 'x-fieldset-header';
+ }
+ }
+
+ Ext.form.FieldSet.superclass.onRender.call(this, ct, position);
+
+ if(this.checkboxToggle){
+ var o = typeof this.checkboxToggle == 'object' ?
+ this.checkboxToggle :
+ {tag: 'input', type: 'checkbox', name: this.checkboxName || this.id+'-checkbox'};
+ this.checkbox = this.header.insertFirst(o);
+ this.checkbox.dom.checked = !this.collapsed;
+ this.mon(this.checkbox, 'click', this.onCheckClick, this);
+ }
+ },
+
+ // private
+ onCollapse : function(doAnim, animArg){
+ if(this.checkbox){
+ this.checkbox.dom.checked = false;
+ }
+ Ext.form.FieldSet.superclass.onCollapse.call(this, doAnim, animArg);
+
+ },
+
+ // private
+ onExpand : function(doAnim, animArg){
+ if(this.checkbox){
+ this.checkbox.dom.checked = true;
+ }
+ Ext.form.FieldSet.superclass.onExpand.call(this, doAnim, animArg);
+ },
+
+ /**
+ * This function is called by the fieldset's checkbox when it is toggled (only applies when
+ * checkboxToggle = true). This method should never be called externally, but can be
+ * overridden to provide custom behavior when the checkbox is toggled if needed.
+ */
+ onCheckClick : function(){
+ this[this.checkbox.dom.checked ? 'expand' : 'collapse']();
+ }
+
+ /**
+ * @cfg {String/Number} activeItem
+ * @hide
+ */
+ /**
+ * @cfg {Mixed} applyTo
+ * @hide
+ */
+ /**
+ * @cfg {Boolean} bodyBorder
+ * @hide
+ */
+ /**
+ * @cfg {Boolean} border
+ * @hide
+ */
+ /**
+ * @cfg {Boolean/Number} bufferResize
+ * @hide
+ */
+ /**
+ * @cfg {Boolean} collapseFirst
+ * @hide
+ */
+ /**
+ * @cfg {String} defaultType
+ * @hide
+ */
+ /**
+ * @cfg {String} disabledClass
+ * @hide
+ */
+ /**
+ * @cfg {String} elements
+ * @hide
+ */
+ /**
+ * @cfg {Boolean} floating
+ * @hide
+ */
+ /**
+ * @cfg {Boolean} footer
+ * @hide
+ */
+ /**
+ * @cfg {Boolean} frame
+ * @hide
+ */
+ /**
+ * @cfg {Boolean} header
+ * @hide
+ */
+ /**
+ * @cfg {Boolean} headerAsText
+ * @hide
+ */
+ /**
+ * @cfg {Boolean} hideCollapseTool
+ * @hide
+ */
+ /**
+ * @cfg {String} iconCls
+ * @hide
+ */
+ /**
+ * @cfg {Boolean/String} shadow
+ * @hide
+ */
+ /**
+ * @cfg {Number} shadowOffset
+ * @hide
+ */
+ /**
+ * @cfg {Boolean} shim
+ * @hide
+ */
+ /**
+ * @cfg {Object/Array} tbar
+ * @hide
+ */
+ /**
+ * @cfg {String} tabTip
+ * @hide
+ */
+ /**
+ * @cfg {Boolean} titleCollapse
+ * @hide
+ */
+ /**
+ * @cfg {Array} tools
+ * @hide
+ */
+ /**
+ * @cfg {Ext.Template/Ext.XTemplate} toolTemplate
+ * @hide
+ */
+ /**
+ * @cfg {String} xtype
+ * @hide
+ */
+ /**
+ * @property header
+ * @hide
+ */
+ /**
+ * @property footer
+ * @hide
+ */
+ /**
+ * @method focus
+ * @hide
+ */
+ /**
+ * @method getBottomToolbar
+ * @hide
+ */
+ /**
+ * @method getTopToolbar
+ * @hide
+ */
+ /**
+ * @method setIconClass
+ * @hide
+ */
+ /**
+ * @event activate
+ * @hide
+ */
+ /**
+ * @event beforeclose
+ * @hide
+ */
+ /**
+ * @event bodyresize
+ * @hide
+ */
+ /**
+ * @event close
+ * @hide
+ */
+ /**
+ * @event deactivate
+ * @hide
+ */
+});
+Ext.reg('fieldset', Ext.form.FieldSet);
+/**
+ * @class Ext.form.HtmlEditor
+ * @extends Ext.form.Field
+ * Provides a lightweight HTML Editor component. Some toolbar features are not supported by Safari and will be
+ * automatically hidden when needed. These are noted in the config options where appropriate.
+ *
+// Simple example rendered with default options:
+Ext.QuickTips.init(); // enable tooltips
+new Ext.form.HtmlEditor({
+ renderTo: Ext.getBody(),
+ width: 800,
+ height: 300
+});
+
+// Passed via xtype into a container and with custom options:
+Ext.QuickTips.init(); // enable tooltips
+new Ext.Panel({
+ title: 'HTML Editor',
+ renderTo: Ext.getBody(),
+ width: 600,
+ height: 300,
+ frame: true,
+ layout: 'fit',
+ items: {
+ xtype: 'htmleditor',
+ enableColors: false,
+ enableAlignments: false
+ }
+});
+
+ * @constructor
+ * Create a new HtmlEditor
+ * @param {Object} config
+ * @xtype htmleditor
+ */
+
+Ext.form.HtmlEditor = Ext.extend(Ext.form.Field, {
+ /**
+ * @cfg {Boolean} enableFormat Enable the bold, italic and underline buttons (defaults to true)
+ */
+ enableFormat : true,
+ /**
+ * @cfg {Boolean} enableFontSize Enable the increase/decrease font size buttons (defaults to true)
+ */
+ enableFontSize : true,
+ /**
+ * @cfg {Boolean} enableColors Enable the fore/highlight color buttons (defaults to true)
+ */
+ enableColors : true,
+ /**
+ * @cfg {Boolean} enableAlignments Enable the left, center, right alignment buttons (defaults to true)
+ */
+ enableAlignments : true,
+ /**
+ * @cfg {Boolean} enableLists Enable the bullet and numbered list buttons. Not available in Safari. (defaults to true)
+ */
+ enableLists : true,
+ /**
+ * @cfg {Boolean} enableSourceEdit Enable the switch to source edit button. Not available in Safari. (defaults to true)
+ */
+ enableSourceEdit : true,
+ /**
+ * @cfg {Boolean} enableLinks Enable the create link button. Not available in Safari. (defaults to true)
+ */
+ enableLinks : true,
+ /**
+ * @cfg {Boolean} enableFont Enable font selection. Not available in Safari. (defaults to true)
+ */
+ enableFont : true,
+ /**
+ * @cfg {String} createLinkText The default text for the create link prompt
+ */
+ createLinkText : 'Please enter the URL for the link:',
+ /**
+ * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
+ */
+ defaultLinkValue : 'http:/'+'/',
+ /**
+ * @cfg {Array} fontFamilies An array of available font families
+ */
+ fontFamilies : [
+ 'Arial',
+ 'Courier New',
+ 'Tahoma',
+ 'Times New Roman',
+ 'Verdana'
+ ],
+ defaultFont: 'tahoma',
+ /**
+ * @cfg {String} defaultValue A default value to be put into the editor to resolve focus issues (defaults to (Zero-width space), (Non-breaking space) in Opera and IE6).
+ */
+ defaultValue: (Ext.isOpera || Ext.isIE6) ? ' ' : '',
+
+ // private properties
+ actionMode: 'wrap',
+ validationEvent : false,
+ deferHeight: true,
+ initialized : false,
+ activated : false,
+ sourceEditMode : false,
+ onFocus : Ext.emptyFn,
+ iframePad:3,
+ hideMode:'offsets',
+ defaultAutoCreate : {
+ tag: "textarea",
+ style:"width:500px;height:300px;",
+ autocomplete: "off"
+ },
+
+ // private
+ initComponent : function(){
+ this.addEvents(
+ /**
+ * @event initialize
+ * Fires when the editor is fully initialized (including the iframe)
+ * @param {HtmlEditor} this
+ */
+ 'initialize',
+ /**
+ * @event activate
+ * Fires when the editor is first receives the focus. Any insertion must wait
+ * until after this event.
+ * @param {HtmlEditor} this
+ */
+ 'activate',
+ /**
+ * @event beforesync
+ * Fires before the textarea is updated with content from the editor iframe. Return false
+ * to cancel the sync.
+ * @param {HtmlEditor} this
+ * @param {String} html
+ */
+ 'beforesync',
+ /**
+ * @event beforepush
+ * Fires before the iframe editor is updated with content from the textarea. Return false
+ * to cancel the push.
+ * @param {HtmlEditor} this
+ * @param {String} html
+ */
+ 'beforepush',
+ /**
+ * @event sync
+ * Fires when the textarea is updated with content from the editor iframe.
+ * @param {HtmlEditor} this
+ * @param {String} html
+ */
+ 'sync',
+ /**
+ * @event push
+ * Fires when the iframe editor is updated with content from the textarea.
+ * @param {HtmlEditor} this
+ * @param {String} html
+ */
+ 'push',
+ /**
+ * @event editmodechange
+ * Fires when the editor switches edit modes
+ * @param {HtmlEditor} this
+ * @param {Boolean} sourceEdit True if source edit, false if standard editing.
+ */
+ 'editmodechange'
+ )
+ },
+
+ // private
+ createFontOptions : function(){
+ var buf = [], fs = this.fontFamilies, ff, lc;
+ for(var i = 0, len = fs.length; i< len; i++){
+ ff = fs[i];
+ lc = ff.toLowerCase();
+ buf.push(
+ ''
+ );
+ }
+ return buf.join('');
+ },
+
+ /*
+ * Protected method that will not generally be called directly. It
+ * is called when the editor creates its toolbar. Override this method if you need to
+ * add custom toolbar buttons.
+ * @param {HtmlEditor} editor
+ */
+ createToolbar : function(editor){
+
+ var tipsEnabled = Ext.QuickTips && Ext.QuickTips.isEnabled();
+
+ function btn(id, toggle, handler){
+ return {
+ itemId : id,
+ cls : 'x-btn-icon',
+ iconCls: 'x-edit-'+id,
+ enableToggle:toggle !== false,
+ scope: editor,
+ handler:handler||editor.relayBtnCmd,
+ clickEvent:'mousedown',
+ tooltip: tipsEnabled ? editor.buttonTips[id] || undefined : undefined,
+ overflowText: editor.buttonTips[id].title || undefined,
+ tabIndex:-1
+ };
+ }
+
+ // build the toolbar
+ var tb = new Ext.Toolbar({
+ renderTo:this.wrap.dom.firstChild
+ });
+
+ // stop form submits
+ this.mon(tb.el, 'click', function(e){
+ e.preventDefault();
+ });
+
+ if(this.enableFont && !Ext.isSafari2){
+ this.fontSelect = tb.el.createChild({
+ tag:'select',
+ cls:'x-font-select',
+ html: this.createFontOptions()
+ });
+ this.mon(this.fontSelect, 'change', function(){
+ var font = this.fontSelect.dom.value;
+ this.relayCmd('fontname', font);
+ this.deferFocus();
+ }, this);
+
+ tb.add(
+ this.fontSelect.dom,
+ '-'
+ );
+ }
+
+ if(this.enableFormat){
+ tb.add(
+ btn('bold'),
+ btn('italic'),
+ btn('underline')
+ );
+ }
+
+ if(this.enableFontSize){
+ tb.add(
+ '-',
+ btn('increasefontsize', false, this.adjustFont),
+ btn('decreasefontsize', false, this.adjustFont)
+ );
+ }
+
+ if(this.enableColors){
+ tb.add(
+ '-', {
+ itemId:'forecolor',
+ cls:'x-btn-icon',
+ iconCls: 'x-edit-forecolor',
+ clickEvent:'mousedown',
+ tooltip: tipsEnabled ? editor.buttonTips.forecolor || undefined : undefined,
+ tabIndex:-1,
+ menu : new Ext.menu.ColorMenu({
+ allowReselect: true,
+ focus: Ext.emptyFn,
+ value:'000000',
+ plain:true,
+ listeners: {
+ scope: this,
+ select: function(cp, color){
+ this.execCmd('forecolor', Ext.isWebKit || Ext.isIE ? '#'+color : color);
+ this.deferFocus();
+ }
+ },
+ clickEvent:'mousedown'
+ })
+ }, {
+ itemId:'backcolor',
+ cls:'x-btn-icon',
+ iconCls: 'x-edit-backcolor',
+ clickEvent:'mousedown',
+ tooltip: tipsEnabled ? editor.buttonTips.backcolor || undefined : undefined,
+ tabIndex:-1,
+ menu : new Ext.menu.ColorMenu({
+ focus: Ext.emptyFn,
+ value:'FFFFFF',
+ plain:true,
+ allowReselect: true,
+ listeners: {
+ scope: this,
+ select: function(cp, color){
+ if(Ext.isGecko){
+ this.execCmd('useCSS', false);
+ this.execCmd('hilitecolor', color);
+ this.execCmd('useCSS', true);
+ this.deferFocus();
+ }else{
+ this.execCmd(Ext.isOpera ? 'hilitecolor' : 'backcolor', Ext.isWebKit || Ext.isIE ? '#'+color : color);
+ this.deferFocus();
+ }
+ }
+ },
+ clickEvent:'mousedown'
+ })
+ }
+ );
+ }
+
+ if(this.enableAlignments){
+ tb.add(
+ '-',
+ btn('justifyleft'),
+ btn('justifycenter'),
+ btn('justifyright')
+ );
+ }
+
+ if(!Ext.isSafari2){
+ if(this.enableLinks){
+ tb.add(
+ '-',
+ btn('createlink', false, this.createLink)
+ );
+ }
+
+ if(this.enableLists){
+ tb.add(
+ '-',
+ btn('insertorderedlist'),
+ btn('insertunorderedlist')
+ );
+ }
+ if(this.enableSourceEdit){
+ tb.add(
+ '-',
+ btn('sourceedit', true, function(btn){
+ this.toggleSourceEdit(!this.sourceEditMode);
+ })
+ );
+ }
+ }
+
+ this.tb = tb;
+ },
+
+ /**
+ * Protected method that will not generally be called directly. It
+ * is called when the editor initializes the iframe with HTML contents. Override this method if you
+ * want to change the initialization markup of the iframe (e.g. to add stylesheets).
+ */
+ getDocMarkup : function(){
+ return '';
+ },
+
+ // private
+ getEditorBody : function(){
+ return this.doc.body || this.doc.documentElement;
+ },
+
+ // private
+ getDoc : function(){
+ return Ext.isIE ? this.getWin().document : (this.iframe.contentDocument || this.getWin().document);
+ },
+
+ // private
+ getWin : function(){
+ return Ext.isIE ? this.iframe.contentWindow : window.frames[this.iframe.name];
+ },
+
+ // private
+ onRender : function(ct, position){
+ Ext.form.HtmlEditor.superclass.onRender.call(this, ct, position);
+ this.el.dom.style.border = '0 none';
+ this.el.dom.setAttribute('tabIndex', -1);
+ this.el.addClass('x-hidden');
+ if(Ext.isIE){ // fix IE 1px bogus margin
+ this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
+ }
+ this.wrap = this.el.wrap({
+ cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
+ });
+
+ this.createToolbar(this);
+
+ this.disableItems(true);
+ // is this needed?
+ // this.tb.doLayout();
+
+ this.createIFrame();
+
+ if(!this.width){
+ var sz = this.el.getSize();
+ this.setSize(sz.width, this.height || sz.height);
+ }
+ },
+
+ createIFrame: function(){
+ var iframe = document.createElement('iframe');
+ iframe.name = Ext.id();
+ iframe.frameBorder = '0';
+ iframe.src = Ext.isIE ? Ext.SSL_SECURE_URL : "javascript:;";
+ this.wrap.dom.appendChild(iframe);
+
+ this.iframe = iframe;
+
+ this.monitorTask = Ext.TaskMgr.start({
+ run: this.checkDesignMode,
+ scope: this,
+ interval:100
+ });
+ },
+
+ initFrame : function(){
+ Ext.TaskMgr.stop(this.monitorTask);
+ this.doc = this.getDoc();
+ this.win = this.getWin();
+
+ this.doc.open();
+ this.doc.write(this.getDocMarkup());
+ this.doc.close();
+
+ var task = { // must defer to wait for browser to be ready
+ run : function(){
+ if(this.doc.body || this.doc.readyState == 'complete'){
+ Ext.TaskMgr.stop(task);
+ this.doc.designMode="on";
+ this.initEditor.defer(10, this);
+ }
+ },
+ interval : 10,
+ duration:10000,
+ scope: this
+ };
+ Ext.TaskMgr.start(task);
+ },
+
+
+ checkDesignMode : function(){
+ if(this.wrap && this.wrap.dom.offsetWidth){
+ var doc = this.getDoc();
+ if(!doc){
+ return;
+ }
+ if(!doc.editorInitialized || String(doc.designMode).toLowerCase() != 'on'){
+ this.initFrame();
+ }
+ }
+ },
+
+ disableItems: function(disabled){
+ if(this.fontSelect){
+ this.fontSelect.dom.disabled = disabled;
+ }
+ this.tb.items.each(function(item){
+ if(item.itemId != 'sourceedit'){
+ item.setDisabled(disabled);
+ }
+ });
+ },
+
+ // private
+ onResize : function(w, h){
+ Ext.form.HtmlEditor.superclass.onResize.apply(this, arguments);
+ if(this.el && this.iframe){
+ if(typeof w == 'number'){
+ var aw = w - this.wrap.getFrameWidth('lr');
+ this.el.setWidth(this.adjustWidth('textarea', aw));
+ this.tb.setWidth(aw);
+ this.iframe.style.width = Math.max(aw, 0) + 'px';
+ }
+ if(typeof h == 'number'){
+ var ah = h - this.wrap.getFrameWidth('tb') - this.tb.el.getHeight();
+ this.el.setHeight(this.adjustWidth('textarea', ah));
+ this.iframe.style.height = Math.max(ah, 0) + 'px';
+ if(this.doc){
+ this.getEditorBody().style.height = Math.max((ah - (this.iframePad*2)), 0) + 'px';
+ }
+ }
+ }
+ },
+
+ /**
+ * Toggles the editor between standard and source edit mode.
+ * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
+ */
+ toggleSourceEdit : function(sourceEditMode){
+ if(sourceEditMode === undefined){
+ sourceEditMode = !this.sourceEditMode;
+ }
+ this.sourceEditMode = sourceEditMode === true;
+ var btn = this.tb.items.get('sourceedit');
+ if(btn.pressed !== this.sourceEditMode){
+ btn.toggle(this.sourceEditMode);
+ if(!btn.xtbHidden){
+ return;
+ }
+ }
+ if(this.sourceEditMode){
+ this.disableItems(true);
+ this.syncValue();
+ this.iframe.className = 'x-hidden';
+ this.el.removeClass('x-hidden');
+ this.el.dom.removeAttribute('tabIndex');
+ this.el.focus();
+ }else{
+ if(this.initialized){
+ this.disableItems(false);
+ }
+ this.pushValue();
+ this.iframe.className = '';
+ this.el.addClass('x-hidden');
+ this.el.dom.setAttribute('tabIndex', -1);
+ this.deferFocus();
+ }
+ var lastSize = this.lastSize;
+ if(lastSize){
+ delete this.lastSize;
+ this.setSize(lastSize);
+ }
+ this.fireEvent('editmodechange', this, this.sourceEditMode);
+ },
+
+ // private used internally
+ createLink : function(){
+ var url = prompt(this.createLinkText, this.defaultLinkValue);
+ if(url && url != 'http:/'+'/'){
+ this.relayCmd('createlink', url);
+ }
+ },
+
+ // private (for BoxComponent)
+ adjustSize : Ext.BoxComponent.prototype.adjustSize,
+
+ // private (for BoxComponent)
+ getResizeEl : function(){
+ return this.wrap;
+ },
+
+ // private (for BoxComponent)
+ getPositionEl : function(){
+ return this.wrap;
+ },
+
+ // private
+ initEvents : function(){
+ this.originalValue = this.getValue();
+ },
+
+ /**
+ * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
+ * @method
+ */
+ markInvalid : Ext.emptyFn,
+
+ /**
+ * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
+ * @method
+ */
+ clearInvalid : Ext.emptyFn,
+
+ // docs inherit from Field
+ setValue : function(v){
+ Ext.form.HtmlEditor.superclass.setValue.call(this, v);
+ this.pushValue();
+ return this;
+ },
+
+ /**
+ * Protected method that will not generally be called directly. If you need/want
+ * custom HTML cleanup, this is the method you should override.
+ * @param {String} html The HTML to be cleaned
+ * @return {String} The cleaned HTML
+ */
+ cleanHtml : function(html){
+ html = String(html);
+ if(html.length > 5){
+ if(Ext.isWebKit){ // strip safari nonsense
+ html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
+ }
+ }
+ if(html == this.defaultValue){
+ html = '';
+ }
+ return html;
+ },
+
+ /**
+ * Protected method that will not generally be called directly. Syncs the contents
+ * of the editor iframe with the textarea.
+ */
+ syncValue : function(){
+ if(this.initialized){
+ var bd = this.getEditorBody();
+ var html = bd.innerHTML;
+ if(Ext.isWebKit){
+ var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
+ var m = bs.match(/text-align:(.*?);/i);
+ if(m && m[1]){
+ html = '
+{
+ bold : {
+ title: 'Bold (Ctrl+B)',
+ text: 'Make the selected text bold.',
+ cls: 'x-html-editor-tip'
+ },
+ italic : {
+ title: 'Italic (Ctrl+I)',
+ text: 'Make the selected text italic.',
+ cls: 'x-html-editor-tip'
+ },
+ ...
+
+ * @type Object
+ */
+ buttonTips : {
+ bold : {
+ title: 'Bold (Ctrl+B)',
+ text: 'Make the selected text bold.',
+ cls: 'x-html-editor-tip'
+ },
+ italic : {
+ title: 'Italic (Ctrl+I)',
+ text: 'Make the selected text italic.',
+ cls: 'x-html-editor-tip'
+ },
+ underline : {
+ title: 'Underline (Ctrl+U)',
+ text: 'Underline the selected text.',
+ cls: 'x-html-editor-tip'
+ },
+ increasefontsize : {
+ title: 'Grow Text',
+ text: 'Increase the font size.',
+ cls: 'x-html-editor-tip'
+ },
+ decreasefontsize : {
+ title: 'Shrink Text',
+ text: 'Decrease the font size.',
+ cls: 'x-html-editor-tip'
+ },
+ backcolor : {
+ title: 'Text Highlight Color',
+ text: 'Change the background color of the selected text.',
+ cls: 'x-html-editor-tip'
+ },
+ forecolor : {
+ title: 'Font Color',
+ text: 'Change the color of the selected text.',
+ cls: 'x-html-editor-tip'
+ },
+ justifyleft : {
+ title: 'Align Text Left',
+ text: 'Align text to the left.',
+ cls: 'x-html-editor-tip'
+ },
+ justifycenter : {
+ title: 'Center Text',
+ text: 'Center text in the editor.',
+ cls: 'x-html-editor-tip'
+ },
+ justifyright : {
+ title: 'Align Text Right',
+ text: 'Align text to the right.',
+ cls: 'x-html-editor-tip'
+ },
+ insertunorderedlist : {
+ title: 'Bullet List',
+ text: 'Start a bulleted list.',
+ cls: 'x-html-editor-tip'
+ },
+ insertorderedlist : {
+ title: 'Numbered List',
+ text: 'Start a numbered list.',
+ cls: 'x-html-editor-tip'
+ },
+ createlink : {
+ title: 'Hyperlink',
+ text: 'Make the selected text a hyperlink.',
+ cls: 'x-html-editor-tip'
+ },
+ sourceedit : {
+ title: 'Source Edit',
+ text: 'Switch to source editing mode.',
+ cls: 'x-html-editor-tip'
+ }
+ }
+
+ // hide stuff that is not compatible
+ /**
+ * @event blur
+ * @hide
+ */
+ /**
+ * @event change
+ * @hide
+ */
+ /**
+ * @event focus
+ * @hide
+ */
+ /**
+ * @event specialkey
+ * @hide
+ */
+ /**
+ * @cfg {String} fieldClass @hide
+ */
+ /**
+ * @cfg {String} focusClass @hide
+ */
+ /**
+ * @cfg {String} autoCreate @hide
+ */
+ /**
+ * @cfg {String} inputType @hide
+ */
+ /**
+ * @cfg {String} invalidClass @hide
+ */
+ /**
+ * @cfg {String} invalidText @hide
+ */
+ /**
+ * @cfg {String} msgFx @hide
+ */
+ /**
+ * @cfg {String} validateOnBlur @hide
+ */
+ /**
+ * @cfg {Boolean} allowDomMove @hide
+ */
+ /**
+ * @cfg {String} applyTo @hide
+ */
+ /**
+ * @cfg {String} autoHeight @hide
+ */
+ /**
+ * @cfg {String} autoWidth @hide
+ */
+ /**
+ * @cfg {String} cls @hide
+ */
+ /**
+ * @cfg {String} disabled @hide
+ */
+ /**
+ * @cfg {String} disabledClass @hide
+ */
+ /**
+ * @cfg {String} msgTarget @hide
+ */
+ /**
+ * @cfg {String} readOnly @hide
+ */
+ /**
+ * @cfg {String} style @hide
+ */
+ /**
+ * @cfg {String} validationDelay @hide
+ */
+ /**
+ * @cfg {String} validationEvent @hide
+ */
+ /**
+ * @cfg {String} tabIndex @hide
+ */
+ /**
+ * @property disabled
+ * @hide
+ */
+ /**
+ * @method applyToMarkup
+ * @hide
+ */
+ /**
+ * @method disable
+ * @hide
+ */
+ /**
+ * @method enable
+ * @hide
+ */
+ /**
+ * @method validate
+ * @hide
+ */
+ /**
+ * @event valid
+ * @hide
+ */
+ /**
+ * @method setDisabled
+ * @hide
+ */
+ /**
+ * @cfg keys
+ * @hide
+ */
+});
+Ext.reg('htmleditor', Ext.form.HtmlEditor);/**
+ * @class Ext.form.TimeField
+ * @extends Ext.form.ComboBox
+ * Provides a time input field with a time dropdown and automatic time validation. Example usage:
+ *
+new Ext.form.TimeField({
+ minValue: '9:00 AM',
+ maxValue: '6:00 PM',
+ increment: 30
+});
+
+ * @constructor
+ * Create a new TimeField
+ * @param {Object} config
+ * @xtype timefield
+ */
+Ext.form.TimeField = Ext.extend(Ext.form.ComboBox, {
+ /**
+ * @cfg {Date/String} minValue
+ * The minimum allowed time. Can be either a Javascript date object with a valid time value or a string
+ * time in a valid format -- see {@link #format} and {@link #altFormats} (defaults to null).
+ */
+ minValue : null,
+ /**
+ * @cfg {Date/String} maxValue
+ * The maximum allowed time. Can be either a Javascript date object with a valid time value or a string
+ * time in a valid format -- see {@link #format} and {@link #altFormats} (defaults to null).
+ */
+ maxValue : null,
+ /**
+ * @cfg {String} minText
+ * The error text to display when the date in the cell is before minValue (defaults to
+ * 'The time in this field must be equal to or after {0}').
+ */
+ minText : "The time in this field must be equal to or after {0}",
+ /**
+ * @cfg {String} maxText
+ * The error text to display when the time is after maxValue (defaults to
+ * 'The time in this field must be equal to or before {0}').
+ */
+ maxText : "The time in this field must be equal to or before {0}",
+ /**
+ * @cfg {String} invalidText
+ * The error text to display when the time in the field is invalid (defaults to
+ * '{value} is not a valid time').
+ */
+ invalidText : "{0} is not a valid time",
+ /**
+ * @cfg {String} format
+ * The default time format string which can be overriden for localization support. The format must be
+ * valid according to {@link Date#parseDate} (defaults to 'g:i A', e.g., '3:15 PM'). For 24-hour time
+ * format try 'H:i' instead.
+ */
+ format : "g:i A",
+ /**
+ * @cfg {String} altFormats
+ * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
+ * format (defaults to 'g:ia|g:iA|g:i a|g:i A|h:i|g:i|H:i|ga|ha|gA|h a|g a|g A|gi|hi|gia|hia|g|H').
+ */
+ altFormats : "g:ia|g:iA|g:i a|g:i A|h:i|g:i|H:i|ga|ha|gA|h a|g a|g A|gi|hi|gia|hia|g|H",
+ /**
+ * @cfg {Number} increment
+ * The number of minutes between each time value in the list (defaults to 15).
+ */
+ increment: 15,
+
+ // private override
+ mode: 'local',
+ // private override
+ triggerAction: 'all',
+ // private override
+ typeAhead: false,
+
+ // private - This is the date to use when generating time values in the absence of either minValue
+ // or maxValue. Using the current date causes DST issues on DST boundary dates, so this is an
+ // arbitrary "safe" date that can be any date aside from DST boundary dates.
+ initDate: '1/1/2008',
+
+ // private
+ initComponent : function(){
+ if(typeof this.minValue == "string"){
+ this.minValue = this.parseDate(this.minValue);
+ }
+ if(typeof this.maxValue == "string"){
+ this.maxValue = this.parseDate(this.maxValue);
+ }
+
+ if(!this.store){
+ var min = this.parseDate(this.minValue) || new Date(this.initDate).clearTime();
+ var max = this.parseDate(this.maxValue) || new Date(this.initDate).clearTime().add('mi', (24 * 60) - 1);
+ var times = [];
+ while(min <= max){
+ times.push(min.dateFormat(this.format));
+ min = min.add('mi', this.increment);
+ }
+ this.store = times;
+ }
+ Ext.form.TimeField.superclass.initComponent.call(this);
+ },
+
+ // inherited docs
+ getValue : function(){
+ var v = Ext.form.TimeField.superclass.getValue.call(this);
+ return this.formatDate(this.parseDate(v)) || '';
+ },
+
+ // inherited docs
+ setValue : function(value){
+ return Ext.form.TimeField.superclass.setValue.call(this, this.formatDate(this.parseDate(value)));
+ },
+
+ // private overrides
+ validateValue : Ext.form.DateField.prototype.validateValue,
+ parseDate : Ext.form.DateField.prototype.parseDate,
+ formatDate : Ext.form.DateField.prototype.formatDate,
+
+ // private
+ beforeBlur : function(){
+ var v = this.parseDate(this.getRawValue());
+ if(v){
+ this.setValue(v.dateFormat(this.format));
+ }
+ Ext.form.TimeField.superclass.beforeBlur.call(this);
+ }
+
+ /**
+ * @cfg {Boolean} grow @hide
+ */
+ /**
+ * @cfg {Number} growMin @hide
+ */
+ /**
+ * @cfg {Number} growMax @hide
+ */
+ /**
+ * @hide
+ * @method autoSize
+ */
+});
+Ext.reg('timefield', Ext.form.TimeField);/**
+ * @class Ext.form.Label
+ * @extends Ext.BoxComponent
+ * Basic Label field.
+ * @constructor
+ * Creates a new Label
+ * @param {Ext.Element/String/Object} config The configuration options. If an element is passed, it is set as the internal
+ * element and its id used as the component id. If a string is passed, it is assumed to be the id of an existing element
+ * and is used as the component id. Otherwise, it is assumed to be a standard config object and is applied to the component.
+ * @xtype label
+ */
+Ext.form.Label = Ext.extend(Ext.BoxComponent, {
+ /**
+ * @cfg {String} text The plain text to display within the label (defaults to ''). If you need to include HTML
+ * tags within the label's innerHTML, use the {@link #html} config instead.
+ */
+ /**
+ * @cfg {String} forId The id of the input element to which this label will be bound via the standard HTML 'for'
+ * attribute. If not specified, the attribute will not be added to the label.
+ */
+ /**
+ * @cfg {String} html An HTML fragment that will be used as the label's innerHTML (defaults to '').
+ * Note that if {@link #text} is specified it will take precedence and this value will be ignored.
+ */
+
+ // private
+ onRender : function(ct, position){
+ if(!this.el){
+ this.el = document.createElement('label');
+ this.el.id = this.getId();
+ this.el.innerHTML = this.text ? Ext.util.Format.htmlEncode(this.text) : (this.html || '');
+ if(this.forId){
+ this.el.setAttribute('for', this.forId);
+ }
+ }
+ Ext.form.Label.superclass.onRender.call(this, ct, position);
+ },
+
+ /**
+ * Updates the label's innerHTML with the specified string.
+ * @param {String} text The new label text
+ * @param {Boolean} encode (optional) False to skip HTML-encoding the text when rendering it
+ * to the label (defaults to true which encodes the value). This might be useful if you want to include
+ * tags in the label's innerHTML rather than rendering them as string literals per the default logic.
+ * @return {Label} this
+ */
+ setText : function(t, encode){
+ var e = encode === false;
+ this[!e ? 'text' : 'html'] = t;
+ delete this[e ? 'text' : 'html'];
+ if(this.rendered){
+ this.el.dom.innerHTML = encode !== false ? Ext.util.Format.htmlEncode(t) : t;
+ }
+ return this;
+ }
+});
+
+Ext.reg('label', Ext.form.Label);/**
+ * @class Ext.form.Action
+ * The subclasses of this class provide actions to perform upon {@link Ext.form.BasicForm Form}s.
+ *Instances of this class are only created by a {@link Ext.form.BasicForm Form} when + * the Form needs to perform an action such as submit or load. The Configuration options + * listed for this class are set through the Form's action methods: {@link Ext.form.BasicForm#submit submit}, + * {@link Ext.form.BasicForm#load load} and {@link Ext.form.BasicForm#doAction doAction}
+ *The instance of Action which performed the action is passed to the success + * and failure callbacks of the Form's action methods ({@link Ext.form.BasicForm#submit submit}, + * {@link Ext.form.BasicForm#load load} and {@link Ext.form.BasicForm#doAction doAction}), + * and to the {@link Ext.form.BasicForm#actioncomplete actioncomplete} and + * {@link Ext.form.BasicForm#actionfailed actionfailed} event handlers.
+ */ +Ext.form.Action = function(form, options){ + this.form = form; + this.options = options || {}; +}; + +/** + * Failure type returned when client side validation of the Form fails + * thus aborting a submit action. Client side validation is performed unless + * {@link #clientValidation} is explicitly set to false. + * @type {String} + * @static + */ +Ext.form.Action.CLIENT_INVALID = 'client'; +/** + *Failure type returned when server side processing fails and the {@link #result}'s + * success property is set to false.
+ *In the case of a form submission, field-specific error messages may be returned in the + * {@link #result}'s errors property.
+ * @type {String} + * @static + */ +Ext.form.Action.SERVER_INVALID = 'server'; +/** + * Failure type returned when a communication error happens when attempting + * to send a request to the remote server. The {@link #response} may be examined to + * provide further information. + * @type {String} + * @static + */ +Ext.form.Action.CONNECT_FAILURE = 'connect'; +/** + * Failure type returned when the response's success + * property is set to false, or no field values are returned in the response's + * data property. + * @type {String} + * @static + */ +Ext.form.Action.LOAD_FAILURE = 'load'; + +Ext.form.Action.prototype = { +/** + * @cfg {String} url The URL that the Action is to invoke. + */ +/** + * @cfg {Boolean} reset When set to true, causes the Form to be + * {@link Ext.form.BasicForm.reset reset} on Action success. If specified, this happens + * before the {@link #success} callback is called and before the Form's + * {@link Ext.form.BasicForm.actioncomplete actioncomplete} event fires. + */ +/** + * @cfg {String} method The HTTP method to use to access the requested URL. Defaults to the + * {@link Ext.form.BasicForm}'s method, or if that is not specified, the underlying DOM form's method. + */ +/** + * @cfg {Mixed} paramsExtra parameter values to pass. These are added to the Form's + * {@link Ext.form.BasicForm#baseParams} and passed to the specified URL along with the Form's + * input fields.
+ *Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.
+ */ +/** + * @cfg {Number} timeout The number of seconds to wait for a server response before + * failing with the {@link #failureType} as {@link #Action.CONNECT_FAILURE}. If not specified, + * defaults to the configured {@link Ext.form.BasicForm#timeout timeout} of the + * {@link Ext.form.BasicForm form}. + */ +/** + * @cfg {Function} success The function to call when a valid success return packet is recieved. + * The function is passed the following parameters:
+var fp = new Ext.form.FormPanel({
+...
+buttons: [{
+ text: 'Save',
+ formBind: true,
+ handler: function(){
+ if(fp.getForm().isValid()){
+ fp.getForm().submit({
+ url: 'form-submit.php',
+ waitMsg: 'Submitting your data...',
+ success: function(form, action){
+ // server responded with success = true
+ var result = action.{@link #result};
+ },
+ failure: function(form, action){
+ if (action.{@link #failureType} === Ext.form.Action.{@link #CONNECT_FAILURE}) {
+ Ext.Msg.alert('Error',
+ 'Status:'+action.{@link #response}.status+': '+
+ action.{@link #response}.statusText);
+ }
+ if (action.failureType === Ext.form.Action.{@link #SERVER_INVALID}){
+ // server responded with success = false
+ Ext.Msg.alert('Invalid', action.{@link #result}.errormsg);
+ }
+ }
+ });
+ }
+ }
+},{
+ text: 'Reset',
+ handler: function(){
+ fp.getForm().reset();
+ }
+}]
+ *
+ * @property failureType
+ * @type {String}
+ */
+ /**
+ * The XMLHttpRequest object used to perform the action.
+ * @property response
+ * @type {Object}
+ */
+ /**
+ * The decoded response object containing a boolean success property and
+ * other, action-specific properties.
+ * @property result
+ * @type {Object}
+ */
+
+ // interface method
+ run : function(options){
+
+ },
+
+ // interface method
+ success : function(response){
+
+ },
+
+ // interface method
+ handleResponse : function(response){
+
+ },
+
+ // default connection failure
+ failure : function(response){
+ this.response = response;
+ this.failureType = Ext.form.Action.CONNECT_FAILURE;
+ this.form.afterAction(this, false);
+ },
+
+ // private
+ // shared code among all Actions to validate that there was a response
+ // with either responseText or responseXml
+ processResponse : function(response){
+ this.response = response;
+ if(!response.responseText && !response.responseXML){
+ return true;
+ }
+ this.result = this.handleResponse(response);
+ return this.result;
+ },
+
+ // utility functions used internally
+ getUrl : function(appendParams){
+ var url = this.options.url || this.form.url || this.form.el.dom.action;
+ if(appendParams){
+ var p = this.getParams();
+ if(p){
+ url = Ext.urlAppend(url, p);
+ }
+ }
+ return url;
+ },
+
+ // private
+ getMethod : function(){
+ return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
+ },
+
+ // private
+ getParams : function(){
+ var bp = this.form.baseParams;
+ var p = this.options.params;
+ if(p){
+ if(typeof p == "object"){
+ p = Ext.urlEncode(Ext.applyIf(p, bp));
+ }else if(typeof p == 'string' && bp){
+ p += '&' + Ext.urlEncode(bp);
+ }
+ }else if(bp){
+ p = Ext.urlEncode(bp);
+ }
+ return p;
+ },
+
+ // private
+ createCallback : function(opts){
+ var opts = opts || {};
+ return {
+ success: this.success,
+ failure: this.failure,
+ scope: this,
+ timeout: (opts.timeout*1000) || (this.form.timeout*1000),
+ upload: this.form.fileUpload ? this.success : undefined
+ };
+ }
+};
+
+/**
+ * @class Ext.form.Action.Submit
+ * @extends Ext.form.Action
+ * A class which handles submission of data from {@link Ext.form.BasicForm Form}s + * and processes the returned response.
+ *Instances of this class are only created by a {@link Ext.form.BasicForm Form} when + * {@link Ext.form.BasicForm#submit submit}ting.
+ *Response Packet Criteria
+ *A response packet may contain: + *
success
property : Boolean
+ * success
property is required.errors
property : Object
+ * errors
property,
+ * which is optional, contains error messages for invalid fields.JSON Packets
+ *By default, response packets are assumed to be JSON, so a typical response + * packet may look like this:
+{
+ success: false,
+ errors: {
+ clientCode: "Client not found",
+ portOfLoading: "This field must not be null"
+ }
+}
+ * Other data may be placed into the response for processing by the {@link Ext.form.BasicForm}'s callback + * or event handler methods. The object decoded from this JSON is available in the + * {@link Ext.form.Action#result result} property.
+ *Alternatively, if an {@link #errorReader} is specified as an {@link Ext.data.XmlReader XmlReader}:
+ errorReader: new Ext.data.XmlReader({
+ record : 'field',
+ success: '@success'
+ }, [
+ 'id', 'msg'
+ ]
+ )
+
+ * then the results may be sent back in XML format:
+<?xml version="1.0" encoding="UTF-8"?>
+<message success="false">
+<errors>
+ <field>
+ <id>clientCode</id>
+ <msg><![CDATA[Code not found. <br /><i>This is a test validation message from the server </i>]]></msg>
+ </field>
+ <field>
+ <id>portOfLoading</id>
+ <msg><![CDATA[Port not found. <br /><i>This is a test validation message from the server </i>]]></msg>
+ </field>
+</errors>
+</message>
+
+ * Other elements may be placed into the response XML for processing by the {@link Ext.form.BasicForm}'s callback + * or event handler methods. The XML document is available in the {@link #errorReader}'s {@link Ext.data.XmlReader#xmlData xmlData} property.
+ */ +Ext.form.Action.Submit = function(form, options){ + Ext.form.Action.Submit.superclass.constructor.call(this, form, options); +}; + +Ext.extend(Ext.form.Action.Submit, Ext.form.Action, { + /** + * @cfg {Ext.data.DataReader} errorReaderOptional. JSON is interpreted with + * no need for an errorReader.
+ *A Reader which reads a single record from the returned data. The DataReader's + * success property specifies how submission success is determined. The Record's + * data provides the error messages to apply to any invalid form Fields.
+ */ + /** + * @cfg {boolean} clientValidation Determines whether a Form's fields are validated + * in a final call to {@link Ext.form.BasicForm#isValid isValid} prior to submission. + * Pass false in the Form's submit options to prevent this. If not defined, pre-submission field validation + * is performed. + */ + type : 'submit', + + // private + run : function(){ + var o = this.options; + var method = this.getMethod(); + var isGet = method == 'GET'; + if(o.clientValidation === false || this.form.isValid()){ + Ext.Ajax.request(Ext.apply(this.createCallback(o), { + form:this.form.el.dom, + url:this.getUrl(isGet), + method: method, + headers: o.headers, + params:!isGet ? this.getParams() : null, + isUpload: this.form.fileUpload + })); + }else if (o.clientValidation !== false){ // client validation failed + this.failureType = Ext.form.Action.CLIENT_INVALID; + this.form.afterAction(this, false); + } + }, + + // private + success : function(response){ + var result = this.processResponse(response); + if(result === true || result.success){ + this.form.afterAction(this, true); + return; + } + if(result.errors){ + this.form.markInvalid(result.errors); + this.failureType = Ext.form.Action.SERVER_INVALID; + } + this.form.afterAction(this, false); + }, + + // private + handleResponse : function(response){ + if(this.form.errorReader){ + var rs = this.form.errorReader.read(response); + var errors = []; + if(rs.records){ + for(var i = 0, len = rs.records.length; i < len; i++) { + var r = rs.records[i]; + errors[i] = r.data; + } + } + if(errors.length < 1){ + errors = null; + } + return { + success : rs.success, + errors : errors + }; + } + return Ext.decode(response.responseText); + } +}); + + +/** + * @class Ext.form.Action.Load + * @extends Ext.form.Action + *A class which handles loading of data from a server into the Fields of an {@link Ext.form.BasicForm}.
+ *Instances of this class are only created by a {@link Ext.form.BasicForm Form} when + * {@link Ext.form.BasicForm#load load}ing.
+ *Response Packet Criteria
+ *A response packet must contain: + *
success
property : Booleandata
property : Objectdata
property contains the values of Fields to load.
+ * The individual value object for each Field is passed to the Field's
+ * {@link Ext.form.Field#setValue setValue} method.JSON Packets
+ *By default, response packets are assumed to be JSON, so for the following form load call:
+var myFormPanel = new Ext.form.FormPanel({
+ title: 'Client and routing info',
+ items: [{
+ fieldLabel: 'Client',
+ name: 'clientName'
+ }, {
+ fieldLabel: 'Port of loading',
+ name: 'portOfLoading'
+ }, {
+ fieldLabel: 'Port of discharge',
+ name: 'portOfDischarge'
+ }]
+});
+myFormPanel.{@link Ext.form.FormPanel#getForm getForm}().{@link Ext.form.BasicForm#load load}({
+ url: '/getRoutingInfo.php',
+ params: {
+ consignmentRef: myConsignmentRef
+ },
+ failure: function(form, action() {
+ Ext.Msg.alert("Load failed", action.result.errorMessage);
+ }
+});
+
+ * a success response packet may look like this:
+{
+ success: true,
+ data: {
+ clientName: "Fred. Olsen Lines",
+ portOfLoading: "FXT",
+ portOfDischarge: "OSL"
+ }
+}
+ * while a failure response packet may look like this:
+{
+ success: false,
+ errorMessage: "Consignment reference not found"
+}
+ * Other data may be placed into the response for processing the {@link Ext.form.BasicForm Form}'s + * callback or event handler methods. The object decoded from this JSON is available in the + * {@link Ext.form.Action#result result} property.
+ */ +Ext.form.Action.Load = function(form, options){ + Ext.form.Action.Load.superclass.constructor.call(this, form, options); + this.reader = this.form.reader; +}; + +Ext.extend(Ext.form.Action.Load, Ext.form.Action, { + // private + type : 'load', + + // private + run : function(){ + Ext.Ajax.request(Ext.apply( + this.createCallback(this.options), { + method:this.getMethod(), + url:this.getUrl(false), + headers: this.options.headers, + params:this.getParams() + })); + }, + + // private + success : function(response){ + var result = this.processResponse(response); + if(result === true || !result.success || !result.data){ + this.failureType = Ext.form.Action.LOAD_FAILURE; + this.form.afterAction(this, false); + return; + } + this.form.clearInvalid(); + this.form.setValues(result.data); + this.form.afterAction(this, true); + }, + + // private + handleResponse : function(response){ + if(this.form.reader){ + var rs = this.form.reader.read(response); + var data = rs.records && rs.records[0] ? rs.records[0].data : null; + return { + success : rs.success, + data : data + }; + } + return Ext.decode(response.responseText); + } +}); + + + +/** + * @class Ext.form.Action.DirectLoad + * @extends Ext.form.Action.Load + * Provides Ext.direct support for loading form data. This example illustrates usage + * of Ext.Direct to load a submit a form through Ext.Direct. + *
+var myFormPanel = new Ext.form.FormPanel({
+ // configs for FormPanel
+ title: 'Basic Information',
+ border: false,
+ padding: 10,
+ buttons:[{
+ text: 'Submit',
+ handler: function(){
+ basicInfo.getForm().submit({
+ params: {
+ uid: 5
+ }
+ });
+ }
+ }],
+
+ // configs apply to child items
+ defaults: {anchor: '100%'},
+ defaultType: 'textfield',
+ items: [
+ // form fields go here
+ ],
+
+ // configs for BasicForm
+ api: {
+ load: Profile.getBasicInfo,
+ // The server-side must mark the submit handler as a 'formHandler'
+ submit: Profile.updateBasicInfo
+ },
+ paramOrder: ['uid']
+});
+
+// load the form
+myFormPanel.getForm().load({
+ params: {
+ uid: 5
+ }
+});
+ *
+ */
+Ext.form.Action.DirectLoad = Ext.extend(Ext.form.Action.Load, {
+ constructor: function(form, opts) {
+ Ext.form.Action.DirectLoad.superclass.constructor.call(this, form, opts);
+ },
+ type: 'directload',
+
+ run : function(){
+ var args = this.getParams();
+ args.push(this.success, this);
+ this.form.api.load.apply(window, args);
+ },
+
+ getParams: function() {
+ var buf = [], o = {};
+ var bp = this.form.baseParams;
+ var p = this.options.params;
+ Ext.apply(o, p, bp);
+ var paramOrder = this.form.paramOrder;
+ if(paramOrder){
+ for(var i = 0, len = paramOrder.length; i < len; i++){
+ buf.push(o[paramOrder[i]]);
+ }
+ }else if(this.form.paramsAsHash){
+ buf.push(o);
+ }
+ return buf;
+ },
+ // Direct actions have already been processed and therefore
+ // we can directly set the result; Direct Actions do not have
+ // a this.response property.
+ processResponse: function(result) {
+ this.result = result;
+ return result;
+ }
+});
+
+/**
+ * @class Ext.form.Action.DirectSubmit
+ * @extends Ext.form.Action.Submit
+ * Provides Ext.direct support for submitting form data.
+ * See {@link Ext.form.Action.DirectLoad}.
+ */
+Ext.form.Action.DirectSubmit = Ext.extend(Ext.form.Action.Submit, {
+ constructor: function(form, opts) {
+ Ext.form.Action.DirectSubmit.superclass.constructor.call(this, form, opts);
+ },
+ type: 'directsubmit',
+ // override of Submit
+ run : function(){
+ var o = this.options;
+ if(o.clientValidation === false || this.form.isValid()){
+ // tag on any additional params to be posted in the
+ // form scope
+ this.success.params = this.getParams();
+ this.form.api.submit(this.form.el.dom, this.success, this);
+ }else if (o.clientValidation !== false){ // client validation failed
+ this.failureType = Ext.form.Action.CLIENT_INVALID;
+ this.form.afterAction(this, false);
+ }
+ },
+
+ getParams: function() {
+ var o = {};
+ var bp = this.form.baseParams;
+ var p = this.options.params;
+ Ext.apply(o, p, bp);
+ return o;
+ },
+ // Direct actions have already been processed and therefore
+ // we can directly set the result; Direct Actions do not have
+ // a this.response property.
+ processResponse: function(result) {
+ this.result = result;
+ return result;
+ }
+});
+
+
+Ext.form.Action.ACTION_TYPES = {
+ 'load' : Ext.form.Action.Load,
+ 'submit' : Ext.form.Action.Submit,
+ 'directload': Ext.form.Action.DirectLoad,
+ 'directsubmit': Ext.form.Action.DirectSubmit
+};
+/**
+ * @class Ext.form.VTypes
+ * This is a singleton object which contains a set of commonly used field validation functions. + * The validations provided are basic and intended to be easily customizable and extended.
+ *To add custom VTypes specify the {@link Ext.form.TextField#vtype vtype}
validation
+ * test function, and optionally specify any corresponding error text to display and any keystroke
+ * filtering mask to apply. For example:
+// custom Vtype for vtype:'time'
+var timeTest = /^([1-9]|1[0-9]):([0-5][0-9])(\s[a|p]m)$/i;
+Ext.apply(Ext.form.VTypes, {
+ // vtype validation function
+ time: function(val, field) {
+ return timeTest.test(val);
+ },
+ // vtype Text property: The error text to display when the validation function returns false
+ timeText: 'Not a valid time. Must be in the format "12:34 PM".',
+ // vtype Mask property: The keystroke filter mask
+ timeMask: /[\d\s:amp]/i
+});
+ *
+ * Another example:
+ *
+// custom Vtype for vtype:'IPAddress'
+Ext.apply(Ext.form.VTypes, {
+ IPAddress: function(v) {
+ return /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(v);
+ },
+ IPAddressText: 'Must be a numeric IP address',
+ IPAddressMask: /[\d\.]/i
+});
+ *
+ * @singleton
+ */
+Ext.form.VTypes = function(){
+ // closure these in so they are only created once.
+ var alpha = /^[a-zA-Z_]+$/;
+ var alphanum = /^[a-zA-Z0-9_]+$/;
+ var email = /^(\w+)([-+.][\w]+)*@(\w[-\w]*\.){1,5}([A-Za-z]){2,4}$/;
+ var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@`~=%!]*)(\.\w{2,})?)*\/?)/i;
+
+ // All these messages and functions are configurable
+ return {
+ /**
+ * The function used to validate email addresses. Note that this is a very basic validation -- complete
+ * validation per the email RFC specifications is very complex and beyond the scope of this class, although
+ * this function can be overridden if a more comprehensive validation scheme is desired. See the validation
+ * section of the Wikipedia article on email addresses
+ * for additional information. This implementation is intended to validate the following emails:
+ * 'barney@example.de', 'barney.rubble@example.com', 'barney-rubble@example.coop', 'barney+rubble@example.com'
+ * .
+ * @param {String} value The email address
+ * @return {Boolean} true if the RegExp test passed, and false if not.
+ */
+ 'email' : function(v){
+ return email.test(v);
+ },
+ /**
+ * The error text to display when the email validation function returns false. Defaults to:
+ * 'This field should be an e-mail address in the format "user@example.com"'
+ * @type String
+ */
+ 'emailText' : 'This field should be an e-mail address in the format "user@example.com"',
+ /**
+ * The keystroke filter mask to be applied on email input. See the {@link #email} method for
+ * information about more complex email validation. Defaults to:
+ * /[a-z0-9_\.\-@]/i
+ * @type RegExp
+ */
+ 'emailMask' : /[a-z0-9_\.\-@]/i,
+
+ /**
+ * The function used to validate URLs
+ * @param {String} value The URL
+ * @return {Boolean} true if the RegExp test passed, and false if not.
+ */
+ 'url' : function(v){
+ return url.test(v);
+ },
+ /**
+ * The error text to display when the url validation function returns false. Defaults to:
+ * 'This field should be a URL in the format "http:/'+'/www.example.com"'
+ * @type String
+ */
+ 'urlText' : 'This field should be a URL in the format "http:/'+'/www.example.com"',
+
+ /**
+ * The function used to validate alpha values
+ * @param {String} value The value
+ * @return {Boolean} true if the RegExp test passed, and false if not.
+ */
+ 'alpha' : function(v){
+ return alpha.test(v);
+ },
+ /**
+ * The error text to display when the alpha validation function returns false. Defaults to:
+ * 'This field should only contain letters and _'
+ * @type String
+ */
+ 'alphaText' : 'This field should only contain letters and _',
+ /**
+ * The keystroke filter mask to be applied on alpha input. Defaults to:
+ * /[a-z_]/i
+ * @type RegExp
+ */
+ 'alphaMask' : /[a-z_]/i,
+
+ /**
+ * The function used to validate alphanumeric values
+ * @param {String} value The value
+ * @return {Boolean} true if the RegExp test passed, and false if not.
+ */
+ 'alphanum' : function(v){
+ return alphanum.test(v);
+ },
+ /**
+ * The error text to display when the alphanumeric validation function returns false. Defaults to:
+ * 'This field should only contain letters, numbers and _'
+ * @type String
+ */
+ 'alphanumText' : 'This field should only contain letters, numbers and _',
+ /**
+ * The keystroke filter mask to be applied on alphanumeric input. Defaults to:
+ * /[a-z0-9_]/i
+ * @type RegExp
+ */
+ 'alphanumMask' : /[a-z0-9_]/i
+ };
+}();
\ No newline at end of file