UNPKG

numbo

Version:

Convert number and monetary amount to written text. Also helpful for writing cheques (checks). Supports English. 支援中文

474 lines (457 loc) 15.9 kB
// Generated by CoffeeScript 1.12.7 /* Numbo is open source in: https://github.com/Edditoria/numbo under MIT license: https://github.com/Edditoria/numbo/blob/master/LICENSE.md */ (function() { var Numbo, numbo, slice = [].slice; Numbo = (function() { var convert_enUS, tools; function Numbo() {} tools = { trimWhitespace: function(str) { return str.replace(/^\s+|\s+$/g, ''); }, parse99: function(arr1, arr10, separator) { var i, lenArr1, num, results; if (separator == null) { separator = ' '; } lenArr1 = arr1.length; results = []; for (num = i = 0; i <= 99; num = ++i) { if (num < lenArr1) { results.push(arr1[num]); } else if (num % 10 === 0) { results.push(arr10[Math.floor(num / 10)]); } else { results.push(arr10[Math.floor(num / 10)] + separator + arr1[num % 10].toLowerCase()); } } return results; }, parseCents: function(str, option) { var mathOption, str3dp; if (option == null) { option = 'ceil'; } console.log('Depreciated: Parse the whole string using adjustDecimal() instead'); str3dp = (str + '000').slice(0, 3); mathOption = (function() { switch (option) { case 'round': return 'round'; case 'floor': return 'floor'; case 'ceil': return 'ceil'; default: console.log('Error: Option in parseCents() is invalid. Use the default option (Math.ceil)'); return 'ceil'; } })(); return Math[mathOption](parseInt(str3dp, 10) / 10); }, speakByDigit: function(str, n1, separator) { var i, item, len1, output; if (separator == null) { separator = ' '; } if (typeof str === 'string' && str.search(/\D/g) < 0) { output = []; for (i = 0, len1 = str.length; i < len1; i++) { item = str[i]; output.push(n1[+item]); } return output.join(separator); } else { console.log('Error: Invalid argument of speakByDigit()'); return null; } }, check: function(input, characters) { var acceptRegex, commaAfterDot, dotCount, illegalStr, inputStr, result; if (characters == null) { characters = ''; } result = false; inputStr = input.toString(); acceptRegex = /^[\+\-\$]*[\d\.\,]*|[\d\.\,]*/g; illegalStr = inputStr.replace(acceptRegex, ''); dotCount = (inputStr.match(/\./g) || []).length; commaAfterDot = inputStr.indexOf(',', inputStr.indexOf('.')) > 0; if (illegalStr === '' && dotCount < 2 && !commaAfterDot) { result = true; } return result; }, normalize: function(input, characters) { var dotIndex, inputArr, output, regexHead, regexTail; if (characters == null) { characters = ''; } if (typeof input !== 'string') { input = input.toString(); } regexHead = /,|^[$|\-]*(0|,)*(?!\.)/g; regexTail = /0*$/g; dotIndex = input.indexOf('.'); if (dotIndex === -1) { output = input.replace(regexHead, ''); } else { inputArr = tools.splitNum(input); inputArr[0] = inputArr[0].replace(regexHead, ''); inputArr[1] = inputArr[1].replace(regexTail, ''); output = inputArr.join('.'); } if (output.indexOf('.') === 0) { output = '0' + output; } if (output === '') { output = '0'; } return output.replace(/\.$/, ''); }, adjustDecimal: function(str, type, dp) { var carryInt, dec, dotIndex, int, parseDec; if (type == null) { type = 'ceil'; } if (dp == null) { dp = 2; } if (typeof str !== 'string') { str = str.toString(); } dotIndex = str.indexOf('.'); if (dotIndex === -1) { return str; } if (type !== 'ceil' && type !== 'floor' && type !== 'round') { type = 'ceil'; console.log('Error: type in adjustDecimal() is invalid. Use the default option (Math.ceil)'); } if (typeof dp !== 'number' || dp < 0) { dp = 2; console.log('Error: dp in adjustDecimal() is invalid. Use the default option (adjust to 2 decimal place)'); } parseDec = function(dec, type, dp) { dec = '0.' + dec; dec = Math[type](+(dec + 'e+' + dp)); dec = +(dec + 'e-' + dp); if (dec === 0) { return ''; } else if (dec === 1) { return '1'; } else { return (dec + '').replace(/^0/g, ''); } }; carryInt = function(int) { var index, intArr, isCarry, len, thisChar; len = int.length; if (len < 16) { return +int + 1 + ''; } else { intArr = int.split(''); index = len; isCarry = true; while (index && isCarry) { index--; thisChar = +intArr[index] + 1 + ''; if (thisChar === '10') { thisChar = '0'; } else { isCarry = false; } intArr[index] = thisChar; } if (isCarry) { intArr.unshift('1'); } return intArr.join(''); } }; if (type === 'ceil' || type === 'round') { int = str.slice(0, dotIndex); dec = str.slice(dotIndex + 1); dec = parseDec(dec, type, dp); if (dec === '1') { dec = ''; int = carryInt(int); } return int + dec; } else if (type === 'floor') { int = str.slice(0, dotIndex); dec = str.slice(dotIndex, dotIndex + dp + 1); if (+dec === 0) { dec = ''; } return int + dec; } else { console.log('Error: Unknown error in tools.adjustDecimal()'); return str; } }, splitNum: function(input) { var output; if (typeof input !== 'string') { input = input.toString(); } if (input.indexOf('.') === -1) { output = [input, '']; } else { output = input.split('.'); } if (output[0] === '') { output[0] = '0'; } return output; }, splitInt: function(int, digit) { var intTail, len, output, times; if (digit == null) { digit = 3; } if (typeof int !== 'string') { int = int.toString(); } output = []; times = Math.ceil(int.length / digit); while (times) { len = int.length; intTail = int.substring(len - digit, len); int = int.substring(0, len - digit); output.unshift(intTail); times--; } return output; } }; Numbo.prototype.tools = { trimWhitespace: tools.trimWhitespace, parse99: tools.parse99, parseCent: tools.parseCents, parseCents: tools.parseCents, speakByDigit: tools.speakByDigit, check: tools.check, validate: tools.check, normalize: tools.normalize, normalise: tools.normalize, adjustDecimal: tools.adjustDecimal, splitNum: tools.splitNum, splitInt: tools.splitInt }; convert_enUS = function(input, options, zeroCent) { var main, n1, n10, n1000, n1withZero, n99, speak999, speakAmt, speakInt, speakNum; if (options == null) { options = 'default'; } if (zeroCent == null) { zeroCent = false; } n1 = ['', 'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine', 'Ten', 'Eleven', 'Twelve', 'Thirteen', 'Fourteen', 'Fifteen', 'Sixteen', 'Seventeen', 'Eighteen', 'Nineteen']; n1withZero = ['Zero', 'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine', 'Ten', 'Eleven', 'Twelve', 'Thirteen', 'Fourteen', 'Fifteen', 'Sixteen', 'Seventeen', 'Eighteen', 'Nineteen']; n10 = ['', 'Ten', 'Twenty', 'Thirty', 'Forty', 'Fifty', 'Sixty', 'Seventy', 'Eighty', 'Ninety']; n1000 = ['', 'Thousand', 'Million', 'Billion', 'Trillion', 'Quadrillion', 'Quintillion', 'Sextillion', 'Septillion', 'Octillion', 'Nonillion', 'Decillion', 'Undecillion', 'Duodecillion', 'Tredecillion', 'Quattuordecillion', 'Quindecillion', 'Sexdecillion', 'Septendecillion', 'Octodecillion', 'Novemdecillion', 'Vigintillion']; n99 = tools.parse99(n1, n10, '-'); speak999 = function(num, addAnd) { var d100, hundred, output; if (addAnd == null) { addAnd = true; } if (num <= 99) { return n99[num]; } else if (num % 100 === 0) { return n1[Math.floor(num / 100)] + ' Hundred'; } else { d100 = Math.floor(num / 100); hundred = addAnd ? ' Hundred and ' : ' Hundred '; return output = n1[d100] + hundred + n99[num % 100]; } }; speakInt = function(numArr, addAnd) { var i, item, len1, num, output, times, unit; if (addAnd == null) { addAnd = true; } output = []; times = numArr.length; for (num = i = 0, len1 = numArr.length; i < len1; num = ++i) { item = numArr[num]; times--; if (item !== '000') { unit = ' ' + n1000[times]; item = speak999(parseInt(item, 10), addAnd); output.push(item + unit); } } return output.join(' ').replace(/^\s+|\s+$/g, ''); }; speakNum = function(str) { var dec, int, intArr, strSplited; if (str === '0') { return 'zero'; } else if (str === '1') { return 'one'; } else { strSplited = tools.splitNum(str); intArr = tools.splitInt(strSplited[0]); int = speakInt(intArr); if (int === '') { int = 'zero'; } dec = strSplited[1] === '' ? '' : ' point ' + tools.speakByDigit(strSplited[1], n1withZero, ' '); return (int + dec).toLowerCase(); } }; speakAmt = function(str, options, zeroCent) { var andWord, centUnit, cents, dec, dollarUnit, dollars, int, intArr, output, strSplited; if (options == null) { options = 'amount'; } if (str === '0') { output = 'Null'; } else { dollarUnit = ['Dollar', 'Dollars']; centUnit = ['Cent', 'Cents']; str = tools.adjustDecimal(str, 'ceil', 2); strSplited = tools.splitNum(str); intArr = tools.splitInt(strSplited[0]); int = speakInt(intArr, false); dollars = int === '' ? '' : int === 'One' ? ' ' + dollarUnit[0] : ' ' + dollarUnit[1]; dec = +((strSplited[1] + '0').slice(0, 2)); dec = n99[dec]; cents = dec === '' && int === '' ? 'Null' : dec === '' ? 'No Cent' : dec === 'One' ? ' Cent' : ' Cents'; andWord = dollars === '' ? '' : ' and '; output = int + dollars + andWord + dec + cents; } output = options === 'cheque' ? output === 'Null' ? 'Null' : output + ' Only' : options === 'amount' ? output.replace(' and No Cent', '').toLowerCase() : (console.log('Error: Option in speakAmt() is invalid.'), output = null); if (zeroCent === true) { output = output.replace('and No Cent', 'and Zero Cent'); } return output; }; main = function(input, options, zeroCent) { if (options == null) { options = 'default'; } if (zeroCent == null) { zeroCent = false; } if (input === '') { return null; } else if (input === '1e+100') { return 'Ding! One Google... Oops... One Googol!!'; } else { if (tools.check(input) === false) { console.log('Error: Invalid input value. Return null'); return null; } else { input = tools.normalize(input); switch (options) { case 'default': case 'number': case 'num': return speakNum(input); case 'cheque': case 'check': case 'chk': case 'chq': return speakAmt(input, 'cheque', zeroCent); case 'amount': case 'amt': return speakAmt(input, 'amount'); default: console.log('Error: Option in enUS is not valid'); return null; } } } }; return main(input, options, zeroCent); }; Numbo.prototype.enUS = convert_enUS; Numbo.prototype.convert = function() { var error, i, input, item, len1, options, otherOptions, plugin, template, zeroCent; input = arguments[0], options = 2 <= arguments.length ? slice.call(arguments, 1) : []; if (options.length === 0) { return convert_enUS(input); } else { plugin = ''; template = ''; zeroCent = false; otherOptions = []; error = false; for (i = 0, len1 = options.length; i < len1; i++) { item = options[i]; if (typeof item === 'string') { if (this[item] != null) { if (plugin === '') { plugin = item; } else { console.log('Error: Invalid option. You have selected more than one language/plugin. Returns null'); error = true; } } else if (template === '') { switch (item) { case 'default': template = 'default'; break; case 'number': case 'num': template = 'number'; break; case 'check': case 'cheque': case 'chk': case 'chq': template = 'cheque'; break; case 'amount': case 'amt': template = 'amount'; break; default: otherOptions.push(item); } } else { otherOptions.push(item); } } else { console.log('Error: Invalid option. Each option has to be a string.'); error = true; } } if (error === true) { return null; } else if (otherOptions.length > 0 && otherOptions.toString() !== 'zeroCent') { console.log('Error: Invalid option. Possibly more than one template is selected. Or, some option(s) are parsed into [otherOptions], but it is not allowed in current version yet.'); return null; } else { if (template === '') { template = 'default'; } if (plugin === '') { plugin = 'enUS'; } if (otherOptions.toString() === 'zeroCent') { zeroCent = true; } return this[plugin](input, template, zeroCent); } } }; return Numbo; })(); numbo = new Numbo(); if ((typeof module !== "undefined" && module !== null) && module.exports) { module.exports = numbo; } if (typeof window !== "undefined" && window !== null) { window.numbo = numbo; } }).call(this);