Comparing version 0.5.1 to 0.5.2
{ | ||
"name": "ssf", | ||
"version": "0.5.1", | ||
"version": "0.5.2", | ||
"author": "SheetJS", | ||
@@ -23,2 +23,7 @@ "description": "pure-JS library to format data using ECMA-376 spreadsheet Format Codes", | ||
}, | ||
"config": { | ||
"blanket": { | ||
"pattern": "ssf.js" | ||
} | ||
}, | ||
"bugs": { "url": "https://github.com/SheetJS/ssf/issues" }, | ||
@@ -25,0 +30,0 @@ "license": "Apache-2.0", |
@@ -26,7 +26,7 @@ # SSF | ||
`.load(fmt, idx)` sets custom formats (generally indices above `164`) | ||
`.load(fmt, idx)` sets custom formats (generally indices above `164`). | ||
`.format(fmt, val)` formats `val` using the format `fmt`. If `fmt` is of type | ||
`number`, the internal table (and custom formats) will be used. If `fmt` is a | ||
literal format, then it will be parsed and evaluated. | ||
`.format(fmt, val, opts)` formats `val` using the format `fmt`. If `fmt` is of | ||
type `number`, the internal table (and custom formats) will be used. If `fmt` | ||
is a literal format, then it will be parsed and evaluated. | ||
@@ -40,2 +40,6 @@ `.parse_date_code(val, opts)` parses `val` as date code and returns object: | ||
`.get_table()` gets the internal format table (number to format mapping). | ||
`.load_table(table)` sets the internal format table. | ||
## Notes | ||
@@ -42,0 +46,0 @@ |
87
ssf.js
/* ssf.js (C) 2013-2014 SheetJS -- http://sheetjs.com */ | ||
var SSF = {}; | ||
var make_ssf = function(SSF){ | ||
String.prototype.reverse=function(){return this.split("").reverse().join("");}; | ||
var _strrev = function(x) { return String(x).reverse(); }; | ||
var _strrev = function(x) { return String(x).split("").reverse().join("");}; | ||
function fill(c,l) { return new Array(l+1).join(c); } | ||
@@ -17,2 +16,3 @@ function pad(v,d,c){var t=String(v);return t.length>=d?t:(fill(c||0,d-t.length)+t);} | ||
var table_fmt = { | ||
0: 'General', | ||
1: '0', | ||
@@ -87,3 +87,3 @@ 2: '0.00', | ||
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); | ||
@@ -118,3 +118,3 @@ return [q, sgn*P - q*Q, Q]; | ||
var parse_date_code = function parse_date_code(v,opts) { | ||
var date = Math.floor(v), time = Math.floor(86400 * (v - date)), 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||{})); | ||
@@ -132,3 +132,3 @@ if(opts.date1904) date += 1462; | ||
dow = d.getDay(); | ||
if(opts.mode === 'excel' && date < 60) dow = (dow + 6) % 7; | ||
if(/* opts.mode === 'excel' && */ date < 60) dow = (dow + 6) % 7; | ||
} | ||
@@ -143,9 +143,12 @@ out.y = dout[0]; out.m = dout[1]; out.d = dout[2]; | ||
SSF.parse_date_code = parse_date_code; | ||
/*jshint -W086 */ | ||
var write_date = function(type, fmt, val) { | ||
if(val < 0) return ""; | ||
var o; | ||
switch(type) { | ||
case 'y': switch(fmt) { /* year */ | ||
case 'y': case 'yy': return pad(val.y % 100,2); | ||
default: return val.y; | ||
} break; | ||
case 'yyy': case 'yyyy': return pad(val.y % 10000,4); | ||
default: throw 'bad year format: ' + fmt; | ||
} | ||
case 'm': switch(fmt) { /* month */ | ||
@@ -158,3 +161,3 @@ case 'm': return val.m; | ||
default: throw 'bad month format: ' + fmt; | ||
} break; | ||
} | ||
case 'd': switch(fmt) { /* day */ | ||
@@ -166,3 +169,3 @@ case 'd': return val.d; | ||
default: throw 'bad day format: ' + fmt; | ||
} break; | ||
} | ||
case 'h': switch(fmt) { /* 12-hour */ | ||
@@ -172,3 +175,3 @@ case 'h': return 1+(val.H+11)%12; | ||
default: throw 'bad hour format: ' + fmt; | ||
} break; | ||
} | ||
case 'H': switch(fmt) { /* 24-hour */ | ||
@@ -178,3 +181,3 @@ case 'h': return val.H; | ||
default: throw 'bad hour format: ' + fmt; | ||
} break; | ||
} | ||
case 'M': switch(fmt) { /* minutes */ | ||
@@ -184,13 +187,17 @@ case 'm': return val.M; | ||
default: throw 'bad minute format: ' + fmt; | ||
} break; | ||
} | ||
case 's': switch(fmt) { /* seconds */ | ||
case 's': return val.S; | ||
case 's': return Math.round(val.S+val.u); | ||
case 'ss': return pad(Math.round(val.S+val.u), 2); | ||
case 'ss.0': var o = pad(Math.round(10*(val.S+val.u)),3); return o.substr(0,2)+"." + o.substr(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; | ||
} break; | ||
} | ||
case 'Z': switch(fmt) { | ||
case '[h]': return val.D*24+val.H; | ||
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; | ||
} break; | ||
} return fmt.length === 3 ? o : pad(o, 2); | ||
/* TODO: handle the ECMA spec format ee -> yy */ | ||
@@ -202,4 +209,4 @@ case 'e': { return val.y; } break; | ||
}; | ||
String.prototype.reverse = function() { return this.split("").reverse().join(""); }; | ||
var commaify = function(s) { return s.reverse().replace(/.../g,"$&,").reverse().replace(/^,/,""); }; | ||
/*jshint +W086 */ | ||
var commaify = function(s) { return _strrev(_strrev(s).replace(/.../g,"$&,")).replace(/^,/,""); }; | ||
var write_num = function(type, fmt, val) { | ||
@@ -216,11 +223,13 @@ if(type === '(') { | ||
var idx = fmt.indexOf("E") - fmt.indexOf(".") - 1; | ||
//if(fmt.match(/^#+0\.0E\+0$/)) | ||
if(fmt == '##0.0E+0') { | ||
var ee = (Number(val.toExponential(0).substr(2+(val<0))))%3; | ||
o = (val/Math.pow(10,ee)).toPrecision(idx+1+(3+ee)%3); | ||
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"; | ||
else throw "missing E |" + o; | ||
} | ||
o = o.replace(/^([+-]?)([0-9]*)\.([0-9]*)[Ee]/,function($$,$1,$2,$3) { return $1 + $2 + $3.substr(0,(3+ee)%3) + "." + $3.substr(ee) + "E"; }); | ||
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); | ||
@@ -238,2 +247,4 @@ 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(/^00*$/)) return (val<0?"-":"")+pad(Math.round(aval),fmt.length); | ||
if(fmt.match(/^####*$/)) return Math.round(val); | ||
switch(fmt) { | ||
@@ -247,2 +258,5 @@ case "0": return Math.round(val); | ||
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": return sign + commaify(String(Math.round(aval))); | ||
@@ -281,2 +295,6 @@ 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; | ||
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 '"': /* Literal text */ | ||
@@ -291,2 +309,5 @@ for(o="";fmt[++i] !== '"' && i < fmt.length;) o += fmt[i]; | ||
/* 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': | ||
@@ -296,6 +317,7 @@ if(v < 0) return ""; | ||
if(!dt) return ""; | ||
o = fmt[i]; while(fmt[++i] === c) o+=c; | ||
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(); | ||
q={t:c, v:o}; out.push(q); lst = c; break; | ||
@@ -308,3 +330,3 @@ case 'A': | ||
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"; | ||
else { q.t = "t"; i++; } | ||
out.push(q); lst = c; break; | ||
@@ -314,3 +336,7 @@ case '[': /* TODO: Fix this -- ignore all conditionals and formatting */ | ||
while(fmt[i++] !== ']') o += fmt[i]; | ||
if(o == "[h]") out.push({t:'Z', v: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; | ||
@@ -362,2 +388,3 @@ /* Numbers */ | ||
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; | ||
@@ -374,4 +401,5 @@ } | ||
switch(fmt.length) { | ||
case 1: fmt = [fmt[0], fmt[0], fmt[0], "@"]; break; | ||
case 2: fmt = [fmt[0], fmt[fmt[1] === "@"?0:1], fmt[0], "@"]; 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; | ||
@@ -385,6 +413,7 @@ default: throw "cannot find right format for |" + fmt + "|"; | ||
fixopts(o = (o||{})); | ||
if(fmt === 0 || (typeof fmt === "string" && fmt.toLowerCase() === "general")) return general_fmt(v, o); | ||
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"; | ||
return eval_fmt(f[1], v, o, f[0]); | ||
@@ -391,0 +420,0 @@ }; |
258
ssf.md
@@ -183,2 +183,3 @@ # SSF | ||
var table_fmt = { | ||
0: 'General', | ||
1: '0', | ||
@@ -266,3 +267,3 @@ 2: '0.00', | ||
var parse_date_code = function parse_date_code(v,opts) { | ||
var date = Math.floor(v), time = Math.floor(86400 * (v - date)), 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||{})); | ||
@@ -315,3 +316,3 @@ ``` | ||
``` | ||
if(opts.mode === 'excel' && date < 60) dow = (dow + 6) % 7; | ||
if(/* opts.mode === 'excel' && */ date < 60) dow = (dow + 6) % 7; | ||
} | ||
@@ -336,4 +337,3 @@ ``` | ||
```js>tmp/60_number.js | ||
String.prototype.reverse = function() { return this.split("").reverse().join(""); }; | ||
var commaify = function(s) { return s.reverse().replace(/.../g,"$&,").reverse().replace(/^,/,""); }; | ||
var commaify = function(s) { return _strrev(_strrev(s).replace(/.../g,"$&,")).replace(/^,/,""); }; | ||
var write_num = function(type, fmt, val) { | ||
@@ -371,5 +371,7 @@ ``` | ||
``` | ||
//if(fmt.match(/^#+0\.0E\+0$/)) | ||
if(fmt == '##0.0E+0') { | ||
var ee = (Number(val.toExponential(0).substr(2+(val<0))))%3; | ||
o = (val/Math.pow(10,ee)).toPrecision(idx+1+(3+ee)%3); | ||
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]/)) { | ||
@@ -383,5 +385,5 @@ ``` | ||
if(o.indexOf(".") === -1) o = o[0] + "." + o.substr(1) + "E+" + (fakee - o.length+ee); | ||
else throw "missing E"; | ||
else throw "missing E |" + o; | ||
} | ||
o = o.replace(/^([+-]?)([0-9]*)\.([0-9]*)[Ee]/,function($$,$1,$2,$3) { return $1 + $2 + $3.substr(0,(3+ee)%3) + "." + $3.substr(ee) + "E"; }); | ||
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); | ||
@@ -411,2 +413,9 @@ if(fmt.match(/E\+00$/) && o.match(/e[+-][0-9]$/)) o = o.substr(0,o.length-1) + "0" + o[o.length-1]; | ||
A few special general cases can be handled in a very dumb manner: | ||
``` | ||
if(fmt.match(/^00*$/)) return (val<0?"-":"")+pad(Math.round(aval),fmt.length); | ||
if(fmt.match(/^####*$/)) return Math.round(val); | ||
``` | ||
The default cases are hard-coded. TODO: actually parse them | ||
@@ -423,2 +432,5 @@ | ||
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": return sign + commaify(String(Math.round(aval))); | ||
@@ -453,2 +465,11 @@ 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; | ||
LO Formats sometimes leak "GENERAL" or "General" to stand for general format: | ||
``` | ||
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; | ||
``` | ||
Text between double-quotes are treated literally, and individual characters are | ||
@@ -490,2 +511,5 @@ literal if they are preceded by a slash. | ||
/* 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': | ||
@@ -505,3 +529,3 @@ ``` | ||
if(!dt) return ""; | ||
o = fmt[i]; while(fmt[++i] === c) o+=c; | ||
o = fmt[i]; while((fmt[++i]||"").toLowerCase() === c) o+=c; | ||
``` | ||
@@ -520,2 +544,3 @@ | ||
if(c === 'h') c = hr; | ||
o = o.toLowerCase(); | ||
q={t:c, v:o}; out.push(q); lst = c; break; | ||
@@ -539,8 +564,8 @@ ``` | ||
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"; | ||
else { q.t = "t"; i++; } | ||
out.push(q); lst = c; break; | ||
``` | ||
Conditional and color blocks should be handled at one point (TODO). For now, | ||
only the absolute time `[h]` is captured (using the pseudo-type `Z`): | ||
Conditional and color blocks should be handled at one point (TODO). The | ||
pseudo-type `Z` is used to capture absolute time blocks: | ||
@@ -551,3 +576,7 @@ ``` | ||
while(fmt[i++] !== ']') o += fmt[i]; | ||
if(o == "[h]") out.push({t:'Z', v: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; | ||
@@ -634,2 +663,3 @@ ``` | ||
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; | ||
@@ -650,11 +680,13 @@ } | ||
```js>tmp/50_date.js | ||
/*jshint -W086 */ | ||
var write_date = function(type, fmt, val) { | ||
if(val < 0) return ""; | ||
var o; | ||
switch(type) { | ||
case 'y': switch(fmt) { /* year */ | ||
case 'y': case 'yy': return pad(val.y % 100,2); | ||
default: return val.y; | ||
} break; | ||
case 'yyy': case 'yyyy': return pad(val.y % 10000,4); | ||
default: throw 'bad year format: ' + fmt; | ||
} | ||
case 'm': switch(fmt) { /* month */ | ||
@@ -667,3 +699,3 @@ case 'm': return val.m; | ||
default: throw 'bad month format: ' + fmt; | ||
} break; | ||
} | ||
case 'd': switch(fmt) { /* day */ | ||
@@ -675,3 +707,3 @@ case 'd': return val.d; | ||
default: throw 'bad day format: ' + fmt; | ||
} break; | ||
} | ||
case 'h': switch(fmt) { /* 12-hour */ | ||
@@ -681,3 +713,3 @@ case 'h': return 1+(val.H+11)%12; | ||
default: throw 'bad hour format: ' + fmt; | ||
} break; | ||
} | ||
case 'H': switch(fmt) { /* 24-hour */ | ||
@@ -687,3 +719,3 @@ case 'h': return val.H; | ||
default: throw 'bad hour format: ' + fmt; | ||
} break; | ||
} | ||
case 'M': switch(fmt) { /* minutes */ | ||
@@ -693,9 +725,11 @@ case 'm': return val.M; | ||
default: throw 'bad minute format: ' + fmt; | ||
} break; | ||
} | ||
case 's': switch(fmt) { /* seconds */ | ||
case 's': return val.S; | ||
case 's': return Math.round(val.S+val.u); | ||
case 'ss': return pad(Math.round(val.S+val.u), 2); | ||
case 'ss.0': var o = pad(Math.round(10*(val.S+val.u)),3); return o.substr(0,2)+"." + o.substr(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; | ||
} break; | ||
} | ||
``` | ||
@@ -707,5 +741,7 @@ | ||
case 'Z': switch(fmt) { | ||
case '[h]': return val.D*24+val.H; | ||
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; | ||
} break; | ||
} return fmt.length === 3 ? o : pad(o, 2); | ||
``` | ||
@@ -723,2 +759,3 @@ | ||
}; | ||
/*jshint +W086 */ | ||
``` | ||
@@ -735,4 +772,17 @@ | ||
switch(fmt.length) { | ||
case 1: fmt = [fmt[0], fmt[0], fmt[0], "@"]; break; | ||
case 2: fmt = [fmt[0], fmt[fmt[1] === "@"?0:1], fmt[0], "@"]; break; | ||
``` | ||
In the case of one format, if it contains an "@" then it is a text format. | ||
There is a big TODO here regarding how to best handle this case. | ||
``` | ||
case 1: fmt = fmt[0].indexOf("@")>-1 ? ["General", "General", "General", fmt[0]] : [fmt[0], fmt[0], fmt[0], "@"]; break; | ||
``` | ||
In the case of 2 or 3 formats, if an `@` appears in the last field of the format | ||
it is treated as the text format | ||
``` | ||
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; | ||
@@ -756,6 +806,15 @@ default: throw "cannot find right format for |" + fmt + "|"; | ||
``` | ||
if(fmt === 0 || (typeof fmt === "string" && fmt.toLowerCase() === "general")) return general_fmt(v, o); | ||
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); | ||
``` | ||
The boolean TRUE and FALSE are formatted as if they are the uppercase text: | ||
``` | ||
if(v === true) v = "TRUE"; if(v === false) v = "FALSE"; | ||
``` | ||
``` | ||
return eval_fmt(f[1], v, o, f[0]); | ||
@@ -807,3 +866,3 @@ }; | ||
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); | ||
@@ -820,4 +879,3 @@ return [q, sgn*P - q*Q, Q]; | ||
var make_ssf = function(SSF){ | ||
String.prototype.reverse=function(){return this.split("").reverse().join("");}; | ||
var _strrev = function(x) { return String(x).reverse(); }; | ||
var _strrev = function(x) { return String(x).split("").reverse().join("");}; | ||
function fill(c,l) { return new Array(l+1).join(c); } | ||
@@ -855,2 +913,10 @@ function pad(v,d,c){var t=String(v);return t.length>=d?t:(fill(c||0,d-t.length)+t);} | ||
```>.npmignore | ||
test/*.tsv | ||
node_modules/ | ||
tmp/ | ||
.gitignore | ||
.vocrc | ||
``` | ||
```make>Makefile | ||
@@ -863,8 +929,32 @@ .PHONY: test ssf | ||
npm test | ||
.PHONY: lint | ||
lint: | ||
jshint ssf.js test/ | ||
``` | ||
Coverage tests use [blanket](http://npm.im/blanket): | ||
``` | ||
.PHONY: cov | ||
cov: tmp/coverage.html | ||
tmp/coverage.html: ssf.md | ||
mocha --require blanket -R html-cov > tmp/coverage.html | ||
``` | ||
Coveralls.io support | ||
``` | ||
.PHONY: coveralls | ||
coveralls: | ||
mocha --require blanket --reporter mocha-lcov-reporter | ./node_modules/coveralls/bin/coveralls.js | ||
``` | ||
```json>package.json | ||
{ | ||
"name": "ssf", | ||
"version": "0.5.1", | ||
"version": "0.5.2", | ||
"author": "SheetJS", | ||
@@ -889,2 +979,7 @@ "description": "pure-JS library to format data using ECMA-376 spreadsheet Format Codes", | ||
}, | ||
"config": { | ||
"blanket": { | ||
"pattern": "ssf.js" | ||
} | ||
}, | ||
"bugs": { "url": "https://github.com/SheetJS/ssf/issues" }, | ||
@@ -907,2 +1002,6 @@ "license": "Apache-2.0", | ||
- "npm install -g mocha" | ||
- "npm install blanket" | ||
- "npm install coveralls mocha-lcov-reporter" | ||
after_success: | ||
- "make coveralls" | ||
``` | ||
@@ -944,2 +1043,8 @@ | ||
}); | ||
it('should fail for undefined and null', function() { | ||
assert.throws(function() { | ||
SSF.format("General", undefined); | ||
SSF.format("General", null); | ||
}); | ||
}); | ||
}); | ||
@@ -959,3 +1064,3 @@ ``` | ||
it(d[1]+" for "+d[0], skip.indexOf(d[1]) > -1 ? null : function(){ | ||
var expected = d[2], actual = SSF.format(d[1], d[0], {}) | ||
var expected = d[2], actual = SSF.format(d[1], d[0], {}); | ||
//var r = actual.match(/(-?)\d* *\d+\/\d+/); | ||
@@ -968,2 +1073,87 @@ assert.equal(actual, expected); | ||
The dates test driver tests the date and time formats: | ||
```js>test/date.js | ||
/* vim: set ts=2: */ | ||
/*jshint loopfunc:true */ | ||
var SSF = require('../'); | ||
var fs = require('fs'), assert = require('assert'); | ||
var dates = fs.readFileSync('./test/dates.tsv','utf8').split("\n"); | ||
var times = fs.readFileSync('./test/times.tsv','utf8').split("\n"); | ||
function doit(data) { | ||
var step = Math.ceil(data.length/100), i = 1; | ||
var headers = data[0].split("\t"); | ||
for(j=0;j<=100;++j) it(j, function() { | ||
for(var k = 0; k <= step; ++k,++i) { | ||
if(!data[i]) return; | ||
var d = data[i].replace(/#{255}/g,"").split("\t"); | ||
for(var w = 1; w < headers.length; ++w) { | ||
var expected = d[w], actual = SSF.format(headers[w], Number(d[0]), {}); | ||
if(actual != expected) throw [actual, expected, w, headers[w],d[0],d].join("|"); | ||
actual = SSF.format(headers[w].toUpperCase(), Number(d[0]), {}); | ||
if(actual != expected) throw [actual, expected, w, headers[w],d[0],d].join("|"); | ||
} | ||
} | ||
}); | ||
} | ||
describe('time formats', function() { doit(times.slice(0,1000)); }); | ||
describe('date formats', function() { | ||
doit(dates); | ||
it('should fail for bad formats', function() { | ||
var bad = ['yyyyy', 'mmmmmm', 'ddddd']; | ||
var chk = function(fmt){ return function(){ SSF.format(fmt,0); }; }; | ||
bad.forEach(function(fmt){assert.throws(chk(fmt));}); | ||
}); | ||
}); | ||
``` | ||
The exponential test driver tests exponential formats (pipe denotes fails) | ||
```js>test/exp.js | ||
/* vim: set ts=2: */ | ||
/*jshint loopfunc:true */ | ||
var SSF = require('../'); | ||
var fs = require('fs'), assert = require('assert'); | ||
var data = fs.readFileSync('./test/exp.tsv','utf8').split("\n"); | ||
function doit(d, headers) { | ||
it(d[0], function() { | ||
for(var w = 2; w < 3 /*TODO: 1:headers.length */; ++w) { | ||
var expected = d[w].replace("|", ""), actual; | ||
try { actual = SSF.format(headers[w], Number(d[0]), {}); } catch(e) { } | ||
if(actual != expected && d[w][0] !== "|") throw [actual, expected, w, headers[w],d[0],d].join("|"); | ||
} | ||
}); | ||
} | ||
describe('exponential formats', function() { | ||
var headers = data[0].split("\t"); | ||
for(var j=14/* TODO: start from 1 */;j<data.length;++j) { | ||
if(!data[j]) return; | ||
doit(data[j].replace(/#{255}/g,"").split("\t"), headers); | ||
} | ||
}); | ||
``` | ||
The oddities test driver tests random odd formats | ||
```js>test/oddities.js | ||
/* vim: set ts=2: */ | ||
/*jshint loopfunc:true */ | ||
var SSF = require('../'); | ||
var fs = require('fs'), assert = require('assert'); | ||
var data = JSON.parse(fs.readFileSync('./test/oddities.json','utf8')); | ||
describe('oddities', function() { | ||
data.forEach(function(d) { | ||
it(d[0], function(){ | ||
for(j=1;j<d.length;++j) { | ||
if(d[j].length == 2) { | ||
var expected = d[j][1], actual = SSF.format(d[0], d[j][0], {}); | ||
assert.equal(actual, expected); | ||
} else assert.throws(function() { SSF.format(d[0], d[j][0]); }); | ||
} | ||
}); | ||
}); | ||
}); | ||
``` | ||
# LICENSE | ||
@@ -970,0 +1160,0 @@ |
@@ -9,3 +9,3 @@ /* vim: set ts=2: */ | ||
it(d[1]+" for "+d[0], skip.indexOf(d[1]) > -1 ? null : function(){ | ||
var expected = d[2], actual = SSF.format(d[1], d[0], {}) | ||
var expected = d[2], actual = SSF.format(d[1], d[0], {}); | ||
//var r = actual.match(/(-?)\d* *\d+\/\d+/); | ||
@@ -12,0 +12,0 @@ assert.equal(actual, expected); |
@@ -12,2 +12,8 @@ /* vim: set ts=2: */ | ||
}); | ||
it('should fail for undefined and null', function() { | ||
assert.throws(function() { | ||
SSF.format("General", undefined); | ||
SSF.format("General", null); | ||
}); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
99191
20
1882
58
6