forked from sheetjs/sheetjs
		
	Miscellaneous format fixes
- SSF update to address extendscript issue (fixes #608 h/t @mjdb3d) - XLSX cellDates set date format (fixes #647 h/t @psalmody) - ODS add stub stylesheet for Excel (fixes #649 h/t @WaqasJaved1) - CSV with UTF8 BOM (fixes #650 h/t @charlesread) - DIF/CSV fuzzy date speculation - SYLK parse RC formulae - CSF utils for manipulating workbooks
This commit is contained in:
		
							parent
							
								
									a8736580a5
								
							
						
					
					
						commit
						7b4bafba49
					
				| @ -101,7 +101,7 @@ 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*/, opts/*:?any*/)/*:string*/ { return ""+v; } | ||||
| function general_fmt_int(v/*:number*/)/*: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)$/; | ||||
| @ -121,11 +121,9 @@ function gfn4(o) { | ||||
| 	return o; | ||||
| } | ||||
| function gfn5(o) { | ||||
| 	//for(var i = 0; i != o.length; ++i) if(o.charCodeAt(i) === 46) return o.replace(gnr2,"").replace(gnr1,".$1");
 | ||||
| 	//return o;
 | ||||
| 	return o.indexOf(".") > -1 ? o.replace(gnr2,"").replace(gnr1,".$1") : o; | ||||
| } | ||||
| return function general_fmt_num(v/*:number*/, opts/*:?any*/)/*:string*/ { | ||||
| return function general_fmt_num(v/*:number*/)/*: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); | ||||
| @ -134,18 +132,18 @@ return function general_fmt_num(v/*:number*/, opts/*:?any*/)/*:string*/ { | ||||
| 	return gfn5(gfn4(o)); | ||||
| };})(); | ||||
| SSF._general_num = general_fmt_num; | ||||
| function general_fmt(v/*:any*/, opts/*:?any*/) { | ||||
| function general_fmt(v/*: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/*, opts*/) : general_fmt_num(v/*, opts*/); | ||||
| 		case 'undefined': return ""; | ||||
| 		case 'object': if(v == null) return ""; | ||||
| 	} | ||||
| 	throw new Error("unsupported value in General format: " + v); | ||||
| } | ||||
| SSF._general = general_fmt; | ||||
| function fix_hijri(date, o) { return 0; } | ||||
| 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; | ||||
| @ -526,8 +524,7 @@ function write_num_int(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string | ||||
| 		case "##,###": | ||||
| 		case "#,###": var x = commaify(""+aval); return x !== "0" ? sign + x : ""; | ||||
| 		default: | ||||
| 			if(fmt.slice(-3) == ".00") return write_num_int(type, fmt.slice(0,-3), val) + ".00"; | ||||
| 			if(fmt.slice(-2) == ".0") return write_num_int(type, fmt.slice(0,-2), val) + ".0"; | ||||
| 			if(fmt.match(/\.[0#?]*$/)) return write_num_int(type, fmt.slice(0,fmt.lastIndexOf(".")), val) + hashq(fmt.slice(fmt.lastIndexOf("."))); | ||||
| 	} | ||||
| 	throw new Error("unsupported format |" + fmt + "|"); | ||||
| } | ||||
| @ -536,8 +533,8 @@ return function write_num(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:str | ||||
| };})(); | ||||
| function split_fmt(fmt/*:string*/)/*:Array<string>*/ { | ||||
| 	var out/*:Array<string>*/ = []; | ||||
| 	var in_str = false, cc; | ||||
| 	for(var i = 0, j = 0; i < fmt.length; ++i) switch((cc=fmt.charCodeAt(i))) { | ||||
| 	var in_str = false/*, cc*/; | ||||
| 	for(var i = 0, j = 0; i < fmt.length; ++i) switch((/*cc=*/fmt.charCodeAt(i))) { | ||||
| 		case 34: /* '"' */ | ||||
| 			in_str = !in_str; break; | ||||
| 		case 95: case 42: case 92: /* '_' '*' '\\' */ | ||||
| @ -553,11 +550,11 @@ function split_fmt(fmt/*:string*/)/*:Array<string>*/ { | ||||
| SSF._split = split_fmt; | ||||
| var abstime = /\[[HhMmSs]*\]/; | ||||
| function fmt_is_date(fmt/*:string*/)/*:boolean*/ { | ||||
| 	var i = 0, cc = 0, c = "", o = ""; | ||||
| 	var i = 0, /*cc = 0,*/ c = "", o = ""; | ||||
| 	while(i < fmt.length) { | ||||
| 		switch((c = fmt.charAt(i))) { | ||||
| 			case 'G': if(isgeneral(fmt, i)) i+= 6; i++; break; | ||||
| 			case '"': for(;(cc=fmt.charCodeAt(++i)) !== 34 && i < fmt.length;) ++i; ++i; break; | ||||
| 			case '"': for(;(/*cc=*/fmt.charCodeAt(++i)) !== 34 && i < fmt.length;) ++i; ++i; break; | ||||
| 			case '\\': i+=2; break; | ||||
| 			case '_': i+=2; break; | ||||
| 			case '@': ++i; break; | ||||
| @ -579,13 +576,13 @@ function fmt_is_date(fmt/*:string*/)/*:boolean*/ { | ||||
| 			case '.': | ||||
| 				/* falls through */ | ||||
| 			case '0': case '#': | ||||
| 				while(i < fmt.length && ("0#?.,E+-%".indexOf(c=fmt.charAt(++i)) > -1 || c=='\\' && fmt.charAt(i+1) == "-" && "0#".indexOf(fmt.charAt(i+2))>-1)){} | ||||
| 				while(i < fmt.length && ("0#?.,E+-%".indexOf(c=fmt.charAt(++i)) > -1 || (c=='\\' && fmt.charAt(i+1) == "-" && "0#".indexOf(fmt.charAt(i+2))>-1))){/* empty */} | ||||
| 				break; | ||||
| 			case '?': while(fmt.charAt(++i) === c){} break; | ||||
| 			case '?': while(fmt.charAt(++i) === c){/* empty */} break; | ||||
| 			case '*': ++i; if(fmt.charAt(i) == ' ' || fmt.charAt(i) == '*') ++i; break; | ||||
| 			case '(': case ')': ++i; break; | ||||
| 			case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': | ||||
| 				while(i < fmt.length && "0123456789".indexOf(fmt.charAt(++i)) > -1){} break; | ||||
| 				while(i < fmt.length && "0123456789".indexOf(fmt.charAt(++i)) > -1){/* empty */} break; | ||||
| 			case ' ': ++i; break; | ||||
| 			default: ++i; break; | ||||
| 		} | ||||
| @ -655,7 +652,7 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) { | ||||
| 				} | ||||
| 				/* falls through */ | ||||
| 			case '0': case '#': | ||||
| 				o = c; while(++i < fmt.length && "0#?.,E+-%".indexOf(c=fmt.charAt(i)) > -1 || c=='\\' && fmt.charAt(i+1) == "-" && i < fmt.length - 2 && "0#".indexOf(fmt.charAt(i+2))>-1) o += c; | ||||
| 				o = c; while((++i < fmt.length && "0#?.,E+-%".indexOf(c=fmt.charAt(i)) > -1) || (c=='\\' && fmt.charAt(i+1) == "-" && i < fmt.length - 2 && "0#".indexOf(fmt.charAt(i+2))>-1)) o += c; | ||||
| 				out[out.length] = {t:'n', v:o}; break; | ||||
| 			case '?': | ||||
| 				o = c; while(fmt.charAt(++i) === c) o+=c; | ||||
| @ -717,9 +714,9 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) { | ||||
| 				jj = i+1; | ||||
| 				while(out[jj] != null && ( | ||||
| 					(c=out[jj].t) === "?" || c === "D" || | ||||
| 					(c === " " || c === "t") && out[jj+1] != null && (out[jj+1].t === '?' || out[jj+1].t === "t" && out[jj+1].v === '/') || | ||||
| 					out[i].t === '(' && (c === ' ' || c === 'n' || c === ')') || | ||||
| 					c === 't' && (out[jj].v === '/' || out[jj].v === ' ' && out[jj+1] != null && out[jj+1].t == '?') | ||||
| 					((c === " " || c === "t") && out[jj+1] != null && (out[jj+1].t === '?' || out[jj+1].t === "t" && out[jj+1].v === '/')) || | ||||
| 					(out[i].t === '(' && (c === ' ' || c === 'n' || c === ')')) || | ||||
| 					(c === 't' && (out[jj].v === '/' || out[jj].v === ' ' && out[jj+1] != null && out[jj+1].t == '?')) | ||||
| 				)) { | ||||
| 					out[i].v += out[jj].v; | ||||
| 					out[jj] = {v:"", t:";"}; ++jj; | ||||
| @ -773,7 +770,7 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) { | ||||
| 			if(jj>=0 && lasti<out.length) out[lasti].v = ostr.substr(0,jj+1) + out[lasti].v; | ||||
| 			jj = ostr.indexOf(".")+1; | ||||
| 			for(i=decpt; i<out.length; ++i) { | ||||
| 				if(out[i] == null || 'n?('.indexOf(out[i].t) === -1 && i !== decpt ) continue; | ||||
| 				if(out[i] == null || ('n?('.indexOf(out[i].t) === -1 && i !== decpt)) continue; | ||||
| 				j=out[i].v.indexOf(".")>-1&&i===decpt?out[i].v.indexOf(".")+1:0; | ||||
| 				vv = out[i].v.substr(0,j); | ||||
| 				for(; j<out[i].v.length; ++j) { | ||||
|  | ||||
| @ -104,3 +104,16 @@ function dup(o/*:any*/)/*:any*/ { | ||||
| } | ||||
| 
 | ||||
| function fill(c/*:string*/,l/*:number*/)/*:string*/ { var o = ""; while(o.length < l) o+=c; return o; } | ||||
| 
 | ||||
| /* TODO: stress test */ | ||||
| function fuzzydate(s/*:string*/)/*:Date*/ { | ||||
| 	var o = new Date(s), n = new Date(NaN); | ||||
| 	var y = o.getYear(), m = o.getMonth(), d = o.getDate(); | ||||
| 	if(isNaN(d)) return n; | ||||
| 	if(y < 0 || y > 8099) return n; | ||||
| 	if((m > 0 || d > 1) && y != 101) return o; | ||||
| 	if(s.toLowerCase().match(/jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec/)) return o; | ||||
| 	if(!s.match(/[a-zA-Z]/)) return o; | ||||
| 	return n; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -89,11 +89,12 @@ function aoa_to_sheet(data/*:AOA*/, opts/*:?any*/)/*:Worksheet*/ { | ||||
| 		for(var C = 0; C != data[R].length; ++C) { | ||||
| 			if(typeof data[R][C] === 'undefined') continue; | ||||
| 			var cell/*:Cell*/ = ({v: data[R][C] }/*:any*/); | ||||
| 			if(Array.isArray(cell.v)) { cell.f = data[R][C][1]; cell.v = cell.v[0]; } | ||||
| 			if(range.s.r > R) range.s.r = R; | ||||
| 			if(range.s.c > C) range.s.c = C; | ||||
| 			if(range.e.r < R) range.e.r = R; | ||||
| 			if(range.e.c < C) range.e.c = C; | ||||
| 			if(cell.v === null) { if(!o.cellStubs) continue; cell.t = 'z'; } | ||||
| 			if(cell.v === null) { if(cell.f) cell.t = 'n'; else if(!o.cellStubs) continue; else cell.t = 'z'; } | ||||
| 			else if(typeof cell.v === 'number') cell.t = 'n'; | ||||
| 			else if(typeof cell.v === 'boolean') cell.t = 'b'; | ||||
| 			else if(cell.v instanceof Date) { | ||||
|  | ||||
| @ -55,3 +55,11 @@ function write_rdf(rdf, opts) { | ||||
| 	o.push('</rdf:RDF>'); | ||||
| 	return o.join(""); | ||||
| } | ||||
| /* TODO: pull properties */ | ||||
| var write_meta_ods/*:{(wb:any, opts:any):string}*/ = (function() { | ||||
| 	var payload = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><office:document-meta xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xlink="http://www.w3.org/1999/xlink" office:version="1.2"><office:meta><meta:generator>Sheet' + 'JS ' + XLSX.version + '</meta:generator></office:meta></office:document-meta>'; | ||||
| 	return function wmo(wb, opts) { | ||||
| 		return payload; | ||||
| 	}; | ||||
| })(); | ||||
| 
 | ||||
|  | ||||
| @ -160,7 +160,7 @@ function dbf_to_aoa(buf, opts)/*:AOA*/ { | ||||
| 				case 'T': | ||||
| 					var day = dd.read_shift(4), ms = dd.read_shift(4); | ||||
| 					throw new Error(day + " | " + ms); | ||||
| 					//out[R][C] = new Date(); // FIXME!!!
 | ||||
| 					//out[R][C] = new Date(); // TODO
 | ||||
| 					//break;
 | ||||
| 				case 'Y': out[R][C] = dd.read(4,'i')/1e4; break; | ||||
| 				case '0': | ||||
| @ -210,7 +210,9 @@ var SYLK = (function() { | ||||
| 		var Mval = 0, j; | ||||
| 		for (; ri !== records.length; ++ri) { | ||||
| 			Mval = 0; | ||||
| 			var rstr=records[ri].trim(), record=rstr.split(";"), RT=record[0], val; | ||||
| 			var rstr=records[ri].trim(); | ||||
| 			var record=rstr.replace(/;;/g, "\u0001").split(";").map(function(x) { return x.replace(/\u0001/g, ";"); }); | ||||
| 			var RT=record[0], val; | ||||
| 			if(rstr.length > 0) switch(RT) { | ||||
| 			case 'ID': break; /* header */ | ||||
| 			case 'E': break; /* EOF */ | ||||
| @ -240,17 +242,19 @@ var SYLK = (function() { | ||||
| 					next_cell_format = null; | ||||
| 					break; | ||||
| 				case 'E': | ||||
| 					/* formula = record[rj].substr(1); */ | ||||
| 					break; /* TODO: formula */ | ||||
| 					formula = rc_to_a1(record[rj].substr(1), {r:R,c:C}); | ||||
| 					arr[R][C] = [arr[R][C], formula]; | ||||
| 					break; | ||||
| 				default: if(opts && opts.WTF) throw new Error("SYLK bad record " + rstr); | ||||
| 			} break; | ||||
| 			case 'F': | ||||
| 			var F_seen = 0; | ||||
| 			for(rj=1; rj<record.length; ++rj) switch(record[rj].charAt(0)) { | ||||
| 				case 'X': C = parseInt(record[rj].substr(1))-1; break; | ||||
| 				case 'X': C = parseInt(record[rj].substr(1))-1; ++F_seen; break; | ||||
| 				case 'Y': | ||||
| 					R = parseInt(record[rj].substr(1))-1; C = 0; | ||||
| 					R = parseInt(record[rj].substr(1))-1; /*C = 0;*/ | ||||
| 					for(j = arr.length; j <= R; ++j) arr[j] = []; | ||||
| 					break; | ||||
| 					++F_seen; break; | ||||
| 				case 'M': Mval = parseInt(record[rj].substr(1)) / 20; break; | ||||
| 				case 'F': break; /* ??? */ | ||||
| 				case 'P': | ||||
| @ -265,14 +269,19 @@ var SYLK = (function() { | ||||
| 						Mval = parseInt(cw[2], 10); | ||||
| 						colinfo[j-1] = Mval == 0 ? {hidden:true}: {wch:Mval}; process_col(colinfo[j-1]); | ||||
| 					} break; | ||||
| 				case 'R': | ||||
| 				case 'C': /* default column format */ | ||||
| 					C = parseInt(record[rj].substr(1))-1; | ||||
| 					if(!colinfo[C]) colinfo[C] = {}; | ||||
| 					break; | ||||
| 				case 'R': /* row properties */ | ||||
| 					R = parseInt(record[rj].substr(1))-1; | ||||
| 					rowinfo[R] = {}; | ||||
| 					if(!rowinfo[R]) rowinfo[R] = {}; | ||||
| 					if(Mval > 0) { rowinfo[R].hpt = Mval; rowinfo[R].hpx = pt2px(Mval); } | ||||
| 					else if(Mval == 0) rowinfo[R].hidden = true; | ||||
| 					break; | ||||
| 				default: if(opts && opts.WTF) throw new Error("SYLK bad record " + rstr); | ||||
| 			} break; | ||||
| 			} | ||||
| 			if(F_seen < 2) next_cell_format = null; break; | ||||
| 			default: if(opts && opts.WTF) throw new Error("SYLK bad record " + rstr); | ||||
| 			} | ||||
| 		} | ||||
| @ -387,7 +396,7 @@ var DIF = (function() { | ||||
| 					if(data === 'TRUE') arr[R][C] = true; | ||||
| 					else if(data === 'FALSE') arr[R][C] = false; | ||||
| 					else if(+value == +value) arr[R][C] = +value; | ||||
| 					else if(!isNaN(new Date(value).getDate())) arr[R][C] = parseDate(value); | ||||
| 					else if(!isNaN(fuzzydate(value).getDate())) arr[R][C] = parseDate(value); | ||||
| 					else arr[R][C] = value; | ||||
| 					++C; break; | ||||
| 				case 1: | ||||
| @ -519,7 +528,7 @@ var PRN = (function() { | ||||
| 			else if(s == "TRUE") { cell.t = 'b'; cell.v = true; } | ||||
| 			else if(s == "FALSE") { cell.t = 'b'; cell.v = false; } | ||||
| 			else if(!isNaN(v = +s)) { cell.t = 'n'; cell.w = s; cell.v = v; } | ||||
| 			else if(!isNaN(new Date(s).getDate())) { cell.t = 'd'; cell.v = parseDate(s); } | ||||
| 			else if(!isNaN(fuzzydate(s).getDate())) { cell.t = 'd'; cell.v = parseDate(s); } | ||||
| 			else { | ||||
| 				cell.t = 's'; | ||||
| 				if(s.charAt(0) == '"' && s.charAt(s.length - 1) == '"') s = s.slice(1,-1).replace(/""/g,'"'); | ||||
| @ -550,13 +559,16 @@ var PRN = (function() { | ||||
| 	} | ||||
| 
 | ||||
| 	function prn_to_sheet(d/*:RawData*/, opts)/*:Worksheet*/ { | ||||
| 		var str = "", bytes = firstbyte(d, opts); | ||||
| 		switch(opts.type) { | ||||
| 			case 'base64': return prn_to_sheet_str(Base64.decode(d), opts); | ||||
| 			case 'binary': return prn_to_sheet_str(d, opts); | ||||
| 			case 'buffer': return prn_to_sheet_str(d.toString('binary'), opts); | ||||
| 			case 'array': return prn_to_sheet_str(cc2str(d), opts); | ||||
| 			case 'base64': str = Base64.decode(d); break; | ||||
| 			case 'binary': str = d; break; | ||||
| 			case 'buffer': str = d.toString('binary'); break; | ||||
| 			case 'array': str = cc2str(d); break; | ||||
| 			default: throw new Error("Unrecognized type " + opts.type); | ||||
| 		} | ||||
| 		throw new Error("Unrecognized type " + opts.type); | ||||
| 		if(bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF) str = utf8read(str); | ||||
| 		return prn_to_sheet_str(str, opts); | ||||
| 	} | ||||
| 
 | ||||
| 	function prn_to_workbook(str/*:string*/, opts)/*:Workbook*/ { return sheet_to_workbook(prn_to_sheet(str, opts), opts); } | ||||
|  | ||||
| @ -198,8 +198,8 @@ function write_ws_xml_cell(cell, ref, ws, opts, idx, wb) { | ||||
| 			else { | ||||
| 				cell.t = 'n'; | ||||
| 				vv = ''+(cell.v = datenum(parseDate(cell.v))); | ||||
| 				if(typeof cell.z === 'undefined') cell.z = SSF._table[14]; | ||||
| 			} | ||||
| 			if(typeof cell.z === 'undefined') cell.z = SSF._table[14]; | ||||
| 			break; | ||||
| 		default: vv = cell.v; break; | ||||
| 	} | ||||
| @ -312,8 +312,10 @@ return function parse_ws_xml_data(sdata, s, opts, guess, themes, styles) { | ||||
| 			} | ||||
| 
 | ||||
| 			if(tag.t == null && p.v === undefined) { | ||||
| 				if(!opts.sheetStubs) continue; | ||||
| 				p.t = "z"; | ||||
| 				if(p.f || p.F) { | ||||
| 					p.v = 0; p.t = "n"; | ||||
| 				} else if(!opts.sheetStubs) continue; | ||||
| 				else p.t = "z"; | ||||
| 			} | ||||
| 			else p.t = tag.t || "n"; | ||||
| 			if(guess.s.c > idx) guess.s.c = idx; | ||||
|  | ||||
| @ -99,11 +99,20 @@ function parse_wb_defaults(wb) { | ||||
| 	_ssfopts.date1904 = parsexmlbool(wb.WBProps.date1904, 'date1904'); | ||||
| } | ||||
| 
 | ||||
| var badchars = "][*?\/\\".split(""); | ||||
| function check_ws_name(n/*:string*/, safe/*:boolean*/)/*:boolean*/ { | ||||
| 	if(n.length > 31) { if(safe) return false; throw new Error("Sheet names cannot exceed 31 chars"); } | ||||
| 	var _good = true; | ||||
| 	badchars.forEach(function(c) { | ||||
| 		if(n.indexOf(c) == -1) return; | ||||
| 		if(!safe) throw new Error("Sheet name cannot contain : \\ / ? * [ ]"); | ||||
| 		_good = false; | ||||
| 	}); | ||||
| 	return _good; | ||||
| } | ||||
| function check_wb_names(N) { | ||||
| 	var badchars = "][*?\/\\".split(""); | ||||
| 	N.forEach(function(n,i) { | ||||
| 		badchars.forEach(function(c) { if(n.indexOf(c) > -1) throw new Error("Sheet name cannot contain : \\ / ? * [ ]"); }); | ||||
| 		if(n.length > 31) throw new Error("Sheet names cannot exceed 31 chars"); | ||||
| 		check_ws_name(n); | ||||
| 		for(var j = 0; j < i; ++j) if(n == N[j]) throw new Error("Duplicate Sheet Name: " + n); | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| @ -219,7 +219,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ { | ||||
| 					opts.enc = val; | ||||
| 					if(opts.WTF) console.error(val); | ||||
| 					if(!options.password) throw new Error("File is password-protected"); | ||||
| 					if(val.Type !== 0) throw new Error("Encryption scheme unsupported"); | ||||
| 					if(val.valid == null) throw new Error("Encryption scheme unsupported"); | ||||
| 					if(!val.valid) throw new Error("Password is incorrect"); | ||||
| 					break; | ||||
| 				case 'WriteAccess': opts.lastuser = val; break; | ||||
| @ -236,7 +236,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ { | ||||
| 				case 'Template': break; // TODO
 | ||||
| 				case 'RefreshAll': wb.opts.RefreshAll = val; break; | ||||
| 				case 'BookBool': break; // TODO
 | ||||
| 				case 'UsesELFs': /* if(val) console.error("Unsupported ELFs"); */ break; | ||||
| 				case 'UsesELFs': break; | ||||
| 				case 'MTRSettings': break; | ||||
| 				case 'CalcCount': wb.opts.CalcCount = val; break; | ||||
| 				case 'CalcDelta': wb.opts.CalcDelta = val; break; | ||||
|  | ||||
| @ -1,3 +1,4 @@ | ||||
| /* OpenDocument */ | ||||
| var parse_content_xml = (function() { | ||||
| 
 | ||||
| 	var parse_text_p = function(text, tag) { | ||||
| @ -437,3 +438,16 @@ var parse_content_xml = (function() { | ||||
| 		return out; | ||||
| 	}; | ||||
| })(); | ||||
| 
 | ||||
| function parse_ods(zip/*:ZIPFile*/, opts/*:?ParseOpts*/) { | ||||
| 	opts = opts || ({}/*:any*/); | ||||
| 	var ods = !!safegetzipfile(zip, 'objectdata'); | ||||
| 	if(ods) var manifest = parse_manifest(getzipdata(zip, 'META-INF/manifest.xml'), opts); | ||||
| 	var content = getzipstr(zip, 'content.xml'); | ||||
| 	if(!content) throw new Error("Missing content.xml in " + (ods ? "ODS" : "UOF")+ " file"); | ||||
| 	return parse_content_xml(ods ? content : utf8read(content), opts); | ||||
| } | ||||
| function parse_fods(data/*:string*/, opts/*:?ParseOpts*/) { | ||||
| 	return parse_content_xml(data, opts); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -1,4 +1,11 @@ | ||||
| var write_content_xml/*:{(wb:any, opts:any):string}*/ = (function() { | ||||
| /* OpenDocument */ | ||||
| var write_styles_ods/*:{(wb:any, opts:any):string}*/ = (function() { | ||||
| 	var payload = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><office:document-styles xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" office:version="1.2"></office:document-styles>'; | ||||
| 	return function wso(wb, opts) { | ||||
| 		return payload; | ||||
| 	}; | ||||
| })(); | ||||
| var write_content_ods/*:{(wb:any, opts:any):string}*/ = (function() { | ||||
| 	var null_cell_xml = '          <table:table-cell />\n'; | ||||
| 	var covered_cell_xml = '          <table:covered-table-cell/>\n'; | ||||
| 	var write_ws = function(ws, wb, i/*:number*/, opts)/*:string*/ { | ||||
| @ -61,7 +68,7 @@ var write_content_xml/*:{(wb:any, opts:any):string}*/ = (function() { | ||||
| 					//case 'e':
 | ||||
| 					default: o.push(null_cell_xml); continue; | ||||
| 				} | ||||
| 				o.push(writextag('table:table-cell', writextag('text:p', textp, {}), ct)); | ||||
| 				o.push('          ' + writextag('table:table-cell', writextag('text:p', textp, {}), ct) + '\n'); | ||||
| 			} | ||||
| 			o.push('        </table:table-row>\n'); | ||||
| 		} | ||||
| @ -143,3 +150,48 @@ var write_content_xml/*:{(wb:any, opts:any):string}*/ = (function() { | ||||
| 		return o.join(""); | ||||
| 	}; | ||||
| })(); | ||||
| 
 | ||||
| function write_ods(wb/*:any*/, opts/*:any*/) { | ||||
| 	if(opts.bookType == "fods") return write_content_ods(wb, opts); | ||||
| 
 | ||||
| 	/*:: if(!jszip) throw new Error("JSZip is not available"); */ | ||||
| 	var zip = new jszip(); | ||||
| 	var f = ""; | ||||
| 
 | ||||
| 	var manifest/*:Array<Array<string> >*/ = []; | ||||
| 	var rdf = []; | ||||
| 
 | ||||
| 	/* 3:3.3 and 2:2.2.4 */ | ||||
| 	f = "mimetype"; | ||||
| 	zip.file(f, "application/vnd.oasis.opendocument.spreadsheet"); | ||||
| 
 | ||||
| 	/* Part 1 Section 2.2 Documents */ | ||||
| 	f = "content.xml"; | ||||
| 	zip.file(f, write_content_ods(wb, opts)); | ||||
| 	manifest.push([f, "text/xml"]); | ||||
| 	rdf.push([f, "ContentFile"]); | ||||
| 
 | ||||
| 	/* TODO: these are hard-coded styles to satiate excel */ | ||||
| 	f = "styles.xml"; | ||||
| 	zip.file(f, write_styles_ods(wb, opts)); | ||||
| 	manifest.push([f, "text/xml"]); | ||||
| 	rdf.push([f, "StylesFile"]); | ||||
| 
 | ||||
| 	/* Part 3 Section 6 Metadata Manifest File */ | ||||
| 	f = "manifest.rdf"; | ||||
| 	zip.file(f, write_rdf(rdf, opts)); | ||||
| 	manifest.push([f, "application/rdf+xml"]); | ||||
| 
 | ||||
| 	/* TODO: this is hard-coded to satiate excel */ | ||||
| 	f = "meta.xml"; | ||||
| 	zip.file(f, write_meta_ods(wb, opts)); | ||||
| 	manifest.push([f, "text/xml"]); | ||||
| 	rdf.push([f, "MetadataFile"]); | ||||
| 
 | ||||
| 	/* Part 3 Section 4 Manifest File */ | ||||
| 	f = "META-INF/manifest.xml"; | ||||
| 	zip.file(f, write_manifest(manifest, opts)); | ||||
| 
 | ||||
| 	return zip; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -1,44 +0,0 @@ | ||||
| /* Part 3: Packages */ | ||||
| function parse_ods(zip/*:ZIPFile*/, opts/*:?ParseOpts*/) { | ||||
| 	opts = opts || ({}/*:any*/); | ||||
| 	var ods = !!safegetzipfile(zip, 'objectdata'); | ||||
| 	if(ods) var manifest = parse_manifest(getzipdata(zip, 'META-INF/manifest.xml'), opts); | ||||
| 	var content = getzipstr(zip, 'content.xml'); | ||||
| 	if(!content) throw new Error("Missing content.xml in " + (ods ? "ODS" : "UOF")+ " file"); | ||||
| 	return parse_content_xml(ods ? content : utf8read(content), opts); | ||||
| } | ||||
| function parse_fods(data/*:string*/, opts/*:?ParseOpts*/) { | ||||
| 	return parse_content_xml(data, opts); | ||||
| } | ||||
| 
 | ||||
| function write_ods(wb/*:any*/, opts/*:any*/) { | ||||
| 	if(opts.bookType == "fods") return write_content_xml(wb, opts); | ||||
| 
 | ||||
| 	/*:: if(!jszip) throw new Error("JSZip is not available"); */ | ||||
| 	var zip = new jszip(); | ||||
| 	var f = ""; | ||||
| 
 | ||||
| 	var manifest/*:Array<Array<string> >*/ = []; | ||||
| 	var rdf = []; | ||||
| 
 | ||||
| 	/* 3:3.3 and 2:2.2.4 */ | ||||
| 	f = "mimetype"; | ||||
| 	zip.file(f, "application/vnd.oasis.opendocument.spreadsheet"); | ||||
| 
 | ||||
| 	/* Part 1 Section 2.2 Documents */ | ||||
| 	f = "content.xml"; | ||||
| 	zip.file(f, write_content_xml(wb, opts)); | ||||
| 	manifest.push([f, "text/xml"]); | ||||
| 	rdf.push([f, "ContentFile"]); | ||||
| 
 | ||||
| 	/* Part 3 Section 6 Metadata Manifest File */ | ||||
| 	f = "manifest.rdf"; | ||||
| 	zip.file(f, write_rdf(rdf, opts)); | ||||
| 	manifest.push([f, "application/rdf+xml"]); | ||||
| 
 | ||||
| 	/* Part 3 Section 4 Manifest File */ | ||||
| 	f = "META-INF/manifest.xml"; | ||||
| 	zip.file(f, write_manifest(manifest, opts)); | ||||
| 
 | ||||
| 	return zip; | ||||
| } | ||||
| @ -52,7 +52,7 @@ function readSync(data/*:RawData*/, opts/*:?ParseOpts*/)/*:Workbook*/ { | ||||
| 		case 0x49: if(n[1] == 0x44) return SYLK.to_workbook(d, o); break; | ||||
| 		case 0x54: if(n[1] == 0x41 && n[2] == 0x42 && n[3] == 0x4C) return DIF.to_workbook(d, o); break; | ||||
| 		case 0x50: if(n[1] == 0x4B && n[2] < 0x20 && n[3] < 0x20) return read_zip(d, o); break; | ||||
| 		case 0xEF: return parse_xlml(d, o); | ||||
| 		case 0xEF: return n[3] == 0x3C ? parse_xlml(d, o) : PRN.to_workbook(d,o); | ||||
| 		case 0xFF: if(n[1] == 0xFE){ return read_utf16(d, o); } break; | ||||
| 		case 0x00: if(n[1] == 0x00 && n[2] >= 0x02 && n[3] == 0x00) return WK_.to_workbook(d, o); break; | ||||
| 		case 0x03: case 0x83: case 0x8B: return DBF.to_workbook(d, o); | ||||
|  | ||||
| @ -21,7 +21,7 @@ function sheet_to_json(sheet/*:Worksheet*/, opts/*:?Sheet2JSONOpts*/){ | ||||
| 	var outi = 0, counter = 0; | ||||
| 	var dense = Array.isArray(sheet); | ||||
| 	var R = r.s.r, C = 0, CC = 0; | ||||
| 	if(!sheet[R]) sheet[R] = []; | ||||
| 	if(dense && !sheet[R]) sheet[R] = []; | ||||
| 	for(C = r.s.c; C <= r.e.c; ++C) { | ||||
| 		cols[C] = encode_col(C); | ||||
| 		val = dense ? sheet[R][C] : sheet[cols[C] + rr]; | ||||
| @ -189,3 +189,4 @@ var utils = { | ||||
| 	sheet_to_formulae: sheet_to_formulae, | ||||
| 	sheet_to_row_object_array: sheet_to_json | ||||
| }; | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										103
									
								
								bits/95_api.js
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										103
									
								
								bits/95_api.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,103 @@ | ||||
| (function(utils) { | ||||
| utils.consts = utils.consts || {}; | ||||
| function add_consts(R) { R.forEach(function(a){ utils.consts[a[0]] = a[1]; }); } | ||||
| 
 | ||||
| function get_default(x, y, z) { return x[y] != null ? x[y] : (x[y] = z); } | ||||
| 
 | ||||
| /* get cell, creating a stub if necessary */ | ||||
| function ws_get_cell_stub(ws/*:Worksheet*/, R, C/*:?number*/)/*:Cell*/ { | ||||
| 	/* A1 cell address */ | ||||
| 	if(typeof R == "string") return ws[R] || (ws[R] = {t:'z'}); | ||||
| 	/* cell address object */ | ||||
| 	if(typeof R != "number") return ws_get_cell_stub(ws, encode_cell(R)); | ||||
| 	/* R and C are 0-based indices */ | ||||
| 	return ws_get_cell_stub(ws, encode_cell({r:R,c:C})); | ||||
| } | ||||
| 
 | ||||
| /* find sheet index for given name / validate index */ | ||||
| function wb_sheet_idx(wb/*:Workbook*/, sh/*:number|string*/) { | ||||
| 	if(typeof sh == "number") { | ||||
| 		if(sh >= 0 && wb.SheetNames.length > sh) return sh; | ||||
| 		throw new Error("Cannot find sheet # " + sh); | ||||
| 	} else if(typeof sh == "string") { | ||||
| 		var idx = wb.SheetNames.indexOf(sh); | ||||
| 		if(idx > -1) return idx; | ||||
| 		throw new Error("Cannot find sheet name |" + sh + "|"); | ||||
| 	} else throw new Error("Cannot find sheet |" + sh + "|"); | ||||
| } | ||||
| 
 | ||||
| /* simple blank workbook object */ | ||||
| utils.book_new = function()/*:Workbook*/ { | ||||
| 	return { SheetNames: [], Sheets: {} }; | ||||
| }; | ||||
| 
 | ||||
| /* add a worksheet to the end of a given workbook */ | ||||
| utils.book_append_sheet = function(wb/*:Workbook*/, ws/*:Worksheet*/, name/*:?string*/) { | ||||
| 	if(!name) for(var i = 1; i <= 0xFFFF; ++i) if(wb.SheetNames.indexOf("Sheet" + i) == -1) break; | ||||
| 	check_ws_name(name); | ||||
| 	if(wb.SheetNames.indexOf(name) >= 0) throw new Error("Worksheet with name |" + name + "| already exists!"); | ||||
| 
 | ||||
| 	wb.SheetNames.push(name); | ||||
| 	wb.Sheets[name] = ws; | ||||
| }; | ||||
| 
 | ||||
| /* set sheet visibility (visible/hidden/very hidden) */ | ||||
| utils.book_set_sheet_visibility = function(wb/*:Workbook*/, sh/*:number|string*/, vis/*:number*/) { | ||||
| 	get_default(wb,"Workbook",{}); | ||||
| 	get_default(wb.Workbook,"Sheets",[]); | ||||
| 
 | ||||
| 	var idx = wb_sheet_idx(wb, sh); | ||||
| 	get_default(wb.Workbook.Sheets,idx, {}); | ||||
| 
 | ||||
| 	switch(vis) { | ||||
| 		case 0: case 1: case 2: break; | ||||
| 		default: throw new Error("Bad sheet visibility setting " + vis); | ||||
| 	} | ||||
| 	wb.Workbook.Sheets[idx].Hidden = vis; | ||||
| }; | ||||
| add_consts([ | ||||
| 	["SHEET_VISIBLE", 0], | ||||
| 	["SHEET_HIDDEN", 1], | ||||
| 	["SHEET_VERY_HIDDEN", 2] | ||||
| ]); | ||||
| 
 | ||||
| /* set number format */ | ||||
| utils.cell_set_number_format = function(cell/*:Cell*/, fmt/*:string|number*/) { | ||||
| 	cell.z = fmt; | ||||
| 	return cell; | ||||
| }; | ||||
| 
 | ||||
| /* set cell hyperlink */ | ||||
| utils.cell_set_hyperlink = function(cell/*:Cell*/, target/*:string*/, tooltip/*:?string*/) { | ||||
| 	if(!target) { | ||||
| 		delete cell.l; | ||||
| 	} else { | ||||
| 		cell.l = { Target: target }; | ||||
| 		if(tooltip) cell.l.Tooltip = tooltip; | ||||
| 	} | ||||
| 	return cell; | ||||
| }; | ||||
| 
 | ||||
| /* add to cell comments */ | ||||
| utils.cell_add_comment = function(cell/*:Cell*/, text/*:string*/, author/*:?string*/) { | ||||
| 	if(!cell.c) cell.c = []; | ||||
| 	cell.c.push({t:text, a:author||"SheetJS"}); | ||||
| }; | ||||
| 
 | ||||
| /* set array formula and flush related cells */ | ||||
| utils.sheet_set_array_formula = function(ws/*:Worksheet*/, range, formula/*:string*/) { | ||||
| 	var rng = typeof range != "string" ? range : safe_decode_range(range); | ||||
| 	var rngstr = typeof range == "string" ? range : encode_range(range); | ||||
| 	for(var R = rng.s.r; R <= rng.e.r; ++R) for(var C = rng.s.c; C <= rng.e.c; ++C) { | ||||
| 		var cell = ws_get_cell_stub(ws, R, C); | ||||
| 		cell.t = 'n'; | ||||
| 		cell.F = rngstr; | ||||
| 		delete cell.v;  | ||||
| 		if(R == rng.s.r && C == rng.s.c) cell.f = formula; | ||||
| 	} | ||||
| 	return ws; | ||||
| } | ||||
| 
 | ||||
| return utils; | ||||
| })(utils); | ||||
| 
 | ||||
| @ -60,3 +60,4 @@ if(has_buf && typeof require != 'undefined') (function() { | ||||
| 		to_csv: write_csv_stream | ||||
| 	}; | ||||
| })(); | ||||
| 
 | ||||
|  | ||||
| @ -26,8 +26,8 @@ named_ranges_2011       	.xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml | ||||
| # yes-formula | ||||
| # no-csv (macro serialization in xml) | ||||
| number_format           	.xls .xlsb .xlsm .xls.xml .xlsb.xml .xlsm.xml | ||||
| number_format_entities  	.xls .xlsb .xlsx .xml | ||||
| # yes-csv | ||||
| number_format_entities  	.xls .xlsb .xlsx .xml | ||||
| pivot_table_named_range 	.xls .xlsb .xlsx .xml | ||||
| pivot_table_test        	.xls .xlsb .xlsm | ||||
| rich_text_stress        	.xls .xlsb .xlsx .xls.xml .xlsb.xml .xlsx.xml | ||||
|  | ||||
							
								
								
									
										37
									
								
								test.js
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										37
									
								
								test.js
									
									
									
									
									
								
							| @ -548,16 +548,19 @@ describe('input formats', function() { | ||||
| 		X.read(fs.readFileSync(paths.cstxlsb, 'binary'), {type: 'binary'}); | ||||
| 		X.read(fs.readFileSync(paths.cstxls, 'binary'), {type: 'binary'}); | ||||
| 		X.read(fs.readFileSync(paths.cstxml, 'binary'), {type: 'binary'}); | ||||
| 		X.read(fs.readFileSync(paths.cstods, 'binary'), {type: 'binary'}); | ||||
| 	}); | ||||
| 	it('should read base64 strings', function() { | ||||
| 		X.read(fs.readFileSync(paths.cstxls, 'base64'), {type: 'base64'}); | ||||
| 		X.read(fs.readFileSync(paths.cstxml, 'base64'), {type: 'base64'}); | ||||
| 		X.read(fs.readFileSync(paths.cstods, 'base64'), {type: 'base64'}); | ||||
| 		X.read(fs.readFileSync(paths.cstxlsx, 'base64'), {type: 'base64'}); | ||||
| 		X.read(fs.readFileSync(paths.cstxlsb, 'base64'), {type: 'base64'}); | ||||
| 	}); | ||||
| 	it('should read buffers', function() { | ||||
| 		X.read(fs.readFileSync(paths.cstxls), {type: 'buffer'}); | ||||
| 		X.read(fs.readFileSync(paths.cstxml), {type: 'buffer'}); | ||||
| 		X.read(fs.readFileSync(paths.cstods), {type: 'buffer'}); | ||||
| 		X.read(fs.readFileSync(paths.cstxlsx), {type: 'buffer'}); | ||||
| 		X.read(fs.readFileSync(paths.cstxlsb), {type: 'buffer'}); | ||||
| 	}); | ||||
| @ -571,12 +574,14 @@ describe('input formats', function() { | ||||
| 	it('should throw if format is unknown', function() { | ||||
| 		assert.throws(function() { X.read(fs.readFileSync(paths.cstxls), {type: 'dafuq'}); }); | ||||
| 		assert.throws(function() { X.read(fs.readFileSync(paths.cstxml), {type: 'dafuq'}); }); | ||||
| 		assert.throws(function() { X.read(fs.readFileSync(paths.cstods), {type: 'dafuq'}); }); | ||||
| 		assert.throws(function() { X.read(fs.readFileSync(paths.cstxlsx), {type: 'dafuq'}); }); | ||||
| 		assert.throws(function() { X.read(fs.readFileSync(paths.cstxlsb), {type: 'dafuq'}); }); | ||||
| 	}); | ||||
| 	it('should infer buffer type', function() { | ||||
| 		X.read(fs.readFileSync(paths.cstxls)); | ||||
| 		X.read(fs.readFileSync(paths.cstxml)); | ||||
| 		X.read(fs.readFileSync(paths.cstods)); | ||||
| 		X.read(fs.readFileSync(paths.cstxlsx)); | ||||
| 		X.read(fs.readFileSync(paths.cstxlsb)); | ||||
| 	}); | ||||
| @ -809,8 +814,9 @@ describe('parse features', function() { | ||||
| 			var wb2 = X.readFile(paths.fstxlsb, opts); | ||||
| 			var wb3 = X.readFile(paths.fstxls, opts); | ||||
| 			var wb4 = X.readFile(paths.fstxml, opts); | ||||
| 			var wb5 = X.readFile(paths.fstods, opts); | ||||
| 			/* TODO */ | ||||
| 			[wb1, wb2 /*, wb3, wb4 */].forEach(function(wb) { | ||||
| 			[wb1, wb2 /*, wb3, wb4, wb5 */].forEach(function(wb) { | ||||
| 				assert.equal(wb.Sheets.Text["!fullref"],"A1:F49"); | ||||
| 				assert.equal(wb.Sheets.Text["!ref"],"A1:F10"); | ||||
| 			}); | ||||
| @ -821,8 +827,9 @@ describe('parse features', function() { | ||||
| 			var wb2 = X.readFile(paths.cstxlsb, opts); | ||||
| 			var wb3 = X.readFile(paths.cstxls, opts); | ||||
| 			var wb4 = X.readFile(paths.cstxml, opts); | ||||
| 			var wb5 = X.readFile(paths.cstods, opts); | ||||
| 			/* TODO */ | ||||
| 			[wb1, wb2 /*, wb3, wb4 */].forEach(function(wb) { | ||||
| 			[wb1, wb2 /*, wb3, wb4, wb5 */].forEach(function(wb) { | ||||
| 				assert.equal(wb.Sheets.Sheet7["!fullref"],"A1:N34"); | ||||
| 				assert.equal(wb.Sheets.Sheet7["!ref"],"A1"); | ||||
| 			}); | ||||
| @ -1408,8 +1415,12 @@ describe('roundtrip features', function() { | ||||
| 	}); | ||||
| }); | ||||
| 
 | ||||
| function password_file(x){return x.match(/^password.*\.xls$/); } | ||||
| var password_files = fs.readdirSync('test_files').filter(password_file); | ||||
| //function password_file(x){return x.match(/^password.*\.xls$/); }
 | ||||
| //var password_files = fs.readdirSync('test_files').filter(password_file);
 | ||||
| var password_files = [ | ||||
| 	//"password_2002_40_972000.xls",
 | ||||
| 	"password_2002_40_xor.xls" | ||||
| ]; | ||||
| describe('invalid files', function() { | ||||
| 	describe('parse', function() { [ | ||||
| 			['password', 'apachepoi_password.xls'], | ||||
| @ -1765,10 +1776,20 @@ describe('encryption', function() { | ||||
| 	password_files.forEach(function(x) { | ||||
| 		describe(x, function() { | ||||
| 			it('should throw with no password', function() {assert.throws(function() { X.readFile(dir + x); }); }); | ||||
| 			it('should throw with wrong password', function() {assert.throws(function() { X.readFile(dir + x, {password:'passwor',WTF:opts.WTF}); }); }); | ||||
| 			it.skip('should recognize correct password', function() { | ||||
| 				try { X.readFile(dir + x, {password:'password',WTF:opts.WTF}); } | ||||
| 				catch(e) { if(e.message == "Password is incorrect") throw e; } | ||||
| 			it('should throw with wrong password', function() { | ||||
| 				try { | ||||
| 					X.readFile(dir + x, {password:'passwor',WTF:opts.WTF}); | ||||
| 					throw new Error("incorrect password was accepted"); | ||||
| 				} catch(e) { | ||||
| 					if(e.message != "Password is incorrect") throw e; | ||||
| 				} | ||||
| 			}); | ||||
| 			it('should recognize correct password', function() { | ||||
| 				try { | ||||
| 					X.readFile(dir + x, {password:'password',WTF:opts.WTF}); | ||||
| 				} catch(e) { | ||||
| 					if(e.message == "Password is incorrect") throw e; | ||||
| 				} | ||||
| 			}); | ||||
| 			it.skip('should decrypt file', function() { | ||||
| 				var wb = X.readFile(dir + x, {password:'password',WTF:opts.WTF}); | ||||
|  | ||||
| @ -28,7 +28,7 @@ var wsrows = [ | ||||
| 	{hpx: 24}, | ||||
| 	{hidden:true}, // hide row
 | ||||
| 	{hidden:false} | ||||
| ] | ||||
| ]; | ||||
| 
 | ||||
| console.log("Sheet Name: " + ws_name); | ||||
| console.log("Data: "); for(var i=0; i!=data.length; ++i) console.log(data[i]); | ||||
| @ -40,27 +40,39 @@ console.log("Columns :"); for(i=0; i!=wscols.length;++i) console.log(wscols[i]); | ||||
| if(typeof XLSX === "undefined") { try { XLSX = require('./'); } catch(e) { XLSX = require('../'); } } | ||||
| 
 | ||||
| /* blank workbook constructor */ | ||||
| /* | ||||
| var wb = { SheetNames: [], Sheets: {} }; | ||||
| */ | ||||
| var wb = XLSX.utils.book_new(); | ||||
| 
 | ||||
| /* convert an array of arrays in JS to a CSF spreadsheet */ | ||||
| var ws = XLSX.utils.aoa_to_sheet(data, {cellDates:true}); | ||||
| 
 | ||||
| /* TEST: add worksheet to workbook */ | ||||
| /* | ||||
| wb.SheetNames.push(ws_name); | ||||
| wb.Sheets[ws_name] = ws; | ||||
| */ | ||||
| XLSX.utils.book_append_sheet(wb, ws, ws_name); | ||||
| 
 | ||||
| /* TEST: simple formula */ | ||||
| ws['C1'].f = "A1+B1"; | ||||
| ws['C2'] = {t:'n', f:"A1+B1"}; | ||||
| 
 | ||||
| /* TEST: single-cell array formula */ | ||||
| /* | ||||
| ws['D1'] = {t:'n', f:"SUM(A1:C1*A1:C1)", F:"D1:D1"}; | ||||
| */ | ||||
| XLSX.utils.sheet_set_array_formula(ws, 'D1:D1', "SUM(A1:C1*A1:C1)"); | ||||
| 
 | ||||
| /* TEST: multi-cell array formula */ | ||||
| /* | ||||
| ws['E1'] = {t:'n', f:"TRANSPOSE(A1:D1)", F:"E1:E4"}; | ||||
| ws['E2'] = {t:'n', F:"E1:E4"}; | ||||
| ws['E3'] = {t:'n', F:"E1:E4"}; | ||||
| ws['E4'] = {t:'n', F:"E1:E4"}; | ||||
| */ | ||||
| XLSX.utils.sheet_set_array_formula(ws, 'E1:E4', "TRANSPOSE(A1:D1)"); | ||||
| ws["!ref"] = "A1:E6"; | ||||
| 
 | ||||
| /* TEST: column props */ | ||||
| @ -70,25 +82,40 @@ ws['!cols'] = wscols; | ||||
| ws['!rows'] = wsrows; | ||||
| 
 | ||||
| /* TEST: hyperlink note: Excel does not automatically style hyperlinks */ | ||||
| /* | ||||
| ws['A3'].l = { Target: "http://sheetjs.com", Tooltip: "Visit us <SheetJS.com!>" }; | ||||
| */ | ||||
| XLSX.utils.cell_set_hyperlink(ws['A3'], "http://sheetjs.com", "Visit us <SheetJS.com!>" ); | ||||
| 
 | ||||
| /* TEST: built-in format */ | ||||
| /* | ||||
| ws['B1'].z = "0%"; // Format Code 9
 | ||||
| */ | ||||
| XLSX.utils.cell_set_number_format(ws['B1'], "0%"); | ||||
| 
 | ||||
| /* TEST: custom format */ | ||||
| var custfmt = "\"This is \"\\ 0.0"; | ||||
| /* | ||||
| ws['C2'].z = custfmt; | ||||
| */ | ||||
| XLSX.utils.cell_set_number_format(ws['C2'], custfmt); | ||||
| 
 | ||||
| /* TEST: page margins */ | ||||
| ws['!margins'] =  { left:1.0, right:1.0, top:1.0, bottom:1.0, header:0.5, footer:0.5 }; | ||||
| 
 | ||||
| console.log("JSON Data:");console.log(XLSX.utils.sheet_to_json(ws, {header:1})); | ||||
| console.log("JSON Data:"); | ||||
| console.log(XLSX.utils.sheet_to_json(ws, {header:1})); | ||||
| 
 | ||||
| /* TEST: hidden sheets */ | ||||
| /* | ||||
| wb.SheetNames.push("Hidden"); | ||||
| wb.Sheets["Hidden"] = XLSX.utils.aoa_to_sheet(["Hidden".split(""), [1,2,3]]); | ||||
| wb.Workbook = {Sheets:[]}; | ||||
| wb.Workbook.Sheets[1] = {Hidden:1}; | ||||
| */ | ||||
| var data_2 = ["Hidden".split(""), [1,2,3]]; | ||||
| XLSX.utils.book_append_sheet(wb, XLSX.utils.aoa_to_sheet(data_2), "Hidden"); | ||||
| XLSX.utils.book_set_sheet_visibility(wb, "Hidden", XLSX.utils.consts.SHEET_HIDDEN); | ||||
| 
 | ||||
| /* TEST: properties */ | ||||
| wb.Props = { | ||||
| @ -102,11 +129,14 @@ wb.Props = { | ||||
| 	Comments: "Nothing to say here", | ||||
| 	LastAuthor: "Not SheetJS", | ||||
| 	CreatedDate: new Date(2017,1,19) | ||||
| } | ||||
| }; | ||||
| 
 | ||||
| /* TEST: comments */ | ||||
| /* | ||||
| ws['A4'].c = []; | ||||
| ws['A4'].c.push({a:"SheetJS",t:"I'm a little comment, short and stout!\n\nWell, Stout may be the wrong word"}); | ||||
| */ | ||||
| XLSX.utils.cell_add_comment(ws['A4'], "I'm a little comment, short and stout!\n\nWell, Stout may be the wrong word", "SheetJS"); | ||||
| 
 | ||||
| /* TEST: sheet protection */ | ||||
| ws['!protect'] = { | ||||
| @ -119,7 +149,7 @@ ws['!protect'] = { | ||||
| 	scenarios:1 | ||||
| }; | ||||
| 
 | ||||
| console.log("Worksheet Model:") | ||||
| console.log("Worksheet Model:"); | ||||
| console.log(ws); | ||||
| 
 | ||||
| [ | ||||
|  | ||||
							
								
								
									
										359
									
								
								xlsx.flow.js
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										359
									
								
								xlsx.flow.js
									
									
									
									
									
								
							| @ -218,7 +218,7 @@ 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*/, opts/*:?any*/)/*:string*/ { return ""+v; } | ||||
| function general_fmt_int(v/*:number*/)/*: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)$/; | ||||
| @ -238,11 +238,9 @@ function gfn4(o) { | ||||
| 	return o; | ||||
| } | ||||
| function gfn5(o) { | ||||
| 	//for(var i = 0; i != o.length; ++i) if(o.charCodeAt(i) === 46) return o.replace(gnr2,"").replace(gnr1,".$1");
 | ||||
| 	//return o;
 | ||||
| 	return o.indexOf(".") > -1 ? o.replace(gnr2,"").replace(gnr1,".$1") : o; | ||||
| } | ||||
| return function general_fmt_num(v/*:number*/, opts/*:?any*/)/*:string*/ { | ||||
| return function general_fmt_num(v/*:number*/)/*: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); | ||||
| @ -251,18 +249,18 @@ return function general_fmt_num(v/*:number*/, opts/*:?any*/)/*:string*/ { | ||||
| 	return gfn5(gfn4(o)); | ||||
| };})(); | ||||
| SSF._general_num = general_fmt_num; | ||||
| function general_fmt(v/*:any*/, opts/*:?any*/) { | ||||
| function general_fmt(v/*: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/*, opts*/) : general_fmt_num(v/*, opts*/); | ||||
| 		case 'undefined': return ""; | ||||
| 		case 'object': if(v == null) return ""; | ||||
| 	} | ||||
| 	throw new Error("unsupported value in General format: " + v); | ||||
| } | ||||
| SSF._general = general_fmt; | ||||
| function fix_hijri(date, o) { return 0; } | ||||
| 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; | ||||
| @ -643,8 +641,7 @@ function write_num_int(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string | ||||
| 		case "##,###": | ||||
| 		case "#,###": var x = commaify(""+aval); return x !== "0" ? sign + x : ""; | ||||
| 		default: | ||||
| 			if(fmt.slice(-3) == ".00") return write_num_int(type, fmt.slice(0,-3), val) + ".00"; | ||||
| 			if(fmt.slice(-2) == ".0") return write_num_int(type, fmt.slice(0,-2), val) + ".0"; | ||||
| 			if(fmt.match(/\.[0#?]*$/)) return write_num_int(type, fmt.slice(0,fmt.lastIndexOf(".")), val) + hashq(fmt.slice(fmt.lastIndexOf("."))); | ||||
| 	} | ||||
| 	throw new Error("unsupported format |" + fmt + "|"); | ||||
| } | ||||
| @ -653,8 +650,8 @@ return function write_num(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:str | ||||
| };})(); | ||||
| function split_fmt(fmt/*:string*/)/*:Array<string>*/ { | ||||
| 	var out/*:Array<string>*/ = []; | ||||
| 	var in_str = false, cc; | ||||
| 	for(var i = 0, j = 0; i < fmt.length; ++i) switch((cc=fmt.charCodeAt(i))) { | ||||
| 	var in_str = false/*, cc*/; | ||||
| 	for(var i = 0, j = 0; i < fmt.length; ++i) switch((/*cc=*/fmt.charCodeAt(i))) { | ||||
| 		case 34: /* '"' */ | ||||
| 			in_str = !in_str; break; | ||||
| 		case 95: case 42: case 92: /* '_' '*' '\\' */ | ||||
| @ -670,11 +667,11 @@ function split_fmt(fmt/*:string*/)/*:Array<string>*/ { | ||||
| SSF._split = split_fmt; | ||||
| var abstime = /\[[HhMmSs]*\]/; | ||||
| function fmt_is_date(fmt/*:string*/)/*:boolean*/ { | ||||
| 	var i = 0, cc = 0, c = "", o = ""; | ||||
| 	var i = 0, /*cc = 0,*/ c = "", o = ""; | ||||
| 	while(i < fmt.length) { | ||||
| 		switch((c = fmt.charAt(i))) { | ||||
| 			case 'G': if(isgeneral(fmt, i)) i+= 6; i++; break; | ||||
| 			case '"': for(;(cc=fmt.charCodeAt(++i)) !== 34 && i < fmt.length;) ++i; ++i; break; | ||||
| 			case '"': for(;(/*cc=*/fmt.charCodeAt(++i)) !== 34 && i < fmt.length;) ++i; ++i; break; | ||||
| 			case '\\': i+=2; break; | ||||
| 			case '_': i+=2; break; | ||||
| 			case '@': ++i; break; | ||||
| @ -696,13 +693,13 @@ function fmt_is_date(fmt/*:string*/)/*:boolean*/ { | ||||
| 			case '.': | ||||
| 				/* falls through */ | ||||
| 			case '0': case '#': | ||||
| 				while(i < fmt.length && ("0#?.,E+-%".indexOf(c=fmt.charAt(++i)) > -1 || c=='\\' && fmt.charAt(i+1) == "-" && "0#".indexOf(fmt.charAt(i+2))>-1)){} | ||||
| 				while(i < fmt.length && ("0#?.,E+-%".indexOf(c=fmt.charAt(++i)) > -1 || (c=='\\' && fmt.charAt(i+1) == "-" && "0#".indexOf(fmt.charAt(i+2))>-1))){/* empty */} | ||||
| 				break; | ||||
| 			case '?': while(fmt.charAt(++i) === c){} break; | ||||
| 			case '?': while(fmt.charAt(++i) === c){/* empty */} break; | ||||
| 			case '*': ++i; if(fmt.charAt(i) == ' ' || fmt.charAt(i) == '*') ++i; break; | ||||
| 			case '(': case ')': ++i; break; | ||||
| 			case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': | ||||
| 				while(i < fmt.length && "0123456789".indexOf(fmt.charAt(++i)) > -1){} break; | ||||
| 				while(i < fmt.length && "0123456789".indexOf(fmt.charAt(++i)) > -1){/* empty */} break; | ||||
| 			case ' ': ++i; break; | ||||
| 			default: ++i; break; | ||||
| 		} | ||||
| @ -772,7 +769,7 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) { | ||||
| 				} | ||||
| 				/* falls through */ | ||||
| 			case '0': case '#': | ||||
| 				o = c; while(++i < fmt.length && "0#?.,E+-%".indexOf(c=fmt.charAt(i)) > -1 || c=='\\' && fmt.charAt(i+1) == "-" && i < fmt.length - 2 && "0#".indexOf(fmt.charAt(i+2))>-1) o += c; | ||||
| 				o = c; while((++i < fmt.length && "0#?.,E+-%".indexOf(c=fmt.charAt(i)) > -1) || (c=='\\' && fmt.charAt(i+1) == "-" && i < fmt.length - 2 && "0#".indexOf(fmt.charAt(i+2))>-1)) o += c; | ||||
| 				out[out.length] = {t:'n', v:o}; break; | ||||
| 			case '?': | ||||
| 				o = c; while(fmt.charAt(++i) === c) o+=c; | ||||
| @ -834,9 +831,9 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) { | ||||
| 				jj = i+1; | ||||
| 				while(out[jj] != null && ( | ||||
| 					(c=out[jj].t) === "?" || c === "D" || | ||||
| 					(c === " " || c === "t") && out[jj+1] != null && (out[jj+1].t === '?' || out[jj+1].t === "t" && out[jj+1].v === '/') || | ||||
| 					out[i].t === '(' && (c === ' ' || c === 'n' || c === ')') || | ||||
| 					c === 't' && (out[jj].v === '/' || out[jj].v === ' ' && out[jj+1] != null && out[jj+1].t == '?') | ||||
| 					((c === " " || c === "t") && out[jj+1] != null && (out[jj+1].t === '?' || out[jj+1].t === "t" && out[jj+1].v === '/')) || | ||||
| 					(out[i].t === '(' && (c === ' ' || c === 'n' || c === ')')) || | ||||
| 					(c === 't' && (out[jj].v === '/' || out[jj].v === ' ' && out[jj+1] != null && out[jj+1].t == '?')) | ||||
| 				)) { | ||||
| 					out[i].v += out[jj].v; | ||||
| 					out[jj] = {v:"", t:";"}; ++jj; | ||||
| @ -890,7 +887,7 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) { | ||||
| 			if(jj>=0 && lasti<out.length) out[lasti].v = ostr.substr(0,jj+1) + out[lasti].v; | ||||
| 			jj = ostr.indexOf(".")+1; | ||||
| 			for(i=decpt; i<out.length; ++i) { | ||||
| 				if(out[i] == null || 'n?('.indexOf(out[i].t) === -1 && i !== decpt ) continue; | ||||
| 				if(out[i] == null || ('n?('.indexOf(out[i].t) === -1 && i !== decpt)) continue; | ||||
| 				j=out[i].v.indexOf(".")>-1&&i===decpt?out[i].v.indexOf(".")+1:0; | ||||
| 				vv = out[i].v.substr(0,j); | ||||
| 				for(; j<out[i].v.length; ++j) { | ||||
| @ -1513,6 +1510,19 @@ function dup(o/*:any*/)/*:any*/ { | ||||
| } | ||||
| 
 | ||||
| function fill(c/*:string*/,l/*:number*/)/*:string*/ { var o = ""; while(o.length < l) o+=c; return o; } | ||||
| 
 | ||||
| /* TODO: stress test */ | ||||
| function fuzzydate(s/*:string*/)/*:Date*/ { | ||||
| 	var o = new Date(s), n = new Date(NaN); | ||||
| 	var y = o.getYear(), m = o.getMonth(), d = o.getDate(); | ||||
| 	if(isNaN(d)) return n; | ||||
| 	if(y < 0 || y > 8099) return n; | ||||
| 	if((m > 0 || d > 1) && y != 101) return o; | ||||
| 	if(s.toLowerCase().match(/jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec/)) return o; | ||||
| 	if(!s.match(/[a-zA-Z]/)) return o; | ||||
| 	return n; | ||||
| } | ||||
| 
 | ||||
| function getdatastr(data)/*:?string*/ { | ||||
| 	if(!data) return null; | ||||
| 	if(data.data) return debom(data.data); | ||||
| @ -2237,11 +2247,12 @@ function aoa_to_sheet(data/*:AOA*/, opts/*:?any*/)/*:Worksheet*/ { | ||||
| 		for(var C = 0; C != data[R].length; ++C) { | ||||
| 			if(typeof data[R][C] === 'undefined') continue; | ||||
| 			var cell/*:Cell*/ = ({v: data[R][C] }/*:any*/); | ||||
| 			if(Array.isArray(cell.v)) { cell.f = data[R][C][1]; cell.v = cell.v[0]; } | ||||
| 			if(range.s.r > R) range.s.r = R; | ||||
| 			if(range.s.c > C) range.s.c = C; | ||||
| 			if(range.e.r < R) range.e.r = R; | ||||
| 			if(range.e.c < C) range.e.c = C; | ||||
| 			if(cell.v === null) { if(!o.cellStubs) continue; cell.t = 'z'; } | ||||
| 			if(cell.v === null) { if(cell.f) cell.t = 'n'; else if(!o.cellStubs) continue; else cell.t = 'z'; } | ||||
| 			else if(typeof cell.v === 'number') cell.t = 'n'; | ||||
| 			else if(typeof cell.v === 'boolean') cell.t = 'b'; | ||||
| 			else if(cell.v instanceof Date) { | ||||
| @ -3221,6 +3232,14 @@ function write_rdf(rdf, opts) { | ||||
| 	o.push('</rdf:RDF>'); | ||||
| 	return o.join(""); | ||||
| } | ||||
| /* TODO: pull properties */ | ||||
| var write_meta_ods/*:{(wb:any, opts:any):string}*/ = (function() { | ||||
| 	var payload = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><office:document-meta xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xlink="http://www.w3.org/1999/xlink" office:version="1.2"><office:meta><meta:generator>Sheet' + 'JS ' + XLSX.version + '</meta:generator></office:meta></office:document-meta>'; | ||||
| 	return function wmo(wb, opts) { | ||||
| 		return payload; | ||||
| 	}; | ||||
| })(); | ||||
| 
 | ||||
| /* ECMA-376 Part II 11.1 Core Properties Part */ | ||||
| /* [MS-OSHARED] 2.3.3.2.[1-2].1 (PIDSI/PIDDSI) */ | ||||
| var CORE_PROPS/*:Array<Array<string> >*/ = [ | ||||
| @ -5278,7 +5297,7 @@ function dbf_to_aoa(buf, opts)/*:AOA*/ { | ||||
| 				case 'T': | ||||
| 					var day = dd.read_shift(4), ms = dd.read_shift(4); | ||||
| 					throw new Error(day + " | " + ms); | ||||
| 					//out[R][C] = new Date(); // FIXME!!!
 | ||||
| 					//out[R][C] = new Date(); // TODO
 | ||||
| 					//break;
 | ||||
| 				case 'Y': out[R][C] = dd.read(4,'i')/1e4; break; | ||||
| 				case '0': | ||||
| @ -5328,7 +5347,9 @@ var SYLK = (function() { | ||||
| 		var Mval = 0, j; | ||||
| 		for (; ri !== records.length; ++ri) { | ||||
| 			Mval = 0; | ||||
| 			var rstr=records[ri].trim(), record=rstr.split(";"), RT=record[0], val; | ||||
| 			var rstr=records[ri].trim(); | ||||
| 			var record=rstr.replace(/;;/g, "\u0001").split(";").map(function(x) { return x.replace(/\u0001/g, ";"); }); | ||||
| 			var RT=record[0], val; | ||||
| 			if(rstr.length > 0) switch(RT) { | ||||
| 			case 'ID': break; /* header */ | ||||
| 			case 'E': break; /* EOF */ | ||||
| @ -5358,17 +5379,19 @@ var SYLK = (function() { | ||||
| 					next_cell_format = null; | ||||
| 					break; | ||||
| 				case 'E': | ||||
| 					/* formula = record[rj].substr(1); */ | ||||
| 					break; /* TODO: formula */ | ||||
| 					formula = rc_to_a1(record[rj].substr(1), {r:R,c:C}); | ||||
| 					arr[R][C] = [arr[R][C], formula]; | ||||
| 					break; | ||||
| 				default: if(opts && opts.WTF) throw new Error("SYLK bad record " + rstr); | ||||
| 			} break; | ||||
| 			case 'F': | ||||
| 			var F_seen = 0; | ||||
| 			for(rj=1; rj<record.length; ++rj) switch(record[rj].charAt(0)) { | ||||
| 				case 'X': C = parseInt(record[rj].substr(1))-1; break; | ||||
| 				case 'X': C = parseInt(record[rj].substr(1))-1; ++F_seen; break; | ||||
| 				case 'Y': | ||||
| 					R = parseInt(record[rj].substr(1))-1; C = 0; | ||||
| 					R = parseInt(record[rj].substr(1))-1; /*C = 0;*/ | ||||
| 					for(j = arr.length; j <= R; ++j) arr[j] = []; | ||||
| 					break; | ||||
| 					++F_seen; break; | ||||
| 				case 'M': Mval = parseInt(record[rj].substr(1)) / 20; break; | ||||
| 				case 'F': break; /* ??? */ | ||||
| 				case 'P': | ||||
| @ -5383,14 +5406,19 @@ var SYLK = (function() { | ||||
| 						Mval = parseInt(cw[2], 10); | ||||
| 						colinfo[j-1] = Mval == 0 ? {hidden:true}: {wch:Mval}; process_col(colinfo[j-1]); | ||||
| 					} break; | ||||
| 				case 'R': | ||||
| 				case 'C': /* default column format */ | ||||
| 					C = parseInt(record[rj].substr(1))-1; | ||||
| 					if(!colinfo[C]) colinfo[C] = {}; | ||||
| 					break; | ||||
| 				case 'R': /* row properties */ | ||||
| 					R = parseInt(record[rj].substr(1))-1; | ||||
| 					rowinfo[R] = {}; | ||||
| 					if(!rowinfo[R]) rowinfo[R] = {}; | ||||
| 					if(Mval > 0) { rowinfo[R].hpt = Mval; rowinfo[R].hpx = pt2px(Mval); } | ||||
| 					else if(Mval == 0) rowinfo[R].hidden = true; | ||||
| 					break; | ||||
| 				default: if(opts && opts.WTF) throw new Error("SYLK bad record " + rstr); | ||||
| 			} break; | ||||
| 			} | ||||
| 			if(F_seen < 2) next_cell_format = null; break; | ||||
| 			default: if(opts && opts.WTF) throw new Error("SYLK bad record " + rstr); | ||||
| 			} | ||||
| 		} | ||||
| @ -5505,7 +5533,7 @@ var DIF = (function() { | ||||
| 					if(data === 'TRUE') arr[R][C] = true; | ||||
| 					else if(data === 'FALSE') arr[R][C] = false; | ||||
| 					else if(+value == +value) arr[R][C] = +value; | ||||
| 					else if(!isNaN(new Date(value).getDate())) arr[R][C] = parseDate(value); | ||||
| 					else if(!isNaN(fuzzydate(value).getDate())) arr[R][C] = parseDate(value); | ||||
| 					else arr[R][C] = value; | ||||
| 					++C; break; | ||||
| 				case 1: | ||||
| @ -5637,7 +5665,7 @@ var PRN = (function() { | ||||
| 			else if(s == "TRUE") { cell.t = 'b'; cell.v = true; } | ||||
| 			else if(s == "FALSE") { cell.t = 'b'; cell.v = false; } | ||||
| 			else if(!isNaN(v = +s)) { cell.t = 'n'; cell.w = s; cell.v = v; } | ||||
| 			else if(!isNaN(new Date(s).getDate())) { cell.t = 'd'; cell.v = parseDate(s); } | ||||
| 			else if(!isNaN(fuzzydate(s).getDate())) { cell.t = 'd'; cell.v = parseDate(s); } | ||||
| 			else { | ||||
| 				cell.t = 's'; | ||||
| 				if(s.charAt(0) == '"' && s.charAt(s.length - 1) == '"') s = s.slice(1,-1).replace(/""/g,'"'); | ||||
| @ -5668,13 +5696,16 @@ var PRN = (function() { | ||||
| 	} | ||||
| 
 | ||||
| 	function prn_to_sheet(d/*:RawData*/, opts)/*:Worksheet*/ { | ||||
| 		var str = "", bytes = firstbyte(d, opts); | ||||
| 		switch(opts.type) { | ||||
| 			case 'base64': return prn_to_sheet_str(Base64.decode(d), opts); | ||||
| 			case 'binary': return prn_to_sheet_str(d, opts); | ||||
| 			case 'buffer': return prn_to_sheet_str(d.toString('binary'), opts); | ||||
| 			case 'array': return prn_to_sheet_str(cc2str(d), opts); | ||||
| 			case 'base64': str = Base64.decode(d); break; | ||||
| 			case 'binary': str = d; break; | ||||
| 			case 'buffer': str = d.toString('binary'); break; | ||||
| 			case 'array': str = cc2str(d); break; | ||||
| 			default: throw new Error("Unrecognized type " + opts.type); | ||||
| 		} | ||||
| 		throw new Error("Unrecognized type " + opts.type); | ||||
| 		if(bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF) str = utf8read(str); | ||||
| 		return prn_to_sheet_str(str, opts); | ||||
| 	} | ||||
| 
 | ||||
| 	function prn_to_workbook(str/*:string*/, opts)/*:Workbook*/ { return sheet_to_workbook(prn_to_sheet(str, opts), opts); } | ||||
| @ -10788,8 +10819,8 @@ function write_ws_xml_cell(cell, ref, ws, opts, idx, wb) { | ||||
| 			else { | ||||
| 				cell.t = 'n'; | ||||
| 				vv = ''+(cell.v = datenum(parseDate(cell.v))); | ||||
| 				if(typeof cell.z === 'undefined') cell.z = SSF._table[14]; | ||||
| 			} | ||||
| 			if(typeof cell.z === 'undefined') cell.z = SSF._table[14]; | ||||
| 			break; | ||||
| 		default: vv = cell.v; break; | ||||
| 	} | ||||
| @ -10902,8 +10933,10 @@ return function parse_ws_xml_data(sdata, s, opts, guess, themes, styles) { | ||||
| 			} | ||||
| 
 | ||||
| 			if(tag.t == null && p.v === undefined) { | ||||
| 				if(!opts.sheetStubs) continue; | ||||
| 				p.t = "z"; | ||||
| 				if(p.f || p.F) { | ||||
| 					p.v = 0; p.t = "n"; | ||||
| 				} else if(!opts.sheetStubs) continue; | ||||
| 				else p.t = "z"; | ||||
| 			} | ||||
| 			else p.t = tag.t || "n"; | ||||
| 			if(guess.s.c > idx) guess.s.c = idx; | ||||
| @ -12152,11 +12185,20 @@ function parse_wb_defaults(wb) { | ||||
| 	_ssfopts.date1904 = parsexmlbool(wb.WBProps.date1904, 'date1904'); | ||||
| } | ||||
| 
 | ||||
| var badchars = "][*?\/\\".split(""); | ||||
| function check_ws_name(n/*:string*/, safe/*:boolean*/)/*:boolean*/ { | ||||
| 	if(n.length > 31) { if(safe) return false; throw new Error("Sheet names cannot exceed 31 chars"); } | ||||
| 	var _good = true; | ||||
| 	badchars.forEach(function(c) { | ||||
| 		if(n.indexOf(c) == -1) return; | ||||
| 		if(!safe) throw new Error("Sheet name cannot contain : \\ / ? * [ ]"); | ||||
| 		_good = false; | ||||
| 	}); | ||||
| 	return _good; | ||||
| } | ||||
| function check_wb_names(N) { | ||||
| 	var badchars = "][*?\/\\".split(""); | ||||
| 	N.forEach(function(n,i) { | ||||
| 		badchars.forEach(function(c) { if(n.indexOf(c) > -1) throw new Error("Sheet name cannot contain : \\ / ? * [ ]"); }); | ||||
| 		if(n.length > 31) throw new Error("Sheet names cannot exceed 31 chars"); | ||||
| 		check_ws_name(n); | ||||
| 		for(var j = 0; j < i; ++j) if(n == N[j]) throw new Error("Duplicate Sheet Name: " + n); | ||||
| 	}); | ||||
| } | ||||
| @ -14009,7 +14051,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ { | ||||
| 					opts.enc = val; | ||||
| 					if(opts.WTF) console.error(val); | ||||
| 					if(!options.password) throw new Error("File is password-protected"); | ||||
| 					if(val.Type !== 0) throw new Error("Encryption scheme unsupported"); | ||||
| 					if(val.valid == null) throw new Error("Encryption scheme unsupported"); | ||||
| 					if(!val.valid) throw new Error("Password is incorrect"); | ||||
| 					break; | ||||
| 				case 'WriteAccess': opts.lastuser = val; break; | ||||
| @ -14026,7 +14068,7 @@ function parse_workbook(blob, options/*:ParseOpts*/)/*:Workbook*/ { | ||||
| 				case 'Template': break; // TODO
 | ||||
| 				case 'RefreshAll': wb.opts.RefreshAll = val; break; | ||||
| 				case 'BookBool': break; // TODO
 | ||||
| 				case 'UsesELFs': /* if(val) console.error("Unsupported ELFs"); */ break; | ||||
| 				case 'UsesELFs': break; | ||||
| 				case 'MTRSettings': break; | ||||
| 				case 'CalcCount': wb.opts.CalcCount = val; break; | ||||
| 				case 'CalcDelta': wb.opts.CalcDelta = val; break; | ||||
| @ -16117,6 +16159,7 @@ function parse_dom_table(table/*:HTMLElement*/, _opts/*:?any*/)/*:Worksheet*/ { | ||||
| function table_to_book(table/*:HTMLElement*/, opts/*:?any*/)/*:Workbook*/ { | ||||
| 	return sheet_to_workbook(parse_dom_table(table, opts), opts); | ||||
| } | ||||
| /* OpenDocument */ | ||||
| var parse_content_xml = (function() { | ||||
| 
 | ||||
| 	var parse_text_p = function(text, tag) { | ||||
| @ -16556,7 +16599,27 @@ var parse_content_xml = (function() { | ||||
| 		return out; | ||||
| 	}; | ||||
| })(); | ||||
| var write_content_xml/*:{(wb:any, opts:any):string}*/ = (function() { | ||||
| 
 | ||||
| function parse_ods(zip/*:ZIPFile*/, opts/*:?ParseOpts*/) { | ||||
| 	opts = opts || ({}/*:any*/); | ||||
| 	var ods = !!safegetzipfile(zip, 'objectdata'); | ||||
| 	if(ods) var manifest = parse_manifest(getzipdata(zip, 'META-INF/manifest.xml'), opts); | ||||
| 	var content = getzipstr(zip, 'content.xml'); | ||||
| 	if(!content) throw new Error("Missing content.xml in " + (ods ? "ODS" : "UOF")+ " file"); | ||||
| 	return parse_content_xml(ods ? content : utf8read(content), opts); | ||||
| } | ||||
| function parse_fods(data/*:string*/, opts/*:?ParseOpts*/) { | ||||
| 	return parse_content_xml(data, opts); | ||||
| } | ||||
| 
 | ||||
| /* OpenDocument */ | ||||
| var write_styles_ods/*:{(wb:any, opts:any):string}*/ = (function() { | ||||
| 	var payload = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><office:document-styles xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" office:version="1.2"></office:document-styles>'; | ||||
| 	return function wso(wb, opts) { | ||||
| 		return payload; | ||||
| 	}; | ||||
| })(); | ||||
| var write_content_ods/*:{(wb:any, opts:any):string}*/ = (function() { | ||||
| 	var null_cell_xml = '          <table:table-cell />\n'; | ||||
| 	var covered_cell_xml = '          <table:covered-table-cell/>\n'; | ||||
| 	var write_ws = function(ws, wb, i/*:number*/, opts)/*:string*/ { | ||||
| @ -16619,7 +16682,7 @@ var write_content_xml/*:{(wb:any, opts:any):string}*/ = (function() { | ||||
| 					//case 'e':
 | ||||
| 					default: o.push(null_cell_xml); continue; | ||||
| 				} | ||||
| 				o.push(writextag('table:table-cell', writextag('text:p', textp, {}), ct)); | ||||
| 				o.push('          ' + writextag('table:table-cell', writextag('text:p', textp, {}), ct) + '\n'); | ||||
| 			} | ||||
| 			o.push('        </table:table-row>\n'); | ||||
| 		} | ||||
| @ -16701,6 +16764,51 @@ var write_content_xml/*:{(wb:any, opts:any):string}*/ = (function() { | ||||
| 		return o.join(""); | ||||
| 	}; | ||||
| })(); | ||||
| 
 | ||||
| function write_ods(wb/*:any*/, opts/*:any*/) { | ||||
| 	if(opts.bookType == "fods") return write_content_ods(wb, opts); | ||||
| 
 | ||||
| 	/*:: if(!jszip) throw new Error("JSZip is not available"); */ | ||||
| 	var zip = new jszip(); | ||||
| 	var f = ""; | ||||
| 
 | ||||
| 	var manifest/*:Array<Array<string> >*/ = []; | ||||
| 	var rdf = []; | ||||
| 
 | ||||
| 	/* 3:3.3 and 2:2.2.4 */ | ||||
| 	f = "mimetype"; | ||||
| 	zip.file(f, "application/vnd.oasis.opendocument.spreadsheet"); | ||||
| 
 | ||||
| 	/* Part 1 Section 2.2 Documents */ | ||||
| 	f = "content.xml"; | ||||
| 	zip.file(f, write_content_ods(wb, opts)); | ||||
| 	manifest.push([f, "text/xml"]); | ||||
| 	rdf.push([f, "ContentFile"]); | ||||
| 
 | ||||
| 	/* TODO: these are hard-coded styles to satiate excel */ | ||||
| 	f = "styles.xml"; | ||||
| 	zip.file(f, write_styles_ods(wb, opts)); | ||||
| 	manifest.push([f, "text/xml"]); | ||||
| 	rdf.push([f, "StylesFile"]); | ||||
| 
 | ||||
| 	/* Part 3 Section 6 Metadata Manifest File */ | ||||
| 	f = "manifest.rdf"; | ||||
| 	zip.file(f, write_rdf(rdf, opts)); | ||||
| 	manifest.push([f, "application/rdf+xml"]); | ||||
| 
 | ||||
| 	/* TODO: this is hard-coded to satiate excel */ | ||||
| 	f = "meta.xml"; | ||||
| 	zip.file(f, write_meta_ods(wb, opts)); | ||||
| 	manifest.push([f, "text/xml"]); | ||||
| 	rdf.push([f, "MetadataFile"]); | ||||
| 
 | ||||
| 	/* Part 3 Section 4 Manifest File */ | ||||
| 	f = "META-INF/manifest.xml"; | ||||
| 	zip.file(f, write_manifest(manifest, opts)); | ||||
| 
 | ||||
| 	return zip; | ||||
| } | ||||
| 
 | ||||
| /* actual implementation elsewhere, wrappers are for read/write */ | ||||
| function write_obj_str(factory/*:WriteObjStrFactory*/) { | ||||
| 	return function write_str(wb/*:Workbook*/, o/*:WriteOpts*/)/*:string*/ { | ||||
| @ -16717,50 +16825,6 @@ var write_slk_str = write_obj_str(SYLK); | ||||
| var write_dif_str = write_obj_str(DIF); | ||||
| var write_prn_str = write_obj_str(PRN); | ||||
| var write_txt_str = write_obj_str({from_sheet:sheet_to_txt}); | ||||
| /* Part 3: Packages */ | ||||
| function parse_ods(zip/*:ZIPFile*/, opts/*:?ParseOpts*/) { | ||||
| 	opts = opts || ({}/*:any*/); | ||||
| 	var ods = !!safegetzipfile(zip, 'objectdata'); | ||||
| 	if(ods) var manifest = parse_manifest(getzipdata(zip, 'META-INF/manifest.xml'), opts); | ||||
| 	var content = getzipstr(zip, 'content.xml'); | ||||
| 	if(!content) throw new Error("Missing content.xml in " + (ods ? "ODS" : "UOF")+ " file"); | ||||
| 	return parse_content_xml(ods ? content : utf8read(content), opts); | ||||
| } | ||||
| function parse_fods(data/*:string*/, opts/*:?ParseOpts*/) { | ||||
| 	return parse_content_xml(data, opts); | ||||
| } | ||||
| 
 | ||||
| function write_ods(wb/*:any*/, opts/*:any*/) { | ||||
| 	if(opts.bookType == "fods") return write_content_xml(wb, opts); | ||||
| 
 | ||||
| 	/*:: if(!jszip) throw new Error("JSZip is not available"); */ | ||||
| 	var zip = new jszip(); | ||||
| 	var f = ""; | ||||
| 
 | ||||
| 	var manifest/*:Array<Array<string> >*/ = []; | ||||
| 	var rdf = []; | ||||
| 
 | ||||
| 	/* 3:3.3 and 2:2.2.4 */ | ||||
| 	f = "mimetype"; | ||||
| 	zip.file(f, "application/vnd.oasis.opendocument.spreadsheet"); | ||||
| 
 | ||||
| 	/* Part 1 Section 2.2 Documents */ | ||||
| 	f = "content.xml"; | ||||
| 	zip.file(f, write_content_xml(wb, opts)); | ||||
| 	manifest.push([f, "text/xml"]); | ||||
| 	rdf.push([f, "ContentFile"]); | ||||
| 
 | ||||
| 	/* Part 3 Section 6 Metadata Manifest File */ | ||||
| 	f = "manifest.rdf"; | ||||
| 	zip.file(f, write_rdf(rdf, opts)); | ||||
| 	manifest.push([f, "application/rdf+xml"]); | ||||
| 
 | ||||
| 	/* Part 3 Section 4 Manifest File */ | ||||
| 	f = "META-INF/manifest.xml"; | ||||
| 	zip.file(f, write_manifest(manifest, opts)); | ||||
| 
 | ||||
| 	return zip; | ||||
| } | ||||
| function fix_opts_func(defaults/*:Array<Array<any> >*/)/*:{(o:any):void}*/ { | ||||
| 	return function fix_opts(opts) { | ||||
| 		for(var i = 0; i != defaults.length; ++i) { | ||||
| @ -17199,7 +17263,7 @@ function readSync(data/*:RawData*/, opts/*:?ParseOpts*/)/*:Workbook*/ { | ||||
| 		case 0x49: if(n[1] == 0x44) return SYLK.to_workbook(d, o); break; | ||||
| 		case 0x54: if(n[1] == 0x41 && n[2] == 0x42 && n[3] == 0x4C) return DIF.to_workbook(d, o); break; | ||||
| 		case 0x50: if(n[1] == 0x4B && n[2] < 0x20 && n[3] < 0x20) return read_zip(d, o); break; | ||||
| 		case 0xEF: return parse_xlml(d, o); | ||||
| 		case 0xEF: return n[3] == 0x3C ? parse_xlml(d, o) : PRN.to_workbook(d,o); | ||||
| 		case 0xFF: if(n[1] == 0xFE){ return read_utf16(d, o); } break; | ||||
| 		case 0x00: if(n[1] == 0x00 && n[2] >= 0x02 && n[3] == 0x00) return WK_.to_workbook(d, o); break; | ||||
| 		case 0x03: case 0x83: case 0x8B: return DBF.to_workbook(d, o); | ||||
| @ -17353,7 +17417,7 @@ function sheet_to_json(sheet/*:Worksheet*/, opts/*:?Sheet2JSONOpts*/){ | ||||
| 	var outi = 0, counter = 0; | ||||
| 	var dense = Array.isArray(sheet); | ||||
| 	var R = r.s.r, C = 0, CC = 0; | ||||
| 	if(!sheet[R]) sheet[R] = []; | ||||
| 	if(dense && !sheet[R]) sheet[R] = []; | ||||
| 	for(C = r.s.c; C <= r.e.c; ++C) { | ||||
| 		cols[C] = encode_col(C); | ||||
| 		val = dense ? sheet[R][C] : sheet[cols[C] + rr]; | ||||
| @ -17521,6 +17585,110 @@ var utils = { | ||||
| 	sheet_to_formulae: sheet_to_formulae, | ||||
| 	sheet_to_row_object_array: sheet_to_json | ||||
| }; | ||||
| 
 | ||||
| (function(utils) { | ||||
| utils.consts = utils.consts || {}; | ||||
| function add_consts(R) { R.forEach(function(a){ utils.consts[a[0]] = a[1]; }); } | ||||
| 
 | ||||
| function get_default(x, y, z) { return x[y] != null ? x[y] : (x[y] = z); } | ||||
| 
 | ||||
| /* get cell, creating a stub if necessary */ | ||||
| function ws_get_cell_stub(ws/*:Worksheet*/, R, C/*:?number*/)/*:Cell*/ { | ||||
| 	/* A1 cell address */ | ||||
| 	if(typeof R == "string") return ws[R] || (ws[R] = {t:'z'}); | ||||
| 	/* cell address object */ | ||||
| 	if(typeof R != "number") return ws_get_cell_stub(ws, encode_cell(R)); | ||||
| 	/* R and C are 0-based indices */ | ||||
| 	return ws_get_cell_stub(ws, encode_cell({r:R,c:C})); | ||||
| } | ||||
| 
 | ||||
| /* find sheet index for given name / validate index */ | ||||
| function wb_sheet_idx(wb/*:Workbook*/, sh/*:number|string*/) { | ||||
| 	if(typeof sh == "number") { | ||||
| 		if(sh >= 0 && wb.SheetNames.length > sh) return sh; | ||||
| 		throw new Error("Cannot find sheet # " + sh); | ||||
| 	} else if(typeof sh == "string") { | ||||
| 		var idx = wb.SheetNames.indexOf(sh); | ||||
| 		if(idx > -1) return idx; | ||||
| 		throw new Error("Cannot find sheet name |" + sh + "|"); | ||||
| 	} else throw new Error("Cannot find sheet |" + sh + "|"); | ||||
| } | ||||
| 
 | ||||
| /* simple blank workbook object */ | ||||
| utils.book_new = function()/*:Workbook*/ { | ||||
| 	return { SheetNames: [], Sheets: {} }; | ||||
| }; | ||||
| 
 | ||||
| /* add a worksheet to the end of a given workbook */ | ||||
| utils.book_append_sheet = function(wb/*:Workbook*/, ws/*:Worksheet*/, name/*:?string*/) { | ||||
| 	if(!name) for(var i = 1; i <= 0xFFFF; ++i) if(wb.SheetNames.indexOf("Sheet" + i) == -1) break; | ||||
| 	check_ws_name(name); | ||||
| 	if(wb.SheetNames.indexOf(name) >= 0) throw new Error("Worksheet with name |" + name + "| already exists!"); | ||||
| 
 | ||||
| 	wb.SheetNames.push(name); | ||||
| 	wb.Sheets[name] = ws; | ||||
| }; | ||||
| 
 | ||||
| /* set sheet visibility (visible/hidden/very hidden) */ | ||||
| utils.book_set_sheet_visibility = function(wb/*:Workbook*/, sh/*:number|string*/, vis/*:number*/) { | ||||
| 	get_default(wb,"Workbook",{}); | ||||
| 	get_default(wb.Workbook,"Sheets",[]); | ||||
| 
 | ||||
| 	var idx = wb_sheet_idx(wb, sh); | ||||
| 	get_default(wb.Workbook.Sheets,idx, {}); | ||||
| 
 | ||||
| 	switch(vis) { | ||||
| 		case 0: case 1: case 2: break; | ||||
| 		default: throw new Error("Bad sheet visibility setting " + vis); | ||||
| 	} | ||||
| 	wb.Workbook.Sheets[idx].Hidden = vis; | ||||
| }; | ||||
| add_consts([ | ||||
| 	["SHEET_VISIBLE", 0], | ||||
| 	["SHEET_HIDDEN", 1], | ||||
| 	["SHEET_VERY_HIDDEN", 2] | ||||
| ]); | ||||
| 
 | ||||
| /* set number format */ | ||||
| utils.cell_set_number_format = function(cell/*:Cell*/, fmt/*:string|number*/) { | ||||
| 	cell.z = fmt; | ||||
| 	return cell; | ||||
| }; | ||||
| 
 | ||||
| /* set cell hyperlink */ | ||||
| utils.cell_set_hyperlink = function(cell/*:Cell*/, target/*:string*/, tooltip/*:?string*/) { | ||||
| 	if(!target) { | ||||
| 		delete cell.l; | ||||
| 	} else { | ||||
| 		cell.l = { Target: target }; | ||||
| 		if(tooltip) cell.l.Tooltip = tooltip; | ||||
| 	} | ||||
| 	return cell; | ||||
| }; | ||||
| 
 | ||||
| /* add to cell comments */ | ||||
| utils.cell_add_comment = function(cell/*:Cell*/, text/*:string*/, author/*:?string*/) { | ||||
| 	if(!cell.c) cell.c = []; | ||||
| 	cell.c.push({t:text, a:author||"SheetJS"}); | ||||
| }; | ||||
| 
 | ||||
| /* set array formula and flush related cells */ | ||||
| utils.sheet_set_array_formula = function(ws/*:Worksheet*/, range, formula/*:string*/) { | ||||
| 	var rng = typeof range != "string" ? range : safe_decode_range(range); | ||||
| 	var rngstr = typeof range == "string" ? range : encode_range(range); | ||||
| 	for(var R = rng.s.r; R <= rng.e.r; ++R) for(var C = rng.s.c; C <= rng.e.c; ++C) { | ||||
| 		var cell = ws_get_cell_stub(ws, R, C); | ||||
| 		cell.t = 'n'; | ||||
| 		cell.F = rngstr; | ||||
| 		delete cell.v;  | ||||
| 		if(R == rng.s.r && C == rng.s.c) cell.f = formula; | ||||
| 	} | ||||
| 	return ws; | ||||
| } | ||||
| 
 | ||||
| return utils; | ||||
| })(utils); | ||||
| 
 | ||||
| if(has_buf && typeof require != 'undefined') (function() { | ||||
| 	var Readable = require('stream').Readable; | ||||
| 
 | ||||
| @ -17583,6 +17751,7 @@ if(has_buf && typeof require != 'undefined') (function() { | ||||
| 		to_csv: write_csv_stream | ||||
| 	}; | ||||
| })(); | ||||
| 
 | ||||
| XLSX.parse_xlscfb = parse_xlscfb; | ||||
| XLSX.parse_ods = parse_ods; | ||||
| XLSX.parse_fods = parse_fods; | ||||
|  | ||||
							
								
								
									
										357
									
								
								xlsx.js
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										357
									
								
								xlsx.js
									
									
									
									
									
								
							| @ -198,7 +198,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) { 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)$/; | ||||
| @ -218,11 +218,9 @@ function gfn4(o) { | ||||
| 	return o; | ||||
| } | ||||
| function gfn5(o) { | ||||
| 	//for(var i = 0; i != o.length; ++i) if(o.charCodeAt(i) === 46) return o.replace(gnr2,"").replace(gnr1,".$1");
 | ||||
| 	//return o;
 | ||||
| 	return o.indexOf(".") > -1 ? o.replace(gnr2,"").replace(gnr1,".$1") : o; | ||||
| } | ||||
| return function general_fmt_num(v, opts) { | ||||
| return function general_fmt_num(v) { | ||||
| 	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); | ||||
| @ -231,18 +229,18 @@ 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) { | ||||
| 	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/*, opts*/) : general_fmt_num(v/*, opts*/); | ||||
| 		case 'undefined': return ""; | ||||
| 		case 'object': if(v == null) return ""; | ||||
| 	} | ||||
| 	throw new Error("unsupported value in General format: " + v); | ||||
| } | ||||
| SSF._general = general_fmt; | ||||
| function fix_hijri(date, o) { return 0; } | ||||
| 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; | ||||
| @ -620,8 +618,7 @@ return "." + $1 + fill("0", r[1].length-$1.length); }); | ||||
| 		case "##,###": | ||||
| 		case "#,###": var x = commaify(""+aval); return x !== "0" ? sign + x : ""; | ||||
| 		default: | ||||
| 			if(fmt.slice(-3) == ".00") return write_num_int(type, fmt.slice(0,-3), val) + ".00"; | ||||
| 			if(fmt.slice(-2) == ".0") return write_num_int(type, fmt.slice(0,-2), val) + ".0"; | ||||
| 			if(fmt.match(/\.[0#?]*$/)) return write_num_int(type, fmt.slice(0,fmt.lastIndexOf(".")), val) + hashq(fmt.slice(fmt.lastIndexOf("."))); | ||||
| 	} | ||||
| 	throw new Error("unsupported format |" + fmt + "|"); | ||||
| } | ||||
| @ -630,8 +627,8 @@ return function write_num(type, fmt, val) { | ||||
| };})(); | ||||
| function split_fmt(fmt) { | ||||
| 	var out = []; | ||||
| 	var in_str = false, cc; | ||||
| 	for(var i = 0, j = 0; i < fmt.length; ++i) switch((cc=fmt.charCodeAt(i))) { | ||||
| 	var in_str = false/*, cc*/; | ||||
| 	for(var i = 0, j = 0; i < fmt.length; ++i) switch((/*cc=*/fmt.charCodeAt(i))) { | ||||
| 		case 34: /* '"' */ | ||||
| 			in_str = !in_str; break; | ||||
| 		case 95: case 42: case 92: /* '_' '*' '\\' */ | ||||
| @ -647,11 +644,11 @@ function split_fmt(fmt) { | ||||
| SSF._split = split_fmt; | ||||
| var abstime = /\[[HhMmSs]*\]/; | ||||
| function fmt_is_date(fmt) { | ||||
| 	var i = 0, cc = 0, c = "", o = ""; | ||||
| 	var i = 0, /*cc = 0,*/ c = "", o = ""; | ||||
| 	while(i < fmt.length) { | ||||
| 		switch((c = fmt.charAt(i))) { | ||||
| 			case 'G': if(isgeneral(fmt, i)) i+= 6; i++; break; | ||||
| 			case '"': for(;(cc=fmt.charCodeAt(++i)) !== 34 && i < fmt.length;) ++i; ++i; break; | ||||
| 			case '"': for(;(/*cc=*/fmt.charCodeAt(++i)) !== 34 && i < fmt.length;) ++i; ++i; break; | ||||
| 			case '\\': i+=2; break; | ||||
| 			case '_': i+=2; break; | ||||
| 			case '@': ++i; break; | ||||
| @ -673,13 +670,13 @@ function fmt_is_date(fmt) { | ||||
| 			case '.': | ||||
| 				/* falls through */ | ||||
| 			case '0': case '#': | ||||
| 				while(i < fmt.length && ("0#?.,E+-%".indexOf(c=fmt.charAt(++i)) > -1 || c=='\\' && fmt.charAt(i+1) == "-" && "0#".indexOf(fmt.charAt(i+2))>-1)){} | ||||
| 				while(i < fmt.length && ("0#?.,E+-%".indexOf(c=fmt.charAt(++i)) > -1 || (c=='\\' && fmt.charAt(i+1) == "-" && "0#".indexOf(fmt.charAt(i+2))>-1))){/* empty */} | ||||
| 				break; | ||||
| 			case '?': while(fmt.charAt(++i) === c){} break; | ||||
| 			case '?': while(fmt.charAt(++i) === c){/* empty */} break; | ||||
| 			case '*': ++i; if(fmt.charAt(i) == ' ' || fmt.charAt(i) == '*') ++i; break; | ||||
| 			case '(': case ')': ++i; break; | ||||
| 			case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': | ||||
| 				while(i < fmt.length && "0123456789".indexOf(fmt.charAt(++i)) > -1){} break; | ||||
| 				while(i < fmt.length && "0123456789".indexOf(fmt.charAt(++i)) > -1){/* empty */} break; | ||||
| 			case ' ': ++i; break; | ||||
| 			default: ++i; break; | ||||
| 		} | ||||
| @ -749,7 +746,7 @@ function eval_fmt(fmt, v, opts, flen) { | ||||
| 				} | ||||
| 				/* falls through */ | ||||
| 			case '0': case '#': | ||||
| 				o = c; while(++i < fmt.length && "0#?.,E+-%".indexOf(c=fmt.charAt(i)) > -1 || c=='\\' && fmt.charAt(i+1) == "-" && i < fmt.length - 2 && "0#".indexOf(fmt.charAt(i+2))>-1) o += c; | ||||
| 				o = c; while((++i < fmt.length && "0#?.,E+-%".indexOf(c=fmt.charAt(i)) > -1) || (c=='\\' && fmt.charAt(i+1) == "-" && i < fmt.length - 2 && "0#".indexOf(fmt.charAt(i+2))>-1)) o += c; | ||||
| 				out[out.length] = {t:'n', v:o}; break; | ||||
| 			case '?': | ||||
| 				o = c; while(fmt.charAt(++i) === c) o+=c; | ||||
| @ -808,9 +805,9 @@ out[i].v = write_date(out[i].t.charCodeAt(0), out[i].v, dt, ss0); | ||||
| 				jj = i+1; | ||||
| 				while(out[jj] != null && ( | ||||
| 					(c=out[jj].t) === "?" || c === "D" || | ||||
| 					(c === " " || c === "t") && out[jj+1] != null && (out[jj+1].t === '?' || out[jj+1].t === "t" && out[jj+1].v === '/') || | ||||
| 					out[i].t === '(' && (c === ' ' || c === 'n' || c === ')') || | ||||
| 					c === 't' && (out[jj].v === '/' || out[jj].v === ' ' && out[jj+1] != null && out[jj+1].t == '?') | ||||
| 					((c === " " || c === "t") && out[jj+1] != null && (out[jj+1].t === '?' || out[jj+1].t === "t" && out[jj+1].v === '/')) || | ||||
| 					(out[i].t === '(' && (c === ' ' || c === 'n' || c === ')')) || | ||||
| 					(c === 't' && (out[jj].v === '/' || out[jj].v === ' ' && out[jj+1] != null && out[jj+1].t == '?')) | ||||
| 				)) { | ||||
| 					out[i].v += out[jj].v; | ||||
| 					out[jj] = {v:"", t:";"}; ++jj; | ||||
| @ -864,7 +861,7 @@ out[i].v = write_date(out[i].t.charCodeAt(0), out[i].v, dt, ss0); | ||||
| 			if(jj>=0 && lasti<out.length) out[lasti].v = ostr.substr(0,jj+1) + out[lasti].v; | ||||
| 			jj = ostr.indexOf(".")+1; | ||||
| 			for(i=decpt; i<out.length; ++i) { | ||||
| 				if(out[i] == null || 'n?('.indexOf(out[i].t) === -1 && i !== decpt ) continue; | ||||
| 				if(out[i] == null || ('n?('.indexOf(out[i].t) === -1 && i !== decpt)) continue; | ||||
| 				j=out[i].v.indexOf(".")>-1&&i===decpt?out[i].v.indexOf(".")+1:0; | ||||
| 				vv = out[i].v.substr(0,j); | ||||
| 				for(; j<out[i].v.length; ++j) { | ||||
| @ -1463,6 +1460,19 @@ function dup(o) { | ||||
| } | ||||
| 
 | ||||
| function fill(c,l) { var o = ""; while(o.length < l) o+=c; return o; } | ||||
| 
 | ||||
| /* TODO: stress test */ | ||||
| function fuzzydate(s) { | ||||
| 	var o = new Date(s), n = new Date(NaN); | ||||
| 	var y = o.getYear(), m = o.getMonth(), d = o.getDate(); | ||||
| 	if(isNaN(d)) return n; | ||||
| 	if(y < 0 || y > 8099) return n; | ||||
| 	if((m > 0 || d > 1) && y != 101) return o; | ||||
| 	if(s.toLowerCase().match(/jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec/)) return o; | ||||
| 	if(!s.match(/[a-zA-Z]/)) return o; | ||||
| 	return n; | ||||
| } | ||||
| 
 | ||||
| function getdatastr(data) { | ||||
| 	if(!data) return null; | ||||
| 	if(data.data) return debom(data.data); | ||||
| @ -2178,11 +2188,12 @@ function aoa_to_sheet(data, opts) { | ||||
| 		for(var C = 0; C != data[R].length; ++C) { | ||||
| 			if(typeof data[R][C] === 'undefined') continue; | ||||
| 			var cell = ({v: data[R][C] }); | ||||
| 			if(Array.isArray(cell.v)) { cell.f = data[R][C][1]; cell.v = cell.v[0]; } | ||||
| 			if(range.s.r > R) range.s.r = R; | ||||
| 			if(range.s.c > C) range.s.c = C; | ||||
| 			if(range.e.r < R) range.e.r = R; | ||||
| 			if(range.e.c < C) range.e.c = C; | ||||
| 			if(cell.v === null) { if(!o.cellStubs) continue; cell.t = 'z'; } | ||||
| 			if(cell.v === null) { if(cell.f) cell.t = 'n'; else if(!o.cellStubs) continue; else cell.t = 'z'; } | ||||
| 			else if(typeof cell.v === 'number') cell.t = 'n'; | ||||
| 			else if(typeof cell.v === 'boolean') cell.t = 'b'; | ||||
| 			else if(cell.v instanceof Date) { | ||||
| @ -3162,6 +3173,14 @@ function write_rdf(rdf, opts) { | ||||
| 	o.push('</rdf:RDF>'); | ||||
| 	return o.join(""); | ||||
| } | ||||
| /* TODO: pull properties */ | ||||
| var write_meta_ods = (function() { | ||||
| 	var payload = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><office:document-meta xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xlink="http://www.w3.org/1999/xlink" office:version="1.2"><office:meta><meta:generator>Sheet' + 'JS ' + XLSX.version + '</meta:generator></office:meta></office:document-meta>'; | ||||
| 	return function wmo(wb, opts) { | ||||
| 		return payload; | ||||
| 	}; | ||||
| })(); | ||||
| 
 | ||||
| /* ECMA-376 Part II 11.1 Core Properties Part */ | ||||
| /* [MS-OSHARED] 2.3.3.2.[1-2].1 (PIDSI/PIDDSI) */ | ||||
| var CORE_PROPS = [ | ||||
| @ -5217,7 +5236,7 @@ function dbf_to_aoa(buf, opts) { | ||||
| 				case 'T': | ||||
| 					var day = dd.read_shift(4), ms = dd.read_shift(4); | ||||
| 					throw new Error(day + " | " + ms); | ||||
| 					//out[R][C] = new Date(); // FIXME!!!
 | ||||
| 					//out[R][C] = new Date(); // TODO
 | ||||
| 					//break;
 | ||||
| 				case 'Y': out[R][C] = dd.read(4,'i')/1e4; break; | ||||
| 				case '0': | ||||
| @ -5267,7 +5286,9 @@ var SYLK = (function() { | ||||
| 		var Mval = 0, j; | ||||
| 		for (; ri !== records.length; ++ri) { | ||||
| 			Mval = 0; | ||||
| 			var rstr=records[ri].trim(), record=rstr.split(";"), RT=record[0], val; | ||||
| 			var rstr=records[ri].trim(); | ||||
| 			var record=rstr.replace(/;;/g, "\u0001").split(";").map(function(x) { return x.replace(/\u0001/g, ";"); }); | ||||
| 			var RT=record[0], val; | ||||
| 			if(rstr.length > 0) switch(RT) { | ||||
| 			case 'ID': break; /* header */ | ||||
| 			case 'E': break; /* EOF */ | ||||
| @ -5297,17 +5318,19 @@ var SYLK = (function() { | ||||
| 					next_cell_format = null; | ||||
| 					break; | ||||
| 				case 'E': | ||||
| 					/* formula = record[rj].substr(1); */ | ||||
| 					break; /* TODO: formula */ | ||||
| 					formula = rc_to_a1(record[rj].substr(1), {r:R,c:C}); | ||||
| 					arr[R][C] = [arr[R][C], formula]; | ||||
| 					break; | ||||
| 				default: if(opts && opts.WTF) throw new Error("SYLK bad record " + rstr); | ||||
| 			} break; | ||||
| 			case 'F': | ||||
| 			var F_seen = 0; | ||||
| 			for(rj=1; rj<record.length; ++rj) switch(record[rj].charAt(0)) { | ||||
| 				case 'X': C = parseInt(record[rj].substr(1))-1; break; | ||||
| 				case 'X': C = parseInt(record[rj].substr(1))-1; ++F_seen; break; | ||||
| 				case 'Y': | ||||
| 					R = parseInt(record[rj].substr(1))-1; C = 0; | ||||
| 					R = parseInt(record[rj].substr(1))-1; /*C = 0;*/ | ||||
| 					for(j = arr.length; j <= R; ++j) arr[j] = []; | ||||
| 					break; | ||||
| 					++F_seen; break; | ||||
| 				case 'M': Mval = parseInt(record[rj].substr(1)) / 20; break; | ||||
| 				case 'F': break; /* ??? */ | ||||
| 				case 'P': | ||||
| @ -5322,14 +5345,19 @@ var SYLK = (function() { | ||||
| 						Mval = parseInt(cw[2], 10); | ||||
| 						colinfo[j-1] = Mval == 0 ? {hidden:true}: {wch:Mval}; process_col(colinfo[j-1]); | ||||
| 					} break; | ||||
| 				case 'R': | ||||
| 				case 'C': /* default column format */ | ||||
| 					C = parseInt(record[rj].substr(1))-1; | ||||
| 					if(!colinfo[C]) colinfo[C] = {}; | ||||
| 					break; | ||||
| 				case 'R': /* row properties */ | ||||
| 					R = parseInt(record[rj].substr(1))-1; | ||||
| 					rowinfo[R] = {}; | ||||
| 					if(!rowinfo[R]) rowinfo[R] = {}; | ||||
| 					if(Mval > 0) { rowinfo[R].hpt = Mval; rowinfo[R].hpx = pt2px(Mval); } | ||||
| 					else if(Mval == 0) rowinfo[R].hidden = true; | ||||
| 					break; | ||||
| 				default: if(opts && opts.WTF) throw new Error("SYLK bad record " + rstr); | ||||
| 			} break; | ||||
| 			} | ||||
| 			if(F_seen < 2) next_cell_format = null; break; | ||||
| 			default: if(opts && opts.WTF) throw new Error("SYLK bad record " + rstr); | ||||
| 			} | ||||
| 		} | ||||
| @ -5444,7 +5472,7 @@ var DIF = (function() { | ||||
| 					if(data === 'TRUE') arr[R][C] = true; | ||||
| 					else if(data === 'FALSE') arr[R][C] = false; | ||||
| 					else if(+value == +value) arr[R][C] = +value; | ||||
| 					else if(!isNaN(new Date(value).getDate())) arr[R][C] = parseDate(value); | ||||
| 					else if(!isNaN(fuzzydate(value).getDate())) arr[R][C] = parseDate(value); | ||||
| 					else arr[R][C] = value; | ||||
| 					++C; break; | ||||
| 				case 1: | ||||
| @ -5576,7 +5604,7 @@ var PRN = (function() { | ||||
| 			else if(s == "TRUE") { cell.t = 'b'; cell.v = true; } | ||||
| 			else if(s == "FALSE") { cell.t = 'b'; cell.v = false; } | ||||
| 			else if(!isNaN(v = +s)) { cell.t = 'n'; cell.w = s; cell.v = v; } | ||||
| 			else if(!isNaN(new Date(s).getDate())) { cell.t = 'd'; cell.v = parseDate(s); } | ||||
| 			else if(!isNaN(fuzzydate(s).getDate())) { cell.t = 'd'; cell.v = parseDate(s); } | ||||
| 			else { | ||||
| 				cell.t = 's'; | ||||
| 				if(s.charAt(0) == '"' && s.charAt(s.length - 1) == '"') s = s.slice(1,-1).replace(/""/g,'"'); | ||||
| @ -5607,13 +5635,16 @@ var PRN = (function() { | ||||
| 	} | ||||
| 
 | ||||
| 	function prn_to_sheet(d, opts) { | ||||
| 		var str = "", bytes = firstbyte(d, opts); | ||||
| 		switch(opts.type) { | ||||
| 			case 'base64': return prn_to_sheet_str(Base64.decode(d), opts); | ||||
| 			case 'binary': return prn_to_sheet_str(d, opts); | ||||
| 			case 'buffer': return prn_to_sheet_str(d.toString('binary'), opts); | ||||
| 			case 'array': return prn_to_sheet_str(cc2str(d), opts); | ||||
| 			case 'base64': str = Base64.decode(d); break; | ||||
| 			case 'binary': str = d; break; | ||||
| 			case 'buffer': str = d.toString('binary'); break; | ||||
| 			case 'array': str = cc2str(d); break; | ||||
| 			default: throw new Error("Unrecognized type " + opts.type); | ||||
| 		} | ||||
| 		throw new Error("Unrecognized type " + opts.type); | ||||
| 		if(bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF) str = utf8read(str); | ||||
| 		return prn_to_sheet_str(str, opts); | ||||
| 	} | ||||
| 
 | ||||
| 	function prn_to_workbook(str, opts) { return sheet_to_workbook(prn_to_sheet(str, opts), opts); } | ||||
| @ -10726,8 +10757,8 @@ function write_ws_xml_cell(cell, ref, ws, opts, idx, wb) { | ||||
| 			else { | ||||
| 				cell.t = 'n'; | ||||
| 				vv = ''+(cell.v = datenum(parseDate(cell.v))); | ||||
| 				if(typeof cell.z === 'undefined') cell.z = SSF._table[14]; | ||||
| 			} | ||||
| 			if(typeof cell.z === 'undefined') cell.z = SSF._table[14]; | ||||
| 			break; | ||||
| 		default: vv = cell.v; break; | ||||
| 	} | ||||
| @ -10840,8 +10871,10 @@ return function parse_ws_xml_data(sdata, s, opts, guess, themes, styles) { | ||||
| 			} | ||||
| 
 | ||||
| 			if(tag.t == null && p.v === undefined) { | ||||
| 				if(!opts.sheetStubs) continue; | ||||
| 				p.t = "z"; | ||||
| 				if(p.f || p.F) { | ||||
| 					p.v = 0; p.t = "n"; | ||||
| 				} else if(!opts.sheetStubs) continue; | ||||
| 				else p.t = "z"; | ||||
| 			} | ||||
| 			else p.t = tag.t || "n"; | ||||
| 			if(guess.s.c > idx) guess.s.c = idx; | ||||
| @ -12090,11 +12123,20 @@ function parse_wb_defaults(wb) { | ||||
| 	_ssfopts.date1904 = parsexmlbool(wb.WBProps.date1904, 'date1904'); | ||||
| } | ||||
| 
 | ||||
| var badchars = "][*?\/\\".split(""); | ||||
| function check_ws_name(n, safe) { | ||||
| 	if(n.length > 31) { if(safe) return false; throw new Error("Sheet names cannot exceed 31 chars"); } | ||||
| 	var _good = true; | ||||
| 	badchars.forEach(function(c) { | ||||
| 		if(n.indexOf(c) == -1) return; | ||||
| 		if(!safe) throw new Error("Sheet name cannot contain : \\ / ? * [ ]"); | ||||
| 		_good = false; | ||||
| 	}); | ||||
| 	return _good; | ||||
| } | ||||
| function check_wb_names(N) { | ||||
| 	var badchars = "][*?\/\\".split(""); | ||||
| 	N.forEach(function(n,i) { | ||||
| 		badchars.forEach(function(c) { if(n.indexOf(c) > -1) throw new Error("Sheet name cannot contain : \\ / ? * [ ]"); }); | ||||
| 		if(n.length > 31) throw new Error("Sheet names cannot exceed 31 chars"); | ||||
| 		check_ws_name(n); | ||||
| 		for(var j = 0; j < i; ++j) if(n == N[j]) throw new Error("Duplicate Sheet Name: " + n); | ||||
| 	}); | ||||
| } | ||||
| @ -13943,7 +13985,7 @@ function parse_workbook(blob, options) { | ||||
| 					opts.enc = val; | ||||
| 					if(opts.WTF) console.error(val); | ||||
| 					if(!options.password) throw new Error("File is password-protected"); | ||||
| 					if(val.Type !== 0) throw new Error("Encryption scheme unsupported"); | ||||
| 					if(val.valid == null) throw new Error("Encryption scheme unsupported"); | ||||
| 					if(!val.valid) throw new Error("Password is incorrect"); | ||||
| 					break; | ||||
| 				case 'WriteAccess': opts.lastuser = val; break; | ||||
| @ -13960,7 +14002,7 @@ function parse_workbook(blob, options) { | ||||
| 				case 'Template': break; // TODO
 | ||||
| 				case 'RefreshAll': wb.opts.RefreshAll = val; break; | ||||
| 				case 'BookBool': break; // TODO
 | ||||
| 				case 'UsesELFs': /* if(val) console.error("Unsupported ELFs"); */ break; | ||||
| 				case 'UsesELFs': break; | ||||
| 				case 'MTRSettings': break; | ||||
| 				case 'CalcCount': wb.opts.CalcCount = val; break; | ||||
| 				case 'CalcDelta': wb.opts.CalcDelta = val; break; | ||||
| @ -16051,6 +16093,7 @@ function parse_dom_table(table, _opts) { | ||||
| function table_to_book(table, opts) { | ||||
| 	return sheet_to_workbook(parse_dom_table(table, opts), opts); | ||||
| } | ||||
| /* OpenDocument */ | ||||
| var parse_content_xml = (function() { | ||||
| 
 | ||||
| 	var parse_text_p = function(text, tag) { | ||||
| @ -16490,7 +16533,27 @@ var parse_content_xml = (function() { | ||||
| 		return out; | ||||
| 	}; | ||||
| })(); | ||||
| var write_content_xml = (function() { | ||||
| 
 | ||||
| function parse_ods(zip, opts) { | ||||
| 	opts = opts || ({}); | ||||
| 	var ods = !!safegetzipfile(zip, 'objectdata'); | ||||
| 	if(ods) var manifest = parse_manifest(getzipdata(zip, 'META-INF/manifest.xml'), opts); | ||||
| 	var content = getzipstr(zip, 'content.xml'); | ||||
| 	if(!content) throw new Error("Missing content.xml in " + (ods ? "ODS" : "UOF")+ " file"); | ||||
| 	return parse_content_xml(ods ? content : utf8read(content), opts); | ||||
| } | ||||
| function parse_fods(data, opts) { | ||||
| 	return parse_content_xml(data, opts); | ||||
| } | ||||
| 
 | ||||
| /* OpenDocument */ | ||||
| var write_styles_ods = (function() { | ||||
| 	var payload = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><office:document-styles xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" office:version="1.2"></office:document-styles>'; | ||||
| 	return function wso(wb, opts) { | ||||
| 		return payload; | ||||
| 	}; | ||||
| })(); | ||||
| var write_content_ods = (function() { | ||||
| 	var null_cell_xml = '          <table:table-cell />\n'; | ||||
| 	var covered_cell_xml = '          <table:covered-table-cell/>\n'; | ||||
| 	var write_ws = function(ws, wb, i, opts) { | ||||
| @ -16553,7 +16616,7 @@ var write_content_xml = (function() { | ||||
| 					//case 'e':
 | ||||
| 					default: o.push(null_cell_xml); continue; | ||||
| 				} | ||||
| 				o.push(writextag('table:table-cell', writextag('text:p', textp, {}), ct)); | ||||
| 				o.push('          ' + writextag('table:table-cell', writextag('text:p', textp, {}), ct) + '\n'); | ||||
| 			} | ||||
| 			o.push('        </table:table-row>\n'); | ||||
| 		} | ||||
| @ -16635,6 +16698,50 @@ var write_content_xml = (function() { | ||||
| 		return o.join(""); | ||||
| 	}; | ||||
| })(); | ||||
| 
 | ||||
| function write_ods(wb, opts) { | ||||
| 	if(opts.bookType == "fods") return write_content_ods(wb, opts); | ||||
| 
 | ||||
| var zip = new jszip(); | ||||
| 	var f = ""; | ||||
| 
 | ||||
| 	var manifest = []; | ||||
| 	var rdf = []; | ||||
| 
 | ||||
| 	/* 3:3.3 and 2:2.2.4 */ | ||||
| 	f = "mimetype"; | ||||
| 	zip.file(f, "application/vnd.oasis.opendocument.spreadsheet"); | ||||
| 
 | ||||
| 	/* Part 1 Section 2.2 Documents */ | ||||
| 	f = "content.xml"; | ||||
| 	zip.file(f, write_content_ods(wb, opts)); | ||||
| 	manifest.push([f, "text/xml"]); | ||||
| 	rdf.push([f, "ContentFile"]); | ||||
| 
 | ||||
| 	/* TODO: these are hard-coded styles to satiate excel */ | ||||
| 	f = "styles.xml"; | ||||
| 	zip.file(f, write_styles_ods(wb, opts)); | ||||
| 	manifest.push([f, "text/xml"]); | ||||
| 	rdf.push([f, "StylesFile"]); | ||||
| 
 | ||||
| 	/* Part 3 Section 6 Metadata Manifest File */ | ||||
| 	f = "manifest.rdf"; | ||||
| 	zip.file(f, write_rdf(rdf, opts)); | ||||
| 	manifest.push([f, "application/rdf+xml"]); | ||||
| 
 | ||||
| 	/* TODO: this is hard-coded to satiate excel */ | ||||
| 	f = "meta.xml"; | ||||
| 	zip.file(f, write_meta_ods(wb, opts)); | ||||
| 	manifest.push([f, "text/xml"]); | ||||
| 	rdf.push([f, "MetadataFile"]); | ||||
| 
 | ||||
| 	/* Part 3 Section 4 Manifest File */ | ||||
| 	f = "META-INF/manifest.xml"; | ||||
| 	zip.file(f, write_manifest(manifest, opts)); | ||||
| 
 | ||||
| 	return zip; | ||||
| } | ||||
| 
 | ||||
| /* actual implementation elsewhere, wrappers are for read/write */ | ||||
| function write_obj_str(factory) { | ||||
| 	return function write_str(wb, o) { | ||||
| @ -16651,49 +16758,6 @@ var write_slk_str = write_obj_str(SYLK); | ||||
| var write_dif_str = write_obj_str(DIF); | ||||
| var write_prn_str = write_obj_str(PRN); | ||||
| var write_txt_str = write_obj_str({from_sheet:sheet_to_txt}); | ||||
| /* Part 3: Packages */ | ||||
| function parse_ods(zip, opts) { | ||||
| 	opts = opts || ({}); | ||||
| 	var ods = !!safegetzipfile(zip, 'objectdata'); | ||||
| 	if(ods) var manifest = parse_manifest(getzipdata(zip, 'META-INF/manifest.xml'), opts); | ||||
| 	var content = getzipstr(zip, 'content.xml'); | ||||
| 	if(!content) throw new Error("Missing content.xml in " + (ods ? "ODS" : "UOF")+ " file"); | ||||
| 	return parse_content_xml(ods ? content : utf8read(content), opts); | ||||
| } | ||||
| function parse_fods(data, opts) { | ||||
| 	return parse_content_xml(data, opts); | ||||
| } | ||||
| 
 | ||||
| function write_ods(wb, opts) { | ||||
| 	if(opts.bookType == "fods") return write_content_xml(wb, opts); | ||||
| 
 | ||||
| var zip = new jszip(); | ||||
| 	var f = ""; | ||||
| 
 | ||||
| 	var manifest = []; | ||||
| 	var rdf = []; | ||||
| 
 | ||||
| 	/* 3:3.3 and 2:2.2.4 */ | ||||
| 	f = "mimetype"; | ||||
| 	zip.file(f, "application/vnd.oasis.opendocument.spreadsheet"); | ||||
| 
 | ||||
| 	/* Part 1 Section 2.2 Documents */ | ||||
| 	f = "content.xml"; | ||||
| 	zip.file(f, write_content_xml(wb, opts)); | ||||
| 	manifest.push([f, "text/xml"]); | ||||
| 	rdf.push([f, "ContentFile"]); | ||||
| 
 | ||||
| 	/* Part 3 Section 6 Metadata Manifest File */ | ||||
| 	f = "manifest.rdf"; | ||||
| 	zip.file(f, write_rdf(rdf, opts)); | ||||
| 	manifest.push([f, "application/rdf+xml"]); | ||||
| 
 | ||||
| 	/* Part 3 Section 4 Manifest File */ | ||||
| 	f = "META-INF/manifest.xml"; | ||||
| 	zip.file(f, write_manifest(manifest, opts)); | ||||
| 
 | ||||
| 	return zip; | ||||
| } | ||||
| function fix_opts_func(defaults) { | ||||
| 	return function fix_opts(opts) { | ||||
| 		for(var i = 0; i != defaults.length; ++i) { | ||||
| @ -17129,7 +17193,7 @@ function readSync(data, opts) { | ||||
| 		case 0x49: if(n[1] == 0x44) return SYLK.to_workbook(d, o); break; | ||||
| 		case 0x54: if(n[1] == 0x41 && n[2] == 0x42 && n[3] == 0x4C) return DIF.to_workbook(d, o); break; | ||||
| 		case 0x50: if(n[1] == 0x4B && n[2] < 0x20 && n[3] < 0x20) return read_zip(d, o); break; | ||||
| 		case 0xEF: return parse_xlml(d, o); | ||||
| 		case 0xEF: return n[3] == 0x3C ? parse_xlml(d, o) : PRN.to_workbook(d,o); | ||||
| 		case 0xFF: if(n[1] == 0xFE){ return read_utf16(d, o); } break; | ||||
| 		case 0x00: if(n[1] == 0x00 && n[2] >= 0x02 && n[3] == 0x00) return WK_.to_workbook(d, o); break; | ||||
| 		case 0x03: case 0x83: case 0x8B: return DBF.to_workbook(d, o); | ||||
| @ -17283,7 +17347,7 @@ function sheet_to_json(sheet, opts){ | ||||
| 	var outi = 0, counter = 0; | ||||
| 	var dense = Array.isArray(sheet); | ||||
| 	var R = r.s.r, C = 0, CC = 0; | ||||
| 	if(!sheet[R]) sheet[R] = []; | ||||
| 	if(dense && !sheet[R]) sheet[R] = []; | ||||
| 	for(C = r.s.c; C <= r.e.c; ++C) { | ||||
| 		cols[C] = encode_col(C); | ||||
| 		val = dense ? sheet[R][C] : sheet[cols[C] + rr]; | ||||
| @ -17451,6 +17515,110 @@ var utils = { | ||||
| 	sheet_to_formulae: sheet_to_formulae, | ||||
| 	sheet_to_row_object_array: sheet_to_json | ||||
| }; | ||||
| 
 | ||||
| (function(utils) { | ||||
| utils.consts = utils.consts || {}; | ||||
| function add_consts(R) { R.forEach(function(a){ utils.consts[a[0]] = a[1]; }); } | ||||
| 
 | ||||
| function get_default(x, y, z) { return x[y] != null ? x[y] : (x[y] = z); } | ||||
| 
 | ||||
| /* get cell, creating a stub if necessary */ | ||||
| function ws_get_cell_stub(ws, R, C) { | ||||
| 	/* A1 cell address */ | ||||
| 	if(typeof R == "string") return ws[R] || (ws[R] = {t:'z'}); | ||||
| 	/* cell address object */ | ||||
| 	if(typeof R != "number") return ws_get_cell_stub(ws, encode_cell(R)); | ||||
| 	/* R and C are 0-based indices */ | ||||
| 	return ws_get_cell_stub(ws, encode_cell({r:R,c:C})); | ||||
| } | ||||
| 
 | ||||
| /* find sheet index for given name / validate index */ | ||||
| function wb_sheet_idx(wb, sh) { | ||||
| 	if(typeof sh == "number") { | ||||
| 		if(sh >= 0 && wb.SheetNames.length > sh) return sh; | ||||
| 		throw new Error("Cannot find sheet # " + sh); | ||||
| 	} else if(typeof sh == "string") { | ||||
| 		var idx = wb.SheetNames.indexOf(sh); | ||||
| 		if(idx > -1) return idx; | ||||
| 		throw new Error("Cannot find sheet name |" + sh + "|"); | ||||
| 	} else throw new Error("Cannot find sheet |" + sh + "|"); | ||||
| } | ||||
| 
 | ||||
| /* simple blank workbook object */ | ||||
| utils.book_new = function() { | ||||
| 	return { SheetNames: [], Sheets: {} }; | ||||
| }; | ||||
| 
 | ||||
| /* add a worksheet to the end of a given workbook */ | ||||
| utils.book_append_sheet = function(wb, ws, name) { | ||||
| 	if(!name) for(var i = 1; i <= 0xFFFF; ++i) if(wb.SheetNames.indexOf("Sheet" + i) == -1) break; | ||||
| 	check_ws_name(name); | ||||
| 	if(wb.SheetNames.indexOf(name) >= 0) throw new Error("Worksheet with name |" + name + "| already exists!"); | ||||
| 
 | ||||
| 	wb.SheetNames.push(name); | ||||
| 	wb.Sheets[name] = ws; | ||||
| }; | ||||
| 
 | ||||
| /* set sheet visibility (visible/hidden/very hidden) */ | ||||
| utils.book_set_sheet_visibility = function(wb, sh, vis) { | ||||
| 	get_default(wb,"Workbook",{}); | ||||
| 	get_default(wb.Workbook,"Sheets",[]); | ||||
| 
 | ||||
| 	var idx = wb_sheet_idx(wb, sh); | ||||
| 	get_default(wb.Workbook.Sheets,idx, {}); | ||||
| 
 | ||||
| 	switch(vis) { | ||||
| 		case 0: case 1: case 2: break; | ||||
| 		default: throw new Error("Bad sheet visibility setting " + vis); | ||||
| 	} | ||||
| 	wb.Workbook.Sheets[idx].Hidden = vis; | ||||
| }; | ||||
| add_consts([ | ||||
| 	["SHEET_VISIBLE", 0], | ||||
| 	["SHEET_HIDDEN", 1], | ||||
| 	["SHEET_VERY_HIDDEN", 2] | ||||
| ]); | ||||
| 
 | ||||
| /* set number format */ | ||||
| utils.cell_set_number_format = function(cell, fmt) { | ||||
| 	cell.z = fmt; | ||||
| 	return cell; | ||||
| }; | ||||
| 
 | ||||
| /* set cell hyperlink */ | ||||
| utils.cell_set_hyperlink = function(cell, target, tooltip) { | ||||
| 	if(!target) { | ||||
| 		delete cell.l; | ||||
| 	} else { | ||||
| 		cell.l = { Target: target }; | ||||
| 		if(tooltip) cell.l.Tooltip = tooltip; | ||||
| 	} | ||||
| 	return cell; | ||||
| }; | ||||
| 
 | ||||
| /* add to cell comments */ | ||||
| utils.cell_add_comment = function(cell, text, author) { | ||||
| 	if(!cell.c) cell.c = []; | ||||
| 	cell.c.push({t:text, a:author||"SheetJS"}); | ||||
| }; | ||||
| 
 | ||||
| /* set array formula and flush related cells */ | ||||
| utils.sheet_set_array_formula = function(ws, range, formula) { | ||||
| 	var rng = typeof range != "string" ? range : safe_decode_range(range); | ||||
| 	var rngstr = typeof range == "string" ? range : encode_range(range); | ||||
| 	for(var R = rng.s.r; R <= rng.e.r; ++R) for(var C = rng.s.c; C <= rng.e.c; ++C) { | ||||
| 		var cell = ws_get_cell_stub(ws, R, C); | ||||
| 		cell.t = 'n'; | ||||
| 		cell.F = rngstr; | ||||
| 		delete cell.v;  | ||||
| 		if(R == rng.s.r && C == rng.s.c) cell.f = formula; | ||||
| 	} | ||||
| 	return ws; | ||||
| } | ||||
| 
 | ||||
| return utils; | ||||
| })(utils); | ||||
| 
 | ||||
| if(has_buf && typeof require != 'undefined') (function() { | ||||
| 	var Readable = require('stream').Readable; | ||||
| 
 | ||||
| @ -17513,6 +17681,7 @@ if(has_buf && typeof require != 'undefined') (function() { | ||||
| 		to_csv: write_csv_stream | ||||
| 	}; | ||||
| })(); | ||||
| 
 | ||||
| XLSX.parse_xlscfb = parse_xlscfb; | ||||
| XLSX.parse_ods = parse_ods; | ||||
| XLSX.parse_fods = parse_fods; | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user