From 354f2bce4ff4831ef0347924e8c323c7bddc2c53 Mon Sep 17 00:00:00 2001 From: SheetJS Date: Tue, 21 Mar 2017 03:45:12 -0400 Subject: [PATCH] version bump 0.9.0: is_date helper --- .gitignore | 2 +- .npmignore | 14 +- Makefile | 40 +- README.md | 1 + bits/00_header.js | 4 + bits/01_version.js | 1 + bits/02_utilities.js | 10 + bits/10_opts.js | 10 + bits/20_consts.js | 55 ++ bits/30_frac.js | 22 + bits/40_general.js | 42 ++ bits/45_hijri.js | 1 + bits/50_date.js | 103 +++ bits/56_commaify.js | 6 + bits/57_numhead.js | 1 + bits/59_numhelp.js | 5 + bits/60_number.js | 265 ++++++++ bits/80_split.js | 17 + bits/81_fmttype.js | 42 ++ bits/82_eval.js | 189 ++++++ bits/88_cond.js | 15 + bits/90_main.js | 35 + bits/98_exports.js | 5 + bits/99_footer.js | 5 + misc/flow.js | 1 + package.json | 60 +- ssf.flow.js | 833 ++++++++++++++++++++++++ ssf.js | 1455 ++++++++++++++++++++++-------------------- ssf.md | 52 +- test/is_date.json | 447 +++++++++++++ test/utilities.js | 11 + 31 files changed, 2976 insertions(+), 773 deletions(-) create mode 100644 bits/00_header.js create mode 100644 bits/01_version.js create mode 100644 bits/02_utilities.js create mode 100644 bits/10_opts.js create mode 100644 bits/20_consts.js create mode 100644 bits/30_frac.js create mode 100644 bits/40_general.js create mode 100644 bits/45_hijri.js create mode 100644 bits/50_date.js create mode 100644 bits/56_commaify.js create mode 100644 bits/57_numhead.js create mode 100644 bits/59_numhelp.js create mode 100644 bits/60_number.js create mode 100644 bits/80_split.js create mode 100644 bits/81_fmttype.js create mode 100644 bits/82_eval.js create mode 100644 bits/88_cond.js create mode 100644 bits/90_main.js create mode 100644 bits/98_exports.js create mode 100644 bits/99_footer.js create mode 100644 ssf.flow.js create mode 100644 test/is_date.json create mode 100644 test/utilities.js diff --git a/.gitignore b/.gitignore index 1a35068..3f4fd02 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ node_modules/ -tmp/ +misc/coverage.html .vocrc v8.log perf.log diff --git a/.npmignore b/.npmignore index 19fb245..fb4bab2 100644 --- a/.npmignore +++ b/.npmignore @@ -1,7 +1,17 @@ -test/*.tsv +test/ +misc/ node_modules/ -tmp/ +misc/coverage.html .gitignore +.jshintrc +Makefile +.npmignore +.jscs.json +.gitmodules +.travis.yml +.flowconfig +bits/ .vocrc v8.log perf.log +ssf*.tgz diff --git a/Makefile b/Makefile index 6c5aac2..ed78f56 100755 --- a/Makefile +++ b/Makefile @@ -4,33 +4,57 @@ CMDS=bin/ssf.njs HTMLLINT= ULIB=$(shell echo $(LIB) | tr a-z A-Z) +DEPS=$(sort $(wildcard bits/*.js)) TARGET=$(LIB).js +FLOWTARGET=$(LIB).flow.js +FLOWAUX=$(patsubst %.js,%.flow.js,$(AUXTARGETS)) +AUXSCPTS= +FLOWTGTS=$(TARGET) $(AUXTARGETS) $(AUXSCPTS) +UGLIFYOPTS=--support-ie8 ## Main Targets +.PHONY: all +all: $(TARGET) $(AUXTARGETS) $(AUXSCPTS) ## Build library and auxiliary scripts + +$(FLOWTGTS): %.js : %.flow.js + node -e 'process.stdout.write(require("fs").readFileSync("$<","utf8").replace(/^[ \t]*\/\*[:#][^*]*\*\/\s*(\n)?/gm,"").replace(/\/\*[:#][^*]*\*\//gm,""))' > $@ + +$(FLOWTARGET): $(DEPS) + cat $^ | tr -d '\15\32' > $@ + +bits/01_version.js: package.json + echo "$(ULIB).version = '"`grep version package.json | awk '{gsub(/[^0-9a-z\.-]/,"",$$2); print $$2}'`"';" > $@ + +.PHONY: clean +clean: ## Remove targets and build artifacts + rm -f $(TARGET) $(FLOWTARGET) -.PHONY: ssf -ssf: ssf.md - voc ssf.md ## Testing .PHONY: test mocha test mocha: ## Run test suite - npm test + mocha -R spec +.PHONY: test_min test_min: MINTEST=1 npm test +.PHONY: travis +travis: ## Run test suite with minimal output + mocha -R dot -t 30000 + ## Code Checking .PHONY: lint -lint: ## Run jshint and jscs checks - @jshint --show-non-errors $(TARGET) test/ +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) + @jscs $(TARGET) $(AUXTARGETS) .PHONY: flow flow: lint ## Run flow checker @@ -43,7 +67,7 @@ cov: tmp/coverage.html ## Run coverage test cov_min: MINTEST=1 make cov -tmp/coverage.html: ssf +misc/coverage.html: $(TARGET) mocha --require blanket -R html-cov -t 20000 > $@ .PHONY: full_coveralls diff --git a/README.md b/README.md index 76c597a..ae22e17 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,7 @@ type SSFDate = { } ``` +`SSF.is_date(fmt:string):boolean` returns `true` if `fmt` encodes a date format. ## License diff --git a/bits/00_header.js b/bits/00_header.js new file mode 100644 index 0000000..cd5b3e3 --- /dev/null +++ b/bits/00_header.js @@ -0,0 +1,4 @@ +/* ssf.js (C) 2013-present SheetJS -- http://sheetjs.com */ +/*jshint -W041 */ +var SSF = {}; +var make_ssf = function make_ssf(SSF){ diff --git a/bits/01_version.js b/bits/01_version.js new file mode 100644 index 0000000..aa5adaa --- /dev/null +++ b/bits/01_version.js @@ -0,0 +1 @@ +SSF.version = '0.9.0'; diff --git a/bits/02_utilities.js b/bits/02_utilities.js new file mode 100644 index 0000000..f9d7b62 --- /dev/null +++ b/bits/02_utilities.js @@ -0,0 +1,10 @@ +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;} +function pad_(v/*:any*/,d/*:number*/)/*:string*/{var t=""+v;return t.length>=d?t:fill(' ',d-t.length)+t;} +function rpad_(v/*:any*/,d/*:number*/)/*:string*/{var t=""+v; return t.length>=d?t:t+fill(' ',d-t.length);} +function pad0r1(v/*:any*/,d/*:number*/)/*:string*/{var t=""+Math.round(v); return t.length>=d?t:fill('0',d-t.length)+t;} +function pad0r2(v/*:any*/,d/*:number*/)/*:string*/{var t=""+v; return t.length>=d?t:fill('0',d-t.length)+t;} +var p2_32 = Math.pow(2,32); +function pad0r(v/*:any*/,d/*:number*/)/*:string*/{if(v>p2_32||v<-p2_32) return pad0r1(v,d); var i = Math.round(v); return pad0r2(i,d); } +function isgeneral(s/*:string*/, i/*:?number*/)/*:boolean*/ { i = i || 0; return s.length >= 7 + i && (s.charCodeAt(i)|32) === 103 && (s.charCodeAt(i+1)|32) === 101 && (s.charCodeAt(i+2)|32) === 110 && (s.charCodeAt(i+3)|32) === 101 && (s.charCodeAt(i+4)|32) === 114 && (s.charCodeAt(i+5)|32) === 97 && (s.charCodeAt(i+6)|32) === 108; } diff --git a/bits/10_opts.js b/bits/10_opts.js new file mode 100644 index 0000000..fec7177 --- /dev/null +++ b/bits/10_opts.js @@ -0,0 +1,10 @@ +/* Options */ +var opts_fmt/*:Array >*/ = [ + ["date1904", 0], + ["output", ""], + ["WTF", false] +]; +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; diff --git a/bits/20_consts.js b/bits/20_consts.js new file mode 100644 index 0000000..7586250 --- /dev/null +++ b/bits/20_consts.js @@ -0,0 +1,55 @@ +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'], + ['Tue', 'Tuesday'], + ['Wed', 'Wednesday'], + ['Thu', 'Thursday'], + ['Fri', 'Friday'], + ['Sat', 'Saturday'] +]; +var months/*:Array >*/ = [ + ['J', 'Jan', 'January'], + ['F', 'Feb', 'February'], + ['M', 'Mar', 'March'], + ['A', 'Apr', 'April'], + ['M', 'May', 'May'], + ['J', 'Jun', 'June'], + ['J', 'Jul', 'July'], + ['A', 'Aug', 'August'], + ['S', 'Sep', 'September'], + ['O', 'Oct', 'October'], + ['N', 'Nov', 'November'], + ['D', 'Dec', 'December'] +]; diff --git a/bits/30_frac.js b/bits/30_frac.js new file mode 100644 index 0000000..dacffd0 --- /dev/null +++ b/bits/30_frac.js @@ -0,0 +1,22 @@ +function frac(x, D, mixed) { + var sgn = x < 0 ? -1 : 1; + var B = x * sgn; + var P_2 = 0, P_1 = 1, P = 0; + var Q_2 = 1, Q_1 = 0, Q = 0; + var A = Math.floor(B); + while(Q_1 < D) { + A = Math.floor(B); + P = A * P_1 + P_2; + Q = A * Q_1 + Q_2; + if((B - A) < 0.0000000005) 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(!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 new file mode 100644 index 0000000..340f20b --- /dev/null +++ b/bits/40_general.js @@ -0,0 +1,42 @@ +function general_fmt_int(v/*:number*/, opts/*:?any*/)/*:string*/ { return ""+v; } +SSF._general_int = general_fmt_int; +var general_fmt_num = (function make_general_fmt_num() { +var gnr1 = /\.(\d*[1-9])0+$/, gnr2 = /\.0*$/, gnr4 = /\.(\d*[1-9])0+/, gnr5 = /\.0*[Ee]/, gnr6 = /(E[+-])(\d)$/; +function gfn2(v) { + var w = (v<0?12:11); + var o = gfn5(v.toFixed(12)); if(o.length <= w) return o; + o = v.toPrecision(10); if(o.length <= w) return o; + return v.toExponential(5); +} +function gfn3(v) { + var o = v.toFixed(11).replace(gnr1,".$1"); + if(o.length > (v<0?12:11)) o = v.toPrecision(6); + return o; +} +function gfn4(o) { + for(var i = 0; i != o.length; ++i) if((o.charCodeAt(i) | 0x20) === 101) return o.replace(gnr4,".$1").replace(gnr5,"E").replace("e","E").replace(gnr6,"$10$2"); + return o; +} +function gfn5(o) { + //for(var i = 0; i != o.length; ++i) if(o.charCodeAt(i) === 46) return o.replace(gnr2,"").replace(gnr1,".$1"); + //return o; + return o.indexOf(".") > -1 ? o.replace(gnr2,"").replace(gnr1,".$1") : o; +} +return function general_fmt_num(v/*:number*/, opts/*:?any*/)/*:string*/ { + var V = Math.floor(Math.log(Math.abs(v))*Math.LOG10E), o; + if(V >= -4 && V <= -1) o = v.toPrecision(10+V); + else if(Math.abs(V) <= 9) o = gfn2(v); + else if(V === 10) o = v.toFixed(10).substr(0,12); + else o = gfn3(v); + return gfn5(gfn4(o)); +};})(); +SSF._general_num = general_fmt_num; +function general_fmt(v/*:any*/, opts/*:?any*/) { + switch(typeof v) { + 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); + } + throw new Error("unsupported value in General format: " + v); +} +SSF._general = general_fmt; diff --git a/bits/45_hijri.js b/bits/45_hijri.js new file mode 100644 index 0000000..939b6c9 --- /dev/null +++ b/bits/45_hijri.js @@ -0,0 +1 @@ +function fix_hijri(date, o) { return 0; } diff --git a/bits/50_date.js b/bits/50_date.js new file mode 100644 index 0000000..3a15080 --- /dev/null +++ b/bits/50_date.js @@ -0,0 +1,103 @@ +function parse_date_code(v/*:number*/,opts/*:?any*/,b2/*:?boolean*/) { + if(v > 2958465 || v < 0) return null; + var date = (v|0), time = Math.floor(86400 * (v - date)), dow=0; + var dout=[]; + var out={D:date, T:time, u:86400*(v-date)-time,y:0,m:0,d:0,H:0,M:0,S:0,q:0}; + if(Math.abs(out.u) < 1e-6) out.u = 0; + fixopts(opts != null ? opts : (opts=[])); + if(opts.date1904) date += 1462; + if(out.u > 0.999) { + out.u = 0; + if(++time == 86400) { time = 0; ++date; } + } + if(date === 60) {dout = b2 ? [1317,10,29] : [1900,2,29]; dow=3;} + else if(date === 0) {dout = b2 ? [1317,8,29] : [1900,1,0]; dow=6;} + else { + if(date > 60) --date; + /* 1 = Jan 1 1900 in Gregorian */ + var d = new Date(1900, 0, 1); + d.setDate(d.getDate() + date - 1); + dout = [d.getFullYear(), d.getMonth()+1,d.getDate()]; + dow = d.getDay(); + if(date < 60) dow = (dow + 6) % 7; + if(b2) dow = fix_hijri(d, dout); + } + out.y = dout[0]; out.m = dout[1]; out.d = dout[2]; + out.S = time % 60; time = Math.floor(time / 60); + out.M = time % 60; time = Math.floor(time / 60); + out.H = time; + out.q = dow; + return out; +} +SSF.parse_date_code = parse_date_code; +/*jshint -W086 */ +function write_date(type/*:number*/, fmt/*:string*/, val, ss0/*:?number*/)/*:string*/ { + var o="", ss=0, tt=0, y = val.y, out, outl = 0; + switch(type) { + case 98: /* 'b' buddhist year */ + y = val.y + 543; + /* falls through */ + case 121: /* 'y' year */ + switch(fmt.length) { + case 1: case 2: out = y % 100; outl = 2; break; + default: out = y % 10000; outl = 4; break; + } break; + case 109: /* 'm' month */ + switch(fmt.length) { + case 1: case 2: out = val.m; outl = fmt.length; break; + case 3: return months[val.m-1][1]; + case 5: return months[val.m-1][0]; + default: return months[val.m-1][2]; + } break; + case 100: /* 'd' day */ + switch(fmt.length) { + case 1: case 2: out = val.d; outl = fmt.length; break; + case 3: return days[val.q][0]; + default: return days[val.q][1]; + } break; + case 104: /* 'h' 12-hour */ + switch(fmt.length) { + case 1: case 2: out = 1+(val.H+11)%12; outl = fmt.length; break; + default: throw 'bad hour format: ' + fmt; + } break; + case 72: /* 'H' 24-hour */ + switch(fmt.length) { + case 1: case 2: out = val.H; outl = fmt.length; break; + default: throw 'bad hour format: ' + fmt; + } break; + case 77: /* 'M' minutes */ + switch(fmt.length) { + case 1: case 2: out = val.M; outl = fmt.length; break; + default: throw 'bad minute format: ' + fmt; + } break; + case 115: /* 's' seconds */ + if(val.u === 0) switch(fmt) { + case 's': case 'ss': return pad0(val.S, fmt.length); + case '.0': case '.00': case '.000': + } + switch(fmt) { + case 's': case 'ss': case '.0': case '.00': case '.000': + /*::if(!ss0) ss0 = 0; */ + if(ss0 >= 2) tt = ss0 === 3 ? 1000 : 100; + else tt = ss0 === 1 ? 10 : 1; + ss = Math.round((tt)*(val.S + val.u)); + if(ss >= 60*tt) ss = 0; + if(fmt === 's') return ss === 0 ? "0" : ""+ss/tt; + o = pad0(ss,2 + ss0); + if(fmt === 'ss') return o.substr(0,2); + return "." + o.substr(2,fmt.length-1); + default: throw 'bad second format: ' + fmt; + } + case 90: /* 'Z' absolute time */ + switch(fmt) { + case '[h]': case '[hh]': out = val.D*24+val.H; break; + case '[m]': case '[mm]': out = (val.D*24+val.H)*60+val.M; break; + case '[s]': case '[ss]': out = ((val.D*24+val.H)*60+val.M)*60+Math.round(val.S+val.u); break; + default: throw 'bad abstime format: ' + fmt; + } outl = fmt.length === 3 ? 1 : 2; break; + case 101: /* 'e' era */ + out = y; outl = 1; + } + if(outl > 0) return pad0(out, outl); else return ""; +} +/*jshint +W086 */ diff --git a/bits/56_commaify.js b/bits/56_commaify.js new file mode 100644 index 0000000..249e1f3 --- /dev/null +++ b/bits/56_commaify.js @@ -0,0 +1,6 @@ +function commaify(s/*:string*/)/*:string*/ { + if(s.length <= 3) return s; + var j = (s.length % 3), o = s.substr(0,j); + for(; j!=s.length; j+=3) o+=(o.length > 0 ? "," : "") + s.substr(j,3); + return o; +} diff --git a/bits/57_numhead.js b/bits/57_numhead.js new file mode 100644 index 0000000..6452285 --- /dev/null +++ b/bits/57_numhead.js @@ -0,0 +1 @@ +var write_num = (function make_write_num(){ diff --git a/bits/59_numhelp.js b/bits/59_numhelp.js new file mode 100644 index 0000000..139427c --- /dev/null +++ b/bits/59_numhelp.js @@ -0,0 +1,5 @@ +var pct1 = /%/g; +function write_num_pct(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string*/{ + var sfmt = fmt.replace(pct1,""), mul = fmt.length - sfmt.length; + return write_num(type, sfmt, val * Math.pow(10,2*mul)) + fill("%",mul); +} diff --git a/bits/60_number.js b/bits/60_number.js new file mode 100644 index 0000000..3805108 --- /dev/null +++ b/bits/60_number.js @@ -0,0 +1,265 @@ +function write_num_cm(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string*/{ + var idx = fmt.length - 1; + while(fmt.charCodeAt(idx-1) === 44) --idx; + return write_num(type, fmt.substr(0,idx), val / Math.pow(10,3*(fmt.length-idx))); +} +function write_num_exp(fmt/*:string*/, val/*:number*/)/*:string*/{ + var o/*:string*/; + var idx = fmt.indexOf("E") - fmt.indexOf(".") - 1; + if(fmt.match(/^#+0.0E\+0$/)) { + var period = fmt.indexOf("."); if(period === -1) period=fmt.indexOf('E'); + var ee = Math.floor(Math.log(Math.abs(val))*Math.LOG10E)%period; + if(ee < 0) ee += period; + o = (val/Math.pow(10,ee)).toPrecision(idx+1+(period+ee)%period); + if(o.indexOf("e") === -1) { + var fakee = Math.floor(Math.log(Math.abs(val))*Math.LOG10E); + if(o.indexOf(".") === -1) o = o.charAt(0) + "." + o.substr(1) + "E+" + (fakee - o.length+ee); + else o += "E+" + (fakee - ee); + while(o.substr(0,2) === "0.") { + o = o.charAt(0) + o.substr(2,period) + "." + o.substr(2+period); + o = o.replace(/^0+([1-9])/,"$1").replace(/^0+\./,"0."); + } + o = o.replace(/\+-/,"-"); + } + o = o.replace(/^([+-]?)(\d*)\.(\d*)[Ee]/,function($$,$1,$2,$3) { return $1 + $2 + $3.substr(0,(period+ee)%period) + "." + $3.substr(ee) + "E"; }); + } else o = val.toExponential(idx); + if(fmt.match(/E\+00$/) && o.match(/e[+-]\d$/)) o = o.substr(0,o.length-1) + "0" + o.charAt(o.length-1); + if(fmt.match(/E\-/) && o.match(/e\+/)) o = o.replace(/e\+/,"e"); + return o.replace("e","E"); +} +var frac1 = /# (\?+)( ?)\/( ?)(\d+)/; +function write_num_f1(r/*:Array*/, aval/*:number*/, sign/*:string*/)/*:string*/ { + var den = parseInt(r[4],10), rr = Math.round(aval * den), base = Math.floor(rr/den); + var myn = (rr - base*den), myd = den; + return sign + (base === 0 ? "" : ""+base) + " " + (myn === 0 ? fill(" ", r[1].length + 1 + r[4].length) : pad_(myn,r[1].length) + r[2] + "/" + r[3] + pad0(myd,r[4].length)); +} +function write_num_f2(r/*:Array*/, aval/*:number*/, sign/*:string*/)/*:string*/ { + return sign + (aval === 0 ? "" : ""+aval) + fill(" ", r[1].length + 2 + r[4].length); +} +var dec1 = /^#*0*\.(0+)/; +var closeparen = /\).*[0#]/; +var phone = /\(###\) ###\\?-####/; +function hashq(str/*:string*/)/*:string*/ { + var o = "", cc; + for(var i = 0; i != str.length; ++i) switch((cc=str.charCodeAt(i))) { + case 35: break; + case 63: o+= " "; break; + case 48: o+= "0"; break; + default: o+= String.fromCharCode(cc); + } + return o; +} +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)); +} +function carry(val/*:number*/, d/*:number*/)/*:number*/ { + 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[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[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"; */ + // $FlowIgnore + o = (""+val).replace(/^([^\.]+)$/,"$1."+r[1]).replace(/\.$/,"."+r[1]); + // $FlowIgnore + o = o.replace(/\.(\d*)$/,function($$, $1) { + /*:: if(!Array.isArray(r)) throw "unreachable"; */ + // $FlowIgnore + return "." + $1 + fill("0", r[1].length-$1.length); }); + // $FlowIgnore + 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*/ { + var out/*:Array*/ = []; + var in_str = false, cc; + for(var i = 0, j = 0; i < fmt.length; ++i) switch((cc=fmt.charCodeAt(i))) { + case 34: /* '"' */ + in_str = !in_str; break; + case 95: case 42: case 92: /* '_' '*' '\\' */ + ++i; break; + case 59: /* ';' */ + out[out.length] = fmt.substr(j,i-j); + j = i+1; + } + out[out.length] = fmt.substr(j); + if(in_str === true) throw new Error("Format |" + fmt + "| unterminated string "); + return out; +} +SSF._split = split_fmt; diff --git a/bits/81_fmttype.js b/bits/81_fmttype.js new file mode 100644 index 0000000..30e608c --- /dev/null +++ b/bits/81_fmttype.js @@ -0,0 +1,42 @@ +var abstime = /\[[HhMmSs]*\]/; +function fmt_is_date(fmt/*:string*/)/*:boolean*/ { + var i = 0, cc = 0, c = "", o = ""; + while(i < fmt.length) { + switch((c = fmt.charAt(i))) { + case 'G': if(isgeneral(fmt, i)) i+= 6; i++; break; + case '"': for(;(cc=fmt.charCodeAt(++i)) !== 34 && i < fmt.length;) ++i; ++i; break; + case '\\': i+=2; break; + case '_': i+=2; break; + case '@': ++i; break; + case 'B': case 'b': + if(fmt.charAt(i+1) === "1" || fmt.charAt(i+1) === "2") return true; + /* falls through */ + case 'M': case 'D': case 'Y': case 'H': case 'S': case 'E': + /* falls through */ + case 'm': case 'd': case 'y': case 'h': case 's': case 'e': case 'g': return true; + case 'A': + if(fmt.substr(i, 3) === "A/P") return true; + if(fmt.substr(i, 5) === "AM/PM") return true; + ++i; break; + case '[': + o = c; + while(fmt.charAt(i++) !== ']' && i < fmt.length) o += fmt.charAt(i); + if(o.match(abstime)) return true; + break; + 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)); + 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; + case ' ': ++i; break; + default: ++i; break; + } + } + return false; +} +SSF.is_date = fmt_is_date; diff --git a/bits/82_eval.js b/bits/82_eval.js new file mode 100644 index 0000000..138e7e2 --- /dev/null +++ b/bits/82_eval.js @@ -0,0 +1,189 @@ +function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) { + var out = [], o = "", i = 0, c = "", lst='t', q, dt, j, cc; + var hr='H'; + /* Tokenize */ + while(i < fmt.length) { + switch((c = fmt.charAt(i))) { + case 'G': /* General */ + if(!isgeneral(fmt, i)) throw new Error('unrecognized character ' + c + ' in ' +fmt); + out[out.length] = {t:'G', v:'General'}; i+=7; break; + case '"': /* Literal text */ + for(o="";(cc=fmt.charCodeAt(++i)) !== 34 && i < fmt.length;) o += String.fromCharCode(cc); + out[out.length] = {t:'t', v:o}; ++i; break; + case '\\': var w = fmt.charAt(++i), t = (w === "(" || w === ")") ? w : 't'; + out[out.length] = {t:t, v:w}; ++i; break; + case '_': out[out.length] = {t:'t', v:" "}; i+=2; break; + case '@': /* Text Placeholder */ + out[out.length] = {t:'T', v:v}; ++i; break; + case 'B': case 'b': + if(fmt.charAt(i+1) === "1" || fmt.charAt(i+1) === "2") { + if(dt==null) { dt=parse_date_code(v, opts, fmt.charAt(i+1) === "2"); if(dt==null) return ""; } + out[out.length] = {t:'X', v:fmt.substr(i,2)}; lst = c; i+=2; break; + } + /* falls through */ + case 'M': case 'D': case 'Y': case 'H': case 'S': case 'E': + c = c.toLowerCase(); + /* falls through */ + case 'm': case 'd': case 'y': case 'h': case 's': case 'e': case 'g': + if(v < 0) return ""; + if(dt==null) { dt=parse_date_code(v, opts); if(dt==null) return ""; } + o = c; while(++i= 12 ? "P" : "A"; q.t = 'T'; hr='h';i+=3;} + else if(fmt.substr(i,5) === "AM/PM") { if(dt!=null) q.v = dt.H >= 12 ? "PM" : "AM"; q.t = 'T'; i+=5; hr='h'; } + else { q.t = "t"; ++i; } + if(dt==null && q.t === 'T') return ""; + out[out.length] = q; lst = c; break; + case '[': + o = c; + while(fmt.charAt(i++) !== ']' && i < fmt.length) o += fmt.charAt(i); + if(o.slice(-1) !== ']') throw 'unterminated "[" block: |' + o + '|'; + 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=""; } + break; + /* Numbers */ + case '.': + if(dt != null) { + o = c; while((c=fmt.charAt(++i)) === "0") o += c; + out[out.length] = {t:'s', v:o}; break; + } + /* falls through */ + case '0': case '#': + o = c; while(++i < fmt.length && "0#?.,E+-%".indexOf(c=fmt.charAt(i)) > -1 || c=='\\' && fmt.charAt(i+1) == "-" && "0#".indexOf(fmt.charAt(i+2))>-1) o += c; + out[out.length] = {t:'n', v:o}; break; + case '?': + o = c; while(fmt.charAt(++i) === c) o+=c; + q={t:c, v:o}; out[out.length] = q; lst = c; break; + case '*': ++i; if(fmt.charAt(i) == ' ' || fmt.charAt(i) == '*') ++i; break; // ** + case '(': case ')': out[out.length] = {t:(flen===1?'t':c), v:c}; ++i; break; + case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': + o = c; while(i < fmt.length && "0123456789".indexOf(fmt.charAt(++i)) > -1) o+=fmt.charAt(i); + out[out.length] = {t:'D', v:o}; break; + case ' ': out[out.length] = {t:c, v:c}; ++i; break; + default: + if(",$-+/():!^&'~{}<>=€acfijklopqrtuvwxz".indexOf(c) === -1) throw new Error('unrecognized character ' + c + ' in ' + fmt); + out[out.length] = {t:'t', v:c}; ++i; break; + } + } + var bt = 0, ss0 = 0, ssm; + for(i=out.length-1, lst='t'; i >= 0; --i) { + switch(out[i].t) { + case 'h': case 'H': out[i].t = hr; lst='h'; if(bt < 1) bt = 1; break; + case 's': + if((ssm=out[i].v.match(/\.0+$/))) ss0=Math.max(ss0,ssm[0].length-1); + if(bt < 3) bt = 3; + /* 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"); + break; + case 'Z': + if(bt < 1 && out[i].v.match(/[Hh]/)) bt = 1; + if(bt < 2 && out[i].v.match(/[Mm]/)) bt = 2; + if(bt < 3 && out[i].v.match(/[Ss]/)) bt = 3; + } + } + switch(bt) { + case 0: break; + case 1: + /*::if(!dt) break;*/ + if(dt.u >= 0.5) { dt.u = 0; ++dt.S; } + if(dt.S >= 60) { dt.S = 0; ++dt.M; } + if(dt.M >= 60) { dt.M = 0; ++dt.H; } + break; + case 2: + /*::if(!dt) break;*/ + if(dt.u >= 0.5) { dt.u = 0; ++dt.S; } + if(dt.S >= 60) { dt.S = 0; ++dt.M; } + break; + } + /* replace fields */ + var nstr = "", jj; + for(i=0; i < out.length; ++i) { + switch(out[i].t) { + case 't': case 'T': case ' ': case 'D': break; + case 'X': out[i].v = ""; out[i].t = ";"; break; + case 'd': case 'm': case 'y': case 'h': case 'H': case 'M': case 's': case 'e': case 'b': case 'Z': + /*::if(!dt) throw "unreachable"; */ + out[i].v = write_date(out[i].t.charCodeAt(0), out[i].v, dt, ss0); + out[i].t = 't'; break; + case 'n': case '(': case '?': + jj = i+1; + while(out[jj] != null && ( + (c=out[jj].t) === "?" || c === "D" || + (c === " " || c === "t") && out[jj+1] != null && (out[jj+1].t === '?' || out[jj+1].t === "t" && out[jj+1].v === '/') || + out[i].t === '(' && (c === ' ' || c === 'n' || c === ')') || + c === 't' && (out[jj].v === '/' || '$€'.indexOf(out[jj].v) > -1 || out[jj].v === ' ' && out[jj+1] != null && out[jj+1].t == '?') + )) { + out[i].v += out[jj].v; + out[jj] = {v:"", t:";"}; ++jj; + } + nstr += out[i].v; + i = jj-1; break; + case 'G': out[i].t = 't'; out[i].v = general_fmt(v,opts); break; + } + } + 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); /* '(' */ + 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; } + var lasti=out.length; + if(decpt === out.length && ostr.indexOf("E") === -1) { + for(i=out.length-1; i>= 0;--i) { + if(out[i] == null || 'n?('.indexOf(out[i].t) === -1) continue; + if(jj>=out[i].v.length-1) { jj -= out[i].v.length; out[i].v = ostr.substr(jj+1, out[i].v.length); } + else if(jj < 0) out[i].v = ""; + else { out[i].v = ostr.substr(0, jj+1); jj = -1; } + out[i].t = 't'; + lasti = i; + } + if(jj>=0 && lasti= 0; --i) { + if(out[i] == null || 'n?('.indexOf(out[i].t) === -1) continue; + j=out[i].v.indexOf(".")>-1&&i===decpt?out[i].v.indexOf(".")-1:out[i].v.length-1; + vv = out[i].v.substr(j+1); + for(; j>=0; --j) { + if(jj>=0 && (out[i].v[j] === "0" || out[i].v[j] === "#")) vv = ostr[jj--] + vv; + } + out[i].v = vv; + out[i].t = 't'; + lasti = i; + } + if(jj>=0 && lasti-1&&i===decpt?out[i].v.indexOf(".")+1:0; + vv = out[i].v.substr(0,j); + for(; j-1) { + myv = (flen >1 && v < 0 && i>0 && out[i-1].v === "-" ? -v:v); + out[i].v = write_num(out[i].t, out[i].v, myv); + out[i].t = 't'; + } + var retval = ""; + for(i=0; i !== out.length; ++i) if(out[i] != null) retval += out[i].v; + return retval; +} +SSF._eval = eval_fmt; diff --git a/bits/88_cond.js b/bits/88_cond.js new file mode 100644 index 0000000..ef0dd73 --- /dev/null +++ b/bits/88_cond.js @@ -0,0 +1,15 @@ +var cfregex = /\[[=<>]/; +var cfregex2 = /\[([=<>]*)(-?\d+\.?\d*)\]/; +function chkcond(v, rr) { + if(rr == null) return false; + var thresh = parseFloat(rr[2]); + switch(rr[1]) { + case "=": if(v == thresh) return true; break; + case ">": if(v > thresh) return true; break; + case "<": if(v < thresh) return true; break; + case "<>": if(v != thresh) return true; break; + case ">=": if(v >= thresh) return true; break; + case "<=": if(v <= thresh) return true; break; + } + return false; +} diff --git a/bits/90_main.js b/bits/90_main.js new file mode 100644 index 0000000..0ff122f --- /dev/null +++ b/bits/90_main.js @@ -0,0 +1,35 @@ +function choose_fmt(f/*:string*/, v) { + var fmt = split_fmt(f); + var l = fmt.length, lat = fmt[l-1].indexOf("@"); + if(l<4 && lat>-1) --l; + if(fmt.length > 4) throw new Error("cannot find right format for |" + fmt.join("|") + "|"); + if(typeof v !== "number") return [4, fmt.length === 4 || lat>-1?fmt[fmt.length-1]:"@"]; + switch(fmt.length) { + case 1: fmt = lat>-1 ? ["General", "General", "General", fmt[0]] : [fmt[0], fmt[0], fmt[0], "@"]; break; + case 2: fmt = lat>-1 ? [fmt[0], fmt[0], fmt[0], fmt[1]] : [fmt[0], fmt[1], fmt[0], "@"]; break; + case 3: fmt = lat>-1 ? [fmt[0], fmt[1], fmt[0], fmt[2]] : [fmt[0], fmt[1], fmt[2], "@"]; break; + case 4: break; + } + var ff = v > 0 ? fmt[0] : v < 0 ? fmt[1] : fmt[2]; + if(fmt[0].indexOf("[") === -1 && fmt[1].indexOf("[") === -1) return [l, ff]; + if(fmt[0].match(cfregex) != null || fmt[1].match(cfregex) != null) { + var m1 = fmt[0].match(cfregex2); + var m2 = fmt[1].match(cfregex2); + return chkcond(v, m1) ? [l, fmt[0]] : chkcond(v, m2) ? [l, fmt[1]] : [l, fmt[m1 != null && m2 != null ? 2 : 1]]; + } + return [l, ff]; +} +function format(fmt/*:string|number*/,v/*:any*/,o/*:?any*/) { + 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; + } + if(isgeneral(sfmt,0)) return general_fmt(v, o); + var f = choose_fmt(sfmt, v); + if(isgeneral(f[1])) return general_fmt(v, o); + if(v === true) v = "TRUE"; else if(v === false) v = "FALSE"; + else if(v === "" || v == null) return ""; + return eval_fmt(f[1], v, o, f[0]); +} diff --git a/bits/98_exports.js b/bits/98_exports.js new file mode 100644 index 0000000..ad35fac --- /dev/null +++ b/bits/98_exports.js @@ -0,0 +1,5 @@ +SSF._table = table_fmt; +SSF.load = function load_entry(fmt/*:string*/, idx/*:number*/) { table_fmt[idx] = fmt; }; +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); }; diff --git a/bits/99_footer.js b/bits/99_footer.js new file mode 100644 index 0000000..036c6e1 --- /dev/null +++ b/bits/99_footer.js @@ -0,0 +1,5 @@ +}; +make_ssf(SSF); +/*global module */ +/*:: declare var DO_NOT_EXPORT_SSF: any; */ +if(typeof module !== 'undefined' && typeof DO_NOT_EXPORT_SSF === 'undefined') module.exports = SSF; diff --git a/misc/flow.js b/misc/flow.js index 47c528c..b81c296 100644 --- a/misc/flow.js +++ b/misc/flow.js @@ -3,6 +3,7 @@ type SSFModule = { format(fmt:string|number, v:any, o:any):string; + is_date(fmt:string):boolean; }; */ diff --git a/package.json b/package.json index afee4ab..905dac6 100644 --- a/package.json +++ b/package.json @@ -1,31 +1,33 @@ { - "name": "ssf", - "version": "0.8.1", - "author": "SheetJS", - "description": "Format data using ECMA-376 spreadsheet Format Codes", - "keywords": [ "format", "sprintf", "spreadsheet" ], - "main": "ssf.js", - "dependencies": { - "voc":"", - "colors":"0.6.2", - "frac":"0.3.1" - }, - "devDependencies": { - "mocha":"" - }, - "repository": { "type":"git", "url":"git://github.com/SheetJS/ssf.git" }, - "scripts": { - "test": "mocha -R spec" - }, - "bin": { - "ssf": "./bin/ssf.njs" - }, - "config": { - "blanket": { - "pattern": "ssf.js" - } - }, - "bugs": { "url": "https://github.com/SheetJS/ssf/issues" }, - "license": "Apache-2.0", - "engines": { "node": ">=0.8" } + "name": "ssf", + "version": "0.9.0", + "author": "SheetJS", + "description": "Format data using ECMA-376 spreadsheet Format Codes", + "keywords": [ "format", "sprintf", "spreadsheet" ], + "bin": { + "ssf": "./bin/ssf.njs" + }, + "main": "./ssf", + "dependencies": { + "voc":"", + "colors":"0.6.2", + "frac":"0.3.1" + }, + "devDependencies": { + "mocha":"", + "uglify-js":"" + }, + "repository": { "type":"git", "url":"git://github.com/SheetJS/ssf.git" }, + "scripts": { + "test": "make test" + }, + "config": { + "blanket": { + "pattern": "ssf.js" + } + }, + "homepage": "https://oss.sheetjs.com/ssf", + "bugs": { "url": "https://github.com/SheetJS/ssf/issues" }, + "license": "Apache-2.0", + "engines": { "node": ">=0.8" } } diff --git a/ssf.flow.js b/ssf.flow.js new file mode 100644 index 0000000..125412a --- /dev/null +++ b/ssf.flow.js @@ -0,0 +1,833 @@ +/* ssf.js (C) 2013-present SheetJS -- http://sheetjs.com */ +/*jshint -W041 */ +var SSF = {}; +var make_ssf = function make_ssf(SSF){ +SSF.version = '0.9.0'; +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;} +function pad_(v/*:any*/,d/*:number*/)/*:string*/{var t=""+v;return t.length>=d?t:fill(' ',d-t.length)+t;} +function rpad_(v/*:any*/,d/*:number*/)/*:string*/{var t=""+v; return t.length>=d?t:t+fill(' ',d-t.length);} +function pad0r1(v/*:any*/,d/*:number*/)/*:string*/{var t=""+Math.round(v); return t.length>=d?t:fill('0',d-t.length)+t;} +function pad0r2(v/*:any*/,d/*:number*/)/*:string*/{var t=""+v; return t.length>=d?t:fill('0',d-t.length)+t;} +var p2_32 = Math.pow(2,32); +function pad0r(v/*:any*/,d/*:number*/)/*:string*/{if(v>p2_32||v<-p2_32) return pad0r1(v,d); var i = Math.round(v); return pad0r2(i,d); } +function isgeneral(s/*:string*/, i/*:?number*/)/*:boolean*/ { i = i || 0; return s.length >= 7 + i && (s.charCodeAt(i)|32) === 103 && (s.charCodeAt(i+1)|32) === 101 && (s.charCodeAt(i+2)|32) === 110 && (s.charCodeAt(i+3)|32) === 101 && (s.charCodeAt(i+4)|32) === 114 && (s.charCodeAt(i+5)|32) === 97 && (s.charCodeAt(i+6)|32) === 108; } +/* Options */ +var opts_fmt/*:Array >*/ = [ + ["date1904", 0], + ["output", ""], + ["WTF", false] +]; +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'], + ['Tue', 'Tuesday'], + ['Wed', 'Wednesday'], + ['Thu', 'Thursday'], + ['Fri', 'Friday'], + ['Sat', 'Saturday'] +]; +var months/*:Array >*/ = [ + ['J', 'Jan', 'January'], + ['F', 'Feb', 'February'], + ['M', 'Mar', 'March'], + ['A', 'Apr', 'April'], + ['M', 'May', 'May'], + ['J', 'Jun', 'June'], + ['J', 'Jul', 'July'], + ['A', 'Aug', 'August'], + ['S', 'Sep', 'September'], + ['O', 'Oct', 'October'], + ['N', 'Nov', 'November'], + ['D', 'Dec', 'December'] +]; +function frac(x, D, mixed) { + var sgn = x < 0 ? -1 : 1; + var B = x * sgn; + var P_2 = 0, P_1 = 1, P = 0; + var Q_2 = 1, Q_1 = 0, Q = 0; + var A = Math.floor(B); + while(Q_1 < D) { + A = Math.floor(B); + P = A * P_1 + P_2; + Q = A * Q_1 + Q_2; + if((B - A) < 0.0000000005) 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(!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]; +} +function general_fmt_int(v/*:number*/, opts/*:?any*/)/*:string*/ { return ""+v; } +SSF._general_int = general_fmt_int; +var general_fmt_num = (function make_general_fmt_num() { +var gnr1 = /\.(\d*[1-9])0+$/, gnr2 = /\.0*$/, gnr4 = /\.(\d*[1-9])0+/, gnr5 = /\.0*[Ee]/, gnr6 = /(E[+-])(\d)$/; +function gfn2(v) { + var w = (v<0?12:11); + var o = gfn5(v.toFixed(12)); if(o.length <= w) return o; + o = v.toPrecision(10); if(o.length <= w) return o; + return v.toExponential(5); +} +function gfn3(v) { + var o = v.toFixed(11).replace(gnr1,".$1"); + if(o.length > (v<0?12:11)) o = v.toPrecision(6); + return o; +} +function gfn4(o) { + for(var i = 0; i != o.length; ++i) if((o.charCodeAt(i) | 0x20) === 101) return o.replace(gnr4,".$1").replace(gnr5,"E").replace("e","E").replace(gnr6,"$10$2"); + return o; +} +function gfn5(o) { + //for(var i = 0; i != o.length; ++i) if(o.charCodeAt(i) === 46) return o.replace(gnr2,"").replace(gnr1,".$1"); + //return o; + return o.indexOf(".") > -1 ? o.replace(gnr2,"").replace(gnr1,".$1") : o; +} +return function general_fmt_num(v/*:number*/, opts/*:?any*/)/*:string*/ { + var V = Math.floor(Math.log(Math.abs(v))*Math.LOG10E), o; + if(V >= -4 && V <= -1) o = v.toPrecision(10+V); + else if(Math.abs(V) <= 9) o = gfn2(v); + else if(V === 10) o = v.toFixed(10).substr(0,12); + else o = gfn3(v); + return gfn5(gfn4(o)); +};})(); +SSF._general_num = general_fmt_num; +function general_fmt(v/*:any*/, opts/*:?any*/) { + switch(typeof v) { + 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); + } + throw new Error("unsupported value in General format: " + v); +} +SSF._general = general_fmt; +function fix_hijri(date, o) { return 0; } +function parse_date_code(v/*:number*/,opts/*:?any*/,b2/*:?boolean*/) { + if(v > 2958465 || v < 0) return null; + var date = (v|0), time = Math.floor(86400 * (v - date)), dow=0; + var dout=[]; + var out={D:date, T:time, u:86400*(v-date)-time,y:0,m:0,d:0,H:0,M:0,S:0,q:0}; + if(Math.abs(out.u) < 1e-6) out.u = 0; + fixopts(opts != null ? opts : (opts=[])); + if(opts.date1904) date += 1462; + if(out.u > 0.999) { + out.u = 0; + if(++time == 86400) { time = 0; ++date; } + } + if(date === 60) {dout = b2 ? [1317,10,29] : [1900,2,29]; dow=3;} + else if(date === 0) {dout = b2 ? [1317,8,29] : [1900,1,0]; dow=6;} + else { + if(date > 60) --date; + /* 1 = Jan 1 1900 in Gregorian */ + var d = new Date(1900, 0, 1); + d.setDate(d.getDate() + date - 1); + dout = [d.getFullYear(), d.getMonth()+1,d.getDate()]; + dow = d.getDay(); + if(date < 60) dow = (dow + 6) % 7; + if(b2) dow = fix_hijri(d, dout); + } + out.y = dout[0]; out.m = dout[1]; out.d = dout[2]; + out.S = time % 60; time = Math.floor(time / 60); + out.M = time % 60; time = Math.floor(time / 60); + out.H = time; + out.q = dow; + return out; +} +SSF.parse_date_code = parse_date_code; +/*jshint -W086 */ +function write_date(type/*:number*/, fmt/*:string*/, val, ss0/*:?number*/)/*:string*/ { + var o="", ss=0, tt=0, y = val.y, out, outl = 0; + switch(type) { + case 98: /* 'b' buddhist year */ + y = val.y + 543; + /* falls through */ + case 121: /* 'y' year */ + switch(fmt.length) { + case 1: case 2: out = y % 100; outl = 2; break; + default: out = y % 10000; outl = 4; break; + } break; + case 109: /* 'm' month */ + switch(fmt.length) { + case 1: case 2: out = val.m; outl = fmt.length; break; + case 3: return months[val.m-1][1]; + case 5: return months[val.m-1][0]; + default: return months[val.m-1][2]; + } break; + case 100: /* 'd' day */ + switch(fmt.length) { + case 1: case 2: out = val.d; outl = fmt.length; break; + case 3: return days[val.q][0]; + default: return days[val.q][1]; + } break; + case 104: /* 'h' 12-hour */ + switch(fmt.length) { + case 1: case 2: out = 1+(val.H+11)%12; outl = fmt.length; break; + default: throw 'bad hour format: ' + fmt; + } break; + case 72: /* 'H' 24-hour */ + switch(fmt.length) { + case 1: case 2: out = val.H; outl = fmt.length; break; + default: throw 'bad hour format: ' + fmt; + } break; + case 77: /* 'M' minutes */ + switch(fmt.length) { + case 1: case 2: out = val.M; outl = fmt.length; break; + default: throw 'bad minute format: ' + fmt; + } break; + case 115: /* 's' seconds */ + if(val.u === 0) switch(fmt) { + case 's': case 'ss': return pad0(val.S, fmt.length); + case '.0': case '.00': case '.000': + } + switch(fmt) { + case 's': case 'ss': case '.0': case '.00': case '.000': + /*::if(!ss0) ss0 = 0; */ + if(ss0 >= 2) tt = ss0 === 3 ? 1000 : 100; + else tt = ss0 === 1 ? 10 : 1; + ss = Math.round((tt)*(val.S + val.u)); + if(ss >= 60*tt) ss = 0; + if(fmt === 's') return ss === 0 ? "0" : ""+ss/tt; + o = pad0(ss,2 + ss0); + if(fmt === 'ss') return o.substr(0,2); + return "." + o.substr(2,fmt.length-1); + default: throw 'bad second format: ' + fmt; + } + case 90: /* 'Z' absolute time */ + switch(fmt) { + case '[h]': case '[hh]': out = val.D*24+val.H; break; + case '[m]': case '[mm]': out = (val.D*24+val.H)*60+val.M; break; + case '[s]': case '[ss]': out = ((val.D*24+val.H)*60+val.M)*60+Math.round(val.S+val.u); break; + default: throw 'bad abstime format: ' + fmt; + } outl = fmt.length === 3 ? 1 : 2; break; + case 101: /* 'e' era */ + out = y; outl = 1; + } + if(outl > 0) return pad0(out, outl); else return ""; +} +/*jshint +W086 */ +function commaify(s/*:string*/)/*:string*/ { + if(s.length <= 3) return s; + var j = (s.length % 3), o = s.substr(0,j); + for(; j!=s.length; j+=3) o+=(o.length > 0 ? "," : "") + s.substr(j,3); + return o; +} +var write_num = (function make_write_num(){ +var pct1 = /%/g; +function write_num_pct(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string*/{ + var sfmt = fmt.replace(pct1,""), mul = fmt.length - sfmt.length; + return write_num(type, sfmt, val * Math.pow(10,2*mul)) + fill("%",mul); +} +function write_num_cm(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string*/{ + var idx = fmt.length - 1; + while(fmt.charCodeAt(idx-1) === 44) --idx; + return write_num(type, fmt.substr(0,idx), val / Math.pow(10,3*(fmt.length-idx))); +} +function write_num_exp(fmt/*:string*/, val/*:number*/)/*:string*/{ + var o/*:string*/; + var idx = fmt.indexOf("E") - fmt.indexOf(".") - 1; + if(fmt.match(/^#+0.0E\+0$/)) { + var period = fmt.indexOf("."); if(period === -1) period=fmt.indexOf('E'); + var ee = Math.floor(Math.log(Math.abs(val))*Math.LOG10E)%period; + if(ee < 0) ee += period; + o = (val/Math.pow(10,ee)).toPrecision(idx+1+(period+ee)%period); + if(o.indexOf("e") === -1) { + var fakee = Math.floor(Math.log(Math.abs(val))*Math.LOG10E); + if(o.indexOf(".") === -1) o = o.charAt(0) + "." + o.substr(1) + "E+" + (fakee - o.length+ee); + else o += "E+" + (fakee - ee); + while(o.substr(0,2) === "0.") { + o = o.charAt(0) + o.substr(2,period) + "." + o.substr(2+period); + o = o.replace(/^0+([1-9])/,"$1").replace(/^0+\./,"0."); + } + o = o.replace(/\+-/,"-"); + } + o = o.replace(/^([+-]?)(\d*)\.(\d*)[Ee]/,function($$,$1,$2,$3) { return $1 + $2 + $3.substr(0,(period+ee)%period) + "." + $3.substr(ee) + "E"; }); + } else o = val.toExponential(idx); + if(fmt.match(/E\+00$/) && o.match(/e[+-]\d$/)) o = o.substr(0,o.length-1) + "0" + o.charAt(o.length-1); + if(fmt.match(/E\-/) && o.match(/e\+/)) o = o.replace(/e\+/,"e"); + return o.replace("e","E"); +} +var frac1 = /# (\?+)( ?)\/( ?)(\d+)/; +function write_num_f1(r/*:Array*/, aval/*:number*/, sign/*:string*/)/*:string*/ { + var den = parseInt(r[4],10), rr = Math.round(aval * den), base = Math.floor(rr/den); + var myn = (rr - base*den), myd = den; + return sign + (base === 0 ? "" : ""+base) + " " + (myn === 0 ? fill(" ", r[1].length + 1 + r[4].length) : pad_(myn,r[1].length) + r[2] + "/" + r[3] + pad0(myd,r[4].length)); +} +function write_num_f2(r/*:Array*/, aval/*:number*/, sign/*:string*/)/*:string*/ { + return sign + (aval === 0 ? "" : ""+aval) + fill(" ", r[1].length + 2 + r[4].length); +} +var dec1 = /^#*0*\.(0+)/; +var closeparen = /\).*[0#]/; +var phone = /\(###\) ###\\?-####/; +function hashq(str/*:string*/)/*:string*/ { + var o = "", cc; + for(var i = 0; i != str.length; ++i) switch((cc=str.charCodeAt(i))) { + case 35: break; + case 63: o+= " "; break; + case 48: o+= "0"; break; + default: o+= String.fromCharCode(cc); + } + return o; +} +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)); +} +function carry(val/*:number*/, d/*:number*/)/*:number*/ { + 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[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[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"; */ + // $FlowIgnore + o = (""+val).replace(/^([^\.]+)$/,"$1."+r[1]).replace(/\.$/,"."+r[1]); + // $FlowIgnore + o = o.replace(/\.(\d*)$/,function($$, $1) { + /*:: if(!Array.isArray(r)) throw "unreachable"; */ + // $FlowIgnore + return "." + $1 + fill("0", r[1].length-$1.length); }); + // $FlowIgnore + 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*/ { + var out/*:Array*/ = []; + var in_str = false, cc; + for(var i = 0, j = 0; i < fmt.length; ++i) switch((cc=fmt.charCodeAt(i))) { + case 34: /* '"' */ + in_str = !in_str; break; + case 95: case 42: case 92: /* '_' '*' '\\' */ + ++i; break; + case 59: /* ';' */ + out[out.length] = fmt.substr(j,i-j); + j = i+1; + } + out[out.length] = fmt.substr(j); + if(in_str === true) throw new Error("Format |" + fmt + "| unterminated string "); + return out; +} +SSF._split = split_fmt; +var abstime = /\[[HhMmSs]*\]/; +function fmt_is_date(fmt/*:string*/)/*:boolean*/ { + var i = 0, cc = 0, c = "", o = ""; + while(i < fmt.length) { + switch((c = fmt.charAt(i))) { + case 'G': if(isgeneral(fmt, i)) i+= 6; i++; break; + case '"': for(;(cc=fmt.charCodeAt(++i)) !== 34 && i < fmt.length;) ++i; ++i; break; + case '\\': i+=2; break; + case '_': i+=2; break; + case '@': ++i; break; + case 'B': case 'b': + if(fmt.charAt(i+1) === "1" || fmt.charAt(i+1) === "2") return true; + /* falls through */ + case 'M': case 'D': case 'Y': case 'H': case 'S': case 'E': + /* falls through */ + case 'm': case 'd': case 'y': case 'h': case 's': case 'e': case 'g': return true; + case 'A': + if(fmt.substr(i, 3) === "A/P") return true; + if(fmt.substr(i, 5) === "AM/PM") return true; + ++i; break; + case '[': + o = c; + while(fmt.charAt(i++) !== ']' && i < fmt.length) o += fmt.charAt(i); + if(o.match(abstime)) return true; + break; + 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)); + 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; + case ' ': ++i; break; + default: ++i; break; + } + } + return false; +} +SSF.is_date = fmt_is_date; +function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) { + var out = [], o = "", i = 0, c = "", lst='t', q, dt, j, cc; + var hr='H'; + /* Tokenize */ + while(i < fmt.length) { + switch((c = fmt.charAt(i))) { + case 'G': /* General */ + if(!isgeneral(fmt, i)) throw new Error('unrecognized character ' + c + ' in ' +fmt); + out[out.length] = {t:'G', v:'General'}; i+=7; break; + case '"': /* Literal text */ + for(o="";(cc=fmt.charCodeAt(++i)) !== 34 && i < fmt.length;) o += String.fromCharCode(cc); + out[out.length] = {t:'t', v:o}; ++i; break; + case '\\': var w = fmt.charAt(++i), t = (w === "(" || w === ")") ? w : 't'; + out[out.length] = {t:t, v:w}; ++i; break; + case '_': out[out.length] = {t:'t', v:" "}; i+=2; break; + case '@': /* Text Placeholder */ + out[out.length] = {t:'T', v:v}; ++i; break; + case 'B': case 'b': + if(fmt.charAt(i+1) === "1" || fmt.charAt(i+1) === "2") { + if(dt==null) { dt=parse_date_code(v, opts, fmt.charAt(i+1) === "2"); if(dt==null) return ""; } + out[out.length] = {t:'X', v:fmt.substr(i,2)}; lst = c; i+=2; break; + } + /* falls through */ + case 'M': case 'D': case 'Y': case 'H': case 'S': case 'E': + c = c.toLowerCase(); + /* falls through */ + case 'm': case 'd': case 'y': case 'h': case 's': case 'e': case 'g': + if(v < 0) return ""; + if(dt==null) { dt=parse_date_code(v, opts); if(dt==null) return ""; } + o = c; while(++i= 12 ? "P" : "A"; q.t = 'T'; hr='h';i+=3;} + else if(fmt.substr(i,5) === "AM/PM") { if(dt!=null) q.v = dt.H >= 12 ? "PM" : "AM"; q.t = 'T'; i+=5; hr='h'; } + else { q.t = "t"; ++i; } + if(dt==null && q.t === 'T') return ""; + out[out.length] = q; lst = c; break; + case '[': + o = c; + while(fmt.charAt(i++) !== ']' && i < fmt.length) o += fmt.charAt(i); + if(o.slice(-1) !== ']') throw 'unterminated "[" block: |' + o + '|'; + 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=""; } + break; + /* Numbers */ + case '.': + if(dt != null) { + o = c; while((c=fmt.charAt(++i)) === "0") o += c; + out[out.length] = {t:'s', v:o}; break; + } + /* falls through */ + case '0': case '#': + o = c; while(++i < fmt.length && "0#?.,E+-%".indexOf(c=fmt.charAt(i)) > -1 || c=='\\' && fmt.charAt(i+1) == "-" && "0#".indexOf(fmt.charAt(i+2))>-1) o += c; + out[out.length] = {t:'n', v:o}; break; + case '?': + o = c; while(fmt.charAt(++i) === c) o+=c; + q={t:c, v:o}; out[out.length] = q; lst = c; break; + case '*': ++i; if(fmt.charAt(i) == ' ' || fmt.charAt(i) == '*') ++i; break; // ** + case '(': case ')': out[out.length] = {t:(flen===1?'t':c), v:c}; ++i; break; + case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': + o = c; while(i < fmt.length && "0123456789".indexOf(fmt.charAt(++i)) > -1) o+=fmt.charAt(i); + out[out.length] = {t:'D', v:o}; break; + case ' ': out[out.length] = {t:c, v:c}; ++i; break; + default: + if(",$-+/():!^&'~{}<>=€acfijklopqrtuvwxz".indexOf(c) === -1) throw new Error('unrecognized character ' + c + ' in ' + fmt); + out[out.length] = {t:'t', v:c}; ++i; break; + } + } + var bt = 0, ss0 = 0, ssm; + for(i=out.length-1, lst='t'; i >= 0; --i) { + switch(out[i].t) { + case 'h': case 'H': out[i].t = hr; lst='h'; if(bt < 1) bt = 1; break; + case 's': + if((ssm=out[i].v.match(/\.0+$/))) ss0=Math.max(ss0,ssm[0].length-1); + if(bt < 3) bt = 3; + /* 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"); + break; + case 'Z': + if(bt < 1 && out[i].v.match(/[Hh]/)) bt = 1; + if(bt < 2 && out[i].v.match(/[Mm]/)) bt = 2; + if(bt < 3 && out[i].v.match(/[Ss]/)) bt = 3; + } + } + switch(bt) { + case 0: break; + case 1: + /*::if(!dt) break;*/ + if(dt.u >= 0.5) { dt.u = 0; ++dt.S; } + if(dt.S >= 60) { dt.S = 0; ++dt.M; } + if(dt.M >= 60) { dt.M = 0; ++dt.H; } + break; + case 2: + /*::if(!dt) break;*/ + if(dt.u >= 0.5) { dt.u = 0; ++dt.S; } + if(dt.S >= 60) { dt.S = 0; ++dt.M; } + break; + } + /* replace fields */ + var nstr = "", jj; + for(i=0; i < out.length; ++i) { + switch(out[i].t) { + case 't': case 'T': case ' ': case 'D': break; + case 'X': out[i].v = ""; out[i].t = ";"; break; + case 'd': case 'm': case 'y': case 'h': case 'H': case 'M': case 's': case 'e': case 'b': case 'Z': + /*::if(!dt) throw "unreachable"; */ + out[i].v = write_date(out[i].t.charCodeAt(0), out[i].v, dt, ss0); + out[i].t = 't'; break; + case 'n': case '(': case '?': + jj = i+1; + while(out[jj] != null && ( + (c=out[jj].t) === "?" || c === "D" || + (c === " " || c === "t") && out[jj+1] != null && (out[jj+1].t === '?' || out[jj+1].t === "t" && out[jj+1].v === '/') || + out[i].t === '(' && (c === ' ' || c === 'n' || c === ')') || + c === 't' && (out[jj].v === '/' || '$€'.indexOf(out[jj].v) > -1 || out[jj].v === ' ' && out[jj+1] != null && out[jj+1].t == '?') + )) { + out[i].v += out[jj].v; + out[jj] = {v:"", t:";"}; ++jj; + } + nstr += out[i].v; + i = jj-1; break; + case 'G': out[i].t = 't'; out[i].v = general_fmt(v,opts); break; + } + } + 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); /* '(' */ + 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; } + var lasti=out.length; + if(decpt === out.length && ostr.indexOf("E") === -1) { + for(i=out.length-1; i>= 0;--i) { + if(out[i] == null || 'n?('.indexOf(out[i].t) === -1) continue; + if(jj>=out[i].v.length-1) { jj -= out[i].v.length; out[i].v = ostr.substr(jj+1, out[i].v.length); } + else if(jj < 0) out[i].v = ""; + else { out[i].v = ostr.substr(0, jj+1); jj = -1; } + out[i].t = 't'; + lasti = i; + } + if(jj>=0 && lasti= 0; --i) { + if(out[i] == null || 'n?('.indexOf(out[i].t) === -1) continue; + j=out[i].v.indexOf(".")>-1&&i===decpt?out[i].v.indexOf(".")-1:out[i].v.length-1; + vv = out[i].v.substr(j+1); + for(; j>=0; --j) { + if(jj>=0 && (out[i].v[j] === "0" || out[i].v[j] === "#")) vv = ostr[jj--] + vv; + } + out[i].v = vv; + out[i].t = 't'; + lasti = i; + } + if(jj>=0 && lasti-1&&i===decpt?out[i].v.indexOf(".")+1:0; + vv = out[i].v.substr(0,j); + for(; j-1) { + myv = (flen >1 && v < 0 && i>0 && out[i-1].v === "-" ? -v:v); + out[i].v = write_num(out[i].t, out[i].v, myv); + out[i].t = 't'; + } + var retval = ""; + for(i=0; i !== out.length; ++i) if(out[i] != null) retval += out[i].v; + return retval; +} +SSF._eval = eval_fmt; +var cfregex = /\[[=<>]/; +var cfregex2 = /\[([=<>]*)(-?\d+\.?\d*)\]/; +function chkcond(v, rr) { + if(rr == null) return false; + var thresh = parseFloat(rr[2]); + switch(rr[1]) { + case "=": if(v == thresh) return true; break; + case ">": if(v > thresh) return true; break; + case "<": if(v < thresh) return true; break; + case "<>": if(v != thresh) return true; break; + case ">=": if(v >= thresh) return true; break; + case "<=": if(v <= thresh) return true; break; + } + return false; +} +function choose_fmt(f/*:string*/, v) { + var fmt = split_fmt(f); + var l = fmt.length, lat = fmt[l-1].indexOf("@"); + if(l<4 && lat>-1) --l; + if(fmt.length > 4) throw new Error("cannot find right format for |" + fmt.join("|") + "|"); + if(typeof v !== "number") return [4, fmt.length === 4 || lat>-1?fmt[fmt.length-1]:"@"]; + switch(fmt.length) { + case 1: fmt = lat>-1 ? ["General", "General", "General", fmt[0]] : [fmt[0], fmt[0], fmt[0], "@"]; break; + case 2: fmt = lat>-1 ? [fmt[0], fmt[0], fmt[0], fmt[1]] : [fmt[0], fmt[1], fmt[0], "@"]; break; + case 3: fmt = lat>-1 ? [fmt[0], fmt[1], fmt[0], fmt[2]] : [fmt[0], fmt[1], fmt[2], "@"]; break; + case 4: break; + } + var ff = v > 0 ? fmt[0] : v < 0 ? fmt[1] : fmt[2]; + if(fmt[0].indexOf("[") === -1 && fmt[1].indexOf("[") === -1) return [l, ff]; + if(fmt[0].match(cfregex) != null || fmt[1].match(cfregex) != null) { + var m1 = fmt[0].match(cfregex2); + var m2 = fmt[1].match(cfregex2); + return chkcond(v, m1) ? [l, fmt[0]] : chkcond(v, m2) ? [l, fmt[1]] : [l, fmt[m1 != null && m2 != null ? 2 : 1]]; + } + return [l, ff]; +} +function format(fmt/*:string|number*/,v/*:any*/,o/*:?any*/) { + 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; + } + if(isgeneral(sfmt,0)) return general_fmt(v, o); + var f = choose_fmt(sfmt, v); + if(isgeneral(f[1])) return general_fmt(v, o); + if(v === true) v = "TRUE"; else if(v === false) v = "FALSE"; + else if(v === "" || v == null) return ""; + return eval_fmt(f[1], v, o, f[0]); +} +SSF._table = table_fmt; +SSF.load = function load_entry(fmt/*:string*/, idx/*:number*/) { table_fmt[idx] = fmt; }; +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); }; +}; +make_ssf(SSF); +/*global module */ +/*:: declare var DO_NOT_EXPORT_SSF: any; */ +if(typeof module !== 'undefined' && typeof DO_NOT_EXPORT_SSF === 'undefined') module.exports = SSF; diff --git a/ssf.js b/ssf.js index 61b1ac9..82b365f 100644 --- a/ssf.js +++ b/ssf.js @@ -2,784 +2,825 @@ /*jshint -W041 */ var SSF = {}; var make_ssf = function make_ssf(SSF){ -SSF.version = '0.8.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;} -function pad_(v/*:any*/,d/*:number*/)/*:string*/{var t=""+v;return t.length>=d?t:fill(' ',d-t.length)+t;} -function rpad_(v/*:any*/,d/*:number*/)/*:string*/{var t=""+v; return t.length>=d?t:t+fill(' ',d-t.length);} -function pad0r1(v/*:any*/,d/*:number*/)/*:string*/{var t=""+Math.round(v); return t.length>=d?t:fill('0',d-t.length)+t;} -function pad0r2(v/*:any*/,d/*:number*/)/*:string*/{var t=""+v; return t.length>=d?t:fill('0',d-t.length)+t;} +SSF.version = '0.9.0'; +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;} +function pad_(v,d){var t=""+v;return t.length>=d?t:fill(' ',d-t.length)+t;} +function rpad_(v,d){var t=""+v; return t.length>=d?t:t+fill(' ',d-t.length);} +function pad0r1(v,d){var t=""+Math.round(v); return t.length>=d?t:fill('0',d-t.length)+t;} +function pad0r2(v,d){var t=""+v; return t.length>=d?t:fill('0',d-t.length)+t;} var p2_32 = Math.pow(2,32); -function pad0r(v/*:any*/,d/*:number*/)/*:string*/{if(v>p2_32||v<-p2_32) return pad0r1(v,d); var i = Math.round(v); return pad0r2(i,d); } -function isgeneral(s/*:string*/, i/*:?number*/)/*:boolean*/ { i = i || 0; return s.length >= 7 + i && (s.charCodeAt(i)|32) === 103 && (s.charCodeAt(i+1)|32) === 101 && (s.charCodeAt(i+2)|32) === 110 && (s.charCodeAt(i+3)|32) === 101 && (s.charCodeAt(i+4)|32) === 114 && (s.charCodeAt(i+5)|32) === 97 && (s.charCodeAt(i+6)|32) === 108; } +function pad0r(v,d){if(v>p2_32||v<-p2_32) return pad0r1(v,d); var i = Math.round(v); return pad0r2(i,d); } +function isgeneral(s, i) { i = i || 0; return s.length >= 7 + i && (s.charCodeAt(i)|32) === 103 && (s.charCodeAt(i+1)|32) === 101 && (s.charCodeAt(i+2)|32) === 110 && (s.charCodeAt(i+3)|32) === 101 && (s.charCodeAt(i+4)|32) === 114 && (s.charCodeAt(i+5)|32) === 97 && (s.charCodeAt(i+6)|32) === 108; } /* Options */ -var opts_fmt/*:Array >*/ = [ - ["date1904", 0], - ["output", ""], - ["WTF", false] +var opts_fmt = [ + ["date1904", 0], + ["output", ""], + ["WTF", false] ]; 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]; + 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' +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'], - ['Tue', 'Tuesday'], - ['Wed', 'Wednesday'], - ['Thu', 'Thursday'], - ['Fri', 'Friday'], - ['Sat', 'Saturday'] + ['Sun', 'Sunday'], + ['Mon', 'Monday'], + ['Tue', 'Tuesday'], + ['Wed', 'Wednesday'], + ['Thu', 'Thursday'], + ['Fri', 'Friday'], + ['Sat', 'Saturday'] ]; var months = [ - ['J', 'Jan', 'January'], - ['F', 'Feb', 'February'], - ['M', 'Mar', 'March'], - ['A', 'Apr', 'April'], - ['M', 'May', 'May'], - ['J', 'Jun', 'June'], - ['J', 'Jul', 'July'], - ['A', 'Aug', 'August'], - ['S', 'Sep', 'September'], - ['O', 'Oct', 'October'], - ['N', 'Nov', 'November'], - ['D', 'Dec', 'December'] + ['J', 'Jan', 'January'], + ['F', 'Feb', 'February'], + ['M', 'Mar', 'March'], + ['A', 'Apr', 'April'], + ['M', 'May', 'May'], + ['J', 'Jun', 'June'], + ['J', 'Jul', 'July'], + ['A', 'Aug', 'August'], + ['S', 'Sep', 'September'], + ['O', 'Oct', 'October'], + ['N', 'Nov', 'November'], + ['D', 'Dec', 'December'] ]; function frac(x, D, mixed) { - var sgn = x < 0 ? -1 : 1; - var B = x * sgn; - var P_2 = 0, P_1 = 1, P = 0; - var Q_2 = 1, Q_1 = 0, Q = 0; - var A = Math.floor(B); - while(Q_1 < D) { - A = Math.floor(B); - P = A * P_1 + P_2; - Q = A * Q_1 + Q_2; - if((B - A) < 0.0000000005) 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(!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]; + var sgn = x < 0 ? -1 : 1; + var B = x * sgn; + var P_2 = 0, P_1 = 1, P = 0; + var Q_2 = 1, Q_1 = 0, Q = 0; + var A = Math.floor(B); + while(Q_1 < D) { + A = Math.floor(B); + P = A * P_1 + P_2; + Q = A * Q_1 + Q_2; + if((B - A) < 0.0000000005) 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(!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]; } -function general_fmt_int(v/*:number*/, opts/*:?any*/)/*:string*/ { return ""+v; } +function general_fmt_int(v, opts) { return ""+v; } SSF._general_int = general_fmt_int; var general_fmt_num = (function make_general_fmt_num() { var gnr1 = /\.(\d*[1-9])0+$/, gnr2 = /\.0*$/, gnr4 = /\.(\d*[1-9])0+/, gnr5 = /\.0*[Ee]/, gnr6 = /(E[+-])(\d)$/; function gfn2(v) { - var w = (v<0?12:11); - var o = gfn5(v.toFixed(12)); if(o.length <= w) return o; - o = v.toPrecision(10); if(o.length <= w) return o; - return v.toExponential(5); + var w = (v<0?12:11); + var o = gfn5(v.toFixed(12)); if(o.length <= w) return o; + o = v.toPrecision(10); if(o.length <= w) return o; + return v.toExponential(5); } function gfn3(v) { - var o = v.toFixed(11).replace(gnr1,".$1"); - if(o.length > (v<0?12:11)) o = v.toPrecision(6); - return o; + var o = v.toFixed(11).replace(gnr1,".$1"); + if(o.length > (v<0?12:11)) o = v.toPrecision(6); + return o; } function gfn4(o) { - for(var i = 0; i != o.length; ++i) if((o.charCodeAt(i) | 0x20) === 101) return o.replace(gnr4,".$1").replace(gnr5,"E").replace("e","E").replace(gnr6,"$10$2"); - return o; + for(var i = 0; i != o.length; ++i) if((o.charCodeAt(i) | 0x20) === 101) return o.replace(gnr4,".$1").replace(gnr5,"E").replace("e","E").replace(gnr6,"$10$2"); + return o; } function gfn5(o) { - //for(var i = 0; i != o.length; ++i) if(o.charCodeAt(i) === 46) return o.replace(gnr2,"").replace(gnr1,".$1"); - //return o; - return o.indexOf(".") > -1 ? o.replace(gnr2,"").replace(gnr1,".$1") : o; + //for(var i = 0; i != o.length; ++i) if(o.charCodeAt(i) === 46) return o.replace(gnr2,"").replace(gnr1,".$1"); + //return o; + return o.indexOf(".") > -1 ? o.replace(gnr2,"").replace(gnr1,".$1") : o; } -return function general_fmt_num(v/*:number*/, opts/*:?any*/)/*:string*/ { - var V = Math.floor(Math.log(Math.abs(v))*Math.LOG10E), o; - if(V >= -4 && V <= -1) o = v.toPrecision(10+V); - else if(Math.abs(V) <= 9) o = gfn2(v); - else if(V === 10) o = v.toFixed(10).substr(0,12); - else o = gfn3(v); - return gfn5(gfn4(o)); +return function general_fmt_num(v, opts) { + var V = Math.floor(Math.log(Math.abs(v))*Math.LOG10E), o; + if(V >= -4 && V <= -1) o = v.toPrecision(10+V); + else if(Math.abs(V) <= 9) o = gfn2(v); + else if(V === 10) o = v.toFixed(10).substr(0,12); + else o = gfn3(v); + return gfn5(gfn4(o)); };})(); SSF._general_num = general_fmt_num; -function general_fmt(v/*:any*/, opts/*:?any*/) { - switch(typeof v) { - 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); - } - throw new Error("unsupported value in General format: " + v); +function general_fmt(v, opts) { + switch(typeof v) { + 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); + } + throw new Error("unsupported value in General format: " + v); } SSF._general = general_fmt; function fix_hijri(date, o) { return 0; } -function parse_date_code(v/*:number*/,opts/*:?any*/,b2/*:?boolean*/) { - if(v > 2958465 || v < 0) return null; - var date = (v|0), time = Math.floor(86400 * (v - date)), dow=0; - var dout=[]; - var out={D:date, T:time, u:86400*(v-date)-time,y:0,m:0,d:0,H:0,M:0,S:0,q:0}; - if(Math.abs(out.u) < 1e-6) out.u = 0; - fixopts(opts != null ? opts : (opts=[])); - if(opts.date1904) date += 1462; - if(out.u > 0.999) { - out.u = 0; - if(++time == 86400) { time = 0; ++date; } - } - if(date === 60) {dout = b2 ? [1317,10,29] : [1900,2,29]; dow=3;} - else if(date === 0) {dout = b2 ? [1317,8,29] : [1900,1,0]; dow=6;} - else { - if(date > 60) --date; - /* 1 = Jan 1 1900 in Gregorian */ - var d = new Date(1900, 0, 1); - d.setDate(d.getDate() + date - 1); - dout = [d.getFullYear(), d.getMonth()+1,d.getDate()]; - dow = d.getDay(); - if(date < 60) dow = (dow + 6) % 7; - if(b2) dow = fix_hijri(d, dout); - } - out.y = dout[0]; out.m = dout[1]; out.d = dout[2]; - out.S = time % 60; time = Math.floor(time / 60); - out.M = time % 60; time = Math.floor(time / 60); - out.H = time; - out.q = dow; - return out; +function parse_date_code(v,opts,b2) { + if(v > 2958465 || v < 0) return null; + var date = (v|0), time = Math.floor(86400 * (v - date)), dow=0; + var dout=[]; + var out={D:date, T:time, u:86400*(v-date)-time,y:0,m:0,d:0,H:0,M:0,S:0,q:0}; + if(Math.abs(out.u) < 1e-6) out.u = 0; + fixopts(opts != null ? opts : (opts=[])); + if(opts.date1904) date += 1462; + if(out.u > 0.999) { + out.u = 0; + if(++time == 86400) { time = 0; ++date; } + } + if(date === 60) {dout = b2 ? [1317,10,29] : [1900,2,29]; dow=3;} + else if(date === 0) {dout = b2 ? [1317,8,29] : [1900,1,0]; dow=6;} + else { + if(date > 60) --date; + /* 1 = Jan 1 1900 in Gregorian */ + var d = new Date(1900, 0, 1); + d.setDate(d.getDate() + date - 1); + dout = [d.getFullYear(), d.getMonth()+1,d.getDate()]; + dow = d.getDay(); + if(date < 60) dow = (dow + 6) % 7; + if(b2) dow = fix_hijri(d, dout); + } + out.y = dout[0]; out.m = dout[1]; out.d = dout[2]; + out.S = time % 60; time = Math.floor(time / 60); + out.M = time % 60; time = Math.floor(time / 60); + out.H = time; + out.q = dow; + return out; } SSF.parse_date_code = parse_date_code; /*jshint -W086 */ -function write_date(type/*:number*/, fmt/*:string*/, val, ss0/*:?number*/)/*:string*/ { - var o="", ss=0, tt=0, y = val.y, out, outl = 0; - switch(type) { - case 98: /* 'b' buddhist year */ - y = val.y + 543; - /* falls through */ - case 121: /* 'y' year */ - switch(fmt.length) { - case 1: case 2: out = y % 100; outl = 2; break; - default: out = y % 10000; outl = 4; break; - } break; - case 109: /* 'm' month */ - switch(fmt.length) { - case 1: case 2: out = val.m; outl = fmt.length; break; - case 3: return months[val.m-1][1]; - case 5: return months[val.m-1][0]; - default: return months[val.m-1][2]; - } break; - case 100: /* 'd' day */ - switch(fmt.length) { - case 1: case 2: out = val.d; outl = fmt.length; break; - case 3: return days[val.q][0]; - default: return days[val.q][1]; - } break; - case 104: /* 'h' 12-hour */ - switch(fmt.length) { - case 1: case 2: out = 1+(val.H+11)%12; outl = fmt.length; break; - default: throw 'bad hour format: ' + fmt; - } break; - case 72: /* 'H' 24-hour */ - switch(fmt.length) { - case 1: case 2: out = val.H; outl = fmt.length; break; - default: throw 'bad hour format: ' + fmt; - } break; - case 77: /* 'M' minutes */ - switch(fmt.length) { - case 1: case 2: out = val.M; outl = fmt.length; break; - default: throw 'bad minute format: ' + fmt; - } break; - case 115: /* 's' seconds */ - if(val.u === 0) switch(fmt) { - case 's': case 'ss': return pad0(val.S, fmt.length); - case '.0': case '.00': case '.000': - } - switch(fmt) { - case 's': case 'ss': case '.0': case '.00': case '.000': - /*::if(!ss0) ss0 = 0; */ - if(ss0 >= 2) tt = ss0 === 3 ? 1000 : 100; - else tt = ss0 === 1 ? 10 : 1; - ss = Math.round((tt)*(val.S + val.u)); - if(ss >= 60*tt) ss = 0; - if(fmt === 's') return ss === 0 ? "0" : ""+ss/tt; - o = pad0(ss,2 + ss0); - if(fmt === 'ss') return o.substr(0,2); - return "." + o.substr(2,fmt.length-1); - default: throw 'bad second format: ' + fmt; - } - case 90: /* 'Z' absolute time */ - switch(fmt) { - case '[h]': case '[hh]': out = val.D*24+val.H; break; - case '[m]': case '[mm]': out = (val.D*24+val.H)*60+val.M; break; - case '[s]': case '[ss]': out = ((val.D*24+val.H)*60+val.M)*60+Math.round(val.S+val.u); break; - default: throw 'bad abstime format: ' + fmt; - } outl = fmt.length === 3 ? 1 : 2; break; - case 101: /* 'e' era */ - out = y; outl = 1; - } - if(outl > 0) return pad0(out, outl); else return ""; +function write_date(type, fmt, val, ss0) { + var o="", ss=0, tt=0, y = val.y, out, outl = 0; + switch(type) { + case 98: /* 'b' buddhist year */ + y = val.y + 543; + /* falls through */ + case 121: /* 'y' year */ + switch(fmt.length) { + case 1: case 2: out = y % 100; outl = 2; break; + default: out = y % 10000; outl = 4; break; + } break; + case 109: /* 'm' month */ + switch(fmt.length) { + case 1: case 2: out = val.m; outl = fmt.length; break; + case 3: return months[val.m-1][1]; + case 5: return months[val.m-1][0]; + default: return months[val.m-1][2]; + } break; + case 100: /* 'd' day */ + switch(fmt.length) { + case 1: case 2: out = val.d; outl = fmt.length; break; + case 3: return days[val.q][0]; + default: return days[val.q][1]; + } break; + case 104: /* 'h' 12-hour */ + switch(fmt.length) { + case 1: case 2: out = 1+(val.H+11)%12; outl = fmt.length; break; + default: throw 'bad hour format: ' + fmt; + } break; + case 72: /* 'H' 24-hour */ + switch(fmt.length) { + case 1: case 2: out = val.H; outl = fmt.length; break; + default: throw 'bad hour format: ' + fmt; + } break; + case 77: /* 'M' minutes */ + switch(fmt.length) { + case 1: case 2: out = val.M; outl = fmt.length; break; + default: throw 'bad minute format: ' + fmt; + } break; + case 115: /* 's' seconds */ + if(val.u === 0) switch(fmt) { + case 's': case 'ss': return pad0(val.S, fmt.length); + case '.0': case '.00': case '.000': + } + switch(fmt) { + case 's': case 'ss': case '.0': case '.00': case '.000': +if(ss0 >= 2) tt = ss0 === 3 ? 1000 : 100; + else tt = ss0 === 1 ? 10 : 1; + ss = Math.round((tt)*(val.S + val.u)); + if(ss >= 60*tt) ss = 0; + if(fmt === 's') return ss === 0 ? "0" : ""+ss/tt; + o = pad0(ss,2 + ss0); + if(fmt === 'ss') return o.substr(0,2); + return "." + o.substr(2,fmt.length-1); + default: throw 'bad second format: ' + fmt; + } + case 90: /* 'Z' absolute time */ + switch(fmt) { + case '[h]': case '[hh]': out = val.D*24+val.H; break; + case '[m]': case '[mm]': out = (val.D*24+val.H)*60+val.M; break; + case '[s]': case '[ss]': out = ((val.D*24+val.H)*60+val.M)*60+Math.round(val.S+val.u); break; + default: throw 'bad abstime format: ' + fmt; + } outl = fmt.length === 3 ? 1 : 2; break; + case 101: /* 'e' era */ + out = y; outl = 1; + } + if(outl > 0) return pad0(out, outl); else return ""; } /*jshint +W086 */ -function commaify(s/*:string*/)/*:string*/ { - if(s.length <= 3) return s; - var j = (s.length % 3), o = s.substr(0,j); - for(; j!=s.length; j+=3) o+=(o.length > 0 ? "," : "") + s.substr(j,3); - return o; +function commaify(s) { + if(s.length <= 3) return s; + var j = (s.length % 3), o = s.substr(0,j); + for(; j!=s.length; j+=3) o+=(o.length > 0 ? "," : "") + s.substr(j,3); + return o; } var write_num = (function make_write_num(){ var pct1 = /%/g; -function write_num_pct(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string*/{ - var sfmt = fmt.replace(pct1,""), mul = fmt.length - sfmt.length; - return write_num(type, sfmt, val * Math.pow(10,2*mul)) + fill("%",mul); +function write_num_pct(type, fmt, val){ + var sfmt = fmt.replace(pct1,""), mul = fmt.length - sfmt.length; + return write_num(type, sfmt, val * Math.pow(10,2*mul)) + fill("%",mul); } -function write_num_cm(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string*/{ - var idx = fmt.length - 1; - while(fmt.charCodeAt(idx-1) === 44) --idx; - return write_num(type, fmt.substr(0,idx), val / Math.pow(10,3*(fmt.length-idx))); +function write_num_cm(type, fmt, val){ + var idx = fmt.length - 1; + while(fmt.charCodeAt(idx-1) === 44) --idx; + return write_num(type, fmt.substr(0,idx), val / Math.pow(10,3*(fmt.length-idx))); } -function write_num_exp(fmt/*:string*/, val/*:number*/)/*:string*/{ - var o/*:string*/; - var idx = fmt.indexOf("E") - fmt.indexOf(".") - 1; - if(fmt.match(/^#+0.0E\+0$/)) { - var period = fmt.indexOf("."); if(period === -1) period=fmt.indexOf('E'); - var ee = Math.floor(Math.log(Math.abs(val))*Math.LOG10E)%period; - if(ee < 0) ee += period; - o = (val/Math.pow(10,ee)).toPrecision(idx+1+(period+ee)%period); - if(o.indexOf("e") === -1) { - var fakee = Math.floor(Math.log(Math.abs(val))*Math.LOG10E); - if(o.indexOf(".") === -1) o = o.charAt(0) + "." + o.substr(1) + "E+" + (fakee - o.length+ee); - else o += "E+" + (fakee - ee); - while(o.substr(0,2) === "0.") { - o = o.charAt(0) + o.substr(2,period) + "." + o.substr(2+period); - o = o.replace(/^0+([1-9])/,"$1").replace(/^0+\./,"0."); - } - o = o.replace(/\+-/,"-"); - } - o = o.replace(/^([+-]?)(\d*)\.(\d*)[Ee]/,function($$,$1,$2,$3) { return $1 + $2 + $3.substr(0,(period+ee)%period) + "." + $3.substr(ee) + "E"; }); - } else o = val.toExponential(idx); - if(fmt.match(/E\+00$/) && o.match(/e[+-]\d$/)) o = o.substr(0,o.length-1) + "0" + o.charAt(o.length-1); - if(fmt.match(/E\-/) && o.match(/e\+/)) o = o.replace(/e\+/,"e"); - return o.replace("e","E"); +function write_num_exp(fmt, val){ + var o; + var idx = fmt.indexOf("E") - fmt.indexOf(".") - 1; + if(fmt.match(/^#+0.0E\+0$/)) { + var period = fmt.indexOf("."); if(period === -1) period=fmt.indexOf('E'); + var ee = Math.floor(Math.log(Math.abs(val))*Math.LOG10E)%period; + if(ee < 0) ee += period; + o = (val/Math.pow(10,ee)).toPrecision(idx+1+(period+ee)%period); + if(o.indexOf("e") === -1) { + var fakee = Math.floor(Math.log(Math.abs(val))*Math.LOG10E); + if(o.indexOf(".") === -1) o = o.charAt(0) + "." + o.substr(1) + "E+" + (fakee - o.length+ee); + else o += "E+" + (fakee - ee); + while(o.substr(0,2) === "0.") { + o = o.charAt(0) + o.substr(2,period) + "." + o.substr(2+period); + o = o.replace(/^0+([1-9])/,"$1").replace(/^0+\./,"0."); + } + o = o.replace(/\+-/,"-"); + } + o = o.replace(/^([+-]?)(\d*)\.(\d*)[Ee]/,function($$,$1,$2,$3) { return $1 + $2 + $3.substr(0,(period+ee)%period) + "." + $3.substr(ee) + "E"; }); + } else o = val.toExponential(idx); + if(fmt.match(/E\+00$/) && o.match(/e[+-]\d$/)) o = o.substr(0,o.length-1) + "0" + o.charAt(o.length-1); + if(fmt.match(/E\-/) && o.match(/e\+/)) o = o.replace(/e\+/,"e"); + return o.replace("e","E"); } var frac1 = /# (\?+)( ?)\/( ?)(\d+)/; -function write_num_f1(r/*:Array*/, aval/*:number*/, sign/*:string*/)/*:string*/ { - var den = parseInt(r[4],10), rr = Math.round(aval * den), base = Math.floor(rr/den); - var myn = (rr - base*den), myd = den; - return sign + (base === 0 ? "" : ""+base) + " " + (myn === 0 ? fill(" ", r[1].length + 1 + r[4].length) : pad_(myn,r[1].length) + r[2] + "/" + r[3] + pad0(myd,r[4].length)); +function write_num_f1(r, aval, sign) { + var den = parseInt(r[4],10), rr = Math.round(aval * den), base = Math.floor(rr/den); + var myn = (rr - base*den), myd = den; + return sign + (base === 0 ? "" : ""+base) + " " + (myn === 0 ? fill(" ", r[1].length + 1 + r[4].length) : pad_(myn,r[1].length) + r[2] + "/" + r[3] + pad0(myd,r[4].length)); } -function write_num_f2(r/*:Array*/, aval/*:number*/, sign/*:string*/)/*:string*/ { - return sign + (aval === 0 ? "" : ""+aval) + fill(" ", r[1].length + 2 + r[4].length); +function write_num_f2(r, aval, sign) { + return sign + (aval === 0 ? "" : ""+aval) + fill(" ", r[1].length + 2 + r[4].length); } var dec1 = /^#*0*\.(0+)/; var closeparen = /\).*[0#]/; var phone = /\(###\) ###\\?-####/; -function hashq(str/*:string*/)/*:string*/ { - var o = "", cc; - for(var i = 0; i != str.length; ++i) switch((cc=str.charCodeAt(i))) { - case 35: break; - case 63: o+= " "; break; - case 48: o+= "0"; break; - default: o+= String.fromCharCode(cc); - } - return o; +function hashq(str) { + var o = "", cc; + for(var i = 0; i != str.length; ++i) switch((cc=str.charCodeAt(i))) { + case 35: break; + case 63: o+= " "; break; + case 48: o+= "0"; break; + default: o+= String.fromCharCode(cc); + } + return o; } -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)); +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)); } -function carry(val/*:number*/, d/*:number*/)/*:number*/ { - if (d < ('' + Math.round((val-Math.floor(val))*Math.pow(10,d))).length) { - return 1; - } - return 0; +function carry(val, d) { + 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[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 -2147483648) return ""+(val >= 0 ? (val|0) : (val-1|0)); return ""+Math.floor(val); } +function write_num_flt(type, fmt, val) { + 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[1]==' '?2:1),val); + var o; + var r, 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[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))) { - // $FlowIgnore - o = (""+val).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 + (""+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_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[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))) { +// $FlowIgnore + o = (""+val).replace(/^([^\.]+)$/,"$1."+r[1]).replace(/\.$/,"."+r[1]); + // $FlowIgnore + o = o.replace(/\.(\d*)$/,function($$, $1) { +// $FlowIgnore + return "." + $1 + fill("0", r[1].length-$1.length); }); + // $FlowIgnore + 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*/ { - var out/*:Array*/ = []; - var in_str = false, cc; - for(var i = 0, j = 0; i < fmt.length; ++i) switch((cc=fmt.charCodeAt(i))) { - case 34: /* '"' */ - in_str = !in_str; break; - case 95: case 42: case 92: /* '_' '*' '\\' */ - ++i; break; - case 59: /* ';' */ - out[out.length] = fmt.substr(j,i-j); - j = i+1; - } - out[out.length] = fmt.substr(j); - if(in_str === true) throw new Error("Format |" + fmt + "| unterminated string "); - return out; +function split_fmt(fmt) { + var out = []; + var in_str = false, cc; + for(var i = 0, j = 0; i < fmt.length; ++i) switch((cc=fmt.charCodeAt(i))) { + case 34: /* '"' */ + in_str = !in_str; break; + case 95: case 42: case 92: /* '_' '*' '\\' */ + ++i; break; + case 59: /* ';' */ + out[out.length] = fmt.substr(j,i-j); + j = i+1; + } + out[out.length] = fmt.substr(j); + if(in_str === true) throw new Error("Format |" + fmt + "| unterminated string "); + return out; } SSF._split = split_fmt; var abstime = /\[[HhMmSs]*\]/; -function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) { - var out = [], o = "", i = 0, c = "", lst='t', q, dt, j, cc; - var hr='H'; - /* Tokenize */ - while(i < fmt.length) { - switch((c = fmt.charAt(i))) { - case 'G': /* General */ - if(!isgeneral(fmt, i)) throw new Error('unrecognized character ' + c + ' in ' +fmt); - out[out.length] = {t:'G', v:'General'}; i+=7; break; - case '"': /* Literal text */ - for(o="";(cc=fmt.charCodeAt(++i)) !== 34 && i < fmt.length;) o += String.fromCharCode(cc); - out[out.length] = {t:'t', v:o}; ++i; break; - case '\\': var w = fmt[++i], t = (w === "(" || w === ")") ? w : 't'; - out[out.length] = {t:t, v:w}; ++i; break; - case '_': out[out.length] = {t:'t', v:" "}; i+=2; break; - case '@': /* Text Placeholder */ - out[out.length] = {t:'T', v:v}; ++i; break; - case 'B': case 'b': - if(fmt[i+1] === "1" || fmt[i+1] === "2") { - if(dt==null) { dt=parse_date_code(v, opts, fmt[i+1] === "2"); if(dt==null) return ""; } - out[out.length] = {t:'X', v:fmt.substr(i,2)}; lst = c; i+=2; break; - } - /* falls through */ - case 'M': case 'D': case 'Y': case 'H': case 'S': case 'E': - c = c.toLowerCase(); - /* falls through */ - case 'm': case 'd': case 'y': case 'h': case 's': case 'e': case 'g': - if(v < 0) return ""; - if(dt==null) { dt=parse_date_code(v, opts); if(dt==null) return ""; } - o = c; while(++i= 12 ? "P" : "A"; q.t = 'T'; hr='h';i+=3;} - else if(fmt.substr(i,5) === "AM/PM") { if(dt!=null) q.v = dt.H >= 12 ? "PM" : "AM"; q.t = 'T'; i+=5; hr='h'; } - else { q.t = "t"; ++i; } - if(dt==null && q.t === 'T') return ""; - out[out.length] = q; lst = c; break; - case '[': - o = c; - while(fmt[i++] !== ']' && i < fmt.length) o += fmt[i]; - if(o.substr(-1) !== ']') throw 'unterminated "[" block: |' + o + '|'; - 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=""; } - break; - /* Numbers */ - case '.': - if(dt != null) { - o = c; while((c=fmt[++i]) === "0") o += c; - out[out.length] = {t:'s', v:o}; break; - } - /* falls through */ - case '0': case '#': - o = c; while("0#?.,E+-%".indexOf(c=fmt[++i]) > -1 || c=='\\' && fmt[i+1] == "-" && "0#".indexOf(fmt[i+2])>-1) o += c; - out[out.length] = {t:'n', v:o}; break; - case '?': - o = c; while(fmt[++i] === c) o+=c; - q={t:c, v:o}; out[out.length] = q; lst = c; break; - case '*': ++i; if(fmt[i] == ' ' || fmt[i] == '*') ++i; break; // ** - case '(': case ')': out[out.length] = {t:(flen===1?'t':c), v:c}; ++i; break; - case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': - o = c; while("0123456789".indexOf(fmt[++i]) > -1) o+=fmt[i]; - out[out.length] = {t:'D', v:o}; break; - case ' ': out[out.length] = {t:c, v:c}; ++i; break; - default: - if(",$-+/():!^&'~{}<>=€acfijklopqrtuvwxz".indexOf(c) === -1) throw new Error('unrecognized character ' + c + ' in ' + fmt); - out[out.length] = {t:'t', v:c}; ++i; break; - } - } - var bt = 0, ss0 = 0, ssm; - for(i=out.length-1, lst='t'; i >= 0; --i) { - switch(out[i].t) { - case 'h': case 'H': out[i].t = hr; lst='h'; if(bt < 1) bt = 1; break; - case 's': - if((ssm=out[i].v.match(/\.0+$/))) ss0=Math.max(ss0,ssm[0].length-1); - if(bt < 3) bt = 3; - /* 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"); - break; - case 'Z': - if(bt < 1 && out[i].v.match(/[Hh]/)) bt = 1; - if(bt < 2 && out[i].v.match(/[Mm]/)) bt = 2; - if(bt < 3 && out[i].v.match(/[Ss]/)) bt = 3; - } - } - switch(bt) { - case 0: break; - case 1: - /*::if(!dt) break;*/ - if(dt.u >= 0.5) { dt.u = 0; ++dt.S; } - if(dt.S >= 60) { dt.S = 0; ++dt.M; } - if(dt.M >= 60) { dt.M = 0; ++dt.H; } - break; - case 2: - /*::if(!dt) break;*/ - if(dt.u >= 0.5) { dt.u = 0; ++dt.S; } - if(dt.S >= 60) { dt.S = 0; ++dt.M; } - break; - } - /* replace fields */ - var nstr = "", jj; - for(i=0; i < out.length; ++i) { - switch(out[i].t) { - case 't': case 'T': case ' ': case 'D': break; - case 'X': out[i].v = ""; out[i].t = ";"; break; - case 'd': case 'm': case 'y': case 'h': case 'H': case 'M': case 's': case 'e': case 'b': case 'Z': - /*::if(!dt) throw "unreachable"; */ - out[i].v = write_date(out[i].t.charCodeAt(0), out[i].v, dt, ss0); - out[i].t = 't'; break; - case 'n': case '(': case '?': - jj = i+1; - while(out[jj] != null && ( - (c=out[jj].t) === "?" || c === "D" || - (c === " " || c === "t") && out[jj+1] != null && (out[jj+1].t === '?' || out[jj+1].t === "t" && out[jj+1].v === '/') || - out[i].t === '(' && (c === ' ' || c === 'n' || c === ')') || - c === 't' && (out[jj].v === '/' || '$€'.indexOf(out[jj].v) > -1 || out[jj].v === ' ' && out[jj+1] != null && out[jj+1].t == '?') - )) { - out[i].v += out[jj].v; - out[jj] = {v:"", t:";"}; ++jj; - } - nstr += out[i].v; - i = jj-1; break; - case 'G': out[i].t = 't'; out[i].v = general_fmt(v,opts); break; - } - } - 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); /* '(' */ - 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; } - var lasti=out.length; - if(decpt === out.length && ostr.indexOf("E") === -1) { - for(i=out.length-1; i>= 0;--i) { - if(out[i] == null || 'n?('.indexOf(out[i].t) === -1) continue; - if(jj>=out[i].v.length-1) { jj -= out[i].v.length; out[i].v = ostr.substr(jj+1, out[i].v.length); } - else if(jj < 0) out[i].v = ""; - else { out[i].v = ostr.substr(0, jj+1); jj = -1; } - out[i].t = 't'; - lasti = i; - } - if(jj>=0 && lasti= 0; --i) { - if(out[i] == null || 'n?('.indexOf(out[i].t) === -1) continue; - j=out[i].v.indexOf(".")>-1&&i===decpt?out[i].v.indexOf(".")-1:out[i].v.length-1; - vv = out[i].v.substr(j+1); - for(; j>=0; --j) { - if(jj>=0 && (out[i].v[j] === "0" || out[i].v[j] === "#")) vv = ostr[jj--] + vv; - } - out[i].v = vv; - out[i].t = 't'; - lasti = i; - } - if(jj>=0 && lasti-1&&i===decpt?out[i].v.indexOf(".")+1:0; - vv = out[i].v.substr(0,j); - for(; j-1) { - myv = (flen >1 && v < 0 && i>0 && out[i-1].v === "-" ? -v:v); - out[i].v = write_num(out[i].t, out[i].v, myv); - out[i].t = 't'; - } - var retval = ""; - for(i=0; i !== out.length; ++i) if(out[i] != null) retval += out[i].v; - return retval; +function fmt_is_date(fmt) { + var i = 0, cc = 0, c = "", o = ""; + while(i < fmt.length) { + switch((c = fmt.charAt(i))) { + case 'G': if(isgeneral(fmt, i)) i+= 6; i++; break; + case '"': for(;(cc=fmt.charCodeAt(++i)) !== 34 && i < fmt.length;) ++i; ++i; break; + case '\\': i+=2; break; + case '_': i+=2; break; + case '@': ++i; break; + case 'B': case 'b': + if(fmt.charAt(i+1) === "1" || fmt.charAt(i+1) === "2") return true; + /* falls through */ + case 'M': case 'D': case 'Y': case 'H': case 'S': case 'E': + /* falls through */ + case 'm': case 'd': case 'y': case 'h': case 's': case 'e': case 'g': return true; + case 'A': + if(fmt.substr(i, 3) === "A/P") return true; + if(fmt.substr(i, 5) === "AM/PM") return true; + ++i; break; + case '[': + o = c; + while(fmt.charAt(i++) !== ']' && i < fmt.length) o += fmt.charAt(i); + if(o.match(abstime)) return true; + break; + 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)); + 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; + case ' ': ++i; break; + default: ++i; break; + } + } + return false; +} +SSF.is_date = fmt_is_date; +function eval_fmt(fmt, v, opts, flen) { + var out = [], o = "", i = 0, c = "", lst='t', q, dt, j, cc; + var hr='H'; + /* Tokenize */ + while(i < fmt.length) { + switch((c = fmt.charAt(i))) { + case 'G': /* General */ + if(!isgeneral(fmt, i)) throw new Error('unrecognized character ' + c + ' in ' +fmt); + out[out.length] = {t:'G', v:'General'}; i+=7; break; + case '"': /* Literal text */ + for(o="";(cc=fmt.charCodeAt(++i)) !== 34 && i < fmt.length;) o += String.fromCharCode(cc); + out[out.length] = {t:'t', v:o}; ++i; break; + case '\\': var w = fmt.charAt(++i), t = (w === "(" || w === ")") ? w : 't'; + out[out.length] = {t:t, v:w}; ++i; break; + case '_': out[out.length] = {t:'t', v:" "}; i+=2; break; + case '@': /* Text Placeholder */ + out[out.length] = {t:'T', v:v}; ++i; break; + case 'B': case 'b': + if(fmt.charAt(i+1) === "1" || fmt.charAt(i+1) === "2") { + if(dt==null) { dt=parse_date_code(v, opts, fmt.charAt(i+1) === "2"); if(dt==null) return ""; } + out[out.length] = {t:'X', v:fmt.substr(i,2)}; lst = c; i+=2; break; + } + /* falls through */ + case 'M': case 'D': case 'Y': case 'H': case 'S': case 'E': + c = c.toLowerCase(); + /* falls through */ + case 'm': case 'd': case 'y': case 'h': case 's': case 'e': case 'g': + if(v < 0) return ""; + if(dt==null) { dt=parse_date_code(v, opts); if(dt==null) return ""; } + o = c; while(++i= 12 ? "P" : "A"; q.t = 'T'; hr='h';i+=3;} + else if(fmt.substr(i,5) === "AM/PM") { if(dt!=null) q.v = dt.H >= 12 ? "PM" : "AM"; q.t = 'T'; i+=5; hr='h'; } + else { q.t = "t"; ++i; } + if(dt==null && q.t === 'T') return ""; + out[out.length] = q; lst = c; break; + case '[': + o = c; + while(fmt.charAt(i++) !== ']' && i < fmt.length) o += fmt.charAt(i); + if(o.slice(-1) !== ']') throw 'unterminated "[" block: |' + o + '|'; + 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=""; } + break; + /* Numbers */ + case '.': + if(dt != null) { + o = c; while((c=fmt.charAt(++i)) === "0") o += c; + out[out.length] = {t:'s', v:o}; break; + } + /* falls through */ + case '0': case '#': + o = c; while(++i < fmt.length && "0#?.,E+-%".indexOf(c=fmt.charAt(i)) > -1 || c=='\\' && fmt.charAt(i+1) == "-" && "0#".indexOf(fmt.charAt(i+2))>-1) o += c; + out[out.length] = {t:'n', v:o}; break; + case '?': + o = c; while(fmt.charAt(++i) === c) o+=c; + q={t:c, v:o}; out[out.length] = q; lst = c; break; + case '*': ++i; if(fmt.charAt(i) == ' ' || fmt.charAt(i) == '*') ++i; break; // ** + case '(': case ')': out[out.length] = {t:(flen===1?'t':c), v:c}; ++i; break; + case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': + o = c; while(i < fmt.length && "0123456789".indexOf(fmt.charAt(++i)) > -1) o+=fmt.charAt(i); + out[out.length] = {t:'D', v:o}; break; + case ' ': out[out.length] = {t:c, v:c}; ++i; break; + default: + if(",$-+/():!^&'~{}<>=€acfijklopqrtuvwxz".indexOf(c) === -1) throw new Error('unrecognized character ' + c + ' in ' + fmt); + out[out.length] = {t:'t', v:c}; ++i; break; + } + } + var bt = 0, ss0 = 0, ssm; + for(i=out.length-1, lst='t'; i >= 0; --i) { + switch(out[i].t) { + case 'h': case 'H': out[i].t = hr; lst='h'; if(bt < 1) bt = 1; break; + case 's': + if((ssm=out[i].v.match(/\.0+$/))) ss0=Math.max(ss0,ssm[0].length-1); + if(bt < 3) bt = 3; + /* 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"); + break; + case 'Z': + if(bt < 1 && out[i].v.match(/[Hh]/)) bt = 1; + if(bt < 2 && out[i].v.match(/[Mm]/)) bt = 2; + if(bt < 3 && out[i].v.match(/[Ss]/)) bt = 3; + } + } + switch(bt) { + case 0: break; + case 1: +if(dt.u >= 0.5) { dt.u = 0; ++dt.S; } + if(dt.S >= 60) { dt.S = 0; ++dt.M; } + if(dt.M >= 60) { dt.M = 0; ++dt.H; } + break; + case 2: +if(dt.u >= 0.5) { dt.u = 0; ++dt.S; } + if(dt.S >= 60) { dt.S = 0; ++dt.M; } + break; + } + /* replace fields */ + var nstr = "", jj; + for(i=0; i < out.length; ++i) { + switch(out[i].t) { + case 't': case 'T': case ' ': case 'D': break; + case 'X': out[i].v = ""; out[i].t = ";"; break; + case 'd': case 'm': case 'y': case 'h': case 'H': case 'M': case 's': case 'e': case 'b': case 'Z': +out[i].v = write_date(out[i].t.charCodeAt(0), out[i].v, dt, ss0); + out[i].t = 't'; break; + case 'n': case '(': case '?': + jj = i+1; + while(out[jj] != null && ( + (c=out[jj].t) === "?" || c === "D" || + (c === " " || c === "t") && out[jj+1] != null && (out[jj+1].t === '?' || out[jj+1].t === "t" && out[jj+1].v === '/') || + out[i].t === '(' && (c === ' ' || c === 'n' || c === ')') || + c === 't' && (out[jj].v === '/' || '$€'.indexOf(out[jj].v) > -1 || out[jj].v === ' ' && out[jj+1] != null && out[jj+1].t == '?') + )) { + out[i].v += out[jj].v; + out[jj] = {v:"", t:";"}; ++jj; + } + nstr += out[i].v; + i = jj-1; break; + case 'G': out[i].t = 't'; out[i].v = general_fmt(v,opts); break; + } + } + 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); /* '(' */ + 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; } + var lasti=out.length; + if(decpt === out.length && ostr.indexOf("E") === -1) { + for(i=out.length-1; i>= 0;--i) { + if(out[i] == null || 'n?('.indexOf(out[i].t) === -1) continue; + if(jj>=out[i].v.length-1) { jj -= out[i].v.length; out[i].v = ostr.substr(jj+1, out[i].v.length); } + else if(jj < 0) out[i].v = ""; + else { out[i].v = ostr.substr(0, jj+1); jj = -1; } + out[i].t = 't'; + lasti = i; + } + if(jj>=0 && lasti= 0; --i) { + if(out[i] == null || 'n?('.indexOf(out[i].t) === -1) continue; + j=out[i].v.indexOf(".")>-1&&i===decpt?out[i].v.indexOf(".")-1:out[i].v.length-1; + vv = out[i].v.substr(j+1); + for(; j>=0; --j) { + if(jj>=0 && (out[i].v[j] === "0" || out[i].v[j] === "#")) vv = ostr[jj--] + vv; + } + out[i].v = vv; + out[i].t = 't'; + lasti = i; + } + if(jj>=0 && lasti-1&&i===decpt?out[i].v.indexOf(".")+1:0; + vv = out[i].v.substr(0,j); + for(; j-1) { + myv = (flen >1 && v < 0 && i>0 && out[i-1].v === "-" ? -v:v); + out[i].v = write_num(out[i].t, out[i].v, myv); + out[i].t = 't'; + } + var retval = ""; + for(i=0; i !== out.length; ++i) if(out[i] != null) retval += out[i].v; + return retval; } SSF._eval = eval_fmt; var cfregex = /\[[=<>]/; var cfregex2 = /\[([=<>]*)(-?\d+\.?\d*)\]/; function chkcond(v, rr) { - if(rr == null) return false; - var thresh = parseFloat(rr[2]); - switch(rr[1]) { - case "=": if(v == thresh) return true; break; - case ">": if(v > thresh) return true; break; - case "<": if(v < thresh) return true; break; - case "<>": if(v != thresh) return true; break; - case ">=": if(v >= thresh) return true; break; - case "<=": if(v <= thresh) return true; break; - } - return false; + if(rr == null) return false; + var thresh = parseFloat(rr[2]); + switch(rr[1]) { + case "=": if(v == thresh) return true; break; + case ">": if(v > thresh) return true; break; + case "<": if(v < thresh) return true; break; + case "<>": if(v != thresh) return true; break; + case ">=": if(v >= thresh) return true; break; + case "<=": if(v <= thresh) return true; break; + } + return false; } -function choose_fmt(f/*:string*/, v) { - var fmt = split_fmt(f); - var l = fmt.length, lat = fmt[l-1].indexOf("@"); - if(l<4 && lat>-1) --l; - if(fmt.length > 4) throw new Error("cannot find right format for |" + fmt.join("|") + "|"); - if(typeof v !== "number") return [4, fmt.length === 4 || lat>-1?fmt[fmt.length-1]:"@"]; - switch(fmt.length) { - case 1: fmt = lat>-1 ? ["General", "General", "General", fmt[0]] : [fmt[0], fmt[0], fmt[0], "@"]; break; - case 2: fmt = lat>-1 ? [fmt[0], fmt[0], fmt[0], fmt[1]] : [fmt[0], fmt[1], fmt[0], "@"]; break; - case 3: fmt = lat>-1 ? [fmt[0], fmt[1], fmt[0], fmt[2]] : [fmt[0], fmt[1], fmt[2], "@"]; break; - case 4: break; - } - var ff = v > 0 ? fmt[0] : v < 0 ? fmt[1] : fmt[2]; - if(fmt[0].indexOf("[") === -1 && fmt[1].indexOf("[") === -1) return [l, ff]; - if(fmt[0].match(cfregex) != null || fmt[1].match(cfregex) != null) { - var m1 = fmt[0].match(cfregex2); - var m2 = fmt[1].match(cfregex2); - return chkcond(v, m1) ? [l, fmt[0]] : chkcond(v, m2) ? [l, fmt[1]] : [l, fmt[m1 != null && m2 != null ? 2 : 1]]; - } - return [l, ff]; +function choose_fmt(f, v) { + var fmt = split_fmt(f); + var l = fmt.length, lat = fmt[l-1].indexOf("@"); + if(l<4 && lat>-1) --l; + if(fmt.length > 4) throw new Error("cannot find right format for |" + fmt.join("|") + "|"); + if(typeof v !== "number") return [4, fmt.length === 4 || lat>-1?fmt[fmt.length-1]:"@"]; + switch(fmt.length) { + case 1: fmt = lat>-1 ? ["General", "General", "General", fmt[0]] : [fmt[0], fmt[0], fmt[0], "@"]; break; + case 2: fmt = lat>-1 ? [fmt[0], fmt[0], fmt[0], fmt[1]] : [fmt[0], fmt[1], fmt[0], "@"]; break; + case 3: fmt = lat>-1 ? [fmt[0], fmt[1], fmt[0], fmt[2]] : [fmt[0], fmt[1], fmt[2], "@"]; break; + case 4: break; + } + var ff = v > 0 ? fmt[0] : v < 0 ? fmt[1] : fmt[2]; + if(fmt[0].indexOf("[") === -1 && fmt[1].indexOf("[") === -1) return [l, ff]; + if(fmt[0].match(cfregex) != null || fmt[1].match(cfregex) != null) { + var m1 = fmt[0].match(cfregex2); + var m2 = fmt[1].match(cfregex2); + return chkcond(v, m1) ? [l, fmt[0]] : chkcond(v, m2) ? [l, fmt[1]] : [l, fmt[m1 != null && m2 != null ? 2 : 1]]; + } + return [l, ff]; } -function format(fmt/*:string|number*/,v/*:any*/,o/*:?any*/) { - 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; - } - if(isgeneral(sfmt,0)) return general_fmt(v, o); - var f = choose_fmt(sfmt, v); - if(isgeneral(f[1])) return general_fmt(v, o); - if(v === true) v = "TRUE"; else if(v === false) v = "FALSE"; - else if(v === "" || v == null) return ""; - return eval_fmt(f[1], v, o, f[0]); +function format(fmt,v,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; + } + if(isgeneral(sfmt,0)) return general_fmt(v, o); + var f = choose_fmt(sfmt, v); + if(isgeneral(f[1])) return general_fmt(v, o); + if(v === true) v = "TRUE"; else if(v === false) v = "FALSE"; + else if(v === "" || v == null) return ""; + return eval_fmt(f[1], v, o, f[0]); } SSF._table = table_fmt; -SSF.load = function load_entry(fmt/*:string*/, idx/*:number*/) { table_fmt[idx] = fmt; }; +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/*:{[n:number]:string}*/) { for(var i=0; i!=0x0188; ++i) if(tbl[i] !== undefined) SSF.load(tbl[i], i); }; +SSF.load_table = function load_table(tbl) { for(var i=0; i!=0x0188; ++i) if(tbl[i] !== undefined) SSF.load(tbl[i], i); }; }; make_ssf(SSF); /*global module */ -/*:: declare var DO_NOT_EXPORT_SSF: any; */ if(typeof module !== 'undefined' && typeof DO_NOT_EXPORT_SSF === 'undefined') module.exports = SSF; diff --git a/ssf.md b/ssf.md index 3695a90..38456f7 100644 --- a/ssf.md +++ b/ssf.md @@ -8,7 +8,7 @@ spreadsheet format codes. The various API functions take an `opts` argument which control parsing. The default options are described below: -```js>tmp/10_opts.js +```js>bits/10_opts.js /* Options */ var opts_fmt/*:Array >*/ = [ ``` @@ -59,7 +59,7 @@ numbers, zero values, and text, in that order. Semicolons can be escaped with the `\` character, so we need to split on those semicolons that aren't prefaced by a slash or within a quoted string: -```js>tmp/80_split.js +```js>bits/80_split.js function split_fmt(fmt/*:string*/)/*:Array*/ { var out/*:Array*/ = []; var in_str = false, cc; @@ -116,7 +116,7 @@ as the last format. ## Utility Functions -```js>tmp/02_utilities.js +```js>bits/02_utilities.js 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; } ``` @@ -148,7 +148,7 @@ of its abilities given the knowledge. First: 32-bit integers in base 10 are shorter than 11 characters, so they will always be written in full: -```js>tmp/40_general.js +```js>bits/40_general.js function general_fmt_int(v/*:number*/, opts/*:?any*/)/*:string*/ { return ""+v; } SSF._general_int = general_fmt_int; ``` @@ -191,7 +191,7 @@ SSF._general_num = general_fmt_num; Finally -```js>tmp/40_general.js +```js>bits/40_general.js function general_fmt(v/*:any*/, opts/*:?any*/) { switch(typeof v) { ``` @@ -228,7 +228,7 @@ SSF._general = general_fmt; These are the commonly-used formats that have a special implied code. None of the international formats are included here. -```js>tmp/20_consts.js +```js>bits/20_consts.js var table_fmt = { /*::[*/0/*::]*/: 'General', /*::[*/1/*::]*/: '0', @@ -285,7 +285,7 @@ some writers erroneously emit 65535 for general: The code `ddd` displays short day-of-week and `dddd` shows long day-of-week: -```js>tmp/20_consts.js +```js>bits/20_consts.js var days = [ ['Sun', 'Sunday'], ['Mon', 'Monday'], @@ -325,7 +325,7 @@ portion of a 24 hour day). Excel supports the alternative Hijri calendar (indicated with `b2`): -```js>tmp/50_date.js +```js>bits/50_date.js function parse_date_code(v/*:number*/,opts/*:?any*/,b2/*:?boolean*/) { ``` @@ -417,7 +417,7 @@ SSF.parse_date_code = parse_date_code; TODO: suitable hijri correction -```js>tmp/45_hijri.js +```js>bits/45_hijri.js function fix_hijri(date, o) { return 0; } ``` @@ -425,7 +425,7 @@ function fix_hijri(date, o) { return 0; } The utility `commaify` adds commas to integers: -```js>tmp/56_commaify.js +```js>bits/56_commaify.js function commaify(s/*:string*/)/*:string*/ { if(s.length <= 3) return s; var j = (s.length % 3), o = s.substr(0,j); @@ -436,7 +436,7 @@ function commaify(s/*:string*/)/*:string*/ { `write_num` is broken into sub-functions to help with optimization: -```js>tmp/57_numhead.js +```js>bits/57_numhead.js var write_num = (function make_write_num(){ ``` @@ -444,7 +444,7 @@ var write_num = (function make_write_num(){ The underlying number for the percentages should be physically shifted: -```js>tmp/59_numhelp.js +```js>bits/59_numhelp.js var pct1 = /%/g; function write_num_pct(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string*/{ var sfmt = fmt.replace(pct1,""), mul = fmt.length - sfmt.length; @@ -457,7 +457,7 @@ function write_num_pct(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string Formats with multiple commas after the decimal point should be shifted by the appropiate multiple of 1000 (more magic): -```js>tmp/60_number.js +```js>bits/60_number.js function write_num_cm(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string*/{ var idx = fmt.length - 1; while(fmt.charCodeAt(idx-1) === 44) --idx; @@ -562,7 +562,7 @@ function write_num_flt(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string For parentheses, explicitly resolve the sign issue: -```js>tmp/60_number.js +```js>bits/60_number.js if(type.charCodeAt(0) === 40 && !fmt.match(closeparen)) { var ffmt = fmt.replace(/\( */,"").replace(/ \)/,"").replace(/\)/,""); if(val >= 0) return write_num_flt('n', ffmt, val); @@ -575,7 +575,7 @@ Helpers are used for: - Trailing commas - Exponentials -```js>tmp/60_number.js +```js>bits/60_number.js 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); @@ -691,7 +691,7 @@ The general class `/^[#0?]+$/` treats the '0' as literal, '#' as noop, '?' as sp The default cases are hard-coded. TODO: actually parse them -```js>tmp/60_number.js +```js>bits/60_number.js if((r = fmt.match(/^00,000\.([#0]*0)$/))) { ri = dec(val, r[1].length); ``` @@ -707,7 +707,7 @@ Note that this is technically incorrect For now, the default case is an error: -```js>tmp/60_number.js +```js>bits/60_number.js default: } throw new Error("unsupported format |" + fmt + "|"); @@ -839,7 +839,7 @@ return function write_num(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:str ## Evaluating Format Strings -```js>tmp/82_eval.js +```js>bits/82_eval.js var abstime = /\[[HhMmSs]*\]/; function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) { var out = [], o = "", i = 0, c = "", lst='t', q, dt, j, cc; @@ -1197,7 +1197,7 @@ There is some overloading of the `m` character. According to the spec: hours) or immediately before the "ss" code (for seconds), the application shall display minutes instead of the month. -```js>tmp/50_date.js +```js>bits/50_date.js /*jshint -W086 */ function write_date(type/*:number*/, fmt/*:string*/, val, ss0/*:?number*/)/*:string*/ { var o="", ss=0, tt=0, y = val.y, out, outl = 0; @@ -1322,7 +1322,7 @@ it is not exported and is only called when the type is in `ymdhHMsZe` Based on the value, `choose_fmt` picks the right format string. If formats have explicit negative specifications, those values should be passed as positive: -```js>tmp/90_main.js +```js>bits/90_main.js function choose_fmt(f/*:string*/, v) { var fmt = split_fmt(f); var l = fmt.length, lat = fmt[l-1].indexOf("@"); @@ -1357,7 +1357,7 @@ it is treated as the text format Here we have to scan for conditions. Note that the grammar precludes decimals but in practice they are fair game: -```js>tmp/88_cond.js +```js>bits/88_cond.js var cfregex = /\[[=<>]/; var cfregex2 = /\[([=<>]*)(-?\d+\.?\d*)\]/; function chkcond(v, rr) { @@ -1377,7 +1377,7 @@ function chkcond(v, rr) { The main function checks for conditional operators and acts accordingly: -```js>tmp/90_main.js +```js>bits/90_main.js var ff = v > 0 ? fmt[0] : v < 0 ? fmt[1] : fmt[2]; if(fmt[0].indexOf("[") === -1 && fmt[1].indexOf("[") === -1) return [l, ff]; if(fmt[0].match(cfregex) != null || fmt[1].match(cfregex) != null) { @@ -1431,7 +1431,7 @@ Empty string should always emit empty, even if there are other characters: The methods beginning with an underscore are subject to change and should not be used directly in programs. -```js>tmp/98_exports.js +```js>bits/98_exports.js SSF._table = table_fmt; SSF.load = function load_entry(fmt/*:string*/, idx/*:number*/) { table_fmt[idx] = fmt; }; SSF.format = format; @@ -1448,7 +1448,7 @@ SSF.load_table = function load_table(tbl/*:{[n:number]:string}*/) { for(var i=0; The implementation is from [our frac library](https://github.com/SheetJS/frac/): -```js>tmp/30_frac.js +```js>bits/30_frac.js function frac(x, D, mixed) { var sgn = x < 0 ? -1 : 1; var B = x * sgn; @@ -1475,14 +1475,14 @@ function frac(x, D, mixed) { ## JS Boilerplate -```js>tmp/00_header.js +```js>bits/00_header.js /* ssf.js (C) 2013-present SheetJS -- http://sheetjs.com */ /*jshint -W041 */ var SSF = {}; var make_ssf = function make_ssf(SSF){ ``` -```js>tmp/99_footer.js +```js>bits/99_footer.js }; make_ssf(SSF); /*global module */ diff --git a/test/is_date.json b/test/is_date.json new file mode 100644 index 0000000..d15c9e4 --- /dev/null +++ b/test/is_date.json @@ -0,0 +1,447 @@ +[ + ["General", false], + ["yyyy", true], + ["\" Excellent\"", false], + ["\" Fair\"", false], + ["\" Good\"", false], + ["\" Poor\"", false], + ["\" Very Good\"", false], + ["\"$\"#,##0", false], + ["\"$\"#,##0.00", false], + ["\"$\"#,##0.00_);[Red]\\(\"$\"#,##0.00\\)", false], + ["\"$\"#,##0.00_);\\(\"$\"#,##0.00\\)", false], + ["\"$\"#,##0;[Red]\\-\"$\"#,##0", false], + ["\"$\"#,##0_);[Red]\\(\"$\"#,##0\\)", false], + ["\"$\"#,##0_);\\(\"$\"#,##0\\)", false], + ["\"Haha!\"\\ @\\ \"Yeah!\"", false], + ["\"TRUE\";\"TRUE\";\"FALSE\"", false], + ["\"True\";\"True\";\"False\";@", false], + ["\"Years: \"0", false], + ["\"Yes\";\"Yes\";\"No\";@", false], + ["\"kl \"hh:mm:ss;@", false], + ["\"£\"#,##0.00", false], + ["\"£\"#,##0;[Red]\\-\"£\"#,##0", false], + ["\"€\"#,##0.00", false], + ["\"€\"\\ #,##0.00_-", false], + ["\"上午/下午 \"hh\"時\"mm\"分\"ss\"秒 \"", true], + ["\"¥\"#,##0.00;\"¥\"\\-#,##0.00", false], + ["#", false], + ["# ?/?", false], + ["# ??/??", false], + ["#\" \"?/?", false], + ["#\" \"??/??", false], + ["#\"abded\"\\ ??/??", false], + ["###0.00;-###0.00", false], + ["###0;-###0", false], + ["##0.0E+0", false], + ["#,##0", false], + ["#,##0 ;(#,##0)", false], + ["#,##0 ;[Red](#,##0)", false], + ["#,##0\"р.\";[Red]\\-#,##0\"р.\"", false], + ["#,##0.0", false], + ["#,##0.00", false], + ["#,##0.00 \"�\"", false], + ["#,##0.00 €;-#,##0.00 €", false], + ["#,##0.00\"р.\";[Red]\\-#,##0.00\"р.\"", false], + ["#,##0.000", false], + ["#,##0.0000", false], + ["#,##0.00000", false], + ["#,##0.000000", false], + ["#,##0.0000000", false], + ["#,##0.00000000", false], + ["#,##0.000000000", false], + ["#,##0.00000000;[Red]#,##0.00000000", false], + ["#,##0.0000_ ", false], + ["#,##0.000_ ", false], + ["#,##0.000_);\\(#,##0.000\\)", false], + ["#,##0.00;(#,##0.00)", false], + ["#,##0.00;(#,##0.00);0.00", false], + ["#,##0.00;[Red](#,##0.00)", false], + ["#,##0.00;[Red]\\(#,##0.00\\)", false], + ["#,##0.00;\\(#,##0.00\\)", false], + ["#,##0.00[$₹-449]_);\\(#,##0.00[$₹-449]\\)", false], + ["#,##0.00\\ \"р.\"", false], + ["#,##0.00\\ \"р.\";[Red]\\-#,##0.00\\ \"р.\"", false], + ["#,##0.00\\ [$€-407]", false], + ["#,##0.00\\ [$€-40C]", false], + ["#,##0.00_);\\(#,##0.00\\)", false], + ["#,##0.00_р_.;[Red]\\-#,##0.00_р_.", false], + ["#,##0.00_р_.;\\-#,##0.00_р_.", false], + ["#,##0.0;[Red]#,##0.0", false], + ["#,##0.0_ ;\\-#,##0.0\\ ", false], + ["#,##0.0_);[Red]\\(#,##0.0\\)", false], + ["#,##0.0_);\\(#,##0.0\\)", false], + ["#,##0;\\-#,##0;0", false], + ["#,##0\\ \"р.\";[Red]\\-#,##0\\ \"р.\"", false], + ["#,##0\\ \"р.\";\\-#,##0\\ \"р.\"", false], + ["#,##0\\ ;[Red]\\(#,##0\\)", false], + ["#,##0\\ ;\\(#,##0\\)", false], + ["#,##0_ ", false], + ["#,##0_ ;[Red]\\-#,##0\\ ", false], + ["#,##0_);[Red]\\(#,##0\\)", false], + ["#,##0_р_.;[Red]\\-#,##0_р_.", false], + ["#,##0_р_.;\\-#,##0_р_.", false], + ["#.0000,,", false], + ["#0", false], + ["#0.00", false], + ["#0.0000", false], + ["#\\ ?/10", false], + ["#\\ ?/2", false], + ["#\\ ?/4", false], + ["#\\ ?/8", false], + ["#\\ ?/?", false], + ["#\\ ??/100", false], + ["#\\ ??/100;[Red]\\(#\\ ??/16\\)", false], + ["#\\ ??/16", false], + ["#\\ ??/??", false], + ["#\\ ??/?????????", false], + ["#\\ ???/???", false], + ["**\\ #,###,#00,000.00,**", false], + ["0", false], + ["0\"abde\".0\"??\"000E+00", false], + ["0%", false], + ["0.0", false], + ["0.0%", false], + ["0.00", false], + ["0.00\"°\"", false], + ["0.00%", false], + ["0.000", false], + ["0.000%", false], + ["0.0000", false], + ["0.000000", false], + ["0.00000000", false], + ["0.000000000", false], + ["0.000000000%", false], + ["0.00000000000", false], + ["0.000000000000000", false], + ["0.00000000E+00", false], + ["0.0000E+00", false], + ["0.00;[Red]0.00", false], + ["0.00E+00", false], + ["0.00_);[Red]\\(0.00\\)", false], + ["0.00_);\\(0.00\\)", false], + ["0.0_ ", false], + ["00.00.00.000", false], + ["00.000%", false], + ["0000", false], + ["00000", false], + ["00000000", false], + ["000000000", false], + ["00000\\-0000", false], + ["00000\\-00000", false], + ["000\\-00\\-0000", false], + ["0;[Red]0", false], + ["0\\-00000\\-00000\\-0", false], + ["0_);[Red]\\(0\\)", false], + ["0_);\\(0\\)", false], + ["@", false], + ["A/P", true], + ["AM/PM", true], + ["AM/PMh\"時\"mm\"分\"ss\"秒\";@", true], + ["D", true], + ["DD", true], + ["DD/MM/YY;@", true], + ["DD/MM/YYYY", true], + ["DD/MM/YYYY;@", true], + ["DDD", true], + ["DDDD", true], + ["DDDD\", \"MMMM\\ DD\", \"YYYY", true], + ["GENERAL", false], + ["General", false], + ["H", true], + ["H:MM:SS\\ AM/PM", true], + ["HH:MM", true], + ["HH:MM:SS\\ AM/PM", true], + ["HHM", true], + ["HHMM", true], + ["HH[MM]", true], + ["HH[M]", true], + ["M/D/YYYY", true], + ["M/D/YYYY\\ H:MM", true], + ["MM/DD/YY", true], + ["S", true], + ["SS", true], + ["YY", true], + ["YYM", true], + ["YYMM", true], + ["YYMMM", true], + ["YYMMMM", true], + ["YYMMMMM", true], + ["YYYY", true], + ["YYYY-MM-DD HH:MM:SS", true], + ["YYYY\\-MM\\-DD", true], + ["[$$-409]#,##0", false], + ["[$$-409]#,##0.00", false], + ["[$$-409]#,##0.00_);[Red]\\([$$-409]#,##0.00\\)", false], + ["[$$-C09]#,##0.00", false], + ["[$-100042A]h:mm:ss\\ AM/PM;@", true], + ["[$-1010409]0.000%", false], + ["[$-1010409]General", false], + ["[$-1010409]d/m/yyyy\\ h:mm\\ AM/PM;@", true], + ["[$-1010409]dddd, mmmm dd, yyyy", true], + ["[$-1010409]m/d/yyyy", true], + ["[$-1409]h:mm:ss\\ AM/PM;@", true], + ["[$-2000000]h:mm:ss;@", true], + ["[$-2010401]d/mm/yyyy\\ h:mm\\ AM/PM;@", true], + ["[$-4000439]h:mm:ss\\ AM/PM;@", true], + ["[$-4010439]d/m/yyyy\\ h:mm\\ AM/PM;@", true], + ["[$-409]AM/PM\\ hh:mm:ss;@", true], + ["[$-409]d/m/yyyy\\ hh:mm;@", true], + ["[$-409]d\\-mmm;@", true], + ["[$-409]d\\-mmm\\-yy;@", true], + ["[$-409]d\\-mmm\\-yyyy;@", true], + ["[$-409]dd/mm/yyyy\\ hh:mm;@", true], + ["[$-409]dd\\-mmm\\-yy;@", true], + ["[$-409]h:mm:ss\\ AM/PM;@", true], + ["[$-409]h:mm\\ AM/PM;@", true], + ["[$-409]m/d/yy\\ h:mm\\ AM/PM;@", true], + ["[$-409]mmm\\-yy;@", true], + ["[$-409]mmmm\\ d\\,\\ yyyy;@", true], + ["[$-409]mmmm\\-yy;@", true], + ["[$-409]mmmmm;@", true], + ["[$-409]mmmmm\\-yy;@", true], + ["[$-40E]h\\ \"óra\"\\ m\\ \"perckor\"\\ AM/PM;@", true], + ["[$-412]AM/PM\\ h\"시\"\\ mm\"분\"\\ ss\"초\";@", true], + ["[$-41C]h:mm:ss\\.AM/PM;@", true], + ["[$-449]hh:mm:ss\\ AM/PM;@", true], + ["[$-44E]hh:mm:ss\\ AM/PM;@", true], + ["[$-44F]hh:mm:ss\\ AM/PM;@", true], + ["[$-D000409]h:mm\\ AM/PM;@", true], + ["[$-D010000]d/mm/yyyy\\ h:mm\\ \"น.\";@", true], + ["[$-F400]h:mm:ss\\ AM/PM", true], + ["[$-F800]dddd\\,\\ mmmm\\ dd\\,\\ yyyy", true], + ["[$AUD]\\ #,##0.00", false], + ["[$RD$-1C0A]#,##0.00;[Red]\\-[$RD$-1C0A]#,##0.00", false], + ["[$SFr.-810]\\ #,##0.00_);[Red]\\([$SFr.-810]\\ #,##0.00\\)", false], + ["[$£-809]#,##0.00;[Red][$£-809]#,##0.00", false], + ["[$¥-411]#,##0.00", false], + ["[$¥-804]#,##0.00", false], + ["[<0]\"\";0%", false], + ["[<=9999999]###\\-####;\\(###\\)\\ ###\\-####", false], + ["[=0]?;#,##0.00", false], + ["[=0]?;0%", false], + ["[=0]?;[<4.16666666666667][hh]:mm:ss;[hh]:mm", true], + ["[>999999]#,,\"M\";[>999]#,\"K\";#", false], + ["[>999999]#.000,,\"M\";[>999]#.000,\"K\";#.000", false], + ["[>=100000]0.000\\ \\\";[Red]0.000\\ \\<\\ \\>\\ \\\"\\ \\&\\ \\'\\ ", false], + ["[>=100000]0.000\\ \\<;[Red]0.000\\ \\>", false], + ["[BLACK]@", false], + ["[BLUE]GENERAL", false], + ["[Black]@", false], + ["[Blue]General", false], + ["[CYAN]@", false], + ["[Cyan]@", false], + ["[DBNum1][$-804]AM/PMh\"时\"mm\"分\";@", true], + ["[DBNum1][$-804]General", false], + ["[DBNum1][$-804]h\"时\"mm\"分\";@", true], + ["[ENG][$-1004]dddd\\,\\ d\\ mmmm\\,\\ yyyy;@", true], + ["[ENG][$-101040D]d\\ mmmm\\ yyyy;@", true], + ["[ENG][$-101042A]d\\ mmmm\\ yyyy;@", true], + ["[ENG][$-140C]dddd\\ \"YeahWoo!\"\\ ddd\\ mmmm\\ yyyy;@", true], + ["[ENG][$-2C0A]dddd\\ d\" de \"mmmm\" de \"yyyy;@", true], + ["[ENG][$-402]dd\\ mmmm\\ yyyy\\ \"г.\";@", true], + ["[ENG][$-403]dddd\\,\\ d\" / \"mmmm\" / \"yyyy;@", true], + ["[ENG][$-405]d\\.\\ mmmm\\ yyyy;@", true], + ["[ENG][$-408]d\\ mmmm\\ yyyy;@", true], + ["[ENG][$-409]d\\-mmm;@", true], + ["[ENG][$-409]d\\-mmm\\-yy;@", true], + ["[ENG][$-409]d\\-mmm\\-yyyy;@", true], + ["[ENG][$-409]dd\\-mmm\\-yy;@", true], + ["[ENG][$-409]mmm\\-yy;@", true], + ["[ENG][$-409]mmmm\\ d\\,\\ yyyy;@", true], + ["[ENG][$-409]mmmm\\-yy;@", true], + ["[ENG][$-40B]d\\.\\ mmmm\\t\\a\\ yyyy;@", true], + ["[ENG][$-40C]d/mmm/yyyy;@", true], + ["[ENG][$-40E]yyyy/\\ mmmm\\ d\\.;@", true], + ["[ENG][$-40F]dd\\.\\ mmmm\\ yyyy;@", true], + ["[ENG][$-410]d\\ mmmm\\ yyyy;@", true], + ["[ENG][$-415]d\\ mmmm\\ yyyy;@", true], + ["[ENG][$-416]d\\ \\ mmmm\\,\\ yyyy;@", true], + ["[ENG][$-418]d\\ mmmm\\ yyyy;@", true], + ["[ENG][$-41A]d\\.\\ mmmm\\ yyyy\\.;@", true], + ["[ENG][$-41B]d\\.\\ mmmm\\ yyyy;@", true], + ["[ENG][$-41D]\"den \"\\ d\\ mmmm\\ yyyy;@", true], + ["[ENG][$-420]dddd\\,\\ dd\\ mmmm\\,\\ yyyy;@", true], + ["[ENG][$-421]dd\\ mmmm\\ yyyy;@", true], + ["[ENG][$-424]dddd\\,\\ d\\.\\ mmmm\\ yyyy;@", true], + ["[ENG][$-425]dddd\\,\\ d\\.\\ mmmm\\ yyyy;@", true], + ["[ENG][$-426]dddd\\,\\ yyyy\". gada \"d\\.\\ mmmm;@", true], + ["[ENG][$-427]yyyy\\ \"m.\"\\ mmmm\\ d\\ \"d.\";@", true], + ["[ENG][$-42B]dddd\\,\\ d\\ mmmm\\ yyyy;@", true], + ["[ENG][$-42C]d\\ mmmm\\ yyyy;@", true], + ["[ENG][$-42D]yyyy\"(e)ko\"\\ mmmm\"ren\"\\ d\"a\";@", true], + ["[ENG][$-42F]dddd\\,\\ dd\\ mmmm\\ yyyy;@", true], + ["[ENG][$-437]yyyy\\ \\წ\\ლ\\ი\\ს\\ dd\\ mm\\,\\ dddd;@", true], + ["[ENG][$-438]d\\.\\ mmmm\\ yyyy;@", true], + ["[ENG][$-43F]d\\ mmmm\\ yyyy\\ \"ж.\";@", true], + ["[ENG][$-444]d\\ mmmm\\ yyyy;@", true], + ["[ENG][$-449]dd\\ mmmm\\ yyyy;@", true], + ["[ENG][$-44E]d\\ mmmm\\ yyyy;@", true], + ["[ENG][$-44F]dd\\ mmmm\\ yyyy\\ dddd;@", true], + ["[ENG][$-457]dd\\ mmmm\\ yyyy;@", true], + ["[ENG][$-813]dddd\\ d\\ mmmm\\ yyyy;@", true], + ["[ENG][$-81A]dddd\\,\\ d\\.\\ mmmm\\ yyyy;@", true], + ["[ENG][$-82C]d\\ mmmm\\ yyyy;@", true], + ["[ENG][$-843]yyyy\\ \"й\"\"и\"\"л\"\\ d/mmmm;@", true], + ["[ENG][$-C07]dddd\\,\\ dd\\.\\ mmmm\\ yyyy;@", true], + ["[ENG][$-FC19]yyyy\\,\\ dd\\ mmmm;@", true], + ["[ENG][$-FC22]d\\ mmmm\\ yyyy\" р.\";@", true], + ["[ENG][$-FC23]d\\ mmmm\\ yyyy;@", true], + ["[GREEN]#,###", false], + ["[Green]#,###", false], + ["[HH]", true], + ["[HIJ][$-2060401]d/mm/yyyy\\ h:mm\\ AM/PM;@", true], + ["[HIJ][$-2060401]d\\ mmmm\\ yyyy;@", true], + ["[H]", true], + ["[JPN][$-411]gggyy\"年\"m\"月\"d\"日\"\\ dddd;@", true], + ["[MAGENTA]0.00", false], + ["[Magenta]0.00", false], + ["[RED]#.##", false], + ["[Red]#.##", false], + ["[Red][<-25]General;[Blue][>25]General;[Green]General;[Yellow]General\\ ", false], + ["[Red][<=-25]General;[Blue][>=25]General;[Green]General;[Yellow]General", false], + ["[Red][<>50]General;[Blue]000", false], + ["[Red][=50]General;[Blue]000", false], + ["[SS]", true], + ["[S]", true], + ["[TWN][DBNum1][$-404]y\"年\"m\"月\"d\"日\";@", true], + ["[WHITE]0.0", false], + ["[White]0.0", false], + ["[YELLOW]@", false], + ["[Yellow]@", false], + ["[h]", true], + ["[h]:mm:ss", true], + ["[h]:mm:ss;@", true], + ["[h]\\.mm\" Uhr \";@", true], + ["[hh]", true], + ["[s]", true], + ["[ss]", true], + ["\\#\\r\\e\\c", false], + ["\\$#,##0_);[Red]\\\"($\\\"#,##0\\)", false], + ["\\$0.00", false], + ["\\C\\O\\B\\ \\o\\n\\ @", false], + ["\\C\\R\\O\\N\\T\\A\\B\\ \\o\\n\\ @", false], + ["\\R\\e\\s\\u\\l\\t\\ \\o\\n\\ @", false], + ["\\S\\Q\\L\\ \\:\\ @", false], + ["\\S\\Q\\L\\ \\R\\e\\q\\u\\e\\s\\t\\ \\f\\o\\r\\ @", false], + ["\\c\\c\\c?????0\"aaaa\"0\"bbbb\"000000.00%", false], + ["\\u\\n\\t\\i\\l\\ h:mm;@", true], + ["_ * #,##0.00_)[$﷼-429]_ ;_ * \\(#,##0.00\\)[$﷼-429]_ ;_ * \"-\"??_)[$﷼-429]_ ;_ @_ ", false], + ["_ * #,##0_ ;_ * \\-#,##0_ ;[Red]_ * \"-\"_ ;_ @_ ", false], + ["_(\"$\"* #,##0.00_);_(\"$\"* \\(#,##0.00\\);_(\"$\"* \"-\"??_);_(@_)", false], + ["_(\"$\"* #,##0_);_(\"$\"* \\(#,##0\\);_(\"$\"* \"-\"??_);_(@_)", false], + ["_(\"$\"* #,##0_);_(\"$\"* \\(#,##0\\);_(\"$\"* \"-\"_);_(@_)", false], + ["_(* #,##0.0000_);_(* \\(#,##0.0000\\);_(* \"-\"??_);_(@_)", false], + ["_(* #,##0.000_);_(* \\(#,##0.000\\);_(* \"-\"??_);_(@_)", false], + ["_(* #,##0.00_);_(* \\(#,##0.00\\);_(* \"-\"??_);_(@_)", false], + ["_(* #,##0.0_);_(* \\(#,##0.0\\);_(* \"-\"??_);_(@_)", false], + ["_(* #,##0_);_(* \\(#,##0\\);_(* \"-\"??_);_(@_)", false], + ["_(* #,##0_);_(* \\(#,##0\\);_(* \"-\"_);_(@_)", false], + ["_([$ANG]\\ * #,##0.0_);_([$ANG]\\ * \\(#,##0.0\\);_([$ANG]\\ * \"-\"?_);_(@_)", false], + ["_-\"€\"\\ * #,##0.00_-;_-\"€\"\\ * #,##0.00\\-;_-\"€\"\\ * \"-\"??_-;_-@_-", false], + ["_-* #,##0.00\" TL\"_-;\\-* #,##0.00\" TL\"_-;_-* \\-??\" TL\"_-;_-@_-", false], + ["_-* #,##0.00\" €\"_-;\\-* #,##0.00\" €\"_-;_-* \\-??\" €\"_-;_-@_-", false], + ["_-* #,##0.00\\ \"р.\"_-;\\-* #,##0.00\\ \"р.\"_-;_-* \"-\"??\\ \"р.\"_-;_-@_-", false], + ["_-* #,##0.00\\ \"€\"_-;\\-* #,##0.00\\ \"€\"_-;_-* \"-\"??\\ \"€\"_-;_-@_-", false], + ["_-* #,##0.00\\ [$€-407]_-;\\-* #,##0.00\\ [$€-407]_-;_-* \\-??\\ [$€-407]_-;_-@_-", false], + ["_-* #,##0.0\\ _F_-;\\-* #,##0.0\\ _F_-;_-* \"-\"??\\ _F_-;_-@_-", false], + ["_-* #,##0\\ \"€\"_-;\\-* #,##0\\ \"€\"_-;_-* \"-\"\\ \"€\"_-;_-@_-", false], + ["_-* #,##0_-;\\-* #,##0_-;_-* \"-\"??_-;_-@_-", false], + ["_-\\$* #,##0.0_ ;_-\\$* \\-#,##0.0\\ ;_-\\$* \"-\"?_ ;_-@_ ", false], + ["d", true], + ["d-mmm", true], + ["d-mmm-yy", true], + ["d/m", true], + ["d/m/yy;@", true], + ["d/m/yyyy;@", true], + ["d/mm/yy;@", true], + ["d/mm/yyyy;@", true], + ["d\\-mmm", true], + ["d\\-mmm\\-yyyy", true], + ["dd", true], + ["dd\"-\"mmm\"-\"yyyy", true], + ["dd/m/yyyy", true], + ["dd/mm/yy", true], + ["dd/mm/yy;@", true], + ["dd/mm/yy\\ hh:mm", true], + ["dd/mm/yyyy", true], + ["dd/mm/yyyy\\ hh:mm:ss", true], + ["dd/mmm", true], + ["dd\\-mm\\-yy", true], + ["dd\\-mmm\\-yy", true], + ["dd\\-mmm\\-yyyy\\ hh:mm:ss.000", true], + ["dd\/mm\/yy", true], + ["dd\/mm\/yyyy", true], + ["ddd", true], + ["dddd", true], + ["dddd, mmmm dd, yyyy", true], + ["h", true], + ["h\"时\"mm\"分\"ss\"秒\";@", true], + ["h\"時\"mm\"分\"ss\"秒\";@", true], + ["h:mm", true], + ["h:mm AM/PM", true], + ["h:mm:ss", true], + ["h:mm:ss AM/PM", true], + ["h:mm:ss;@", true], + ["h:mm;@", true], + ["h\\.mm\" Uhr \";@", true], + ["h\\.mm\" h\";@", true], + ["h\\.mm\" u.\";@", true], + ["hh\":\"mm AM/PM", true], + ["hh:mm:ss", true], + ["hh:mm:ss\\ AM/PM", true], + ["hh\\.mm\" h\";@", true], + ["hhm", true], + ["hhmm", true], + ["m\"月\"d\"日\"", true], + ["m/d/yy", true], + ["m/d/yy h:mm", true], + ["m/d/yy;@", true], + ["m/d/yy\\ h:mm", true], + ["m/d/yy\\ h:mm;@", true], + ["m/d/yyyy", true], + ["m/d/yyyy;@", true], + ["m/d/yyyy\\ h:mm:ss;@", true], + ["m/d;@", true], + ["m\/d\/yyyy", true], + ["mm/dd", true], + ["mm/dd/yy", true], + ["mm/dd/yy;@", true], + ["mm/dd/yyyy", true], + ["mm:ss", true], + ["mm:ss.0;@", true], + ["mmm d, yyyy", true], + ["mmm\" \"d\", \"yyyy", true], + ["mmm-yy", true], + ["mmm-yy;@", true], + ["mmm/yy", true], + ["mmm\\-yy", true], + ["mmm\\-yy;@", true], + ["mmm\\-yyyy", true], + ["mmmm\\ d\\,\\ yyyy", true], + ["mmmm\\ yyyy", true], + ["mmss.0", true], + ["s", true], + ["ss", true], + ["yy", true], + ["yy/mm/dd", true], + ["yy\\.mm\\.dd", true], + ["yym", true], + ["yymm", true], + ["yymmm", true], + ["yymmmm", true], + ["yymmmmm", true], + ["yyyy", true], + ["yyyy\"년\"\\ m\"월\"\\ d\"일\";@", true], + ["yyyy-m-d h:mm AM/PM", true], + ["yyyy-mm-dd", true], + ["yyyy/mm/dd", true], + ["yyyy\\-m\\-d\\ hh:mm:ss", true], + ["yyyy\\-mm\\-dd", true], + ["yyyy\\-mm\\-dd;@", true], + ["yyyy\\-mm\\-dd\\ h:mm", true], + ["yyyy\\-mm\\-dd\\Thh:mm", true], + ["yyyy\\-mm\\-dd\\Thhmmss.000", true], + ["###,##0", false], + ["#,###.00", false] +] diff --git a/test/utilities.js b/test/utilities.js new file mode 100644 index 0000000..ec03199 --- /dev/null +++ b/test/utilities.js @@ -0,0 +1,11 @@ +/* vim: set ts=2: */ +/*jshint loopfunc:true, mocha:true, node:true */ +var SSF = require('../'); +var fs = require('fs'), assert = require('assert'); +var is_date = JSON.parse(fs.readFileSync('./test/is_date.json','utf8')); +var skip = []; +describe('utilities', function() { + it('correctly determines if formats are dates', function() { + is_date.forEach(function(d) { assert.equal(SSF.is_date(d[0]), d[1], d[0]); }); + }); +});