From f335d310acd84a3d7cd5af3f0876a18a1c1887ac Mon Sep 17 00:00:00 2001 From: SheetJS Date: Sun, 30 Apr 2017 00:54:41 -0400 Subject: [PATCH] version bump 0.9.1: dateNF + sign - dateNF option for default date format override - general format renders undefined/null as empty string - ignore text elements when searching for decimal point - bubble negative sign to the front when format starts with text - fixes for eslint + closure - updated frac to 1.0.6 Issues: - fixes #10 h/t @adamgundy @SegFaultx64 @RichardCzechowski - fixes #15 h/t @wilg - fixes #25 h/t @dougschiller - fixes #26 h/t @rjmcguire --- .eslintrc | 19 ++++ .gitignore | 23 ++++- .npmignore | 30 ++++++- Makefile | 23 ++++- README.md | 2 +- bits/01_version.js | 2 +- bits/20_consts.js | 32 ------- bits/25_table.js | 35 ++++++++ bits/30_frac.js | 8 +- bits/40_general.js | 2 + bits/60_number.js | 212 ++------------------------------------------- bits/63_numflt.js | 80 +++++++++++++++++ bits/65_numinth.js | 29 +++++++ bits/66_numint.js | 83 ++++++++++++++++++ bits/69_numfoot.js | 3 + bits/81_fmttype.js | 6 +- bits/82_eval.js | 22 +++-- bits/90_main.js | 13 ++- bits/98_exports.js | 1 + index.html | 113 ++++++++++++++---------- package.json | 4 +- ssf.flow.js | 142 +++++++++++++++++------------- ssf.js | 136 +++++++++++++++++------------ test/dateNF.js | 16 ++++ test/general.js | 2 +- test/oddities.json | 12 ++- 26 files changed, 620 insertions(+), 430 deletions(-) create mode 100644 .eslintrc create mode 100644 bits/25_table.js create mode 100644 bits/63_numflt.js create mode 100644 bits/65_numinth.js create mode 100644 bits/66_numint.js create mode 100644 bits/69_numfoot.js create mode 100644 test/dateNF.js diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..686a33e --- /dev/null +++ b/.eslintrc @@ -0,0 +1,19 @@ +{ + "env": { "shared-node-browser":true }, + "globals": {}, + "parserOptions": { + "ecmaVersion": 3, + }, + "plugins": [ "html", "json" ], + "rules": { + "no-use-before-define": [ 1, { + "functions":false, "classes":true, "variables":false + }], + "no-bitwise": 0, + "curly": 0, + "comma-style": [ 2, "last" ], + "no-trailing-spaces": 2, + "semi": [ 2, "always" ], + "comma-dangle": [ 2, "never" ] + } +} diff --git a/.gitignore b/.gitignore index 6c59530..9e77414 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,28 @@ -node_modules/ +node_modules misc/coverage.html .vocrc v8.log +tmp +*.tgz +*.[tT][xX][tT] +*.[cC][sS][vV] +*.[dD][iIbB][fF] +*.[pP][rR][nN] +*.[sS][lL][kK] +*.socialcalc +*.[xX][lL][sSwWcC] +*.[xX][lL][sS][xXmMbB] +*.[oO][dD][sS] +*.[fF][oO][dD][sS] +*.[xX][mM][lL] +*.[uU][oO][sS] +*.[wW][kKqQbB][S1234567890] +*.[qQ][pP][wW] +*.123 +*.htm +*.html +*.sheetjs +*.exe perf.log ctest/test.js ctest/sauce* diff --git a/.npmignore b/.npmignore index fb4bab2..5773d51 100644 --- a/.npmignore +++ b/.npmignore @@ -1,8 +1,33 @@ test/ +ctest/ +index.html misc/ -node_modules/ -misc/coverage.html +node_modules +*.tgz +_book +book.json +tmp +*.[tT][xX][tT] +*.[cC][sS][vV] +*.[dD][iIbB][fF] +*.[pP][rR][nN] +*.[sS][lL][kK] +*.socialcalc +*.[xX][lL][sSwWcC] +*.[xX][lL][sS][xXmMbB] +*.[oO][dD][sS] +*.[fF][oO][dD][sS] +*.[xX][mM][lL] +*.[uU][oO][sS] +*.[wW][kKqQbB][S1234567890] +*.[qQ][pP][wW] +*.123 +*.htm +*.html +*.sheetjs +*.exe .gitignore +.eslintrc .jshintrc Makefile .npmignore @@ -14,4 +39,3 @@ bits/ .vocrc v8.log perf.log -ssf*.tgz diff --git a/Makefile b/Makefile index 80cab86..f490d39 100755 --- a/Makefile +++ b/Makefile @@ -1,5 +1,8 @@ SHELL=/bin/bash LIB=ssf +REQS= +ADDONS= +AUXTARGETS= CMDS=bin/ssf.njs HTMLLINT= @@ -11,6 +14,7 @@ FLOWAUX=$(patsubst %.js,%.flow.js,$(AUXTARGETS)) AUXSCPTS= FLOWTGTS=$(TARGET) $(AUXTARGETS) $(AUXSCPTS) UGLIFYOPTS=--support-ie8 +CLOSURE=/usr/local/lib/node_modules/google-closure-compiler/compiler.jar ## Main Targets @@ -37,8 +41,8 @@ clean: ## Remove targets and build artifacts test mocha: ## Run test suite mocha -R spec -t 30000 -.PHONY: test_min -test_min: +.PHONY: test_misc +test_misc: MINTEST=1 npm test .PHONY: travis @@ -47,18 +51,29 @@ travis: ## Run test suite with minimal output .PHONY: ctest ctest: - browserify -t brfs test/{exp,fraction,general,implied,oddities,utilities,comma}.js > ctest/test.js + browserify -t brfs test/{dateNF,exp,fraction,general,implied,oddities,utilities,comma}.js > ctest/test.js + +.PHONY: ctestserv +ctestserv: ## Start a test server on port 8000 + @cd ctest && python -mSimpleHTTPServer + ## Code Checking .PHONY: lint -lint: $(TARGET) $(AUXTARGETS) ## Run jshint and jscs checks +lint: $(TARGET) $(AUXTARGETS) ## Run eslint checks + @eslint --ext .js,.njs,.json,.html,.htm $(TARGET) $(AUXTARGETS) $(CMDS) $(HTMLLINT) package.json bower.json + if [ -e $(CLOSURE) ]; then java -jar $(CLOSURE) $(REQS) $(FLOWTARGET) --jscomp_warning=reportUnknownTypes >/dev/null; fi + +.PHONY: old-lint +old-lint: $(TARGET) $(AUXTARGETS) ## Run jshint and jscs checks @jshint --show-non-errors $(TARGET) $(AUXTARGETS) @jshint --show-non-errors test/ @jshint --show-non-errors $(CMDS) @jshint --show-non-errors package.json @jshint --show-non-errors --extract=always $(HTMLLINT) @jscs $(TARGET) $(AUXTARGETS) + if [ -e $(CLOSURE) ]; then java -jar $(CLOSURE) $(REQS) $(FLOWTARGET) --jscomp_warning=reportUnknownTypes >/dev/null; fi .PHONY: flow flow: lint ## Run flow checker diff --git a/README.md b/README.md index ae22e17..54132fa 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# SSF +# [SheetJS SSF](http://sheetjs.com) ssf (SpreadSheet Format) is a pure-JS library to format data using ECMA-376 spreadsheet format codes (used in popular spreadsheet software packages). diff --git a/bits/01_version.js b/bits/01_version.js index aa5adaa..e54e7fd 100644 --- a/bits/01_version.js +++ b/bits/01_version.js @@ -1 +1 @@ -SSF.version = '0.9.0'; +SSF.version = '0.9.1'; diff --git a/bits/20_consts.js b/bits/20_consts.js index 7586250..a0b8885 100644 --- a/bits/20_consts.js +++ b/bits/20_consts.js @@ -1,35 +1,3 @@ -var table_fmt = { - /*::[*/0/*::]*/: 'General', - /*::[*/1/*::]*/: '0', - /*::[*/2/*::]*/: '0.00', - /*::[*/3/*::]*/: '#,##0', - /*::[*/4/*::]*/: '#,##0.00', - /*::[*/9/*::]*/: '0%', - /*::[*/10/*::]*/: '0.00%', - /*::[*/11/*::]*/: '0.00E+00', - /*::[*/12/*::]*/: '# ?/?', - /*::[*/13/*::]*/: '# ??/??', - /*::[*/14/*::]*/: 'm/d/yy', - /*::[*/15/*::]*/: 'd-mmm-yy', - /*::[*/16/*::]*/: 'd-mmm', - /*::[*/17/*::]*/: 'mmm-yy', - /*::[*/18/*::]*/: 'h:mm AM/PM', - /*::[*/19/*::]*/: 'h:mm:ss AM/PM', - /*::[*/20/*::]*/: 'h:mm', - /*::[*/21/*::]*/: 'h:mm:ss', - /*::[*/22/*::]*/: 'm/d/yy h:mm', - /*::[*/37/*::]*/: '#,##0 ;(#,##0)', - /*::[*/38/*::]*/: '#,##0 ;[Red](#,##0)', - /*::[*/39/*::]*/: '#,##0.00;(#,##0.00)', - /*::[*/40/*::]*/: '#,##0.00;[Red](#,##0.00)', - /*::[*/45/*::]*/: 'mm:ss', - /*::[*/46/*::]*/: '[h]:mm:ss', - /*::[*/47/*::]*/: 'mmss.0', - /*::[*/48/*::]*/: '##0.0E+0', - /*::[*/49/*::]*/: '@', - /*::[*/56/*::]*/: '"上午/下午 "hh"時"mm"分"ss"秒 "', - /*::[*/65535/*::]*/: 'General' -}; var days/*:Array >*/ = [ ['Sun', 'Sunday'], ['Mon', 'Monday'], diff --git a/bits/25_table.js b/bits/25_table.js new file mode 100644 index 0000000..01644ba --- /dev/null +++ b/bits/25_table.js @@ -0,0 +1,35 @@ +function init_table(t/*:any*/) { + t[0]= 'General'; + t[1]= '0'; + t[2]= '0.00'; + t[3]= '#,##0'; + t[4]= '#,##0.00'; + t[9]= '0%'; + t[10]= '0.00%'; + t[11]= '0.00E+00'; + t[12]= '# ?/?'; + t[13]= '# ??/??'; + t[14]= 'm/d/yy'; + t[15]= 'd-mmm-yy'; + t[16]= 'd-mmm'; + t[17]= 'mmm-yy'; + t[18]= 'h:mm AM/PM'; + t[19]= 'h:mm:ss AM/PM'; + t[20]= 'h:mm'; + t[21]= 'h:mm:ss'; + t[22]= 'm/d/yy h:mm'; + t[37]= '#,##0 ;(#,##0)'; + t[38]= '#,##0 ;[Red](#,##0)'; + t[39]= '#,##0.00;(#,##0.00)'; + t[40]= '#,##0.00;[Red](#,##0.00)'; + t[45]= 'mm:ss'; + t[46]= '[h]:mm:ss'; + t[47]= 'mmss.0'; + t[48]= '##0.0E+0'; + t[49]= '@'; + t[56]= '"上午/下午 "hh"時"mm"分"ss"秒 "'; + t[65535]= 'General'; +} + +var table_fmt = {}; +init_table(table_fmt); diff --git a/bits/30_frac.js b/bits/30_frac.js index dacffd0..28c87b9 100644 --- a/bits/30_frac.js +++ b/bits/30_frac.js @@ -1,4 +1,4 @@ -function frac(x, D, mixed) { +function frac(x/*:number*/, D/*:number*/, mixed/*:?boolean*/)/*:Array*/ { var sgn = x < 0 ? -1 : 1; var B = x * sgn; var P_2 = 0, P_1 = 1, P = 0; @@ -8,15 +8,13 @@ function frac(x, D, mixed) { A = Math.floor(B); P = A * P_1 + P_2; Q = A * Q_1 + Q_2; - if((B - A) < 0.0000000005) break; + if((B - A) < 0.00000005) break; B = 1 / (B - A); P_2 = P_1; P_1 = P; Q_2 = Q_1; Q_1 = Q; } - if(Q > D) { Q = Q_1; P = P_1; } - if(Q > D) { Q = Q_2; P = P_2; } + if(Q > D) { if(Q_1 > D) { Q = Q_2; P = P_2; } else { Q = Q_1; P = P_1; } } if(!mixed) return [0, sgn * P, Q]; - if(Q===0) throw "Unexpected state: "+P+" "+P_1+" "+P_2+" "+Q+" "+Q_1+" "+Q_2; var q = Math.floor(sgn * P/Q); return [q, sgn*P - q*Q, Q]; } diff --git a/bits/40_general.js b/bits/40_general.js index 340f20b..95fcb78 100644 --- a/bits/40_general.js +++ b/bits/40_general.js @@ -36,6 +36,8 @@ function general_fmt(v/*:any*/, opts/*:?any*/) { case 'string': return v; case 'boolean': return v ? "TRUE" : "FALSE"; case 'number': return (v|0) === v ? general_fmt_int(v, opts) : general_fmt_num(v, opts); + case 'undefined': return ""; + case 'object': if(v == null) return ""; } throw new Error("unsupported value in General format: " + v); } diff --git a/bits/60_number.js b/bits/60_number.js index a6521b8..093568c 100644 --- a/bits/60_number.js +++ b/bits/60_number.js @@ -51,211 +51,15 @@ function hashq(str/*:string*/)/*:string*/ { } function rnd(val/*:number*/, d/*:number*/)/*:string*/ { var dd = Math.pow(10,d); return ""+(Math.round(val * dd)/dd); } function dec(val/*:number*/, d/*:number*/)/*:number*/ { - if (d < ('' + Math.round((val-Math.floor(val))*Math.pow(10,d))).length) { - return 0; - } - return Math.round((val-Math.floor(val))*Math.pow(10,d)); + if (d < ('' + Math.round((val-Math.floor(val))*Math.pow(10,d))).length) { + return 0; + } + return Math.round((val-Math.floor(val))*Math.pow(10,d)); } function carry(val/*:number*/, d/*:number*/)/*:number*/ { - if (d < ('' + Math.round((val-Math.floor(val))*Math.pow(10,d))).length) { - return 1; - } - return 0; + if (d < ('' + Math.round((val-Math.floor(val))*Math.pow(10,d))).length) { + return 1; + } + return 0; } function flr(val/*:number*/)/*:string*/ { if(val < 2147483647 && val > -2147483648) return ""+(val >= 0 ? (val|0) : (val-1|0)); return ""+Math.floor(val); } -function write_num_flt(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string*/ { - if(type.charCodeAt(0) === 40 && !fmt.match(closeparen)) { - var ffmt = fmt.replace(/\( */,"").replace(/ \)/,"").replace(/\)/,""); - if(val >= 0) return write_num_flt('n', ffmt, val); - return '(' + write_num_flt('n', ffmt, -val) + ')'; - } - if(fmt.charCodeAt(fmt.length - 1) === 44) return write_num_cm(type, fmt, val); - if(fmt.indexOf('%') !== -1) return write_num_pct(type, fmt, val); - if(fmt.indexOf('E') !== -1) return write_num_exp(fmt, val); - if(fmt.charCodeAt(0) === 36) return "$"+write_num_flt(type,fmt.substr(fmt.charAt(1)==' '?2:1),val); - var o; - var r/*:?Array*/, ri, ff, aval = Math.abs(val), sign = val < 0 ? "-" : ""; - if(fmt.match(/^00+$/)) return sign + pad0r(aval,fmt.length); - if(fmt.match(/^[#?]+$/)) { - o = pad0r(val,0); if(o === "0") o = ""; - return o.length > fmt.length ? o : hashq(fmt.substr(0,fmt.length-o.length)) + o; - } - if((r = fmt.match(frac1))) return write_num_f1(r, aval, sign); - if(fmt.match(/^#+0+$/)) return sign + pad0r(aval,fmt.length - fmt.indexOf("0")); - if((r = fmt.match(dec1))) { - // $FlowIgnore - o = rnd(val, r[1].length).replace(/^([^\.]+)$/,"$1."+r[1]).replace(/\.$/,"."+r[1]).replace(/\.(\d*)$/,function($$, $1) { return "." + $1 + fill("0", /*::(*/r/*::||[""])*/[1].length-$1.length); }); - return fmt.indexOf("0.") !== -1 ? o : o.replace(/^0\./,"."); - } - fmt = fmt.replace(/^#+([0.])/, "$1"); - if((r = fmt.match(/^(0*)\.(#*)$/))) { - return sign + rnd(aval, r[2].length).replace(/\.(\d*[1-9])0*$/,".$1").replace(/^(-?\d*)$/,"$1.").replace(/^0\./,r[1].length?"0.":"."); - } - if((r = fmt.match(/^#,##0(\.?)$/))) return sign + commaify(pad0r(aval,0)); - if((r = fmt.match(/^#,##0\.([#0]*0)$/))) { - return val < 0 ? "-" + write_num_flt(type, fmt, -val) : commaify(""+(Math.floor(val) + carry(val, r[1].length))) + "." + pad0(dec(val, r[1].length),r[1].length); - } - if((r = fmt.match(/^#,#*,#0/))) return write_num_flt(type,fmt.replace(/^#,#*,/,""),val); - if((r = fmt.match(/^([0#]+)(\\?-([0#]+))+$/))) { - o = _strrev(write_num_flt(type, fmt.replace(/[\\-]/g,""), val)); - ri = 0; - return _strrev(_strrev(fmt.replace(/\\/g,"")).replace(/[0#]/g,function(x){return ri= 0) return write_num_int('n', ffmt, val); - return '(' + write_num_int('n', ffmt, -val) + ')'; - } - if(fmt.charCodeAt(fmt.length - 1) === 44) return write_num_cm2(type, fmt, val); - if(fmt.indexOf('%') !== -1) return write_num_pct2(type, fmt, val); - if(fmt.indexOf('E') !== -1) return write_num_exp2(fmt, val); - if(fmt.charCodeAt(0) === 36) return "$"+write_num_int(type,fmt.substr(fmt.charAt(1)==' '?2:1),val); - var o; - var r, ri, ff, aval = Math.abs(val), sign = val < 0 ? "-" : ""; - if(fmt.match(/^00+$/)) return sign + pad0(aval,fmt.length); - if(fmt.match(/^[#?]+$/)) { - o = (""+val); if(val === 0) o = ""; - return o.length > fmt.length ? o : hashq(fmt.substr(0,fmt.length-o.length)) + o; - } - if((r = fmt.match(frac1))) return write_num_f2(r, aval, sign); - if(fmt.match(/^#+0+$/)) return sign + pad0(aval,fmt.length - fmt.indexOf("0")); - if((r = fmt.match(dec1))) { - /*:: if(!Array.isArray(r)) throw "unreachable"; */ - o = (""+val).replace(/^([^\.]+)$/,"$1."+r[1]).replace(/\.$/,"."+r[1]); - o = o.replace(/\.(\d*)$/,function($$, $1) { - /*:: if(!Array.isArray(r)) throw "unreachable"; */ - return "." + $1 + fill("0", r[1].length-$1.length); }); - return fmt.indexOf("0.") !== -1 ? o : o.replace(/^0\./,"."); - } - fmt = fmt.replace(/^#+([0.])/, "$1"); - if((r = fmt.match(/^(0*)\.(#*)$/))) { - return sign + (""+aval).replace(/\.(\d*[1-9])0*$/,".$1").replace(/^(-?\d*)$/,"$1.").replace(/^0\./,r[1].length?"0.":"."); - } - if((r = fmt.match(/^#,##0(\.?)$/))) return sign + commaify((""+aval)); - if((r = fmt.match(/^#,##0\.([#0]*0)$/))) { - return val < 0 ? "-" + write_num_int(type, fmt, -val) : commaify((""+val)) + "." + fill('0',r[1].length); - } - if((r = fmt.match(/^#,#*,#0/))) return write_num_int(type,fmt.replace(/^#,#*,/,""),val); - if((r = fmt.match(/^([0#]+)(\\?-([0#]+))+$/))) { - o = _strrev(write_num_int(type, fmt.replace(/[\\-]/g,""), val)); - ri = 0; - return _strrev(_strrev(fmt.replace(/\\/g,"")).replace(/[0#]/g,function(x){return ri= 0) return write_num_flt('n', ffmt, val); + return '(' + write_num_flt('n', ffmt, -val) + ')'; + } + if(fmt.charCodeAt(fmt.length - 1) === 44) return write_num_cm(type, fmt, val); + if(fmt.indexOf('%') !== -1) return write_num_pct(type, fmt, val); + if(fmt.indexOf('E') !== -1) return write_num_exp(fmt, val); + if(fmt.charCodeAt(0) === 36) return "$"+write_num_flt(type,fmt.substr(fmt.charAt(1)==' '?2:1),val); + var o; + var r/*:?Array*/, ri, ff, aval = Math.abs(val), sign = val < 0 ? "-" : ""; + if(fmt.match(/^00+$/)) return sign + pad0r(aval,fmt.length); + if(fmt.match(/^[#?]+$/)) { + o = pad0r(val,0); if(o === "0") o = ""; + return o.length > fmt.length ? o : hashq(fmt.substr(0,fmt.length-o.length)) + o; + } + if((r = fmt.match(frac1))) return write_num_f1(r, aval, sign); + if(fmt.match(/^#+0+$/)) return sign + pad0r(aval,fmt.length - fmt.indexOf("0")); + if((r = fmt.match(dec1))) { + o = rnd(val, r[1].length).replace(/^([^\.]+)$/,"$1."+r[1]).replace(/\.$/,"."+r[1]).replace(/\.(\d*)$/,function($$, $1) { return "." + $1 + fill("0", /*::(*/r/*::||[""])*/[1].length-$1.length); }); + return fmt.indexOf("0.") !== -1 ? o : o.replace(/^0\./,"."); + } + fmt = fmt.replace(/^#+([0.])/, "$1"); + if((r = fmt.match(/^(0*)\.(#*)$/))) { + return sign + rnd(aval, r[2].length).replace(/\.(\d*[1-9])0*$/,".$1").replace(/^(-?\d*)$/,"$1.").replace(/^0\./,r[1].length?"0.":"."); + } + if((r = fmt.match(/^#,##0(\.?)$/))) return sign + commaify(pad0r(aval,0)); + if((r = fmt.match(/^#,##0\.([#0]*0)$/))) { + return val < 0 ? "-" + write_num_flt(type, fmt, -val) : commaify(""+(Math.floor(val) + carry(val, r[1].length))) + "." + pad0(dec(val, r[1].length),r[1].length); + } + if((r = fmt.match(/^#,#*,#0/))) return write_num_flt(type,fmt.replace(/^#,#*,/,""),val); + if((r = fmt.match(/^([0#]+)(\\?-([0#]+))+$/))) { + o = _strrev(write_num_flt(type, fmt.replace(/[\\-]/g,""), val)); + ri = 0; + return _strrev(_strrev(fmt.replace(/\\/g,"")).replace(/[0#]/g,function(x){return ri= 0) return write_num_int('n', ffmt, val); + return '(' + write_num_int('n', ffmt, -val) + ')'; + } + if(fmt.charCodeAt(fmt.length - 1) === 44) return write_num_cm2(type, fmt, val); + if(fmt.indexOf('%') !== -1) return write_num_pct2(type, fmt, val); + if(fmt.indexOf('E') !== -1) return write_num_exp2(fmt, val); + if(fmt.charCodeAt(0) === 36) return "$"+write_num_int(type,fmt.substr(fmt.charAt(1)==' '?2:1),val); + var o; + var r, ri, ff, aval = Math.abs(val), sign = val < 0 ? "-" : ""; + if(fmt.match(/^00+$/)) return sign + pad0(aval,fmt.length); + if(fmt.match(/^[#?]+$/)) { + o = (""+val); if(val === 0) o = ""; + return o.length > fmt.length ? o : hashq(fmt.substr(0,fmt.length-o.length)) + o; + } + if((r = fmt.match(frac1))) return write_num_f2(r, aval, sign); + if(fmt.match(/^#+0+$/)) return sign + pad0(aval,fmt.length - fmt.indexOf("0")); + if((r = fmt.match(dec1))) { + /*:: if(!Array.isArray(r)) throw new Error("unreachable"); */ + o = (""+val).replace(/^([^\.]+)$/,"$1."+r[1]).replace(/\.$/,"."+r[1]); + o = o.replace(/\.(\d*)$/,function($$, $1) { + /*:: if(!Array.isArray(r)) throw new Error("unreachable"); */ + return "." + $1 + fill("0", r[1].length-$1.length); }); + return fmt.indexOf("0.") !== -1 ? o : o.replace(/^0\./,"."); + } + fmt = fmt.replace(/^#+([0.])/, "$1"); + if((r = fmt.match(/^(0*)\.(#*)$/))) { + return sign + (""+aval).replace(/\.(\d*[1-9])0*$/,".$1").replace(/^(-?\d*)$/,"$1.").replace(/^0\./,r[1].length?"0.":"."); + } + if((r = fmt.match(/^#,##0(\.?)$/))) return sign + commaify((""+aval)); + if((r = fmt.match(/^#,##0\.([#0]*0)$/))) { + return val < 0 ? "-" + write_num_int(type, fmt, -val) : commaify((""+val)) + "." + fill('0',r[1].length); + } + if((r = fmt.match(/^#,#*,#0/))) return write_num_int(type,fmt.replace(/^#,#*,/,""),val); + if((r = fmt.match(/^([0#]+)(\\?-([0#]+))+$/))) { + o = _strrev(write_num_int(type, fmt.replace(/[\\-]/g,""), val)); + ri = 0; + return _strrev(_strrev(fmt.replace(/\\/g,"")).replace(/[0#]/g,function(x){return ri -1 || c=='\\' && fmt.charAt(i+1) == "-" && "0#".indexOf(fmt.charAt(i+2))>-1)); + while(i < fmt.length && ("0#?.,E+-%".indexOf(c=fmt.charAt(++i)) > -1 || c=='\\' && fmt.charAt(i+1) == "-" && "0#".indexOf(fmt.charAt(i+2))>-1)){} break; - case '?': while(fmt.charAt(++i) === c); break; + case '?': while(fmt.charAt(++i) === c){} break; case '*': ++i; if(fmt.charAt(i) == ' ' || fmt.charAt(i) == '*') ++i; break; case '(': case ')': ++i; break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': - while(i < fmt.length && "0123456789".indexOf(fmt.charAt(++i)) > -1); break; + while(i < fmt.length && "0123456789".indexOf(fmt.charAt(++i)) > -1){} break; case ' ': ++i; break; default: ++i; break; } diff --git a/bits/82_eval.js b/bits/82_eval.js index cc229a8..764f39f 100644 --- a/bits/82_eval.js +++ b/bits/82_eval.js @@ -46,7 +46,10 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) { if(o.match(abstime)) { if(dt==null) { dt=parse_date_code(v, opts); if(dt==null) return ""; } out[out.length] = {t:'Z', v:o.toLowerCase()}; - } else { o=""; } + } else if(o.indexOf("$") > -1) { + o = (o.match(/\$([^-\[\]]*)/)||[])[1]||"$"; + if(!fmt_is_date(fmt)) out[out.length] = {t:'t',v:o}; + } break; /* Numbers */ case '.': @@ -82,7 +85,7 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) { /* falls through */ case 'd': case 'y': case 'M': case 'e': lst=out[i].t; break; case 'm': if(lst === 's') { out[i].t = 'M'; if(bt < 2) bt = 2; } break; - case 'X': if(out[i].v === "B2"); + case 'X': /*if(out[i].v === "B2");*/ break; case 'Z': if(bt < 1 && out[i].v.match(/[Hh]/)) bt = 1; @@ -132,11 +135,20 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) { } var vv = "", myv, ostr; if(nstr.length > 0) { - myv = (v<0&&nstr.charCodeAt(0) === 45 ? -v : v); /* '-' */ - ostr = write_num(nstr.charCodeAt(0) === 40 ? '(' : 'n', nstr, myv); /* '(' */ + if(nstr.charCodeAt(0) == 40) /* '(' */ { + myv = (v<0&&nstr.charCodeAt(0) === 45 ? -v : v); + ostr = write_num('(', nstr, myv); + } else { + myv = (v<0 && flen > 1 ? -v : v); + ostr = write_num('n', nstr, myv); + if(myv < 0 && out[0] && out[0].t == 't') { + ostr = ostr.substr(1); + out[0].v = "-" + out[0].v; + } + } jj=ostr.length-1; var decpt = out.length; - for(i=0; i < out.length; ++i) if(out[i] != null && out[i].v.indexOf(".") > -1) { decpt = i; break; } + for(i=0; i < out.length; ++i) if(out[i] != null && out[i].t != 't' && out[i].v.indexOf(".") > -1) { decpt = i; break; } var lasti=out.length; if(decpt === out.length && ostr.indexOf("E") === -1) { for(i=out.length-1; i>= 0;--i) { diff --git a/bits/90_main.js b/bits/90_main.js index 0ff122f..d06a839 100644 --- a/bits/90_main.js +++ b/bits/90_main.js @@ -20,11 +20,18 @@ function choose_fmt(f/*:string*/, v) { return [l, ff]; } function format(fmt/*:string|number*/,v/*:any*/,o/*:?any*/) { - fixopts(o != null ? o : (o=[])); + if(o == null) o = {}; + //fixopts(o != null ? o : (o=[])); var sfmt = ""; switch(typeof fmt) { - case "string": sfmt = fmt; break; - case "number": sfmt = (o.table != null ? (o.table/*:any*/) : table_fmt)[fmt]; break; + case "string": + if(fmt == "m/d/yy" && o.dateNF) sfmt = o.dateNF; + else sfmt = fmt; + break; + case "number": + if(fmt == 14 && o.dateNF) sfmt = o.dateNF; + else sfmt = (o.table != null ? (o.table/*:any*/) : table_fmt)[fmt]; + break; } if(isgeneral(sfmt,0)) return general_fmt(v, o); var f = choose_fmt(sfmt, v); diff --git a/bits/98_exports.js b/bits/98_exports.js index ad35fac..f086c80 100644 --- a/bits/98_exports.js +++ b/bits/98_exports.js @@ -3,3 +3,4 @@ SSF.load = function load_entry(fmt/*:string*/, idx/*:number*/) { table_fmt[idx] SSF.format = format; SSF.get_table = function get_table() { return table_fmt; }; SSF.load_table = function load_table(tbl/*:{[n:number]:string}*/) { for(var i=0; i!=0x0188; ++i) if(tbl[i] !== undefined) SSF.load(tbl[i], i); }; +SSF.init_table = init_table; diff --git a/index.html b/index.html index 8d90e5a..ac821b0 100644 --- a/index.html +++ b/index.html @@ -1,49 +1,70 @@ - + + + - - - - -SSF (Spreadsheet Number Format) Live Demo
-Github Repo
-Issues? Something look weird? Click here and report an issue
-
-

Format code:

-

Value:

-

Formatted Number:

-

Formatted Text

-
- - - +Source Code Repo +Issues? Something look weird? Click here and report an issue + + + + + + + +
Format code:
Value:
 
Formatted Number:
Formatted Text
+ + + + diff --git a/package.json b/package.json index 905dac6..0840ec0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ssf", - "version": "0.9.0", + "version": "0.9.1", "author": "SheetJS", "description": "Format data using ECMA-376 spreadsheet Format Codes", "keywords": [ "format", "sprintf", "spreadsheet" ], @@ -11,7 +11,7 @@ "dependencies": { "voc":"", "colors":"0.6.2", - "frac":"0.3.1" + "frac":"~1.0.6" }, "devDependencies": { "mocha":"", diff --git a/ssf.flow.js b/ssf.flow.js index 9c06bec..ae9e124 100644 --- a/ssf.flow.js +++ b/ssf.flow.js @@ -2,7 +2,7 @@ /*jshint -W041 */ var SSF = {}; var make_ssf = function make_ssf(SSF){ -SSF.version = '0.9.0'; +SSF.version = '0.9.1'; function _strrev(x/*:string*/)/*:string*/ { var o = "", i = x.length-1; while(i>=0) o += x.charAt(i--); return o; } function fill(c/*:string*/,l/*:number*/)/*:string*/ { var o = ""; while(o.length < l) o+=c; return o; } function pad0(v/*:any*/,d/*:number*/)/*:string*/{var t=""+v; return t.length>=d?t:fill('0',d-t.length)+t;} @@ -23,38 +23,6 @@ function fixopts(o){ for(var y = 0; y != opts_fmt.length; ++y) if(o[opts_fmt[y][0]]===undefined) o[opts_fmt[y][0]]=opts_fmt[y][1]; } SSF.opts = opts_fmt; -var table_fmt = { - /*::[*/0/*::]*/: 'General', - /*::[*/1/*::]*/: '0', - /*::[*/2/*::]*/: '0.00', - /*::[*/3/*::]*/: '#,##0', - /*::[*/4/*::]*/: '#,##0.00', - /*::[*/9/*::]*/: '0%', - /*::[*/10/*::]*/: '0.00%', - /*::[*/11/*::]*/: '0.00E+00', - /*::[*/12/*::]*/: '# ?/?', - /*::[*/13/*::]*/: '# ??/??', - /*::[*/14/*::]*/: 'm/d/yy', - /*::[*/15/*::]*/: 'd-mmm-yy', - /*::[*/16/*::]*/: 'd-mmm', - /*::[*/17/*::]*/: 'mmm-yy', - /*::[*/18/*::]*/: 'h:mm AM/PM', - /*::[*/19/*::]*/: 'h:mm:ss AM/PM', - /*::[*/20/*::]*/: 'h:mm', - /*::[*/21/*::]*/: 'h:mm:ss', - /*::[*/22/*::]*/: 'm/d/yy h:mm', - /*::[*/37/*::]*/: '#,##0 ;(#,##0)', - /*::[*/38/*::]*/: '#,##0 ;[Red](#,##0)', - /*::[*/39/*::]*/: '#,##0.00;(#,##0.00)', - /*::[*/40/*::]*/: '#,##0.00;[Red](#,##0.00)', - /*::[*/45/*::]*/: 'mm:ss', - /*::[*/46/*::]*/: '[h]:mm:ss', - /*::[*/47/*::]*/: 'mmss.0', - /*::[*/48/*::]*/: '##0.0E+0', - /*::[*/49/*::]*/: '@', - /*::[*/56/*::]*/: '"上午/下午 "hh"時"mm"分"ss"秒 "', - /*::[*/65535/*::]*/: 'General' -}; var days/*:Array >*/ = [ ['Sun', 'Sunday'], ['Mon', 'Monday'], @@ -78,7 +46,42 @@ var months/*:Array >*/ = [ ['N', 'Nov', 'November'], ['D', 'Dec', 'December'] ]; -function frac(x, D, mixed) { +function init_table(t/*:any*/) { + t[0]= 'General'; + t[1]= '0'; + t[2]= '0.00'; + t[3]= '#,##0'; + t[4]= '#,##0.00'; + t[9]= '0%'; + t[10]= '0.00%'; + t[11]= '0.00E+00'; + t[12]= '# ?/?'; + t[13]= '# ??/??'; + t[14]= 'm/d/yy'; + t[15]= 'd-mmm-yy'; + t[16]= 'd-mmm'; + t[17]= 'mmm-yy'; + t[18]= 'h:mm AM/PM'; + t[19]= 'h:mm:ss AM/PM'; + t[20]= 'h:mm'; + t[21]= 'h:mm:ss'; + t[22]= 'm/d/yy h:mm'; + t[37]= '#,##0 ;(#,##0)'; + t[38]= '#,##0 ;[Red](#,##0)'; + t[39]= '#,##0.00;(#,##0.00)'; + t[40]= '#,##0.00;[Red](#,##0.00)'; + t[45]= 'mm:ss'; + t[46]= '[h]:mm:ss'; + t[47]= 'mmss.0'; + t[48]= '##0.0E+0'; + t[49]= '@'; + t[56]= '"上午/下午 "hh"時"mm"分"ss"秒 "'; + t[65535]= 'General'; +} + +var table_fmt = {}; +init_table(table_fmt); +function frac(x/*:number*/, D/*:number*/, mixed/*:?boolean*/)/*:Array*/ { var sgn = x < 0 ? -1 : 1; var B = x * sgn; var P_2 = 0, P_1 = 1, P = 0; @@ -88,15 +91,13 @@ function frac(x, D, mixed) { A = Math.floor(B); P = A * P_1 + P_2; Q = A * Q_1 + Q_2; - if((B - A) < 0.0000000005) break; + if((B - A) < 0.00000005) break; B = 1 / (B - A); P_2 = P_1; P_1 = P; Q_2 = Q_1; Q_1 = Q; } - if(Q > D) { Q = Q_1; P = P_1; } - if(Q > D) { Q = Q_2; P = P_2; } + if(Q > D) { if(Q_1 > D) { Q = Q_2; P = P_2; } else { Q = Q_1; P = P_1; } } if(!mixed) return [0, sgn * P, Q]; - if(Q===0) throw "Unexpected state: "+P+" "+P_1+" "+P_2+" "+Q+" "+Q_1+" "+Q_2; var q = Math.floor(sgn * P/Q); return [q, sgn*P - q*Q, Q]; } @@ -138,6 +139,8 @@ function general_fmt(v/*:any*/, opts/*:?any*/) { case 'string': return v; case 'boolean': return v ? "TRUE" : "FALSE"; case 'number': return (v|0) === v ? general_fmt_int(v, opts) : general_fmt_num(v, opts); + case 'undefined': return ""; + case 'object': if(v == null) return ""; } throw new Error("unsupported value in General format: " + v); } @@ -311,16 +314,16 @@ function hashq(str/*:string*/)/*:string*/ { } function rnd(val/*:number*/, d/*:number*/)/*:string*/ { var dd = Math.pow(10,d); return ""+(Math.round(val * dd)/dd); } function dec(val/*:number*/, d/*:number*/)/*:number*/ { - if (d < ('' + Math.round((val-Math.floor(val))*Math.pow(10,d))).length) { - return 0; - } - return Math.round((val-Math.floor(val))*Math.pow(10,d)); + if (d < ('' + Math.round((val-Math.floor(val))*Math.pow(10,d))).length) { + return 0; + } + return Math.round((val-Math.floor(val))*Math.pow(10,d)); } function carry(val/*:number*/, d/*:number*/)/*:number*/ { - if (d < ('' + Math.round((val-Math.floor(val))*Math.pow(10,d))).length) { - return 1; - } - return 0; + if (d < ('' + Math.round((val-Math.floor(val))*Math.pow(10,d))).length) { + return 1; + } + return 0; } function flr(val/*:number*/)/*:string*/ { if(val < 2147483647 && val > -2147483648) return ""+(val >= 0 ? (val|0) : (val-1|0)); return ""+Math.floor(val); } function write_num_flt(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string*/ { @@ -343,7 +346,6 @@ function write_num_flt(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string if((r = fmt.match(frac1))) return write_num_f1(r, aval, sign); if(fmt.match(/^#+0+$/)) return sign + pad0r(aval,fmt.length - fmt.indexOf("0")); if((r = fmt.match(dec1))) { - // $FlowIgnore o = rnd(val, r[1].length).replace(/^([^\.]+)$/,"$1."+r[1]).replace(/\.$/,"."+r[1]).replace(/\.(\d*)$/,function($$, $1) { return "." + $1 + fill("0", /*::(*/r/*::||[""])*/[1].length-$1.length); }); return fmt.indexOf("0.") !== -1 ? o : o.replace(/^0\./,"."); } @@ -453,10 +455,10 @@ function write_num_int(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string if((r = fmt.match(frac1))) return write_num_f2(r, aval, sign); if(fmt.match(/^#+0+$/)) return sign + pad0(aval,fmt.length - fmt.indexOf("0")); if((r = fmt.match(dec1))) { - /*:: if(!Array.isArray(r)) throw "unreachable"; */ + /*:: if(!Array.isArray(r)) throw new Error("unreachable"); */ o = (""+val).replace(/^([^\.]+)$/,"$1."+r[1]).replace(/\.$/,"."+r[1]); o = o.replace(/\.(\d*)$/,function($$, $1) { - /*:: if(!Array.isArray(r)) throw "unreachable"; */ + /*:: if(!Array.isArray(r)) throw new Error("unreachable"); */ return "." + $1 + fill("0", r[1].length-$1.length); }); return fmt.indexOf("0.") !== -1 ? o : o.replace(/^0\./,"."); } @@ -564,13 +566,13 @@ function fmt_is_date(fmt/*:string*/)/*:boolean*/ { case '.': /* falls through */ case '0': case '#': - while(i < fmt.length && ("0#?.,E+-%".indexOf(c=fmt.charAt(++i)) > -1 || c=='\\' && fmt.charAt(i+1) == "-" && "0#".indexOf(fmt.charAt(i+2))>-1)); + while(i < fmt.length && ("0#?.,E+-%".indexOf(c=fmt.charAt(++i)) > -1 || c=='\\' && fmt.charAt(i+1) == "-" && "0#".indexOf(fmt.charAt(i+2))>-1)){} break; - case '?': while(fmt.charAt(++i) === c); break; + case '?': while(fmt.charAt(++i) === c){} break; case '*': ++i; if(fmt.charAt(i) == ' ' || fmt.charAt(i) == '*') ++i; break; case '(': case ')': ++i; break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': - while(i < fmt.length && "0123456789".indexOf(fmt.charAt(++i)) > -1); break; + while(i < fmt.length && "0123456789".indexOf(fmt.charAt(++i)) > -1){} break; case ' ': ++i; break; default: ++i; break; } @@ -626,7 +628,10 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) { if(o.match(abstime)) { if(dt==null) { dt=parse_date_code(v, opts); if(dt==null) return ""; } out[out.length] = {t:'Z', v:o.toLowerCase()}; - } else { o=""; } + } else if(o.indexOf("$") > -1) { + o = (o.match(/\$([^-\[\]]*)/)||[])[1]||"$"; + if(!fmt_is_date(fmt)) out[out.length] = {t:'t',v:o}; + } break; /* Numbers */ case '.': @@ -662,7 +667,7 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) { /* falls through */ case 'd': case 'y': case 'M': case 'e': lst=out[i].t; break; case 'm': if(lst === 's') { out[i].t = 'M'; if(bt < 2) bt = 2; } break; - case 'X': if(out[i].v === "B2"); + case 'X': /*if(out[i].v === "B2");*/ break; case 'Z': if(bt < 1 && out[i].v.match(/[Hh]/)) bt = 1; @@ -712,11 +717,20 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) { } var vv = "", myv, ostr; if(nstr.length > 0) { - myv = (v<0&&nstr.charCodeAt(0) === 45 ? -v : v); /* '-' */ - ostr = write_num(nstr.charCodeAt(0) === 40 ? '(' : 'n', nstr, myv); /* '(' */ + if(nstr.charCodeAt(0) == 40) /* '(' */ { + myv = (v<0&&nstr.charCodeAt(0) === 45 ? -v : v); + ostr = write_num('(', nstr, myv); + } else { + myv = (v<0 && flen > 1 ? -v : v); + ostr = write_num('n', nstr, myv); + if(myv < 0 && out[0] && out[0].t == 't') { + ostr = ostr.substr(1); + out[0].v = "-" + out[0].v; + } + } jj=ostr.length-1; var decpt = out.length; - for(i=0; i < out.length; ++i) if(out[i] != null && out[i].v.indexOf(".") > -1) { decpt = i; break; } + for(i=0; i < out.length; ++i) if(out[i] != null && out[i].t != 't' && out[i].v.indexOf(".") > -1) { decpt = i; break; } var lasti=out.length; if(decpt === out.length && ostr.indexOf("E") === -1) { for(i=out.length-1; i>= 0;--i) { @@ -804,11 +818,18 @@ function choose_fmt(f/*:string*/, v) { return [l, ff]; } function format(fmt/*:string|number*/,v/*:any*/,o/*:?any*/) { - fixopts(o != null ? o : (o=[])); + if(o == null) o = {}; + //fixopts(o != null ? o : (o=[])); var sfmt = ""; switch(typeof fmt) { - case "string": sfmt = fmt; break; - case "number": sfmt = (o.table != null ? (o.table/*:any*/) : table_fmt)[fmt]; break; + case "string": + if(fmt == "m/d/yy" && o.dateNF) sfmt = o.dateNF; + else sfmt = fmt; + break; + case "number": + if(fmt == 14 && o.dateNF) sfmt = o.dateNF; + else sfmt = (o.table != null ? (o.table/*:any*/) : table_fmt)[fmt]; + break; } if(isgeneral(sfmt,0)) return general_fmt(v, o); var f = choose_fmt(sfmt, v); @@ -822,6 +843,7 @@ SSF.load = function load_entry(fmt/*:string*/, idx/*:number*/) { table_fmt[idx] SSF.format = format; SSF.get_table = function get_table() { return table_fmt; }; SSF.load_table = function load_table(tbl/*:{[n:number]:string}*/) { for(var i=0; i!=0x0188; ++i) if(tbl[i] !== undefined) SSF.load(tbl[i], i); }; +SSF.init_table = init_table; }; make_ssf(SSF); /*global module */ diff --git a/ssf.js b/ssf.js index 7f04a38..080f88e 100644 --- a/ssf.js +++ b/ssf.js @@ -2,7 +2,7 @@ /*jshint -W041 */ var SSF = {}; var make_ssf = function make_ssf(SSF){ -SSF.version = '0.9.0'; +SSF.version = '0.9.1'; function _strrev(x) { var o = "", i = x.length-1; while(i>=0) o += x.charAt(i--); return o; } function fill(c,l) { var o = ""; while(o.length < l) o+=c; return o; } function pad0(v,d){var t=""+v; return t.length>=d?t:fill('0',d-t.length)+t;} @@ -23,38 +23,6 @@ function fixopts(o){ for(var y = 0; y != opts_fmt.length; ++y) if(o[opts_fmt[y][0]]===undefined) o[opts_fmt[y][0]]=opts_fmt[y][1]; } SSF.opts = opts_fmt; -var table_fmt = { -0: 'General', -1: '0', -2: '0.00', -3: '#,##0', -4: '#,##0.00', -9: '0%', -10: '0.00%', -11: '0.00E+00', -12: '# ?/?', -13: '# ??/??', -14: 'm/d/yy', -15: 'd-mmm-yy', -16: 'd-mmm', -17: 'mmm-yy', -18: 'h:mm AM/PM', -19: 'h:mm:ss AM/PM', -20: 'h:mm', -21: 'h:mm:ss', -22: 'm/d/yy h:mm', -37: '#,##0 ;(#,##0)', -38: '#,##0 ;[Red](#,##0)', -39: '#,##0.00;(#,##0.00)', -40: '#,##0.00;[Red](#,##0.00)', -45: 'mm:ss', -46: '[h]:mm:ss', -47: 'mmss.0', -48: '##0.0E+0', -49: '@', -56: '"上午/下午 "hh"時"mm"分"ss"秒 "', -65535: 'General' -}; var days = [ ['Sun', 'Sunday'], ['Mon', 'Monday'], @@ -78,6 +46,41 @@ var months = [ ['N', 'Nov', 'November'], ['D', 'Dec', 'December'] ]; +function init_table(t) { + t[0]= 'General'; + t[1]= '0'; + t[2]= '0.00'; + t[3]= '#,##0'; + t[4]= '#,##0.00'; + t[9]= '0%'; + t[10]= '0.00%'; + t[11]= '0.00E+00'; + t[12]= '# ?/?'; + t[13]= '# ??/??'; + t[14]= 'm/d/yy'; + t[15]= 'd-mmm-yy'; + t[16]= 'd-mmm'; + t[17]= 'mmm-yy'; + t[18]= 'h:mm AM/PM'; + t[19]= 'h:mm:ss AM/PM'; + t[20]= 'h:mm'; + t[21]= 'h:mm:ss'; + t[22]= 'm/d/yy h:mm'; + t[37]= '#,##0 ;(#,##0)'; + t[38]= '#,##0 ;[Red](#,##0)'; + t[39]= '#,##0.00;(#,##0.00)'; + t[40]= '#,##0.00;[Red](#,##0.00)'; + t[45]= 'mm:ss'; + t[46]= '[h]:mm:ss'; + t[47]= 'mmss.0'; + t[48]= '##0.0E+0'; + t[49]= '@'; + t[56]= '"上午/下午 "hh"時"mm"分"ss"秒 "'; + t[65535]= 'General'; +} + +var table_fmt = {}; +init_table(table_fmt); function frac(x, D, mixed) { var sgn = x < 0 ? -1 : 1; var B = x * sgn; @@ -88,15 +91,13 @@ function frac(x, D, mixed) { A = Math.floor(B); P = A * P_1 + P_2; Q = A * Q_1 + Q_2; - if((B - A) < 0.0000000005) break; + if((B - A) < 0.00000005) break; B = 1 / (B - A); P_2 = P_1; P_1 = P; Q_2 = Q_1; Q_1 = Q; } - if(Q > D) { Q = Q_1; P = P_1; } - if(Q > D) { Q = Q_2; P = P_2; } + if(Q > D) { if(Q_1 > D) { Q = Q_2; P = P_2; } else { Q = Q_1; P = P_1; } } if(!mixed) return [0, sgn * P, Q]; - if(Q===0) throw "Unexpected state: "+P+" "+P_1+" "+P_2+" "+Q+" "+Q_1+" "+Q_2; var q = Math.floor(sgn * P/Q); return [q, sgn*P - q*Q, Q]; } @@ -138,6 +139,8 @@ function general_fmt(v, opts) { case 'string': return v; case 'boolean': return v ? "TRUE" : "FALSE"; case 'number': return (v|0) === v ? general_fmt_int(v, opts) : general_fmt_num(v, opts); + case 'undefined': return ""; + case 'object': if(v == null) return ""; } throw new Error("unsupported value in General format: " + v); } @@ -310,16 +313,16 @@ function hashq(str) { } function rnd(val, d) { var dd = Math.pow(10,d); return ""+(Math.round(val * dd)/dd); } function dec(val, d) { - if (d < ('' + Math.round((val-Math.floor(val))*Math.pow(10,d))).length) { - return 0; - } - return Math.round((val-Math.floor(val))*Math.pow(10,d)); + if (d < ('' + Math.round((val-Math.floor(val))*Math.pow(10,d))).length) { + return 0; + } + return Math.round((val-Math.floor(val))*Math.pow(10,d)); } function carry(val, d) { - if (d < ('' + Math.round((val-Math.floor(val))*Math.pow(10,d))).length) { - return 1; - } - return 0; + if (d < ('' + Math.round((val-Math.floor(val))*Math.pow(10,d))).length) { + return 1; + } + return 0; } function flr(val) { if(val < 2147483647 && val > -2147483648) return ""+(val >= 0 ? (val|0) : (val-1|0)); return ""+Math.floor(val); } function write_num_flt(type, fmt, val) { @@ -342,7 +345,6 @@ function write_num_flt(type, fmt, val) { if((r = fmt.match(frac1))) return write_num_f1(r, aval, sign); if(fmt.match(/^#+0+$/)) return sign + pad0r(aval,fmt.length - fmt.indexOf("0")); if((r = fmt.match(dec1))) { - // $FlowIgnore o = rnd(val, r[1].length).replace(/^([^\.]+)$/,"$1."+r[1]).replace(/\.$/,"."+r[1]).replace(/\.(\d*)$/,function($$, $1) { return "." + $1 + fill("0", r[1].length-$1.length); }); return fmt.indexOf("0.") !== -1 ? o : o.replace(/^0\./,"."); } @@ -561,13 +563,13 @@ function fmt_is_date(fmt) { case '.': /* falls through */ case '0': case '#': - while(i < fmt.length && ("0#?.,E+-%".indexOf(c=fmt.charAt(++i)) > -1 || c=='\\' && fmt.charAt(i+1) == "-" && "0#".indexOf(fmt.charAt(i+2))>-1)); + while(i < fmt.length && ("0#?.,E+-%".indexOf(c=fmt.charAt(++i)) > -1 || c=='\\' && fmt.charAt(i+1) == "-" && "0#".indexOf(fmt.charAt(i+2))>-1)){} break; - case '?': while(fmt.charAt(++i) === c); break; + case '?': while(fmt.charAt(++i) === c){} break; case '*': ++i; if(fmt.charAt(i) == ' ' || fmt.charAt(i) == '*') ++i; break; case '(': case ')': ++i; break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': - while(i < fmt.length && "0123456789".indexOf(fmt.charAt(++i)) > -1); break; + while(i < fmt.length && "0123456789".indexOf(fmt.charAt(++i)) > -1){} break; case ' ': ++i; break; default: ++i; break; } @@ -623,7 +625,10 @@ function eval_fmt(fmt, v, opts, flen) { if(o.match(abstime)) { if(dt==null) { dt=parse_date_code(v, opts); if(dt==null) return ""; } out[out.length] = {t:'Z', v:o.toLowerCase()}; - } else { o=""; } + } else if(o.indexOf("$") > -1) { + o = (o.match(/\$([^-\[\]]*)/)||[])[1]||"$"; + if(!fmt_is_date(fmt)) out[out.length] = {t:'t',v:o}; + } break; /* Numbers */ case '.': @@ -659,7 +664,7 @@ function eval_fmt(fmt, v, opts, flen) { /* falls through */ case 'd': case 'y': case 'M': case 'e': lst=out[i].t; break; case 'm': if(lst === 's') { out[i].t = 'M'; if(bt < 2) bt = 2; } break; - case 'X': if(out[i].v === "B2"); + case 'X': /*if(out[i].v === "B2");*/ break; case 'Z': if(bt < 1 && out[i].v.match(/[Hh]/)) bt = 1; @@ -706,11 +711,20 @@ out[i].v = write_date(out[i].t.charCodeAt(0), out[i].v, dt, ss0); } var vv = "", myv, ostr; if(nstr.length > 0) { - myv = (v<0&&nstr.charCodeAt(0) === 45 ? -v : v); /* '-' */ - ostr = write_num(nstr.charCodeAt(0) === 40 ? '(' : 'n', nstr, myv); /* '(' */ + if(nstr.charCodeAt(0) == 40) /* '(' */ { + myv = (v<0&&nstr.charCodeAt(0) === 45 ? -v : v); + ostr = write_num('(', nstr, myv); + } else { + myv = (v<0 && flen > 1 ? -v : v); + ostr = write_num('n', nstr, myv); + if(myv < 0 && out[0] && out[0].t == 't') { + ostr = ostr.substr(1); + out[0].v = "-" + out[0].v; + } + } jj=ostr.length-1; var decpt = out.length; - for(i=0; i < out.length; ++i) if(out[i] != null && out[i].v.indexOf(".") > -1) { decpt = i; break; } + for(i=0; i < out.length; ++i) if(out[i] != null && out[i].t != 't' && out[i].v.indexOf(".") > -1) { decpt = i; break; } var lasti=out.length; if(decpt === out.length && ostr.indexOf("E") === -1) { for(i=out.length-1; i>= 0;--i) { @@ -798,11 +812,18 @@ function choose_fmt(f, v) { return [l, ff]; } function format(fmt,v,o) { - fixopts(o != null ? o : (o=[])); + if(o == null) o = {}; + //fixopts(o != null ? o : (o=[])); var sfmt = ""; switch(typeof fmt) { - case "string": sfmt = fmt; break; - case "number": sfmt = (o.table != null ? (o.table) : table_fmt)[fmt]; break; + case "string": + if(fmt == "m/d/yy" && o.dateNF) sfmt = o.dateNF; + else sfmt = fmt; + break; + case "number": + if(fmt == 14 && o.dateNF) sfmt = o.dateNF; + else sfmt = (o.table != null ? (o.table) : table_fmt)[fmt]; + break; } if(isgeneral(sfmt,0)) return general_fmt(v, o); var f = choose_fmt(sfmt, v); @@ -816,6 +837,7 @@ SSF.load = function load_entry(fmt, idx) { table_fmt[idx] = fmt; }; SSF.format = format; SSF.get_table = function get_table() { return table_fmt; }; SSF.load_table = function load_table(tbl) { for(var i=0; i!=0x0188; ++i) if(tbl[i] !== undefined) SSF.load(tbl[i], i); }; +SSF.init_table = init_table; }; make_ssf(SSF); /*global module */ diff --git a/test/dateNF.js b/test/dateNF.js new file mode 100644 index 0000000..6455c93 --- /dev/null +++ b/test/dateNF.js @@ -0,0 +1,16 @@ +/* vim: set ts=2: */ +/*jshint loopfunc:true, mocha:true, node:true */ +var SSF = require('../'); +var fs = require('fs'), assert = require('assert'); +describe('dateNF override', function() { + it('should override format code 14', function() { + assert.equal(SSF.format(14, 43880), "2/19/20"); + assert.equal(SSF.format(14, 43880, {dateNF:"yyyy-mm-dd"}), "2020-02-19"); + assert.equal(SSF.format(14, 43880, {dateNF:"dd/mm/yyyy"}), "19/02/2020"); + }); + it('should override format "m/d/yy"', function() { + assert.equal(SSF.format('m/d/yy', 43880), "2/19/20"); + assert.equal(SSF.format('m/d/yy', 43880, {dateNF:"yyyy-mm-dd"}), "2020-02-19"); + assert.equal(SSF.format('m/d/yy', 43880, {dateNF:"dd/mm/yyyy"}), "19/02/2020"); + }); +}); diff --git a/test/general.js b/test/general.js index 10e71a9..b17d5dd 100644 --- a/test/general.js +++ b/test/general.js @@ -10,7 +10,7 @@ describe('General format', function() { assert.equal(SSF.format(d[1], d[0], {}), d[2]); }); }); - it.skip('should handle special values', function() { + it('should handle special values', function() { assert.equal(SSF.format("General", true), "TRUE"); assert.equal(SSF.format("General", undefined), ""); assert.equal(SSF.format("General", null), ""); diff --git a/test/oddities.json b/test/oddities.json index 3639263..339623b 100644 --- a/test/oddities.json +++ b/test/oddities.json @@ -31,7 +31,7 @@ ["#,##0.000000000", [1000000, "1,000,000.000000000"]], ["#,###", [1, "1"], [-1, "-1"], [0,""], [12345.6789, "12,346"], ["TODO", "TODO"]], ["#.##", [1, "1."], [-1, "-1."], [0,"."], ["sheetjs", "sheetjs"]], - ["0;0", [1.1, "1"], [-1.1, "-1"], [0,"0"], ["sheetjs", "sheetjs"]], + ["0;0", [1.1, "1"], [-1.1, "1"], [0,"0"], ["sheetjs", "sheetjs"]], ["0.0", [1, "1.0"], [-1, "-1.0"], [0,"0.0"], ["sheetjs", "sheetjs"]], ["0.00", [1.0001, "1.00"], [-1, "-1.00"], [0,"0.00"], ["sheetjs", "sheetjs"]], ["0.000", [1, "1.000"], [-1, "-1.000"], [0,"0.000"], ["sheetjs", "sheetjs"]], @@ -146,6 +146,14 @@ ["☃", [0], [1], [-1]], ["#0#######", [12345, "012345"], [12345.4321, "012345"], [12345.6789, "012346"]], ["##,##", [12345, "12,345", ""], [12345.4321, "12,345", ""], [12345.6789, "12,346", ""]], - [0, [12345,"12345"], [4294967296.5, 4294967297]], + [0, [12345,"12345"], [4294967296.5, "4294967297"]], + ["\"Rs.\"#,##0.00", [-51968287, "-Rs.51,968,287.00"], [2000000, "Rs.2,000,000.00"]], + ["$#.00", [3.14159, "$3.14"], [-3.14159, "-$3.14"]], + ["\"This is a \".00\"test\"000", [-3.14159, "-This is a 3.14test159"], [3.14159, "This is a 3.14test159"]], + ["[$INR]\\ #,##0.00", [3.14159, "INR 3.14"], [-3.14159, "-INR 3.14"]], + ["[$₹-4009]\\ #,##0.00", [3.14159, "₹ 3.14"], [-3.14159, "-₹ 3.14"]], + ["[$£-809]#,##0.0000;\\-[$£-809]#,##0.0000", [3.14159, "£3.1416"], [-3.14159, "-£3.1416"]], + ["\"-\"0.00", [3.14159, "-3.14"], [-3.14159, "--3.14"]], + ["[$-409]mmm\\-yy", [12345, "Oct-33"]], ["\"foo\";\"bar\";\"baz\";\"qux\";\"foobar\"", [1], [0], [-1], ["sheetjs"]] ]