dojo.provide("dijit.form.NumberTextBox"); dojo.require("dijit.form.ValidationTextBox"); dojo.require("dojo.number"); /*===== dojo.declare( "dijit.form.NumberTextBox.__Constraints", [dijit.form.RangeBoundTextBox.__Constraints, dojo.number.__FormatOptions, dojo.number.__ParseOptions] ); =====*/ dojo.declare("dijit.form.NumberTextBoxMixin", null, { // summary: // A mixin for all number textboxes // tags: // protected // Override ValidationTextBox.regExpGen().... we use a reg-ex generating function rather // than a straight regexp to deal with locale (plus formatting options too?) regExpGen: dojo.number.regexp, /*===== // constraints: dijit.form.NumberTextBox.__Constraints // Minimum/maximum allowed values. constraints: {}, ======*/ // value: Number // The value of this NumberTextBox as a javascript Number (ie, not a String). // If the displayed value is blank, the value is NaN, and if the user types in // an gibberish value (like "hello world"), the value is undefined // (i.e. attr('value') returns undefined). // // Symetrically, attr('value', NaN) will clear the displayed value, // whereas attr('value', undefined) will have no effect. value: NaN, // editOptions: [protected] Object // Properties to mix into constraints when the value is being edited. // This is here because we edit the number in the format "12345", which is // different than the display value (ex: "12,345") editOptions: { pattern: '#.######' }, /*===== _formatter: function(value, options){ // summary: // _formatter() is called by format(). It's the base routine for formatting a number, // as a string, for example converting 12345 into "12,345". // value: Number // The number to be converted into a string. // options: dojo.number.__FormatOptions? // Formatting options // tags: // protected extension return "12345"; // String }, =====*/ _formatter: dojo.number.format, postMixInProperties: function(){ if(typeof this.constraints.max != "number"){ this.constraints.max = 9e+15; } this.inherited(arguments); }, _onFocus: function(){ if(this.disabled){ return; } var val = this.attr('value'); if(typeof val == "number" && !isNaN(val)){ var formattedValue = this.format(val, this.constraints); if(formattedValue !== undefined){ this.textbox.value = formattedValue; } } this.inherited(arguments); }, format: function(/*Number*/ value, /*dojo.number.__FormatOptions*/ constraints){ // summary: // Formats the value as a Number, according to constraints. // tags: // protected if(typeof value != "number") { return String(value) } if(isNaN(value)){ return ""; } if(("rangeCheck" in this) && !this.rangeCheck(value, constraints)){ return String(value) } if(this.editOptions && this._focused){ constraints = dojo.mixin(dojo.mixin({}, this.editOptions), constraints); } return this._formatter(value, constraints); }, /*===== parse: function(value, constraints){ // summary: // Parses the string value as a Number, according to constraints. // value: String // String representing a number // constraints: dojo.number.__ParseOptions // Formatting options // tags: // protected return 123.45; // Number }, =====*/ parse: dojo.number.parse, _getDisplayedValueAttr: function(){ var v = this.inherited(arguments); return isNaN(v) ? this.textbox.value : v; }, filter: function(/*Number*/ value){ // summary: // This is called with both the display value (string), and the actual value (a number). // When called with the actual value it does corrections so that '' etc. are represented as NaN. // Otherwise it dispatches to the superclass's filter() method. // // See `dijit.form.TextBox.filter` for more details. return (value === null || value === '' || value === undefined) ? NaN : this.inherited(arguments); // attr('value', null||''||undefined) should fire onChange(NaN) }, serialize: function(/*Number*/ value, /*Object?*/options){ // summary: // Convert value (a Number) into a canonical string (ie, how the number literal is written in javascript/java/C/etc.) // tags: // protected return (typeof value != "number" || isNaN(value))? '' : this.inherited(arguments); }, _setValueAttr: function(/*Number*/ value, /*Boolean?*/ priorityChange, /*String?*/formattedValue){ // summary: // Hook so attr('value', ...) works. if(value !== undefined && formattedValue === undefined){ if(typeof value == "number"){ if(isNaN(value)){ formattedValue = '' } else if(("rangeCheck" in this) && !this.rangeCheck(value, this.constraints)){ formattedValue = String(value); } }else if(!value){ // 0 processed in if branch above, ''|null|undefined flow thru here formattedValue = ''; value = NaN; }else{ // non-numeric values formattedValue = String(value); value = undefined; } } this.inherited(arguments, [value, priorityChange, formattedValue]); }, _getValueAttr: function(){ // summary: // Hook so attr('value') works. // Returns Number, NaN for '', or undefined for unparsable text var v = this.inherited(arguments); // returns Number for all values accepted by parse() or NaN for all other displayed values // If the displayed value of the textbox is gibberish (ex: "hello world"), this.inherited() above // returns NaN; this if() branch converts the return value to undefined. // Returning undefined prevents user text from being overwritten when doing _setValueAttr(_getValueAttr()). // A blank displayed value is still returned as NaN. if(isNaN(v) && this.textbox.value !== ''){ // if displayed value other than '' var n = Number(this.textbox.value); // check for exponential notation that parse() rejected (erroneously?) return (String(n)===this.textbox.value)? n : undefined; // return exponential Number or undefined for random text }else{ return v } // Number or NaN for '' } } ); dojo.declare("dijit.form.NumberTextBox", [dijit.form.RangeBoundTextBox,dijit.form.NumberTextBoxMixin], { // summary: // A validating, serializable, range-bound text box. } );