From e0e40afd2db690884e325a03344783d56c875efd Mon Sep 17 00:00:00 2001 From: Richard Davies Date: Wed, 10 Sep 2014 09:20:02 -0700 Subject: [PATCH] Update jquery.number.js Changelog: * Added support for the delete key (and modified comments to differentiate between delete and backspace) * Allow negative sign to be inserted at beginning of existing numbers * Remove negative sign when value changes to "-0.00" (like when deleting the "1" in "-1.00") * Don't remove negative sign when deleting numbers to the right of the decimal * Allow more standard browser hotkeys like Ctrl+P, Ctrl+S, Ctrl+F, Ctrl+Z, F3, F5, F11, etc. within fields * Properly initialize 'data.init' value when prepopulated via browser autofill or F5 * Consistent use of tabs for indenting code and consistent equality operators Quickly tested in Firefox 32, Chrome 37, and IE 11 on Win7. --- jquery.number.js | 925 +++++++++++++++++++++++++---------------------- 1 file changed, 491 insertions(+), 434 deletions(-) diff --git a/jquery.number.js b/jquery.number.js index 318a122..438acd5 100644 --- a/jquery.number.js +++ b/jquery.number.js @@ -1,5 +1,5 @@ /** - * jQuery number plug-in 2.1.4 + * jQuery number plug-in 2.1.5 * Copyright 2012, Digital Fusion * Licensed under the MIT license. * http://opensource.teamdf.com/license/ @@ -74,7 +74,7 @@ else if(typeof(this['selection'+part])!="undefined") { - pos = this['selection'+part]; + pos = this['selection'+part]; } return pos; } @@ -85,6 +85,7 @@ */ var _keydown = { codes : { + 46 : 127, 188 : 44, 109 : 45, 190 : 46, @@ -99,8 +100,8 @@ 186 : 59, //IE Key codes 189 : 45, //IE Key codes 110 : 46 //IE Key codes - }, - shifts : { + }, + shifts : { 96 : "~", 49 : "!", 50 : "@", @@ -122,8 +123,8 @@ 44 : "<", 46 : ">", 47 : "?" - } - }; + } + }; /** * jQuery number formatter plugin. This will allow you to format numbers on an element. @@ -134,277 +135,328 @@ */ $.fn.number = function( number, decimals, dec_point, thousands_sep ){ - // Enter the default thousands separator, and the decimal placeholder. - thousands_sep = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep; - dec_point = (typeof dec_point === 'undefined') ? '.' : dec_point; - decimals = (typeof decimals === 'undefined' ) ? 0 : decimals; - - // Work out the unicode character for the decimal placeholder. - var u_dec = ('\\u'+('0000'+(dec_point.charCodeAt(0).toString(16))).slice(-4)), - regex_dec_num = new RegExp('[^'+u_dec+'0-9]','g'), - regex_dec = new RegExp(u_dec,'g'); - - // If we've specified to take the number from the target element, - // we loop over the collection, and get the number. - if( number === true ) - { - // If this element is a number, then we add a keyup - if( this.is('input:text') ) - { - // Return the jquery collection. - return this.on({ - - /** - * Handles keyup events, re-formatting numbers. - * - * Uses 'data' object to keep track of important information. - * - * data.c - * This variable keeps track of where the caret *should* be. It works out the position as - * the number of characters from the end of the string. E.g., '1^,234.56' where ^ denotes the caret, - * would be index -7 (e.g., 7 characters from the end of the string). At the end of both the key down - * and key up events, we'll re-position the caret to wherever data.c tells us the cursor should be. - * This gives us a mechanism for incrementing the cursor position when we come across decimals, commas - * etc. This figure typically doesn't increment for each keypress when to the left of the decimal, - * but does when to the right of the decimal. - * - * @param object e : the keyup event object.s - * - * @return void; - */ - 'keydown.format' : function(e){ - - // Define variables used in the code below. - var $this = $(this), - data = $this.data('numFormat'), - code = (e.keyCode ? e.keyCode : e.which), + // Enter the default thousands separator, and the decimal placeholder. + thousands_sep = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep; + dec_point = (typeof dec_point === 'undefined') ? '.' : dec_point; + decimals = (typeof decimals === 'undefined' ) ? 0 : decimals; + + // Work out the unicode character for the decimal placeholder. + var u_dec = ('\\u'+('0000'+(dec_point.charCodeAt(0).toString(16))).slice(-4)), + regex_dec_num = new RegExp('[^'+u_dec+'0-9]','g'), + regex_dec = new RegExp(u_dec,'g'); + + // If we've specified to take the number from the target element, + // we loop over the collection, and get the number. + if( number === true ) + { + // If this element is a number, then we add a keyup + if( this.is('input:text') ) + { + // Return the jquery collection. + return this.on({ + + /** + * Handles keyup events, re-formatting numbers. + * + * Uses 'data' object to keep track of important information. + * + * data.c + * This variable keeps track of where the caret *should* be. It works out the position as + * the number of characters from the end of the string. E.g., '1^,234.56' where ^ denotes the caret, + * would be index -7 (e.g., 7 characters from the end of the string). At the end of both the key down + * and key up events, we'll re-position the caret to wherever data.c tells us the cursor should be. + * This gives us a mechanism for incrementing the cursor position when we come across decimals, commas + * etc. This figure typically doesn't increment for each keypress when to the left of the decimal, + * but does when to the right of the decimal. + * + * @param object e : the keyup event object.s + * + * @return void; + */ + 'keydown.format' : function(e){ + + // Define variables used in the code below. + var $this = $(this), + data = $this.data('numFormat'), + code = (e.keyCode ? e.keyCode : e.which), chara = '', //unescape(e.originalEvent.keyIdentifier.replace('U+','%u')), - start = getSelection.apply(this,['start']), - end = getSelection.apply(this,['end']), - val = '', - setPos = false; - - // Webkit (Chrome & Safari) on windows screws up the keyIdentifier detection - // for numpad characters. I've disabled this for now, because while keyCode munging - // below is hackish and ugly, it actually works cross browser & platform. - -// if( typeof e.originalEvent.keyIdentifier !== 'undefined' ) -// { -// chara = unescape(e.originalEvent.keyIdentifier.replace('U+','%u')); -// } -// else -// { - if (_keydown.codes.hasOwnProperty(code)) { - code = _keydown.codes[code]; - } - if (!e.shiftKey && (code >= 65 && code <= 90)){ - code += 32; - } else if (!e.shiftKey && (code >= 69 && code <= 105)){ - code -= 48; - } else if (e.shiftKey && _keydown.shifts.hasOwnProperty(code)){ - //get shifted keyCode value - chara = _keydown.shifts[code]; - } - - if( chara == '' ) chara = String.fromCharCode(code); -// } - - - // Stop executing if the user didn't type a number key, a decimal character, or backspace. - if( code !== 8 && code !== 45 && chara != dec_point && !chara.match(/[0-9]/) ) - { - // We need the original keycode now... - var key = (e.keyCode ? e.keyCode : e.which); - if( // Allow control keys to go through... (delete, etc) - key == 46 || key == 8 || key == 9 || key == 27 || key == 13 || - // Allow: Ctrl+A, Ctrl+R - ( (key == 65 || key == 82 ) && ( e.ctrlKey || e.metaKey ) === true ) || - // Allow: Ctrl+V, Ctrl+C - ( (key == 86 || key == 67 ) && ( e.ctrlKey || e.metaKey ) === true ) || - // Allow: home, end, left, right - ( (key >= 35 && key <= 39) ) + start = getSelection.apply(this,['start']), + end = getSelection.apply(this,['end']), + val = '', + setPos = false; + + // Webkit (Chrome & Safari) on windows screws up the keyIdentifier detection + // for numpad characters. I've disabled this for now, because while keyCode munging + // below is hackish and ugly, it actually works cross browser & platform. + +// if( typeof e.originalEvent.keyIdentifier !== 'undefined' ) +// { +// chara = unescape(e.originalEvent.keyIdentifier.replace('U+','%u')); +// } +// else +// { + if (_keydown.codes.hasOwnProperty(code)) { + code = _keydown.codes[code]; + } + if (!e.shiftKey && (code >= 65 && code <= 90)){ + code += 32; + } else if (!e.shiftKey && (code >= 69 && code <= 105)){ + code -= 48; + } else if (e.shiftKey && _keydown.shifts.hasOwnProperty(code)){ + //get shifted keyCode value + chara = _keydown.shifts[code]; + } + + if( chara == '' ) chara = String.fromCharCode(code); +// } + + + + + // Stop executing if the user didn't type a number key, a decimal character, backspace, or delete. + if( code != 8 && code != 45 && code != 127 && chara != dec_point && !chara.match(/[0-9]/) ) + { + // We need the original keycode now... + var key = (e.keyCode ? e.keyCode : e.which); + if( // Allow control keys to go through... (delete, backspace, tab, enter, escape etc) + key == 46 || key == 8 || key == 127 || key == 9 || key == 27 || key == 13 || + // Allow: Ctrl+A, Ctrl+R, Ctrl+P, Ctrl+S, Ctrl+F, Ctrl+H, Ctrl+B, Ctrl+J, Ctrl+T, Ctrl+Z, Ctrl++, Ctrl+-, Ctrl+0 + ( (key == 65 || key == 82 || key == 80 || key == 83 || key == 70 || key == 72 || key == 66 || key == 74 || key == 84 || key == 90|| key == 61 || key == 173 || key == 48) && ( e.ctrlKey || e.metaKey ) === true ) || + // Allow: Ctrl+V, Ctrl+C, Ctrl+X + ( (key == 86 || key == 67 || key == 88) && ( e.ctrlKey || e.metaKey ) === true ) || + // Allow: home, end, left, right + ( (key >= 35 && key <= 39) ) || + // Allow: F1-F12 + ( (key >= 112 && key <= 123) ) ){ return; } // But prevent all other keys. e.preventDefault(); return false; - } - - // The whole lot has been selected, or if the field is empty... - if( start == 0 && end == this.value.length || $this.val() == 0 ) - { - if( code === 8 ) - { - // Blank out the field, but only if the data object has already been instanciated. - start = end = 1; - this.value = ''; - - // Reset the cursor position. - data.init = (decimals>0?-1:0); - data.c = (decimals>0?-(decimals+1):0); - setSelectionRange.apply(this, [0,0]); - } - else if( chara === dec_point ) - { - start = end = 1; - this.value = '0'+ dec_point + (new Array(decimals+1).join('0')); - - // Reset the cursor position. - data.init = (decimals>0?1:0); - data.c = (decimals>0?-(decimals+1):0); - } - else if( code === 45 ) - { - start = end = 2; - this.value = '-0'+dec_point + (new Array(decimals+1).join('0')); - - // Reset the cursor position. - data.init = (decimals>0?1:0); - data.c = (decimals>0?-(decimals+1):0); - - setSelectionRange.apply(this, [2,2]); - } - else - { - // Reset the cursor position. - data.init = (decimals>0?-1:0); - data.c = (decimals>0?-(decimals):0); - } - } - - // Otherwise, we need to reset the caret position - // based on the users selection. - else - { - data.c = end-this.value.length; - } - - // If the start position is before the decimal point, - // and the user has typed a decimal point, we need to move the caret - // past the decimal place. - if( decimals > 0 && chara == dec_point && start == this.value.length-decimals-1 ) - { - data.c++; - data.init = Math.max(0,data.init); - e.preventDefault(); - - // Set the selection position. - setPos = this.value.length+data.c; - } - - else if( code === 45 ) - { - e.preventDefault(); - } - - // If the user is just typing the decimal place, - // we simply ignore it. - else if( chara == dec_point ) - { - data.init = Math.max(0,data.init); - e.preventDefault(); - } - - // If hitting the delete key, and the cursor is behind a decimal place, - // we simply move the cursor to the other side of the decimal place. - else if( decimals > 0 && code == 8 && start == this.value.length-decimals ) - { - e.preventDefault(); - data.c--; - - // Set the selection position. - setPos = this.value.length+data.c; - } - - // If hitting the delete key, and the cursor is to the right of the decimal - // (but not directly to the right) we replace the character preceeding the - // caret with a 0. - else if( decimals > 0 && code == 8 && start > this.value.length-decimals ) - { - if( this.value === '' ) return; - - // If the character preceeding is not already a 0, - // replace it with one. - if( this.value.slice(start-1, start) != '0' ) - { - val = this.value.slice(0, start-1) + '0' + this.value.slice(start); - $this.val(val.replace(regex_dec_num,'').replace(regex_dec,dec_point)); - } - - e.preventDefault(); - data.c--; - - // Set the selection position. - setPos = this.value.length+data.c; - } - - // If the delete key was pressed, and the character immediately - // before the caret is a thousands_separator character, simply - // step over it. - else if( code == 8 && this.value.slice(start-1, start) == thousands_sep ) - { - e.preventDefault(); - data.c--; - - // Set the selection position. - setPos = this.value.length+data.c; - } - - // If the caret is to the right of the decimal place, and the user is entering a - // number, remove the following character before putting in the new one. - else if( - decimals > 0 && - start == end && - this.value.length > decimals+1 && - start > this.value.length-decimals-1 && isFinite(+chara) && - !e.metaKey && !e.ctrlKey && !e.altKey && chara.length === 1 - ) - { - // If the character preceeding is not already a 0, - // replace it with one. - if( end === this.value.length ) - { - val = this.value.slice(0, start-1); - } - else - { - val = this.value.slice(0, start)+this.value.slice(start+1); - } - - // Reset the position. - this.value = val; - setPos = start; - } - - // If we need to re-position the characters. - if( setPos !== false ) - { - //console.log('Setpos keydown: ', setPos ); - setSelectionRange.apply(this, [setPos, setPos]); - } - - // Store the data on the element. - $this.data('numFormat', data); - - }, - - /** - * Handles keyup events, re-formatting numbers. - * - * @param object e : the keyup event object.s - * - * @return void; - */ - 'keyup.format' : function(e){ - - // Store these variables for use below. - var $this = $(this), - data = $this.data('numFormat'), - code = (e.keyCode ? e.keyCode : e.which), - start = getSelection.apply(this,['start']), - end = getSelection.apply(this,['end']), - setPos; + } + + // The whole lot has been selected, or if the field is empty... + if( start == 0 && end == this.value.length || $this.val() == 0 ) + { + if( code == 8 ) // Backspace + { + // Blank out the field, but only if the data object has already been instantiated. + start = end = 1; + this.value = ''; + + // Reset the cursor position. + data.init = (decimals>0?-1:0); + data.c = (decimals>0?-(decimals+1):0); + setSelectionRange.apply(this, [0,0]); + } + else if( chara == dec_point ) + { + start = end = 1; + this.value = '0'+ dec_point + (new Array(decimals+1).join('0')); + + // Reset the cursor position. + data.init = (decimals>0?1:0); + data.c = (decimals>0?-(decimals+1):0); + } + else if( code == 45 ) // Negative sign + { + start = end = 2; + this.value = '-0'+dec_point + (new Array(decimals+1).join('0')); + + // Reset the cursor position. + data.init = (decimals>0?1:0); + data.c = (decimals>0?-(decimals+1):0); + + setSelectionRange.apply(this, [2,2]); + } + else + { + // Reset the cursor position. + data.init = (decimals>0?-1:0); + data.c = (decimals>0?-(decimals):0); + } + } + + // Otherwise, we need to reset the caret position + // based on the users selection. + else + { + data.c = end-this.value.length; + } + + // Track if partial selection was used + data.isPartialSelection = start == end ? false : true; + + // If the start position is before the decimal point, + // and the user has typed a decimal point, we need to move the caret + // past the decimal place. + if( decimals > 0 && chara == dec_point && start == this.value.length-decimals-1 ) + { + data.c++; + data.init = Math.max(0,data.init); + e.preventDefault(); + + // Set the selection position. + setPos = this.value.length+data.c; + } + + // Ignore negative sign unless at beginning of number (and it's not already present) + else if( code == 45 && (start != 0 || this.value.indexOf('-') == 0) ) + { + e.preventDefault(); + } + + // If the user is just typing the decimal place, + // we simply ignore it. + else if( chara == dec_point ) + { + data.init = Math.max(0,data.init); + e.preventDefault(); + } + + // If hitting the delete key, and the cursor is before a decimal place, + // we simply move the cursor to the other side of the decimal place. + else if( decimals > 0 && code == 127 && start == this.value.length-decimals-1 ) + { + // Just prevent default but don't actually move the caret here because it's done in the keyup event + e.preventDefault(); + } + + // If hitting the backspace key, and the cursor is behind a decimal place, + // we simply move the cursor to the other side of the decimal place. + else if( decimals > 0 && code == 8 && start == this.value.length-decimals ) + { + e.preventDefault(); + data.c--; + + // Set the selection position. + setPos = this.value.length+data.c; + } + + // If hitting the delete key, and the cursor is to the right of the decimal + // we replace the character after the caret with a 0. + else if( decimals > 0 && code == 127 && start > this.value.length-decimals-1 ) + { + if( this.value === '' ) return; + + // If the character following is not already a 0, + // replace it with one. + if( this.value.slice(start, start+1) != '0' ) + { + val = this.value.slice(0, start) + '0' + this.value.slice(start+1); + // The regex replacement below removes negative sign from numbers... + // not sure why they're necessary here when none of the other cases use them + //$this.val(val.replace(regex_dec_num,'').replace(regex_dec,dec_point)); + $this.val(val); + } + + e.preventDefault(); + + // Set the selection position. + setPos = this.value.length+data.c; + } + + // If hitting the backspace key, and the cursor is to the right of the decimal + // (but not directly to the right) we replace the character preceding the + // caret with a 0. + else if( decimals > 0 && code == 8 && start > this.value.length-decimals ) + { + if( this.value === '' ) return; + + // If the character preceding is not already a 0, + // replace it with one. + if( this.value.slice(start-1, start) != '0' ) + { + val = this.value.slice(0, start-1) + '0' + this.value.slice(start); + // The regex replacement below removes negative sign from numbers... + // not sure why they're necessary here when none of the other cases use them + //$this.val(val.replace(regex_dec_num,'').replace(regex_dec,dec_point)); + $this.val(val); + } + + e.preventDefault(); + data.c--; + + // Set the selection position. + setPos = this.value.length+data.c; + } + + // If the delete key was pressed, and the character immediately + // after the caret is a thousands_separator character, simply + // step over it. + else if( code == 127 && this.value.slice(start, start+1) == thousands_sep ) + { + // Just prevent default but don't actually move the caret here because it's done in the keyup event + e.preventDefault(); + } + + // If the backspace key was pressed, and the character immediately + // before the caret is a thousands_separator character, simply + // step over it. + else if( code == 8 && this.value.slice(start-1, start) == thousands_sep ) + { + e.preventDefault(); + data.c--; + + // Set the selection position. + setPos = this.value.length+data.c; + } + + // If the caret is to the right of the decimal place, and the user is entering a + // number, remove the following character before putting in the new one. + else if( + decimals > 0 && + start == end && + this.value.length > decimals+1 && + start > this.value.length-decimals-1 && isFinite(+chara) && + !e.metaKey && !e.ctrlKey && !e.altKey && chara.length === 1 + ) + { + // If the character preceding is not already a 0, + // replace it with one. + if( end === this.value.length ) + { + val = this.value.slice(0, start-1); + } + else + { + val = this.value.slice(0, start)+this.value.slice(start+1); + } + + // Reset the position. + this.value = val; + setPos = start; + } + + // If we need to re-position the characters. + if( setPos !== false ) + { + //console.log('Setpos keydown: ', setPos ); + setSelectionRange.apply(this, [setPos, setPos]); + } + + // Store the data on the element. + $this.data('numFormat', data); + + }, + + /** + * Handles keyup events, re-formatting numbers. + * + * @param object e : the keyup event object.s + * + * @return void; + */ + 'keyup.format' : function(e){ + + // Store these variables for use below. + var $this = $(this), + data = $this.data('numFormat'), + code = (e.keyCode ? e.keyCode : e.which), + start = getSelection.apply(this,['start']), + end = getSelection.apply(this,['end']), + setPos; // Check for negative characters being entered at the start of the string. @@ -414,67 +466,76 @@ $this.val('-'+$this.val()); start = 1; - data.c = 1-this.value.length; - data.init = 1; + data.c = 1-this.value.length; + data.init = 1; - $this.data('numFormat', data); + $this.data('numFormat', data); - setPos = this.value.length+data.c; - setSelectionRange.apply(this, [setPos, setPos]); + setPos = this.value.length+data.c; + setSelectionRange.apply(this, [setPos, setPos]); } - // Stop executing if the user didn't type a number key, a decimal, or a comma. - if( this.value === '' || (code < 48 || code > 57) && (code < 96 || code > 105 ) && code !== 8 ) return; - - // Re-format the textarea. - $this.val($this.val()); - - if( decimals > 0 ) - { - // If we haven't marked this item as 'initialised' - // then do so now. It means we should place the caret just - // before the decimal. This will never be un-initialised before - // the decimal character itself is entered. - if( data.init < 1 ) - { - start = this.value.length-decimals-( data.init < 0 ? 1 : 0 ); - data.c = start-this.value.length; - data.init = 1; - - $this.data('numFormat', data); - } - - // Increase the cursor position if the caret is to the right - // of the decimal place, and the character pressed isn't the delete key. - else if( start > this.value.length-decimals && code != 8 ) - { - data.c++; - - // Store the data, now that it's changed. - $this.data('numFormat', data); - } - } - - //console.log( 'Setting pos: ', start, decimals, this.value.length + data.c, this.value.length, data.c ); - - // Set the selection position. - setPos = this.value.length+data.c; - setSelectionRange.apply(this, [setPos, setPos]); - }, - - /** - * Reformat when pasting into the field. - * - * @param object e : jQuery event object. - * - * @return false : prevent default action. - */ - 'paste.format' : function(e){ - - // Defint $this. It's used twice!. - var $this = $(this), - original = e.originalEvent, - val = null; + // Stop executing if the user didn't type a number key, a decimal, or a comma. + if( this.value === '' || (code < 48 || code > 57) && (code < 96 || code > 105 ) && code !== 8 && code !== 46 && code !== 110 ) return; + + // Re-format the textarea. + $this.val($this.val()); + + if( decimals > 0 ) + { + // If we haven't marked this item as 'initialized' + // then do so now. It means we should place the caret just + // before the decimal. This will never be un-initialized before + // the decimal character itself is entered. + if( data.init < 1 ) + { + start = this.value.length-decimals-( data.init < 0 ? 1 : 0 ); + data.c = start-this.value.length; + data.init = 1; + + $this.data('numFormat', data); + } + + // Increase the cursor position if the caret is to the right + // of the decimal place, and the character pressed isn't the backspace key. + else if( start > this.value.length-decimals && code != 8 ) + { + data.c++; + + // Store the data, now that it's changed. + $this.data('numFormat', data); + } + } + + // Move caret to the right after delete key pressed + if (code == 46 && !data.isPartialSelection) + { + data.c++; + + // Store the data, now that it's changed. + $this.data('numFormat', data); + } + + //console.log( 'Setting pos: ', start, decimals, this.value.length + data.c, this.value.length, data.c ); + + // Set the selection position. + setPos = this.value.length+data.c; + setSelectionRange.apply(this, [setPos, setPos]); + }, + + /** + * Reformat when pasting into the field. + * + * @param object e : jQuery event object. + * + * @return false : prevent default action. + */ + 'paste.format' : function(e){ + + // Defint $this. It's used twice!. + var $this = $(this), + original = e.originalEvent, + val = null; // Get the text content stream. if (window.clipboardData && window.clipboardData.getData) { // IE @@ -483,48 +544,48 @@ val = original.clipboardData.getData('text/plain'); } - // Do the reformat operation. - $this.val(val); - - // Stop the actual content from being pasted. - e.preventDefault(); - return false; - } - - }) - - // Loop each element (which isn't blank) and do the format. - .each(function(){ - - var $this = $(this).data('numFormat',{ - c : -(decimals+1), - decimals : decimals, - thousands_sep : thousands_sep, - dec_point : dec_point, - regex_dec_num : regex_dec_num, - regex_dec : regex_dec, - init : false - }); - - // Return if the element is empty. - if( this.value === '' ) return; - - // Otherwise... format!! - $this.val($this.val()); - }); - } - else - { - // return the collection. - return this.each(function(){ - var $this = $(this), num = +$this.text().replace(regex_dec_num,'').replace(regex_dec,'.'); - $this.number( !isFinite(num) ? 0 : +num, decimals, dec_point, thousands_sep ); - }); - } - } - - // Add this number to the element as text. - return this.text( $.number.apply(window,arguments) ); + // Do the reformat operation. + $this.val(val); + + // Stop the actual content from being pasted. + e.preventDefault(); + return false; + } + + }) + + // Loop each element (which isn't blank) and do the format. + .each(function(){ + + var $this = $(this).data('numFormat',{ + c : -(decimals+1), + decimals : decimals, + thousands_sep : thousands_sep, + dec_point : dec_point, + regex_dec_num : regex_dec_num, + regex_dec : regex_dec, + init : this.value.indexOf('.') ? true : false + }); + + // Return if the element is empty. + if( this.value === '' ) return; + + // Otherwise... format!! + $this.val($this.val()); + }); + } + else + { + // return the collection. + return this.each(function(){ + var $this = $(this), num = +$this.text().replace(regex_dec_num,'').replace(regex_dec,'.'); + $this.number( !isFinite(num) ? 0 : +num, decimals, dec_point, thousands_sep ); + }); + } + } + + // Add this number to the element as text. + return this.text( $.number.apply(window,arguments) ); }; // @@ -538,47 +599,47 @@ // Check if a text valHook already exists. if( $.isPlainObject( $.valHooks.text ) ) { - // Preserve the original valhook function - // we'll call this for values we're not - // explicitly handling. - if( $.isFunction( $.valHooks.text.get ) ) origHookGet = $.valHooks.text.get; - if( $.isFunction( $.valHooks.text.set ) ) origHookSet = $.valHooks.text.set; + // Preserve the original valhook function + // we'll call this for values we're not + // explicitly handling. + if( $.isFunction( $.valHooks.text.get ) ) origHookGet = $.valHooks.text.get; + if( $.isFunction( $.valHooks.text.set ) ) origHookSet = $.valHooks.text.set; } else { - // Define an object for the new valhook. - $.valHooks.text = {}; + // Define an object for the new valhook. + $.valHooks.text = {}; } /** - * Define the valHook to return normalised field data against an input - * which has been tagged by the number formatter. - * - * @param object el : The raw DOM element that we're getting the value from. - * - * @return mixed : Returns the value that was written to the element as a - * javascript number, or undefined to let jQuery handle it normally. - */ + * Define the valHook to return normalised field data against an input + * which has been tagged by the number formatter. + * + * @param object el : The raw DOM element that we're getting the value from. + * + * @return mixed : Returns the value that was written to the element as a + * javascript number, or undefined to let jQuery handle it normally. + */ $.valHooks.text.get = function( el ){ // Get the element, and its data. var $this = $(el), num, negative, data = $this.data('numFormat'); - // Does this element have our data field? - if( !data ) - { - // Check if the valhook function already existed - if( $.isFunction( origHookGet ) ) - { - // There was, so go ahead and call it - return origHookGet(el); - } - else - { - // No previous function, return undefined to have jQuery - // take care of retrieving the value - return undefined; + // Does this element have our data field? + if( !data ) + { + // Check if the valhook function already existed + if( $.isFunction( origHookGet ) ) + { + // There was, so go ahead and call it + return origHookGet(el); + } + else + { + // No previous function, return undefined to have jQuery + // take care of retrieving the value + return undefined; } } else @@ -587,7 +648,6 @@ if( el.value === '' ) return ''; - // Convert to a number. num = +(el.value .replace( data.regex_dec_num, '' ) @@ -602,15 +662,15 @@ }; /** - * A valhook which formats a number when run against an input - * which has been tagged by the number formatter. - * - * @param object el : The raw DOM element (input element). - * @param float : The number to set into the value field. - * - * @return mixed : Returns the value that was written to the element, - * or undefined to let jQuery handle it normally. - */ + * A valhook which formats a number when run against an input + * which has been tagged by the number formatter. + * + * @param object el : The raw DOM element (input element). + * @param float : The number to set into the value field. + * + * @return mixed : Returns the value that was written to the element, + * or undefined to let jQuery handle it normally. + */ $.valHooks.text.set = function( el, val ) { // Get the element, and its data. @@ -621,17 +681,17 @@ if( !data ) { - // Check if the valhook function already exists - if( $.isFunction( origHookSet ) ) - { - // There was, so go ahead and call it - return origHookSet(el,val); - } - else - { - // No previous function, return undefined to have jQuery - // take care of retrieving the value - return undefined; + // Check if the valhook function already exists + if( $.isFunction( origHookSet ) ) + { + // There was, so go ahead and call it + return origHookSet(el,val); + } + else + { + // No previous function, return undefined to have jQuery + // take care of retrieving the value + return undefined; } } else @@ -639,11 +699,10 @@ var num = $.number( val, data.decimals, data.dec_point, data.thousands_sep ); // Make sure empties are set with correct signs. - if(val.indexOf('-') === 0 && +num === 0) - { - num = '-'+num; - } - +// if(val.indexOf('-') === 0 && +num === 0) +// { +// num = '-'+num; +// } // Otherwise, don't worry about other valhooks, just run ours. return el.value = num; } @@ -668,7 +727,6 @@ * @return string : The formatted number as a string. */ $.number = function( number, decimals, dec_point, thousands_sep ){ - // Set the default values here, instead so we can use them in the replace below. thousands_sep = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep; dec_point = (typeof dec_point === 'undefined') ? '.' : dec_point; @@ -686,22 +744,21 @@ .replace(new RegExp('[^0-9+\-Ee.]','g'),''); var n = !isFinite(+number) ? 0 : +number, - s = '', - toFixedFix = function (n, decimals) { - var k = Math.pow(10, decimals); - return '' + Math.round(n * k) / k; - }; + s = '', + toFixedFix = function (n, decimals) { + var k = Math.pow(10, decimals); + return '' + Math.round(n * k) / k; + }; // Fix for IE parseFloat(0.55).toFixed(0) = 0; s = (decimals ? toFixedFix(n, decimals) : '' + Math.round(n)).split('.'); if (s[0].length > 3) { - s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, thousands_sep); + s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, thousands_sep); } if ((s[1] || '').length < decimals) { - s[1] = s[1] || ''; - s[1] += new Array(decimals - s[1].length + 1).join('0'); + s[1] = s[1] || ''; + s[1] += new Array(decimals - s[1].length + 1).join('0'); } - return s.join(dec_point); }