forked from sheetjs/sheetjs
		
	version bump 0.10.0:
- pin dependencies - JS Date object support - resolved some out of bounds accesses - load scans for available index if not specified - flow improvements
This commit is contained in:
		
							parent
							
								
									aa25491492
								
							
						
					
					
						commit
						80c1a0fec7
					
				
							
								
								
									
										17
									
								
								.travis.yml
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										17
									
								
								.travis.yml
									
									
									
									
									
								
							| @ -1,5 +1,6 @@ | ||||
| language: node_js | ||||
| node_js: | ||||
|   - "8" | ||||
|   - "7" | ||||
|   - "6" | ||||
|   - "5" | ||||
| @ -8,6 +9,22 @@ node_js: | ||||
|   - "0.10" | ||||
|   - "0.9" | ||||
|   - "0.8" | ||||
| matrix: | ||||
|   include: | ||||
|     - node_js: "6" | ||||
|       env: TZ="America/New_York" | ||||
|     - node_js: "8" | ||||
|       env: TZ="America/Los_Angeles" | ||||
|     - node_js: "6" | ||||
|       env: TZ="Europe/London" | ||||
|     - node_js: "8" | ||||
|       env: TZ="Europe/Berlin" | ||||
|     - node_js: "6" | ||||
|       env: TZ="Asia/Kolkata" | ||||
|     - node_js: "7" | ||||
|       env: TZ="Asia/Shanghai" | ||||
|     - node_js: "8" | ||||
|       env: TZ="Asia/Seoul" | ||||
| before_install: | ||||
|   - "npm install -g npm@4.3.0" | ||||
|   - "npm install -g mocha@2.x voc" | ||||
|  | ||||
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							| @ -51,7 +51,7 @@ travis: ## Run test suite with minimal output | ||||
| 
 | ||||
| .PHONY: ctest | ||||
| ctest: ## Build browser test fixtures
 | ||||
| 	browserify -t brfs test/{dateNF,exp,fraction,general,implied,oddities,utilities,comma,valid}.js > ctest/test.js | ||||
| 	browserify -t brfs test/{dateNF,dates,exp,fraction,general,implied,oddities,utilities,comma,valid}.js > ctest/test.js | ||||
| 
 | ||||
| .PHONY: ctestserv | ||||
| ctestserv: ## Start a test server on port 8000
 | ||||
|  | ||||
							
								
								
									
										16
									
								
								README.md
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										16
									
								
								README.md
									
									
									
									
									
								
							| @ -29,9 +29,19 @@ define `DO_NOT_EXPORT_SSF`. | ||||
| 
 | ||||
| ## Usage | ||||
| 
 | ||||
| `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. | ||||
| `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. | ||||
| For a raw numeric format like `000`, the value should be passed as a string. | ||||
| 
 | ||||
| Date arguments are interpreted in the local time of the JS client. | ||||
| 
 | ||||
| The options argument may contain the following keys: | ||||
| 
 | ||||
| | Option Name | Default | Description                                          | | ||||
| | :---------- | :-----: | :--------------------------------------------------- | | ||||
| | date1904    | false   | Use 1904 date system if true, 1900 system if false   | | ||||
| 
 | ||||
| ### Manipulating the Internal Format Table | ||||
| 
 | ||||
|  | ||||
| @ -1,4 +1,6 @@ | ||||
| /* ssf.js (C) 2013-present SheetJS -- http://sheetjs.com */ | ||||
| /* vim: set ts=2: */ | ||||
| /*jshint -W041 */ | ||||
| var SSF = {}; | ||||
| var make_ssf = function make_ssf(SSF){ | ||||
| /*:: declare var DO_NOT_EXPORT_SSF: any; */ | ||||
| var SSF/*:SSFModule*/ = ({}/*:any*/); | ||||
| var make_ssf = function make_ssf(SSF/*:SSFModule*/){ | ||||
|  | ||||
| @ -1 +1 @@ | ||||
| SSF.version = '0.9.4'; | ||||
| SSF.version = '0.10.0'; | ||||
|  | ||||
| @ -1,10 +0,0 @@ | ||||
| /* Options */ | ||||
| var opts_fmt/*:Array<Array<any> >*/ = [ | ||||
| 	["date1904", 0], | ||||
| 	["output", ""], | ||||
| 	["WTF", false] | ||||
| ]; | ||||
| function fixopts(o){ | ||||
| 	for(var y = 0; y != opts_fmt.length; ++y) if(o[opts_fmt[y][0]]===undefined) o[opts_fmt[y][0]]=opts_fmt[y][1]; | ||||
| } | ||||
| SSF.opts = opts_fmt; | ||||
							
								
								
									
										3
									
								
								bits/10_types.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										3
									
								
								bits/10_types.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| /*:: | ||||
| type SSF_write_num = {(type:string, fmt:string, val:number):string}; | ||||
| */ | ||||
							
								
								
									
										40
									
								
								bits/35_datecode.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										40
									
								
								bits/35_datecode.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,40 @@ | ||||
| function parse_date_code(v/*:number*/,opts/*:?any*/,b2/*:?boolean*/) { | ||||
| 	if(v > 2958465 || v < 0) return null; | ||||
| 	var date = (v|0), time = Math.floor(86400 * (v - date)), dow=0; | ||||
| 	var dout=[]; | ||||
| 	var out={D:date, T:time, u:86400*(v-date)-time,y:0,m:0,d:0,H:0,M:0,S:0,q:0}; | ||||
| 	if(Math.abs(out.u) < 1e-6) out.u = 0; | ||||
| 	if(opts && opts.date1904) date += 1462; | ||||
| 	if(out.u > 0.9999) { | ||||
| 		out.u = 0; | ||||
| 		if(++time == 86400) { out.T = time = 0; ++date; ++out.D; } | ||||
| 	} | ||||
| 	if(date === 60) {dout = b2 ? [1317,10,29] : [1900,2,29]; dow=3;} | ||||
| 	else if(date === 0) {dout = b2 ? [1317,8,29] : [1900,1,0]; dow=6;} | ||||
| 	else { | ||||
| 		if(date > 60) --date; | ||||
| 		/* 1 = Jan 1 1900 in Gregorian */ | ||||
| 		var d = new Date(1900, 0, 1); | ||||
| 		d.setDate(d.getDate() + date - 1); | ||||
| 		dout = [d.getFullYear(), d.getMonth()+1,d.getDate()]; | ||||
| 		dow = d.getDay(); | ||||
| 		if(date < 60) dow = (dow + 6) % 7; | ||||
| 		if(b2) dow = fix_hijri(d, dout); | ||||
| 	} | ||||
| 	out.y = dout[0]; out.m = dout[1]; out.d = dout[2]; | ||||
| 	out.S = time % 60; time = Math.floor(time / 60); | ||||
| 	out.M = time % 60; time = Math.floor(time / 60); | ||||
| 	out.H = time; | ||||
| 	out.q = dow; | ||||
| 	return out; | ||||
| } | ||||
| SSF.parse_date_code = parse_date_code; | ||||
| var basedate = new Date(1899, 11, 31, 0, 0, 0); | ||||
| var dnthresh = basedate.getTime(); | ||||
| var base1904 = new Date(1900, 2, 1, 0, 0, 0); | ||||
| function datenum_local(v/*:Date*/, date1904/*:?boolean*/)/*:number*/ { | ||||
| 	var epoch = v.getTime(); | ||||
| 	if(date1904) epoch -= 1461*24*60*60*1000; | ||||
| 	else if(v >= base1904) epoch += 24*60*60*1000; | ||||
| 	return (epoch - (dnthresh + (v.getTimezoneOffset() - basedate.getTimezoneOffset()) * 60000)) / (24 * 60 * 60 * 1000); | ||||
| } | ||||
| @ -1,4 +1,4 @@ | ||||
| function general_fmt_int(v/*:number*/)/*:string*/ { return ""+v; } | ||||
| function general_fmt_int(v/*:number*/)/*:string*/ { return v.toString(10); } | ||||
| 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)$/; | ||||
| @ -29,13 +29,15 @@ return function general_fmt_num(v/*:number*/)/*:string*/ { | ||||
| 	return gfn5(gfn4(o)); | ||||
| };})(); | ||||
| SSF._general_num = general_fmt_num; | ||||
| function general_fmt(v/*:any*/) { | ||||
| function general_fmt(v/*:any*/, opts/*:any*/) { | ||||
| 	switch(typeof v) { | ||||
| 		case 'string': return v; | ||||
| 		case 'boolean': return v ? "TRUE" : "FALSE"; | ||||
| 		case 'number': return (v|0) === v ? general_fmt_int(v/*, opts*/) : general_fmt_num(v/*, opts*/); | ||||
| 		case 'number': return (v|0) === v ? general_fmt_int(v) : general_fmt_num(v); | ||||
| 		case 'undefined': return ""; | ||||
| 		case 'object': if(v == null) return ""; | ||||
| 		case 'object': | ||||
| 			if(v == null) return ""; | ||||
| 			if(v instanceof Date) return format(14, datenum_local(v, opts && opts.date1904), opts); | ||||
| 	} | ||||
| 	throw new Error("unsupported value in General format: " + v); | ||||
| } | ||||
|  | ||||
| @ -1 +1 @@ | ||||
| function fix_hijri(/*date, o*/) { return 0; } | ||||
| function fix_hijri(/*::date, o*/) { return 0; } | ||||
|  | ||||
| @ -1,35 +1,3 @@ | ||||
| function parse_date_code(v/*:number*/,opts/*:?any*/,b2/*:?boolean*/) { | ||||
| 	if(v > 2958465 || v < 0) return null; | ||||
| 	var date = (v|0), time = Math.floor(86400 * (v - date)), dow=0; | ||||
| 	var dout=[]; | ||||
| 	var out={D:date, T:time, u:86400*(v-date)-time,y:0,m:0,d:0,H:0,M:0,S:0,q:0}; | ||||
| 	if(Math.abs(out.u) < 1e-6) out.u = 0; | ||||
| 	fixopts(opts != null ? opts : (opts=[])); | ||||
| 	if(opts.date1904) date += 1462; | ||||
| 	if(out.u > 0.9999) { | ||||
| 		out.u = 0; | ||||
| 		if(++time == 86400) { out.T = time = 0; ++date; ++out.D; } | ||||
| 	} | ||||
| 	if(date === 60) {dout = b2 ? [1317,10,29] : [1900,2,29]; dow=3;} | ||||
| 	else if(date === 0) {dout = b2 ? [1317,8,29] : [1900,1,0]; dow=6;} | ||||
| 	else { | ||||
| 		if(date > 60) --date; | ||||
| 		/* 1 = Jan 1 1900 in Gregorian */ | ||||
| 		var d = new Date(1900, 0, 1); | ||||
| 		d.setDate(d.getDate() + date - 1); | ||||
| 		dout = [d.getFullYear(), d.getMonth()+1,d.getDate()]; | ||||
| 		dow = d.getDay(); | ||||
| 		if(date < 60) dow = (dow + 6) % 7; | ||||
| 		if(b2) dow = fix_hijri(d, dout); | ||||
| 	} | ||||
| 	out.y = dout[0]; out.m = dout[1]; out.d = dout[2]; | ||||
| 	out.S = time % 60; time = Math.floor(time / 60); | ||||
| 	out.M = time % 60; time = Math.floor(time / 60); | ||||
| 	out.H = time; | ||||
| 	out.q = dow; | ||||
| 	return out; | ||||
| } | ||||
| SSF.parse_date_code = parse_date_code; | ||||
| /*jshint -W086 */ | ||||
| function write_date(type/*:number*/, fmt/*:string*/, val, ss0/*:?number*/)/*:string*/ { | ||||
| 	var o="", ss=0, tt=0, y = val.y, out, outl = 0; | ||||
| @ -71,23 +39,17 @@ function write_date(type/*:number*/, fmt/*:string*/, val, ss0/*:?number*/)/*:str | ||||
| 			default: throw 'bad minute format: ' + fmt; | ||||
| 		} break; | ||||
| 		case 115: /* 's' seconds */ | ||||
| 		if(val.u === 0) switch(fmt) { | ||||
| 			case 's': case 'ss': return pad0(val.S, fmt.length); | ||||
| 			case '.0': case '.00': case '.000': | ||||
| 		} | ||||
| 		switch(fmt) { | ||||
| 			case 's': case 'ss': case '.0': case '.00': case '.000': | ||||
| 				/*::if(!ss0) ss0 = 0; */ | ||||
| 				if(ss0 >= 2) tt = ss0 === 3 ? 1000 : 100; | ||||
| 				else tt = ss0 === 1 ? 10 : 1; | ||||
| 				ss = Math.round((tt)*(val.S + val.u)); | ||||
| 				if(ss >= 60*tt) ss = 0; | ||||
| 				if(fmt === 's') return ss === 0 ? "0" : ""+ss/tt; | ||||
| 				o = pad0(ss,2 + ss0); | ||||
| 				if(fmt === 'ss') return o.substr(0,2); | ||||
| 				return "." + o.substr(2,fmt.length-1); | ||||
| 			default: throw 'bad second format: ' + fmt; | ||||
| 		} | ||||
| 			if(fmt != 's' && fmt != 'ss' && fmt != '.0' && fmt != '.00' && fmt != '.000') throw 'bad second format: ' + fmt; | ||||
| 			if(val.u === 0 && (fmt == "s" || fmt == "ss")) return pad0(val.S, fmt.length); | ||||
| 			/*::if(!ss0) ss0 = 0; */ | ||||
| 			if(ss0 >= 2) tt = ss0 === 3 ? 1000 : 100; | ||||
| 			else tt = ss0 === 1 ? 10 : 1; | ||||
| 			ss = Math.round((tt)*(val.S + val.u)); | ||||
| 			if(ss >= 60*tt) ss = 0; | ||||
| 			if(fmt === 's') return ss === 0 ? "0" : ""+ss/tt; | ||||
| 			o = pad0(ss,2 + ss0); | ||||
| 			if(fmt === 'ss') return o.substr(0,2); | ||||
| 			return "." + o.substr(2,fmt.length-1); | ||||
| 		case 90: /* 'Z' absolute time */ | ||||
| 		switch(fmt) { | ||||
| 			case '[h]': case '[hh]': out = val.D*24+val.H; break; | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| 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); | ||||
| 	var w = 3; | ||||
| 	if(s.length <= w) return s; | ||||
| 	var j = (s.length % w), o = s.substr(0,j); | ||||
| 	for(; j!=s.length; j+=w) o+=(o.length > 0 ? "," : "") + s.substr(j,w); | ||||
| 	return o; | ||||
| } | ||||
|  | ||||
| @ -1 +1 @@ | ||||
| var write_num = (function make_write_num(){ | ||||
| var write_num/*:SSF_write_num*/ = (function make_write_num(){ | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) { | ||||
| 	var out = [], o = "", i = 0, c = "", lst='t', q, dt, j, cc; | ||||
| 	var out = [], o = "", i = 0, c = "", lst='t', dt, j, cc; | ||||
| 	var hr='H'; | ||||
| 	/* Tokenize */ | ||||
| 	while(i < fmt.length) { | ||||
| @ -27,12 +27,12 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) { | ||||
| 			case 'm': case 'd': case 'y': case 'h': case 's': case 'e': case 'g': | ||||
| 				if(v < 0) return ""; | ||||
| 				if(dt==null) { dt=parse_date_code(v, opts); if(dt==null) return ""; } | ||||
| 				o = c; while(++i<fmt.length && fmt.charAt(i).toLowerCase() === c) o+=c; | ||||
| 				o = c; while(++i < fmt.length && fmt.charAt(i).toLowerCase() === c) o+=c; | ||||
| 				if(c === 'm' && lst.toLowerCase() === 'h') c = 'M'; | ||||
| 				if(c === 'h') c = hr; | ||||
| 				out[out.length] = {t:c, v:o}; lst = c; break; | ||||
| 			case 'A': case 'a': | ||||
| 				q={t:c, v:c}; | ||||
| 				var q={t:c, v:c}; | ||||
| 				if(dt==null) dt=parse_date_code(v, opts); | ||||
| 				if(fmt.substr(i, 3).toUpperCase() === "A/P") { if(dt!=null) q.v = dt.H >= 12 ? "P" : "A"; q.t = 'T'; hr='h';i+=3;} | ||||
| 				else if(fmt.substr(i,5).toUpperCase() === "AM/PM") { if(dt!=null) q.v = dt.H >= 12 ? "PM" : "AM"; q.t = 'T'; i+=5; hr='h'; } | ||||
| @ -55,7 +55,7 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) { | ||||
| 			/* Numbers */ | ||||
| 			case '.': | ||||
| 				if(dt != null) { | ||||
| 					o = c; while((c=fmt.charAt(++i)) === "0") o += c; | ||||
| 					o = c; while(++i < fmt.length && (c=fmt.charAt(i)) === "0") o += c; | ||||
| 					out[out.length] = {t:'s', v:o}; break; | ||||
| 				} | ||||
| 				/* falls through */ | ||||
| @ -64,7 +64,7 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) { | ||||
| 				out[out.length] = {t:'n', v:o}; break; | ||||
| 			case '?': | ||||
| 				o = c; while(fmt.charAt(++i) === c) o+=c; | ||||
| 				q={t:c, v:o}; out[out.length] = q; lst = c; break; | ||||
| 				out[out.length] = {t:c, v:o}; lst = c; break; | ||||
| 			case '*': ++i; if(fmt.charAt(i) == ' ' || fmt.charAt(i) == '*') ++i; break; // **
 | ||||
| 			case '(': case ')': out[out.length] = {t:(flen===1?'t':c), v:c}; ++i; break; | ||||
| 			case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| function choose_fmt(f/*:string*/, v) { | ||||
| function choose_fmt(f/*:string*/, v/*:any*/) { | ||||
| 	var fmt = split_fmt(f); | ||||
| 	var l = fmt.length, lat = fmt[l-1].indexOf("@"); | ||||
| 	if(l<4 && lat>-1) --l; | ||||
| @ -33,6 +33,7 @@ function format(fmt/*:string|number*/,v/*:any*/,o/*:?any*/) { | ||||
| 			break; | ||||
| 	} | ||||
| 	if(isgeneral(sfmt,0)) return general_fmt(v, o); | ||||
| 	if(v instanceof Date) v = datenum_local(v, o.date1904); | ||||
| 	var f = choose_fmt(sfmt, v); | ||||
| 	if(isgeneral(f[1])) return general_fmt(v, o); | ||||
| 	if(v === true) v = "TRUE"; else if(v === false) v = "FALSE"; | ||||
|  | ||||
							
								
								
									
										17
									
								
								bits/95_load.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										17
									
								
								bits/95_load.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| function load_entry(fmt/*:string*/, idx/*:?number*/)/*:number*/ { | ||||
| 	if(typeof idx != 'number') { | ||||
| 		idx = +idx || -1; | ||||
| /*::if(typeof idx != 'number') return 0x188; */ | ||||
| 		for(var i = 0; i < 0x0188; ++i) { | ||||
| /*::if(typeof idx != 'number') return 0x188; */ | ||||
| 			if(table_fmt[i] == undefined) { if(idx < 0) idx = i; continue; } | ||||
| 			if(table_fmt[i] == fmt) { idx = i; break; } | ||||
| 		} | ||||
| /*::if(typeof idx != 'number') return 0x188; */ | ||||
| 		if(idx < 0) idx = 0x187; | ||||
| 	} | ||||
| /*::if(typeof idx != 'number') return 0x188; */ | ||||
| 	table_fmt[idx] = fmt; | ||||
| 	return idx; | ||||
| } | ||||
| SSF.load = load_entry; | ||||
| @ -1,6 +1,8 @@ | ||||
| SSF._table = table_fmt; | ||||
| SSF.load = function load_entry(fmt/*:string*/, idx/*:number*/) { table_fmt[idx] = fmt; }; | ||||
| SSF.format = format; | ||||
| SSF.get_table = function get_table() { return table_fmt; }; | ||||
| SSF.load_table = function load_table(tbl/*:{[n:number]:string}*/) { for(var i=0; i!=0x0188; ++i) if(tbl[i] !== undefined) SSF.load(tbl[i], i); }; | ||||
| SSF.get_table = function get_table()/*:SSFTable*/ { return table_fmt; }; | ||||
| SSF.load_table = function load_table(tbl/*:SSFTable*/)/*:void*/ { | ||||
| 	for(var i=0; i!=0x0188; ++i) | ||||
| 		if(tbl[i] !== undefined) load_entry(tbl[i], i); | ||||
| }; | ||||
| SSF.init_table = init_table; | ||||
| SSF.format = format; | ||||
|  | ||||
| @ -1,5 +1,4 @@ | ||||
| }; | ||||
| 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; | ||||
|  | ||||
| @ -26,9 +26,9 @@ a { text-decoration: none } | ||||
| <script src="ssf.js"></script> | ||||
| <script> | ||||
| /*jshint browser:true */ | ||||
| /*global frac */ | ||||
| var V = document.getElementById('val'); | ||||
| /*global SSF */ | ||||
| var F = document.getElementById('fmt'); | ||||
| var V = document.getElementById('val'); | ||||
| var N = document.getElementById('num'); | ||||
| var T = document.getElementById('txt'); | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										26
									
								
								misc/flow.js
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										26
									
								
								misc/flow.js
									
									
									
									
									
								
							| @ -1,9 +1,31 @@ | ||||
| /*# vim: set ts=2: */ | ||||
| /*:: | ||||
| 
 | ||||
| type SSFTable = {[key:number|string]:string}; | ||||
| type SSFDate = { | ||||
| 	D:number; T:number; | ||||
| 	y:number; m:number; d:number; q:number; | ||||
| 	H:number; M:number; S:number; u:number; | ||||
| }; | ||||
| 
 | ||||
| type SSFModule = { | ||||
| 	format(fmt:string|number, v:any, o:any):string; | ||||
| 	is_date(fmt:string):boolean; | ||||
| 	format:(fmt:string|number, v:any, o:any)=>string; | ||||
| 
 | ||||
| 	is_date:(fmt:string)=>boolean; | ||||
| 	parse_date_code:(v:number,opts:any)=>?SSFDate; | ||||
| 
 | ||||
| 	load:(fmt:string, idx:?number)=>number; | ||||
| 	get_table:()=>SSFTable; | ||||
| 	load_table:(table:any)=>void; | ||||
| 	_table:SSFTable; | ||||
| 	init_table:any; | ||||
| 
 | ||||
| 	_general_int:(v:number)=>string; | ||||
| 	_general_num:(v:number)=>string; | ||||
| 	_general:(v:number, o:?any)=>string; | ||||
| 	_eval:any; | ||||
| 	_split:any; | ||||
| 	version:string; | ||||
| }; | ||||
| 
 | ||||
| */ | ||||
|  | ||||
							
								
								
									
										10
									
								
								package.json
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										10
									
								
								package.json
									
									
									
									
									
								
							| @ -1,6 +1,6 @@ | ||||
| { | ||||
| 	"name": "ssf", | ||||
| 	"version": "0.9.4", | ||||
| 	"version": "0.10.0", | ||||
| 	"author": "SheetJS", | ||||
| 	"description": "Format data using ECMA-376 spreadsheet Format Codes", | ||||
| 	"keywords": [ "format", "sprintf", "spreadsheet" ], | ||||
| @ -9,13 +9,13 @@ | ||||
| 	}, | ||||
| 	"main": "./ssf", | ||||
| 	"dependencies": { | ||||
| 		"voc":"", | ||||
| 		"colors":"0.6.2", | ||||
| 		"voc":"~1.0.0", | ||||
| 		"frac":"~1.0.6" | ||||
| 	}, | ||||
| 	"devDependencies": { | ||||
| 		"mocha":"", | ||||
| 		"@sheetjs/uglify-js":"" | ||||
| 		"mocha":"~2.5.3", | ||||
| 		"blanket": "~1.2.3", | ||||
| 		"@sheetjs/uglify-js":"~2.7.3" | ||||
| 	}, | ||||
| 	"repository": { "type":"git", "url":"git://github.com/SheetJS/ssf.git" }, | ||||
| 	"scripts": { | ||||
|  | ||||
							
								
								
									
										183
									
								
								ssf.flow.js
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										183
									
								
								ssf.flow.js
									
									
									
									
									
								
							| @ -1,8 +1,10 @@ | ||||
| /* ssf.js (C) 2013-present SheetJS -- http://sheetjs.com */ | ||||
| /* vim: set ts=2: */ | ||||
| /*jshint -W041 */ | ||||
| var SSF = {}; | ||||
| var make_ssf = function make_ssf(SSF){ | ||||
| SSF.version = '0.9.4'; | ||||
| /*:: declare var DO_NOT_EXPORT_SSF: any; */ | ||||
| var SSF/*:SSFModule*/ = ({}/*:any*/); | ||||
| var make_ssf = function make_ssf(SSF/*:SSFModule*/){ | ||||
| SSF.version = '0.10.0'; | ||||
| function _strrev(x/*:string*/)/*:string*/ { var o = "", i = x.length-1; while(i>=0) o += x.charAt(i--); return o; } | ||||
| function fill(c/*:string*/,l/*:number*/)/*:string*/ { var o = ""; while(o.length < l) o+=c; return o; } | ||||
| function pad0(v/*:any*/,d/*:number*/)/*:string*/{var t=""+v; return t.length>=d?t:fill('0',d-t.length)+t;} | ||||
| @ -13,16 +15,9 @@ function pad0r2(v/*:any*/,d/*:number*/)/*:string*/{var t=""+v; return t.length>= | ||||
| var p2_32 = Math.pow(2,32); | ||||
| function pad0r(v/*:any*/,d/*:number*/)/*:string*/{if(v>p2_32||v<-p2_32) return pad0r1(v,d); var i = Math.round(v); return pad0r2(i,d); } | ||||
| function isgeneral(s/*:string*/, i/*:?number*/)/*:boolean*/ { i = i || 0; return s.length >= 7 + i && (s.charCodeAt(i)|32) === 103 && (s.charCodeAt(i+1)|32) === 101 && (s.charCodeAt(i+2)|32) === 110 && (s.charCodeAt(i+3)|32) === 101 && (s.charCodeAt(i+4)|32) === 114 && (s.charCodeAt(i+5)|32) === 97 && (s.charCodeAt(i+6)|32) === 108; } | ||||
| /* Options */ | ||||
| var opts_fmt/*:Array<Array<any> >*/ = [ | ||||
| 	["date1904", 0], | ||||
| 	["output", ""], | ||||
| 	["WTF", false] | ||||
| ]; | ||||
| function fixopts(o){ | ||||
| 	for(var y = 0; y != opts_fmt.length; ++y) if(o[opts_fmt[y][0]]===undefined) o[opts_fmt[y][0]]=opts_fmt[y][1]; | ||||
| } | ||||
| SSF.opts = opts_fmt; | ||||
| /*:: | ||||
| type SSF_write_num = {(type:string, fmt:string, val:number):string}; | ||||
| */ | ||||
| var days/*:Array<Array<string> >*/ = [ | ||||
| 	['Sun', 'Sunday'], | ||||
| 	['Mon', 'Monday'], | ||||
| @ -101,7 +96,47 @@ function frac(x/*:number*/, D/*:number*/, mixed/*:?boolean*/)/*:Array<number>*/ | ||||
| 	var q = Math.floor(sgn * P/Q); | ||||
| 	return [q, sgn*P - q*Q, Q]; | ||||
| } | ||||
| function general_fmt_int(v/*:number*/)/*:string*/ { return ""+v; } | ||||
| function parse_date_code(v/*:number*/,opts/*:?any*/,b2/*:?boolean*/) { | ||||
| 	if(v > 2958465 || v < 0) return null; | ||||
| 	var date = (v|0), time = Math.floor(86400 * (v - date)), dow=0; | ||||
| 	var dout=[]; | ||||
| 	var out={D:date, T:time, u:86400*(v-date)-time,y:0,m:0,d:0,H:0,M:0,S:0,q:0}; | ||||
| 	if(Math.abs(out.u) < 1e-6) out.u = 0; | ||||
| 	if(opts && opts.date1904) date += 1462; | ||||
| 	if(out.u > 0.9999) { | ||||
| 		out.u = 0; | ||||
| 		if(++time == 86400) { out.T = time = 0; ++date; ++out.D; } | ||||
| 	} | ||||
| 	if(date === 60) {dout = b2 ? [1317,10,29] : [1900,2,29]; dow=3;} | ||||
| 	else if(date === 0) {dout = b2 ? [1317,8,29] : [1900,1,0]; dow=6;} | ||||
| 	else { | ||||
| 		if(date > 60) --date; | ||||
| 		/* 1 = Jan 1 1900 in Gregorian */ | ||||
| 		var d = new Date(1900, 0, 1); | ||||
| 		d.setDate(d.getDate() + date - 1); | ||||
| 		dout = [d.getFullYear(), d.getMonth()+1,d.getDate()]; | ||||
| 		dow = d.getDay(); | ||||
| 		if(date < 60) dow = (dow + 6) % 7; | ||||
| 		if(b2) dow = fix_hijri(d, dout); | ||||
| 	} | ||||
| 	out.y = dout[0]; out.m = dout[1]; out.d = dout[2]; | ||||
| 	out.S = time % 60; time = Math.floor(time / 60); | ||||
| 	out.M = time % 60; time = Math.floor(time / 60); | ||||
| 	out.H = time; | ||||
| 	out.q = dow; | ||||
| 	return out; | ||||
| } | ||||
| SSF.parse_date_code = parse_date_code; | ||||
| var basedate = new Date(1899, 11, 31, 0, 0, 0); | ||||
| var dnthresh = basedate.getTime(); | ||||
| var base1904 = new Date(1900, 2, 1, 0, 0, 0); | ||||
| function datenum_local(v/*:Date*/, date1904/*:?boolean*/)/*:number*/ { | ||||
| 	var epoch = v.getTime(); | ||||
| 	if(date1904) epoch -= 1461*24*60*60*1000; | ||||
| 	else if(v >= base1904) epoch += 24*60*60*1000; | ||||
| 	return (epoch - (dnthresh + (v.getTimezoneOffset() - basedate.getTimezoneOffset()) * 60000)) / (24 * 60 * 60 * 1000); | ||||
| } | ||||
| function general_fmt_int(v/*:number*/)/*:string*/ { return v.toString(10); } | ||||
| 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)$/; | ||||
| @ -132,50 +167,20 @@ return function general_fmt_num(v/*:number*/)/*:string*/ { | ||||
| 	return gfn5(gfn4(o)); | ||||
| };})(); | ||||
| SSF._general_num = general_fmt_num; | ||||
| function general_fmt(v/*:any*/) { | ||||
| function general_fmt(v/*:any*/, opts/*:any*/) { | ||||
| 	switch(typeof v) { | ||||
| 		case 'string': return v; | ||||
| 		case 'boolean': return v ? "TRUE" : "FALSE"; | ||||
| 		case 'number': return (v|0) === v ? general_fmt_int(v/*, opts*/) : general_fmt_num(v/*, opts*/); | ||||
| 		case 'number': return (v|0) === v ? general_fmt_int(v) : general_fmt_num(v); | ||||
| 		case 'undefined': return ""; | ||||
| 		case 'object': if(v == null) return ""; | ||||
| 		case 'object': | ||||
| 			if(v == null) return ""; | ||||
| 			if(v instanceof Date) return format(14, datenum_local(v, opts && opts.date1904), opts); | ||||
| 	} | ||||
| 	throw new Error("unsupported value in General format: " + v); | ||||
| } | ||||
| SSF._general = general_fmt; | ||||
| function fix_hijri(/*date, o*/) { return 0; } | ||||
| function parse_date_code(v/*:number*/,opts/*:?any*/,b2/*:?boolean*/) { | ||||
| 	if(v > 2958465 || v < 0) return null; | ||||
| 	var date = (v|0), time = Math.floor(86400 * (v - date)), dow=0; | ||||
| 	var dout=[]; | ||||
| 	var out={D:date, T:time, u:86400*(v-date)-time,y:0,m:0,d:0,H:0,M:0,S:0,q:0}; | ||||
| 	if(Math.abs(out.u) < 1e-6) out.u = 0; | ||||
| 	fixopts(opts != null ? opts : (opts=[])); | ||||
| 	if(opts.date1904) date += 1462; | ||||
| 	if(out.u > 0.9999) { | ||||
| 		out.u = 0; | ||||
| 		if(++time == 86400) { out.T = time = 0; ++date; ++out.D; } | ||||
| 	} | ||||
| 	if(date === 60) {dout = b2 ? [1317,10,29] : [1900,2,29]; dow=3;} | ||||
| 	else if(date === 0) {dout = b2 ? [1317,8,29] : [1900,1,0]; dow=6;} | ||||
| 	else { | ||||
| 		if(date > 60) --date; | ||||
| 		/* 1 = Jan 1 1900 in Gregorian */ | ||||
| 		var d = new Date(1900, 0, 1); | ||||
| 		d.setDate(d.getDate() + date - 1); | ||||
| 		dout = [d.getFullYear(), d.getMonth()+1,d.getDate()]; | ||||
| 		dow = d.getDay(); | ||||
| 		if(date < 60) dow = (dow + 6) % 7; | ||||
| 		if(b2) dow = fix_hijri(d, dout); | ||||
| 	} | ||||
| 	out.y = dout[0]; out.m = dout[1]; out.d = dout[2]; | ||||
| 	out.S = time % 60; time = Math.floor(time / 60); | ||||
| 	out.M = time % 60; time = Math.floor(time / 60); | ||||
| 	out.H = time; | ||||
| 	out.q = dow; | ||||
| 	return out; | ||||
| } | ||||
| SSF.parse_date_code = parse_date_code; | ||||
| function fix_hijri(/*::date, o*/) { return 0; } | ||||
| /*jshint -W086 */ | ||||
| function write_date(type/*:number*/, fmt/*:string*/, val, ss0/*:?number*/)/*:string*/ { | ||||
| 	var o="", ss=0, tt=0, y = val.y, out, outl = 0; | ||||
| @ -217,23 +222,17 @@ function write_date(type/*:number*/, fmt/*:string*/, val, ss0/*:?number*/)/*:str | ||||
| 			default: throw 'bad minute format: ' + fmt; | ||||
| 		} break; | ||||
| 		case 115: /* 's' seconds */ | ||||
| 		if(val.u === 0) switch(fmt) { | ||||
| 			case 's': case 'ss': return pad0(val.S, fmt.length); | ||||
| 			case '.0': case '.00': case '.000': | ||||
| 		} | ||||
| 		switch(fmt) { | ||||
| 			case 's': case 'ss': case '.0': case '.00': case '.000': | ||||
| 				/*::if(!ss0) ss0 = 0; */ | ||||
| 				if(ss0 >= 2) tt = ss0 === 3 ? 1000 : 100; | ||||
| 				else tt = ss0 === 1 ? 10 : 1; | ||||
| 				ss = Math.round((tt)*(val.S + val.u)); | ||||
| 				if(ss >= 60*tt) ss = 0; | ||||
| 				if(fmt === 's') return ss === 0 ? "0" : ""+ss/tt; | ||||
| 				o = pad0(ss,2 + ss0); | ||||
| 				if(fmt === 'ss') return o.substr(0,2); | ||||
| 				return "." + o.substr(2,fmt.length-1); | ||||
| 			default: throw 'bad second format: ' + fmt; | ||||
| 		} | ||||
| 			if(fmt != 's' && fmt != 'ss' && fmt != '.0' && fmt != '.00' && fmt != '.000') throw 'bad second format: ' + fmt; | ||||
| 			if(val.u === 0 && (fmt == "s" || fmt == "ss")) return pad0(val.S, fmt.length); | ||||
| 			/*::if(!ss0) ss0 = 0; */ | ||||
| 			if(ss0 >= 2) tt = ss0 === 3 ? 1000 : 100; | ||||
| 			else tt = ss0 === 1 ? 10 : 1; | ||||
| 			ss = Math.round((tt)*(val.S + val.u)); | ||||
| 			if(ss >= 60*tt) ss = 0; | ||||
| 			if(fmt === 's') return ss === 0 ? "0" : ""+ss/tt; | ||||
| 			o = pad0(ss,2 + ss0); | ||||
| 			if(fmt === 'ss') return o.substr(0,2); | ||||
| 			return "." + o.substr(2,fmt.length-1); | ||||
| 		case 90: /* 'Z' absolute time */ | ||||
| 		switch(fmt) { | ||||
| 			case '[h]': case '[hh]': out = val.D*24+val.H; break; | ||||
| @ -248,12 +247,13 @@ function write_date(type/*:number*/, fmt/*:string*/, val, ss0/*:?number*/)/*:str | ||||
| } | ||||
| /*jshint +W086 */ | ||||
| function commaify(s/*:string*/)/*:string*/ { | ||||
| 	if(s.length <= 3) return s; | ||||
| 	var j = (s.length % 3), o = s.substr(0,j); | ||||
| 	for(; j!=s.length; j+=3) o+=(o.length > 0 ? "," : "") + s.substr(j,3); | ||||
| 	var w = 3; | ||||
| 	if(s.length <= w) return s; | ||||
| 	var j = (s.length % w), o = s.substr(0,j); | ||||
| 	for(; j!=s.length; j+=w) o+=(o.length > 0 ? "," : "") + s.substr(j,w); | ||||
| 	return o; | ||||
| } | ||||
| var write_num = (function make_write_num(){ | ||||
| var write_num/*:SSF_write_num*/ = (function make_write_num(){ | ||||
| var pct1 = /%/g; | ||||
| function write_num_pct(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string*/{ | ||||
| 	var sfmt = fmt.replace(pct1,""), mul = fmt.length - sfmt.length; | ||||
| @ -591,7 +591,7 @@ function fmt_is_date(fmt/*:string*/)/*:boolean*/ { | ||||
| } | ||||
| SSF.is_date = fmt_is_date; | ||||
| function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) { | ||||
| 	var out = [], o = "", i = 0, c = "", lst='t', q, dt, j, cc; | ||||
| 	var out = [], o = "", i = 0, c = "", lst='t', dt, j, cc; | ||||
| 	var hr='H'; | ||||
| 	/* Tokenize */ | ||||
| 	while(i < fmt.length) { | ||||
| @ -619,12 +619,12 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) { | ||||
| 			case 'm': case 'd': case 'y': case 'h': case 's': case 'e': case 'g': | ||||
| 				if(v < 0) return ""; | ||||
| 				if(dt==null) { dt=parse_date_code(v, opts); if(dt==null) return ""; } | ||||
| 				o = c; while(++i<fmt.length && fmt.charAt(i).toLowerCase() === c) o+=c; | ||||
| 				o = c; while(++i < fmt.length && fmt.charAt(i).toLowerCase() === c) o+=c; | ||||
| 				if(c === 'm' && lst.toLowerCase() === 'h') c = 'M'; | ||||
| 				if(c === 'h') c = hr; | ||||
| 				out[out.length] = {t:c, v:o}; lst = c; break; | ||||
| 			case 'A': case 'a': | ||||
| 				q={t:c, v:c}; | ||||
| 				var q={t:c, v:c}; | ||||
| 				if(dt==null) dt=parse_date_code(v, opts); | ||||
| 				if(fmt.substr(i, 3).toUpperCase() === "A/P") { if(dt!=null) q.v = dt.H >= 12 ? "P" : "A"; q.t = 'T'; hr='h';i+=3;} | ||||
| 				else if(fmt.substr(i,5).toUpperCase() === "AM/PM") { if(dt!=null) q.v = dt.H >= 12 ? "PM" : "AM"; q.t = 'T'; i+=5; hr='h'; } | ||||
| @ -647,7 +647,7 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) { | ||||
| 			/* Numbers */ | ||||
| 			case '.': | ||||
| 				if(dt != null) { | ||||
| 					o = c; while((c=fmt.charAt(++i)) === "0") o += c; | ||||
| 					o = c; while(++i < fmt.length && (c=fmt.charAt(i)) === "0") o += c; | ||||
| 					out[out.length] = {t:'s', v:o}; break; | ||||
| 				} | ||||
| 				/* falls through */ | ||||
| @ -656,7 +656,7 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) { | ||||
| 				out[out.length] = {t:'n', v:o}; break; | ||||
| 			case '?': | ||||
| 				o = c; while(fmt.charAt(++i) === c) o+=c; | ||||
| 				q={t:c, v:o}; out[out.length] = q; lst = c; break; | ||||
| 				out[out.length] = {t:c, v:o}; lst = c; break; | ||||
| 			case '*': ++i; if(fmt.charAt(i) == ' ' || fmt.charAt(i) == '*') ++i; break; // **
 | ||||
| 			case '(': case ')': out[out.length] = {t:(flen===1?'t':c), v:c}; ++i; break; | ||||
| 			case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': | ||||
| @ -807,7 +807,7 @@ function chkcond(v, rr) { | ||||
| 	} | ||||
| 	return false; | ||||
| } | ||||
| function choose_fmt(f/*:string*/, v) { | ||||
| function choose_fmt(f/*:string*/, v/*:any*/) { | ||||
| 	var fmt = split_fmt(f); | ||||
| 	var l = fmt.length, lat = fmt[l-1].indexOf("@"); | ||||
| 	if(l<4 && lat>-1) --l; | ||||
| @ -842,20 +842,39 @@ function format(fmt/*:string|number*/,v/*:any*/,o/*:?any*/) { | ||||
| 			break; | ||||
| 	} | ||||
| 	if(isgeneral(sfmt,0)) return general_fmt(v, o); | ||||
| 	if(v instanceof Date) v = datenum_local(v, o.date1904); | ||||
| 	var f = choose_fmt(sfmt, v); | ||||
| 	if(isgeneral(f[1])) return general_fmt(v, o); | ||||
| 	if(v === true) v = "TRUE"; else if(v === false) v = "FALSE"; | ||||
| 	else if(v === "" || v == null) return ""; | ||||
| 	return eval_fmt(f[1], v, o, f[0]); | ||||
| } | ||||
| function load_entry(fmt/*:string*/, idx/*:?number*/)/*:number*/ { | ||||
| 	if(typeof idx != 'number') { | ||||
| 		idx = +idx || -1; | ||||
| /*::if(typeof idx != 'number') return 0x188; */ | ||||
| 		for(var i = 0; i < 0x0188; ++i) { | ||||
| /*::if(typeof idx != 'number') return 0x188; */ | ||||
| 			if(table_fmt[i] == undefined) { if(idx < 0) idx = i; continue; } | ||||
| 			if(table_fmt[i] == fmt) { idx = i; break; } | ||||
| 		} | ||||
| /*::if(typeof idx != 'number') return 0x188; */ | ||||
| 		if(idx < 0) idx = 0x187; | ||||
| 	} | ||||
| /*::if(typeof idx != 'number') return 0x188; */ | ||||
| 	table_fmt[idx] = fmt; | ||||
| 	return idx; | ||||
| } | ||||
| SSF.load = load_entry; | ||||
| SSF._table = table_fmt; | ||||
| SSF.load = function load_entry(fmt/*:string*/, idx/*:number*/) { table_fmt[idx] = fmt; }; | ||||
| SSF.format = format; | ||||
| SSF.get_table = function get_table() { return table_fmt; }; | ||||
| SSF.load_table = function load_table(tbl/*:{[n:number]:string}*/) { for(var i=0; i!=0x0188; ++i) if(tbl[i] !== undefined) SSF.load(tbl[i], i); }; | ||||
| SSF.get_table = function get_table()/*:SSFTable*/ { return table_fmt; }; | ||||
| SSF.load_table = function load_table(tbl/*:SSFTable*/)/*:void*/ { | ||||
| 	for(var i=0; i!=0x0188; ++i) | ||||
| 		if(tbl[i] !== undefined) load_entry(tbl[i], i); | ||||
| }; | ||||
| SSF.init_table = init_table; | ||||
| SSF.format = format; | ||||
| }; | ||||
| 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; | ||||
|  | ||||
							
								
								
									
										162
									
								
								ssf.js
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										162
									
								
								ssf.js
									
									
									
									
									
								
							| @ -1,8 +1,9 @@ | ||||
| /* ssf.js (C) 2013-present SheetJS -- http://sheetjs.com */ | ||||
| /* vim: set ts=2: */ | ||||
| /*jshint -W041 */ | ||||
| var SSF = {}; | ||||
| var SSF = ({}); | ||||
| var make_ssf = function make_ssf(SSF){ | ||||
| SSF.version = '0.9.4'; | ||||
| SSF.version = '0.10.0'; | ||||
| function _strrev(x) { var o = "", i = x.length-1; while(i>=0) o += x.charAt(i--); return o; } | ||||
| function fill(c,l) { var o = ""; while(o.length < l) o+=c; return o; } | ||||
| function pad0(v,d){var t=""+v; return t.length>=d?t:fill('0',d-t.length)+t;} | ||||
| @ -13,16 +14,6 @@ function pad0r2(v,d){var t=""+v; return t.length>=d?t:fill('0',d-t.length)+t;} | ||||
| var p2_32 = Math.pow(2,32); | ||||
| function pad0r(v,d){if(v>p2_32||v<-p2_32) return pad0r1(v,d); var i = Math.round(v); return pad0r2(i,d); } | ||||
| function isgeneral(s, i) { i = i || 0; return s.length >= 7 + i && (s.charCodeAt(i)|32) === 103 && (s.charCodeAt(i+1)|32) === 101 && (s.charCodeAt(i+2)|32) === 110 && (s.charCodeAt(i+3)|32) === 101 && (s.charCodeAt(i+4)|32) === 114 && (s.charCodeAt(i+5)|32) === 97 && (s.charCodeAt(i+6)|32) === 108; } | ||||
| /* Options */ | ||||
| var opts_fmt = [ | ||||
| 	["date1904", 0], | ||||
| 	["output", ""], | ||||
| 	["WTF", false] | ||||
| ]; | ||||
| function fixopts(o){ | ||||
| 	for(var y = 0; y != opts_fmt.length; ++y) if(o[opts_fmt[y][0]]===undefined) o[opts_fmt[y][0]]=opts_fmt[y][1]; | ||||
| } | ||||
| SSF.opts = opts_fmt; | ||||
| var days = [ | ||||
| 	['Sun', 'Sunday'], | ||||
| 	['Mon', 'Monday'], | ||||
| @ -101,7 +92,47 @@ function frac(x, D, mixed) { | ||||
| 	var q = Math.floor(sgn * P/Q); | ||||
| 	return [q, sgn*P - q*Q, Q]; | ||||
| } | ||||
| function general_fmt_int(v) { return ""+v; } | ||||
| function parse_date_code(v,opts,b2) { | ||||
| 	if(v > 2958465 || v < 0) return null; | ||||
| 	var date = (v|0), time = Math.floor(86400 * (v - date)), dow=0; | ||||
| 	var dout=[]; | ||||
| 	var out={D:date, T:time, u:86400*(v-date)-time,y:0,m:0,d:0,H:0,M:0,S:0,q:0}; | ||||
| 	if(Math.abs(out.u) < 1e-6) out.u = 0; | ||||
| 	if(opts && opts.date1904) date += 1462; | ||||
| 	if(out.u > 0.9999) { | ||||
| 		out.u = 0; | ||||
| 		if(++time == 86400) { out.T = time = 0; ++date; ++out.D; } | ||||
| 	} | ||||
| 	if(date === 60) {dout = b2 ? [1317,10,29] : [1900,2,29]; dow=3;} | ||||
| 	else if(date === 0) {dout = b2 ? [1317,8,29] : [1900,1,0]; dow=6;} | ||||
| 	else { | ||||
| 		if(date > 60) --date; | ||||
| 		/* 1 = Jan 1 1900 in Gregorian */ | ||||
| 		var d = new Date(1900, 0, 1); | ||||
| 		d.setDate(d.getDate() + date - 1); | ||||
| 		dout = [d.getFullYear(), d.getMonth()+1,d.getDate()]; | ||||
| 		dow = d.getDay(); | ||||
| 		if(date < 60) dow = (dow + 6) % 7; | ||||
| 		if(b2) dow = fix_hijri(d, dout); | ||||
| 	} | ||||
| 	out.y = dout[0]; out.m = dout[1]; out.d = dout[2]; | ||||
| 	out.S = time % 60; time = Math.floor(time / 60); | ||||
| 	out.M = time % 60; time = Math.floor(time / 60); | ||||
| 	out.H = time; | ||||
| 	out.q = dow; | ||||
| 	return out; | ||||
| } | ||||
| SSF.parse_date_code = parse_date_code; | ||||
| var basedate = new Date(1899, 11, 31, 0, 0, 0); | ||||
| var dnthresh = basedate.getTime(); | ||||
| var base1904 = new Date(1900, 2, 1, 0, 0, 0); | ||||
| function datenum_local(v, date1904) { | ||||
| 	var epoch = v.getTime(); | ||||
| 	if(date1904) epoch -= 1461*24*60*60*1000; | ||||
| 	else if(v >= base1904) epoch += 24*60*60*1000; | ||||
| 	return (epoch - (dnthresh + (v.getTimezoneOffset() - basedate.getTimezoneOffset()) * 60000)) / (24 * 60 * 60 * 1000); | ||||
| } | ||||
| function general_fmt_int(v) { return v.toString(10); } | ||||
| 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)$/; | ||||
| @ -132,50 +163,20 @@ return function general_fmt_num(v) { | ||||
| 	return gfn5(gfn4(o)); | ||||
| };})(); | ||||
| SSF._general_num = general_fmt_num; | ||||
| function general_fmt(v) { | ||||
| function general_fmt(v, opts) { | ||||
| 	switch(typeof v) { | ||||
| 		case 'string': return v; | ||||
| 		case 'boolean': return v ? "TRUE" : "FALSE"; | ||||
| 		case 'number': return (v|0) === v ? general_fmt_int(v/*, opts*/) : general_fmt_num(v/*, opts*/); | ||||
| 		case 'number': return (v|0) === v ? general_fmt_int(v) : general_fmt_num(v); | ||||
| 		case 'undefined': return ""; | ||||
| 		case 'object': if(v == null) return ""; | ||||
| 		case 'object': | ||||
| 			if(v == null) return ""; | ||||
| 			if(v instanceof Date) return format(14, datenum_local(v, opts && opts.date1904), opts); | ||||
| 	} | ||||
| 	throw new Error("unsupported value in General format: " + v); | ||||
| } | ||||
| SSF._general = general_fmt; | ||||
| function fix_hijri(/*date, o*/) { return 0; } | ||||
| function parse_date_code(v,opts,b2) { | ||||
| 	if(v > 2958465 || v < 0) return null; | ||||
| 	var date = (v|0), time = Math.floor(86400 * (v - date)), dow=0; | ||||
| 	var dout=[]; | ||||
| 	var out={D:date, T:time, u:86400*(v-date)-time,y:0,m:0,d:0,H:0,M:0,S:0,q:0}; | ||||
| 	if(Math.abs(out.u) < 1e-6) out.u = 0; | ||||
| 	fixopts(opts != null ? opts : (opts=[])); | ||||
| 	if(opts.date1904) date += 1462; | ||||
| 	if(out.u > 0.9999) { | ||||
| 		out.u = 0; | ||||
| 		if(++time == 86400) { out.T = time = 0; ++date; ++out.D; } | ||||
| 	} | ||||
| 	if(date === 60) {dout = b2 ? [1317,10,29] : [1900,2,29]; dow=3;} | ||||
| 	else if(date === 0) {dout = b2 ? [1317,8,29] : [1900,1,0]; dow=6;} | ||||
| 	else { | ||||
| 		if(date > 60) --date; | ||||
| 		/* 1 = Jan 1 1900 in Gregorian */ | ||||
| 		var d = new Date(1900, 0, 1); | ||||
| 		d.setDate(d.getDate() + date - 1); | ||||
| 		dout = [d.getFullYear(), d.getMonth()+1,d.getDate()]; | ||||
| 		dow = d.getDay(); | ||||
| 		if(date < 60) dow = (dow + 6) % 7; | ||||
| 		if(b2) dow = fix_hijri(d, dout); | ||||
| 	} | ||||
| 	out.y = dout[0]; out.m = dout[1]; out.d = dout[2]; | ||||
| 	out.S = time % 60; time = Math.floor(time / 60); | ||||
| 	out.M = time % 60; time = Math.floor(time / 60); | ||||
| 	out.H = time; | ||||
| 	out.q = dow; | ||||
| 	return out; | ||||
| } | ||||
| SSF.parse_date_code = parse_date_code; | ||||
| function fix_hijri() { return 0; } | ||||
| /*jshint -W086 */ | ||||
| function write_date(type, fmt, val, ss0) { | ||||
| 	var o="", ss=0, tt=0, y = val.y, out, outl = 0; | ||||
| @ -217,22 +218,16 @@ function write_date(type, fmt, val, ss0) { | ||||
| 			default: throw 'bad minute format: ' + fmt; | ||||
| 		} break; | ||||
| 		case 115: /* 's' seconds */ | ||||
| 		if(val.u === 0) switch(fmt) { | ||||
| 			case 's': case 'ss': return pad0(val.S, fmt.length); | ||||
| 			case '.0': case '.00': case '.000': | ||||
| 		} | ||||
| 		switch(fmt) { | ||||
| 			case 's': case 'ss': case '.0': case '.00': case '.000': | ||||
| 			if(fmt != 's' && fmt != 'ss' && fmt != '.0' && fmt != '.00' && fmt != '.000') throw 'bad second format: ' + fmt; | ||||
| 			if(val.u === 0 && (fmt == "s" || fmt == "ss")) return pad0(val.S, fmt.length); | ||||
| if(ss0 >= 2) tt = ss0 === 3 ? 1000 : 100; | ||||
| 				else tt = ss0 === 1 ? 10 : 1; | ||||
| 				ss = Math.round((tt)*(val.S + val.u)); | ||||
| 				if(ss >= 60*tt) ss = 0; | ||||
| 				if(fmt === 's') return ss === 0 ? "0" : ""+ss/tt; | ||||
| 				o = pad0(ss,2 + ss0); | ||||
| 				if(fmt === 'ss') return o.substr(0,2); | ||||
| 				return "." + o.substr(2,fmt.length-1); | ||||
| 			default: throw 'bad second format: ' + fmt; | ||||
| 		} | ||||
| 			else tt = ss0 === 1 ? 10 : 1; | ||||
| 			ss = Math.round((tt)*(val.S + val.u)); | ||||
| 			if(ss >= 60*tt) ss = 0; | ||||
| 			if(fmt === 's') return ss === 0 ? "0" : ""+ss/tt; | ||||
| 			o = pad0(ss,2 + ss0); | ||||
| 			if(fmt === 'ss') return o.substr(0,2); | ||||
| 			return "." + o.substr(2,fmt.length-1); | ||||
| 		case 90: /* 'Z' absolute time */ | ||||
| 		switch(fmt) { | ||||
| 			case '[h]': case '[hh]': out = val.D*24+val.H; break; | ||||
| @ -247,9 +242,10 @@ if(ss0 >= 2) tt = ss0 === 3 ? 1000 : 100; | ||||
| } | ||||
| /*jshint +W086 */ | ||||
| function commaify(s) { | ||||
| 	if(s.length <= 3) return s; | ||||
| 	var j = (s.length % 3), o = s.substr(0,j); | ||||
| 	for(; j!=s.length; j+=3) o+=(o.length > 0 ? "," : "") + s.substr(j,3); | ||||
| 	var w = 3; | ||||
| 	if(s.length <= w) return s; | ||||
| 	var j = (s.length % w), o = s.substr(0,j); | ||||
| 	for(; j!=s.length; j+=w) o+=(o.length > 0 ? "," : "") + s.substr(j,w); | ||||
| 	return o; | ||||
| } | ||||
| var write_num = (function make_write_num(){ | ||||
| @ -588,7 +584,7 @@ function fmt_is_date(fmt) { | ||||
| } | ||||
| SSF.is_date = fmt_is_date; | ||||
| function eval_fmt(fmt, v, opts, flen) { | ||||
| 	var out = [], o = "", i = 0, c = "", lst='t', q, dt, j, cc; | ||||
| 	var out = [], o = "", i = 0, c = "", lst='t', dt, j, cc; | ||||
| 	var hr='H'; | ||||
| 	/* Tokenize */ | ||||
| 	while(i < fmt.length) { | ||||
| @ -616,12 +612,12 @@ function eval_fmt(fmt, v, opts, flen) { | ||||
| 			case 'm': case 'd': case 'y': case 'h': case 's': case 'e': case 'g': | ||||
| 				if(v < 0) return ""; | ||||
| 				if(dt==null) { dt=parse_date_code(v, opts); if(dt==null) return ""; } | ||||
| 				o = c; while(++i<fmt.length && fmt.charAt(i).toLowerCase() === c) o+=c; | ||||
| 				o = c; while(++i < fmt.length && fmt.charAt(i).toLowerCase() === c) o+=c; | ||||
| 				if(c === 'm' && lst.toLowerCase() === 'h') c = 'M'; | ||||
| 				if(c === 'h') c = hr; | ||||
| 				out[out.length] = {t:c, v:o}; lst = c; break; | ||||
| 			case 'A': case 'a': | ||||
| 				q={t:c, v:c}; | ||||
| 				var q={t:c, v:c}; | ||||
| 				if(dt==null) dt=parse_date_code(v, opts); | ||||
| 				if(fmt.substr(i, 3).toUpperCase() === "A/P") { if(dt!=null) q.v = dt.H >= 12 ? "P" : "A"; q.t = 'T'; hr='h';i+=3;} | ||||
| 				else if(fmt.substr(i,5).toUpperCase() === "AM/PM") { if(dt!=null) q.v = dt.H >= 12 ? "PM" : "AM"; q.t = 'T'; i+=5; hr='h'; } | ||||
| @ -644,7 +640,7 @@ function eval_fmt(fmt, v, opts, flen) { | ||||
| 			/* Numbers */ | ||||
| 			case '.': | ||||
| 				if(dt != null) { | ||||
| 					o = c; while((c=fmt.charAt(++i)) === "0") o += c; | ||||
| 					o = c; while(++i < fmt.length && (c=fmt.charAt(i)) === "0") o += c; | ||||
| 					out[out.length] = {t:'s', v:o}; break; | ||||
| 				} | ||||
| 				/* falls through */ | ||||
| @ -653,7 +649,7 @@ function eval_fmt(fmt, v, opts, flen) { | ||||
| 				out[out.length] = {t:'n', v:o}; break; | ||||
| 			case '?': | ||||
| 				o = c; while(fmt.charAt(++i) === c) o+=c; | ||||
| 				q={t:c, v:o}; out[out.length] = q; lst = c; break; | ||||
| 				out[out.length] = {t:c, v:o}; lst = c; break; | ||||
| 			case '*': ++i; if(fmt.charAt(i) == ' ' || fmt.charAt(i) == '*') ++i; break; // **
 | ||||
| 			case '(': case ')': out[out.length] = {t:(flen===1?'t':c), v:c}; ++i; break; | ||||
| 			case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': | ||||
| @ -836,18 +832,34 @@ function format(fmt,v,o) { | ||||
| 			break; | ||||
| 	} | ||||
| 	if(isgeneral(sfmt,0)) return general_fmt(v, o); | ||||
| 	if(v instanceof Date) v = datenum_local(v, o.date1904); | ||||
| 	var f = choose_fmt(sfmt, v); | ||||
| 	if(isgeneral(f[1])) return general_fmt(v, o); | ||||
| 	if(v === true) v = "TRUE"; else if(v === false) v = "FALSE"; | ||||
| 	else if(v === "" || v == null) return ""; | ||||
| 	return eval_fmt(f[1], v, o, f[0]); | ||||
| } | ||||
| function load_entry(fmt, idx) { | ||||
| 	if(typeof idx != 'number') { | ||||
| 		idx = +idx || -1; | ||||
| for(var i = 0; i < 0x0188; ++i) { | ||||
| if(table_fmt[i] == undefined) { if(idx < 0) idx = i; continue; } | ||||
| 			if(table_fmt[i] == fmt) { idx = i; break; } | ||||
| 		} | ||||
| if(idx < 0) idx = 0x187; | ||||
| 	} | ||||
| table_fmt[idx] = fmt; | ||||
| 	return idx; | ||||
| } | ||||
| SSF.load = load_entry; | ||||
| SSF._table = table_fmt; | ||||
| SSF.load = function load_entry(fmt, idx) { table_fmt[idx] = fmt; }; | ||||
| SSF.format = format; | ||||
| SSF.get_table = function get_table() { return table_fmt; }; | ||||
| SSF.load_table = function load_table(tbl) { for(var i=0; i!=0x0188; ++i) if(tbl[i] !== undefined) SSF.load(tbl[i], i); }; | ||||
| SSF.load_table = function load_table(tbl) { | ||||
| 	for(var i=0; i!=0x0188; ++i) | ||||
| 		if(tbl[i] !== undefined) load_entry(tbl[i], i); | ||||
| }; | ||||
| SSF.init_table = init_table; | ||||
| SSF.format = format; | ||||
| }; | ||||
| make_ssf(SSF); | ||||
| /*global module */ | ||||
|  | ||||
							
								
								
									
										22
									
								
								test/date.json
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										22
									
								
								test/date.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | ||||
| [ | ||||
| 	["new Date(1900,0,1)", "1900-01-01 00:00:00", false], | ||||
| 	["new Date(1900,1,1)", "1900-02-01 00:00:00", false], | ||||
| 	["new Date(1900,2,1)", "1900-03-01 00:00:00", false], | ||||
| 	["new Date(1900,3,1)", "1900-04-01 00:00:00", false], | ||||
| 	["new Date(2000,0,1)", "2000-01-01 00:00:00", false], | ||||
| 	["new Date(2000,1,1)", "2000-02-01 00:00:00", false], | ||||
| 	["new Date(2000,2,1)", "2000-03-01 00:00:00", false], | ||||
| 	["new Date(2000,3,1)", "2000-04-01 00:00:00", false], | ||||
| 	["new Date(2000,0,1)", "2000-01-01 00:00:00", true], | ||||
| 	["new Date(2000,1,3)", "2000-02-03 00:00:00", true], | ||||
| 	["new Date(2000,2,5)", "2000-03-05 00:00:00", true], | ||||
| 	["new Date(2000,3,7)", "2000-04-07 00:00:00", true], | ||||
| 	["new Date(2100,0,1)", "2100-01-01 00:00:00", false], | ||||
| 	["new Date(2100,1,1)", "2100-02-01 00:00:00", false], | ||||
| 	["new Date(2100,2,1)", "2100-03-01 00:00:00", false], | ||||
| 	["new Date(2100,3,1)", "2100-04-01 00:00:00", false], | ||||
| 	["new Date(2100,0,1)", "2100-01-01 00:00:00", true], | ||||
| 	["new Date(2100,1,3)", "2100-02-03 00:00:00", true], | ||||
| 	["new Date(2100,2,5)", "2100-03-05 00:00:00", true], | ||||
| 	["new Date(2100,3,7)", "2100-04-07 00:00:00", true] | ||||
| ] | ||||
							
								
								
									
										12
									
								
								test/dates.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										12
									
								
								test/dates.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| /* vim: set ts=2: */ | ||||
| /*jshint -W041 */ | ||||
| /*jshint loopfunc:true, mocha:true, node:true */ | ||||
| var SSF = require('../'); | ||||
| var fs = require('fs'), assert = require('assert'); | ||||
| var data = JSON.parse(fs.readFileSync('./test/date.json','utf8')); | ||||
| 
 | ||||
| describe('date values', function() { | ||||
| 	it('should roundtrip dates', function() { data.forEach(function(d) { | ||||
| 		assert.equal(SSF.format("yyyy-mm-dd HH:MM:SS", eval(d[0]), {date1904:!!d[2]}), d[1]); | ||||
| 	}); }); | ||||
| }); | ||||
| @ -15,4 +15,12 @@ describe('General format', function() { | ||||
|     assert.equal(SSF.format("General", undefined), ""); | ||||
|     assert.equal(SSF.format("General", null), ""); | ||||
|   }); | ||||
|   it('should handle dates', function() { | ||||
|     assert.equal(SSF.format("General", new Date(2017, 1, 19)), "2/19/17"); | ||||
|     assert.equal(SSF.format("General", new Date(2017, 1, 19), {date1904:true}), "2/19/17"); | ||||
|     assert.equal(SSF.format("General", new Date(1901, 0, 1)), "1/1/01"); | ||||
|     if(SSF.format("General", new Date(1901, 0, 1), {date1904:true}) == "1/1/01") throw new Error("date1904 invalid date"); | ||||
|     assert.equal(SSF.format("General", new Date(1904, 0, 1)), "1/1/04"); | ||||
|     assert.equal(SSF.format("General", new Date(1904, 0, 1), {date1904:true}), "1/1/04"); | ||||
|   }); | ||||
| }); | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user