<html><head>
<title>jQuery Number Format Plugin</title>
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<script src="script.js"></script>
<script type="text/javascript">
$(function(){
// Set up the number formatting.
$('#price').number(true);
// Get the value of the number for the demo.
$('#get_number').on('click',function(){
var val = $('#price').val();
$('#number_container').slideDown('fast');
$('#the_number').text( val !== '' ? val : '(empty)' );
});
});
</script>
<style>
#number_container{
border: 1px dotted #d0d0d0;
padding: 15px;
margin: 10px;
display: none;
background: #fafafa;
}
div.wrap{
margin: 10px;
padding-top: 15px;
}
button{
display: block;
margin-top: 25px;
}
</style>
</head>
<body cz-shortcut-listen="true">
<div id="container">
<div class="wrap">
<label for="number">How much would you like to pay?</label><br>
$<input type="text" id="price" name="number" class="test isNegative">
<button id="get_number">Get the Number</button>
</div>
<div id="number_container" style="display: block;">
<label for="the_number">The number is:</label>
<pre id="the_number"></pre>
</div>
</div>
<div class="selection_bubble_root" style="display: none;"></div></body></html>
/**
* jQuery number plug-in 2.1.5
* Copyright 2012, Digital Fusion
* Licensed under the MIT license.
* http://opensource.teamdf.com/license/
*
* A jQuery plugin which implements a permutation of phpjs.org's number_format to provide
* simple number formatting, insertion, and as-you-type masking of a number.
*
* @author Sam Sehnert
* @docs http://www.teamdf.com/web/jquery-number-format-redux/196/
*/
(function($){
"use strict";
/**
* Method for selecting a range of characters in an input/textarea.
*
* @param int rangeStart : Where we want the selection to start.
* @param int rangeEnd : Where we want the selection to end.
*
* @return void;
*/
function setSelectionRange( rangeStart, rangeEnd )
{
// Check which way we need to define the text range.
if( this.createTextRange )
{
var range = this.createTextRange();
range.collapse( true );
range.moveStart( 'character', rangeStart );
range.moveEnd( 'character', rangeEnd-rangeStart );
range.select();
}
// Alternate setSelectionRange method for supporting browsers.
else if( this.setSelectionRange )
{
this.focus();
this.setSelectionRange( rangeStart, rangeEnd );
}
}
/**
* Get the selection position for the given part.
*
* @param string part : Options, 'Start' or 'End'. The selection position to get.
*
* @return int : The index position of the selection part.
*/
function getSelection( part )
{
var pos = this.value.length;
// Work out the selection part.
part = ( part.toLowerCase() == 'start' ? 'Start' : 'End' );
if( document.selection ){
// The current selection
var range = document.selection.createRange(), stored_range, selectionStart, selectionEnd;
// We'll use this as a 'dummy'
stored_range = range.duplicate();
// Select all text
//stored_range.moveToElementText( this );
stored_range.expand('textedit');
// Now move 'dummy' end point to end point of original range
stored_range.setEndPoint( 'EndToEnd', range );
// Now we can calculate start and end points
selectionStart = stored_range.text.length - range.text.length;
selectionEnd = selectionStart + range.text.length;
return part == 'Start' ? selectionStart : selectionEnd;
}
else if(typeof(this['selection'+part])!="undefined")
{
pos = this['selection'+part];
}
return pos;
}
/**
* Substitutions for keydown keycodes.
* Allows conversion from e.which to ascii characters.
*/
var _keydown = {
codes : {
46 : 127,
188 : 44,
109 : 45,
190 : 46,
191 : 47,
192 : 96,
220 : 92,
222 : 39,
221 : 93,
219 : 91,
173 : 45,
187 : 61, //IE Key codes
186 : 59, //IE Key codes
189 : 45, //IE Key codes
110 : 46 //IE Key codes
},
shifts : {
96 : "~",
49 : "!",
50 : "@",
51 : "#",
52 : "$",
53 : "%",
54 : "^",
55 : "&",
56 : "*",
57 : "(",
48 : ")",
45 : "_",
61 : "+",
91 : "{",
93 : "}",
92 : "|",
59 : ":",
39 : "\"",
44 : "<",
46 : ">",
47 : "?"
}
};
/**
* jQuery number formatter plugin. This will allow you to format numbers on an element.
*
* @params proxied for format_number method.
*
* @return : The jQuery collection the method was called with.
*/
$.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),
chara = '', //unescape(e.originalEvent.keyIdentifier.replace('U+','%u')),
start = getSelection.apply(this,['start']),
end = getSelection.apply(this,['end']),
val = '',
setPos = false;
//an_test
// check to enable Negative sign
var isNegative = this.className.indexOf("isNegative");
console.log("code press:" + code);
if(isNegative == -1 && code == 189){
console.log("is -1");
e.preventDefault();
}
console.log(isNegative);
//end
// 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 ) // 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
{
// console.log("go here");
// 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.
// If there's any kind of selection, just ignore the input.
if( start === 0 && end === 0 && ( code === 189 || code === 109 ) )
{
$this.val('-'+$this.val());
start = 1;
data.c = 1-this.value.length;
data.init = 1;
$this.data('numFormat', data);
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 && 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
val = window.clipboardData.getData('Text');
} else if (original.clipboardData && original.clipboardData.getData) {
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 : 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) );
};
//
// Create .val() hooks to get and set formatted numbers in inputs.
//
// We check if any hooks already exist, and cache
// them in case we need to re-use them later on.
var origHookGet = null, origHookSet = null;
// 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;
}
else
{
// 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.
*/
$.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;
}
}
else
{
// Remove formatting, and return as number.
if( el.value === '' ) return '';
console.log("el value: "+ el.value);
// Convert to a number.
num = +(el.value
.replace(/[,\-]/g, '' )
.replace( data.regex_dec, '.' ));
console.log(num);
// If we've got a finite number, return it.
// Otherwise, simply return 0.
// Return as a string... thats what we're
// used to with .val()
return (el.value.indexOf('-') === 0 ? '-' : '')+num ;
}
};
/**
* 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.
var $this = $(el),
data = $this.data('numFormat');
// Does this element have our data field?
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;
}
}
else
{
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;
// }
return $.isFunction(origHookSet) ? origHookSet(el, num) : el.value = num;
}
};
/**
* The (modified) excellent number formatting method from PHPJS.org.
* http://phpjs.org/functions/number_format/
*
* @modified by Sam Sehnert (teamdf.com)
* - don't redefine dec_point, thousands_sep... just overwrite with defaults.
* - don't redefine decimals, just overwrite as numeric.
* - Generate regex for normalizing pre-formatted numbers.
*
* @param float number : The number you wish to format, or TRUE to use the text contents
* of the element as the number. Please note that this won't work for
* elements which have child nodes with text content.
* @param int decimals : The number of decimal places that should be displayed. Defaults to 0.
* @param string dec_point : The character to use as a decimal point. Defaults to '.'.
* @param string thousands_sep : The character to use as a thousands separator. Defaults to ','.
*
* @return string : The formatted number as a string.
*/
$.number = function( number, decimals, dec_point, thousands_sep ){
console.log("test here");
// Set the default values here, instead so we can use them in the replace below.
thousands_sep = (typeof thousands_sep === 'undefined') ? ( new Number(1000).toLocaleString() !== '1000' ? new Number(1000).toLocaleString().charAt(1) : '' ) : thousands_sep;
dec_point = (typeof dec_point === 'undefined') ? new Number(0.1).toLocaleString().charAt(1) : dec_point;
decimals = !isFinite(+decimals) ? 0 : Math.abs(decimals);
// Work out the unicode representation for the decimal place and thousand sep.
var u_dec = ('\\u'+('0000'+(dec_point.charCodeAt(0).toString(16))).slice(-4));
var u_sep = ('\\u'+('0000'+(thousands_sep.charCodeAt(0).toString(16))).slice(-4));
// Fix the number, so that it's an actual number.
number = (number + '')
.replace('\.', dec_point) // because the number if passed in as a float (having . as decimal point per definition) we need to replace this with the passed in decimal point character
.replace(new RegExp(u_sep,'g'),'')
.replace(new RegExp(u_dec,'g'),'.')
.replace(new RegExp('[^0-9+\-Ee.]','g'),'');
var n = !isFinite(+number) ? "" : +number,
s = '',
toFixedFix = function (n, decimals) {
return '' + (+(Math.round(('' + n).indexOf('e') > 0 ? n : n + 'e+' + decimals) + 'e-' + decimals));
};
// Fix for IE parseFloat(0.55).toFixed(0) = 0;
console.log("number: " + number);
var r = Math.round(n) === 0 ? '' : Math.round(n);
s = (decimals ? toFixedFix(n, decimals) : ''+ r).split('.');
console.log("s:" +s);
console.log("n:" +n);
if (s[0].length > 3) {
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');
}
console.log("s value:"+ s);
return s.join(dec_point);
}
})(jQuery);
/* Styles go here */