1/** 2 * jquery.numberformatter - Formatting/Parsing Numbers in jQuery 3 * 4 * Written by 5 * Michael Abernethy ([email protected]), 6 * Andrew Parry ([email protected]) 7 * 8 * Dual licensed under the MIT (MIT-LICENSE.txt) 9 * and GPL (GPL-LICENSE.txt) licenses. 10 * 11 * @author Michael Abernethy, Andrew Parry 12 * @version 1.2.3-SNAPSHOT ($Id$) 13 * 14 * Dependencies 15 * 16 * jQuery (http://jquery.com) 17 * jshashtable (http://www.timdown.co.uk/jshashtable) 18 * 19 * Notes & Thanks 20 * 21 * many thanks to advweb.nanasi.jp for his bug fixes 22 * jsHashtable is now used also, so thanks to the author for that excellent little class. 23 * 24 * This plugin can be used to format numbers as text and parse text as Numbers 25 * Because we live in an international world, we cannot assume that everyone 26 * uses "," to divide thousands, and "." as a decimal point. 27 * 28 * As of 1.2 the way this plugin works has changed slightly, parsing text to a number 29 * has 1 set of functions, formatting a number to text has it's own. Before things 30 * were a little confusing, so I wanted to separate the 2 out more. 31 * 32 * 33 * jQuery extension functions: 34 * 35 * formatNumber(options, writeBack, giveReturnValue) - Reads the value from the subject, parses to 36 * a Javascript Number object, then formats back to text using the passed options and write back to 37 * the subject. 38 * 39 * parseNumber(options) - Parses the value in the subject to a Number object using the passed options 40 * to decipher the actual number from the text, then writes the value as text back to the subject. 41 * 42 * 43 * Generic functions: 44 * 45 * formatNumber(numberString, options) - Takes a plain number as a string (e.g. '1002.0123') and returns 46 * a string of the given format options. 47 * 48 * parseNumber(numberString, options) - Takes a number as text that is formatted the same as the given 49 * options then and returns it as a plain Number object. 50 * 51 * To achieve the old way of combining parsing and formatting to keep say a input field always formatted 52 * to a given format after it has lost focus you'd simply use a combination of the functions. 53 * 54 * e.g. 55 * $("#salary").blur(function(){ 56 * $(this).parseNumber({format:"#,###.00", locale:"us"}); 57 * $(this).formatNumber({format:"#,###.00", locale:"us"}); 58 * }); 59 * 60 * The syntax for the formatting is: 61 * 0 = Digit 62 * # = Digit, zero shows as absent 63 * . = Decimal separator 64 * - = Negative sign 65 * , = Grouping Separator 66 * % = Percent (multiplies the number by 100) 67 * 68 * For example, a format of "#,###.00" and text of 4500.20 will 69 * display as "4.500,20" with a locale of "de", and "4,500.20" with a locale of "us" 70 * 71 * 72 * As of now, the only acceptable locales are 73 * Arab Emirates -> "ae" 74 * Australia -> "au" 75 * Austria -> "at" 76 * Brazil -> "br" 77 * Canada -> "ca" 78 * China -> "cn" 79 * Czech -> "cz" 80 * Denmark -> "dk" 81 * Egypt -> "eg" 82 * Finland -> "fi" 83 * France -> "fr" 84 * Germany -> "de" 85 * Greece -> "gr" 86 * Great Britain -> "gb" 87 * Hong Kong -> "hk" 88 * India -> "in" 89 * Israel -> "il" 90 * Japan -> "jp" 91 * Russia -> "ru" 92 * South Korea -> "kr" 93 * Spain -> "es" 94 * Sweden -> "se" 95 * Switzerland -> "ch" 96 * Taiwan -> "tw" 97 * Thailand -> "th" 98 * United States -> "us" 99 * Vietnam -> "vn" 100 **/ 101 102(function(jQuery) { 103 104 var nfLocales = new Hashtable(); 105 106 var nfLocalesLikeUS = [ 'ae','au','ca','cn','eg','gb','hk','il','in','jp','sk','th','tw','us' ]; 107 var nfLocalesLikeDE = [ 'at','br','de','dk','es','gr','it','nl','pt','tr','vn' ]; 108 var nfLocalesLikeFR = [ 'cz','fi','fr','ru','se','pl' ]; 109 var nfLocalesLikeCH = [ 'ch' ]; 110 111 var nfLocaleFormatting = [ [".", ","], [",", "."], [",", " "], [".", "'"] ]; 112 var nfAllLocales = [ nfLocalesLikeUS, nfLocalesLikeDE, nfLocalesLikeFR, nfLocalesLikeCH ] 113 114 function FormatData(dec, group, neg) { 115 this.dec = dec; 116 this.group = group; 117 this.neg = neg; 118 }; 119 120 function init() { 121 // write the arrays into the hashtable 122 for (var localeGroupIdx = 0; localeGroupIdx < nfAllLocales.length; localeGroupIdx++) { 123 localeGroup = nfAllLocales[localeGroupIdx]; 124 for (var i = 0; i < localeGroup.length; i++) { 125 nfLocales.put(localeGroup[i], localeGroupIdx); 126 } 127 } 128 }; 129 130 function formatCodes(locale, isFullLocale) { 131 if (nfLocales.size() == 0) 132 init(); 133 134 // default values 135 var dec = "."; 136 var group = ","; 137 var neg = "-"; 138 139 if (isFullLocale == false) { 140 // Extract and convert to lower-case any language code from a real 'locale' formatted string, if not use as-is 141 // (To prevent locale format like : "fr_FR", "en_US", "de_DE", "fr_FR", "en-US", "de-DE") 142 if (locale.indexOf('_') != -1) 143 locale = locale.split('_')[1].toLowerCase(); 144 else if (locale.indexOf('-') != -1) 145 locale = locale.split('-')[1].toLowerCase(); 146 } 147 148 // hashtable lookup to match locale with codes 149 var codesIndex = nfLocales.get(locale); 150 if (codesIndex) { 151 var codes = nfLocaleFormatting[codesIndex];
152 if (codes) { 153 dec = codes[0]; 154 group = codes[1]; 155 } 156 } 157 return new FormatData(dec, group, neg); 158 }; 159 160 161 /* Formatting Methods */ 162 163 164 /** 165 * Formats anything containing a number in standard js number notation. 166 * 167 * @param {Object} options The formatting options to use 168 * @param {Boolean} writeBack (true) If the output value should be written back to the subject 169 * @param {Boolean} giveReturnValue (true) If the function should return the output string 170 */ 171 jQuery.fn.formatNumber = function(options, writeBack, giveReturnValue) { 172 173 return this.each(function() { 174 // enforce defaults 175 if (writeBack == null) 176 writeBack = true; 177 if (giveReturnValue == null) 178 giveReturnValue = true; 179 180 // get text 181 var text; 182 if (jQuery(this).is(":input")) 183 text = new String(jQuery(this).val()); 184 else 185 text = new String(jQuery(this).text()); 186 187 // format 188 var returnString = jQuery.formatNumber(text, options); 189 190 // set formatted string back, only if a success 191// if (returnString) { 192 if (writeBack) { 193 if (jQuery(this).is(":input")) 194 jQuery(this).val(returnString); 195 else 196 jQuery(this).text(returnString); 197 } 198 if (giveReturnValue) 199 return returnString; 200// } 201// return ''; 202 }); 203 }; 204 205 /** 206 * First parses a string and reformats it with the given options. 207 * 208 * @param {Object} numberString 209 * @param {Object} options 210 */ 211 jQuery.formatNumber = function(numberString, options){ 212 var options = jQuery.extend({}, jQuery.fn.formatNumber.defaults, options); 213 var formatData = formatCodes(options.locale.toLowerCase(), options.isFullLocale); 214 215 var dec = formatData.dec; 216 var group = formatData.group; 217 var neg = formatData.neg; 218 219 var validFormat = "0#-,."; 220 221 // strip all the invalid characters at the beginning and the end 222 // of the format, and we'll stick them back on at the end 223 // make a special case for the negative sign "-" though, so 224 // we can have formats like -$23.32 225 var prefix = ""; 226 var negativeInFront = false; 227 for (var i = 0; i < options.format.length; i++) { 228 if (validFormat.indexOf(options.format.charAt(i)) == -1) 229 prefix = prefix + options.format.charAt(i); 230 else 231 if (i == 0 && options.format.charAt(i) == '-') { 232 negativeInFront = true; 233 continue; 234 } 235 else 236 break; 237 } 238 var suffix = ""; 239 for (var i = options.format.length - 1; i >= 0; i--) { 240 if (validFormat.indexOf(options.format.charAt(i)) == -1) 241 suffix = options.format.charAt(i) + suffix; 242 else 243 break; 244 } 245 246 options.format = options.format.substring(prefix.length); 247 options.format = options.format.substring(0, options.format.length - suffix.length); 248 249 // now we need to convert it into a number 250 //while (numberString.indexOf(group) > -1) 251 // numberString = numberString.replace(group, ''); 252 //var number = new Number(numberString.replace(dec, ".").replace(neg, "-")); 253 var number = new Number(numberString); 254 255 return jQuery._formatNumber(number, options, suffix, prefix, negativeInFront); 256 }; 257 258 /** 259 * Formats a Number object into a string, using the given formatting options 260 * 261 * @param {Object} numberString 262 * @param {Object} options 263 */ 264 jQuery._formatNumber = function(number, options, suffix, prefix, negativeInFront) { 265 var options = jQuery.extend({}, jQuery.fn.formatNumber.defaults, options); 266 var formatData = formatCodes(options.locale.toLowerCase(), options.isFullLocale); 267 268 var dec = formatData.dec; 269 var group = formatData.group; 270 var neg = formatData.neg; 271 272 var forcedToZero = false; 273 if (isNaN(number)) { 274 if (options.nanForceZero == true) { 275 number = 0; 276 forcedToZero = true; 277 } else 278 return null; 279 } 280 281 // special case for percentages 282 if (suffix == "%") 283 number = number * 100; 284 285 var returnString = ""; 286 if (options.format.indexOf(".") > -1) { 287 var decimalPortion = dec; 288 var decimalFormat = options.format.substring(options.format.lastIndexOf(".") + 1); 289 290 // round or truncate number as needed 291 if (options.round == true) 292 number = new Number(number.toFixed(decimalFormat.length)); 293 else { 294 var numStr = number.toString(); 295 numStr = numStr.substring(0, numStr.lastIndexOf('.') + decimalFormat.length + 1); 296 number = new Number(numStr); 297 } 298 299 var decimalValue = number % 1; 300 var decimalString = new String(decimalValue.toFixed(decimalFormat.length)); 301 decimalString = decimalString.substring(decimalString.lastIndexOf(".") + 1); 302 303 for (var i = 0; i < decimalFormat.length; i++) { 304 if (decimalFormat.charAt(i) == '#' && decimalString.charAt(i) != '0') { 305 decimalPortion += decimalString.charAt(i); 306 continue; 307 } else if (decimalFormat.charAt(i) == '#' && decimalString.charAt(i) == '0') { 308 var notParsed = decimalString.substring(i); 309 if (notParsed.match('[1-9]')) { 310 decimalPortion += decimalString.charAt(i); 311 continue; 312 } else 313 break; 314 } else if (decimalFormat.charAt(i) == "0") 315 decimalPortion += decimalString.charAt(i); 316 } 317 returnString += decimalPortion 318 } else 319 number = Math.round(number); 320 321 var ones = Math.floor(number); 322 if (number < 0) 323 ones = Math.ceil(number); 324 325 var onesFormat = ""; 326 if (options.format.indexOf(".") == -1) 327 onesFormat = options.format; 328 else 329 onesFormat = options.format.substring(0, options.format.indexOf(".")); 330 331 var onePortion = ""; 332 if (!(ones == 0 && onesFormat.substr(onesFormat.length - 1) == '#') || forcedToZero) { 333 // find how many digits are in the group 334 var oneText = new String(Math.abs(ones)); 335 var groupLength = 9999; 336 if (onesFormat.lastIndexOf(",") != -1) 337 groupLength = onesFormat.length - onesFormat.lastIndexOf(",") - 1; 338 var groupCount = 0; 339 for (var i = oneText.length - 1; i > -1; i--) { 340 onePortion = oneText.charAt(i) + onePortion; 341 groupCount++; 342 if (groupCount == groupLength && i != 0) { 343 onePortion = group + onePortion; 344 groupCount = 0; 345 } 346 } 347 348 // account for any pre-data padding 349 if (onesFormat.length > onePortion.length) { 350 var padStart = onesFormat.indexOf('0'); 351 if (padStart != -1) { 352 var padLen = onesFormat.length - padStart; 353 354 // pad to left with 0's or group char 355 var pos = onesFormat.length - onePortion.length - 1; 356 while (onePortion.length < padLen) { 357 var padChar = onesFormat.charAt(pos); 358 // replace with real group char if needed 359 if (padChar == ',') 360 padChar = group; 361 onePortion = padChar + onePortion; 362 pos--; 363 } 364 } 365 } 366 } 367 368 if (!onePortion && onesFormat.indexOf('0', onesFormat.length - 1) !== -1) 369 onePortion = '0'; 370 371 returnString = onePortion + returnString; 372 373 // handle special case where negative is in front of the invalid characters 374 if (number < 0 && negativeInFront && prefix.length > 0) 375 prefix = neg + prefix; 376 else if (number < 0) 377 returnString = neg + returnString; 378 379 if (!options.decimalSeparatorAlwaysShown) { 380 if (returnString.lastIndexOf(dec) == returnString.length - 1) { 381 returnString = returnString.substring(0, returnString.length - 1); 382 } 383 } 384 returnString = prefix + returnString + suffix; 385 return returnString; 386 }; 387 388 389 /* Parsing Methods */ 390 391 392 /** 393 * Parses a number of given format from the element and returns a Number object. 394 * @param {Object} options 395 */ 396 jQuery.fn.parseNumber = function(options, writeBack, giveReturnValue) { 397 // enforce defaults 398 if (writeBack == null) 399 writeBack = true; 400 if (giveReturnValue == null) 401 giveReturnValue = true; 402 403 // get text 404 var text; 405 if (jQuery(this).is(":input")) 406 text = new String(jQuery(this).val()); 407 else 408 text = new String(jQuery(this).text()); 409 410 // parse text 411 var number = jQuery.parseNumber(text, options); 412 413 if (number) { 414 if (writeBack) { 415 if (jQuery(this).is(":input")) 416 jQuery(this).val(number.toString()); 417 else 418 jQuery(this).text(number.toString()); 419 } 420 if (giveReturnValue) 421 return number; 422 } 423 }; 424 425 /** 426 * Parses a string of given format into a Number object. 427 * 428 * @param {Object} string 429 * @param {Object} options 430 */ 431 jQuery.parseNumber = function(numberString, options) { 432 var options = jQuery.extend({}, jQuery.fn.parseNumber.defaults, options); 433 var formatData = formatCodes(options.locale.toLowerCase(), options.isFullLocale); 434 435 var dec = formatData.dec; 436 var group = formatData.group; 437 var neg = formatData.neg; 438
439 var valid = "1234567890.-"; 440 441 // now we need to convert it into a number 442 while (numberString.indexOf(group)>-1) 443 numberString = numberString.replace(group,''); 444 numberString = numberString.replace(dec,".").replace(neg,"-"); 445 var validText = ""; 446 var hasPercent = false; 447 if (numberString.charAt(numberString.length - 1) == "%" || options.isPercentage == true) 448 hasPercent = true; 449 for (var i=0; i<numberString.length; i++) { 450 if (valid.indexOf(numberString.charAt(i))>-1) 451 validText = validText + numberString.charAt(i); 452 } 453 var number = new Number(validText); 454 if (hasPercent) { 455 number = number / 100; 456 var decimalPos = validText.indexOf('.'); 457 if (decimalPos != -1) { 458 var decimalPoints = validText.length - decimalPos - 1; 459 number = number.toFixed(decimalPoints + 2); 460 } else { 461 number = number.toFixed(validText.length - 1); 462 } 463 } 464 465 return number; 466 }; 467 468 jQuery.fn.parseNumber.defaults = { 469 locale: "us", 470 decimalSeparatorAlwaysShown: false, 471 isPercentage: false, 472 isFullLocale: false 473 }; 474 475 jQuery.fn.formatNumber.defaults = { 476 format: "#,###.00", 477 locale: "us", 478 decimalSeparatorAlwaysShown: false, 479 nanForceZero: true, 480 round: true, 481 isFullLocale: false 482 }; 483 484 Number.prototype.toFixed = function(precision) { 485 return jQuery._roundNumber(this, precision); 486 }; 487 488 jQuery._roundNumber = function(number, decimalPlaces) { 489 var power = Math.pow(10, decimalPlaces || 0); 490 var value = String(Math.round(number * power) / power); 491 492 // ensure the decimal places are there 493 if (decimalPlaces > 0) { 494 var dp = value.indexOf("."); 495 if (dp == -1) { 496 value += '.'; 497 dp = 0; 498 } else { 499 dp = value.length - (dp + 1); 500 } 501 502 while (dp < decimalPlaces) { 503 value += '0'; 504 dp++; 505 } 506 } 507 return value; 508 }; 509 510 })(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.