diff --git a/.flowconfig b/.flowconfig
new file mode 100644
index 0000000..4ea8dcc
--- /dev/null
+++ b/.flowconfig
@@ -0,0 +1,29 @@
+[ignore]
+.*/node_modules/.*
+.*/dist/.*
+.*/test/bits/.*
+.*/test/.*
+.*/ssf.js
+.*/ssf_lc.js
+
+.*/bits/.*
+.*/ctest/.*
+.*/misc/.*
+.*/perf/.*
+.*/tmp/.*
+.*/tmp/.*
+
+.*/demo/browser.js
+.*/shim.js
+
+[include]
+ssf.js
+.*/bin/.*.njs
+
+[libs]
+misc/flow.js
+misc/flowdeps.js
+
+[options]
+module.file_ext=.js
+module.file_ext=.njs
diff --git a/.travis.yml b/.travis.yml
index dc9e596..f31a7c7 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,9 +1,16 @@
language: node_js
node_js:
+ - "7"
+ - "6"
+ - "5"
+ - "4"
+ - "0.12"
- "0.10"
+ - "0.9"
- "0.8"
before_install:
- - "npm install -g mocha"
+ - "npm install -g npm@4.3.0"
+ - "npm install -g mocha@2.x voc"
- "npm install blanket"
- "npm install coveralls mocha-lcov-reporter"
after_success:
diff --git a/LICENSE b/LICENSE
index 6d41660..a5fc80a 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (C) 2013-2014 SheetJS
+Copyright (C) 2013-present SheetJS
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/Makefile b/Makefile
index 735ff4f..6c5aac2 100755
--- a/Makefile
+++ b/Makefile
@@ -1,35 +1,64 @@
-.PHONY: test ssf
+SHELL=/bin/bash
+LIB=ssf
+CMDS=bin/ssf.njs
+HTMLLINT=
+
+ULIB=$(shell echo $(LIB) | tr a-z A-Z)
+TARGET=$(LIB).js
+
+## Main Targets
+
+
+.PHONY: ssf
ssf: ssf.md
voc ssf.md
-test:
+## Testing
+
+.PHONY: test mocha
+test mocha: ## Run test suite
npm test
test_min:
MINTEST=1 npm test
-.PHONY: lint
-lint:
- jshint ssf.js test/
- jscs ssf.js
+## Code Checking
-.PHONY: perf
-perf:
- bash misc/perf.sh
+.PHONY: lint
+lint: ## Run jshint and jscs checks
+ @jshint --show-non-errors $(TARGET) test/
+ @jshint --show-non-errors $(CMDS)
+ @jshint --show-non-errors package.json
+ @jshint --show-non-errors --extract=always $(HTMLLINT)
+ @jscs $(TARGET)
+
+.PHONY: flow
+flow: lint ## Run flow checker
+ @flow check --all --show-all-errors
.PHONY: cov
-cov: tmp/coverage.html
-
-tmp/coverage.html: ssf
- mocha --require blanket -R html-cov > tmp/coverage.html
+cov: tmp/coverage.html ## Run coverage test
.PHONY: cov_min
cov_min:
MINTEST=1 make cov
-.PHONY: coveralls full_coveralls
+tmp/coverage.html: ssf
+ mocha --require blanket -R html-cov -t 20000 > $@
+
+.PHONY: full_coveralls
full_coveralls:
mocha --require blanket --reporter mocha-lcov-reporter | ./node_modules/coveralls/bin/coveralls.js
-coveralls:
+.PHONY: coveralls
+coveralls: ## Coverage Test + Send to coveralls.io
MINTEST=1 make full_coveralls
+
+
+.PHONY: help
+help:
+ @grep -hE '(^[a-zA-Z_-][ a-zA-Z_-]*:.*?|^#[#*])' $(MAKEFILE_LIST) | bash misc/help.sh
+
+#* To show a spinner, append "-spin" to any target e.g. cov-spin
+%-spin:
+ @make $* & bash misc/spin.sh $$!
diff --git a/README.md b/README.md
index c6c6b4f..76c597a 100644
--- a/README.md
+++ b/README.md
@@ -1,59 +1,98 @@
# SSF
-SpreadSheet Format (SSF) is a pure-JS library to format data using ECMA-376
-spreadsheet format codes (like those used in Microsoft Excel)
+ssf (SpreadSheet Format) is a pure-JS library to format data using ECMA-376
+spreadsheet format codes (used in popular spreadsheet software packages).
-This is written in [voc](https://npmjs.org/package/voc) -- see ssf.md for code.
-To build: `voc ssf.md`
+## Installation
-## Setup
+With [npm](https://www.npmjs.org/package/ssf):
+
+```bash
+$ npm install ssf
+```
In the browser:
-
+```html
+
+```
-In node:
+The browser exposes a variable `SSF`
- var SSF = require('ssf');
+When installed globally, npm installs a script `ssf` that renders the format
+string with the given arguments. Running the script with `-h` displays help.
-The script will manipulate `module.exports` if available (e.g. in a CommonJS
-`require` context). This is not always desirable. To prevent the behavior,
-define `DO_NOT_EXPORT_SSF`:
+The script will manipulate `module.exports` if available (e.g. in a CommonJS
+`require` context). This is not always desirable. To prevent the behavior,
+define `DO_NOT_EXPORT_SSF`.
## Usage
-`.load(fmt, idx)` sets custom formats (generally indices above `164`).
+`SSF.format(fmt, val, opts)` formats `val` using the format `fmt`. If `fmt` is
+a string, it will be parsed and evaluated. If `fmt` is a `number`, the actual
+format will be the corresponding entry in the internal format table.
-`.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.
+### Manipulating the Internal Format Table
-`.parse_date_code(val, opts)` parses `val` as date code and returns object:
+Binary spreadsheet formats store cell formats in a table and reference by index.
+This library uses a global table:
-- `D,T`: Date (`[val]`) Time (`{val}`)
-- `y,m,d`: Year, Month, Day
-- `H,M,S,u`: (0-23)Hour, Minute, Second, Sub-second
-- `q`: Day of Week (0=Sunday, 1=Monday, ..., 5=Friday, 6=Saturday)
+`SSF._table` is the underlying object, mapping numeric keys to format strings.
-`.get_table()` gets the internal format table (number to format mapping).
+`SSF.load(fmt:string, idx:?number):number` assigns the format to the specified
+index and returns the index. If the index is not specified, SSF will search the
+space for an available format slot pick an unused slot. For compatibility with
+the XLS and XLSB file formats, custom indices should be in the valid ranges
+`5-8`, `23-26`, `41-44`, `63-66`, `164-382` (see `[MS-XLSB] 2.4.655 BrtFmt`)
-`.load_table(table)` sets the internal format table.
+`SSF.get_table()` gets the internal format table (number to format mapping).
-## Notes
+`SSF.load_table(table)` sets the internal format table.
+
+### Other Utilities
+
+`SSF.parse_date_code(val:number, opts:?any)` parses `val`, returning an object:
+
+```typescript
+type SSFDate = {
+ D:number; /* number of whole days since relevant epoch, 0 <= D */
+ y:number; /* integral year portion, epoch_year <= y */
+ m:number; /* integral month portion, 1 <= m <= 12 */
+ d:number; /* integral day portion, subject to gregorian YMD constraints */
+ q:number; /* integral day of week (0=Sunday .. 6=Saturday) 0 <= q <= 6 */
+
+ T:number; /* number of seconds since midnight, 0 <= T < 86400 */
+ H:number; /* integral number of hours since midnight, 0 <= H < 24 */
+ M:number; /* integral number of minutes since the last hour, 0 <= M < 60 */
+ S:number; /* integral number of seconds since the last minute, 0 <= S < 60 */
+ u:number; /* sub-second part of time, 0 <= u < 1 */
+}
+```
-Format code 14 in the spec is broken; the correct format is 'mm/dd/yy' (dashes,
-not spaces)
## License
-Apache 2.0
+Please consult the attached LICENSE file for details. All rights not explicitly
+granted by the Apache 2.0 license are reserved by the Original Author.
-## Tests
+## References
+
+- [ECMA-376] Office Open XML File Formats
+- [MS-XLSB] Excel (.xlsb) Binary File Format
+
+## Badges
+
+[![Sauce Test Status](https://saucelabs.com/browser-matrix/ssfjs.svg)](https://saucelabs.com/u/ssfjs)
[![Build Status](https://travis-ci.org/SheetJS/ssf.svg?branch=master)](https://travis-ci.org/SheetJS/ssf)
-[![Coverage Status](https://coveralls.io/repos/SheetJS/ssf/badge.png?branch=master)](https://coveralls.io/r/SheetJS/ssf?branch=master)
+[![Coverage Status](http://img.shields.io/coveralls/SheetJS/ssf/master.svg)](https://coveralls.io/r/SheetJS/ssf?branch=master)
-[![githalytics.com alpha](https://cruel-carlota.pagodabox.com/c1dac903f4b43f82a529bc8df145d085 "githalytics.com")](http://githalytics.com/SheetJS/ssf)
+[![NPM Downloads](https://img.shields.io/npm/dt/ssf.svg)](https://npmjs.org/package/ssf)
+[![Dependencies Status](https://david-dm.org/sheetjs/ssf/status.svg)](https://david-dm.org/sheetjs/ssf)
+
+[![ghit.me](https://ghit.me/badge.svg?repo=sheetjs/js-xlsx)](https://ghit.me/repo/sheetjs/js-xlsx)
+
+[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/ssf?pixel)](https://github.com/SheetJS/ssf)
diff --git a/bin/ssf.njs b/bin/ssf.njs
index 4329b71..42622db 100755
--- a/bin/ssf.njs
+++ b/bin/ssf.njs
@@ -1,5 +1,5 @@
#!/usr/bin/env node
-/* ssf.js (C) 2013-2014 SheetJS -- http://sheetjs.com */
+/* ssf.js (C) 2013-present SheetJS -- http://sheetjs.com */
var SSF = require('../');
var argv = process.argv.slice(2);
if(argv.length < 2 || argv[0] == "-h" || argv[0] == "--help") {
diff --git a/misc/flow.js b/misc/flow.js
new file mode 100644
index 0000000..47c528c
--- /dev/null
+++ b/misc/flow.js
@@ -0,0 +1,8 @@
+/*# vim: set ts=2: */
+/*::
+
+type SSFModule = {
+ format(fmt:string|number, v:any, o:any):string;
+};
+
+*/
diff --git a/misc/flowdeps.js b/misc/flowdeps.js
new file mode 100644
index 0000000..e6ff407
--- /dev/null
+++ b/misc/flowdeps.js
@@ -0,0 +1,8 @@
+/*# vim: set ts=2: */
+/*::
+
+declare module './' { declare var exports:SSFModule; };
+declare module '../' { declare var exports:SSFModule; };
+declare module 'ssf' { declare var exports:SSFModule; };
+
+*/
diff --git a/misc/help.sh b/misc/help.sh
new file mode 100755
index 0000000..7a1c4c4
--- /dev/null
+++ b/misc/help.sh
@@ -0,0 +1,42 @@
+#!/bin/bash
+# make_help.sh -- process listing of targets and special items in Makefile
+# Copyright (C) 2016-present SheetJS
+#
+# usage in makefile: pipe the output of the following command:
+# @grep -hE '(^[a-zA-Z_-][ a-zA-Z_-]*:.*?|^#[#*])' $(MAKEFILE_LIST)
+#
+# lines starting with "## " are treated as subtitles
+# lines starting with "#* " are treated as plaintext comments
+# multiple targets with "## " after the ":" are rendered as separate targets
+# if the presumed default target is labeled, it will be assigned a unique color
+
+awk '
+BEGIN{recipes=0;}
+ !/#[#*] .*$/ {next;}
+ {multi=0; isrecipe=0;}
+ /^[^#]*:/ {isrecipe=1; ++recipes;}
+ /^[^ :]* .*:/ {multi=1}
+ multi==0 && isrecipe>0 { if(recipes > 1) print; else print $0, "[default]"; next}
+ isrecipe == 0 {print; next}
+ multi>0 {
+ k=split($0, msg, "##"); m=split($0, a, ":"); n=split(a[1], b, " ");
+ for(i=1; i<=n; ++i) print b[i] ":", "##" msg[2], (recipes==1 && i==1 ? "[default]" : "")
+ }
+END {}
+' | if [[ -t 1 ]]; then
+awk '
+BEGIN {FS = ":.*?## "}
+ {color=36}
+ /\[default\]/ {color=35}
+ NF==1 && /^##/ {color=34}
+ NF==1 && /^#\*/ {color=20; $1 = substr($1, 4)}
+ {printf "\033[" color "m%-20s\033[0m %s\n", $1, $2;}
+END{}' -
+else
+awk '
+BEGIN {FS = ":.*?## "}
+ /^#\* / {$1 = substr($1, 4)}
+ {printf "%-20s %s\n", $1, $2;}
+END{}' -
+fi
+
diff --git a/misc/spin.sh b/misc/spin.sh
new file mode 100755
index 0000000..471dfee
--- /dev/null
+++ b/misc/spin.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+# spin.sh -- show a spinner (for coverage test)
+# Copyright (C) 2014-present SheetJS
+
+wpid=$1
+delay=1
+str="|/-\\"
+while [ $(ps -a|awk '$1=='$wpid' {print $1}') ]; do
+ t=${str#?}
+ printf " [%c]" "$str"
+ str=$t${str%"$t"}
+ sleep $delay
+ printf "\b\b\b\b"
+done
diff --git a/ssf.js b/ssf.js
index 84125f0..607d185 100644
--- a/ssf.js
+++ b/ssf.js
@@ -1,20 +1,20 @@
-/* ssf.js (C) 2013-2014 SheetJS -- http://sheetjs.com */
+/* ssf.js (C) 2013-present SheetJS -- http://sheetjs.com */
/*jshint -W041 */
var SSF = {};
var make_ssf = function make_ssf(SSF){
SSF.version = '0.8.1';
-function _strrev(x) { var o = "", i = x.length-1; while(i>=0) o += x.charAt(i--); return o; }
-function fill(c,l) { var o = ""; while(o.length < l) o+=c; return o; }
-function pad0(v,d){var t=""+v; return t.length>=d?t:fill('0',d-t.length)+t;}
-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;}
+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,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) { 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/*: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 = [
+var opts_fmt/*:Array >*/ = [
["date1904", 0],
["output", ""],
["WTF", false]
@@ -24,36 +24,36 @@ function fixopts(o){
}
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'],
@@ -100,7 +100,7 @@ function frac(x, D, mixed) {
var q = Math.floor(sgn * P/Q);
return [q, sgn*P - q*Q, Q];
}
-function general_fmt_int(v, opts) { return ""+v; }
+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)$/;
@@ -124,7 +124,7 @@ function gfn5(o) {
//return o;
return o.indexOf(".") > -1 ? o.replace(gnr2,"").replace(gnr1,".$1") : o;
}
-return function general_fmt_num(v, opts) {
+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);
@@ -133,7 +133,7 @@ return function general_fmt_num(v, opts) {
return gfn5(gfn4(o));
};})();
SSF._general_num = general_fmt_num;
-function general_fmt(v, opts) {
+function general_fmt(v/*:any*/, opts/*:?any*/) {
switch(typeof v) {
case 'string': return v;
case 'boolean': return v ? "TRUE" : "FALSE";
@@ -143,7 +143,7 @@ function general_fmt(v, opts) {
}
SSF._general = general_fmt;
function fix_hijri(date, o) { return 0; }
-function parse_date_code(v,opts,b2) {
+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=[];
@@ -159,8 +159,8 @@ function parse_date_code(v,opts,b2) {
else if(date === 0) {dout = b2 ? [1317,8,29] : [1900,1,0]; dow=6;}
else {
if(date > 60) --date;
- /* 1 = Jan 1 1900 */
- var d = new Date(1900,0,1);
+ /* 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();
@@ -176,7 +176,7 @@ function parse_date_code(v,opts,b2) {
}
SSF.parse_date_code = parse_date_code;
/*jshint -W086 */
-function write_date(type, fmt, val, ss0) {
+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 */
@@ -222,6 +222,7 @@ function write_date(type, fmt, val, ss0) {
}
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));
@@ -245,7 +246,7 @@ function write_date(type, fmt, val, ss0) {
if(outl > 0) return pad0(out, outl); else return "";
}
/*jshint +W086 */
-function commaify(s) {
+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);
@@ -253,17 +254,17 @@ function commaify(s) {
}
var write_num = (function make_write_num(){
var pct1 = /%/g;
-function write_num_pct(type, fmt, val){
+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, fmt, val){
+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, val){
- var o;
+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');
@@ -272,33 +273,33 @@ function write_num_exp(fmt, val){
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[0] + "." + o.substr(1) + "E+" + (fakee - o.length+ee);
+ 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[0] + o.substr(2,period) + "." + o.substr(2+period);
+ 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[o.length-1];
+ 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, aval, sign) {
- var den = parseInt(r[4]), rr = Math.round(aval * den), base = Math.floor(rr/den);
+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, aval, sign) {
+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) {
+function hashq(str/*:string*/)/*:string*/ {
var o = "", cc;
for(var i = 0; i != str.length; ++i) switch((cc=str.charCodeAt(i))) {
case 35: break;
@@ -308,10 +309,10 @@ function hashq(str) {
}
return o;
}
-function rnd(val, d) { var dd = Math.pow(10,d); return ""+(Math.round(val * dd)/dd); }
-function dec(val, d) { return Math.round((val-Math.floor(val))*Math.pow(10,d)); }
-function flr(val) { if(val < 2147483647 && val > -2147483648) return ""+(val >= 0 ? (val|0) : (val-1|0)); return ""+Math.floor(val); }
-function write_num_flt(type, fmt, val) {
+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*/ { return Math.round((val-Math.floor(val))*Math.pow(10,d)); }
+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);
@@ -321,67 +322,68 @@ function write_num_flt(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, oo;
- var r, ri, ff, aval = Math.abs(val), sign = val < 0 ? "-" : "";
+ 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)) !== null) return write_num_f1(r, aval, sign);
- if(fmt.match(/^#+0+$/) !== null) return sign + pad0r(aval,fmt.length - fmt.indexOf("0"));
- if((r = fmt.match(dec1)) !== null) {
+ 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*)\.(#*)$/)) !== null) {
+ 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(\.?)$/)) !== null) return sign + commaify(pad0r(aval,0));
- if((r = fmt.match(/^#,##0\.([#0]*0)$/)) !== null) {
+ 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))) + "." + pad0(dec(val, r[1].length),r[1].length);
}
- if((r = fmt.match(/^#,#*,#0/)) !== null) return write_num_flt(type,fmt.replace(/^#,#*,/,""),val);
- if((r = fmt.match(/^([0#]+)(\\?-([0#]+))+$/)) !== null) {
+ 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);
@@ -437,32 +439,33 @@ function write_num_int(type, fmt, val) {
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)) !== null) return write_num_f2(r, aval, sign);
- if(fmt.match(/^#+0+$/) !== null) return sign + pad0(aval,fmt.length - fmt.indexOf("0"));
- if((r = fmt.match(dec1)) !== null) {
+ 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*)\.(#*)$/)) !== null) {
+ 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(\.?)$/)) !== null) return sign + commaify((""+aval));
- if((r = fmt.match(/^#,##0\.([#0]*0)$/)) !== null) {
+ 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/)) !== null) return write_num_int(type,fmt.replace(/^#,#*,/,""),val);
- if((r = fmt.match(/^([0#]+)(\\?-([0#]+))+$/)) !== null) {
+ 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: /* '"' */
@@ -520,12 +523,12 @@ function split_fmt(fmt) {
}
SSF._split = split_fmt;
var abstime = /\[[HhMmSs]*\]/;
-function eval_fmt(fmt, v, opts, flen) {
+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[i])) {
+ 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;
@@ -615,11 +618,13 @@ function eval_fmt(fmt, v, opts, flen) {
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;
@@ -629,8 +634,9 @@ function eval_fmt(fmt, v, opts, flen) {
for(i=0; i < out.length; ++i) {
switch(out[i].t) {
case 't': case 'T': case ' ': case 'D': break;
- case 'X': out[i] = undefined; 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 '?':
@@ -642,7 +648,7 @@ function eval_fmt(fmt, v, opts, flen) {
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] = undefined; ++jj;
+ out[jj] = {v:"", t:";"}; ++jj;
}
nstr += out[i].v;
i = jj-1; break;
@@ -721,11 +727,11 @@ function chkcond(v, rr) {
}
return false;
}
-function choose_fmt(f, v) {
+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 "cannot find right format for |" + fmt + "|";
+ 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;
@@ -742,12 +748,12 @@ function choose_fmt(f, v) {
}
return [l, ff];
}
-function format(fmt,v,o) {
+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 : table_fmt)[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);
@@ -757,10 +763,12 @@ function format(fmt,v,o) {
return eval_fmt(f[1], v, o, f[0]);
}
SSF._table = table_fmt;
-SSF.load = function load_entry(fmt, idx) { table_fmt[idx] = 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) { for(var i=0; i!=0x0188; ++i) if(tbl[i] !== undefined) SSF.load(tbl[i], i); };
+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.md b/ssf.md
index 2c14a0f..89916af 100644
--- a/ssf.md
+++ b/ssf.md
@@ -10,7 +10,7 @@ default options are described below:
```js>tmp/10_opts.js
/* Options */
-var opts_fmt = [
+var opts_fmt/*:Array >*/ = [
```
There are two commonly-recognized date code formats:
@@ -60,8 +60,8 @@ 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
-function split_fmt(fmt) {
- var out = [];
+function split_fmt(fmt/*:string*/)/*:Array*/ {
+ 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: /* '"' */
@@ -117,26 +117,26 @@ as the last format.
## Utility Functions
```js>tmp/02_utilities.js
-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 _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; }
```
The next few helpers break up the general `pad` function into special cases:
```
-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;}
+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,d){if(v>p2_32||v<-p2_32) return pad0r1(v,d); var i = Math.round(v); return pad0r2(i,d); }
+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); }
```
Comparing against the string "general" is faster via char codes:
```
-function isgeneral(s, i) { 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 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; }
```
## General Number Format
@@ -149,7 +149,7 @@ 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
-function general_fmt_int(v, opts) { return ""+v; }
+function general_fmt_int(v/*:number*/, opts/*:?any*/)/*:string*/ { return ""+v; }
SSF._general_int = general_fmt_int;
```
@@ -178,7 +178,7 @@ function gfn5(o) {
//return o;
return o.indexOf(".") > -1 ? o.replace(gnr2,"").replace(gnr1,".$1") : o;
}
-return function general_fmt_num(v, opts) {
+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);
@@ -192,7 +192,7 @@ SSF._general_num = general_fmt_num;
Finally
```js>tmp/40_general.js
-function general_fmt(v, opts) {
+function general_fmt(v/*:any*/, opts/*:?any*/) {
switch(typeof v) {
```
@@ -230,16 +230,16 @@ None of the international formats are included here.
```js>tmp/20_consts.js
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: '# ??/??',
+ /*::[*/0/*::]*/: 'General',
+ /*::[*/1/*::]*/: '0',
+ /*::[*/2/*::]*/: '0.00',
+ /*::[*/3/*::]*/: '#,##0',
+ /*::[*/4/*::]*/: '#,##0.00',
+ /*::[*/9/*::]*/: '0%',
+ /*::[*/10/*::]*/: '0.00%',
+ /*::[*/11/*::]*/: '0.00E+00',
+ /*::[*/12/*::]*/: '# ?/?',
+ /*::[*/13/*::]*/: '# ??/??',
```
Now Excel and other formats treat code 14 as `m/d/yy` (with slashes). Given
@@ -247,37 +247,37 @@ that the spec gives no internationalization considerations, erring on the side
of the applications makes sense here:
```
- 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: '@',
+ /*::[*/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/*::]*/: '@',
```
There are special implicit format codes identified in [ECMA-376] 18.8.30.
Assuming zh-tw is the default:
```
- 56: '"上午/下午 "hh"時"mm"分"ss"秒 "',
+ /*::[*/56/*::]*/: '"上午/下午 "hh"時"mm"分"ss"秒 "',
```
some writers erroneously emit 65535 for general:
```
- 65535: 'General'
+ /*::[*/65535/*::]*/: 'General'
};
```
@@ -326,7 +326,7 @@ portion of a 24 hour day).
Excel supports the alternative Hijri calendar (indicated with `b2`):
```js>tmp/50_date.js
-function parse_date_code(v,opts,b2) {
+function parse_date_code(v/*:number*/,opts/*:?any*/,b2/*:?boolean*/) {
```
Date codes beyond 12/31/9999 are invalid:
@@ -378,8 +378,8 @@ For the other dates, using the JS date mechanism suffices.
```
else {
if(date > 60) --date;
- /* 1 = Jan 1 1900 */
- var d = new Date(1900,0,1);
+ /* 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();
@@ -426,7 +426,7 @@ function fix_hijri(date, o) { return 0; }
The utility `commaify` adds commas to integers:
```js>tmp/56_commaify.js
-function commaify(s) {
+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);
@@ -446,7 +446,7 @@ The underlying number for the percentages should be physically shifted:
```js>tmp/59_numhelp.js
var pct1 = /%/g;
-function write_num_pct(type, fmt, val){
+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);
}
@@ -458,7 +458,7 @@ Formats with multiple commas after the decimal point should be shifted by the
appropiate multiple of 1000 (more magic):
```js>tmp/60_number.js
-function write_num_cm(type, fmt, val){
+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)));
@@ -470,8 +470,8 @@ function write_num_cm(type, fmt, val){
For exponents, get the exponent and mantissa and format them separately:
```
-function write_num_exp(fmt, val){
- var o;
+function write_num_exp(fmt/*:string*/, val/*:number*/)/*:string*/{
+ var o/*:string*/;
var idx = fmt.indexOf("E") - fmt.indexOf(".") - 1;
```
@@ -490,17 +490,17 @@ TODO: something cleaner
```
var fakee = Math.floor(Math.log(Math.abs(val))*Math.LOG10E);
- if(o.indexOf(".") === -1) o = o[0] + "." + o.substr(1) + "E+" + (fakee - o.length+ee);
+ 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[0] + o.substr(2,period) + "." + o.substr(2+period);
+ 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[o.length-1];
+ 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");
}
@@ -510,18 +510,18 @@ TODO: something cleaner
```
var frac1 = /# (\?+)( ?)\/( ?)(\d+)/;
-function write_num_f1(r, aval, sign) {
- var den = parseInt(r[4]), rr = Math.round(aval * den), base = Math.floor(rr/den);
+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, aval, sign) {
+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) {
+function hashq(str/*:string*/)/*:string*/ {
var o = "", cc;
for(var i = 0; i != str.length; ++i) switch((cc=str.charCodeAt(i))) {
case 35: break;
@@ -536,9 +536,9 @@ function hashq(str) {
V8 has an annoying habit of deoptimizing round and floor
```
-function rnd(val, d) { var dd = Math.pow(10,d); return ""+(Math.round(val * dd)/dd); }
-function dec(val, d) { return Math.round((val-Math.floor(val))*Math.pow(10,d)); }
-function flr(val) { if(val < 2147483647 && val > -2147483648) return ""+(val >= 0 ? (val|0) : (val-1|0)); return ""+Math.floor(val); }
+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*/ { return Math.round((val-Math.floor(val))*Math.pow(10,d)); }
+function flr(val/*:number*/)/*:string*/ { if(val < 2147483647 && val > -2147483648) return ""+(val >= 0 ? (val|0) : (val-1|0)); return ""+Math.floor(val); }
```
### Main Number Writing Function
@@ -546,7 +546,7 @@ function flr(val) { if(val < 2147483647 && val > -2147483648) return ""+(val >=
Finally the body:
```
-function write_num_flt(type, fmt, val) {
+function write_num_flt(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string*/ {
```
For parentheses, explicitly resolve the sign issue:
@@ -579,8 +579,8 @@ TODO: localize the currency:
Some simple cases should be resolved first:
```
- var o, oo;
- var r, ri, ff, aval = Math.abs(val), sign = val < 0 ? "-" : "";
+ 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 = "";
@@ -591,14 +591,15 @@ Some simple cases should be resolved first:
Fractions with known denominator are resolved by rounding:
```
- if((r = fmt.match(frac1)) !== null) return write_num_f1(r, aval, sign);
+ if((r = fmt.match(frac1))) return write_num_f1(r, aval, sign);
```
A few special general cases can be handled in a very dumb manner:
```
- if(fmt.match(/^#+0+$/) !== null) return sign + pad0r(aval,fmt.length - fmt.indexOf("0"));
- if((r = fmt.match(dec1)) !== null) {
+ 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\./,".");
}
@@ -608,20 +609,20 @@ The next few simplifications ignore leading optional sigils (`#`):
```
fmt = fmt.replace(/^#+([0.])/, "$1");
- if((r = fmt.match(/^(0*)\.(#*)$/)) !== null) {
+ 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(\.?)$/)) !== null) return sign + commaify(pad0r(aval,0));
- if((r = fmt.match(/^#,##0\.([#0]*0)$/)) !== null) {
+ 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))) + "." + pad0(dec(val, r[1].length),r[1].length);
}
- if((r = fmt.match(/^#,#*,#0/)) !== null) return write_num_flt(type,fmt.replace(/^#,#*,/,""),val);
+ if((r = fmt.match(/^#,#*,#0/))) return write_num_flt(type,fmt.replace(/^#,#*,/,""),val);
```
The `Zip Code + 4` format needs to treat an interstitial hyphen as a character:
```
- if((r = fmt.match(/^([0#]+)(\\?-([0#]+))+$/)) !== null) {
+ 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 ritmp/60_number.js
- if((r = fmt.match(/^00,000\.([#0]*0)$/)) !== null) {
+ if((r = fmt.match(/^00,000\.([#0]*0)$/))) {
ri = dec(val, r[1].length);
```
@@ -706,17 +707,17 @@ For now, the default case is an error:
### Integer Optimizations
```
-function write_num_cm2(type, fmt, val){
+function write_num_cm2(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_pct2(type, fmt, val){
+function write_num_pct2(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_exp2(fmt, val){
- var o;
+function write_num_exp2(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');
@@ -725,17 +726,17 @@ function write_num_exp2(fmt, val){
o = (val/Math.pow(10,ee)).toPrecision(idx+1+(period+ee)%period);
if(!o.match(/[Ee]/)) {
var fakee = Math.floor(Math.log(Math.abs(val))*Math.LOG10E);
- if(o.indexOf(".") === -1) o = o[0] + "." + o.substr(1) + "E+" + (fakee - o.length+ee);
+ if(o.indexOf(".") === -1) o = o.charAt(0) + "." + o.substr(1) + "E+" + (fakee - o.length+ee);
else o += "E+" + (fakee - ee);
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[o.length-1];
+ 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_int(type, fmt, val) {
+function write_num_int(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_int('n', ffmt, val);
@@ -752,32 +753,33 @@ function write_num_int(type, fmt, val) {
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)) !== null) return write_num_f2(r, aval, sign);
- if(fmt.match(/^#+0+$/) !== null) return sign + pad0(aval,fmt.length - fmt.indexOf("0"));
- if((r = fmt.match(dec1)) !== null) {
+ 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*)\.(#*)$/)) !== null) {
+ 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(\.?)$/)) !== null) return sign + commaify((""+aval));
- if((r = fmt.match(/^#,##0\.([#0]*0)$/)) !== null) {
+ 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/)) !== null) return write_num_int(type,fmt.replace(/^#,#*,/,""),val);
- if((r = fmt.match(/^([0#]+)(\\?-([0#]+))+$/)) !== null) {
+ 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 ritmp/82_eval.js
var abstime = /\[[HhMmSs]*\]/;
-function eval_fmt(fmt, v, opts, flen) {
+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[i])) {
+ switch((c = fmt.charAt(i))) {
```
LO Formats sometimes leak "GENERAL" or "General" to stand for general format:
@@ -1045,11 +1047,13 @@ Having determined the smallest time unit, round appropriately:
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;
@@ -1065,8 +1069,9 @@ group them together to construct the real number string:
for(i=0; i < out.length; ++i) {
switch(out[i].t) {
case 't': case 'T': case ' ': case 'D': break;
- case 'X': out[i] = undefined; 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 '?':
@@ -1078,7 +1083,7 @@ group them together to construct the real number string:
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] = undefined; ++jj;
+ out[jj] = {v:"", t:";"}; ++jj;
}
nstr += out[i].v;
i = jj-1; break;
@@ -1183,7 +1188,7 @@ display minutes instead of the month.
```js>tmp/50_date.js
/*jshint -W086 */
-function write_date(type, fmt, val, ss0) {
+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) {
```
@@ -1260,6 +1265,7 @@ terms. That is passed via the `ss0` parameter:
}
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));
@@ -1306,11 +1312,11 @@ 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
-function choose_fmt(f, v) {
+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 "cannot find right format for |" + fmt + "|";
+ if(fmt.length > 4) throw new Error("cannot find right format for |" + fmt.join("|") + "|");
```
Short-circuit the string case by using the last format if it has "@":
@@ -1375,7 +1381,7 @@ The main function checks for conditional operators and acts accordingly:
Finally, the format wrapper brings everything together:
```
-function format(fmt,v,o) {
+function format(fmt/*:string|number*/,v/*:any*/,o/*:?any*/) {
fixopts(o != null ? o : (o=[]));
```
@@ -1385,7 +1391,7 @@ The string format is saved to a different variable:
var sfmt = "";
switch(typeof fmt) {
case "string": sfmt = fmt; break;
- case "number": sfmt = (o.table != null ? o.table : table_fmt)[fmt]; break;
+ case "number": sfmt = (o.table != null ? (o.table/*:any*/) : table_fmt)[fmt]; break;
}
```
@@ -1416,7 +1422,7 @@ used directly in programs.
```js>tmp/98_exports.js
SSF._table = table_fmt;
-SSF.load = function load_entry(fmt, idx) { table_fmt[idx] = fmt; };
+SSF.load = function load_entry(fmt/*:string*/, idx/*:number*/) { table_fmt[idx] = fmt; };
SSF.format = format;
```
@@ -1424,7 +1430,7 @@ To support multiple SSF tables:
```
SSF.get_table = function get_table() { return table_fmt; };
-SSF.load_table = function load_table(tbl) { for(var i=0; i!=0x0188; ++i) if(tbl[i] !== undefined) SSF.load(tbl[i], i); };
+SSF.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); };
```
## Fraction Library
@@ -1459,7 +1465,7 @@ function frac(x, D, mixed) {
## JS Boilerplate
```js>tmp/00_header.js
-/* ssf.js (C) 2013-2014 SheetJS -- http://sheetjs.com */
+/* ssf.js (C) 2013-present SheetJS -- http://sheetjs.com */
/*jshint -W041 */
var SSF = {};
var make_ssf = function make_ssf(SSF){
@@ -1468,6 +1474,8 @@ var make_ssf = function make_ssf(SSF){
```js>tmp/99_footer.js
};
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;
```
@@ -1486,294 +1494,3 @@ cat tmp/*.js > ssf.js
}
```
-```>.gitignore
-node_modules/
-tmp/
-.vocrc
-v8.log
-perf.log
-```
-
-```>.npmignore
-test/*.tsv
-node_modules/
-tmp/
-.gitignore
-.vocrc
-v8.log
-perf.log
-```
-
-```make>Makefile
-.PHONY: test ssf
-ssf: ssf.md
- voc ssf.md
-
-test:
- npm test
-
-test_min:
- MINTEST=1 npm test
-
-.PHONY: lint
-lint:
- jshint ssf.js test/
- jscs ssf.js
-
-.PHONY: perf
-perf:
- bash misc/perf.sh
-```
-
-Coverage tests use [blanket](http://npm.im/blanket):
-
-```
-
-.PHONY: cov
-cov: tmp/coverage.html
-
-tmp/coverage.html: ssf
- mocha --require blanket -R html-cov > tmp/coverage.html
-
-.PHONY: cov_min
-cov_min:
- MINTEST=1 make cov
-```
-
-Coveralls.io support
-
-```
-
-.PHONY: coveralls full_coveralls
-full_coveralls:
- mocha --require blanket --reporter mocha-lcov-reporter | ./node_modules/coveralls/bin/coveralls.js
-
-coveralls:
- MINTEST=1 make full_coveralls
-
-```
-
-```json>package.json
-{
- "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" }
-}
-```
-
-# Test Driver
-
-Travis CI is used for node testing:
-
-```>.travis.yml
-language: node_js
-node_js:
- - "0.10"
- - "0.8"
-before_install:
- - "npm install -g mocha"
- - "npm install blanket"
- - "npm install coveralls mocha-lcov-reporter"
-after_success:
- - "make coveralls"
-```
-
-The mocha test driver tests the implied formats:
-
-```js>test/implied.js
-var SSF = require('../');
-var fs = require('fs'), assert = require('assert');
-var data = JSON.parse(fs.readFileSync('./test/implied.json','utf8'));
-var skip = [];
-function doit(d) {
- d[1].forEach(function(r){if(r.length === 2)assert.equal(SSF.format(r[0],d[0]),r[1]);});
-}
-describe('implied formats', function() {
- data.forEach(function(d) {
- if(d.length == 2) it(d[0], function() { doit(d); });
- else it(d[1]+" for "+d[0], skip.indexOf(d[1]) > -1 ? null : function(){
- assert.equal(SSF.format(d[1], d[0], {}), d[2]);
- });
- });
-});
-```
-
-The general test driver tests the General format:
-
-```js>test/general.js
-/* vim: set ts=2: */
-var SSF = require('../');
-var fs = require('fs'), assert = require('assert');
-var data = JSON.parse(fs.readFileSync('./test/general.json','utf8'));
-var skip = [];
-describe('General format', function() {
- data.forEach(function(d) {
- it(d[1]+" for "+d[0], skip.indexOf(d[1]) > -1 ? null : function(){
- assert.equal(SSF.format(d[1], d[0], {}), d[2]);
- });
- });
- it('should fail for undefined and null', function() {
- assert.throws(function() {
- SSF.format("General", undefined);
- SSF.format("General", null);
- });
- });
-});
-```
-
-The fraction test driver tests fractional formats:
-
-```js>test/fraction.js
-/* vim: set ts=2: */
-var SSF = require('../');
-var fs = require('fs'), assert = require('assert');
-var data = JSON.parse(fs.readFileSync('./test/fraction.json','utf8'));
-var skip = [];
-describe('fractional formats', function() {
- data.forEach(function(d) {
- 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 r = actual.match(/(-?)\d* *\d+\/\d+/);
- assert.equal(actual, expected);
- });
- });
-});
-```
-
-The dates test driver tests the date and time formats:
-
-```js>test/date.js
-/* vim: set ts=2: */
-/*jshint -W041 */
-/*jshint loopfunc:true */
-var SSF = require('../');
-var fs = require('fs'), assert = require('assert');
-var dates = fs.readFileSync('./test/dates.tsv','utf8').split("\n");
-var date2 = fs.readFileSync('./test/cal.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] == null || data[i].length < 3) 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], parseFloat(d[0]), {});
- if(actual != expected) throw [actual, expected, w, headers[w],d[0],d,i].join("|");
- actual = SSF.format(headers[w].toUpperCase(), parseFloat(d[0]), {});
- if(actual != expected) throw [actual, expected, w, headers[w].toUpperCase(),d[0],d,i].join("|");
- }
- }
- });
-}
-describe('time formats', function() {
- doit(process.env.MINTEST ? times.slice(0,4000) : times);
-});
-describe('date formats', function() {
- doit(process.env.MINTEST ? dates.slice(0,4000) : dates);
- //doit(process.env.MINTEST ? date2.slice(0,1000) : date2);
- it('should fail for bad formats', function() {
- var bad = [];
- 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 = 1; w < headers.length; ++w) {
- var expected = d[w].replace("|", ""), actual;
- try { actual = SSF.format(headers[w], parseFloat(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=1;jtest/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(var j=1;jLICENSE
-Copyright (C) 2013-2014 SheetJS
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-```
diff --git a/test/comma.js b/test/comma.js
index ce0270f..4a9e311 100644
--- a/test/comma.js
+++ b/test/comma.js
@@ -1,7 +1,7 @@
/* vim: set ts=2: */
-/*jshint loopfunc:true */
+/*jshint loopfunc:true, mocha:true, node:true */
var SSF = require('../');
-var fs = require('fs'), assert = require('assert');
+var fs = require('fs');
var data = fs.readFileSync('./test/comma.tsv','utf8').split("\n");
function doit(w, headers) {
@@ -11,7 +11,7 @@ function doit(w, headers) {
var d = data[j].replace(/#{255}/g,"").split("\t");
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("|");
+ if(actual != expected && d[w][0] !== "|") throw new Error([actual, expected, w, headers[w],d[0],d].join("|"));
}
});
}
diff --git a/test/date.js b/test/date.js
index 4753a59..f854f5d 100644
--- a/test/date.js
+++ b/test/date.js
@@ -1,6 +1,6 @@
/* vim: set ts=2: */
/*jshint -W041 */
-/*jshint loopfunc:true */
+/*jshint loopfunc:true, mocha:true, node:true */
var SSF = require('../');
var fs = require('fs'), assert = require('assert');
var dates = fs.readFileSync('./test/dates.tsv','utf8').split("\n");
@@ -9,25 +9,27 @@ 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 j = 0; j <= 100; ++j) it(String(j), function() {
for(var k = 0; k <= step; ++k,++i) {
if(data[i] == null || data[i].length < 3) 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], parseFloat(d[0]), {});
- if(actual != expected) throw [actual, expected, w, headers[w],d[0],d,i].join("|");
+ if(actual != expected) throw new Error([actual, expected, w, headers[w],d[0],d,i].join("|"));
actual = SSF.format(headers[w].toUpperCase(), parseFloat(d[0]), {});
- if(actual != expected) throw [actual, expected, w, headers[w].toUpperCase(),d[0],d,i].join("|");
+ if(actual != expected) throw new Error([actual, expected, w, headers[w].toUpperCase(),d[0],d,i].join("|"));
}
}
});
}
+
describe('time formats', function() {
doit(process.env.MINTEST ? times.slice(0,4000) : times);
});
+
describe('date formats', function() {
doit(process.env.MINTEST ? dates.slice(0,4000) : dates);
- //doit(process.env.MINTEST ? date2.slice(0,1000) : date2);
+ if(0) doit(process.env.MINTEST ? date2.slice(0,1000) : date2);
it('should fail for bad formats', function() {
var bad = [];
var chk = function(fmt){ return function(){ SSF.format(fmt,0); }; };
diff --git a/test/exp.js b/test/exp.js
index 1b73676..fec0ce8 100644
--- a/test/exp.js
+++ b/test/exp.js
@@ -1,14 +1,14 @@
/* vim: set ts=2: */
-/*jshint loopfunc:true */
+/*jshint loopfunc:true, mocha:true, node:true */
var SSF = require('../');
-var fs = require('fs'), assert = require('assert');
+var fs = require('fs');
var data = fs.readFileSync('./test/exp.tsv','utf8').split("\n");
function doit(d, headers) {
it(d[0], function() {
for(var w = 1; w < headers.length; ++w) {
var expected = d[w].replace("|", ""), actual;
try { actual = SSF.format(headers[w], parseFloat(d[0]), {}); } catch(e) { }
- if(actual != expected && d[w][0] !== "|") throw [actual, expected, w, headers[w],d[0],d].join("|");
+ if(actual != expected && d[w][0] !== "|") throw new Error([actual, expected, w, headers[w],d[0],d].join("|"));
}
});
}
diff --git a/test/fraction.js b/test/fraction.js
index 6b75cbf..a0deff2 100644
--- a/test/fraction.js
+++ b/test/fraction.js
@@ -1,4 +1,5 @@
/* vim: set ts=2: */
+/*jshint loopfunc:true, mocha:true, node:true */
var SSF = require('../');
var fs = require('fs'), assert = require('assert');
var data = JSON.parse(fs.readFileSync('./test/fraction.json','utf8'));
diff --git a/test/general.js b/test/general.js
index 99a109e..10e71a9 100644
--- a/test/general.js
+++ b/test/general.js
@@ -1,4 +1,5 @@
/* vim: set ts=2: */
+/*jshint loopfunc:true, mocha:true, node:true */
var SSF = require('../');
var fs = require('fs'), assert = require('assert');
var data = JSON.parse(fs.readFileSync('./test/general.json','utf8'));
@@ -9,10 +10,9 @@ describe('General format', function() {
assert.equal(SSF.format(d[1], d[0], {}), d[2]);
});
});
- it('should fail for undefined and null', function() {
- assert.throws(function() {
- SSF.format("General", undefined);
- SSF.format("General", null);
- });
+ it.skip('should handle special values', function() {
+ assert.equal(SSF.format("General", true), "TRUE");
+ assert.equal(SSF.format("General", undefined), "");
+ assert.equal(SSF.format("General", null), "");
});
});
diff --git a/test/implied.js b/test/implied.js
index e2105ce..29e2c34 100644
--- a/test/implied.js
+++ b/test/implied.js
@@ -1,3 +1,5 @@
+/* vim: set ts=2: */
+/*jshint loopfunc:true, mocha:true, node:true */
var SSF = require('../');
var fs = require('fs'), assert = require('assert');
var data = JSON.parse(fs.readFileSync('./test/implied.json','utf8'));
@@ -7,7 +9,7 @@ function doit(d) {
}
describe('implied formats', function() {
data.forEach(function(d) {
- if(d.length == 2) it(d[0], function() { doit(d); });
+ if(d.length == 2) it(String(d[0]), function() { doit(d); });
else it(d[1]+" for "+d[0], skip.indexOf(d[1]) > -1 ? null : function(){
assert.equal(SSF.format(d[1], d[0], {}), d[2]);
});
diff --git a/test/oddities.js b/test/oddities.js
index 8c8728a..fb59c85 100644
--- a/test/oddities.js
+++ b/test/oddities.js
@@ -1,11 +1,11 @@
/* vim: set ts=2: */
-/*jshint loopfunc:true */
+/*jshint loopfunc:true, mocha:true, node: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(){
+ it(String(d[0]), function(){
for(var j=1;j