From a1ccaf511d6d477bf7a3ffae292518082fc705c4 Mon Sep 17 00:00:00 2001 From: SheetJS Date: Thu, 23 Jan 2014 01:20:19 -0500 Subject: [PATCH] version bump 0.3.8: more formatting - updated SSF to 0.5.3 tests without baselines now try to generate CSV output --- bin/xlsx2csv.njs | 43 ++++--- bits/10_ssf.js | 194 +++++++++++++++-------------- bits/51_version.js | 2 +- bits/90_utils.js | 106 ++++++++-------- index.html | 19 +-- package.json | 2 +- test.js | 14 ++- xlsx.js | 302 ++++++++++++++++++++++----------------------- 8 files changed, 355 insertions(+), 327 deletions(-) diff --git a/bin/xlsx2csv.njs b/bin/xlsx2csv.njs index 359d0cc..f601189 100755 --- a/bin/xlsx2csv.njs +++ b/bin/xlsx2csv.njs @@ -1,19 +1,30 @@ #!/usr/bin/env node -/* xlsx.js (C) 2013 SheetJS -- http://sheetjs.com */ +/* xlsx.js (C) 2013-2014 SheetJS -- http://sheetjs.com */ +var n = "xlsx"; /* vim: set ts=2: */ -var XLSX = require('../'); +var X = require('../'); var fs = require('fs'), program = require('commander'); program - .version('0.3.1') + .version(X.version) .usage('[options] [sheetname]') .option('-f, --file ', 'use specified workbook') .option('-s, --sheet ', 'print specified sheet (default first sheet)') .option('-l, --list-sheets', 'list sheet names and exit') - .option('-F, --formulae', 'print formulae') + .option('-S, --formulae', 'print formulae') + .option('-j, --json', 'emit formatted JSON rather than CSV (all fields text)') + .option('-J, --raw-js', 'emit raw JS object rather than CSV (raw numbers)') + .option('-F, --field-sep ', 'CSV field separator', ",") + .option('-R, --row-sep ', 'CSV row separator', "\n") .option('--dev', 'development mode') .option('--read', 'read but do not print out contents') - .option('-q, --quiet', 'quiet mode') - .parse(process.argv); + .option('-q, --quiet', 'quiet mode'); + +program.on('--help', function() { + console.log(' Support email: dev@sheetjs.com'); + console.log(' Web Demo: http://oss.sheetjs.com/js-'+n+'/'); +}); + +program.parse(process.argv); var filename, sheetname = ''; if(program.args[0]) { @@ -24,23 +35,23 @@ if(program.sheet) sheetname = program.sheet; if(program.file) filename = program.file; if(!filename) { - console.error("xlsx2csv: must specify a filename"); + console.error(n + "2csv: must specify a filename"); process.exit(1); } if(!fs.existsSync(filename)) { - console.error("xlsx2csv: " + filename + ": No such file or directory"); + console.error(n + "2csv: " + filename + ": No such file or directory"); process.exit(2); } -if(program.dev) XLSX.verbose = 2; +if(program.dev) X.verbose = 2; var wb; -if(program.dev) wb = XLSX.readFile(filename); +if(program.dev) wb = X.readFile(filename); else try { - wb = XLSX.readFile(filename); + wb = X.readFile(filename); } catch(e) { - var msg = (program.quiet) ? "" : "xlsx2csv: error parsing "; + var msg = (program.quiet) ? "" : n + "2csv: error parsing "; msg += filename + ": " + e; console.error(msg); process.exit(3); @@ -60,10 +71,12 @@ try { ws = wb.Sheets[target_sheet]; if(!ws) throw "Sheet " + target_sheet + " cannot be found"; } catch(e) { - console.error("xlsx2csv: error parsing "+filename+" "+target_sheet+": " + e); + console.error(n + "2csv: error parsing "+filename+" "+target_sheet+": " + e); process.exit(4); } if(!program.quiet) console.error(target_sheet); -if(program.formulae) console.log(XLSX.utils.get_formulae(ws).join("\n")); -else console.log(XLSX.utils.make_csv(ws)); +if(program.formulae) console.log(X.utils.get_formulae(ws).join("\n")); +else if(program.json) console.log(JSON.stringify(X.utils.sheet_to_row_object_array(ws))); +else if(program.rawJs) console.log(JSON.stringify(X.utils.sheet_to_row_object_array(ws,{raw:true}))); +else console.log(X.utils.make_csv(ws, {FS:program.fieldSep, RS:program.rowSep})); diff --git a/bits/10_ssf.js b/bits/10_ssf.js index a048b0e..d828615 100644 --- a/bits/10_ssf.js +++ b/bits/10_ssf.js @@ -1,11 +1,12 @@ /* Spreadsheet Format -- jump to XLSX for the XLSX code */ -/* ssf.js (C) 2013 SheetJS -- http://sheetjs.com */ +/* ssf.js (C) 2013-2014 SheetJS -- http://sheetjs.com */ var SSF = {}; var make_ssf = function(SSF){ var _strrev = function(x) { return String(x).split("").reverse().join("");}; function fill(c,l) { return new Array(l+1).join(c); } function pad(v,d,c){var t=String(v);return t.length>=d?t:(fill(c||0,d-t.length)+t);} function rpad(v,d,c){var t=String(v);return t.length>=d?t:(t+fill(c||0,d-t.length));} +SSF.version = '0.5.3'; /* Options */ var opts_fmt = {}; function fixopts(o){for(var y in opts_fmt) if(o[y]===undefined) o[y]=opts_fmt[y];} @@ -14,7 +15,7 @@ opts_fmt.date1904 = 0; opts_fmt.output = ""; opts_fmt.mode = ""; var table_fmt = { - 0: 'General', + 0: 'General', 1: '0', 2: '0.00', 3: '#,##0', @@ -41,7 +42,9 @@ var table_fmt = { 46: '[h]:mm:ss', 47: 'mmss.0', 48: '##0.0E+0', - 49: '@' + 49: '@', + 56: '"上午/下午 "hh"時"mm"分"ss"秒 "', + 65535: 'General' }; var days = [ ['Sun', 'Sunday'], @@ -71,12 +74,12 @@ var frac = function frac(x, D, mixed) { 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); + var A = Math.floor(B); while(Q_1 < D) { - A = Math.floor(B); + A = Math.floor(B); P = A * P_1 + P_2; Q = A * Q_1 + Q_2; - if((B - A) < 0.0000000005) break; + if((B - A) < 0.0000000005) break; B = 1 / (B - A); P_2 = P_1; P_1 = P; Q_2 = Q_1; Q_1 = Q; @@ -84,7 +87,7 @@ var frac = function frac(x, D, mixed) { 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; + 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]; }; @@ -114,10 +117,10 @@ var general_fmt = function(v) { }; SSF._general = general_fmt; var parse_date_code = function parse_date_code(v,opts) { - var date = Math.floor(v), time = Math.floor(86400 * (v - date)+1e-6), dow=0; + var date = Math.floor(v), time = Math.floor(86400 * (v - date)+1e-6), dow=0; var dout=[], out={D:date, T:time, u:86400*(v-date)-time}; fixopts(opts = (opts||{})); if(opts.date1904) date += 1462; - if(date > 2958465) return null; + if(date > 2958465) return null; if(date === 60) {dout = [1900,2,29]; dow=3;} else if(date === 0) {dout = [1900,1,0]; dow=6;} else { @@ -127,7 +130,7 @@ var parse_date_code = function parse_date_code(v,opts) { d.setDate(d.getDate() + date - 1); dout = [d.getFullYear(), d.getMonth()+1,d.getDate()]; dow = d.getDay(); - if(/* opts.mode === 'excel' && */ date < 60) dow = (dow + 6) % 7; + if(/* opts.mode === 'excel' && */ date < 60) dow = (dow + 6) % 7; } out.y = dout[0]; out.m = dout[1]; out.d = dout[2]; out.S = time % 60; time = Math.floor(time / 60); @@ -140,13 +143,13 @@ SSF.parse_date_code = parse_date_code; /*jshint -W086 */ var write_date = function(type, fmt, val) { if(val < 0) return ""; - var o; + var o; switch(type) { case 'y': switch(fmt) { /* year */ case 'y': case 'yy': return pad(val.y % 100,2); - case 'yyy': case 'yyyy': return pad(val.y % 10000,4); - default: throw 'bad year format: ' + fmt; - } + case 'yyy': case 'yyyy': return pad(val.y % 10000,4); + default: throw 'bad year format: ' + fmt; + } case 'm': switch(fmt) { /* month */ case 'm': return val.m; case 'mm': return pad(val.m,2); @@ -154,46 +157,45 @@ var write_date = function(type, fmt, val) { case 'mmmm': return months[val.m-1][2]; case 'mmmmm': return months[val.m-1][0]; default: throw 'bad month format: ' + fmt; - } + } case 'd': switch(fmt) { /* day */ case 'd': return val.d; case 'dd': return pad(val.d,2); case 'ddd': return days[val.q][0]; case 'dddd': return days[val.q][1]; default: throw 'bad day format: ' + fmt; - } + } case 'h': switch(fmt) { /* 12-hour */ case 'h': return 1+(val.H+11)%12; case 'hh': return pad(1+(val.H+11)%12, 2); default: throw 'bad hour format: ' + fmt; - } + } case 'H': switch(fmt) { /* 24-hour */ case 'h': return val.H; case 'hh': return pad(val.H, 2); default: throw 'bad hour format: ' + fmt; - } + } case 'M': switch(fmt) { /* minutes */ case 'm': return val.M; case 'mm': return pad(val.M, 2); default: throw 'bad minute format: ' + fmt; - } + } case 's': switch(fmt) { /* seconds */ - case 's': return Math.round(val.S+val.u); - case 'ss': return pad(Math.round(val.S+val.u), 2); - case 'ss.0': o = pad(Math.round(10*(val.S+val.u)),3); return o.substr(0,2)+"." + o.substr(2); - case 'ss.00': o = pad(Math.round(100*(val.S+val.u)),4); return o.substr(0,2)+"." + o.substr(2); - case 'ss.000': o = pad(Math.round(1000*(val.S+val.u)),5); return o.substr(0,2)+"." + o.substr(2); + case 's': return Math.round(val.S+val.u); + case 'ss': return pad(Math.round(val.S+val.u), 2); + case 'ss.0': o = pad(Math.round(10*(val.S+val.u)),3); return o.substr(0,2)+"." + o.substr(2); + case 'ss.00': o = pad(Math.round(100*(val.S+val.u)),4); return o.substr(0,2)+"." + o.substr(2); + case 'ss.000': o = pad(Math.round(1000*(val.S+val.u)),5); return o.substr(0,2)+"." + o.substr(2); default: throw 'bad second format: ' + fmt; - } + } case 'Z': switch(fmt) { - case '[h]': case '[hh]': o = val.D*24+val.H; break; - case '[m]': case '[mm]': o = (val.D*24+val.H)*60+val.M; break; - case '[s]': case '[ss]': o = ((val.D*24+val.H)*60+val.M)*60+Math.round(val.S+val.u); break; + case '[h]': case '[hh]': o = val.D*24+val.H; break; + case '[m]': case '[mm]': o = (val.D*24+val.H)*60+val.M; break; + case '[s]': case '[ss]': o = ((val.D*24+val.H)*60+val.M)*60+Math.round(val.S+val.u); break; default: throw 'bad abstime format: ' + fmt; - } return fmt.length === 3 ? o : pad(o, 2); + } return fmt.length === 3 ? o : pad(o, 2); /* TODO: handle the ECMA spec format ee -> yy */ case 'e': { return val.y; } break; - case 'A': return (val.h>=12 ? 'P' : 'A') + fmt.substr(1); default: throw 'bad format type ' + type + ' in ' + fmt; } }; @@ -210,48 +212,54 @@ var write_num = function(type, fmt, val) { if(mul !== 0) return write_num(type, fmt, val * Math.pow(10,2*mul)) + fill("%",mul); if(fmt.indexOf("E") > -1) { var idx = fmt.indexOf("E") - fmt.indexOf(".") - 1; - //if(fmt.match(/^#+0\.0E\+0$/)) if(fmt == '##0.0E+0') { - var period = fmt.length - 5; - var ee = (Number(val.toExponential(0).substr(2+(val<0))))%period; - o = (val/Math.pow(10,ee)).toPrecision(idx+1+(period+ee)%period); - if(!o.match(/[Ee]/)) { - var fakee = (Number(val.toExponential(0).substr(2+(val<0)))); - if(o.indexOf(".") === -1) o = o[0] + "." + o.substr(1) + "E+" + (fakee - o.length+ee); - else throw "missing E |" + o; - } - o = o.replace(/^([+-]?)([0-9]*)\.([0-9]*)[Ee]/,function($$,$1,$2,$3) { return $1 + $2 + $3.substr(0,(period+ee)%period) + "." + $3.substr(ee) + "E"; }); + var period = fmt.length - 5; + var ee = (Number(val.toExponential(0).substr(2+(val<0))))%period; + o = (val/Math.pow(10,ee)).toPrecision(idx+1+(period+ee)%period); + if(!o.match(/[Ee]/)) { + var fakee = (Number(val.toExponential(0).substr(2+(val<0)))); + if(o.indexOf(".") === -1) o = o[0] + "." + o.substr(1) + "E+" + (fakee - o.length+ee); + else throw "missing E |" + o; + } + o = o.replace(/^([+-]?)([0-9]*)\.([0-9]*)[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[+-][0-9]$/)) o = o.substr(0,o.length-1) + "0" + o[o.length-1]; if(fmt.match(/E\-/) && o.match(/e\+/)) o = o.replace(/e\+/,"e"); return o.replace("e","E"); } - if(fmt[0] === "$") return "$"+write_num(type,fmt.substr(fmt[1]==' '?2:1),val); + if(fmt[0] === "$") return "$"+write_num(type,fmt.substr(fmt[1]==' '?2:1),val); var r, ff, aval = val < 0 ? -val : val, sign = val < 0 ? "-" : ""; - if((r = fmt.match(/# (\?+) \/ (\d+)/))) { - var den = Number(r[2]), rnd = Math.round(aval * den), base = Math.floor(rnd/den); + if((r = fmt.match(/# (\?+)([ ]?)\/([ ]?)(\d+)/))) { + var den = Number(r[4]), rnd = Math.round(aval * den), base = Math.floor(rnd/den); var myn = (rnd - base*den), myd = den; - return sign + (base?base:"") + " " + (myn === 0 ? fill(" ", r[1].length + 1 + r[2].length) : pad(myn,r[1].length," ") + "/" + pad(myd,r[2].length)); + return sign + (base?base:"") + " " + (myn === 0 ? fill(" ", r[1].length + 1 + r[4].length) : pad(myn,r[1].length," ") + r[2] + "/" + r[3] + pad(myd,r[4].length)); + } + if(fmt.match(/^00+$/)) return (val<0?"-":"")+pad(Math.round(aval),fmt.length); + if(fmt.match(/^[#?]+$/)) return String(Math.round(val)).replace(/^0$/,""); + if(r = fmt.match(/^#*0+\.(0+)/)) { + o = Math.round(val * Math.pow(10,r[1].length)); + return String(o/Math.pow(10,r[1].length)).replace(/^([^\.]+)$/,"$1."+r[1]).replace(/\.$/,"."+r[1]).replace(/\.([0-9]*)$/,function($$, $1) { return "." + $1 + fill("0", r[1].length-$1.length); }); + } + if(r = fmt.match(/^# ([?]+)([ ]?)\/([ ]?)([?]+)/)) { + var rr = Math.min(Math.max(r[1].length, r[4].length),7); + ff = frac(aval, Math.pow(10,rr)-1, true); + return sign + (ff[0]||(ff[1] ? "" : "0")) + " " + (ff[1] ? pad(ff[1],rr," ") + r[2] + "/" + r[3] + rpad(ff[2],rr," "): fill(" ", 2*rr+1 + r[2].length + r[3].length)); } - if(fmt.match(/^00*$/)) return (val<0?"-":"")+pad(Math.round(aval),fmt.length); - if(fmt.match(/^####*$/)) return Math.round(val); switch(fmt) { - case "0": return Math.round(val); - case "0.0": o = Math.round(val*10); - return String(o/10).replace(/^([^\.]+)$/,"$1.0").replace(/\.$/,".0"); - case "0.00": o = Math.round(val*100); - return String(o/100).replace(/^([^\.]+)$/,"$1.00").replace(/\.$/,".00").replace(/\.([0-9])$/,".$1"+"0"); - case "0.000": o = Math.round(val*1000); - return String(o/1000).replace(/^([^\.]+)$/,"$1.000").replace(/\.$/,".000").replace(/\.([0-9])$/,".$1"+"00").replace(/\.([0-9][0-9])$/,".$1"+"0"); - case "#.##": o = Math.round(val*100); - return String(o/100).replace(/^([^\.]+)$/,"$1.").replace(/^0\.$/,"."); - case "#,###": var x = commaify(String(Math.round(aval))); return x !== "0" ? sign + x : ""; + case "0": case "#0": return Math.round(val); + case "#.##": o = Math.round(val*100); + return String(o/100).replace(/^([^\.]+)$/,"$1.").replace(/^0\.$/,"."); + case "#,###": var x = commaify(String(Math.round(aval))); return x !== "0" ? sign + x : ""; case "#,##0": return sign + commaify(String(Math.round(aval))); - case "#,##0.0": r = Math.round((val-Math.floor(val))*10); return val < 0 ? "-" + write_num(type, fmt, -val) : commaify(String(Math.floor(val))) + "." + r; - case "#,##0.00": r = Math.round((val-Math.floor(val))*100); return val < 0 ? "-" + write_num(type, fmt, -val) : commaify(String(Math.floor(val))) + "." + (r < 10 ? "0"+r:r); - case "# ? / ?": ff = frac(aval, 9, true); return sign + (ff[0]||(ff[1] ? "" : "0")) + " " + (ff[1] === 0 ? " " : ff[1] + "/" + ff[2]); - case "# ?? / ??": ff = frac(aval, 99, true); return sign + (ff[0]||(ff[1] ? "" : "0")) + " " + (ff[1] ? pad(ff[1],2," ") + "/" + rpad(ff[2],2," ") : " "); - case "# ??? / ???": ff = frac(aval, 999, true); return sign + (ff[0]||(ff[1] ? "" : "0")) + " " + (ff[1] ? pad(ff[1],3," ") + "/" + rpad(ff[2],3," ") : " "); + case "#,##0.0": r = Math.round((val-Math.floor(val))*10); return val < 0 ? "-" + write_num(type, fmt, -val) : commaify(String(Math.floor(val))) + "." + pad(r,1,"0"); + case "#,##0.00": r = Math.round((val-Math.floor(val))*100); return val < 0 ? "-" + write_num(type, fmt, -val) : commaify(String(Math.floor(val))) + "." + pad(r,2,"0"); + case "#,##0.000": r = Math.round((val-Math.floor(val))*1000); return val < 0 ? "-" + write_num(type, fmt, -val) : commaify(String(Math.floor(val))) + "." + pad(r,3,0); + case "#,##0.0000": r = Math.round((val-Math.floor(val))*10000); return val < 0 ? "-" + write_num(type, fmt, -val) : commaify(String(Math.floor(val))) + "." + pad(r,4,0); + case "#,##0.00000": r = Math.round((val-Math.floor(val))*Math.pow(10,5)); return val < 0 ? "-" + write_num(type, fmt, -val) : commaify(String(Math.floor(val))) + "." + pad(r,5,0); + case "#,##0.000000": r = Math.round((val-Math.floor(val))*Math.pow(10,6)); return val < 0 ? "-" + write_num(type, fmt, -val) : commaify(String(Math.floor(val))) + "." + pad(r,6,0); + case "#,##0.0000000": r = Math.round((val-Math.floor(val))*Math.pow(10,7)); return val < 0 ? "-" + write_num(type, fmt, -val) : commaify(String(Math.floor(val))) + "." + pad(r,7,0); + case "#,##0.00000000": r = Math.round((val-Math.floor(val))*Math.pow(10,8)); return val < 0 ? "-" + write_num(type, fmt, -val) : commaify(String(Math.floor(val))) + "." + pad(r,8,0); + case "#,##0.000000000": r = Math.round((val-Math.floor(val))*Math.pow(10,9)); return val < 0 ? "-" + write_num(type, fmt, -val) : commaify(String(Math.floor(val))) + "." + pad(r,9,0); default: } throw new Error("unsupported format |" + fmt + "|"); @@ -279,10 +287,10 @@ function eval_fmt(fmt, v, opts, flen) { /* Tokenize */ while(i < fmt.length) { switch((c = fmt[i])) { - case 'G': /* General */ - if(fmt.substr(i, i+6).toLowerCase() !== "general") - throw 'unrecognized character ' + fmt[i] + ' in ' + fmt; - out.push({t:'G',v:'General'}); i+=7; break; + case 'G': /* General */ + if(fmt.substr(i, i+6).toLowerCase() !== "general") + throw 'unrecognized character ' + fmt[i] + ' in ' + fmt; + out.push({t:'G',v:'General'}); i+=7; break; case '"': /* Literal text */ for(o="";fmt[++i] !== '"' && i < fmt.length;) o += fmt[i]; out.push({t:'t', v:o}); ++i; break; @@ -292,35 +300,35 @@ function eval_fmt(fmt, v, opts, flen) { case '@': /* Text Placeholder */ out.push({t:'T', v:v}); ++i; break; /* Dates */ - 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': + c = c.toLowerCase(); + /* falls through */ case 'm': case 'd': case 'y': case 'h': case 's': case 'e': if(v < 0) return ""; if(!dt) dt = parse_date_code(v, opts); - if(!dt) return ""; - o = fmt[i]; while((fmt[++i]||"").toLowerCase() === c) o+=c; + if(!dt) return ""; + o = fmt[i]; while((fmt[++i]||"").toLowerCase() === c) o+=c; if(c === 's' && fmt[i] === '.' && fmt[i+1] === '0') { o+='.'; while(fmt[++i] === '0') o+= '0'; } if(c === 'm' && lst.toLowerCase() === 'h') c = 'M'; /* m = minute */ if(c === 'h') c = hr; - o = o.toLowerCase(); + o = o.toLowerCase(); q={t:c, v:o}; out.push(q); lst = c; break; case 'A': if(!dt) dt = parse_date_code(v, opts); - if(!dt) return ""; + if(!dt) return ""; q={t:c,v:"A"}; if(fmt.substr(i, 3) === "A/P") {q.v = dt.H >= 12 ? "P" : "A"; q.t = 'T'; hr='h';i+=3;} else if(fmt.substr(i,5) === "AM/PM") { q.v = dt.H >= 12 ? "PM" : "AM"; q.t = 'T'; i+=5; hr='h'; } - else { q.t = "t"; i++; } + else { q.t = "t"; i++; } out.push(q); lst = c; break; case '[': /* TODO: Fix this -- ignore all conditionals and formatting */ o = c; while(fmt[i++] !== ']') o += fmt[i]; - if(o.match(/\[[HhMmSs]*\]/)) { - if(!dt) dt = parse_date_code(v, opts); - if(!dt) return ""; - out.push({t:'Z', v:o.toLowerCase()}); - } else { o=""; } + if(o.match(/\[[HhMmSs]*\]/)) { + if(!dt) dt = parse_date_code(v, opts); + if(!dt) return ""; + out.push({t:'Z', v:o.toLowerCase()}); + } else { o=""; } break; /* Numbers */ case '0': case '#': @@ -336,7 +344,7 @@ function eval_fmt(fmt, v, opts, flen) { out.push({t:'D', v:o}); break; case ' ': out.push({t:c,v:c}); ++i; break; default: - if("$-+/():!^&'~{}<>=".indexOf(c) === -1) + if(",$-+/():!^&'~{}<>=".indexOf(c) === -1) throw 'unrecognized character ' + fmt[i] + ' in ' + fmt; out.push({t:'t', v:c}); ++i; break; } @@ -358,30 +366,30 @@ function eval_fmt(fmt, v, opts, flen) { case 'd': case 'm': case 'y': case 'h': case 'H': case 'M': case 's': case 'A': case 'e': case 'Z': out[i].v = write_date(out[i].t, out[i].v, dt); out[i].t = 't'; break; - case 'n': case '(': + case 'n': case '(': case '?': var jj = i+1; - while(out[jj] && ("?D".indexOf(out[jj].t) > -1 || (out[jj].t == " " && (out[jj+1]||{}).t === "?" ) || out[i].t == '(' && (out[jj].t == ')' || out[jj].t == 'n') || out[jj].t == 't' && (out[jj].v == '/' || out[jj].v == '$' || (out[jj].v == ' ' && (out[jj+1]||{}).t == '?')))) { - if(out[jj].v!==' ') out[i].v += ' ' + out[jj].v; + while(out[jj] && ("?D".indexOf(out[jj].t) > -1 || (" t".indexOf(out[jj].t) > -1 && "?t".indexOf((out[jj+1]||{}).t)>-1 && (out[jj+1].t == '?' || out[jj+1].v == '/')) || out[i].t == '(' && (out[jj].t == ')' || out[jj].t == 'n') || out[jj].t == 't' && (out[jj].v == '/' || out[jj].v == '$' || (out[jj].v == ' ' && (out[jj+1]||{}).t == '?')))) { + out[i].v += out[jj].v; delete out[jj]; ++jj; } out[i].v = write_num(out[i].t, out[i].v, v); out[i].t = 't'; - i = jj-1; break; - case 'G': out[i].t = 't'; out[i].v = general_fmt(v,opts); break; - default: throw "unrecognized type " + out[i].t; + i = jj-1; break; + case 'G': out[i].t = 't'; out[i].v = general_fmt(v,opts); break; + default: console.error(out); throw "unrecognized type " + out[i].t; } } return out.map(function(x){return x.v;}).join(""); } SSF._eval = eval_fmt; function choose_fmt(fmt, v, o) { - if(typeof fmt === 'number') fmt = ((o&&o.table) ? o.table : table_fmt)[fmt]; + if(typeof fmt === 'number') fmt = ((o&&o.table) ? o.table : table_fmt)[fmt]; if(typeof fmt === "string") fmt = split_fmt(fmt); var l = fmt.length; switch(fmt.length) { - case 1: fmt = fmt[0].indexOf("@")>-1 ? ["General", "General", "General", fmt[0]] : [fmt[0], fmt[0], fmt[0], "@"]; break; - case 2: fmt = fmt[1].indexOf("@")>-1 ? [fmt[0], fmt[0], fmt[0], fmt[1]] : [fmt[0], fmt[1], fmt[0], "@"]; break; - case 3: fmt = fmt[2].indexOf("@")>-1 ? [fmt[0], fmt[1], fmt[0], fmt[2]] : [fmt[0], fmt[1], fmt[2], "@"]; break; + case 1: fmt = fmt[0].indexOf("@")>-1 ? ["General", "General", "General", fmt[0]] : [fmt[0], fmt[0], fmt[0], "@"]; break; + case 2: fmt = fmt[1].indexOf("@")>-1 ? [fmt[0], fmt[0], fmt[0], fmt[1]] : [fmt[0], fmt[1], fmt[0], "@"]; break; + case 3: fmt = fmt[2].indexOf("@")>-1 ? [fmt[0], fmt[1], fmt[0], fmt[2]] : [fmt[0], fmt[1], fmt[2], "@"]; break; case 4: break; default: throw "cannot find right format for |" + fmt + "|"; } @@ -390,11 +398,11 @@ function choose_fmt(fmt, v, o) { } var format = function format(fmt,v,o) { fixopts(o = (o||{})); - if(typeof fmt === "string" && fmt.toLowerCase() === "general") return general_fmt(v, o); - if(typeof fmt === 'number') fmt = (o.table || table_fmt)[fmt]; + if(typeof fmt === "string" && fmt.toLowerCase() === "general") return general_fmt(v, o); + if(typeof fmt === 'number') fmt = (o.table || table_fmt)[fmt]; var f = choose_fmt(fmt, v, o); - if(f[1].toLowerCase() === "general") return general_fmt(v,o); - if(v === true) v = "TRUE"; if(v === false) v = "FALSE"; + if(f[1].toLowerCase() === "general") return general_fmt(v,o); + if(v === true) v = "TRUE"; if(v === false) v = "FALSE"; return eval_fmt(f[1], v, o, f[0]); }; diff --git a/bits/51_version.js b/bits/51_version.js index 44f874b..e3fc123 100644 --- a/bits/51_version.js +++ b/bits/51_version.js @@ -1 +1 @@ -XLSX.version = '0.3.7'; +XLSX.version = '0.3.8'; diff --git a/bits/90_utils.js b/bits/90_utils.js index 74d125d..88bf616 100644 --- a/bits/90_utils.js +++ b/bits/90_utils.js @@ -10,60 +10,50 @@ function split_cell(cstr) { return cstr.replace(/(\$?[A-Z]*)(\$?[0-9]*)/,"$1,$2" function decode_cell(cstr) { var splt = split_cell(cstr); return { c:decode_col(splt[0]), r:decode_row(splt[1]) }; } function decode_range(range) { var x =range.split(":").map(decode_cell); return {s:x[0],e:x[x.length-1]}; } function encode_range(range) { return encode_cell(range.s) + ":" + encode_cell(range.e); } -/** - * Convert a sheet into an array of objects where the column headers are keys. - **/ -function sheet_to_row_object_array(sheet){ - var val, rowObject, range, columnHeaders, emptyRow, C; - var outSheet = []; - if (sheet["!ref"]) { - range = decode_range(sheet["!ref"]); - columnHeaders = {}; - for (C = range.s.c; C <= range.e.c; ++C) { - val = sheet[encode_cell({ - c: C, - r: range.s.r - })]; - if(val){ - switch(val.t) { - case 's': case 'str': columnHeaders[C] = val.v; break; - case 'n': columnHeaders[C] = val.v; break; - } - } - } - - for (var R = range.s.r + 1; R <= range.e.r; ++R) { - emptyRow = true; - //Row number is recorded in the prototype - //so that it doesn't appear when stringified. - rowObject = Object.create({ __rowNum__ : R }); - for (C = range.s.c; C <= range.e.c; ++C) { - val = sheet[encode_cell({ - c: C, - r: R - })]; - if(val !== undefined) switch(val.t){ - case 's': case 'str': case 'b': case 'n': - if(val.v !== undefined) { - rowObject[columnHeaders[C]] = val.v; - emptyRow = false; - } - break; - case 'e': break; /* throw */ - default: throw 'unrecognized type ' + val.t; - } - } - if(!emptyRow) { - outSheet.push(rowObject); +function sheet_to_row_object_array(sheet, opts){ + var val, row, r, hdr = {}, isempty, R, C, v; + var out = []; + opts = opts || {}; + if(!sheet["!ref"]) return out; + r = XLSX.utils.decode_range(sheet["!ref"]); + for(R=r.s.r, C = r.s.c; C <= r.e.c; ++C) { + val = sheet[encode_cell({c:C,r:R})]; + if(val){ + switch(val.t) { + case 's': case 'str': hdr[C] = val.v; break; + case 'n': hdr[C] = val.v; break; } } } - return outSheet; + + for (R = r.s.r + 1; R <= r.e.r; ++R) { + isempty = true; + /* row index available as __rowNum__ */ + row = Object.create({ __rowNum__ : R }); + for (C = r.s.c; C <= r.e.c; ++C) { + val = sheet[encode_cell({c: C,r: R})]; + if(!val) continue; + v = (val || {}).v; + switch(val.t){ + case 's': case 'str': case 'b': case 'n': + if(val.v !== undefined) { + row[hdr[C]] = val.v; + isempty = false; + } + break; + case 'e': break; /* throw */ + default: throw 'unrecognized type ' + val.t; + } + } + if(!isempty) out.push(row); + } + return out; } -function sheet_to_csv(sheet) { +function sheet_to_csv(sheet, opts) { var stringify = function stringify(val) { + if(!val.t) return ""; switch(val.t){ case 'n': return String(val.v); case 's': case 'str': @@ -74,17 +64,19 @@ function sheet_to_csv(sheet) { default: throw 'unrecognized type ' + val.t; } }; - var out = ""; - if(sheet["!ref"]) { - var r = XLSX.utils.decode_range(sheet["!ref"]); - for(var R = r.s.r; R <= r.e.r; ++R) { - var row = []; - for(var C = r.s.c; C <= r.e.c; ++C) { - var val = sheet[XLSX.utils.encode_cell({c:C,r:R})]; - row.push(val ? stringify(val).replace(/\\r\\n/g,"\n").replace(/\\t/g,"\t").replace(/\\\\/g,"\\").replace("\\\"","\"\"") : ""); - } - out += row.join(",") + "\n"; + var out = "", txt = ""; + opts = opts || {}; + if(!sheet["!ref"]) return out; + var r = XLSX.utils.decode_range(sheet["!ref"]); + for(var R = r.s.r; R <= r.e.r; ++R) { + var row = []; + for(var C = r.s.c; C <= r.e.c; ++C) { + var val = sheet[XLSX.utils.encode_cell({c:C,r:R})]; + if(!val) { row.push(""); continue; } + txt = stringify(val); + row.push(String(txt).replace(/\\r\\n/g,"\n").replace(/\\t/g,"\t").replace(/\\\\/g,"\\").replace("\\\"","\"\"")); } + out += row.join(opts.FS||",") + (opts.RS||"\n"); } return out; } diff --git a/index.html b/index.html index aeba2da..7e4c207 100644 --- a/index.html +++ b/index.html @@ -32,17 +32,18 @@