forked from sheetjs/sheetjs
version bump 0.10.9: pin dependencies
- updated SSF 0.10.0, codepage 1.10.1, CFB 0.12.0, CRC32 / ADLER32 1.1.0 - pinned all devDependencies (fixes #740 h/t @the-spyke) - flow type updates - clarified meteor usage (fixes #751 h/t @NorthDecoder)
This commit is contained in:
parent
6732eb76f8
commit
ce37f99ec3
@ -6,6 +6,7 @@ node_modules/
|
||||
types/
|
||||
tests/
|
||||
test_files
|
||||
*.md
|
||||
|
||||
*.json
|
||||
*.log
|
||||
|
@ -28,6 +28,7 @@ tmp
|
||||
*.sheetjs
|
||||
*.exe
|
||||
.gitignore
|
||||
.fossaignore
|
||||
.eslintrc
|
||||
.jshintrc
|
||||
CONTRIBUTING.md
|
||||
|
@ -5,7 +5,7 @@ but not limited to API changes and file location changes. Minor behavioral
|
||||
changes may not be included if they are not expected to break existing code.
|
||||
|
||||
|
||||
## Unreleased (2017-??-??)
|
||||
## 0.10.9 (2017-07-28)
|
||||
|
||||
* XLML/HTML resolution logic looks further into the data stream to decide type
|
||||
* Errors thrown on suspected RTF files
|
||||
|
2
Makefile
2
Makefile
@ -14,7 +14,7 @@ FLOWTARGET=$(LIB).flow.js
|
||||
FLOWAUX=$(patsubst %.js,%.flow.js,$(AUXTARGETS))
|
||||
AUXSCPTS=xlsxworker1.js xlsxworker2.js xlsxworker.js
|
||||
FLOWTGTS=$(TARGET) $(AUXTARGETS) $(AUXSCPTS)
|
||||
UGLIFYOPTS=--support-ie8 -c -m
|
||||
UGLIFYOPTS=--support-ie8
|
||||
CLOSURE=/usr/local/lib/node_modules/google-closure-compiler/compiler.jar
|
||||
|
||||
## Main Targets
|
||||
|
@ -1 +1 @@
|
||||
XLSX.version = '0.10.8';
|
||||
XLSX.version = '0.10.9';
|
||||
|
180
bits/10_ssf.js
180
bits/10_ssf.js
@ -1,8 +1,8 @@
|
||||
/* ssf.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
/*jshint -W041 */
|
||||
var SSF = {};
|
||||
var make_ssf = function make_ssf(SSF){
|
||||
SSF.version = '0.9.4';
|
||||
var SSF/*:SSFModule*/ = ({}/*:any*/);
|
||||
var make_ssf = function make_ssf(SSF/*:SSFModule*/){
|
||||
SSF.version = '0.10.0';
|
||||
function _strrev(x/*:string*/)/*:string*/ { var o = "", i = x.length-1; while(i>=0) o += x.charAt(i--); return o; }
|
||||
function fill(c/*:string*/,l/*:number*/)/*:string*/ { var o = ""; while(o.length < l) o+=c; return o; }
|
||||
function pad0(v/*:any*/,d/*:number*/)/*:string*/{var t=""+v; return t.length>=d?t:fill('0',d-t.length)+t;}
|
||||
@ -13,16 +13,9 @@ function pad0r2(v/*:any*/,d/*:number*/)/*:string*/{var t=""+v; return t.length>=
|
||||
var p2_32 = Math.pow(2,32);
|
||||
function pad0r(v/*:any*/,d/*:number*/)/*:string*/{if(v>p2_32||v<-p2_32) return pad0r1(v,d); var i = Math.round(v); return pad0r2(i,d); }
|
||||
function isgeneral(s/*:string*/, i/*:?number*/)/*:boolean*/ { i = i || 0; return s.length >= 7 + i && (s.charCodeAt(i)|32) === 103 && (s.charCodeAt(i+1)|32) === 101 && (s.charCodeAt(i+2)|32) === 110 && (s.charCodeAt(i+3)|32) === 101 && (s.charCodeAt(i+4)|32) === 114 && (s.charCodeAt(i+5)|32) === 97 && (s.charCodeAt(i+6)|32) === 108; }
|
||||
/* Options */
|
||||
var opts_fmt/*:Array<Array<any> >*/ = [
|
||||
["date1904", 0],
|
||||
["output", ""],
|
||||
["WTF", false]
|
||||
];
|
||||
function fixopts(o){
|
||||
for(var y = 0; y != opts_fmt.length; ++y) if(o[opts_fmt[y][0]]===undefined) o[opts_fmt[y][0]]=opts_fmt[y][1];
|
||||
}
|
||||
SSF.opts = opts_fmt;
|
||||
/*::
|
||||
type SSF_write_num = {(type:string, fmt:string, val:number):string};
|
||||
*/
|
||||
var days/*:Array<Array<string> >*/ = [
|
||||
['Sun', 'Sunday'],
|
||||
['Mon', 'Monday'],
|
||||
@ -101,7 +94,47 @@ function frac(x/*:number*/, D/*:number*/, mixed/*:?boolean*/)/*:Array<number>*/
|
||||
var q = Math.floor(sgn * P/Q);
|
||||
return [q, sgn*P - q*Q, Q];
|
||||
}
|
||||
function general_fmt_int(v/*:number*/)/*:string*/ { return ""+v; }
|
||||
function parse_date_code(v/*:number*/,opts/*:?any*/,b2/*:?boolean*/) {
|
||||
if(v > 2958465 || v < 0) return null;
|
||||
var date = (v|0), time = Math.floor(86400 * (v - date)), dow=0;
|
||||
var dout=[];
|
||||
var out={D:date, T:time, u:86400*(v-date)-time,y:0,m:0,d:0,H:0,M:0,S:0,q:0};
|
||||
if(Math.abs(out.u) < 1e-6) out.u = 0;
|
||||
if(opts && opts.date1904) date += 1462;
|
||||
if(out.u > 0.9999) {
|
||||
out.u = 0;
|
||||
if(++time == 86400) { out.T = time = 0; ++date; ++out.D; }
|
||||
}
|
||||
if(date === 60) {dout = b2 ? [1317,10,29] : [1900,2,29]; dow=3;}
|
||||
else if(date === 0) {dout = b2 ? [1317,8,29] : [1900,1,0]; dow=6;}
|
||||
else {
|
||||
if(date > 60) --date;
|
||||
/* 1 = Jan 1 1900 in Gregorian */
|
||||
var d = new Date(1900, 0, 1);
|
||||
d.setDate(d.getDate() + date - 1);
|
||||
dout = [d.getFullYear(), d.getMonth()+1,d.getDate()];
|
||||
dow = d.getDay();
|
||||
if(date < 60) dow = (dow + 6) % 7;
|
||||
if(b2) dow = fix_hijri(d, dout);
|
||||
}
|
||||
out.y = dout[0]; out.m = dout[1]; out.d = dout[2];
|
||||
out.S = time % 60; time = Math.floor(time / 60);
|
||||
out.M = time % 60; time = Math.floor(time / 60);
|
||||
out.H = time;
|
||||
out.q = dow;
|
||||
return out;
|
||||
}
|
||||
SSF.parse_date_code = parse_date_code;
|
||||
var basedate = new Date(1899, 11, 31, 0, 0, 0);
|
||||
var dnthresh = basedate.getTime();
|
||||
var base1904 = new Date(1900, 2, 1, 0, 0, 0);
|
||||
function datenum_local(v/*:Date*/, date1904/*:?boolean*/)/*:number*/ {
|
||||
var epoch = v.getTime();
|
||||
if(date1904) epoch -= 1461*24*60*60*1000;
|
||||
else if(v >= base1904) epoch += 24*60*60*1000;
|
||||
return (epoch - (dnthresh + (v.getTimezoneOffset() - basedate.getTimezoneOffset()) * 60000)) / (24 * 60 * 60 * 1000);
|
||||
}
|
||||
function general_fmt_int(v/*:number*/)/*:string*/ { return v.toString(10); }
|
||||
SSF._general_int = general_fmt_int;
|
||||
var general_fmt_num = (function make_general_fmt_num() {
|
||||
var gnr1 = /\.(\d*[1-9])0+$/, gnr2 = /\.0*$/, gnr4 = /\.(\d*[1-9])0+/, gnr5 = /\.0*[Ee]/, gnr6 = /(E[+-])(\d)$/;
|
||||
@ -132,50 +165,20 @@ return function general_fmt_num(v/*:number*/)/*:string*/ {
|
||||
return gfn5(gfn4(o));
|
||||
};})();
|
||||
SSF._general_num = general_fmt_num;
|
||||
function general_fmt(v/*:any*/) {
|
||||
function general_fmt(v/*:any*/, opts/*:any*/) {
|
||||
switch(typeof v) {
|
||||
case 'string': return v;
|
||||
case 'boolean': return v ? "TRUE" : "FALSE";
|
||||
case 'number': return (v|0) === v ? general_fmt_int(v/*, opts*/) : general_fmt_num(v/*, opts*/);
|
||||
case 'number': return (v|0) === v ? general_fmt_int(v) : general_fmt_num(v);
|
||||
case 'undefined': return "";
|
||||
case 'object': if(v == null) return "";
|
||||
case 'object':
|
||||
if(v == null) return "";
|
||||
if(v instanceof Date) return format(14, datenum_local(v, opts && opts.date1904), opts);
|
||||
}
|
||||
throw new Error("unsupported value in General format: " + v);
|
||||
}
|
||||
SSF._general = general_fmt;
|
||||
function fix_hijri(/*date, o*/) { return 0; }
|
||||
function parse_date_code(v/*:number*/,opts/*:?any*/,b2/*:?boolean*/) {
|
||||
if(v > 2958465 || v < 0) return null;
|
||||
var date = (v|0), time = Math.floor(86400 * (v - date)), dow=0;
|
||||
var dout=[];
|
||||
var out={D:date, T:time, u:86400*(v-date)-time,y:0,m:0,d:0,H:0,M:0,S:0,q:0};
|
||||
if(Math.abs(out.u) < 1e-6) out.u = 0;
|
||||
fixopts(opts != null ? opts : (opts=[]));
|
||||
if(opts.date1904) date += 1462;
|
||||
if(out.u > 0.9999) {
|
||||
out.u = 0;
|
||||
if(++time == 86400) { out.T = time = 0; ++date; ++out.D; }
|
||||
}
|
||||
if(date === 60) {dout = b2 ? [1317,10,29] : [1900,2,29]; dow=3;}
|
||||
else if(date === 0) {dout = b2 ? [1317,8,29] : [1900,1,0]; dow=6;}
|
||||
else {
|
||||
if(date > 60) --date;
|
||||
/* 1 = Jan 1 1900 in Gregorian */
|
||||
var d = new Date(1900, 0, 1);
|
||||
d.setDate(d.getDate() + date - 1);
|
||||
dout = [d.getFullYear(), d.getMonth()+1,d.getDate()];
|
||||
dow = d.getDay();
|
||||
if(date < 60) dow = (dow + 6) % 7;
|
||||
if(b2) dow = fix_hijri(d, dout);
|
||||
}
|
||||
out.y = dout[0]; out.m = dout[1]; out.d = dout[2];
|
||||
out.S = time % 60; time = Math.floor(time / 60);
|
||||
out.M = time % 60; time = Math.floor(time / 60);
|
||||
out.H = time;
|
||||
out.q = dow;
|
||||
return out;
|
||||
}
|
||||
SSF.parse_date_code = parse_date_code;
|
||||
function fix_hijri(/*::date, o*/) { return 0; }
|
||||
/*jshint -W086 */
|
||||
function write_date(type/*:number*/, fmt/*:string*/, val, ss0/*:?number*/)/*:string*/ {
|
||||
var o="", ss=0, tt=0, y = val.y, out, outl = 0;
|
||||
@ -217,23 +220,17 @@ function write_date(type/*:number*/, fmt/*:string*/, val, ss0/*:?number*/)/*:str
|
||||
default: throw 'bad minute format: ' + fmt;
|
||||
} break;
|
||||
case 115: /* 's' seconds */
|
||||
if(val.u === 0) switch(fmt) {
|
||||
case 's': case 'ss': return pad0(val.S, fmt.length);
|
||||
case '.0': case '.00': case '.000':
|
||||
}
|
||||
switch(fmt) {
|
||||
case 's': case 'ss': case '.0': case '.00': case '.000':
|
||||
/*::if(!ss0) ss0 = 0; */
|
||||
if(ss0 >= 2) tt = ss0 === 3 ? 1000 : 100;
|
||||
else tt = ss0 === 1 ? 10 : 1;
|
||||
ss = Math.round((tt)*(val.S + val.u));
|
||||
if(ss >= 60*tt) ss = 0;
|
||||
if(fmt === 's') return ss === 0 ? "0" : ""+ss/tt;
|
||||
o = pad0(ss,2 + ss0);
|
||||
if(fmt === 'ss') return o.substr(0,2);
|
||||
return "." + o.substr(2,fmt.length-1);
|
||||
default: throw 'bad second format: ' + fmt;
|
||||
}
|
||||
if(fmt != 's' && fmt != 'ss' && fmt != '.0' && fmt != '.00' && fmt != '.000') throw 'bad second format: ' + fmt;
|
||||
if(val.u === 0 && (fmt == "s" || fmt == "ss")) return pad0(val.S, fmt.length);
|
||||
/*::if(!ss0) ss0 = 0; */
|
||||
if(ss0 >= 2) tt = ss0 === 3 ? 1000 : 100;
|
||||
else tt = ss0 === 1 ? 10 : 1;
|
||||
ss = Math.round((tt)*(val.S + val.u));
|
||||
if(ss >= 60*tt) ss = 0;
|
||||
if(fmt === 's') return ss === 0 ? "0" : ""+ss/tt;
|
||||
o = pad0(ss,2 + ss0);
|
||||
if(fmt === 'ss') return o.substr(0,2);
|
||||
return "." + o.substr(2,fmt.length-1);
|
||||
case 90: /* 'Z' absolute time */
|
||||
switch(fmt) {
|
||||
case '[h]': case '[hh]': out = val.D*24+val.H; break;
|
||||
@ -248,12 +245,13 @@ function write_date(type/*:number*/, fmt/*:string*/, val, ss0/*:?number*/)/*:str
|
||||
}
|
||||
/*jshint +W086 */
|
||||
function commaify(s/*:string*/)/*:string*/ {
|
||||
if(s.length <= 3) return s;
|
||||
var j = (s.length % 3), o = s.substr(0,j);
|
||||
for(; j!=s.length; j+=3) o+=(o.length > 0 ? "," : "") + s.substr(j,3);
|
||||
var w = 3;
|
||||
if(s.length <= w) return s;
|
||||
var j = (s.length % w), o = s.substr(0,j);
|
||||
for(; j!=s.length; j+=w) o+=(o.length > 0 ? "," : "") + s.substr(j,w);
|
||||
return o;
|
||||
}
|
||||
var write_num = (function make_write_num(){
|
||||
var write_num/*:SSF_write_num*/ = (function make_write_num(){
|
||||
var pct1 = /%/g;
|
||||
function write_num_pct(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string*/{
|
||||
var sfmt = fmt.replace(pct1,""), mul = fmt.length - sfmt.length;
|
||||
@ -591,7 +589,7 @@ function fmt_is_date(fmt/*:string*/)/*:boolean*/ {
|
||||
}
|
||||
SSF.is_date = fmt_is_date;
|
||||
function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) {
|
||||
var out = [], o = "", i = 0, c = "", lst='t', q, dt, j, cc;
|
||||
var out = [], o = "", i = 0, c = "", lst='t', dt, j, cc;
|
||||
var hr='H';
|
||||
/* Tokenize */
|
||||
while(i < fmt.length) {
|
||||
@ -619,12 +617,12 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) {
|
||||
case 'm': case 'd': case 'y': case 'h': case 's': case 'e': case 'g':
|
||||
if(v < 0) return "";
|
||||
if(dt==null) { dt=parse_date_code(v, opts); if(dt==null) return ""; }
|
||||
o = c; while(++i<fmt.length && fmt.charAt(i).toLowerCase() === c) o+=c;
|
||||
o = c; while(++i < fmt.length && fmt.charAt(i).toLowerCase() === c) o+=c;
|
||||
if(c === 'm' && lst.toLowerCase() === 'h') c = 'M';
|
||||
if(c === 'h') c = hr;
|
||||
out[out.length] = {t:c, v:o}; lst = c; break;
|
||||
case 'A': case 'a':
|
||||
q={t:c, v:c};
|
||||
var q={t:c, v:c};
|
||||
if(dt==null) dt=parse_date_code(v, opts);
|
||||
if(fmt.substr(i, 3).toUpperCase() === "A/P") { if(dt!=null) q.v = dt.H >= 12 ? "P" : "A"; q.t = 'T'; hr='h';i+=3;}
|
||||
else if(fmt.substr(i,5).toUpperCase() === "AM/PM") { if(dt!=null) q.v = dt.H >= 12 ? "PM" : "AM"; q.t = 'T'; i+=5; hr='h'; }
|
||||
@ -647,7 +645,7 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) {
|
||||
/* Numbers */
|
||||
case '.':
|
||||
if(dt != null) {
|
||||
o = c; while((c=fmt.charAt(++i)) === "0") o += c;
|
||||
o = c; while(++i < fmt.length && (c=fmt.charAt(i)) === "0") o += c;
|
||||
out[out.length] = {t:'s', v:o}; break;
|
||||
}
|
||||
/* falls through */
|
||||
@ -656,7 +654,7 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) {
|
||||
out[out.length] = {t:'n', v:o}; break;
|
||||
case '?':
|
||||
o = c; while(fmt.charAt(++i) === c) o+=c;
|
||||
q={t:c, v:o}; out[out.length] = q; lst = c; break;
|
||||
out[out.length] = {t:c, v:o}; lst = c; break;
|
||||
case '*': ++i; if(fmt.charAt(i) == ' ' || fmt.charAt(i) == '*') ++i; break; // **
|
||||
case '(': case ')': out[out.length] = {t:(flen===1?'t':c), v:c}; ++i; break;
|
||||
case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
|
||||
@ -807,7 +805,7 @@ function chkcond(v, rr) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function choose_fmt(f/*:string*/, v) {
|
||||
function choose_fmt(f/*:string*/, v/*:any*/) {
|
||||
var fmt = split_fmt(f);
|
||||
var l = fmt.length, lat = fmt[l-1].indexOf("@");
|
||||
if(l<4 && lat>-1) --l;
|
||||
@ -842,17 +840,37 @@ function format(fmt/*:string|number*/,v/*:any*/,o/*:?any*/) {
|
||||
break;
|
||||
}
|
||||
if(isgeneral(sfmt,0)) return general_fmt(v, o);
|
||||
if(v instanceof Date) v = datenum_local(v, o.date1904);
|
||||
var f = choose_fmt(sfmt, v);
|
||||
if(isgeneral(f[1])) return general_fmt(v, o);
|
||||
if(v === true) v = "TRUE"; else if(v === false) v = "FALSE";
|
||||
else if(v === "" || v == null) return "";
|
||||
return eval_fmt(f[1], v, o, f[0]);
|
||||
}
|
||||
function load_entry(fmt/*:string*/, idx/*:?number*/)/*:number*/ {
|
||||
if(typeof idx != 'number') {
|
||||
idx = +idx || -1;
|
||||
/*::if(typeof idx != 'number') return 0x188; */
|
||||
for(var i = 0; i < 0x0188; ++i) {
|
||||
/*::if(typeof idx != 'number') return 0x188; */
|
||||
if(table_fmt[i] == undefined) { if(idx < 0) idx = i; continue; }
|
||||
if(table_fmt[i] == fmt) { idx = i; break; }
|
||||
}
|
||||
/*::if(typeof idx != 'number') return 0x188; */
|
||||
if(idx < 0) idx = 0x187;
|
||||
}
|
||||
/*::if(typeof idx != 'number') return 0x188; */
|
||||
table_fmt[idx] = fmt;
|
||||
return idx;
|
||||
}
|
||||
SSF.load = load_entry;
|
||||
SSF._table = table_fmt;
|
||||
SSF.load = function load_entry(fmt/*:string*/, idx/*:number*/) { table_fmt[idx] = fmt; };
|
||||
SSF.format = format;
|
||||
SSF.get_table = function get_table() { return table_fmt; };
|
||||
SSF.load_table = function load_table(tbl/*:{[n:number]:string}*/) { for(var i=0; i!=0x0188; ++i) if(tbl[i] !== undefined) SSF.load(tbl[i], i); };
|
||||
SSF.get_table = function get_table()/*:SSFTable*/ { return table_fmt; };
|
||||
SSF.load_table = function load_table(tbl/*:SSFTable*/)/*:void*/ {
|
||||
for(var i=0; i!=0x0188; ++i)
|
||||
if(tbl[i] !== undefined) load_entry(tbl[i], i);
|
||||
};
|
||||
SSF.init_table = init_table;
|
||||
SSF.format = format;
|
||||
};
|
||||
make_ssf(SSF);
|
||||
|
147
bits/18_cfb.js
147
bits/18_cfb.js
@ -18,20 +18,25 @@ declare var chr1:any;
|
||||
/*jshint eqnull:true */
|
||||
|
||||
/*::
|
||||
declare var DO_NOT_EXPORT_CFB:any;
|
||||
type SectorEntry = any;
|
||||
declare var DO_NOT_EXPORT_CFB:?boolean;
|
||||
type SectorEntry = {
|
||||
name?:string;
|
||||
nodes?:Array<number>;
|
||||
data:RawBytes;
|
||||
};
|
||||
type SectorList = {
|
||||
(k:string|number):SectorEntry;
|
||||
[k:string|number]:SectorEntry;
|
||||
name:?string;
|
||||
fat_addrs:any;
|
||||
fat_addrs:Array<number>;
|
||||
ssz:number;
|
||||
}
|
||||
type CFBFiles = {[n:string]:CFBEntry};
|
||||
*/
|
||||
/* [MS-CFB] v20130118 */
|
||||
var CFB = (function _CFB(){
|
||||
var exports = {};
|
||||
exports.version = '0.11.1';
|
||||
function parse(file) {
|
||||
var exports/*:CFBModule*/ = /*::(*/{}/*:: :any)*/;
|
||||
exports.version = '0.12.0';
|
||||
function parse(file/*:RawBytes*/, options/*:CFBReadOpts*/)/*:CFBContainer*/ {
|
||||
var mver = 3; // major version
|
||||
var ssz = 512; // sector size
|
||||
var nmfs = 0; // number of mini FAT sectors
|
||||
@ -40,10 +45,10 @@ var dir_start = 0; // first directory sector location
|
||||
var minifat_start = 0; // first mini FAT sector location
|
||||
var difat_start = 0; // first mini FAT sector location
|
||||
|
||||
var fat_addrs = []; // locations of FAT sectors
|
||||
var fat_addrs/*:Array<number>*/ = []; // locations of FAT sectors
|
||||
|
||||
/* [MS-CFB] 2.2 Compound File Header */
|
||||
var blob/*:any*/ = file.slice(0,512);
|
||||
var blob/*:CFBlob*/ = /*::(*/file.slice(0,512)/*:: :any)*/;
|
||||
prep_blob(blob, 0);
|
||||
|
||||
/* major version */
|
||||
@ -55,14 +60,14 @@ switch(mver) {
|
||||
}
|
||||
|
||||
/* reprocess header */
|
||||
if(ssz !== 512) { blob = file.slice(0,ssz); prep_blob(blob, 28 /* blob.l */); }
|
||||
if(ssz !== 512) { blob = /*::(*/file.slice(0,ssz)/*:: :any)*/; prep_blob(blob, 28 /* blob.l */); }
|
||||
/* Save header for final object */
|
||||
var header = file.slice(0,ssz);
|
||||
var header/*:RawBytes*/ = file.slice(0,ssz);
|
||||
|
||||
check_shifts(blob, mver);
|
||||
|
||||
// Number of Directory Sectors
|
||||
var nds = blob.read_shift(4, 'i');
|
||||
var nds/*:number*/ = blob.read_shift(4, 'i');
|
||||
if(mver === 3 && nds !== 0) throw new Error('# Directory Sectors: Expected 0 saw ' + nds);
|
||||
|
||||
// Number of FAT Sectors
|
||||
@ -91,14 +96,14 @@ difat_start = blob.read_shift(4, 'i');
|
||||
ndfs = blob.read_shift(4, 'i');
|
||||
|
||||
// Grab FAT Sector Locations
|
||||
for(var q, j = 0; j < 109; ++j) { /* 109 = (512 - blob.l)>>>2; */
|
||||
for(var q = -1, j = 0; j < 109; ++j) { /* 109 = (512 - blob.l)>>>2; */
|
||||
q = blob.read_shift(4, 'i');
|
||||
if(q<0) break;
|
||||
fat_addrs[j] = q;
|
||||
}
|
||||
|
||||
/** Break the file up into sectors */
|
||||
var sectors = sectorify(file, ssz);
|
||||
var sectors/*:Array<RawBytes>*/ = sectorify(file, ssz);
|
||||
|
||||
sleuth_fat(difat_start, ndfs, sectors, ssz, fat_addrs);
|
||||
|
||||
@ -112,13 +117,12 @@ sector_list.fat_addrs = fat_addrs;
|
||||
sector_list.ssz = ssz;
|
||||
|
||||
/* [MS-CFB] 2.6.1 Compound File Directory Entry */
|
||||
var files = {}, Paths/*:any*/ = [], FileIndex = [], FullPaths = [], FullPathDir = {};
|
||||
var files/*:CFBFiles*/ = {}, Paths/*:Array<string>*/ = [], FileIndex/*:CFBFileIndex*/ = [], FullPaths/*:Array<string>*/ = [], FullPathDir = {};
|
||||
read_directory(dir_start, sector_list, sectors, Paths, nmfs, files, FileIndex);
|
||||
|
||||
build_full_paths(FileIndex, FullPathDir, FullPaths, Paths);
|
||||
|
||||
var root_name = Paths.shift();
|
||||
Paths.root = root_name;
|
||||
var root_name/*:string*/ = Paths.shift();
|
||||
|
||||
/* [MS-CFB] 2.6.4 (Unicode 3.0.1 case conversion) */
|
||||
var find_path = make_find_path(FullPaths, Paths, FileIndex, files, root_name);
|
||||
@ -133,7 +137,7 @@ return {
|
||||
} // parse
|
||||
|
||||
/* [MS-CFB] 2.2 Compound File Header -- read up to major version */
|
||||
function check_get_mver(blob) {
|
||||
function check_get_mver(blob/*:CFBlob*/)/*:[number, number]*/ {
|
||||
// header signature 8
|
||||
blob.chk(HEADER_SIGNATURE, 'Header Signature: ');
|
||||
|
||||
@ -141,11 +145,11 @@ function check_get_mver(blob) {
|
||||
blob.chk(HEADER_CLSID, 'CLSID: ');
|
||||
|
||||
// minor version 2
|
||||
var mver = blob.read_shift(2, 'u');
|
||||
var mver/*:number*/ = blob.read_shift(2, 'u');
|
||||
|
||||
return [blob.read_shift(2,'u'), mver];
|
||||
}
|
||||
function check_shifts(blob, mver) {
|
||||
function check_shifts(blob/*:CFBlob*/, mver/*:number*/)/*:void*/ {
|
||||
var shift = 0x09;
|
||||
|
||||
// Byte Order
|
||||
@ -167,18 +171,18 @@ function check_shifts(blob, mver) {
|
||||
}
|
||||
|
||||
/** Break the file up into sectors */
|
||||
function sectorify(file, ssz) {
|
||||
function sectorify(file/*:RawBytes*/, ssz/*:number*/)/*:Array<RawBytes>*/ {
|
||||
var nsectors = Math.ceil(file.length/ssz)-1;
|
||||
var sectors = new Array(nsectors);
|
||||
var sectors/*:Array<RawBytes>*/ = [];
|
||||
for(var i=1; i < nsectors; ++i) sectors[i-1] = file.slice(i*ssz,(i+1)*ssz);
|
||||
sectors[nsectors-1] = file.slice(nsectors*ssz);
|
||||
return sectors;
|
||||
}
|
||||
|
||||
/* [MS-CFB] 2.6.4 Red-Black Tree */
|
||||
function build_full_paths(FI, FPD, FP, Paths) {
|
||||
function build_full_paths(FI/*:CFBFileIndex*/, FPD/*:CFBFullPathDir*/, FP/*:Array<string>*/, Paths/*:Array<string>*/)/*:void*/ {
|
||||
var i = 0, L = 0, R = 0, C = 0, j = 0, pl = Paths.length;
|
||||
var dad = new Array(pl), q = new Array(pl);
|
||||
var dad/*:Array<number>*/ = [], q/*:Array<number>*/ = [];
|
||||
|
||||
for(; i < pl; ++i) { dad[i]=q[i]=i; FP[i]=Paths[i]; }
|
||||
|
||||
@ -217,17 +221,17 @@ function build_full_paths(FI, FPD, FP, Paths) {
|
||||
}
|
||||
|
||||
/* [MS-CFB] 2.6.4 */
|
||||
function make_find_path(FullPaths, Paths, FileIndex, files, root_name) {
|
||||
var UCFullPaths = new Array(FullPaths.length);
|
||||
var UCPaths = new Array(Paths.length), i;
|
||||
function make_find_path(FullPaths/*:Array<string>*/, Paths/*:Array<string>*/, FileIndex/*:CFBFileIndex*/, files/*:CFBFiles*/, root_name/*:string*/)/*:CFBFindPath*/ {
|
||||
var UCFullPaths/*:Array<string>*/ = [];
|
||||
var UCPaths/*:Array<string>*/ = [], i = 0;
|
||||
for(i = 0; i < FullPaths.length; ++i) UCFullPaths[i] = FullPaths[i].toUpperCase().replace(chr0,'').replace(chr1,'!');
|
||||
for(i = 0; i < Paths.length; ++i) UCPaths[i] = Paths[i].toUpperCase().replace(chr0,'').replace(chr1,'!');
|
||||
return function find_path(path/*:string*/) {
|
||||
var k;
|
||||
return function find_path(path/*:string*/)/*:?CFBEntry*/ {
|
||||
var k/*:boolean*/ = false;
|
||||
if(path.charCodeAt(0) === 47 /* "/" */) { k=true; path = root_name + path; }
|
||||
else k = path.indexOf("/") !== -1;
|
||||
var UCPath = path.toUpperCase().replace(chr0,'').replace(chr1,'!');
|
||||
var w = k === true ? UCFullPaths.indexOf(UCPath) : UCPaths.indexOf(UCPath);
|
||||
var UCPath/*:string*/ = path.toUpperCase().replace(chr0,'').replace(chr1,'!');
|
||||
var w/*:number*/ = k === true ? UCFullPaths.indexOf(UCPath) : UCPaths.indexOf(UCPath);
|
||||
if(w === -1) return null;
|
||||
return k === true ? FileIndex[w] : files[Paths[w]];
|
||||
};
|
||||
@ -235,8 +239,8 @@ function make_find_path(FullPaths, Paths, FileIndex, files, root_name) {
|
||||
|
||||
/** Chase down the rest of the DIFAT chain to build a comprehensive list
|
||||
DIFAT chains by storing the next sector number as the last 32 bytes */
|
||||
function sleuth_fat(idx, cnt, sectors, ssz, fat_addrs) {
|
||||
var q;
|
||||
function sleuth_fat(idx/*:number*/, cnt/*:number*/, sectors/*:Array<RawBytes>*/, ssz/*:number*/, fat_addrs)/*:void*/ {
|
||||
var q/*:number*/ = ENDOFCHAIN;
|
||||
if(idx === ENDOFCHAIN) {
|
||||
if(cnt !== 0) throw new Error("DIFAT chain shorter than expected");
|
||||
} else if(idx !== -1 /*FREESECT*/) {
|
||||
@ -251,13 +255,11 @@ function sleuth_fat(idx, cnt, sectors, ssz, fat_addrs) {
|
||||
}
|
||||
|
||||
/** Follow the linked list of sectors for a given starting point */
|
||||
function get_sector_list(sectors, start, fat_addrs, ssz, chkd) {
|
||||
function get_sector_list(sectors/*:Array<RawBytes>*/, start/*:number*/, fat_addrs/*:Array<number>*/, ssz/*:number*/, chkd/*:?Array<boolean>*/)/*:SectorEntry*/ {
|
||||
var sl = sectors.length;
|
||||
var buf, buf_chain;
|
||||
if(!chkd) chkd = new Array(sl);
|
||||
var modulus = ssz - 1, j, jj;
|
||||
buf = [];
|
||||
buf_chain = [];
|
||||
var buf/*:Array<number>*/ = [], buf_chain/*:Array<any>*/ = [];
|
||||
if(!chkd) chkd = [];
|
||||
var modulus = ssz - 1, j = 0, jj = 0;
|
||||
for(j=start; j>=0;) {
|
||||
chkd[j] = true;
|
||||
buf[buf.length] = j;
|
||||
@ -272,44 +274,43 @@ function get_sector_list(sectors, start, fat_addrs, ssz, chkd) {
|
||||
}
|
||||
|
||||
/** Chase down the sector linked lists */
|
||||
function make_sector_list(sectors, dir_start, fat_addrs, ssz/*:number*/)/*:any*/ {
|
||||
var sl = sectors.length, sector_list = new Array(sl);
|
||||
var chkd = new Array(sl), buf, buf_chain;
|
||||
var modulus = ssz - 1, i, j, k, jj;
|
||||
function make_sector_list(sectors/*:Array<RawBytes>*/, dir_start/*:number*/, fat_addrs/*:Array<number>*/, ssz/*:number*/)/*:SectorList*/ {
|
||||
var sl = sectors.length, sector_list/*:SectorList*/ = ([]/*:any*/);
|
||||
var chkd/*:Array<boolean>*/ = [], buf/*:Array<number>*/ = [], buf_chain/*:Array<RawBytes>*/ = [];
|
||||
var modulus = ssz - 1, i=0, j=0, k=0, jj=0;
|
||||
for(i=0; i < sl; ++i) {
|
||||
buf = [];
|
||||
buf = ([]/*:Array<number>*/);
|
||||
k = (i + dir_start); if(k >= sl) k-=sl;
|
||||
if(chkd[k] === true) continue;
|
||||
if(chkd[k]) continue;
|
||||
buf_chain = [];
|
||||
for(j=k; j>=0;) {
|
||||
chkd[j] = true;
|
||||
buf[buf.length] = j;
|
||||
buf_chain.push(sectors[j]);
|
||||
var addr = fat_addrs[Math.floor(j*4/ssz)];
|
||||
var addr/*:number*/ = fat_addrs[Math.floor(j*4/ssz)];
|
||||
jj = ((j*4) & modulus);
|
||||
if(ssz < 4 + jj) throw new Error("FAT boundary crossed: " + j + " 4 "+ssz);
|
||||
if(!sectors[addr]) break;
|
||||
j = __readInt32LE(sectors[addr], jj);
|
||||
}
|
||||
sector_list[k] = {nodes: buf, data:__toBuffer([buf_chain])};
|
||||
sector_list[k] = ({nodes: buf, data:__toBuffer([buf_chain])}/*:SectorEntry*/);
|
||||
}
|
||||
return sector_list;
|
||||
}
|
||||
|
||||
/* [MS-CFB] 2.6.1 Compound File Directory Entry */
|
||||
function read_directory(dir_start, sector_list, sectors, Paths, nmfs, files, FileIndex) {
|
||||
var blob;
|
||||
function read_directory(dir_start/*:number*/, sector_list/*:SectorList*/, sectors/*:Array<RawBytes>*/, Paths/*:Array<string>*/, nmfs, files, FileIndex) {
|
||||
var minifat_store = 0, pl = (Paths.length?2:0);
|
||||
var sector = sector_list[dir_start].data;
|
||||
var i = 0, namelen = 0, name, o, ctime, mtime;
|
||||
var i = 0, namelen = 0, name;
|
||||
for(; i < sector.length; i+= 128) {
|
||||
blob = sector.slice(i, i+128);
|
||||
var blob/*:CFBlob*/ = /*::(*/sector.slice(i, i+128)/*:: :any)*/;
|
||||
prep_blob(blob, 64);
|
||||
namelen = blob.read_shift(2);
|
||||
if(namelen === 0) continue;
|
||||
name = __utf16le(blob,0,namelen-pl);
|
||||
Paths.push(name);
|
||||
o = ({
|
||||
var o/*:CFBEntry*/ = ({
|
||||
name: name,
|
||||
type: blob.read_shift(1),
|
||||
color: blob.read_shift(1),
|
||||
@ -317,16 +318,14 @@ function read_directory(dir_start, sector_list, sectors, Paths, nmfs, files, Fil
|
||||
R: blob.read_shift(4, 'i'),
|
||||
C: blob.read_shift(4, 'i'),
|
||||
clsid: blob.read_shift(16),
|
||||
state: blob.read_shift(4, 'i')
|
||||
}/*:any*/);
|
||||
ctime = blob.read_shift(2) + blob.read_shift(2) + blob.read_shift(2) + blob.read_shift(2);
|
||||
if(ctime !== 0) {
|
||||
o.ctime = ctime; o.ct = read_date(blob, blob.l-8);
|
||||
}
|
||||
mtime = blob.read_shift(2) + blob.read_shift(2) + blob.read_shift(2) + blob.read_shift(2);
|
||||
if(mtime !== 0) {
|
||||
o.mtime = mtime; o.mt = read_date(blob, blob.l-8);
|
||||
}
|
||||
state: blob.read_shift(4, 'i'),
|
||||
start: 0,
|
||||
size: 0
|
||||
});
|
||||
var ctime/*:number*/ = blob.read_shift(2) + blob.read_shift(2) + blob.read_shift(2) + blob.read_shift(2);
|
||||
if(ctime !== 0) o.ct = read_date(blob, blob.l-8);
|
||||
var mtime/*:number*/ = blob.read_shift(2) + blob.read_shift(2) + blob.read_shift(2) + blob.read_shift(2);
|
||||
if(mtime !== 0) o.mt = read_date(blob, blob.l-8);
|
||||
o.start = blob.read_shift(4, 'i');
|
||||
o.size = blob.read_shift(4, 'i');
|
||||
if(o.type === 5) { /* root */
|
||||
@ -337,12 +336,12 @@ function read_directory(dir_start, sector_list, sectors, Paths, nmfs, files, Fil
|
||||
o.storage = 'fat';
|
||||
if(sector_list[o.start] === undefined) sector_list[o.start] = get_sector_list(sectors, o.start, sector_list.fat_addrs, sector_list.ssz);
|
||||
sector_list[o.start].name = o.name;
|
||||
o.content = sector_list[o.start].data.slice(0,o.size);
|
||||
o.content = (sector_list[o.start].data.slice(0,o.size)/*:any*/);
|
||||
prep_blob(o.content, 0);
|
||||
} else {
|
||||
o.storage = 'minifat';
|
||||
if(minifat_store !== ENDOFCHAIN && o.start !== ENDOFCHAIN) {
|
||||
o.content = sector_list[minifat_store].data.slice(o.start*MSSZ,o.start*MSSZ+o.size);
|
||||
o.content = (sector_list[minifat_store].data.slice(o.start*MSSZ,o.start*MSSZ+o.size)/*:any*/);
|
||||
prep_blob(o.content, 0);
|
||||
}
|
||||
}
|
||||
@ -351,23 +350,23 @@ function read_directory(dir_start, sector_list, sectors, Paths, nmfs, files, Fil
|
||||
}
|
||||
}
|
||||
|
||||
function read_date(blob, offset) {
|
||||
function read_date(blob/*:RawBytes|CFBlob*/, offset/*:number*/)/*:Date*/ {
|
||||
return new Date(( ( (__readUInt32LE(blob,offset+4)/1e7)*Math.pow(2,32)+__readUInt32LE(blob,offset)/1e7 ) - 11644473600)*1000);
|
||||
}
|
||||
|
||||
var fs;
|
||||
function readFileSync(filename/*:string*/, options/*:any*/) {
|
||||
if(fs === undefined) fs = require('fs');
|
||||
var fs/*:: = require('fs'); */;
|
||||
function readFileSync(filename/*:string*/, options/*:CFBReadOpts*/) {
|
||||
if(fs == null) fs = require('fs');
|
||||
return parse(fs.readFileSync(filename), options);
|
||||
}
|
||||
|
||||
function readSync(blob/*:any*/, options/*:any*/) {
|
||||
switch(options !== undefined && options.type !== undefined ? options.type : "base64") {
|
||||
case "file": return readFileSync(blob, options);
|
||||
case "base64": return parse(s2a(Base64.decode(blob)), options);
|
||||
case "binary": return parse(s2a(blob), options);
|
||||
function readSync(blob/*:RawBytes|string*/, options/*:CFBReadOpts*/) {
|
||||
switch(options && options.type || "base64") {
|
||||
case "file": /*:: if(typeof blob !== 'string') throw "Must pass a filename when type='file'"; */return readFileSync(blob, options);
|
||||
case "base64": /*:: if(typeof blob !== 'string') throw "Must pass a base64-encoded binary string when type='file'"; */return parse(s2a(Base64.decode(blob)), options);
|
||||
case "binary": /*:: if(typeof blob !== 'string') throw "Must pass a binary string when type='file'"; */return parse(s2a(blob), options);
|
||||
}
|
||||
return parse(blob);
|
||||
return parse(/*::typeof blob == 'string' ? new Buffer(blob, 'utf-8') : */blob, options);
|
||||
}
|
||||
|
||||
/** CFB Constants */
|
||||
|
@ -26,6 +26,24 @@ The write demo:
|
||||
- generates file on client side
|
||||
- triggers a download.
|
||||
|
||||
This demo uses the FileSaver.js library for writing files, installed through the
|
||||
[`pfafman:filesaver` wrapper](https://atmospherejs.com/pfafman/filesaver):
|
||||
|
||||
```bash
|
||||
meteor add pfafman:filesaver
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
||||
This tree does not include the `.meteor` structure. Rebuild the project with:
|
||||
|
||||
```bash
|
||||
meteor create .
|
||||
npm install babel-runtime meteor-node-stubs xlsx
|
||||
meteor add pfafman:filesaver
|
||||
meteor
|
||||
```
|
||||
|
||||
|
||||
## Environment-specific features
|
||||
|
||||
|
2
dist/cpexcel.js
vendored
2
dist/cpexcel.js
vendored
@ -1,6 +1,6 @@
|
||||
/* cpexcel.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
/*jshint -W100 */
|
||||
var cptable = {version:"1.9.0"};
|
||||
var cptable = {version:"1.10.0"};
|
||||
cptable[437] = (function(){ var d = "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢£¥₧ƒáíóúñѪº¿⌐¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀αßΓπΣσµτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■ ", D = [], e = {}; for(var i=0;i!=d.length;++i) { if(d.charCodeAt(i) !== 0xFFFD) e[d.charAt(i)] = i; D[i] = d.charAt(i); } return {"enc": e, "dec": D }; })();
|
||||
cptable[620] = (function(){ var d = "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ÇüéâäàąçêëèïîćÄĄĘęłôöĆûùŚÖÜ¢Ł¥śƒŹŻóÓńŃźż¿⌐¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀αßΓπΣσµτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■ ", D = [], e = {}; for(var i=0;i!=d.length;++i) { if(d.charCodeAt(i) !== 0xFFFD) e[d.charAt(i)] = i; D[i] = d.charAt(i); } return {"enc": e, "dec": D }; })();
|
||||
cptable[737] = (function(){ var d = "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩαβγδεζηθικλμνξοπρσςτυφχψ░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀ωάέήϊίόύϋώΆΈΉΊΌΎΏ±≥≤ΪΫ÷≈°∙·√ⁿ²■ ", D = [], e = {}; for(var i=0;i!=d.length;++i) { if(d.charCodeAt(i) !== 0xFFFD) e[d.charAt(i)] = i; D[i] = d.charAt(i); } return {"enc": e, "dec": D }; })();
|
||||
|
30
dist/xlsx.core.min.js
vendored
30
dist/xlsx.core.min.js
vendored
File diff suppressed because one or more lines are too long
2
dist/xlsx.core.min.map
vendored
2
dist/xlsx.core.min.map
vendored
File diff suppressed because one or more lines are too long
32
dist/xlsx.full.min.js
vendored
32
dist/xlsx.full.min.js
vendored
File diff suppressed because one or more lines are too long
2
dist/xlsx.full.min.map
vendored
2
dist/xlsx.full.min.map
vendored
File diff suppressed because one or more lines are too long
512
dist/xlsx.js
vendored
512
dist/xlsx.js
vendored
File diff suppressed because it is too large
Load Diff
28
dist/xlsx.min.js
vendored
28
dist/xlsx.min.js
vendored
File diff suppressed because one or more lines are too long
2
dist/xlsx.min.map
vendored
2
dist/xlsx.min.map
vendored
File diff suppressed because one or more lines are too long
114
misc/flowdeps.js
114
misc/flowdeps.js
@ -12,6 +12,120 @@ declare module 'fs' { declare var exports:any; };
|
||||
|
||||
type ZIP = any;
|
||||
|
||||
type SSFTable = {[key:number|string]:string};
|
||||
type SSFDate = {
|
||||
D:number; T:number;
|
||||
y:number; m:number; d:number; q:number;
|
||||
H:number; M:number; S:number; u:number;
|
||||
};
|
||||
|
||||
type SSFModule = {
|
||||
format:(fmt:string|number, v:any, o:any)=>string;
|
||||
|
||||
is_date:(fmt:string)=>boolean;
|
||||
parse_date_code:(v:number,opts:any)=>?SSFDate;
|
||||
|
||||
load:(fmt:string, idx:?number)=>number;
|
||||
get_table:()=>SSFTable;
|
||||
load_table:(table:any)=>void;
|
||||
_table:SSFTable;
|
||||
init_table:any;
|
||||
|
||||
_general_int:(v:number)=>string;
|
||||
_general_num:(v:number)=>string;
|
||||
_general:(v:number, o:?any)=>string;
|
||||
_eval:any;
|
||||
_split:any;
|
||||
version:string;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Note: The following override is needed because ReadShift includes more cases
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
type CFBModule = {
|
||||
version:string;
|
||||
read:(blob:RawBytes|string, opts:CFBReadOpts)=>CFBContainer;
|
||||
parse:(file:RawBytes, opts:CFBReadOpts)=>CFBContainer;
|
||||
utils:CFBUtils;
|
||||
};
|
||||
|
||||
type CFBFullPathDir = {
|
||||
[n:string]: CFBEntry;
|
||||
}
|
||||
|
||||
type CFBUtils = any;
|
||||
|
||||
type CheckFieldFunc = {(hexstr:string, fld:string):void;};
|
||||
|
||||
type RawBytes = Array<number> | Buffer | Uint8Array;
|
||||
|
||||
class CFBlobArray extends Array<number> {
|
||||
l:number;
|
||||
read_shift:ReadShiftFunc;
|
||||
chk:CheckFieldFunc;
|
||||
};
|
||||
interface CFBlobBuffer extends Buffer {
|
||||
l:number;
|
||||
slice:(start:number, end:?number)=>Buffer;
|
||||
read_shift:ReadShiftFunc;
|
||||
chk:CheckFieldFunc;
|
||||
};
|
||||
interface CFBlobUint8 extends Uint8Array {
|
||||
l:number;
|
||||
slice:(start:number, end:?number)=>Uint8Array;
|
||||
read_shift:ReadShiftFunc;
|
||||
chk:CheckFieldFunc;
|
||||
};
|
||||
|
||||
interface CFBlobber {
|
||||
[n:number]:number;
|
||||
l:number;
|
||||
length:number;
|
||||
slice:(start:number, end:?number)=>RawBytes;
|
||||
read_shift:ReadShiftFunc;
|
||||
chk:CheckFieldFunc;
|
||||
};
|
||||
|
||||
type CFBlob = CFBlobArray | CFBlobBuffer | CFBlobUint8;
|
||||
|
||||
interface CFBReadOpts {
|
||||
type?:string;
|
||||
};
|
||||
|
||||
type CFBFileIndex = Array<CFBEntry>;
|
||||
|
||||
type CFBFindPath = (n:string)=>?CFBEntry;
|
||||
|
||||
type CFBContainer = {
|
||||
raw:{
|
||||
header:any;
|
||||
sectors:Array<any>;
|
||||
};
|
||||
FileIndex:CFBFileIndex;
|
||||
FullPathDir:CFBDirectory;
|
||||
FullPaths:Array<string>;
|
||||
find:CFBFindPath;
|
||||
}
|
||||
|
||||
type CFBEntry = {
|
||||
name: string;
|
||||
type: number;
|
||||
ct?: Date;
|
||||
mt?: Date;
|
||||
color: number;
|
||||
clsid: string;
|
||||
state: number;
|
||||
start: number;
|
||||
size: number;
|
||||
storage?: "fat" | "minifat";
|
||||
L: number;
|
||||
R: number;
|
||||
C: number;
|
||||
content?: CFBlob;
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Note: The following override is needed because Flow is missing Date#getYear
|
||||
// ----------------------------------------------------------------------------
|
||||
|
17
package.json
17
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xlsx",
|
||||
"version": "0.10.8",
|
||||
"version": "0.10.9",
|
||||
"author": "sheetjs",
|
||||
"description": "Excel (XLSB/XLSX/XLS/XML) ODS and other spreadsheet format (CSV/DIF/DBF/SYLK) parser and writer",
|
||||
"keywords": [ "excel", "xls", "xlsx", "xlsb", "xlsm", "ods", "csv", "dbf", "dif", "sylk", "office", "spreadsheet" ],
|
||||
@ -18,20 +18,19 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"exit-on-epipe":"~1.0.1",
|
||||
"ssf":"~0.9.4",
|
||||
"codepage":"~1.9.0",
|
||||
"cfb":"~0.11.1",
|
||||
"crc-32":"~1.0.2",
|
||||
"adler-32":"~1.0.0",
|
||||
"ssf":"~0.10.0",
|
||||
"codepage":"~1.10.1",
|
||||
"cfb":"~0.12.0",
|
||||
"crc-32":"~1.1.0",
|
||||
"adler-32":"~1.1.0",
|
||||
"commander":"~2.11.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha":"~2.5.3",
|
||||
"blanket": "~1.2.3",
|
||||
"xlsjs":"*",
|
||||
"@sheetjs/uglify-js":"*",
|
||||
"@sheetjs/uglify-js":"~2.7.3",
|
||||
"@types/node":"^8.0.7",
|
||||
"@types/commander":"*",
|
||||
"@types/commander":"^2.9.0",
|
||||
"dtslint": "^0.1.2",
|
||||
"typescript": "2.2.0"
|
||||
},
|
||||
|
329
xlsx.flow.js
329
xlsx.flow.js
@ -6,7 +6,7 @@
|
||||
/*global global, exports, module, require:false, process:false, Buffer:false */
|
||||
var XLSX = {};
|
||||
(function make_xlsx(XLSX){
|
||||
XLSX.version = '0.10.8';
|
||||
XLSX.version = '0.10.9';
|
||||
var current_codepage = 1200;
|
||||
/*:: declare var cptable:any; */
|
||||
/*global cptable:true */
|
||||
@ -130,9 +130,9 @@ type WriteObjStrFactory = {from_sheet(ws:Worksheet, o:any, wb:?Workbook):string}
|
||||
*/
|
||||
/* ssf.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
/*jshint -W041 */
|
||||
var SSF = {};
|
||||
var make_ssf = function make_ssf(SSF){
|
||||
SSF.version = '0.9.4';
|
||||
var SSF/*:SSFModule*/ = ({}/*:any*/);
|
||||
var make_ssf = function make_ssf(SSF/*:SSFModule*/){
|
||||
SSF.version = '0.10.0';
|
||||
function _strrev(x/*:string*/)/*:string*/ { var o = "", i = x.length-1; while(i>=0) o += x.charAt(i--); return o; }
|
||||
function fill(c/*:string*/,l/*:number*/)/*:string*/ { var o = ""; while(o.length < l) o+=c; return o; }
|
||||
function pad0(v/*:any*/,d/*:number*/)/*:string*/{var t=""+v; return t.length>=d?t:fill('0',d-t.length)+t;}
|
||||
@ -143,16 +143,9 @@ function pad0r2(v/*:any*/,d/*:number*/)/*:string*/{var t=""+v; return t.length>=
|
||||
var p2_32 = Math.pow(2,32);
|
||||
function pad0r(v/*:any*/,d/*:number*/)/*:string*/{if(v>p2_32||v<-p2_32) return pad0r1(v,d); var i = Math.round(v); return pad0r2(i,d); }
|
||||
function isgeneral(s/*:string*/, i/*:?number*/)/*:boolean*/ { i = i || 0; return s.length >= 7 + i && (s.charCodeAt(i)|32) === 103 && (s.charCodeAt(i+1)|32) === 101 && (s.charCodeAt(i+2)|32) === 110 && (s.charCodeAt(i+3)|32) === 101 && (s.charCodeAt(i+4)|32) === 114 && (s.charCodeAt(i+5)|32) === 97 && (s.charCodeAt(i+6)|32) === 108; }
|
||||
/* Options */
|
||||
var opts_fmt/*:Array<Array<any> >*/ = [
|
||||
["date1904", 0],
|
||||
["output", ""],
|
||||
["WTF", false]
|
||||
];
|
||||
function fixopts(o){
|
||||
for(var y = 0; y != opts_fmt.length; ++y) if(o[opts_fmt[y][0]]===undefined) o[opts_fmt[y][0]]=opts_fmt[y][1];
|
||||
}
|
||||
SSF.opts = opts_fmt;
|
||||
/*::
|
||||
type SSF_write_num = {(type:string, fmt:string, val:number):string};
|
||||
*/
|
||||
var days/*:Array<Array<string> >*/ = [
|
||||
['Sun', 'Sunday'],
|
||||
['Mon', 'Monday'],
|
||||
@ -231,7 +224,47 @@ function frac(x/*:number*/, D/*:number*/, mixed/*:?boolean*/)/*:Array<number>*/
|
||||
var q = Math.floor(sgn * P/Q);
|
||||
return [q, sgn*P - q*Q, Q];
|
||||
}
|
||||
function general_fmt_int(v/*:number*/)/*:string*/ { return ""+v; }
|
||||
function parse_date_code(v/*:number*/,opts/*:?any*/,b2/*:?boolean*/) {
|
||||
if(v > 2958465 || v < 0) return null;
|
||||
var date = (v|0), time = Math.floor(86400 * (v - date)), dow=0;
|
||||
var dout=[];
|
||||
var out={D:date, T:time, u:86400*(v-date)-time,y:0,m:0,d:0,H:0,M:0,S:0,q:0};
|
||||
if(Math.abs(out.u) < 1e-6) out.u = 0;
|
||||
if(opts && opts.date1904) date += 1462;
|
||||
if(out.u > 0.9999) {
|
||||
out.u = 0;
|
||||
if(++time == 86400) { out.T = time = 0; ++date; ++out.D; }
|
||||
}
|
||||
if(date === 60) {dout = b2 ? [1317,10,29] : [1900,2,29]; dow=3;}
|
||||
else if(date === 0) {dout = b2 ? [1317,8,29] : [1900,1,0]; dow=6;}
|
||||
else {
|
||||
if(date > 60) --date;
|
||||
/* 1 = Jan 1 1900 in Gregorian */
|
||||
var d = new Date(1900, 0, 1);
|
||||
d.setDate(d.getDate() + date - 1);
|
||||
dout = [d.getFullYear(), d.getMonth()+1,d.getDate()];
|
||||
dow = d.getDay();
|
||||
if(date < 60) dow = (dow + 6) % 7;
|
||||
if(b2) dow = fix_hijri(d, dout);
|
||||
}
|
||||
out.y = dout[0]; out.m = dout[1]; out.d = dout[2];
|
||||
out.S = time % 60; time = Math.floor(time / 60);
|
||||
out.M = time % 60; time = Math.floor(time / 60);
|
||||
out.H = time;
|
||||
out.q = dow;
|
||||
return out;
|
||||
}
|
||||
SSF.parse_date_code = parse_date_code;
|
||||
var basedate = new Date(1899, 11, 31, 0, 0, 0);
|
||||
var dnthresh = basedate.getTime();
|
||||
var base1904 = new Date(1900, 2, 1, 0, 0, 0);
|
||||
function datenum_local(v/*:Date*/, date1904/*:?boolean*/)/*:number*/ {
|
||||
var epoch = v.getTime();
|
||||
if(date1904) epoch -= 1461*24*60*60*1000;
|
||||
else if(v >= base1904) epoch += 24*60*60*1000;
|
||||
return (epoch - (dnthresh + (v.getTimezoneOffset() - basedate.getTimezoneOffset()) * 60000)) / (24 * 60 * 60 * 1000);
|
||||
}
|
||||
function general_fmt_int(v/*:number*/)/*:string*/ { return v.toString(10); }
|
||||
SSF._general_int = general_fmt_int;
|
||||
var general_fmt_num = (function make_general_fmt_num() {
|
||||
var gnr1 = /\.(\d*[1-9])0+$/, gnr2 = /\.0*$/, gnr4 = /\.(\d*[1-9])0+/, gnr5 = /\.0*[Ee]/, gnr6 = /(E[+-])(\d)$/;
|
||||
@ -262,50 +295,20 @@ return function general_fmt_num(v/*:number*/)/*:string*/ {
|
||||
return gfn5(gfn4(o));
|
||||
};})();
|
||||
SSF._general_num = general_fmt_num;
|
||||
function general_fmt(v/*:any*/) {
|
||||
function general_fmt(v/*:any*/, opts/*:any*/) {
|
||||
switch(typeof v) {
|
||||
case 'string': return v;
|
||||
case 'boolean': return v ? "TRUE" : "FALSE";
|
||||
case 'number': return (v|0) === v ? general_fmt_int(v/*, opts*/) : general_fmt_num(v/*, opts*/);
|
||||
case 'number': return (v|0) === v ? general_fmt_int(v) : general_fmt_num(v);
|
||||
case 'undefined': return "";
|
||||
case 'object': if(v == null) return "";
|
||||
case 'object':
|
||||
if(v == null) return "";
|
||||
if(v instanceof Date) return format(14, datenum_local(v, opts && opts.date1904), opts);
|
||||
}
|
||||
throw new Error("unsupported value in General format: " + v);
|
||||
}
|
||||
SSF._general = general_fmt;
|
||||
function fix_hijri(/*date, o*/) { return 0; }
|
||||
function parse_date_code(v/*:number*/,opts/*:?any*/,b2/*:?boolean*/) {
|
||||
if(v > 2958465 || v < 0) return null;
|
||||
var date = (v|0), time = Math.floor(86400 * (v - date)), dow=0;
|
||||
var dout=[];
|
||||
var out={D:date, T:time, u:86400*(v-date)-time,y:0,m:0,d:0,H:0,M:0,S:0,q:0};
|
||||
if(Math.abs(out.u) < 1e-6) out.u = 0;
|
||||
fixopts(opts != null ? opts : (opts=[]));
|
||||
if(opts.date1904) date += 1462;
|
||||
if(out.u > 0.9999) {
|
||||
out.u = 0;
|
||||
if(++time == 86400) { out.T = time = 0; ++date; ++out.D; }
|
||||
}
|
||||
if(date === 60) {dout = b2 ? [1317,10,29] : [1900,2,29]; dow=3;}
|
||||
else if(date === 0) {dout = b2 ? [1317,8,29] : [1900,1,0]; dow=6;}
|
||||
else {
|
||||
if(date > 60) --date;
|
||||
/* 1 = Jan 1 1900 in Gregorian */
|
||||
var d = new Date(1900, 0, 1);
|
||||
d.setDate(d.getDate() + date - 1);
|
||||
dout = [d.getFullYear(), d.getMonth()+1,d.getDate()];
|
||||
dow = d.getDay();
|
||||
if(date < 60) dow = (dow + 6) % 7;
|
||||
if(b2) dow = fix_hijri(d, dout);
|
||||
}
|
||||
out.y = dout[0]; out.m = dout[1]; out.d = dout[2];
|
||||
out.S = time % 60; time = Math.floor(time / 60);
|
||||
out.M = time % 60; time = Math.floor(time / 60);
|
||||
out.H = time;
|
||||
out.q = dow;
|
||||
return out;
|
||||
}
|
||||
SSF.parse_date_code = parse_date_code;
|
||||
function fix_hijri(/*::date, o*/) { return 0; }
|
||||
/*jshint -W086 */
|
||||
function write_date(type/*:number*/, fmt/*:string*/, val, ss0/*:?number*/)/*:string*/ {
|
||||
var o="", ss=0, tt=0, y = val.y, out, outl = 0;
|
||||
@ -347,23 +350,17 @@ function write_date(type/*:number*/, fmt/*:string*/, val, ss0/*:?number*/)/*:str
|
||||
default: throw 'bad minute format: ' + fmt;
|
||||
} break;
|
||||
case 115: /* 's' seconds */
|
||||
if(val.u === 0) switch(fmt) {
|
||||
case 's': case 'ss': return pad0(val.S, fmt.length);
|
||||
case '.0': case '.00': case '.000':
|
||||
}
|
||||
switch(fmt) {
|
||||
case 's': case 'ss': case '.0': case '.00': case '.000':
|
||||
/*::if(!ss0) ss0 = 0; */
|
||||
if(ss0 >= 2) tt = ss0 === 3 ? 1000 : 100;
|
||||
else tt = ss0 === 1 ? 10 : 1;
|
||||
ss = Math.round((tt)*(val.S + val.u));
|
||||
if(ss >= 60*tt) ss = 0;
|
||||
if(fmt === 's') return ss === 0 ? "0" : ""+ss/tt;
|
||||
o = pad0(ss,2 + ss0);
|
||||
if(fmt === 'ss') return o.substr(0,2);
|
||||
return "." + o.substr(2,fmt.length-1);
|
||||
default: throw 'bad second format: ' + fmt;
|
||||
}
|
||||
if(fmt != 's' && fmt != 'ss' && fmt != '.0' && fmt != '.00' && fmt != '.000') throw 'bad second format: ' + fmt;
|
||||
if(val.u === 0 && (fmt == "s" || fmt == "ss")) return pad0(val.S, fmt.length);
|
||||
/*::if(!ss0) ss0 = 0; */
|
||||
if(ss0 >= 2) tt = ss0 === 3 ? 1000 : 100;
|
||||
else tt = ss0 === 1 ? 10 : 1;
|
||||
ss = Math.round((tt)*(val.S + val.u));
|
||||
if(ss >= 60*tt) ss = 0;
|
||||
if(fmt === 's') return ss === 0 ? "0" : ""+ss/tt;
|
||||
o = pad0(ss,2 + ss0);
|
||||
if(fmt === 'ss') return o.substr(0,2);
|
||||
return "." + o.substr(2,fmt.length-1);
|
||||
case 90: /* 'Z' absolute time */
|
||||
switch(fmt) {
|
||||
case '[h]': case '[hh]': out = val.D*24+val.H; break;
|
||||
@ -378,12 +375,13 @@ function write_date(type/*:number*/, fmt/*:string*/, val, ss0/*:?number*/)/*:str
|
||||
}
|
||||
/*jshint +W086 */
|
||||
function commaify(s/*:string*/)/*:string*/ {
|
||||
if(s.length <= 3) return s;
|
||||
var j = (s.length % 3), o = s.substr(0,j);
|
||||
for(; j!=s.length; j+=3) o+=(o.length > 0 ? "," : "") + s.substr(j,3);
|
||||
var w = 3;
|
||||
if(s.length <= w) return s;
|
||||
var j = (s.length % w), o = s.substr(0,j);
|
||||
for(; j!=s.length; j+=w) o+=(o.length > 0 ? "," : "") + s.substr(j,w);
|
||||
return o;
|
||||
}
|
||||
var write_num = (function make_write_num(){
|
||||
var write_num/*:SSF_write_num*/ = (function make_write_num(){
|
||||
var pct1 = /%/g;
|
||||
function write_num_pct(type/*:string*/, fmt/*:string*/, val/*:number*/)/*:string*/{
|
||||
var sfmt = fmt.replace(pct1,""), mul = fmt.length - sfmt.length;
|
||||
@ -721,7 +719,7 @@ function fmt_is_date(fmt/*:string*/)/*:boolean*/ {
|
||||
}
|
||||
SSF.is_date = fmt_is_date;
|
||||
function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) {
|
||||
var out = [], o = "", i = 0, c = "", lst='t', q, dt, j, cc;
|
||||
var out = [], o = "", i = 0, c = "", lst='t', dt, j, cc;
|
||||
var hr='H';
|
||||
/* Tokenize */
|
||||
while(i < fmt.length) {
|
||||
@ -749,12 +747,12 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) {
|
||||
case 'm': case 'd': case 'y': case 'h': case 's': case 'e': case 'g':
|
||||
if(v < 0) return "";
|
||||
if(dt==null) { dt=parse_date_code(v, opts); if(dt==null) return ""; }
|
||||
o = c; while(++i<fmt.length && fmt.charAt(i).toLowerCase() === c) o+=c;
|
||||
o = c; while(++i < fmt.length && fmt.charAt(i).toLowerCase() === c) o+=c;
|
||||
if(c === 'm' && lst.toLowerCase() === 'h') c = 'M';
|
||||
if(c === 'h') c = hr;
|
||||
out[out.length] = {t:c, v:o}; lst = c; break;
|
||||
case 'A': case 'a':
|
||||
q={t:c, v:c};
|
||||
var q={t:c, v:c};
|
||||
if(dt==null) dt=parse_date_code(v, opts);
|
||||
if(fmt.substr(i, 3).toUpperCase() === "A/P") { if(dt!=null) q.v = dt.H >= 12 ? "P" : "A"; q.t = 'T'; hr='h';i+=3;}
|
||||
else if(fmt.substr(i,5).toUpperCase() === "AM/PM") { if(dt!=null) q.v = dt.H >= 12 ? "PM" : "AM"; q.t = 'T'; i+=5; hr='h'; }
|
||||
@ -777,7 +775,7 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) {
|
||||
/* Numbers */
|
||||
case '.':
|
||||
if(dt != null) {
|
||||
o = c; while((c=fmt.charAt(++i)) === "0") o += c;
|
||||
o = c; while(++i < fmt.length && (c=fmt.charAt(i)) === "0") o += c;
|
||||
out[out.length] = {t:'s', v:o}; break;
|
||||
}
|
||||
/* falls through */
|
||||
@ -786,7 +784,7 @@ function eval_fmt(fmt/*:string*/, v/*:any*/, opts/*:any*/, flen/*:number*/) {
|
||||
out[out.length] = {t:'n', v:o}; break;
|
||||
case '?':
|
||||
o = c; while(fmt.charAt(++i) === c) o+=c;
|
||||
q={t:c, v:o}; out[out.length] = q; lst = c; break;
|
||||
out[out.length] = {t:c, v:o}; lst = c; break;
|
||||
case '*': ++i; if(fmt.charAt(i) == ' ' || fmt.charAt(i) == '*') ++i; break; // **
|
||||
case '(': case ')': out[out.length] = {t:(flen===1?'t':c), v:c}; ++i; break;
|
||||
case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
|
||||
@ -937,7 +935,7 @@ function chkcond(v, rr) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function choose_fmt(f/*:string*/, v) {
|
||||
function choose_fmt(f/*:string*/, v/*:any*/) {
|
||||
var fmt = split_fmt(f);
|
||||
var l = fmt.length, lat = fmt[l-1].indexOf("@");
|
||||
if(l<4 && lat>-1) --l;
|
||||
@ -972,18 +970,38 @@ function format(fmt/*:string|number*/,v/*:any*/,o/*:?any*/) {
|
||||
break;
|
||||
}
|
||||
if(isgeneral(sfmt,0)) return general_fmt(v, o);
|
||||
if(v instanceof Date) v = datenum_local(v, o.date1904);
|
||||
var f = choose_fmt(sfmt, v);
|
||||
if(isgeneral(f[1])) return general_fmt(v, o);
|
||||
if(v === true) v = "TRUE"; else if(v === false) v = "FALSE";
|
||||
else if(v === "" || v == null) return "";
|
||||
return eval_fmt(f[1], v, o, f[0]);
|
||||
}
|
||||
function load_entry(fmt/*:string*/, idx/*:?number*/)/*:number*/ {
|
||||
if(typeof idx != 'number') {
|
||||
idx = +idx || -1;
|
||||
/*::if(typeof idx != 'number') return 0x188; */
|
||||
for(var i = 0; i < 0x0188; ++i) {
|
||||
/*::if(typeof idx != 'number') return 0x188; */
|
||||
if(table_fmt[i] == undefined) { if(idx < 0) idx = i; continue; }
|
||||
if(table_fmt[i] == fmt) { idx = i; break; }
|
||||
}
|
||||
/*::if(typeof idx != 'number') return 0x188; */
|
||||
if(idx < 0) idx = 0x187;
|
||||
}
|
||||
/*::if(typeof idx != 'number') return 0x188; */
|
||||
table_fmt[idx] = fmt;
|
||||
return idx;
|
||||
}
|
||||
SSF.load = load_entry;
|
||||
SSF._table = table_fmt;
|
||||
SSF.load = function load_entry(fmt/*:string*/, idx/*:number*/) { table_fmt[idx] = fmt; };
|
||||
SSF.format = format;
|
||||
SSF.get_table = function get_table() { return table_fmt; };
|
||||
SSF.load_table = function load_table(tbl/*:{[n:number]:string}*/) { for(var i=0; i!=0x0188; ++i) if(tbl[i] !== undefined) SSF.load(tbl[i], i); };
|
||||
SSF.get_table = function get_table()/*:SSFTable*/ { return table_fmt; };
|
||||
SSF.load_table = function load_table(tbl/*:SSFTable*/)/*:void*/ {
|
||||
for(var i=0; i!=0x0188; ++i)
|
||||
if(tbl[i] !== undefined) load_entry(tbl[i], i);
|
||||
};
|
||||
SSF.init_table = init_table;
|
||||
SSF.format = format;
|
||||
};
|
||||
make_ssf(SSF);
|
||||
/* map from xlml named formats to SSF TODO: localize */
|
||||
@ -1053,20 +1071,25 @@ declare var chr1:any;
|
||||
/*jshint eqnull:true */
|
||||
|
||||
/*::
|
||||
declare var DO_NOT_EXPORT_CFB:any;
|
||||
type SectorEntry = any;
|
||||
declare var DO_NOT_EXPORT_CFB:?boolean;
|
||||
type SectorEntry = {
|
||||
name?:string;
|
||||
nodes?:Array<number>;
|
||||
data:RawBytes;
|
||||
};
|
||||
type SectorList = {
|
||||
(k:string|number):SectorEntry;
|
||||
[k:string|number]:SectorEntry;
|
||||
name:?string;
|
||||
fat_addrs:any;
|
||||
fat_addrs:Array<number>;
|
||||
ssz:number;
|
||||
}
|
||||
type CFBFiles = {[n:string]:CFBEntry};
|
||||
*/
|
||||
/* [MS-CFB] v20130118 */
|
||||
var CFB = (function _CFB(){
|
||||
var exports = {};
|
||||
exports.version = '0.11.1';
|
||||
function parse(file) {
|
||||
var exports/*:CFBModule*/ = /*::(*/{}/*:: :any)*/;
|
||||
exports.version = '0.12.0';
|
||||
function parse(file/*:RawBytes*/, options/*:CFBReadOpts*/)/*:CFBContainer*/ {
|
||||
var mver = 3; // major version
|
||||
var ssz = 512; // sector size
|
||||
var nmfs = 0; // number of mini FAT sectors
|
||||
@ -1075,10 +1098,10 @@ var dir_start = 0; // first directory sector location
|
||||
var minifat_start = 0; // first mini FAT sector location
|
||||
var difat_start = 0; // first mini FAT sector location
|
||||
|
||||
var fat_addrs = []; // locations of FAT sectors
|
||||
var fat_addrs/*:Array<number>*/ = []; // locations of FAT sectors
|
||||
|
||||
/* [MS-CFB] 2.2 Compound File Header */
|
||||
var blob/*:any*/ = file.slice(0,512);
|
||||
var blob/*:CFBlob*/ = /*::(*/file.slice(0,512)/*:: :any)*/;
|
||||
prep_blob(blob, 0);
|
||||
|
||||
/* major version */
|
||||
@ -1090,14 +1113,14 @@ switch(mver) {
|
||||
}
|
||||
|
||||
/* reprocess header */
|
||||
if(ssz !== 512) { blob = file.slice(0,ssz); prep_blob(blob, 28 /* blob.l */); }
|
||||
if(ssz !== 512) { blob = /*::(*/file.slice(0,ssz)/*:: :any)*/; prep_blob(blob, 28 /* blob.l */); }
|
||||
/* Save header for final object */
|
||||
var header = file.slice(0,ssz);
|
||||
var header/*:RawBytes*/ = file.slice(0,ssz);
|
||||
|
||||
check_shifts(blob, mver);
|
||||
|
||||
// Number of Directory Sectors
|
||||
var nds = blob.read_shift(4, 'i');
|
||||
var nds/*:number*/ = blob.read_shift(4, 'i');
|
||||
if(mver === 3 && nds !== 0) throw new Error('# Directory Sectors: Expected 0 saw ' + nds);
|
||||
|
||||
// Number of FAT Sectors
|
||||
@ -1126,14 +1149,14 @@ difat_start = blob.read_shift(4, 'i');
|
||||
ndfs = blob.read_shift(4, 'i');
|
||||
|
||||
// Grab FAT Sector Locations
|
||||
for(var q, j = 0; j < 109; ++j) { /* 109 = (512 - blob.l)>>>2; */
|
||||
for(var q = -1, j = 0; j < 109; ++j) { /* 109 = (512 - blob.l)>>>2; */
|
||||
q = blob.read_shift(4, 'i');
|
||||
if(q<0) break;
|
||||
fat_addrs[j] = q;
|
||||
}
|
||||
|
||||
/** Break the file up into sectors */
|
||||
var sectors = sectorify(file, ssz);
|
||||
var sectors/*:Array<RawBytes>*/ = sectorify(file, ssz);
|
||||
|
||||
sleuth_fat(difat_start, ndfs, sectors, ssz, fat_addrs);
|
||||
|
||||
@ -1147,13 +1170,12 @@ sector_list.fat_addrs = fat_addrs;
|
||||
sector_list.ssz = ssz;
|
||||
|
||||
/* [MS-CFB] 2.6.1 Compound File Directory Entry */
|
||||
var files = {}, Paths/*:any*/ = [], FileIndex = [], FullPaths = [], FullPathDir = {};
|
||||
var files/*:CFBFiles*/ = {}, Paths/*:Array<string>*/ = [], FileIndex/*:CFBFileIndex*/ = [], FullPaths/*:Array<string>*/ = [], FullPathDir = {};
|
||||
read_directory(dir_start, sector_list, sectors, Paths, nmfs, files, FileIndex);
|
||||
|
||||
build_full_paths(FileIndex, FullPathDir, FullPaths, Paths);
|
||||
|
||||
var root_name = Paths.shift();
|
||||
Paths.root = root_name;
|
||||
var root_name/*:string*/ = Paths.shift();
|
||||
|
||||
/* [MS-CFB] 2.6.4 (Unicode 3.0.1 case conversion) */
|
||||
var find_path = make_find_path(FullPaths, Paths, FileIndex, files, root_name);
|
||||
@ -1168,7 +1190,7 @@ return {
|
||||
} // parse
|
||||
|
||||
/* [MS-CFB] 2.2 Compound File Header -- read up to major version */
|
||||
function check_get_mver(blob) {
|
||||
function check_get_mver(blob/*:CFBlob*/)/*:[number, number]*/ {
|
||||
// header signature 8
|
||||
blob.chk(HEADER_SIGNATURE, 'Header Signature: ');
|
||||
|
||||
@ -1176,11 +1198,11 @@ function check_get_mver(blob) {
|
||||
blob.chk(HEADER_CLSID, 'CLSID: ');
|
||||
|
||||
// minor version 2
|
||||
var mver = blob.read_shift(2, 'u');
|
||||
var mver/*:number*/ = blob.read_shift(2, 'u');
|
||||
|
||||
return [blob.read_shift(2,'u'), mver];
|
||||
}
|
||||
function check_shifts(blob, mver) {
|
||||
function check_shifts(blob/*:CFBlob*/, mver/*:number*/)/*:void*/ {
|
||||
var shift = 0x09;
|
||||
|
||||
// Byte Order
|
||||
@ -1202,18 +1224,18 @@ function check_shifts(blob, mver) {
|
||||
}
|
||||
|
||||
/** Break the file up into sectors */
|
||||
function sectorify(file, ssz) {
|
||||
function sectorify(file/*:RawBytes*/, ssz/*:number*/)/*:Array<RawBytes>*/ {
|
||||
var nsectors = Math.ceil(file.length/ssz)-1;
|
||||
var sectors = new Array(nsectors);
|
||||
var sectors/*:Array<RawBytes>*/ = [];
|
||||
for(var i=1; i < nsectors; ++i) sectors[i-1] = file.slice(i*ssz,(i+1)*ssz);
|
||||
sectors[nsectors-1] = file.slice(nsectors*ssz);
|
||||
return sectors;
|
||||
}
|
||||
|
||||
/* [MS-CFB] 2.6.4 Red-Black Tree */
|
||||
function build_full_paths(FI, FPD, FP, Paths) {
|
||||
function build_full_paths(FI/*:CFBFileIndex*/, FPD/*:CFBFullPathDir*/, FP/*:Array<string>*/, Paths/*:Array<string>*/)/*:void*/ {
|
||||
var i = 0, L = 0, R = 0, C = 0, j = 0, pl = Paths.length;
|
||||
var dad = new Array(pl), q = new Array(pl);
|
||||
var dad/*:Array<number>*/ = [], q/*:Array<number>*/ = [];
|
||||
|
||||
for(; i < pl; ++i) { dad[i]=q[i]=i; FP[i]=Paths[i]; }
|
||||
|
||||
@ -1252,17 +1274,17 @@ function build_full_paths(FI, FPD, FP, Paths) {
|
||||
}
|
||||
|
||||
/* [MS-CFB] 2.6.4 */
|
||||
function make_find_path(FullPaths, Paths, FileIndex, files, root_name) {
|
||||
var UCFullPaths = new Array(FullPaths.length);
|
||||
var UCPaths = new Array(Paths.length), i;
|
||||
function make_find_path(FullPaths/*:Array<string>*/, Paths/*:Array<string>*/, FileIndex/*:CFBFileIndex*/, files/*:CFBFiles*/, root_name/*:string*/)/*:CFBFindPath*/ {
|
||||
var UCFullPaths/*:Array<string>*/ = [];
|
||||
var UCPaths/*:Array<string>*/ = [], i = 0;
|
||||
for(i = 0; i < FullPaths.length; ++i) UCFullPaths[i] = FullPaths[i].toUpperCase().replace(chr0,'').replace(chr1,'!');
|
||||
for(i = 0; i < Paths.length; ++i) UCPaths[i] = Paths[i].toUpperCase().replace(chr0,'').replace(chr1,'!');
|
||||
return function find_path(path/*:string*/) {
|
||||
var k;
|
||||
return function find_path(path/*:string*/)/*:?CFBEntry*/ {
|
||||
var k/*:boolean*/ = false;
|
||||
if(path.charCodeAt(0) === 47 /* "/" */) { k=true; path = root_name + path; }
|
||||
else k = path.indexOf("/") !== -1;
|
||||
var UCPath = path.toUpperCase().replace(chr0,'').replace(chr1,'!');
|
||||
var w = k === true ? UCFullPaths.indexOf(UCPath) : UCPaths.indexOf(UCPath);
|
||||
var UCPath/*:string*/ = path.toUpperCase().replace(chr0,'').replace(chr1,'!');
|
||||
var w/*:number*/ = k === true ? UCFullPaths.indexOf(UCPath) : UCPaths.indexOf(UCPath);
|
||||
if(w === -1) return null;
|
||||
return k === true ? FileIndex[w] : files[Paths[w]];
|
||||
};
|
||||
@ -1270,8 +1292,8 @@ function make_find_path(FullPaths, Paths, FileIndex, files, root_name) {
|
||||
|
||||
/** Chase down the rest of the DIFAT chain to build a comprehensive list
|
||||
DIFAT chains by storing the next sector number as the last 32 bytes */
|
||||
function sleuth_fat(idx, cnt, sectors, ssz, fat_addrs) {
|
||||
var q;
|
||||
function sleuth_fat(idx/*:number*/, cnt/*:number*/, sectors/*:Array<RawBytes>*/, ssz/*:number*/, fat_addrs)/*:void*/ {
|
||||
var q/*:number*/ = ENDOFCHAIN;
|
||||
if(idx === ENDOFCHAIN) {
|
||||
if(cnt !== 0) throw new Error("DIFAT chain shorter than expected");
|
||||
} else if(idx !== -1 /*FREESECT*/) {
|
||||
@ -1286,13 +1308,11 @@ function sleuth_fat(idx, cnt, sectors, ssz, fat_addrs) {
|
||||
}
|
||||
|
||||
/** Follow the linked list of sectors for a given starting point */
|
||||
function get_sector_list(sectors, start, fat_addrs, ssz, chkd) {
|
||||
function get_sector_list(sectors/*:Array<RawBytes>*/, start/*:number*/, fat_addrs/*:Array<number>*/, ssz/*:number*/, chkd/*:?Array<boolean>*/)/*:SectorEntry*/ {
|
||||
var sl = sectors.length;
|
||||
var buf, buf_chain;
|
||||
if(!chkd) chkd = new Array(sl);
|
||||
var modulus = ssz - 1, j, jj;
|
||||
buf = [];
|
||||
buf_chain = [];
|
||||
var buf/*:Array<number>*/ = [], buf_chain/*:Array<any>*/ = [];
|
||||
if(!chkd) chkd = [];
|
||||
var modulus = ssz - 1, j = 0, jj = 0;
|
||||
for(j=start; j>=0;) {
|
||||
chkd[j] = true;
|
||||
buf[buf.length] = j;
|
||||
@ -1307,44 +1327,43 @@ function get_sector_list(sectors, start, fat_addrs, ssz, chkd) {
|
||||
}
|
||||
|
||||
/** Chase down the sector linked lists */
|
||||
function make_sector_list(sectors, dir_start, fat_addrs, ssz/*:number*/)/*:any*/ {
|
||||
var sl = sectors.length, sector_list = new Array(sl);
|
||||
var chkd = new Array(sl), buf, buf_chain;
|
||||
var modulus = ssz - 1, i, j, k, jj;
|
||||
function make_sector_list(sectors/*:Array<RawBytes>*/, dir_start/*:number*/, fat_addrs/*:Array<number>*/, ssz/*:number*/)/*:SectorList*/ {
|
||||
var sl = sectors.length, sector_list/*:SectorList*/ = ([]/*:any*/);
|
||||
var chkd/*:Array<boolean>*/ = [], buf/*:Array<number>*/ = [], buf_chain/*:Array<RawBytes>*/ = [];
|
||||
var modulus = ssz - 1, i=0, j=0, k=0, jj=0;
|
||||
for(i=0; i < sl; ++i) {
|
||||
buf = [];
|
||||
buf = ([]/*:Array<number>*/);
|
||||
k = (i + dir_start); if(k >= sl) k-=sl;
|
||||
if(chkd[k] === true) continue;
|
||||
if(chkd[k]) continue;
|
||||
buf_chain = [];
|
||||
for(j=k; j>=0;) {
|
||||
chkd[j] = true;
|
||||
buf[buf.length] = j;
|
||||
buf_chain.push(sectors[j]);
|
||||
var addr = fat_addrs[Math.floor(j*4/ssz)];
|
||||
var addr/*:number*/ = fat_addrs[Math.floor(j*4/ssz)];
|
||||
jj = ((j*4) & modulus);
|
||||
if(ssz < 4 + jj) throw new Error("FAT boundary crossed: " + j + " 4 "+ssz);
|
||||
if(!sectors[addr]) break;
|
||||
j = __readInt32LE(sectors[addr], jj);
|
||||
}
|
||||
sector_list[k] = {nodes: buf, data:__toBuffer([buf_chain])};
|
||||
sector_list[k] = ({nodes: buf, data:__toBuffer([buf_chain])}/*:SectorEntry*/);
|
||||
}
|
||||
return sector_list;
|
||||
}
|
||||
|
||||
/* [MS-CFB] 2.6.1 Compound File Directory Entry */
|
||||
function read_directory(dir_start, sector_list, sectors, Paths, nmfs, files, FileIndex) {
|
||||
var blob;
|
||||
function read_directory(dir_start/*:number*/, sector_list/*:SectorList*/, sectors/*:Array<RawBytes>*/, Paths/*:Array<string>*/, nmfs, files, FileIndex) {
|
||||
var minifat_store = 0, pl = (Paths.length?2:0);
|
||||
var sector = sector_list[dir_start].data;
|
||||
var i = 0, namelen = 0, name, o, ctime, mtime;
|
||||
var i = 0, namelen = 0, name;
|
||||
for(; i < sector.length; i+= 128) {
|
||||
blob = sector.slice(i, i+128);
|
||||
var blob/*:CFBlob*/ = /*::(*/sector.slice(i, i+128)/*:: :any)*/;
|
||||
prep_blob(blob, 64);
|
||||
namelen = blob.read_shift(2);
|
||||
if(namelen === 0) continue;
|
||||
name = __utf16le(blob,0,namelen-pl);
|
||||
Paths.push(name);
|
||||
o = ({
|
||||
var o/*:CFBEntry*/ = ({
|
||||
name: name,
|
||||
type: blob.read_shift(1),
|
||||
color: blob.read_shift(1),
|
||||
@ -1352,16 +1371,14 @@ function read_directory(dir_start, sector_list, sectors, Paths, nmfs, files, Fil
|
||||
R: blob.read_shift(4, 'i'),
|
||||
C: blob.read_shift(4, 'i'),
|
||||
clsid: blob.read_shift(16),
|
||||
state: blob.read_shift(4, 'i')
|
||||
}/*:any*/);
|
||||
ctime = blob.read_shift(2) + blob.read_shift(2) + blob.read_shift(2) + blob.read_shift(2);
|
||||
if(ctime !== 0) {
|
||||
o.ctime = ctime; o.ct = read_date(blob, blob.l-8);
|
||||
}
|
||||
mtime = blob.read_shift(2) + blob.read_shift(2) + blob.read_shift(2) + blob.read_shift(2);
|
||||
if(mtime !== 0) {
|
||||
o.mtime = mtime; o.mt = read_date(blob, blob.l-8);
|
||||
}
|
||||
state: blob.read_shift(4, 'i'),
|
||||
start: 0,
|
||||
size: 0
|
||||
});
|
||||
var ctime/*:number*/ = blob.read_shift(2) + blob.read_shift(2) + blob.read_shift(2) + blob.read_shift(2);
|
||||
if(ctime !== 0) o.ct = read_date(blob, blob.l-8);
|
||||
var mtime/*:number*/ = blob.read_shift(2) + blob.read_shift(2) + blob.read_shift(2) + blob.read_shift(2);
|
||||
if(mtime !== 0) o.mt = read_date(blob, blob.l-8);
|
||||
o.start = blob.read_shift(4, 'i');
|
||||
o.size = blob.read_shift(4, 'i');
|
||||
if(o.type === 5) { /* root */
|
||||
@ -1372,12 +1389,12 @@ function read_directory(dir_start, sector_list, sectors, Paths, nmfs, files, Fil
|
||||
o.storage = 'fat';
|
||||
if(sector_list[o.start] === undefined) sector_list[o.start] = get_sector_list(sectors, o.start, sector_list.fat_addrs, sector_list.ssz);
|
||||
sector_list[o.start].name = o.name;
|
||||
o.content = sector_list[o.start].data.slice(0,o.size);
|
||||
o.content = (sector_list[o.start].data.slice(0,o.size)/*:any*/);
|
||||
prep_blob(o.content, 0);
|
||||
} else {
|
||||
o.storage = 'minifat';
|
||||
if(minifat_store !== ENDOFCHAIN && o.start !== ENDOFCHAIN) {
|
||||
o.content = sector_list[minifat_store].data.slice(o.start*MSSZ,o.start*MSSZ+o.size);
|
||||
o.content = (sector_list[minifat_store].data.slice(o.start*MSSZ,o.start*MSSZ+o.size)/*:any*/);
|
||||
prep_blob(o.content, 0);
|
||||
}
|
||||
}
|
||||
@ -1386,23 +1403,23 @@ function read_directory(dir_start, sector_list, sectors, Paths, nmfs, files, Fil
|
||||
}
|
||||
}
|
||||
|
||||
function read_date(blob, offset) {
|
||||
function read_date(blob/*:RawBytes|CFBlob*/, offset/*:number*/)/*:Date*/ {
|
||||
return new Date(( ( (__readUInt32LE(blob,offset+4)/1e7)*Math.pow(2,32)+__readUInt32LE(blob,offset)/1e7 ) - 11644473600)*1000);
|
||||
}
|
||||
|
||||
var fs;
|
||||
function readFileSync(filename/*:string*/, options/*:any*/) {
|
||||
if(fs === undefined) fs = require('fs');
|
||||
var fs/*:: = require('fs'); */;
|
||||
function readFileSync(filename/*:string*/, options/*:CFBReadOpts*/) {
|
||||
if(fs == null) fs = require('fs');
|
||||
return parse(fs.readFileSync(filename), options);
|
||||
}
|
||||
|
||||
function readSync(blob/*:any*/, options/*:any*/) {
|
||||
switch(options !== undefined && options.type !== undefined ? options.type : "base64") {
|
||||
case "file": return readFileSync(blob, options);
|
||||
case "base64": return parse(s2a(Base64.decode(blob)), options);
|
||||
case "binary": return parse(s2a(blob), options);
|
||||
function readSync(blob/*:RawBytes|string*/, options/*:CFBReadOpts*/) {
|
||||
switch(options && options.type || "base64") {
|
||||
case "file": /*:: if(typeof blob !== 'string') throw "Must pass a filename when type='file'"; */return readFileSync(blob, options);
|
||||
case "base64": /*:: if(typeof blob !== 'string') throw "Must pass a base64-encoded binary string when type='file'"; */return parse(s2a(Base64.decode(blob)), options);
|
||||
case "binary": /*:: if(typeof blob !== 'string') throw "Must pass a binary string when type='file'"; */return parse(s2a(blob), options);
|
||||
}
|
||||
return parse(blob);
|
||||
return parse(/*::typeof blob == 'string' ? new Buffer(blob, 'utf-8') : */blob, options);
|
||||
}
|
||||
|
||||
/** CFB Constants */
|
||||
|
235
xlsx.js
235
xlsx.js
@ -6,7 +6,7 @@
|
||||
/*global global, exports, module, require:false, process:false, Buffer:false */
|
||||
var XLSX = {};
|
||||
(function make_xlsx(XLSX){
|
||||
XLSX.version = '0.10.8';
|
||||
XLSX.version = '0.10.9';
|
||||
var current_codepage = 1200;
|
||||
/*global cptable:true */
|
||||
if(typeof module !== "undefined" && typeof require !== 'undefined') {
|
||||
@ -110,9 +110,9 @@ var bconcat = function(bufs) { return [].concat.apply([], bufs); };
|
||||
var chr0 = /\u0000/g, chr1 = /[\u0001-\u0006]/;
|
||||
/* ssf.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
/*jshint -W041 */
|
||||
var SSF = {};
|
||||
var SSF = ({});
|
||||
var make_ssf = function make_ssf(SSF){
|
||||
SSF.version = '0.9.4';
|
||||
SSF.version = '0.10.0';
|
||||
function _strrev(x) { var o = "", i = x.length-1; while(i>=0) o += x.charAt(i--); return o; }
|
||||
function fill(c,l) { var o = ""; while(o.length < l) o+=c; return o; }
|
||||
function pad0(v,d){var t=""+v; return t.length>=d?t:fill('0',d-t.length)+t;}
|
||||
@ -123,16 +123,6 @@ function pad0r2(v,d){var t=""+v; return t.length>=d?t:fill('0',d-t.length)+t;}
|
||||
var p2_32 = Math.pow(2,32);
|
||||
function pad0r(v,d){if(v>p2_32||v<-p2_32) return pad0r1(v,d); var i = Math.round(v); return pad0r2(i,d); }
|
||||
function isgeneral(s, i) { i = i || 0; return s.length >= 7 + i && (s.charCodeAt(i)|32) === 103 && (s.charCodeAt(i+1)|32) === 101 && (s.charCodeAt(i+2)|32) === 110 && (s.charCodeAt(i+3)|32) === 101 && (s.charCodeAt(i+4)|32) === 114 && (s.charCodeAt(i+5)|32) === 97 && (s.charCodeAt(i+6)|32) === 108; }
|
||||
/* Options */
|
||||
var opts_fmt = [
|
||||
["date1904", 0],
|
||||
["output", ""],
|
||||
["WTF", false]
|
||||
];
|
||||
function fixopts(o){
|
||||
for(var y = 0; y != opts_fmt.length; ++y) if(o[opts_fmt[y][0]]===undefined) o[opts_fmt[y][0]]=opts_fmt[y][1];
|
||||
}
|
||||
SSF.opts = opts_fmt;
|
||||
var days = [
|
||||
['Sun', 'Sunday'],
|
||||
['Mon', 'Monday'],
|
||||
@ -211,7 +201,47 @@ function frac(x, D, mixed) {
|
||||
var q = Math.floor(sgn * P/Q);
|
||||
return [q, sgn*P - q*Q, Q];
|
||||
}
|
||||
function general_fmt_int(v) { return ""+v; }
|
||||
function parse_date_code(v,opts,b2) {
|
||||
if(v > 2958465 || v < 0) return null;
|
||||
var date = (v|0), time = Math.floor(86400 * (v - date)), dow=0;
|
||||
var dout=[];
|
||||
var out={D:date, T:time, u:86400*(v-date)-time,y:0,m:0,d:0,H:0,M:0,S:0,q:0};
|
||||
if(Math.abs(out.u) < 1e-6) out.u = 0;
|
||||
if(opts && opts.date1904) date += 1462;
|
||||
if(out.u > 0.9999) {
|
||||
out.u = 0;
|
||||
if(++time == 86400) { out.T = time = 0; ++date; ++out.D; }
|
||||
}
|
||||
if(date === 60) {dout = b2 ? [1317,10,29] : [1900,2,29]; dow=3;}
|
||||
else if(date === 0) {dout = b2 ? [1317,8,29] : [1900,1,0]; dow=6;}
|
||||
else {
|
||||
if(date > 60) --date;
|
||||
/* 1 = Jan 1 1900 in Gregorian */
|
||||
var d = new Date(1900, 0, 1);
|
||||
d.setDate(d.getDate() + date - 1);
|
||||
dout = [d.getFullYear(), d.getMonth()+1,d.getDate()];
|
||||
dow = d.getDay();
|
||||
if(date < 60) dow = (dow + 6) % 7;
|
||||
if(b2) dow = fix_hijri(d, dout);
|
||||
}
|
||||
out.y = dout[0]; out.m = dout[1]; out.d = dout[2];
|
||||
out.S = time % 60; time = Math.floor(time / 60);
|
||||
out.M = time % 60; time = Math.floor(time / 60);
|
||||
out.H = time;
|
||||
out.q = dow;
|
||||
return out;
|
||||
}
|
||||
SSF.parse_date_code = parse_date_code;
|
||||
var basedate = new Date(1899, 11, 31, 0, 0, 0);
|
||||
var dnthresh = basedate.getTime();
|
||||
var base1904 = new Date(1900, 2, 1, 0, 0, 0);
|
||||
function datenum_local(v, date1904) {
|
||||
var epoch = v.getTime();
|
||||
if(date1904) epoch -= 1461*24*60*60*1000;
|
||||
else if(v >= base1904) epoch += 24*60*60*1000;
|
||||
return (epoch - (dnthresh + (v.getTimezoneOffset() - basedate.getTimezoneOffset()) * 60000)) / (24 * 60 * 60 * 1000);
|
||||
}
|
||||
function general_fmt_int(v) { return v.toString(10); }
|
||||
SSF._general_int = general_fmt_int;
|
||||
var general_fmt_num = (function make_general_fmt_num() {
|
||||
var gnr1 = /\.(\d*[1-9])0+$/, gnr2 = /\.0*$/, gnr4 = /\.(\d*[1-9])0+/, gnr5 = /\.0*[Ee]/, gnr6 = /(E[+-])(\d)$/;
|
||||
@ -242,50 +272,20 @@ return function general_fmt_num(v) {
|
||||
return gfn5(gfn4(o));
|
||||
};})();
|
||||
SSF._general_num = general_fmt_num;
|
||||
function general_fmt(v) {
|
||||
function general_fmt(v, opts) {
|
||||
switch(typeof v) {
|
||||
case 'string': return v;
|
||||
case 'boolean': return v ? "TRUE" : "FALSE";
|
||||
case 'number': return (v|0) === v ? general_fmt_int(v/*, opts*/) : general_fmt_num(v/*, opts*/);
|
||||
case 'number': return (v|0) === v ? general_fmt_int(v) : general_fmt_num(v);
|
||||
case 'undefined': return "";
|
||||
case 'object': if(v == null) return "";
|
||||
case 'object':
|
||||
if(v == null) return "";
|
||||
if(v instanceof Date) return format(14, datenum_local(v, opts && opts.date1904), opts);
|
||||
}
|
||||
throw new Error("unsupported value in General format: " + v);
|
||||
}
|
||||
SSF._general = general_fmt;
|
||||
function fix_hijri(/*date, o*/) { return 0; }
|
||||
function parse_date_code(v,opts,b2) {
|
||||
if(v > 2958465 || v < 0) return null;
|
||||
var date = (v|0), time = Math.floor(86400 * (v - date)), dow=0;
|
||||
var dout=[];
|
||||
var out={D:date, T:time, u:86400*(v-date)-time,y:0,m:0,d:0,H:0,M:0,S:0,q:0};
|
||||
if(Math.abs(out.u) < 1e-6) out.u = 0;
|
||||
fixopts(opts != null ? opts : (opts=[]));
|
||||
if(opts.date1904) date += 1462;
|
||||
if(out.u > 0.9999) {
|
||||
out.u = 0;
|
||||
if(++time == 86400) { out.T = time = 0; ++date; ++out.D; }
|
||||
}
|
||||
if(date === 60) {dout = b2 ? [1317,10,29] : [1900,2,29]; dow=3;}
|
||||
else if(date === 0) {dout = b2 ? [1317,8,29] : [1900,1,0]; dow=6;}
|
||||
else {
|
||||
if(date > 60) --date;
|
||||
/* 1 = Jan 1 1900 in Gregorian */
|
||||
var d = new Date(1900, 0, 1);
|
||||
d.setDate(d.getDate() + date - 1);
|
||||
dout = [d.getFullYear(), d.getMonth()+1,d.getDate()];
|
||||
dow = d.getDay();
|
||||
if(date < 60) dow = (dow + 6) % 7;
|
||||
if(b2) dow = fix_hijri(d, dout);
|
||||
}
|
||||
out.y = dout[0]; out.m = dout[1]; out.d = dout[2];
|
||||
out.S = time % 60; time = Math.floor(time / 60);
|
||||
out.M = time % 60; time = Math.floor(time / 60);
|
||||
out.H = time;
|
||||
out.q = dow;
|
||||
return out;
|
||||
}
|
||||
SSF.parse_date_code = parse_date_code;
|
||||
function fix_hijri() { return 0; }
|
||||
/*jshint -W086 */
|
||||
function write_date(type, fmt, val, ss0) {
|
||||
var o="", ss=0, tt=0, y = val.y, out, outl = 0;
|
||||
@ -327,22 +327,16 @@ function write_date(type, fmt, val, ss0) {
|
||||
default: throw 'bad minute format: ' + fmt;
|
||||
} break;
|
||||
case 115: /* 's' seconds */
|
||||
if(val.u === 0) switch(fmt) {
|
||||
case 's': case 'ss': return pad0(val.S, fmt.length);
|
||||
case '.0': case '.00': case '.000':
|
||||
}
|
||||
switch(fmt) {
|
||||
case 's': case 'ss': case '.0': case '.00': case '.000':
|
||||
if(fmt != 's' && fmt != 'ss' && fmt != '.0' && fmt != '.00' && fmt != '.000') throw 'bad second format: ' + fmt;
|
||||
if(val.u === 0 && (fmt == "s" || fmt == "ss")) return pad0(val.S, fmt.length);
|
||||
if(ss0 >= 2) tt = ss0 === 3 ? 1000 : 100;
|
||||
else tt = ss0 === 1 ? 10 : 1;
|
||||
ss = Math.round((tt)*(val.S + val.u));
|
||||
if(ss >= 60*tt) ss = 0;
|
||||
if(fmt === 's') return ss === 0 ? "0" : ""+ss/tt;
|
||||
o = pad0(ss,2 + ss0);
|
||||
if(fmt === 'ss') return o.substr(0,2);
|
||||
return "." + o.substr(2,fmt.length-1);
|
||||
default: throw 'bad second format: ' + fmt;
|
||||
}
|
||||
else tt = ss0 === 1 ? 10 : 1;
|
||||
ss = Math.round((tt)*(val.S + val.u));
|
||||
if(ss >= 60*tt) ss = 0;
|
||||
if(fmt === 's') return ss === 0 ? "0" : ""+ss/tt;
|
||||
o = pad0(ss,2 + ss0);
|
||||
if(fmt === 'ss') return o.substr(0,2);
|
||||
return "." + o.substr(2,fmt.length-1);
|
||||
case 90: /* 'Z' absolute time */
|
||||
switch(fmt) {
|
||||
case '[h]': case '[hh]': out = val.D*24+val.H; break;
|
||||
@ -357,9 +351,10 @@ if(ss0 >= 2) tt = ss0 === 3 ? 1000 : 100;
|
||||
}
|
||||
/*jshint +W086 */
|
||||
function commaify(s) {
|
||||
if(s.length <= 3) return s;
|
||||
var j = (s.length % 3), o = s.substr(0,j);
|
||||
for(; j!=s.length; j+=3) o+=(o.length > 0 ? "," : "") + s.substr(j,3);
|
||||
var w = 3;
|
||||
if(s.length <= w) return s;
|
||||
var j = (s.length % w), o = s.substr(0,j);
|
||||
for(; j!=s.length; j+=w) o+=(o.length > 0 ? "," : "") + s.substr(j,w);
|
||||
return o;
|
||||
}
|
||||
var write_num = (function make_write_num(){
|
||||
@ -698,7 +693,7 @@ function fmt_is_date(fmt) {
|
||||
}
|
||||
SSF.is_date = fmt_is_date;
|
||||
function eval_fmt(fmt, v, opts, flen) {
|
||||
var out = [], o = "", i = 0, c = "", lst='t', q, dt, j, cc;
|
||||
var out = [], o = "", i = 0, c = "", lst='t', dt, j, cc;
|
||||
var hr='H';
|
||||
/* Tokenize */
|
||||
while(i < fmt.length) {
|
||||
@ -726,12 +721,12 @@ function eval_fmt(fmt, v, opts, flen) {
|
||||
case 'm': case 'd': case 'y': case 'h': case 's': case 'e': case 'g':
|
||||
if(v < 0) return "";
|
||||
if(dt==null) { dt=parse_date_code(v, opts); if(dt==null) return ""; }
|
||||
o = c; while(++i<fmt.length && fmt.charAt(i).toLowerCase() === c) o+=c;
|
||||
o = c; while(++i < fmt.length && fmt.charAt(i).toLowerCase() === c) o+=c;
|
||||
if(c === 'm' && lst.toLowerCase() === 'h') c = 'M';
|
||||
if(c === 'h') c = hr;
|
||||
out[out.length] = {t:c, v:o}; lst = c; break;
|
||||
case 'A': case 'a':
|
||||
q={t:c, v:c};
|
||||
var q={t:c, v:c};
|
||||
if(dt==null) dt=parse_date_code(v, opts);
|
||||
if(fmt.substr(i, 3).toUpperCase() === "A/P") { if(dt!=null) q.v = dt.H >= 12 ? "P" : "A"; q.t = 'T'; hr='h';i+=3;}
|
||||
else if(fmt.substr(i,5).toUpperCase() === "AM/PM") { if(dt!=null) q.v = dt.H >= 12 ? "PM" : "AM"; q.t = 'T'; i+=5; hr='h'; }
|
||||
@ -754,7 +749,7 @@ function eval_fmt(fmt, v, opts, flen) {
|
||||
/* Numbers */
|
||||
case '.':
|
||||
if(dt != null) {
|
||||
o = c; while((c=fmt.charAt(++i)) === "0") o += c;
|
||||
o = c; while(++i < fmt.length && (c=fmt.charAt(i)) === "0") o += c;
|
||||
out[out.length] = {t:'s', v:o}; break;
|
||||
}
|
||||
/* falls through */
|
||||
@ -763,7 +758,7 @@ function eval_fmt(fmt, v, opts, flen) {
|
||||
out[out.length] = {t:'n', v:o}; break;
|
||||
case '?':
|
||||
o = c; while(fmt.charAt(++i) === c) o+=c;
|
||||
q={t:c, v:o}; out[out.length] = q; lst = c; break;
|
||||
out[out.length] = {t:c, v:o}; lst = c; break;
|
||||
case '*': ++i; if(fmt.charAt(i) == ' ' || fmt.charAt(i) == '*') ++i; break; // **
|
||||
case '(': case ')': out[out.length] = {t:(flen===1?'t':c), v:c}; ++i; break;
|
||||
case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
|
||||
@ -946,18 +941,34 @@ function format(fmt,v,o) {
|
||||
break;
|
||||
}
|
||||
if(isgeneral(sfmt,0)) return general_fmt(v, o);
|
||||
if(v instanceof Date) v = datenum_local(v, o.date1904);
|
||||
var f = choose_fmt(sfmt, v);
|
||||
if(isgeneral(f[1])) return general_fmt(v, o);
|
||||
if(v === true) v = "TRUE"; else if(v === false) v = "FALSE";
|
||||
else if(v === "" || v == null) return "";
|
||||
return eval_fmt(f[1], v, o, f[0]);
|
||||
}
|
||||
function load_entry(fmt, idx) {
|
||||
if(typeof idx != 'number') {
|
||||
idx = +idx || -1;
|
||||
for(var i = 0; i < 0x0188; ++i) {
|
||||
if(table_fmt[i] == undefined) { if(idx < 0) idx = i; continue; }
|
||||
if(table_fmt[i] == fmt) { idx = i; break; }
|
||||
}
|
||||
if(idx < 0) idx = 0x187;
|
||||
}
|
||||
table_fmt[idx] = fmt;
|
||||
return idx;
|
||||
}
|
||||
SSF.load = load_entry;
|
||||
SSF._table = table_fmt;
|
||||
SSF.load = function load_entry(fmt, idx) { table_fmt[idx] = fmt; };
|
||||
SSF.format = format;
|
||||
SSF.get_table = function get_table() { return table_fmt; };
|
||||
SSF.load_table = function load_table(tbl) { for(var i=0; i!=0x0188; ++i) if(tbl[i] !== undefined) SSF.load(tbl[i], i); };
|
||||
SSF.load_table = function load_table(tbl) {
|
||||
for(var i=0; i!=0x0188; ++i)
|
||||
if(tbl[i] !== undefined) load_entry(tbl[i], i);
|
||||
};
|
||||
SSF.init_table = init_table;
|
||||
SSF.format = format;
|
||||
};
|
||||
make_ssf(SSF);
|
||||
/* map from xlml named formats to SSF TODO: localize */
|
||||
@ -1015,8 +1026,8 @@ var DO_NOT_EXPORT_CFB = true;
|
||||
/* [MS-CFB] v20130118 */
|
||||
var CFB = (function _CFB(){
|
||||
var exports = {};
|
||||
exports.version = '0.11.1';
|
||||
function parse(file) {
|
||||
exports.version = '0.12.0';
|
||||
function parse(file, options) {
|
||||
var mver = 3; // major version
|
||||
var ssz = 512; // sector size
|
||||
var nmfs = 0; // number of mini FAT sectors
|
||||
@ -1076,7 +1087,7 @@ difat_start = blob.read_shift(4, 'i');
|
||||
ndfs = blob.read_shift(4, 'i');
|
||||
|
||||
// Grab FAT Sector Locations
|
||||
for(var q, j = 0; j < 109; ++j) { /* 109 = (512 - blob.l)>>>2; */
|
||||
for(var q = -1, j = 0; j < 109; ++j) { /* 109 = (512 - blob.l)>>>2; */
|
||||
q = blob.read_shift(4, 'i');
|
||||
if(q<0) break;
|
||||
fat_addrs[j] = q;
|
||||
@ -1103,7 +1114,6 @@ read_directory(dir_start, sector_list, sectors, Paths, nmfs, files, FileIndex);
|
||||
build_full_paths(FileIndex, FullPathDir, FullPaths, Paths);
|
||||
|
||||
var root_name = Paths.shift();
|
||||
Paths.root = root_name;
|
||||
|
||||
/* [MS-CFB] 2.6.4 (Unicode 3.0.1 case conversion) */
|
||||
var find_path = make_find_path(FullPaths, Paths, FileIndex, files, root_name);
|
||||
@ -1154,7 +1164,7 @@ function check_shifts(blob, mver) {
|
||||
/** Break the file up into sectors */
|
||||
function sectorify(file, ssz) {
|
||||
var nsectors = Math.ceil(file.length/ssz)-1;
|
||||
var sectors = new Array(nsectors);
|
||||
var sectors = [];
|
||||
for(var i=1; i < nsectors; ++i) sectors[i-1] = file.slice(i*ssz,(i+1)*ssz);
|
||||
sectors[nsectors-1] = file.slice(nsectors*ssz);
|
||||
return sectors;
|
||||
@ -1163,7 +1173,7 @@ function sectorify(file, ssz) {
|
||||
/* [MS-CFB] 2.6.4 Red-Black Tree */
|
||||
function build_full_paths(FI, FPD, FP, Paths) {
|
||||
var i = 0, L = 0, R = 0, C = 0, j = 0, pl = Paths.length;
|
||||
var dad = new Array(pl), q = new Array(pl);
|
||||
var dad = [], q = [];
|
||||
|
||||
for(; i < pl; ++i) { dad[i]=q[i]=i; FP[i]=Paths[i]; }
|
||||
|
||||
@ -1203,12 +1213,12 @@ function build_full_paths(FI, FPD, FP, Paths) {
|
||||
|
||||
/* [MS-CFB] 2.6.4 */
|
||||
function make_find_path(FullPaths, Paths, FileIndex, files, root_name) {
|
||||
var UCFullPaths = new Array(FullPaths.length);
|
||||
var UCPaths = new Array(Paths.length), i;
|
||||
var UCFullPaths = [];
|
||||
var UCPaths = [], i = 0;
|
||||
for(i = 0; i < FullPaths.length; ++i) UCFullPaths[i] = FullPaths[i].toUpperCase().replace(chr0,'').replace(chr1,'!');
|
||||
for(i = 0; i < Paths.length; ++i) UCPaths[i] = Paths[i].toUpperCase().replace(chr0,'').replace(chr1,'!');
|
||||
return function find_path(path) {
|
||||
var k;
|
||||
var k = false;
|
||||
if(path.charCodeAt(0) === 47 /* "/" */) { k=true; path = root_name + path; }
|
||||
else k = path.indexOf("/") !== -1;
|
||||
var UCPath = path.toUpperCase().replace(chr0,'').replace(chr1,'!');
|
||||
@ -1221,7 +1231,7 @@ function make_find_path(FullPaths, Paths, FileIndex, files, root_name) {
|
||||
/** Chase down the rest of the DIFAT chain to build a comprehensive list
|
||||
DIFAT chains by storing the next sector number as the last 32 bytes */
|
||||
function sleuth_fat(idx, cnt, sectors, ssz, fat_addrs) {
|
||||
var q;
|
||||
var q = ENDOFCHAIN;
|
||||
if(idx === ENDOFCHAIN) {
|
||||
if(cnt !== 0) throw new Error("DIFAT chain shorter than expected");
|
||||
} else if(idx !== -1 /*FREESECT*/) {
|
||||
@ -1238,11 +1248,9 @@ function sleuth_fat(idx, cnt, sectors, ssz, fat_addrs) {
|
||||
/** Follow the linked list of sectors for a given starting point */
|
||||
function get_sector_list(sectors, start, fat_addrs, ssz, chkd) {
|
||||
var sl = sectors.length;
|
||||
var buf, buf_chain;
|
||||
if(!chkd) chkd = new Array(sl);
|
||||
var modulus = ssz - 1, j, jj;
|
||||
buf = [];
|
||||
buf_chain = [];
|
||||
var buf = [], buf_chain = [];
|
||||
if(!chkd) chkd = [];
|
||||
var modulus = ssz - 1, j = 0, jj = 0;
|
||||
for(j=start; j>=0;) {
|
||||
chkd[j] = true;
|
||||
buf[buf.length] = j;
|
||||
@ -1258,13 +1266,13 @@ function get_sector_list(sectors, start, fat_addrs, ssz, chkd) {
|
||||
|
||||
/** Chase down the sector linked lists */
|
||||
function make_sector_list(sectors, dir_start, fat_addrs, ssz) {
|
||||
var sl = sectors.length, sector_list = new Array(sl);
|
||||
var chkd = new Array(sl), buf, buf_chain;
|
||||
var modulus = ssz - 1, i, j, k, jj;
|
||||
var sl = sectors.length, sector_list = ([]);
|
||||
var chkd = [], buf = [], buf_chain = [];
|
||||
var modulus = ssz - 1, i=0, j=0, k=0, jj=0;
|
||||
for(i=0; i < sl; ++i) {
|
||||
buf = [];
|
||||
buf = ([]);
|
||||
k = (i + dir_start); if(k >= sl) k-=sl;
|
||||
if(chkd[k] === true) continue;
|
||||
if(chkd[k]) continue;
|
||||
buf_chain = [];
|
||||
for(j=k; j>=0;) {
|
||||
chkd[j] = true;
|
||||
@ -1276,25 +1284,24 @@ function make_sector_list(sectors, dir_start, fat_addrs, ssz) {
|
||||
if(!sectors[addr]) break;
|
||||
j = __readInt32LE(sectors[addr], jj);
|
||||
}
|
||||
sector_list[k] = {nodes: buf, data:__toBuffer([buf_chain])};
|
||||
sector_list[k] = ({nodes: buf, data:__toBuffer([buf_chain])});
|
||||
}
|
||||
return sector_list;
|
||||
}
|
||||
|
||||
/* [MS-CFB] 2.6.1 Compound File Directory Entry */
|
||||
function read_directory(dir_start, sector_list, sectors, Paths, nmfs, files, FileIndex) {
|
||||
var blob;
|
||||
var minifat_store = 0, pl = (Paths.length?2:0);
|
||||
var sector = sector_list[dir_start].data;
|
||||
var i = 0, namelen = 0, name, o, ctime, mtime;
|
||||
var i = 0, namelen = 0, name;
|
||||
for(; i < sector.length; i+= 128) {
|
||||
blob = sector.slice(i, i+128);
|
||||
var blob = sector.slice(i, i+128);
|
||||
prep_blob(blob, 64);
|
||||
namelen = blob.read_shift(2);
|
||||
if(namelen === 0) continue;
|
||||
name = __utf16le(blob,0,namelen-pl);
|
||||
Paths.push(name);
|
||||
o = ({
|
||||
var o = ({
|
||||
name: name,
|
||||
type: blob.read_shift(1),
|
||||
color: blob.read_shift(1),
|
||||
@ -1302,16 +1309,14 @@ function read_directory(dir_start, sector_list, sectors, Paths, nmfs, files, Fil
|
||||
R: blob.read_shift(4, 'i'),
|
||||
C: blob.read_shift(4, 'i'),
|
||||
clsid: blob.read_shift(16),
|
||||
state: blob.read_shift(4, 'i')
|
||||
state: blob.read_shift(4, 'i'),
|
||||
start: 0,
|
||||
size: 0
|
||||
});
|
||||
ctime = blob.read_shift(2) + blob.read_shift(2) + blob.read_shift(2) + blob.read_shift(2);
|
||||
if(ctime !== 0) {
|
||||
o.ctime = ctime; o.ct = read_date(blob, blob.l-8);
|
||||
}
|
||||
mtime = blob.read_shift(2) + blob.read_shift(2) + blob.read_shift(2) + blob.read_shift(2);
|
||||
if(mtime !== 0) {
|
||||
o.mtime = mtime; o.mt = read_date(blob, blob.l-8);
|
||||
}
|
||||
var ctime = blob.read_shift(2) + blob.read_shift(2) + blob.read_shift(2) + blob.read_shift(2);
|
||||
if(ctime !== 0) o.ct = read_date(blob, blob.l-8);
|
||||
var mtime = blob.read_shift(2) + blob.read_shift(2) + blob.read_shift(2) + blob.read_shift(2);
|
||||
if(mtime !== 0) o.mt = read_date(blob, blob.l-8);
|
||||
o.start = blob.read_shift(4, 'i');
|
||||
o.size = blob.read_shift(4, 'i');
|
||||
if(o.type === 5) { /* root */
|
||||
@ -1322,12 +1327,12 @@ function read_directory(dir_start, sector_list, sectors, Paths, nmfs, files, Fil
|
||||
o.storage = 'fat';
|
||||
if(sector_list[o.start] === undefined) sector_list[o.start] = get_sector_list(sectors, o.start, sector_list.fat_addrs, sector_list.ssz);
|
||||
sector_list[o.start].name = o.name;
|
||||
o.content = sector_list[o.start].data.slice(0,o.size);
|
||||
o.content = (sector_list[o.start].data.slice(0,o.size));
|
||||
prep_blob(o.content, 0);
|
||||
} else {
|
||||
o.storage = 'minifat';
|
||||
if(minifat_store !== ENDOFCHAIN && o.start !== ENDOFCHAIN) {
|
||||
o.content = sector_list[minifat_store].data.slice(o.start*MSSZ,o.start*MSSZ+o.size);
|
||||
o.content = (sector_list[minifat_store].data.slice(o.start*MSSZ,o.start*MSSZ+o.size));
|
||||
prep_blob(o.content, 0);
|
||||
}
|
||||
}
|
||||
@ -1342,17 +1347,17 @@ function read_date(blob, offset) {
|
||||
|
||||
var fs;
|
||||
function readFileSync(filename, options) {
|
||||
if(fs === undefined) fs = require('fs');
|
||||
if(fs == null) fs = require('fs');
|
||||
return parse(fs.readFileSync(filename), options);
|
||||
}
|
||||
|
||||
function readSync(blob, options) {
|
||||
switch(options !== undefined && options.type !== undefined ? options.type : "base64") {
|
||||
switch(options && options.type || "base64") {
|
||||
case "file": return readFileSync(blob, options);
|
||||
case "base64": return parse(s2a(Base64.decode(blob)), options);
|
||||
case "binary": return parse(s2a(blob), options);
|
||||
}
|
||||
return parse(blob);
|
||||
return parse(blob, options);
|
||||
}
|
||||
|
||||
/** CFB Constants */
|
||||
|
Loading…
Reference in New Issue
Block a user