1. /*---
  2.  * @xtype ux.numberfield
  3.  * @class Ext.ux.form.NumberField
  4.  * @extends Ext.form.NumberField
  5.  * Numeric text field that provides automatic keystroke filtering and numeric validation
  6.  * Also resolves bugs by enforcing allowDecimals (fixed in validation) and allowNegative (fixed by setting minValue if no negatives allowed)
  7.  ---*/
  8.  
  9. Ext.ns('Ext.ux.form');
  10.  
  11. Ext.ux.form.NumberField = Ext.extend(Ext.form.NumberField, {
  12.     //=== PUBLIC PROPERTIES ===//
  13.     // Defaults
  14.     // @cfg {boolean} decorateNumber turns on\off functionality related to applying decoration onblur
  15.     //  and removing decoration onfocus and before submit
  16.     decorateNumber: true,
  17.     // Custom
  18.     selectOnFocus: true,
  19.  
  20.     // Localization
  21.     decimalSeparator: '.',
  22.     groupingSymbol: ',',
  23.     // Custom
  24.     // @cfg {String} allowNegativeText Error text to display if negative values are NOT allowed (see allowNegative)
  25.     // and one is FOUND during validation (this happens due to pasting the value or programmatically assigning the value)
  26.     allowNegativeText: 'Negative values not allowed',
  27.     // Custom
  28.     // @cfg {String} allowDecimalsText Error text to display if decimals are NOT allowed (see allowDecimals)
  29.     // and they are FOUND during validation (this happens due to pasting the value or programmatically assigning the value)
  30.     allowDecimalsText: 'Decimal values not allowed',
  31.     // Custom
  32.     //=== OVERRIDES ===//
  33.     initEvents: function () {
  34.         Ext.ux.form.NumberField.superclass.initEvents.call(this);
  35.  
  36.         var basicForm = this.ownerCt.getForm();
  37.         basicForm.addListener('beforeaction', function (form, action) {
  38.             /*var parsedValue = this.getValue();
  39.              if (this.decorateNumber && action.type == 'submit' && parsedValue)
  40.              this.setRawValue(parsedValue);*/
  41.             if (action.type == 'submit') {
  42.                 this.setRawValue(this.removeDecoration(this.getRawValue()));
  43.                 this.setRawValue(this.getRawValue().replace(this.decimalSeparator, '.'));
  44.             }
  45.         }, this);
  46.     },
  47.     preFocus: function () {
  48.         this.setRawValue(this.removeDecoration(this.getRawValue()));
  49.         /*if (this.decorateNumber)
  50.          this.setRawValue(this.parseValue(this.getRawValue()));*/
  51.         //NOTE:  Remove decorations from raw value before calling into base so that select on focus runs properly
  52.         Ext.ux.form.NumberField.superclass.preFocus.call(this); //NOTE:  Call into base class
  53.     },
  54.     processValue: function (value) {
  55.         //NOTE:  Removing decorations before validation, process value is called internally before a call to validate
  56.         value = Ext.ux.form.NumberField.superclass.processValue.call(this, this.parseValue(value)); //NOTE:  Call into base class
  57.         return value;
  58.     },
  59.     validateValue: function (value) {
  60.         if (!Ext.ux.form.NumberField.superclass.validateValue.call(this, value)) return false;
  61.         if (value.length < 1) return true; //NOTE: if it's blank and textfield didn't flag it then it's valid
  62.         // Validate for presense of negative sign when they are explicitely NOT allowed
  63.         // (resolves bug in this only being enforced by keystroke masking)
  64.         if (!this.allowNegative) {
  65.             regExpDecimals = new RegExp('[-]');
  66.             if (regExpDecimals.test(value)) {
  67.                 this.markInvalid(this.allowNegativeText);
  68.                 return false;
  69.             }
  70.         }
  71.  
  72.         // Validate for presense of a decimal separator when they are explicitely not allowed
  73.         // (resolves bug in this only being enforced by keystroke masking)
  74.         if (!this.allowDecimals) {
  75.             regExpDecimals = new RegExp('[' + this.decimalSeparator + ']');
  76.             if (regExpDecimals.test(value)) {
  77.                 this.markInvalid(this.allowDecimalsText);
  78.                 return false;
  79.             }
  80.         }
  81.  
  82.         return true;
  83.     },
  84.     parseValue: function (v) {
  85.         v = this.removeDecoration(v);
  86.         //NOTE:  Non period decimal separators are replaced with periods by base class
  87.         return Ext.ux.form.NumberField.superclass.parseValue.call(this, v);
  88.     },
  89.     postBlur: function () {
  90.         Ext.ux.form.NumberField.superclass.postBlur.call(this); //NOTE:  Call into base class
  91.         if (this.decorateNumber && this.isValid) {
  92.             //NOTE:  Decorate number value (if present and valid)
  93.             var value = this.getValue();
  94.             if (value) this.setRawValue(this.numberFormat(value));
  95.         }
  96.     },
  97.  
  98.     //=== CUSTOM ===//
  99.     removeDecoration: function (v) {
  100.         // Removes all but digits, negative sign, and specified decimal separator
  101.         if (v && this.decorateNumber) {
  102.             re = new RegExp('[^0-9\\-\\' + this.decimalSeparator + ']', 'g');
  103.             v = String(v).replace(re, '');
  104.         }
  105.         return v;
  106.     },
  107.     numberFormat: function (v) {
  108.         // Format number with ExtJS number format
  109.         var pattern = this.numberPattern();
  110.         var format = Ext.util.Format.number(v, pattern);
  111.         // Replace comma in format with specified grouping symbol if not specified as '.' or ','
  112.         if (this.isI18n() && this.groupingSymbol != '.') {
  113.             re = new RegExp('[\\.]', 'g');
  114.             format = format.replace(re, this.groupingSymbol);
  115.         }
  116.         return format;
  117.     },
  118.     numberPattern: function (decimalSeparator, decimalPrecision, groupingSymbol) {
  119.         // Building the number format pattern for use by ExtJS Ext.util.Format.number
  120.         var pattern = [];
  121.         pattern.push('0');
  122.         if (this.groupingSymbol) pattern.push((this.isI18n() && this.groupingSymbol != '.') ? '.000' : this.groupingSymbol + '000');
  123.         if (this.allowDecimals && this.decimalPrecision) {
  124.             pattern.push(this.decimalSeparator);
  125.             for (var i = 0; i < this.decimalPrecision; i++)
  126.             pattern.push('0');
  127.         }
  128.         if (this.isI18n()) pattern.push('/i');
  129.         return pattern.join('');
  130.     },
  131.     isI18n: function () {
  132.         // As defined by ExtJS ExtJS Ext.util.Format.number
  133.         return (this.decimalSeparator != '.' && this.groupingSymbol != ',')
  134.     }
  135. });
  136. Ext.reg('ux.numberfield', Ext.ux.form.NumberField);