1/* xlsx.js (C) 2013-2015 SheetJS -- http://sheetjs.com */
2/* vim: set ts=2: */
3/*jshint -W041 */
4/*jshint funcscope:true, eqnull:true */
5var XLSX = {};
6(function make_xlsx(XLSX){
7XLSX.version = '0.8.0';
8var current_codepage = 1200, current_cptable;
9if(typeof module !== "undefined" && typeof require !== 'undefined') {
10    if(typeof cptable === 'undefined') cptable = require('./dist/cpexcel');
11    current_cptable = cptable[current_codepage];
12}
13function reset_cp() { set_cp(1200); }
14var set_cp = function(cp) { current_codepage = cp; };
15
16function char_codes(data) { var o = []; for(var i = 0, len = data.length; i < len; ++i) o[i] = data.charCodeAt(i); return o; }
17var debom_xml = function(data) { return data; };
18
19var _getchar = function _gc1(x) { return String.fromCharCode(x); };
20if(typeof cptable !== 'undefined') {
21    set_cp = function(cp) { current_codepage = cp; current_cptable = cptable[cp]; };
22    debom_xml = function(data) {
23        if(data.charCodeAt(0) === 0xFF && data.charCodeAt(1) === 0xFE) { return cptable.utils.decode(1200, char_codes(data.substr(2))); }
24        return data;
25    };
26    _getchar = function _gc2(x) {
27        if(current_codepage === 1200) return String.fromCharCode(x);
28        return cptable.utils.decode(current_codepage, [x&255,x>>8])[0];
29    };
30}
31var Base64 = (function make_b64(){
32    var map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
33    return {
34        encode: function(input, utf8) {
35            var o = "";
36            var c1, c2, c3, e1, e2, e3, e4;
37            for(var i = 0; i < input.length; ) {
38                c1 = input.charCodeAt(i++);
39                c2 = input.charCodeAt(i++);
40                c3 = input.charCodeAt(i++);
41                e1 = c1 >> 2;
42                e2 = (c1 & 3) << 4 | c2 >> 4;
43                e3 = (c2 & 15) << 2 | c3 >> 6;
44                e4 = c3 & 63;
45                if (isNaN(c2)) { e3 = e4 = 64; }
46                else if (isNaN(c3)) { e4 = 64; }
47                o += map.charAt(e1) + map.charAt(e2) + map.charAt(e3) + map.charAt(e4);
48            }
49            return o;
50        },
51        decode: function b64_decode(input, utf8) {
52            var o = "";
53            var c1, c2, c3;
54            var e1, e2, e3, e4;
55            input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
56            for(var i = 0; i < input.length;) {
57                e1 = map.indexOf(input.charAt(i++));
58                e2 = map.indexOf(input.charAt(i++));
59                e3 = map.indexOf(input.charAt(i++));
60                e4 = map.indexOf(input.charAt(i++));
61                c1 = e1 << 2 | e2 >> 4;
62                c2 = (e2 & 15) << 4 | e3 >> 2;
63                c3 = (e3 & 3) << 6 | e4;
64                o += String.fromCharCode(c1);
65                if (e3 != 64) { o += String.fromCharCode(c2); }
66                if (e4 != 64) { o += String.fromCharCode(c3); }
67            }
68            return o;
69        }
70    };
71})();
72var has_buf = (typeof Buffer !== 'undefined');
73
74function new_raw_buf(len) {
75    /* jshint -W056 */
76    return new (has_buf ? Buffer : Array)(len);
77    /* jshint +W056 */
78}
79
80function s2a(s) {
81    if(has_buf) return new Buffer(s, "binary");
82    return s.split("").map(function(x){ return x.charCodeAt(0) & 0xff; });
83}
84
85var bconcat = function(bufs) { return [].concat.apply([], bufs); };
86
87var chr0 = /\u0000/g, chr1 = /[\u0001-\u0006]/;
88/* ssf.js (C) 2013-2014 SheetJS -- http://sheetjs.com */
89/*jshint -W041 */
90var SSF = {};
91var make_ssf = function make_ssf(SSF){
92SSF.version = '0.8.1';
93function _strrev(x) { var o = "", i = x.length-1; while(i>=0) o += x.charAt(i--); return o; }
94function fill(c,l) { var o = ""; while(o.length < l) o+=c; return o; }
95function pad0(v,d){var t=""+v; return t.length>=d?t:fill('0',d-t.length)+t;}
96function pad_(v,d){var t=""+v;return t.length>=d?t:fill(' ',d-t.length)+t;}
97function rpad_(v,d){var t=""+v; return t.length>=d?t:t+fill(' ',d-t.length);}
98function pad0r1(v,d){var t=""+Math.round(v); return t.length>=d?t:fill('0',d-t.length)+t;}
99function pad0r2(v,d){var t=""+v; return t.length>=d?t:fill('0',d-t.length)+t;}
100var p2_32 = Math.pow(2,32);
101function pad0r(v,d){if(v>p2_32||v<-p2_32) return pad0r1(v,d); var i = Math.round(v); return pad0r2(i,d); }
102function isgeneral(s, i) { return s.length >= 7 + i && (s.charCodeAt(i)|32) === 103 && (s.charCodeAt(i+1)|32) === 101 && (s.charCodeAt(i+2)|32) === 110 && (s.charCodeAt(i+3)|32) === 101 && (s.charCodeAt(i+4)|32) === 114 && (s.charCodeAt(i+5)|32) === 97 && (s.charCodeAt(i+6)|32) === 108; }
103/* Options */
104var opts_fmt = [
105    ["date1904", 0],
106    ["output", ""],
107    ["WTF", false]
108];
109function fixopts(o){
110    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];
111}
112SSF.opts = opts_fmt;
113var table_fmt = {
114    0:  'General',
115    1:  '0',
116    2:  '0.00',
117    3:  '#,##0',
118    4:  '#,##0.00',
119    9:  '0%',
120    10: '0.00%',
121    11: '0.00E+00',
122    12: '# ?/?',
123    13: '# ??/??',
124    14: 'm/d/yy',
125    15: 'd-mmm-yy',
126    16: 'd-mmm',
127    17: 'mmm-yy',
128    18: 'h:mm AM/PM',
129    19: 'h:mm:ss AM/PM',
130    20: 'h:mm',
131    21: 'h:mm:ss',
132    22: 'm/d/yy h:mm',
133    37: '#,##0 ;(#,##0)',
134    38: '#,##0 ;[Red](#,##0)',
135    39: '#,##0.00;(#,##0.00)',
136    40: '#,##0.00;[Red](#,##0.00)',
137    45: 'mm:ss',
138    46: '[h]:mm:ss',
139    47: 'mmss.0',
140    48: '##0.0E+0',
141    49: '@',
142    56: '"上午/下午 "hh"時"mm"分"ss"秒 "',
143    65535: 'General'
144};
145var days = [
146    ['Sun', 'Sunday'],
147    ['Mon', 'Monday'],
148    ['Tue', 'Tuesday'],
149    ['Wed', 'Wednesday'],
150    ['Thu', 'Thursday'],
151    ['Fri', 'Friday'],
152    ['Sat', 'Saturday']
153];
154var months = [
155    ['J', 'Jan', 'January'],
156    ['F', 'Feb', 'February'],
157    ['M', 'Mar', 'March'],
158    ['A', 'Apr', 'April'],
159    ['M', 'May', 'May'],
160    ['J', 'Jun', 'June'],
161    ['J', 'Jul', 'July'],
162    ['A', 'Aug', 'August'],
163    ['S', 'Sep', 'September'],
164    ['O', 'Oct', 'October'],
165    ['N', 'Nov', 'November'],
166    ['D', 'Dec', 'December']
167];
168function frac(x, D, mixed) {
169    var sgn = x < 0 ? -1 : 1;
170    var B = x * sgn;
171    var P_2 = 0, P_1 = 1, P = 0;
172    var Q_2 = 1, Q_1 = 0, Q = 0;
173    var A = Math.floor(B);
174    while(Q_1 < D) {
175        A = Math.floor(B);
176        P = A * P_1 + P_2;
177        Q = A * Q_1 + Q_2;
178        if((B - A) < 0.0000000005) break;
179        B = 1 / (B - A);
180        P_2 = P_1; P_1 = P;
181        Q_2 = Q_1; Q_1 = Q;
182    }
183    if(Q > D) { Q = Q_1; P = P_1; }
184    if(Q > D) { Q = Q_2; P = P_2; }
185    if(!mixed) return [0, sgn * P, Q];
186    if(Q===0) throw "Unexpected state: "+P+" "+P_1+" "+P_2+" "+Q+" "+Q_1+" "+Q_2;
187    var q = Math.floor(sgn * P/Q);
188    return [q, sgn*P - q*Q, Q];
189}
190function general_fmt_int(v, opts) { return ""+v; }
191SSF._general_int = general_fmt_int;
192var general_fmt_num = (function make_general_fmt_num() {
193var gnr1 = /\.(\d*[1-9])0+$/, gnr2 = /\.0*$/, gnr4 = /\.(\d*[1-9])0+/, gnr5 = /\.0*[Ee]/, gnr6 = /(E[+-])(\d)$/;
194function gfn2(v) {
195    var w = (v<0?12:11);
196    var o = gfn5(v.toFixed(12)); if(o.length <= w) return o;
197    o = v.toPrecision(10); if(o.length <= w) return o;
198    return v.toExponential(5);
199}
200function gfn3(v) {
201    var o = v.toFixed(11).replace(gnr1,".$1");
202    if(o.length > (v<0?12:11)) o = v.toPrecision(6);
203    return o;
204}
205function gfn4(o) {
206    for(var i = 0; i != o.length; ++i) if((o.charCodeAt(i) | 0x20) === 101) return o.replace(gnr4,".$1").replace(gnr5,"E").replace("e","E").replace(gnr6,"$10$2");
207    return o;
208}
209function gfn5(o) {
210    //for(var i = 0; i != o.length; ++i) if(o.charCodeAt(i) === 46) return o.replace(gnr2,"").replace(gnr1,".$1");
211    //return o;
212    return o.indexOf(".") > -1 ? o.replace(gnr2,"").replace(gnr1,".$1") : o;
213}
214return function general_fmt_num(v, opts) {
215    var V = Math.floor(Math.log(Math.abs(v))*Math.LOG10E), o;
216    if(V >= -4 && V <= -1) o = v.toPrecision(10+V);
217    else if(Math.abs(V) <= 9) o = gfn2(v);
218    else if(V === 10) o = v.toFixed(10).substr(0,12);
219    else o = gfn3(v);
220    return gfn5(gfn4(o));
221};})();
222SSF._general_num = general_fmt_num;
223function general_fmt(v, opts) {
224    switch(typeof v) {
225        case 'string': return v;
226        case 'boolean': return v ? "TRUE" : "FALSE";
227        case 'number': return (v|0) === v ? general_fmt_int(v, opts) : general_fmt_num(v, opts);
228    }
229    throw new Error("unsupported value in General format: " + v);
230}
231SSF._general = general_fmt;
232function fix_hijri(date, o) { return 0; }
233function parse_date_code(v,opts,b2) {
234    if(v > 2958465 || v < 0) return null;
235    var date = (v|0), time = Math.floor(86400 * (v - date)), dow=0;
236    var dout=[];
237    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};
238    if(Math.abs(out.u) < 1e-6) out.u = 0;
239    fixopts(opts != null ? opts : (opts=[]));
240    if(opts.date1904) date += 1462;
241    if(out.u > 0.999) {
242        out.u = 0;
243        if(++time == 86400) { time = 0; ++date; }
244    }
245    if(date === 60) {dout = b2 ? [1317,10,29] : [1900,2,29]; dow=3;}
246    else if(date === 0) {dout = b2 ? [1317,8,29] : [1900,1,0]; dow=6;}
247    else {
248        if(date > 60) --date;
249        /* 1 = Jan 1 1900 */
250        var d = new Date(1900,0,1);
251        d.setDate(d.getDate() + date - 1);
252        dout = [d.getFullYear(), d.getMonth()+1,d.getDate()];
253        dow = d.getDay();
254        if(date < 60) dow = (dow + 6) % 7;
255        if(b2) dow = fix_hijri(d, dout);
256    }
257    out.y = dout[0]; out.m = dout[1]; out.d = dout[2];
258    out.S = time % 60; time = Math.floor(time / 60);
259    out.M = time % 60; time = Math.floor(time / 60);
260    out.H = time;
261    out.q = dow;
262    return out;
263}
264SSF.parse_date_code = parse_date_code;
265/*jshint -W086 */
266function write_date(type, fmt, val, ss0) {
267    var o="", ss=0, tt=0, y = val.y, out, outl = 0;
268    switch(type) {
269        case 98: /* 'b' buddhist year */
270            y = val.y + 543;
271            /* falls through */
272        case 121: /* 'y' year */
273        switch(fmt.length) {
274            case 1: case 2: out = y % 100; outl = 2; break;
275            default: out = y % 10000; outl = 4; break;
276        } break;
277        case 109: /* 'm' month */
278        switch(fmt.length) {
279            case 1: case 2: out = val.m; outl = fmt.length; break;
280            case 3: return months[val.m-1][1];
281            case 5: return months[val.m-1][0];
282            default: return months[val.m-1][2];
283        } break;
284        case 100: /* 'd' day */
285        switch(fmt.length) {
286            case 1: case 2: out = val.d; outl = fmt.length; break;
287            case 3: return days[val.q][0];
288            default: return days[val.q][1];
289        } break;
290        case 104: /* 'h' 12-hour */
291        switch(fmt.length) {
292            case 1: case 2: out = 1+(val.H+11)%12; outl = fmt.length; break;
293            default: throw 'bad hour format: ' + fmt;
294        } break;
295        case 72: /* 'H' 24-hour */
296        switch(fmt.length) {
297            case 1: case 2: out = val.H; outl = fmt.length; break;
298            default: throw 'bad hour format: ' + fmt;
299        } break;
300        case 77: /* 'M' minutes */
301        switch(fmt.length) {
302            case 1: case 2: out = val.M; outl = fmt.length; break;
303            default: throw 'bad minute format: ' + fmt;
304        } break;
305        case 115: /* 's' seconds */
306        if(val.u === 0) switch(fmt) {
307            case 's': case 'ss': return pad0(val.S, fmt.length);
308            case '.0': case '.00': case '.000':
309        }
310        switch(fmt) {
311            case 's': case 'ss': case '.0': case '.00': case '.000':
312                if(ss0 >= 2) tt = ss0 === 3 ? 1000 : 100;
313                else tt = ss0 === 1 ? 10 : 1;
314                ss = Math.round((tt)*(val.S + val.u));
315                if(ss >= 60*tt) ss = 0;
316                if(fmt === 's') return ss === 0 ? "0" : ""+ss/tt;
317                o = pad0(ss,2 + ss0);
318                if(fmt === 'ss') return o.substr(0,2);
319                return "." + o.substr(2,fmt.length-1);
320            default: throw 'bad second format: ' + fmt;
321        }
322        case 90: /* 'Z' absolute time */
323        switch(fmt) {
324            case '[h]': case '[hh]': out = val.D*24+val.H; break;
325            case '[m]': case '[mm]': out = (val.D*24+val.H)*60+val.M; break;
326            case '[s]': case '[ss]': out = ((val.D*24+val.H)*60+val.M)*60+Math.round(val.S+val.u); break;
327            default: throw 'bad abstime format: ' + fmt;
328        } outl = fmt.length === 3 ? 1 : 2; break;
329        case 101: /* 'e' era */
330            out = y; outl = 1;
331    }
332    if(outl > 0) return pad0(out, outl); else return "";
333}
334/*jshint +W086 */
335function commaify(s) {
336    if(s.length <= 3) return s;
337    var j = (s.length % 3), o = s.substr(0,j);
338    for(; j!=s.length; j+=3) o+=(o.length > 0 ? "," : "") + s.substr(j,3);
339    return o;
340}
341var write_num = (function make_write_num(){
342var pct1 = /%/g;
343function write_num_pct(type, fmt, val){
344    var sfmt = fmt.replace(pct1,""), mul = fmt.length - sfmt.length;
345    return write_num(type, sfmt, val * Math.pow(10,2*mul)) + fill("%",mul);
346}
347function write_num_cm(type, fmt, val){
348    var idx = fmt.length - 1;
349    while(fmt.charCodeAt(idx-1) === 44) --idx;
350    return write_num(type, fmt.substr(0,idx), val / Math.pow(10,3*(fmt.length-idx)));
351}
352function write_num_exp(fmt, val){
353    var o;
354    var idx = fmt.indexOf("E") - fmt.indexOf(".") - 1;
355    if(fmt.match(/^#+0.0E\+0$/)) {
356        var period = fmt.indexOf("."); if(period === -1) period=fmt.indexOf('E');
357        var ee = Math.floor(Math.log(Math.abs(val))*Math.LOG10E)%period;
358        if(ee < 0) ee += period;
359        o = (val/Math.pow(10,ee)).toPrecision(idx+1+(period+ee)%period);
360        if(o.indexOf("e") === -1) {
361            var fakee = Math.floor(Math.log(Math.abs(val))*Math.LOG10E);
362            if(o.indexOf(".") === -1) o = o[0] + "." + o.substr(1) + "E+" + (fakee - o.length+ee);
363            else o += "E+" + (fakee - ee);
364            while(o.substr(0,2) === "0.") {
365                o = o[0] + o.substr(2,period) + "." + o.substr(2+period);
366                o = o.replace(/^0+([1-9])/,"$1").replace(/^0+\./,"0.");
367            }
368            o = o.replace(/\+-/,"-");
369        }
370        o = o.replace(/^([+-]?)(\d*)\.(\d*)[Ee]/,function($$,$1,$2,$3) { return $1 + $2 + $3.substr(0,(period+ee)%period) + "." + $3.substr(ee) + "E"; });
371    } else o = val.toExponential(idx);
372    if(fmt.match(/E\+00$/) && o.match(/e[+-]\d$/)) o = o.substr(0,o.length-1) + "0" + o[o.length-1];
373    if(fmt.match(/E\-/) && o.match(/e\+/)) o = o.replace(/e\+/,"e");
374    return o.replace("e","E");
375}
376var frac1 = /# (\?+)( ?)\/( ?)(\d+)/;
377function write_num_f1(r, aval, sign) {
378    var den = parseInt(r[4]), rr = Math.round(aval * den), base = Math.floor(rr/den);
379    var myn = (rr - base*den), myd = den;
380    return sign + (base === 0 ? "" : ""+base) + " " + (myn === 0 ? fill(" ", r[1].length + 1 + r[4].length) : pad_(myn,r[1].length) + r[2] + "/" + r[3] + pad0(myd,r[4].length));
381}
382function write_num_f2(r, aval, sign) {
383    return sign + (aval === 0 ? "" : ""+aval) + fill(" ", r[1].length + 2 + r[4].length);
384}
385var dec1 = /^#*0*\.(0+)/;
386var closeparen = /\).*[0#]/;
387var phone = /\(###\) ###\\?-####/;
388function hashq(str) {
389    var o = "", cc;
390    for(var i = 0; i != str.length; ++i) switch((cc=str.charCodeAt(i))) {
391        case 35: break;
392        case 63: o+= " "; break;
393        case 48: o+= "0"; break;
394        default: o+= String.fromCharCode(cc);
395    }
396    return o;
397}
398function rnd(val, d) { var dd = Math.pow(10,d); return ""+(Math.round(val * dd)/dd); }
399function dec(val, d) { return Math.round((val-Math.floor(val))*Math.pow(10,d)); }
400function flr(val) { if(val < 2147483647 && val > -2147483648) return ""+(val >= 0 ? (val|0) : (val-1|0)); return ""+Math.floor(val); }
401function write_num_flt(type, fmt, val) {
402    if(type.charCodeAt(0) === 40 && !fmt.match(closeparen)) {
403        var ffmt = fmt.replace(/\( */,"").replace(/ \)/,"").replace(/\)/,"");
404        if(val >= 0) return write_num_flt('n', ffmt, val);
405        return '(' + write_num_flt('n', ffmt, -val) + ')';
406    }
407    if(fmt.charCodeAt(fmt.length - 1) === 44) return write_num_cm(type, fmt, val);
408    if(fmt.indexOf('%') !== -1) return write_num_pct(type, fmt, val);
409    if(fmt.indexOf('E') !== -1) return write_num_exp(fmt, val);
410    if(fmt.charCodeAt(0) === 36) return "$"+write_num_flt(type,fmt.substr(fmt[1]==' '?2:1),val);
411    var o, oo;
412    var r, ri, ff, aval = Math.abs(val), sign = val < 0 ? "-" : "";
413    if(fmt.match(/^00+$/)) return sign + pad0r(aval,fmt.length);
414    if(fmt.match(/^[#?]+$/)) {
415        o = pad0r(val,0); if(o === "0") o = "";
416        return o.length > fmt.length ? o : hashq(fmt.substr(0,fmt.length-o.length)) + o;
417    }
418    if((r = fmt.match(frac1)) !== null) return write_num_f1(r, aval, sign);
419    if(fmt.match(/^#+0+$/) !== null) return sign + pad0r(aval,fmt.length - fmt.indexOf("0"));
420    if((r = fmt.match(dec1)) !== null) {
421        o = rnd(val, r[1].length).replace(/^([^\.]+)$/,"$1."+r[1]).replace(/\.$/,"."+r[1]).replace(/\.(\d*)$/,function($$, $1) { return "." + $1 + fill("0", r[1].length-$1.length); });
422        return fmt.indexOf("0.") !== -1 ? o : o.replace(/^0\./,".");
423    }
424    fmt = fmt.replace(/^#+([0.])/, "$1");
425    if((r = fmt.match(/^(0*)\.(#*)$/)) !== null) {
426        return sign + rnd(aval, r[2].length).replace(/\.(\d*[1-9])0*$/,".$1").replace(/^(-?\d*)$/,"$1.").replace(/^0\./,r[1].length?"0.":".");
427    }
428    if((r = fmt.match(/^#,##0(\.?)$/)) !== null) return sign + commaify(pad0r(aval,0));
429    if((r = fmt.match(/^#,##0\.([#0]*0)$/)) !== null) {
430        return val < 0 ? "-" + write_num_flt(type, fmt, -val) : commaify(""+(Math.floor(val))) + "." + pad0(dec(val, r[1].length),r[1].length);
431    }
432    if((r = fmt.match(/^#,#*,#0/)) !== null) return write_num_flt(type,fmt.replace(/^#,#*,/,""),val);
433    if((r = fmt.match(/^([0#]+)(\\?-([0#]+))+$/)) !== null) {
434        o = _strrev(write_num_flt(type, fmt.replace(/[\\-]/g,""), val));
435        ri = 0;
436        return _strrev(_strrev(fmt.replace(/\\/g,"")).replace(/[0#]/g,function(x){return ri<o.length?o[ri++]:x==='0'?'0':"";}));
437    }
438    if(fmt.match(phone) !== null) {
439        o = write_num_flt(type, "##########", val);
440        return "(" + o.substr(0,3) + ") " + o.substr(3, 3) + "-" + o.substr(6);
441    }
442    var oa = "";
443    if((r = fmt.match(/^([#0?]+)( ?)\/( ?)([#0?]+)/)) !== null) {
444        ri = Math.min(r[4].length,7);
445        ff = frac(aval, Math.pow(10,ri)-1, false);
446        o = "" + sign;
447        oa = write_num("n", r[1], ff[1]);
448        if(oa[oa.length-1] == " ") oa = oa.substr(0,oa.length-1) + "0";
449        o += oa + r[2] + "/" + r[3];
450        oa = rpad_(ff[2],ri);
451        if(oa.length < r[4].length) oa = hashq(r[4].substr(r[4].length-oa.length)) + oa;
452        o += oa;
453        return o;
454    }
455    if((r = fmt.match(/^# ([#0?]+)( ?)\/( ?)([#0?]+)/)) !== null) {
456        ri = Math.min(Math.max(r[1].length, r[4].length),7);
457        ff = frac(aval, Math.pow(10,ri)-1, true);
458        return sign + (ff[0]||(ff[1] ? "" : "0")) + " " + (ff[1] ? pad_(ff[1],ri) + r[2] + "/" + r[3] + rpad_(ff[2],ri): fill(" ", 2*ri+1 + r[2].length + r[3].length));
459    }
460    if((r = fmt.match(/^[#0?]+$/)) !== null) {
461        o = pad0r(val, 0);
462        if(fmt.length <= o.length) return o;
463        return hashq(fmt.substr(0,fmt.length-o.length)) + o;
464    }
465  if((r = fmt.match(/^([#0?]+)\.([#0]+)$/)) !== null) {
466        o = "" + val.toFixed(Math.min(r[2].length,10)).replace(/([^0])0+$/,"$1");
467        ri = o.indexOf(".");
468        var lres = fmt.indexOf(".") - ri, rres = fmt.length - o.length - lres;
469        return hashq(fmt.substr(0,lres) + o + fmt.substr(fmt.length-rres));
470    }
471    if((r = fmt.match(/^00,000\.([#0]*0)$/)) !== null) {
472        ri = dec(val, r[1].length);
473        return val < 0 ? "-" + write_num_flt(type, fmt, -val) : commaify(flr(val)).replace(/^\d,\d{3}$/,"0$&").replace(/^\d*$/,function($$) { return "00," + ($$.length < 3 ? pad0(0,3-$$.length) : "") + $$; }) + "." + pad0(ri,r[1].length);
474    }
475    switch(fmt) {
476        case "#,###": var x = commaify(pad0r(aval,0)); return x !== "0" ? sign + x : "";
477        default:
478    }
479    throw new Error("unsupported format |" + fmt + "|");
480}
481function write_num_cm2(type, fmt, val){
482    var idx = fmt.length - 1;
483    while(fmt.charCodeAt(idx-1) === 44) --idx;
484    return write_num(type, fmt.substr(0,idx), val / Math.pow(10,3*(fmt.length-idx)));
485}
486function write_num_pct2(type, fmt, val){
487    var sfmt = fmt.replace(pct1,""), mul = fmt.length - sfmt.length;
488    return write_num(type, sfmt, val * Math.pow(10,2*mul)) + fill("%",mul);
489}
490function write_num_exp2(fmt, val){
491    var o;
492    var idx = fmt.indexOf("E") - fmt.indexOf(".") - 1;
493    if(fmt.match(/^#+0.0E\+0$/)) {
494        var period = fmt.indexOf("."); if(period === -1) period=fmt.indexOf('E');
495        var ee = Math.floor(Math.log(Math.abs(val))*Math.LOG10E)%period;
496        if(ee < 0) ee += period;
497        o = (val/Math.pow(10,ee)).toPrecision(idx+1+(period+ee)%period);
498        if(!o.match(/[Ee]/)) {
499            var fakee = Math.floor(Math.log(Math.abs(val))*Math.LOG10E);
500            if(o.indexOf(".") === -1) o = o[0] + "." + o.substr(1) + "E+" + (fakee - o.length+ee);
501            else o += "E+" + (fakee - ee);
502            o = o.replace(/\+-/,"-");
503        }
504        o = o.replace(/^([+-]?)(\d*)\.(\d*)[Ee]/,function($$,$1,$2,$3) { return $1 + $2 + $3.substr(0,(period+ee)%period) + "." + $3.substr(ee) + "E"; });
505    } else o = val.toExponential(idx);
506    if(fmt.match(/E\+00$/) && o.match(/e[+-]\d$/)) o = o.substr(0,o.length-1) + "0" + o[o.length-1];
507    if(fmt.match(/E\-/) && o.match(/e\+/)) o = o.replace(/e\+/,"e");
508    return o.replace("e","E");
509}
510function write_num_int(type, fmt, val) {
511    if(type.charCodeAt(0) === 40 && !fmt.match(closeparen)) {
512        var ffmt = fmt.replace(/\( */,"").replace(/ \)/,"").replace(/\)/,"");
513        if(val >= 0) return write_num_int('n', ffmt, val);
514        return '(' + write_num_int('n', ffmt, -val) + ')';
515    }
516    if(fmt.charCodeAt(fmt.length - 1) === 44) return write_num_cm2(type, fmt, val);
517    if(fmt.indexOf('%') !== -1) return write_num_pct2(type, fmt, val);
518    if(fmt.indexOf('E') !== -1) return write_num_exp2(fmt, val);
519    if(fmt.charCodeAt(0) === 36) return "$"+write_num_int(type,fmt.substr(fmt[1]==' '?2:1),val);
520    var o;
521    var r, ri, ff, aval = Math.abs(val), sign = val < 0 ? "-" : "";
522    if(fmt.match(/^00+$/)) return sign + pad0(aval,fmt.length);
523    if(fmt.match(/^[#?]+$/)) {
524        o = (""+val); if(val === 0) o = "";
525        return o.length > fmt.length ? o : hashq(fmt.substr(0,fmt.length-o.length)) + o;
526    }
527    if((r = fmt.match(frac1)) !== null) return write_num_f2(r, aval, sign);
528    if(fmt.match(/^#+0+$/) !== null) return sign + pad0(aval,fmt.length - fmt.indexOf("0"));
529    if((r = fmt.match(dec1)) !== null) {
530        o = (""+val).replace(/^([^\.]+)$/,"$1."+r[1]).replace(/\.$/,"."+r[1]).replace(/\.(\d*)$/,function($$, $1) { return "." + $1 + fill("0", r[1].length-$1.length); });
531        return fmt.indexOf("0.") !== -1 ? o : o.replace(/^0\./,".");
532    }
533    fmt = fmt.replace(/^#+([0.])/, "$1");
534    if((r = fmt.match(/^(0*)\.(#*)$/)) !== null) {
535        return sign + (""+aval).replace(/\.(\d*[1-9])0*$/,".$1").replace(/^(-?\d*)$/,"$1.").replace(/^0\./,r[1].length?"0.":".");
536    }
537    if((r = fmt.match(/^#,##0(\.?)$/)) !== null) return sign + commaify((""+aval));
538    if((r = fmt.match(/^#,##0\.([#0]*0)$/)) !== null) {
539        return val < 0 ? "-" + write_num_int(type, fmt, -val) : commaify((""+val)) + "." + fill('0',r[1].length);
540    }
541    if((r = fmt.match(/^#,#*,#0/)) !== null) return write_num_int(type,fmt.replace(/^#,#*,/,""),val);
542    if((r = fmt.match(/^([0#]+)(\\?-([0#]+))+$/)) !== null) {
543        o = _strrev(write_num_int(type, fmt.replace(/[\\-]/g,""), val));
544        ri = 0;
545        return _strrev(_strrev(fmt.replace(/\\/g,"")).replace(/[0#]/g,function(x){return ri<o.length?o[ri++]:x==='0'?'0':"";}));
546    }
547    if(fmt.match(phone) !== null) {
548        o = write_num_int(type, "##########", val);
549        return "(" + o.substr(0,3) + ") " + o.substr(3, 3) + "-" + o.substr(6);
550    }
551    var oa = "";
552    if((r = fmt.match(/^([#0?]+)( ?)\/( ?)([#0?]+)/)) !== null) {
553        ri = Math.min(r[4].length,7);
554        ff = frac(aval, Math.pow(10,ri)-1, false);
555        o = "" + sign;
556        oa = write_num("n", r[1], ff[1]);
557        if(oa[oa.length-1] == " ") oa = oa.substr(0,oa.length-1) + "0";
558        o += oa + r[2] + "/" + r[3];
559        oa = rpad_(ff[2],ri);
560        if(oa.length < r[4].length) oa = hashq(r[4].substr(r[4].length-oa.length)) + oa;
561        o += oa;
562        return o;
563    }
564    if((r = fmt.match(/^# ([#0?]+)( ?)\/( ?)([#0?]+)/)) !== null) {
565        ri = Math.min(Math.max(r[1].length, r[4].length),7);
566        ff = frac(aval, Math.pow(10,ri)-1, true);
567        return sign + (ff[0]||(ff[1] ? "" : "0")) + " " + (ff[1] ? pad_(ff[1],ri) + r[2] + "/" + r[3] + rpad_(ff[2],ri): fill(" ", 2*ri+1 + r[2].length + r[3].length));
568    }
569    if((r = fmt.match(/^[#0?]+$/)) !== null) {
570        o = "" + val;
571        if(fmt.length <= o.length) return o;
572        return hashq(fmt.substr(0,fmt.length-o.length)) + o;
573    }
574    if((r = fmt.match(/^([#0]+)\.([#0]+)$/)) !== null) {
575        o = "" + val.toFixed(Math.min(r[2].length,10)).replace(/([^0])0+$/,"$1");
576        ri = o.indexOf(".");
577        var lres = fmt.indexOf(".") - ri, rres = fmt.length - o.length - lres;
578        return hashq(fmt.substr(0,lres) + o + fmt.substr(fmt.length-rres));
579    }
580    if((r = fmt.match(/^00,000\.([#0]*0)$/)) !== null) {
581        return val < 0 ? "-" + write_num_int(type, fmt, -val) : commaify(""+val).replace(/^\d,\d{3}$/,"0$&").replace(/^\d*$/,function($$) { return "00," + ($$.length < 3 ? pad0(0,3-$$.length) : "") + $$; }) + "." + pad0(0,r[1].length);
582    }
583    switch(fmt) {
584        case "#,###": var x = commaify(""+aval); return x !== "0" ? sign + x : "";
585        default:
586    }
587    throw new Error("unsupported format |" + fmt + "|");
588}
589return function write_num(type, fmt, val) {
590    return (val|0) === val ? write_num_int(type, fmt, val) : write_num_flt(type, fmt, val);
591};})();
592function split_fmt(fmt) {
593    var out = [];
594    var in_str = false, cc;
595    for(var i = 0, j = 0; i < fmt.length; ++i) switch((cc=fmt.charCodeAt(i))) {
596        case 34: /* '"' */
597            in_str = !in_str; break;
598        case 95: case 42: case 92: /* '_' '*' '\\' */
599            ++i; break;
600        case 59: /* ';' */
601            out[out.length] = fmt.substr(j,i-j);
602            j = i+1;
603    }
604    out[out.length] = fmt.substr(j);
605    if(in_str === true) throw new Error("Format |" + fmt + "| unterminated string ");
606    return out;
607}
608SSF._split = split_fmt;
609var abstime = /\[[HhMmSs]*\]/;
610function eval_fmt(fmt, v, opts, flen) {
611    var out = [], o = "", i = 0, c = "", lst='t', q, dt, j, cc;
612    var hr='H';
613    /* Tokenize */
614    while(i < fmt.length) {
615        switch((c = fmt[i])) {
616            case 'G': /* General */
617                if(!isgeneral(fmt, i)) throw new Error('unrecognized character ' + c + ' in ' +fmt);
618                out[out.length] = {t:'G', v:'General'}; i+=7; break;
619            case '"': /* Literal text */
620                for(o="";(cc=fmt.charCodeAt(++i)) !== 34 && i < fmt.length;) o += String.fromCharCode(cc);
621                out[out.length] = {t:'t', v:o}; ++i; break;
622            case '\\': var w = fmt[++i], t = (w === "(" || w === ")") ? w : 't';
623                out[out.length] = {t:t, v:w}; ++i; break;
624            case '_': out[out.length] = {t:'t', v:" "}; i+=2; break;
625            case '@': /* Text Placeholder */
626                out[out.length] = {t:'T', v:v}; ++i; break;
627            case 'B': case 'b':
628                if(fmt[i+1] === "1" || fmt[i+1] === "2") {
629          if(dt==null) { dt=parse_date_code(v, opts, fmt[i+1] === "2"); if(dt==null) return ""; }
630                    out[out.length] = {t:'X', v:fmt.substr(i,2)}; lst = c; i+=2; break;
631                }
632                /* falls through */
633            case 'M': case 'D': case 'Y': case 'H': case 'S': case 'E':
634                c = c.toLowerCase();
635                /* falls through */
636            case 'm': case 'd': case 'y': case 'h': case 's': case 'e': case 'g':
637                if(v < 0) return "";
638                if(dt==null) { dt=parse_date_code(v, opts); if(dt==null) return ""; }
639                o = c; while(++i<fmt.length && fmt[i].toLowerCase() === c) o+=c;
640                if(c === 'm' && lst.toLowerCase() === 'h') c = 'M'; /* m = minute */
641                if(c === 'h') c = hr;
642                out[out.length] = {t:c, v:o}; lst = c; break;
643            case 'A':
644                q={t:c, v:"A"};
645                if(dt==null) dt=parse_date_code(v, opts);
646        if(fmt.substr(i, 3) === "A/P") { if(dt!=null) q.v = dt.H >= 12 ? "P" : "A"; q.t = 'T'; hr='h';i+=3;}
647        else if(fmt.substr(i,5) === "AM/PM") { if(dt!=null) q.v = dt.H >= 12 ? "PM" : "AM"; q.t = 'T'; i+=5; hr='h'; }
648                else { q.t = "t"; ++i; }
649                if(dt==null && q.t === 'T') return "";
650                out[out.length] = q; lst = c; break;
651            case '[':
652                o = c;
653                while(fmt[i++] !== ']' && i < fmt.length) o += fmt[i];
654                if(o.substr(-1) !== ']') throw 'unterminated "[" block: |' + o + '|';
655                if(o.match(abstime)) {
656                    if(dt==null) { dt=parse_date_code(v, opts); if(dt==null) return ""; }
657                    out[out.length] = {t:'Z', v:o.toLowerCase()};
658                } else { o=""; }
659                break;
660            /* Numbers */
661            case '.':
662                if(dt != null) {
663                    o = c; while((c=fmt[++i]) === "0") o += c;
664                    out[out.length] = {t:'s', v:o}; break;
665                }
666                /* falls through */
667            case '0': case '#':
668                o = c; while("0#?.,E+-%".indexOf(c=fmt[++i]) > -1 || c=='\\' && fmt[i+1] == "-" && "0#".indexOf(fmt[i+2])>-1) o += c;
669                out[out.length] = {t:'n', v:o}; break;
670            case '?':
671                o = c; while(fmt[++i] === c) o+=c;
672                q={t:c, v:o}; out[out.length] = q; lst = c; break;
673            case '*': ++i; if(fmt[i] == ' ' || fmt[i] == '*') ++i; break; // **
674            case '(': case ')': out[out.length] = {t:(flen===1?'t':c), v:c}; ++i; break;
675            case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
676                o = c; while("0123456789".indexOf(fmt[++i]) > -1) o+=fmt[i];
677                out[out.length] = {t:'D', v:o}; break;
678            case ' ': out[out.length] = {t:c, v:c}; ++i; break;
679            default:
680                if(",$-+/():!^&'~{}<>=€acfijklopqrtuvwxz".indexOf(c) === -1) throw new Error('unrecognized character ' + c + ' in ' + fmt);
681                out[out.length] = {t:'t', v:c}; ++i; break;
682        }
683    }
684    var bt = 0, ss0 = 0, ssm;
685    for(i=out.length-1, lst='t'; i >= 0; --i) {
686        switch(out[i].t) {
687            case 'h': case 'H': out[i].t = hr; lst='h'; if(bt < 1) bt = 1; break;
688            case 's':
689                if((ssm=out[i].v.match(/\.0+$/))) ss0=Math.max(ss0,ssm[0].length-1);
690                if(bt < 3) bt = 3;
691            /* falls through */
692            case 'd': case 'y': case 'M': case 'e': lst=out[i].t; break;
693            case 'm': if(lst === 's') { out[i].t = 'M'; if(bt < 2) bt = 2; } break;
694            case 'X': if(out[i].v === "B2");
695                break;
696            case 'Z':
697                if(bt < 1 && out[i].v.match(/[Hh]/)) bt = 1;
698                if(bt < 2 && out[i].v.match(/[Mm]/)) bt = 2;
699                if(bt < 3 && out[i].v.match(/[Ss]/)) bt = 3;
700        }
701    }
702    switch(bt) {
703        case 0: break;
704        case 1:
705            if(dt.u >= 0.5) { dt.u = 0; ++dt.S; }
706            if(dt.S >=  60) { dt.S = 0; ++dt.M; }
707            if(dt.M >=  60) { dt.M = 0; ++dt.H; }
708            break;
709        case 2:
710            if(dt.u >= 0.5) { dt.u = 0; ++dt.S; }
711            if(dt.S >=  60) { dt.S = 0; ++dt.M; }
712            break;
713    }
714    /* replace fields */
715    var nstr = "", jj;
716    for(i=0; i < out.length; ++i) {
717        switch(out[i].t) {
718            case 't': case 'T': case ' ': case 'D': break;
719            case 'X': out[i] = undefined; break;
720            case 'd': case 'm': case 'y': case 'h': case 'H': case 'M': case 's': case 'e': case 'b': case 'Z':
721                out[i].v = write_date(out[i].t.charCodeAt(0), out[i].v, dt, ss0);
722                out[i].t = 't'; break;
723            case 'n': case '(': case '?':
724                jj = i+1;
725                while(out[jj] != null && (
726                    (c=out[jj].t) === "?" || c === "D" ||
727                    (c === " " || c === "t") && out[jj+1] != null && (out[jj+1].t === '?' || out[jj+1].t === "t" && out[jj+1].v === '/') ||
728                    out[i].t === '(' && (c === ' ' || c === 'n' || c === ')') ||
729                    c === 't' && (out[jj].v === '/' || '$€'.indexOf(out[jj].v) > -1 || out[jj].v === ' ' && out[jj+1] != null && out[jj+1].t == '?')
730                )) {
731                    out[i].v += out[jj].v;
732                    out[jj] = undefined; ++jj;
733                }
734                nstr += out[i].v;
735                i = jj-1; break;
736            case 'G': out[i].t = 't'; out[i].v = general_fmt(v,opts); break;
737        }
738    }
739    var vv = "", myv, ostr;
740    if(nstr.length > 0) {
741        myv = (v<0&&nstr.charCodeAt(0) === 45 ? -v : v); /* '-' */
742        ostr = write_num(nstr.charCodeAt(0) === 40 ? '(' : 'n', nstr, myv); /* '(' */
743        jj=ostr.length-1;
744        var decpt = out.length;
745        for(i=0; i < out.length; ++i) if(out[i] != null && out[i].v.indexOf(".") > -1) { decpt = i; break; }
746        var lasti=out.length;
747        if(decpt === out.length && ostr.indexOf("E") === -1) {
748            for(i=out.length-1; i>= 0;--i) {
749                if(out[i] == null || 'n?('.indexOf(out[i].t) === -1) continue;
750                if(jj>=out[i].v.length-1) { jj -= out[i].v.length; out[i].v = ostr.substr(jj+1, out[i].v.length); }
751                else if(jj < 0) out[i].v = "";
752                else { out[i].v = ostr.substr(0, jj+1); jj = -1; }
753                out[i].t = 't';
754                lasti = i;
755            }
756            if(jj>=0 && lasti<out.length) out[lasti].v = ostr.substr(0,jj+1) + out[lasti].v;
757        }
758        else if(decpt !== out.length && ostr.indexOf("E") === -1) {
759            jj = ostr.indexOf(".")-1;
760            for(i=decpt; i>= 0; --i) {
761                if(out[i] == null || 'n?('.indexOf(out[i].t) === -1) continue;
762                j=out[i].v.indexOf(".")>-1&&i===decpt?out[i].v.indexOf(".")-1:out[i].v.length-1;
763                vv = out[i].v.substr(j+1);
764                for(; j>=0; --j) {
765                    if(jj>=0 && (out[i].v[j] === "0" || out[i].v[j] === "#")) vv = ostr[jj--] + vv;
766                }
767                out[i].v = vv;
768                out[i].t = 't';
769                lasti = i;
770            }
771            if(jj>=0 && lasti<out.length) out[lasti].v = ostr.substr(0,jj+1) + out[lasti].v;
772            jj = ostr.indexOf(".")+1;
773            for(i=decpt; i<out.length; ++i) {
774                if(out[i] == null || 'n?('.indexOf(out[i].t) === -1 && i !== decpt ) continue;
775                j=out[i].v.indexOf(".")>-1&&i===decpt?out[i].v.indexOf(".")+1:0;
776                vv = out[i].v.substr(0,j);
777                for(; j<out[i].v.length; ++j) {
778                    if(jj<ostr.length) vv += ostr[jj++];
779                }
780                out[i].v = vv;
781                out[i].t = 't';
782                lasti = i;
783            }
784        }
785    }
786    for(i=0; i<out.length; ++i) if(out[i] != null && 'n(?'.indexOf(out[i].t)>-1) {
787        myv = (flen >1 && v < 0 && i>0 && out[i-1].v === "-" ? -v:v);
788        out[i].v = write_num(out[i].t, out[i].v, myv);
789        out[i].t = 't';
790    }
791    var retval = "";
792    for(i=0; i !== out.length; ++i) if(out[i] != null) retval += out[i].v;
793    return retval;
794}
795SSF._eval = eval_fmt;
796var cfregex = /\[[=<>]/;
797var cfregex2 = /\[([=<>]*)(-?\d+\.?\d*)\]/;
798function chkcond(v, rr) {
799    if(rr == null) return false;
800    var thresh = parseFloat(rr[2]);
801    switch(rr[1]) {
802        case "=":  if(v == thresh) return true; break;
803        case ">":  if(v >  thresh) return true; break;
804        case "<":  if(v <  thresh) return true; break;
805        case "<>": if(v != thresh) return true; break;
806        case ">=": if(v >= thresh) return true; break;
807        case "<=": if(v <= thresh) return true; break;
808    }
809    return false;
810}
811function choose_fmt(f, v) {
812    var fmt = split_fmt(f);
813    var l = fmt.length, lat = fmt[l-1].indexOf("@");
814    if(l<4 && lat>-1) --l;
815    if(fmt.length > 4) throw "cannot find right format for |" + fmt + "|";
816    if(typeof v !== "number") return [4, fmt.length === 4 || lat>-1?fmt[fmt.length-1]:"@"];
817    switch(fmt.length) {
818        case 1: fmt = lat>-1 ? ["General", "General", "General", fmt[0]] : [fmt[0], fmt[0], fmt[0], "@"]; break;
819        case 2: fmt = lat>-1 ? [fmt[0], fmt[0], fmt[0], fmt[1]] : [fmt[0], fmt[1], fmt[0], "@"]; break;
820        case 3: fmt = lat>-1 ? [fmt[0], fmt[1], fmt[0], fmt[2]] : [fmt[0], fmt[1], fmt[2], "@"]; break;
821        case 4: break;
822    }
823    var ff = v > 0 ? fmt[0] : v < 0 ? fmt[1] : fmt[2];
824    if(fmt[0].indexOf("[") === -1 && fmt[1].indexOf("[") === -1) return [l, ff];
825    if(fmt[0].match(cfregex) != null || fmt[1].match(cfregex) != null) {
826        var m1 = fmt[0].match(cfregex2);
827        var m2 = fmt[1].match(cfregex2);
828        return chkcond(v, m1) ? [l, fmt[0]] : chkcond(v, m2) ? [l, fmt[1]] : [l, fmt[m1 != null && m2 != null ? 2 : 1]];
829    }
830    return [l, ff];
831}
832function format(fmt,v,o) {
833    fixopts(o != null ? o : (o=[]));
834    var sfmt = "";
835    switch(typeof fmt) {
836        case "string": sfmt = fmt; break;
837        case "number": sfmt = (o.table != null ? o.table : table_fmt)[fmt]; break;
838    }
839    if(isgeneral(sfmt,0)) return general_fmt(v, o);
840    var f = choose_fmt(sfmt, v);
841    if(isgeneral(f[1])) return general_fmt(v, o);
842    if(v === true) v = "TRUE"; else if(v === false) v = "FALSE";
843    else if(v === "" || v == null) return "";
844    return eval_fmt(f[1], v, o, f[0]);
845}
846SSF._table = table_fmt;
847SSF.load = function load_entry(fmt, idx) { table_fmt[idx] = fmt; };
848SSF.format = format;
849SSF.get_table = function get_table() { return table_fmt; };
850SSF.load_table = function load_table(tbl) { for(var i=0; i!=0x0188; ++i) if(tbl[i] !== undefined) SSF.load(tbl[i], i); };
851};
852make_ssf(SSF);
853/* map from xlml named formats to SSF TODO: localize */
854var XLMLFormatMap = {
855    "General Number": "General",
856    "General Date": SSF._table[22],
857    "Long Date": "dddd, mmmm dd, yyyy",
858    "Medium Date": SSF._table[15],
859    "Short Date": SSF._table[14],
860    "Long Time": SSF._table[19],
861    "Medium Time": SSF._table[18],
862    "Short Time": SSF._table[20],
863    "Currency": '"$"#,##0.00_);[Red]\\("$"#,##0.00\\)',
864    "Fixed": SSF._table[2],
865    "Standard": SSF._table[4],
866    "Percent": SSF._table[10],
867    "Scientific": SSF._table[11],
868    "Yes/No": '"Yes";"Yes";"No";@',
869    "True/False": '"True";"True";"False";@',
870    "On/Off": '"Yes";"Yes";"No";@'
871};
872
873var DO_NOT_EXPORT_CFB = true;
874/* cfb.js (C) 2013-2014 SheetJS -- http://sheetjs.com */
875/* vim: set ts=2: */
876/*jshint eqnull:true */
877
878/* [MS-CFB] v20130118 */
879var CFB = (function _CFB(){
880var exports = {};
881exports.version = '0.10.2';
882function parse(file) {
883var mver = 3; // major version
884var ssz = 512; // sector size
885var nmfs = 0; // number of mini FAT sectors
886var ndfs = 0; // number of DIFAT sectors
887var dir_start = 0; // first directory sector location
888var minifat_start = 0; // first mini FAT sector location
889var difat_start = 0; // first mini FAT sector location
890
891var fat_addrs = []; // locations of FAT sectors
892
893/* [MS-CFB] 2.2 Compound File Header */
894var blob = file.slice(0,512);
895prep_blob(blob, 0);
896
897/* major version */
898var mv = check_get_mver(blob);
899mver = mv[0];
900switch(mver) {
901    case 3: ssz = 512; break; case 4: ssz = 4096; break;
902    default: throw "Major Version: Expected 3 or 4 saw " + mver;
903}
904
905/* reprocess header */
906if(ssz !== 512) { blob = file.slice(0,ssz); prep_blob(blob, 28 /* blob.l */); }
907/* Save header for final object */
908var header = file.slice(0,ssz);
909
910check_shifts(blob, mver);
911
912// Number of Directory Sectors
913var nds = blob.read_shift(4, 'i');
914if(mver === 3 && nds !== 0) throw '# Directory Sectors: Expected 0 saw ' + nds;
915
916// Number of FAT Sectors
917//var nfs = blob.read_shift(4, 'i');
918blob.l += 4;
919
920// First Directory Sector Location
921dir_start = blob.read_shift(4, 'i');
922
923// Transaction Signature
924blob.l += 4;
925
926// Mini Stream Cutoff Size
927blob.chk('00100000', 'Mini Stream Cutoff Size: ');
928
929// First Mini FAT Sector Location
930minifat_start = blob.read_shift(4, 'i');
931
932// Number of Mini FAT Sectors
933nmfs = blob.read_shift(4, 'i');
934
935// First DIFAT sector location
936difat_start = blob.read_shift(4, 'i');
937
938// Number of DIFAT Sectors
939ndfs = blob.read_shift(4, 'i');
940
941// Grab FAT Sector Locations
942for(var q, j = 0; j < 109; ++j) { /* 109 = (512 - blob.l)>>>2; */
943    q = blob.read_shift(4, 'i');
944    if(q<0) break;
945    fat_addrs[j] = q;
946}
947
948/** Break the file up into sectors */
949var sectors = sectorify(file, ssz);
950
951sleuth_fat(difat_start, ndfs, sectors, ssz, fat_addrs);
952
953/** Chains */
954var sector_list = make_sector_list(sectors, dir_start, fat_addrs, ssz);
955
956sector_list[dir_start].name = "!Directory";
957if(nmfs > 0 && minifat_start !== ENDOFCHAIN) sector_list[minifat_start].name = "!MiniFAT";
958sector_list[fat_addrs[0]].name = "!FAT";
959sector_list.fat_addrs = fat_addrs;
960sector_list.ssz = ssz;
961
962/* [MS-CFB] 2.6.1 Compound File Directory Entry */
963var files = {}, Paths = [], FileIndex = [], FullPaths = [], FullPathDir = {};
964read_directory(dir_start, sector_list, sectors, Paths, nmfs, files, FileIndex);
965
966build_full_paths(FileIndex, FullPathDir, FullPaths, Paths);
967
968var root_name = Paths.shift();
969Paths.root = root_name;
970
971/* [MS-CFB] 2.6.4 (Unicode 3.0.1 case conversion) */
972var find_path = make_find_path(FullPaths, Paths, FileIndex, files, root_name);
973
974return {
975    raw: {header: header, sectors: sectors},
976    FileIndex: FileIndex,
977    FullPaths: FullPaths,
978    FullPathDir: FullPathDir,
979    find: find_path
980};
981} // parse
982
983/* [MS-CFB] 2.2 Compound File Header -- read up to major version */
984function check_get_mver(blob) {
985    // header signature 8
986    blob.chk(HEADER_SIGNATURE, 'Header Signature: ');
987
988    // clsid 16
989    blob.chk(HEADER_CLSID, 'CLSID: ');
990
991    // minor version 2
992    var mver = blob.read_shift(2, 'u');
993
994    return [blob.read_shift(2,'u'), mver];
995}
996function check_shifts(blob, mver) {
997    var shift = 0x09;
998
999    // Byte Order
1000    blob.chk('feff', 'Byte Order: ');
1001
1002    // Sector Shift
1003    switch((shift = blob.read_shift(2))) {
1004        case 0x09: if(mver !== 3) throw 'MajorVersion/SectorShift Mismatch'; break;
1005        case 0x0c: if(mver !== 4) throw 'MajorVersion/SectorShift Mismatch'; break;
1006        default: throw 'Sector Shift: Expected 9 or 12 saw ' + shift;
1007    }
1008
1009    // Mini Sector Shift
1010    blob.chk('0600', 'Mini Sector Shift: ');
1011
1012    // Reserved
1013    blob.chk('000000000000', 'Reserved: ');
1014}
1015
1016/** Break the file up into sectors */
1017function sectorify(file, ssz) {
1018    var nsectors = Math.ceil(file.length/ssz)-1;
1019    var sectors = new Array(nsectors);
1020    for(var i=1; i < nsectors; ++i) sectors[i-1] = file.slice(i*ssz,(i+1)*ssz);
1021    sectors[nsectors-1] = file.slice(nsectors*ssz);
1022    return sectors;
1023}
1024
1025/* [MS-CFB] 2.6.4 Red-Black Tree */
1026function build_full_paths(FI, FPD, FP, Paths) {
1027    var i = 0, L = 0, R = 0, C = 0, j = 0, pl = Paths.length;
1028    var dad = new Array(pl), q = new Array(pl);
1029
1030    for(; i < pl; ++i) { dad[i]=q[i]=i; FP[i]=Paths[i]; }
1031
1032    for(; j < q.length; ++j) {
1033        i = q[j];
1034        L = FI[i].L; R = FI[i].R; C = FI[i].C;
1035        if(dad[i] === i) {
1036            if(L !== -1 /*NOSTREAM*/ && dad[L] !== L) dad[i] = dad[L];
1037            if(R !== -1 && dad[R] !== R) dad[i] = dad[R];
1038        }
1039        if(C !== -1 /*NOSTREAM*/) dad[C] = i;
1040        if(L !== -1) { dad[L] = dad[i]; q.push(L); }
1041        if(R !== -1) { dad[R] = dad[i]; q.push(R); }
1042    }
1043    for(i=1; i !== pl; ++i) if(dad[i] === i) {
1044        if(R !== -1 /*NOSTREAM*/ && dad[R] !== R) dad[i] = dad[R];
1045        else if(L !== -1 && dad[L] !== L) dad[i] = dad[L];
1046    }
1047
1048    for(i=1; i < pl; ++i) {
1049        if(FI[i].type === 0 /* unknown */) continue;
1050        j = dad[i];
1051        if(j === 0) FP[i] = FP[0] + "/" + FP[i];
1052        else while(j !== 0) {
1053            FP[i] = FP[j] + "/" + FP[i];
1054            j = dad[j];
1055        }
1056        dad[i] = 0;
1057    }
1058
1059    FP[0] += "/";
1060    for(i=1; i < pl; ++i) {
1061        if(FI[i].type !== 2 /* stream */) FP[i] += "/";
1062        FPD[FP[i]] = FI[i];
1063    }
1064}
1065
1066/* [MS-CFB] 2.6.4 */
1067function make_find_path(FullPaths, Paths, FileIndex, files, root_name) {
1068    var UCFullPaths = new Array(FullPaths.length);
1069    var UCPaths = new Array(Paths.length), i;
1070    for(i = 0; i < FullPaths.length; ++i) UCFullPaths[i] = FullPaths[i].toUpperCase().replace(chr0,'').replace(chr1,'!');
1071    for(i = 0; i < Paths.length; ++i) UCPaths[i] = Paths[i].toUpperCase().replace(chr0,'').replace(chr1,'!');
1072    return function find_path(path) {
1073        var k;
1074        if(path.charCodeAt(0) === 47 /* "/" */) { k=true; path = root_name + path; }
1075        else k = path.indexOf("/") !== -1;
1076        var UCPath = path.toUpperCase().replace(chr0,'').replace(chr1,'!');
1077        var w = k === true ? UCFullPaths.indexOf(UCPath) : UCPaths.indexOf(UCPath);
1078        if(w === -1) return null;
1079        return k === true ? FileIndex[w] : files[Paths[w]];
1080    };
1081}
1082
1083/** Chase down the rest of the DIFAT chain to build a comprehensive list
1084    DIFAT chains by storing the next sector number as the last 32 bytes */
1085function sleuth_fat(idx, cnt, sectors, ssz, fat_addrs) {
1086    var q;
1087    if(idx === ENDOFCHAIN) {
1088        if(cnt !== 0) throw "DIFAT chain shorter than expected";
1089    } else if(idx !== -1 /*FREESECT*/) {
1090        var sector = sectors[idx], m = (ssz>>>2)-1;
1091        for(var i = 0; i < m; ++i) {
1092            if((q = __readInt32LE(sector,i*4)) === ENDOFCHAIN) break;
1093            fat_addrs.push(q);
1094        }
1095        sleuth_fat(__readInt32LE(sector,ssz-4),cnt - 1, sectors, ssz, fat_addrs);
1096    }
1097}
1098
1099/** Follow the linked list of sectors for a given starting point */
1100function get_sector_list(sectors, start, fat_addrs, ssz, chkd) {
1101    var sl = sectors.length;
1102    var buf, buf_chain;
1103    if(!chkd) chkd = new Array(sl);
1104    var modulus = ssz - 1, j, jj;
1105    buf = [];
1106    buf_chain = [];
1107    for(j=start; j>=0;) {
1108        chkd[j] = true;
1109        buf[buf.length] = j;
1110        buf_chain.push(sectors[j]);
1111        var addr = fat_addrs[Math.floor(j*4/ssz)];
1112        jj = ((j*4) & modulus);
1113        if(ssz < 4 + jj) throw "FAT boundary crossed: " + j + " 4 "+ssz;
1114        j = __readInt32LE(sectors[addr], jj);
1115    }
1116    return {nodes: buf, data:__toBuffer([buf_chain])};
1117}
1118
1119/** Chase down the sector linked lists */
1120function make_sector_list(sectors, dir_start, fat_addrs, ssz) {
1121    var sl = sectors.length, sector_list = new Array(sl);
1122    var chkd = new Array(sl), buf, buf_chain;
1123    var modulus = ssz - 1, i, j, k, jj;
1124    for(i=0; i < sl; ++i) {
1125        buf = [];
1126        k = (i + dir_start); if(k >= sl) k-=sl;
1127        if(chkd[k] === true) continue;
1128        buf_chain = [];
1129        for(j=k; j>=0;) {
1130            chkd[j] = true;
1131            buf[buf.length] = j;
1132            buf_chain.push(sectors[j]);
1133            var addr = fat_addrs[Math.floor(j*4/ssz)];
1134            jj = ((j*4) & modulus);
1135            if(ssz < 4 + jj) throw "FAT boundary crossed: " + j + " 4 "+ssz;
1136            j = __readInt32LE(sectors[addr], jj);
1137        }
1138        sector_list[k] = {nodes: buf, data:__toBuffer([buf_chain])};
1139    }
1140    return sector_list;
1141}
1142
1143/* [MS-CFB] 2.6.1 Compound File Directory Entry */
1144function read_directory(dir_start, sector_list, sectors, Paths, nmfs, files, FileIndex) {
1145    var blob;
1146    var minifat_store = 0, pl = (Paths.length?2:0);
1147    var sector = sector_list[dir_start].data;
1148    var i = 0, namelen = 0, name, o, ctime, mtime;
1149    for(; i < sector.length; i+= 128) {
1150        blob = sector.slice(i, i+128);
1151        prep_blob(blob, 64);
1152        namelen = blob.read_shift(2);
1153        if(namelen === 0) continue;
1154        name = __utf16le(blob,0,namelen-pl);
1155        Paths.push(name);
1156        o = {
1157            name:  name,
1158            type:  blob.read_shift(1),
1159            color: blob.read_shift(1),
1160            L:     blob.read_shift(4, 'i'),
1161            R:     blob.read_shift(4, 'i'),
1162            C:     blob.read_shift(4, 'i'),
1163            clsid: blob.read_shift(16),
1164            state: blob.read_shift(4, 'i')
1165        };
1166        ctime = blob.read_shift(2) + blob.read_shift(2) + blob.read_shift(2) + blob.read_shift(2);
1167        if(ctime !== 0) {
1168            o.ctime = ctime; o.ct = read_date(blob, blob.l-8);
1169        }
1170        mtime = blob.read_shift(2) + blob.read_shift(2) + blob.read_shift(2) + blob.read_shift(2);
1171        if(mtime !== 0) {
1172            o.mtime = mtime; o.mt = read_date(blob, blob.l-8);
1173        }
1174        o.start = blob.read_shift(4, 'i');
1175        o.size = blob.read_shift(4, 'i');
1176        if(o.type === 5) { /* root */
1177            minifat_store = o.start;
1178            if(nmfs > 0 && minifat_store !== ENDOFCHAIN) sector_list[minifat_store].name = "!StreamData";
1179            /*minifat_size = o.size;*/
1180        } else if(o.size >= 4096 /* MSCSZ */) {
1181            o.storage = 'fat';
1182            if(sector_list[o.start] === undefined) sector_list[o.start] = get_sector_list(sectors, o.start, sector_list.fat_addrs, sector_list.ssz);
1183            sector_list[o.start].name = o.name;
1184            o.content = sector_list[o.start].data.slice(0,o.size);
1185            prep_blob(o.content, 0);
1186        } else {
1187            o.storage = 'minifat';
1188            if(minifat_store !== ENDOFCHAIN && o.start !== ENDOFCHAIN) {
1189                o.content = sector_list[minifat_store].data.slice(o.start*MSSZ,o.start*MSSZ+o.size);
1190                prep_blob(o.content, 0);
1191            }
1192        }
1193        files[name] = o;
1194        FileIndex.push(o);
1195    }
1196}
1197
1198function read_date(blob, offset) {
1199    return new Date(( ( (__readUInt32LE(blob,offset+4)/1e7)*Math.pow(2,32)+__readUInt32LE(blob,offset)/1e7 ) - 11644473600)*1000);
1200}
1201
1202var fs;
1203function readFileSync(filename, options) {
1204    if(fs === undefined) fs = require('fs');
1205    return parse(fs.readFileSync(filename), options);
1206}
1207
1208function readSync(blob, options) {
1209    switch(options !== undefined && options.type !== undefined ? options.type : "base64") {
1210        case "file": return readFileSync(blob, options);
1211        case "base64": return parse(s2a(Base64.decode(blob)), options);
1212        case "binary": return parse(s2a(blob), options);
1213    }
1214    return parse(blob);
1215}
1216
1217/** CFB Constants */
1218var MSSZ = 64; /* Mini Sector Size = 1<<6 */
1219//var MSCSZ = 4096; /* Mini Stream Cutoff Size */
1220/* 2.1 Compound File Sector Numbers and Types */
1221var ENDOFCHAIN = -2;
1222/* 2.2 Compound File Header */
1223var HEADER_SIGNATURE = 'd0cf11e0a1b11ae1';
1224var HEADER_CLSID = '00000000000000000000000000000000';
1225var consts = {
1226    /* 2.1 Compund File Sector Numbers and Types */
1227    MAXREGSECT: -6,
1228    DIFSECT: -4,
1229    FATSECT: -3,
1230    ENDOFCHAIN: ENDOFCHAIN,
1231    FREESECT: -1,
1232    /* 2.2 Compound File Header */
1233    HEADER_SIGNATURE: HEADER_SIGNATURE,
1234    HEADER_MINOR_VERSION: '3e00',
1235    MAXREGSID: -6,
1236    NOSTREAM: -1,
1237    HEADER_CLSID: HEADER_CLSID,
1238    /* 2.6.1 Compound File Directory Entry */
1239    EntryTypes: ['unknown','storage','stream','lockbytes','property','root']
1240};
1241
1242exports.read = readSync;
1243exports.parse = parse;
1244exports.utils = {
1245    ReadShift: ReadShift,
1246    CheckField: CheckField,
1247    prep_blob: prep_blob,
1248    bconcat: bconcat,
1249    consts: consts
1250};
1251
1252return exports;
1253})();
1254
1255if(typeof require !== 'undefined' && typeof module !== 'undefined' && typeof DO_NOT_EXPORT_CFB === 'undefined') { module.exports = CFB; }
1256function isval(x) { return x !== undefined && x !== null; }
1257
1258function keys(o) { return Object.keys(o); }
1259
1260function evert_key(obj, key) {
1261    var o = [], K = keys(obj);
1262    for(var i = 0; i !== K.length; ++i) o[obj[K[i]][key]] = K[i];
1263    return o;
1264}
1265
1266function evert(obj) {
1267    var o = [], K = keys(obj);
1268    for(var i = 0; i !== K.length; ++i) o[obj[K[i]]] = K[i];
1269    return o;
1270}
1271
1272function evert_num(obj) {
1273    var o = [], K = keys(obj);
1274    for(var i = 0; i !== K.length; ++i) o[obj[K[i]]] = parseInt(K[i],10);
1275    return o;
1276}
1277
1278function evert_arr(obj) {
1279    var o = [], K = keys(obj);
1280    for(var i = 0; i !== K.length; ++i) {
1281        if(o[obj[K[i]]] == null) o[obj[K[i]]] = [];
1282        o[obj[K[i]]].push(K[i]);
1283    }
1284    return o;
1285}
1286
1287/* TODO: date1904 logic */
1288function datenum(v, date1904) {
1289    if(date1904) v+=1462;
1290    var epoch = Date.parse(v);
1291    return (epoch + 2209161600000) / (24 * 60 * 60 * 1000);
1292}
1293
1294function cc2str(arr) {
1295    var o = "";
1296    for(var i = 0; i != arr.length; ++i) o += String.fromCharCode(arr[i]);
1297    return o;
1298}
1299
1300function getdata(data) {
1301    if(!data) return null;
1302    if(data.name.substr(-4) === ".bin") {
1303        if(data.data) return char_codes(data.data);
1304        if(data.asNodeBuffer && has_buf) return data.asNodeBuffer();
1305        if(data._data && data._data.getContent) return Array.prototype.slice.call(data._data.getContent());
1306    } else {
1307        if(data.data) return data.name.substr(-4) !== ".bin" ? debom_xml(data.data) : char_codes(data.data);
1308        if(data.asNodeBuffer && has_buf) return debom_xml(data.asNodeBuffer().toString('binary'));
1309        if(data.asBinary) return debom_xml(data.asBinary());
1310        if(data._data && data._data.getContent) return debom_xml(cc2str(Array.prototype.slice.call(data._data.getContent(),0)));
1311    }
1312    return null;
1313}
1314
1315function safegetzipfile(zip, file) {
1316    var f = file; if(zip.files[f]) return zip.files[f];
1317    f = file.toLowerCase(); if(zip.files[f]) return zip.files[f];
1318    f = f.replace(/\//g,'\\'); if(zip.files[f]) return zip.files[f];
1319    return null;
1320}
1321
1322function getzipfile(zip, file) {
1323    var o = safegetzipfile(zip, file);
1324    if(o == null) throw new Error("Cannot find file " + file + " in zip");
1325    return o;
1326}
1327
1328function getzipdata(zip, file, safe) {
1329    if(!safe) return getdata(getzipfile(zip, file));
1330    if(!file) return null;
1331    try { return getzipdata(zip, file); } catch(e) { return null; }
1332}
1333
1334var _fs, jszip;
1335if(typeof JSZip !== 'undefined') jszip = JSZip;
1336if (typeof exports !== 'undefined') {
1337    if (typeof module !== 'undefined' && module.exports) {
1338        if(has_buf && typeof jszip === 'undefined') jszip = require('js'+'zip');
1339        if(typeof jszip === 'undefined') jszip = require('./js'+'zip').JSZip;
1340        _fs = require('f'+'s');
1341    }
1342}
1343var attregexg=/([\w:]+)=((?:")([^"]*)(?:")|(?:')([^']*)(?:'))/g;
1344var tagregex=/<[^>]*>/g;
1345var nsregex=/<\w*:/, nsregex2 = /<(\/?)\w+:/;
1346function parsexmltag(tag, skip_root) {
1347    var z = [];
1348    var eq = 0, c = 0;
1349    for(; eq !== tag.length; ++eq) if((c = tag.charCodeAt(eq)) === 32 || c === 10 || c === 13) break;
1350    if(!skip_root) z[0] = tag.substr(0, eq);
1351    if(eq === tag.length) return z;
1352    var m = tag.match(attregexg), j=0, w="", v="", i=0, q="", cc="";
1353    if(m) for(i = 0; i != m.length; ++i) {
1354        cc = m[i];
1355        for(c=0; c != cc.length; ++c) if(cc.charCodeAt(c) === 61) break;
1356        q = cc.substr(0,c); v = cc.substring(c+2, cc.length-1);
1357        for(j=0;j!=q.length;++j) if(q.charCodeAt(j) === 58) break;
1358        if(j===q.length) z[q] = v;
1359        else z[(j===5 && q.substr(0,5)==="xmlns"?"xmlns":"")+q.substr(j+1)] = v;
1360    }
1361    return z;
1362}
1363function strip_ns(x) { return x.replace(nsregex2, "<$1"); }
1364
1365var encodings = {
1366    '&quot;': '"',
1367    '&apos;': "'",
1368    '&gt;': '>',
1369    '&lt;': '<',
1370    '&amp;': '&'
1371};
1372var rencoding = evert(encodings);
1373var rencstr = "&<>'\"".split("");
1374
1375// TODO: CP remap (need to read file version to determine OS)
1376var unescapexml = (function() {
1377    var encregex = /&[a-z]*;/g, coderegex = /_x([\da-fA-F]+)_/g;
1378    return function unescapexml(text){
1379        var s = text + '';
1380        return s.replace(encregex, function($$) { return encodings[$$]; }).replace(coderegex,function(m,c) {return String.fromCharCode(parseInt(c,16));});
1381    };
1382})();
1383
1384var decregex=/[&<>'"]/g, charegex = /[\u0000-\u0008\u000b-\u001f]/g;
1385function escapexml(text){
1386    var s = text + '';
1387    return s.replace(decregex, function(y) { return rencoding[y]; }).replace(charegex,function(s) { return "_x" + ("000"+s.charCodeAt(0).toString(16)).substr(-4) + "_";});
1388}
1389
1390/* TODO: handle codepages */
1391var xlml_fixstr = (function() {
1392    var entregex = /&#(\d+);/g;
1393    function entrepl($$,$1) { return String.fromCharCode(parseInt($1,10)); }
1394    return function xlml_fixstr(str) { return str.replace(entregex,entrepl); };
1395})();
1396
1397function parsexmlbool(value, tag) {
1398    switch(value) {
1399        case '1': case 'true': case 'TRUE': return true;
1400        /* case '0': case 'false': case 'FALSE':*/
1401        default: return false;
1402    }
1403}
1404
1405var utf8read = function utf8reada(orig) {
1406    var out = "", i = 0, c = 0, d = 0, e = 0, f = 0, w = 0;
1407    while (i < orig.length) {
1408        c = orig.charCodeAt(i++);
1409        if (c < 128) { out += String.fromCharCode(c); continue; }
1410        d = orig.charCodeAt(i++);
1411        if (c>191 && c<224) { out += String.fromCharCode(((c & 31) << 6) | (d & 63)); continue; }
1412        e = orig.charCodeAt(i++);
1413        if (c < 240) { out += String.fromCharCode(((c & 15) << 12) | ((d & 63) << 6) | (e & 63)); continue; }
1414        f = orig.charCodeAt(i++);
1415        w = (((c & 7) << 18) | ((d & 63) << 12) | ((e & 63) << 6) | (f & 63))-65536;
1416        out += String.fromCharCode(0xD800 + ((w>>>10)&1023));
1417        out += String.fromCharCode(0xDC00 + (w&1023));
1418    }
1419    return out;
1420};
1421
1422
1423if(has_buf) {
1424    var utf8readb = function utf8readb(data) {
1425        var out = new Buffer(2*data.length), w, i, j = 1, k = 0, ww=0, c;
1426        for(i = 0; i < data.length; i+=j) {
1427            j = 1;
1428            if((c=data.charCodeAt(i)) < 128) w = c;
1429            else if(c < 224) { w = (c&31)*64+(data.charCodeAt(i+1)&63); j=2; }
1430            else if(c < 240) { w=(c&15)*4096+(data.charCodeAt(i+1)&63)*64+(data.charCodeAt(i+2)&63); j=3; }
1431            else { j = 4;
1432                w = (c & 7)*262144+(data.charCodeAt(i+1)&63)*4096+(data.charCodeAt(i+2)&63)*64+(data.charCodeAt(i+3)&63);
1433                w -= 65536; ww = 0xD800 + ((w>>>10)&1023); w = 0xDC00 + (w&1023);
1434            }
1435            if(ww !== 0) { out[k++] = ww&255; out[k++] = ww>>>8; ww = 0; }
1436            out[k++] = w%256; out[k++] = w>>>8;
1437        }
1438        out.length = k;
1439        return out.toString('ucs2');
1440    };
1441    var corpus = "foo bar baz\u00e2\u0098\u0083\u00f0\u009f\u008d\u00a3";
1442    if(utf8read(corpus) == utf8readb(corpus)) utf8read = utf8readb;
1443    var utf8readc = function utf8readc(data) { return Buffer(data, 'binary').toString('utf8'); };
1444    if(utf8read(corpus) == utf8readc(corpus)) utf8read = utf8readc;
1445}
1446
1447// matches <foo>...</foo> extracts content
1448var matchtag = (function() {
1449    var mtcache = {};
1450    return function matchtag(f,g) {
1451        var t = f+"|"+g;
1452        if(mtcache[t] !== undefined) return mtcache[t];
1453        return (mtcache[t] = new RegExp('<(?:\\w+:)?'+f+'(?: xml:space="preserve")?(?:[^>]*)>([^\u2603]*)</(?:\\w+:)?'+f+'>',(g||"")));
1454    };
1455})();
1456
1457var vtregex = (function(){ var vt_cache = {};
1458    return function vt_regex(bt) {
1459        if(vt_cache[bt] !== undefined) return vt_cache[bt];
1460        return (vt_cache[bt] = new RegExp("<vt:" + bt + ">(.*?)</vt:" + bt + ">", 'g') );
1461};})();
1462var vtvregex = /<\/?vt:variant>/g, vtmregex = /<vt:([^>]*)>(.*)</;
1463function parseVector(data) {
1464    var h = parsexmltag(data);
1465
1466    var matches = data.match(vtregex(h.baseType))||[];
1467    if(matches.length != h.size) throw "unexpected vector length " + matches.length + " != " + h.size;
1468    var res = [];
1469    matches.forEach(function(x) {
1470        var v = x.replace(vtvregex,"").match(vtmregex);
1471        res.push({v:v[2], t:v[1]});
1472    });
1473    return res;
1474}
1475
1476var wtregex = /(^\s|\s$|\n)/;
1477function writetag(f,g) {return '<' + f + (g.match(wtregex)?' xml:space="preserve"' : "") + '>' + g + '</' + f + '>';}
1478
1479function wxt_helper(h) { return keys(h).map(function(k) { return " " + k + '="' + h[k] + '"';}).join(""); }
1480function writextag(f,g,h) { return '<' + f + (isval(h) ? wxt_helper(h) : "") + (isval(g) ? (g.match(wtregex)?' xml:space="preserve"' : "") + '>' + g + '</' + f : "/") + '>';}
1481
1482function write_w3cdtf(d, t) { try { return d.toISOString().replace(/\.\d*/,""); } catch(e) { if(t) throw e; } }
1483
1484function write_vt(s) {
1485    switch(typeof s) {
1486        case 'string': return writextag('vt:lpwstr', s);
1487        case 'number': return writextag((s|0)==s?'vt:i4':'vt:r8', String(s));
1488        case 'boolean': return writextag('vt:bool',s?'true':'false');
1489    }
1490    if(s instanceof Date) return writextag('vt:filetime', write_w3cdtf(s));
1491    throw new Error("Unable to serialize " + s);
1492}
1493
1494var XML_HEADER = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\r\n';
1495var XMLNS = {
1496    'dc': 'http://purl.org/dc/elements/1.1/',
1497    'dcterms': 'http://purl.org/dc/terms/',
1498    'dcmitype': 'http://purl.org/dc/dcmitype/',
1499    'mx': 'http://schemas.microsoft.com/office/mac/excel/2008/main',
1500    'r': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships',
1501    'sjs': 'http://schemas.openxmlformats.org/package/2006/sheetjs/core-properties',
1502    'vt': 'http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes',
1503    'xsi': 'http://www.w3.org/2001/XMLSchema-instance',
1504    'xsd': 'http://www.w3.org/2001/XMLSchema'
1505};
1506
1507XMLNS.main = [
1508    'http://schemas.openxmlformats.org/spreadsheetml/2006/main',
1509    'http://purl.oclc.org/ooxml/spreadsheetml/main',
1510    'http://schemas.microsoft.com/office/excel/2006/main',
1511    'http://schemas.microsoft.com/office/excel/2006/2'
1512];
1513
1514function readIEEE754(buf, idx, isLE, nl, ml) {
1515    if(isLE === undefined) isLE = true;
1516    if(!nl) nl = 8;
1517    if(!ml && nl === 8) ml = 52;
1518    var e, m, el = nl * 8 - ml - 1, eMax = (1 << el) - 1, eBias = eMax >> 1;
1519    var bits = -7, d = isLE ? -1 : 1, i = isLE ? (nl - 1) : 0, s = buf[idx + i];
1520
1521    i += d;
1522    e = s & ((1 << (-bits)) - 1); s >>>= (-bits); bits += el;
1523    for (; bits > 0; e = e * 256 + buf[idx + i], i += d, bits -= 8);
1524    m = e & ((1 << (-bits)) - 1); e >>>= (-bits); bits += ml;
1525    for (; bits > 0; m = m * 256 + buf[idx + i], i += d, bits -= 8);
1526    if (e === eMax) return m ? NaN : ((s ? -1 : 1) * Infinity);
1527    else if (e === 0) e = 1 - eBias;
1528    else { m = m + Math.pow(2, ml); e = e - eBias; }
1529    return (s ? -1 : 1) * m * Math.pow(2, e - ml);
1530}
1531
1532var __toBuffer, ___toBuffer;
1533__toBuffer = ___toBuffer = function toBuffer_(bufs) { var x = []; for(var i = 0; i < bufs[0].length; ++i) { x.push.apply(x, bufs[0][i]); } return x; };
1534var __utf16le, ___utf16le;
1535__utf16le = ___utf16le = function utf16le_(b,s,e) { var ss=[]; for(var i=s; i<e; i+=2) ss.push(String.fromCharCode(__readUInt16LE(b,i))); return ss.join(""); };
1536var __hexlify, ___hexlify;
1537__hexlify = ___hexlify = function hexlify_(b,s,l) { return b.slice(s,(s+l)).map(function(x){return (x<16?"0":"") + x.toString(16);}).join(""); };
1538var __utf8, ___utf8;
1539__utf8 = ___utf8 = function(b,s,e) { var ss=[]; for(var i=s; i<e; i++) ss.push(String.fromCharCode(__readUInt8(b,i))); return ss.join(""); };
1540var __lpstr, ___lpstr;
1541__lpstr = ___lpstr = function lpstr_(b,i) { var len = __readUInt32LE(b,i); return len > 0 ? __utf8(b, i+4,i+4+len-1) : "";};
1542var __lpwstr, ___lpwstr;
1543__lpwstr = ___lpwstr = function lpwstr_(b,i) { var len = 2*__readUInt32LE(b,i); return len > 0 ? __utf8(b, i+4,i+4+len-1) : "";};
1544var __double, ___double;
1545__double = ___double = function(b, idx) { return readIEEE754(b, idx);};
1546
1547var is_buf = function is_buf_a(a) { return Array.isArray(a); };
1548if(has_buf) {
1549    __utf16le = function utf16le_b(b,s,e) { if(!Buffer.isBuffer(b)) return ___utf16le(b,s,e); return b.toString('utf16le',s,e); };
1550    __hexlify = function(b,s,l) { return Buffer.isBuffer(b) ? b.toString('hex',s,s+l) : ___hexlify(b,s,l); };
1551    __lpstr = function lpstr_b(b,i) { if(!Buffer.isBuffer(b)) return ___lpstr(b, i); var len = b.readUInt32LE(i); return len > 0 ? b.toString('utf8',i+4,i+4+len-1) : "";};
1552    __lpwstr = function lpwstr_b(b,i) { if(!Buffer.isBuffer(b)) return ___lpwstr(b, i); var len = 2*b.readUInt32LE(i); return b.toString('utf16le',i+4,i+4+len-1);};
1553    __utf8 = function utf8_b(s,e) { return this.toString('utf8',s,e); };
1554    __toBuffer = function(bufs) { return (bufs[0].length > 0 && Buffer.isBuffer(bufs[0][0])) ? Buffer.concat(bufs[0]) : ___toBuffer(bufs);};
1555    bconcat = function(bufs) { return Buffer.isBuffer(bufs[0]) ? Buffer.concat(bufs) : [].concat.apply([], bufs); };
1556    __double = function double_(b,i) { if(Buffer.isBuffer(b)) return b.readDoubleLE(i); return ___double(b,i); };
1557    is_buf = function is_buf_b(a) { return Buffer.isBuffer(a) || Array.isArray(a); };
1558}
1559
1560/* from js-xls */
1561if(typeof cptable !== 'undefined') {
1562    __utf16le = function(b,s,e) { return cptable.utils.decode(1200, b.slice(s,e)); };
1563    __utf8 = function(b,s,e) { return cptable.utils.decode(65001, b.slice(s,e)); };
1564    __lpstr = function(b,i) { var len = __readUInt32LE(b,i); return len > 0 ? cptable.utils.decode(current_codepage, b.slice(i+4, i+4+len-1)) : "";};
1565    __lpwstr = function(b,i) { var len = 2*__readUInt32LE(b,i); return len > 0 ? cptable.utils.decode(1200, b.slice(i+4,i+4+len-1)) : "";};
1566}
1567
1568var __readUInt8 = function(b, idx) { return b[idx]; };
1569var __readUInt16LE = function(b, idx) { return b[idx+1]*(1<<8)+b[idx]; };
1570var __readInt16LE = function(b, idx) { var u = b[idx+1]*(1<<8)+b[idx]; return (u < 0x8000) ? u : (0xffff - u + 1) * -1; };
1571var __readUInt32LE = function(b, idx) { return b[idx+3]*(1<<24)+(b[idx+2]<<16)+(b[idx+1]<<8)+b[idx]; };
1572var __readInt32LE = function(b, idx) { return (b[idx+3]<<24)|(b[idx+2]<<16)|(b[idx+1]<<8)|b[idx]; };
1573
1574var ___unhexlify = function(s) { return s.match(/../g).map(function(x) { return parseInt(x,16);}); };
1575var __unhexlify = typeof Buffer !== "undefined" ? function(s) { return Buffer.isBuffer(s) ? new Buffer(s, 'hex') : ___unhexlify(s); } : ___unhexlify;
1576
1577function ReadShift(size, t) {
1578    var o="", oI, oR, oo=[], w, vv, i, loc;
1579    switch(t) {
1580        case 'dbcs':
1581            loc = this.l;
1582            if(has_buf && Buffer.isBuffer(this)) o = this.slice(this.l, this.l+2*size).toString("utf16le");
1583            else for(i = 0; i != size; ++i) { o+=String.fromCharCode(__readUInt16LE(this, loc)); loc+=2; }
1584            size *= 2;
1585            break;
1586
1587        case 'utf8': o = __utf8(this, this.l, this.l + size); break;
1588        case 'utf16le': size *= 2; o = __utf16le(this, this.l, this.l + size); break;
1589
1590        /* [MS-OLEDS] 2.1.4 LengthPrefixedAnsiString */
1591        case 'lpstr': o = __lpstr(this, this.l); size = 5 + o.length; break;
1592        /* [MS-OLEDS] 2.1.5 LengthPrefixedUnicodeString */
1593        case 'lpwstr': o = __lpwstr(this, this.l); size = 5 + o.length; if(o[o.length-1] == '\u0000') size += 2; break;
1594
1595        case 'cstr': size = 0; o = "";
1596            while((w=__readUInt8(this, this.l + size++))!==0) oo.push(_getchar(w));
1597            o = oo.join(""); break;
1598        case 'wstr': size = 0; o = "";
1599            while((w=__readUInt16LE(this,this.l +size))!==0){oo.push(_getchar(w));size+=2;}
1600            size+=2; o = oo.join(""); break;
1601
1602        /* sbcs and dbcs support continue records in the SST way TODO codepages */
1603        case 'dbcs-cont': o = ""; loc = this.l;
1604            for(i = 0; i != size; ++i) {
1605                if(this.lens && this.lens.indexOf(loc) !== -1) {
1606                    w = __readUInt8(this, loc);
1607                    this.l = loc + 1;
1608                    vv = ReadShift.call(this, size-i, w ? 'dbcs-cont' : 'sbcs-cont');
1609                    return oo.join("") + vv;
1610                }
1611                oo.push(_getchar(__readUInt16LE(this, loc)));
1612                loc+=2;
1613            } o = oo.join(""); size *= 2; break;
1614
1615        case 'sbcs-cont': o = ""; loc = this.l;
1616            for(i = 0; i != size; ++i) {
1617                if(this.lens && this.lens.indexOf(loc) !== -1) {
1618                    w = __readUInt8(this, loc);
1619                    this.l = loc + 1;
1620                    vv = ReadShift.call(this, size-i, w ? 'dbcs-cont' : 'sbcs-cont');
1621                    return oo.join("") + vv;
1622                }
1623                oo.push(_getchar(__readUInt8(this, loc)));
1624                loc+=1;
1625            } o = oo.join(""); break;
1626
1627        default:
1628    switch(size) {
1629        case 1: oI = __readUInt8(this, this.l); this.l++; return oI;
1630        case 2: oI = (t === 'i' ? __readInt16LE : __readUInt16LE)(this, this.l); this.l += 2; return oI;
1631        case 4:
1632            if(t === 'i' || (this[this.l+3] & 0x80)===0) { oI = __readInt32LE(this, this.l); this.l += 4; return oI; }
1633            else { oR = __readUInt32LE(this, this.l); this.l += 4; return oR; } break;
1634        case 8: if(t === 'f') { oR = __double(this, this.l); this.l += 8; return oR; }
1635        /* falls through */
1636        case 16: o = __hexlify(this, this.l, size); break;
1637    }}
1638    this.l+=size; return o;
1639}
1640
1641function WriteShift(t, val, f) {
1642    var size, i;
1643    if(f === 'dbcs') {
1644        for(i = 0; i != val.length; ++i) this.writeUInt16LE(val.charCodeAt(i), this.l + 2 * i);
1645        size = 2 * val.length;
1646    } else switch(t) {
1647        case  1: size = 1; this[this.l] = val&255; break;
1648        case  3: size = 3; this[this.l+2] = val & 255; val >>>= 8; this[this.l+1] = val&255; val >>>= 8; this[this.l] = val&255; break;
1649        case  4: size = 4; this.writeUInt32LE(val, this.l); break;
1650        case  8: size = 8; if(f === 'f') { this.writeDoubleLE(val, this.l); break; }
1651        /* falls through */
1652        case 16: break;
1653        case -4: size = 4; this.writeInt32LE(val, this.l); break;
1654    }
1655    this.l += size; return this;
1656}
1657
1658function CheckField(hexstr, fld) {
1659    var m = __hexlify(this,this.l,hexstr.length>>1);
1660    if(m !== hexstr) throw fld + 'Expected ' + hexstr + ' saw ' + m;
1661    this.l += hexstr.length>>1;
1662}
1663
1664function prep_blob(blob, pos) {
1665    blob.l = pos;
1666    blob.read_shift = ReadShift;
1667    blob.chk = CheckField;
1668    blob.write_shift = WriteShift;
1669}
1670
1671function parsenoop(blob, length) { blob.l += length; }
1672
1673function writenoop(blob, length) { blob.l += length; }
1674
1675function new_buf(sz) {
1676    var o = new_raw_buf(sz);
1677    prep_blob(o, 0);
1678    return o;
1679}
1680
1681/* [MS-XLSB] 2.1.4 Record */
1682function recordhopper(data, cb, opts) {
1683    var tmpbyte, cntbyte, length;
1684    prep_blob(data, data.l || 0);
1685    while(data.l < data.length) {
1686        var RT = data.read_shift(1);
1687        if(RT & 0x80) RT = (RT & 0x7F) + ((data.read_shift(1) & 0x7F)<<7);
1688        var R = XLSBRecordEnum[RT] || XLSBRecordEnum[0xFFFF];
1689        tmpbyte = data.read_shift(1);
1690        length = tmpbyte & 0x7F;
1691        for(cntbyte = 1; cntbyte <4 && (tmpbyte & 0x80); ++cntbyte) length += ((tmpbyte = data.read_shift(1)) & 0x7F)<<(7*cntbyte);
1692        var d = R.f(data, length, opts);
1693        if(cb(d, R, RT)) return;
1694    }
1695}
1696
1697/* control buffer usage for fixed-length buffers */
1698function buf_array() {
1699    var bufs = [], blksz = 2048;
1700    var newblk = function ba_newblk(sz) {
1701        var o = new_buf(sz);
1702        prep_blob(o, 0);
1703        return o;
1704    };
1705
1706    var curbuf = newblk(blksz);
1707
1708    var endbuf = function ba_endbuf() {
1709        curbuf.length = curbuf.l;
1710        if(curbuf.length > 0) bufs.push(curbuf);
1711        curbuf = null;
1712    };
1713
1714    var next = function ba_next(sz) {
1715        if(sz < curbuf.length - curbuf.l) return curbuf;
1716        endbuf();
1717        return (curbuf = newblk(Math.max(sz+1, blksz)));
1718    };
1719
1720    var end = function ba_end() {
1721        endbuf();
1722        return __toBuffer([bufs]);
1723    };
1724
1725    var push = function ba_push(buf) { endbuf(); curbuf = buf; next(blksz); };
1726
1727    return { next:next, push:push, end:end, _bufs:bufs };
1728}
1729
1730function write_record(ba, type, payload, length) {
1731    var t = evert_RE[type], l;
1732    if(!length) length = XLSBRecordEnum[t].p || (payload||[]).length || 0;
1733    l = 1 + (t >= 0x80 ? 1 : 0) + 1 + length;
1734    if(length >= 0x80) ++l; if(length >= 0x4000) ++l; if(length >= 0x200000) ++l;
1735    var o = ba.next(l);
1736    if(t <= 0x7F) o.write_shift(1, t);
1737    else {
1738        o.write_shift(1, (t & 0x7F) + 0x80);
1739        o.write_shift(1, (t >> 7));
1740    }
1741    for(var i = 0; i != 4; ++i) {
1742        if(length >= 0x80) { o.write_shift(1, (length & 0x7F)+0x80); length >>= 7; }
1743        else { o.write_shift(1, length); break; }
1744    }
1745    if(length > 0 && is_buf(payload)) ba.push(payload);
1746}
1747/* XLS ranges enforced */
1748function shift_cell_xls(cell, tgt) {
1749    if(tgt.s) {
1750        if(cell.cRel) cell.c += tgt.s.c;
1751        if(cell.rRel) cell.r += tgt.s.r;
1752    } else {
1753        cell.c += tgt.c;
1754        cell.r += tgt.r;
1755    }
1756    cell.cRel = cell.rRel = 0;
1757    while(cell.c >= 0x100) cell.c -= 0x100;
1758    while(cell.r >= 0x10000) cell.r -= 0x10000;
1759    return cell;
1760}
1761
1762function shift_range_xls(cell, range) {
1763    cell.s = shift_cell_xls(cell.s, range.s);
1764    cell.e = shift_cell_xls(cell.e, range.s);
1765    return cell;
1766}
1767
1768var OFFCRYPTO = {};
1769var make_offcrypto = function(O, _crypto) {
1770    var crypto;
1771    if(typeof _crypto !== 'undefined') crypto = _crypto;
1772    else if(typeof require !== 'undefined') {
1773        try { crypto = require('cry'+'pto'); }
1774        catch(e) { crypto = null; }
1775    }
1776
1777    O.rc4 = function(key, data) {
1778        var S = new Array(256);
1779        var c = 0, i = 0, j = 0, t = 0;
1780        for(i = 0; i != 256; ++i) S[i] = i;
1781        for(i = 0; i != 256; ++i) {
1782            j = (j + S[i] + (key[i%key.length]).charCodeAt(0))&255;
1783            t = S[i]; S[i] = S[j]; S[j] = t;
1784        }
1785        i = j = 0; out = Buffer(data.length);
1786        for(c = 0; c != data.length; ++c) {
1787            i = (i + 1)&255;
1788            j = (j + S[i])%256;
1789            t = S[i]; S[i] = S[j]; S[j] = t;
1790            out[c] = (data[c] ^ S[(S[i]+S[j])&255]);
1791        }
1792        return out;
1793    };
1794
1795    if(crypto) {
1796        O.md5 = function(hex) { return crypto.createHash('md5').update(hex).digest('hex'); };
1797    } else {
1798        O.md5 = function(hex) { throw "unimplemented"; };
1799    }
1800};
1801make_offcrypto(OFFCRYPTO, typeof crypto !== "undefined" ? crypto : undefined);
1802
1803
1804/* [MS-XLSB] 2.5.143 */
1805function parse_StrRun(data, length) {
1806    return { ich: data.read_shift(2), ifnt: data.read_shift(2) };
1807}
1808
1809/* [MS-XLSB] 2.1.7.121 */
1810function parse_RichStr(data, length) {
1811    var start = data.l;
1812    var flags = data.read_shift(1);
1813    var str = parse_XLWideString(data);
1814    var rgsStrRun = [];
1815    var z = { t: str, h: str };
1816    if((flags & 1) !== 0) { /* fRichStr */
1817        /* TODO: formatted string */
1818        var dwSizeStrRun = data.read_shift(4);
1819        for(var i = 0; i != dwSizeStrRun; ++i) rgsStrRun.push(parse_StrRun(data));
1820        z.r = rgsStrRun;
1821    }
1822    else z.r = "<t>" + escapexml(str) + "</t>";
1823    if((flags & 2) !== 0) { /* fExtStr */
1824        /* TODO: phonetic string */
1825    }
1826    data.l = start + length;
1827    return z;
1828}
1829function write_RichStr(str, o) {
1830    /* TODO: formatted string */
1831    if(o == null) o = new_buf(5+2*str.t.length);
1832    o.write_shift(1,0);
1833    write_XLWideString(str.t, o);
1834    return o;
1835}
1836
1837/* [MS-XLSB] 2.5.9 */
1838function parse_XLSBCell(data) {
1839    var col = data.read_shift(4);
1840    var iStyleRef = data.read_shift(2);
1841    iStyleRef += data.read_shift(1) <<16;
1842    var fPhShow = data.read_shift(1);
1843    return { c:col, iStyleRef: iStyleRef };
1844}
1845function write_XLSBCell(cell, o) {
1846    if(o == null) o = new_buf(8);
1847    o.write_shift(-4, cell.c);
1848    o.write_shift(3, cell.iStyleRef === undefined ? cell.iStyleRef : cell.s);
1849    o.write_shift(1, 0); /* fPhShow */
1850    return o;
1851}
1852
1853
1854/* [MS-XLSB] 2.5.21 */
1855function parse_XLSBCodeName (data, length) { return parse_XLWideString(data, length); }
1856
1857/* [MS-XLSB] 2.5.166 */
1858function parse_XLNullableWideString(data) {
1859    var cchCharacters = data.read_shift(4);
1860    return cchCharacters === 0 || cchCharacters === 0xFFFFFFFF ? "" : data.read_shift(cchCharacters, 'dbcs');
1861}
1862function write_XLNullableWideString(data, o) {
1863    if(!o) o = new_buf(127);
1864    o.write_shift(4, data.length > 0 ? data.length : 0xFFFFFFFF);
1865    if(data.length > 0) o.write_shift(0, data, 'dbcs');
1866    return o;
1867}
1868
1869/* [MS-XLSB] 2.5.168 */
1870function parse_XLWideString(data) {
1871    var cchCharacters = data.read_shift(4);
1872    return cchCharacters === 0 ? "" : data.read_shift(cchCharacters, 'dbcs');
1873}
1874function write_XLWideString(data, o) {
1875    if(o == null) o = new_buf(4+2*data.length);
1876    o.write_shift(4, data.length);
1877    if(data.length > 0) o.write_shift(0, data, 'dbcs');
1878    return o;
1879}
1880
1881/* [MS-XLSB] 2.5.114 */
1882var parse_RelID = parse_XLNullableWideString;
1883var write_RelID = write_XLNullableWideString;
1884
1885
1886/* [MS-XLSB] 2.5.122 */
1887/* [MS-XLS] 2.5.217 */
1888function parse_RkNumber(data) {
1889    var b = data.slice(data.l, data.l+4);
1890    var fX100 = b[0] & 1, fInt = b[0] & 2;
1891    data.l+=4;
1892    b[0] &= 0xFC; // b[0] &= ~3;
1893    var RK = fInt === 0 ? __double([0,0,0,0,b[0],b[1],b[2],b[3]],0) : __readInt32LE(b,0)>>2;
1894    return fX100 ? RK/100 : RK;
1895}
1896
1897/* [MS-XLSB] 2.5.153 */
1898function parse_UncheckedRfX(data) {
1899    var cell = {s: {}, e: {}};
1900    cell.s.r = data.read_shift(4);
1901    cell.e.r = data.read_shift(4);
1902    cell.s.c = data.read_shift(4);
1903    cell.e.c = data.read_shift(4);
1904    return cell;
1905}
1906
1907function write_UncheckedRfX(r, o) {
1908    if(!o) o = new_buf(16);
1909    o.write_shift(4, r.s.r);
1910    o.write_shift(4, r.e.r);
1911    o.write_shift(4, r.s.c);
1912    o.write_shift(4, r.e.c);
1913    return o;
1914}
1915
1916/* [MS-XLSB] 2.5.171 */
1917/* [MS-XLS] 2.5.342 */
1918function parse_Xnum(data, length) { return data.read_shift(8, 'f'); }
1919function write_Xnum(data, o) { return (o || new_buf(8)).write_shift(8, 'f', data); }
1920
1921/* [MS-XLSB] 2.5.198.2 */
1922var BErr = {
1923    0x00: "#NULL!",
1924    0x07: "#DIV/0!",
1925    0x0F: "#VALUE!",
1926    0x17: "#REF!",
1927    0x1D: "#NAME?",
1928    0x24: "#NUM!",
1929    0x2A: "#N/A",
1930    0x2B: "#GETTING_DATA",
1931    0xFF: "#WTF?"
1932};
1933var RBErr = evert_num(BErr);
1934
1935/* [MS-XLSB] 2.4.321 BrtColor */
1936function parse_BrtColor(data, length) {
1937    var out = {};
1938    var d = data.read_shift(1);
1939    out.fValidRGB = d & 1;
1940    out.xColorType = d >>> 1;
1941    out.index = data.read_shift(1);
1942    out.nTintAndShade = data.read_shift(2, 'i');
1943    out.bRed   = data.read_shift(1);
1944    out.bGreen = data.read_shift(1);
1945    out.bBlue  = data.read_shift(1);
1946    out.bAlpha = data.read_shift(1);
1947}
1948
1949/* [MS-XLSB] 2.5.52 */
1950function parse_FontFlags(data, length) {
1951    var d = data.read_shift(1);
1952    data.l++;
1953    var out = {
1954        fItalic: d & 0x2,
1955        fStrikeout: d & 0x8,
1956        fOutline: d & 0x10,
1957        fShadow: d & 0x20,
1958        fCondense: d & 0x40,
1959        fExtend: d & 0x80
1960    };
1961    return out;
1962}
1963/* [MS-OLEPS] 2.2 PropertyType */
1964{
1965    var VT_EMPTY    = 0x0000;
1966    var VT_NULL     = 0x0001;
1967    var VT_I2       = 0x0002;
1968    var VT_I4       = 0x0003;
1969    var VT_R4       = 0x0004;
1970    var VT_R8       = 0x0005;
1971    var VT_CY       = 0x0006;
1972    var VT_DATE     = 0x0007;
1973    var VT_BSTR     = 0x0008;
1974    var VT_ERROR    = 0x000A;
1975    var VT_BOOL     = 0x000B;
1976    var VT_VARIANT  = 0x000C;
1977    var VT_DECIMAL  = 0x000E;
1978    var VT_I1       = 0x0010;
1979    var VT_UI1      = 0x0011;
1980    var VT_UI2      = 0x0012;
1981    var VT_UI4      = 0x0013;
1982    var VT_I8       = 0x0014;
1983    var VT_UI8      = 0x0015;
1984    var VT_INT      = 0x0016;
1985    var VT_UINT     = 0x0017;
1986    var VT_LPSTR    = 0x001E;
1987    var VT_LPWSTR   = 0x001F;
1988    var VT_FILETIME = 0x0040;
1989    var VT_BLOB     = 0x0041;
1990    var VT_STREAM   = 0x0042;
1991    var VT_STORAGE  = 0x0043;
1992    var VT_STREAMED_Object  = 0x0044;
1993    var VT_STORED_Object    = 0x0045;
1994    var VT_BLOB_Object      = 0x0046;
1995    var VT_CF       = 0x0047;
1996    var VT_CLSID    = 0x0048;
1997    var VT_VERSIONED_STREAM = 0x0049;
1998    var VT_VECTOR   = 0x1000;
1999    var VT_ARRAY    = 0x2000;
2000
2001    var VT_STRING   = 0x0050; // 2.3.3.1.11 VtString
2002    var VT_USTR     = 0x0051; // 2.3.3.1.12 VtUnalignedString
2003    var VT_CUSTOM   = [VT_STRING, VT_USTR];
2004}
2005
2006/* [MS-OSHARED] 2.3.3.2.2.1 Document Summary Information PIDDSI */
2007var DocSummaryPIDDSI = {
2008    0x01: { n: 'CodePage', t: VT_I2 },
2009    0x02: { n: 'Category', t: VT_STRING },
2010    0x03: { n: 'PresentationFormat', t: VT_STRING },
2011    0x04: { n: 'ByteCount', t: VT_I4 },
2012    0x05: { n: 'LineCount', t: VT_I4 },
2013    0x06: { n: 'ParagraphCount', t: VT_I4 },
2014    0x07: { n: 'SlideCount', t: VT_I4 },
2015    0x08: { n: 'NoteCount', t: VT_I4 },
2016    0x09: { n: 'HiddenCount', t: VT_I4 },
2017    0x0a: { n: 'MultimediaClipCount', t: VT_I4 },
2018    0x0b: { n: 'Scale', t: VT_BOOL },
2019    0x0c: { n: 'HeadingPair', t: VT_VECTOR | VT_VARIANT },
2020    0x0d: { n: 'DocParts', t: VT_VECTOR | VT_LPSTR },
2021    0x0e: { n: 'Manager', t: VT_STRING },
2022    0x0f: { n: 'Company', t: VT_STRING },
2023    0x10: { n: 'LinksDirty', t: VT_BOOL },
2024    0x11: { n: 'CharacterCount', t: VT_I4 },
2025    0x13: { n: 'SharedDoc', t: VT_BOOL },
2026    0x16: { n: 'HLinksChanged', t: VT_BOOL },
2027    0x17: { n: 'AppVersion', t: VT_I4, p: 'version' },
2028    0x1A: { n: 'ContentType', t: VT_STRING },
2029    0x1B: { n: 'ContentStatus', t: VT_STRING },
2030    0x1C: { n: 'Language', t: VT_STRING },
2031    0x1D: { n: 'Version', t: VT_STRING },
2032    0xFF: {}
2033};
2034
2035/* [MS-OSHARED] 2.3.3.2.1.1 Summary Information Property Set PIDSI */
2036var SummaryPIDSI = {
2037    0x01: { n: 'CodePage', t: VT_I2 },
2038    0x02: { n: 'Title', t: VT_STRING },
2039    0x03: { n: 'Subject', t: VT_STRING },
2040    0x04: { n: 'Author', t: VT_STRING },
2041    0x05: { n: 'Keywords', t: VT_STRING },
2042    0x06: { n: 'Comments', t: VT_STRING },
2043    0x07: { n: 'Template', t: VT_STRING },
2044    0x08: { n: 'LastAuthor', t: VT_STRING },
2045    0x09: { n: 'RevNumber', t: VT_STRING },
2046    0x0A: { n: 'EditTime', t: VT_FILETIME },
2047    0x0B: { n: 'LastPrinted', t: VT_FILETIME },
2048    0x0C: { n: 'CreatedDate', t: VT_FILETIME },
2049    0x0D: { n: 'ModifiedDate', t: VT_FILETIME },
2050    0x0E: { n: 'PageCount', t: VT_I4 },
2051    0x0F: { n: 'WordCount', t: VT_I4 },
2052    0x10: { n: 'CharCount', t: VT_I4 },
2053    0x11: { n: 'Thumbnail', t: VT_CF },
2054    0x12: { n: 'ApplicationName', t: VT_LPSTR },
2055    0x13: { n: 'DocumentSecurity', t: VT_I4 },
2056    0xFF: {}
2057};
2058
2059/* [MS-OLEPS] 2.18 */
2060var SpecialProperties = {
2061    0x80000000: { n: 'Locale', t: VT_UI4 },
2062    0x80000003: { n: 'Behavior', t: VT_UI4 },
2063    0x72627262: {}
2064};
2065
2066(function() {
2067    for(var y in SpecialProperties) if(SpecialProperties.hasOwnProperty(y))
2068    DocSummaryPIDDSI[y] = SummaryPIDSI[y] = SpecialProperties[y];
2069})();
2070
2071/* [MS-XLS] 2.4.63 Country/Region codes */
2072var CountryEnum = {
2073    0x0001: "US", // United States
2074    0x0002: "CA", // Canada
2075    0x0003: "", // Latin America (except Brazil)
2076    0x0007: "RU", // Russia
2077    0x0014: "EG", // Egypt
2078    0x001E: "GR", // Greece
2079    0x001F: "NL", // Netherlands
2080    0x0020: "BE", // Belgium
2081    0x0021: "FR", // France
2082    0x0022: "ES", // Spain
2083    0x0024: "HU", // Hungary
2084    0x0027: "IT", // Italy
2085    0x0029: "CH", // Switzerland
2086    0x002B: "AT", // Austria
2087    0x002C: "GB", // United Kingdom
2088    0x002D: "DK", // Denmark
2089    0x002E: "SE", // Sweden
2090    0x002F: "NO", // Norway
2091    0x0030: "PL", // Poland
2092    0x0031: "DE", // Germany
2093    0x0034: "MX", // Mexico
2094    0x0037: "BR", // Brazil
2095    0x003d: "AU", // Australia
2096    0x0040: "NZ", // New Zealand
2097    0x0042: "TH", // Thailand
2098    0x0051: "JP", // Japan
2099    0x0052: "KR", // Korea
2100    0x0054: "VN", // Viet Nam
2101    0x0056: "CN", // China
2102    0x005A: "TR", // Turkey
2103    0x0069: "JS", // Ramastan
2104    0x00D5: "DZ", // Algeria
2105    0x00D8: "MA", // Morocco
2106    0x00DA: "LY", // Libya
2107    0x015F: "PT", // Portugal
2108    0x0162: "IS", // Iceland
2109    0x0166: "FI", // Finland
2110    0x01A4: "CZ", // Czech Republic
2111    0x0376: "TW", // Taiwan
2112    0x03C1: "LB", // Lebanon
2113    0x03C2: "JO", // Jordan
2114    0x03C3: "SY", // Syria
2115    0x03C4: "IQ", // Iraq
2116    0x03C5: "KW", // Kuwait
2117    0x03C6: "SA", // Saudi Arabia
2118    0x03CB: "AE", // United Arab Emirates
2119    0x03CC: "IL", // Israel
2120    0x03CE: "QA", // Qatar
2121    0x03D5: "IR", // Iran
2122    0xFFFF: "US"  // United States
2123};
2124
2125/* [MS-XLS] 2.5.127 */
2126var XLSFillPattern = [
2127    null,
2128    'solid',
2129    'mediumGray',
2130    'darkGray',
2131    'lightGray',
2132    'darkHorizontal',
2133    'darkVertical',
2134    'darkDown',
2135    'darkUp',
2136    'darkGrid',
2137    'darkTrellis',
2138    'lightHorizontal',
2139    'lightVertical',
2140    'lightDown',
2141    'lightUp',
2142    'lightGrid',
2143    'lightTrellis',
2144    'gray125',
2145    'gray0625'
2146];
2147
2148function rgbify(arr) { return arr.map(function(x) { return [(x>>16)&255,(x>>8)&255,x&255]; }); }
2149
2150/* [MS-XLS] 2.5.161 */
2151var XLSIcv = rgbify([
2152    /* Color Constants */
2153    0x000000,
2154    0xFFFFFF,
2155    0xFF0000,
2156    0x00FF00,
2157    0x0000FF,
2158    0xFFFF00,
2159    0xFF00FF,
2160    0x00FFFF,
2161
2162    /* Defaults */
2163    0x000000,
2164    0xFFFFFF,
2165    0xFF0000,
2166    0x00FF00,
2167    0x0000FF,
2168    0xFFFF00,
2169    0xFF00FF,
2170    0x00FFFF,
2171
2172    0x800000,
2173    0x008000,
2174    0x000080,
2175    0x808000,
2176    0x800080,
2177    0x008080,
2178    0xC0C0C0,
2179    0x808080,
2180    0x9999FF,
2181    0x993366,
2182    0xFFFFCC,
2183    0xCCFFFF,
2184    0x660066,
2185    0xFF8080,
2186    0x0066CC,
2187    0xCCCCFF,
2188
2189    0x000080,
2190    0xFF00FF,
2191    0xFFFF00,
2192    0x00FFFF,
2193    0x800080,
2194    0x800000,
2195    0x008080,
2196    0x0000FF,
2197    0x00CCFF,
2198    0xCCFFFF,
2199    0xCCFFCC,
2200    0xFFFF99,
2201    0x99CCFF,
2202    0xFF99CC,
2203    0xCC99FF,
2204    0xFFCC99,
2205
2206    0x3366FF,
2207    0x33CCCC,
2208    0x99CC00,
2209    0xFFCC00,
2210    0xFF9900,
2211    0xFF6600,
2212    0x666699,
2213    0x969696,
2214    0x003366,
2215    0x339966,
2216    0x003300,
2217    0x333300,
2218    0x993300,
2219    0x993366,
2220    0x333399,
2221    0x333333,
2222
2223    /* Sheet */
2224    0xFFFFFF,
2225    0x000000
2226]);
2227
2228/* Parts enumerated in OPC spec, MS-XLSB and MS-XLSX */
2229/* 12.3 Part Summary <SpreadsheetML> */
2230/* 14.2 Part Summary <DrawingML> */
2231/* [MS-XLSX] 2.1 Part Enumerations */
2232/* [MS-XLSB] 2.1.7 Part Enumeration */
2233var ct2type = {
2234    /* Workbook */
2235    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml": "workbooks",
2236
2237    /* Worksheet */
2238    "application/vnd.ms-excel.binIndexWs": "TODO", /* Binary Index */
2239
2240    /* Chartsheet */
2241    "application/vnd.ms-excel.chartsheet": "TODO",
2242    "application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml": "TODO",
2243
2244    /* Dialogsheet */
2245    "application/vnd.ms-excel.dialogsheet": "TODO",
2246    "application/vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml": "TODO",
2247
2248    /* Macrosheet */
2249    "application/vnd.ms-excel.macrosheet": "TODO",
2250    "application/vnd.ms-excel.macrosheet+xml": "TODO",
2251    "application/vnd.ms-excel.intlmacrosheet": "TODO",
2252    "application/vnd.ms-excel.binIndexMs": "TODO", /* Binary Index */
2253
2254    /* File Properties */
2255    "application/vnd.openxmlformats-package.core-properties+xml": "coreprops",
2256    "application/vnd.openxmlformats-officedocument.custom-properties+xml": "custprops",
2257    "application/vnd.openxmlformats-officedocument.extended-properties+xml": "extprops",
2258
2259    /* Custom Data Properties */
2260    "application/vnd.openxmlformats-officedocument.customXmlProperties+xml": "TODO",
2261
2262    /* Comments */
2263    "application/vnd.ms-excel.comments": "comments",
2264    "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml": "comments",
2265
2266    /* PivotTable */
2267    "application/vnd.ms-excel.pivotTable": "TODO",
2268    "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml": "TODO",
2269
2270    /* Calculation Chain */
2271    "application/vnd.ms-excel.calcChain": "calcchains",
2272    "application/vnd.openxmlformats-officedocument.spreadsheetml.calcChain+xml": "calcchains",
2273
2274    /* Printer Settings */
2275    "application/vnd.openxmlformats-officedocument.spreadsheetml.printerSettings": "TODO",
2276
2277    /* ActiveX */
2278    "application/vnd.ms-office.activeX": "TODO",
2279    "application/vnd.ms-office.activeX+xml": "TODO",
2280
2281    /* Custom Toolbars */
2282    "application/vnd.ms-excel.attachedToolbars": "TODO",
2283
2284    /* External Data Connections */
2285    "application/vnd.ms-excel.connections": "TODO",
2286    "application/vnd.openxmlformats-officedocument.spreadsheetml.connections+xml": "TODO",
2287
2288    /* External Links */
2289    "application/vnd.ms-excel.externalLink": "TODO",
2290    "application/vnd.openxmlformats-officedocument.spreadsheetml.externalLink+xml": "TODO",
2291
2292    /* Metadata */
2293    "application/vnd.ms-excel.sheetMetadata": "TODO",
2294    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheetMetadata+xml": "TODO",
2295
2296    /* PivotCache */
2297    "application/vnd.ms-excel.pivotCacheDefinition": "TODO",
2298    "application/vnd.ms-excel.pivotCacheRecords": "TODO",
2299    "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml": "TODO",
2300    "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml": "TODO",
2301
2302    /* Query Table */
2303    "application/vnd.ms-excel.queryTable": "TODO",
2304    "application/vnd.openxmlformats-officedocument.spreadsheetml.queryTable+xml": "TODO",
2305
2306    /* Shared Workbook */
2307    "application/vnd.ms-excel.userNames": "TODO",
2308    "application/vnd.ms-excel.revisionHeaders": "TODO",
2309    "application/vnd.ms-excel.revisionLog": "TODO",
2310    "application/vnd.openxmlformats-officedocument.spreadsheetml.revisionHeaders+xml": "TODO",
2311    "application/vnd.openxmlformats-officedocument.spreadsheetml.revisionLog+xml": "TODO",
2312    "application/vnd.openxmlformats-officedocument.spreadsheetml.userNames+xml": "TODO",
2313
2314    /* Single Cell Table */
2315    "application/vnd.ms-excel.tableSingleCells": "TODO",
2316    "application/vnd.openxmlformats-officedocument.spreadsheetml.tableSingleCells+xml": "TODO",
2317
2318    /* Slicer */
2319    "application/vnd.ms-excel.slicer": "TODO",
2320    "application/vnd.ms-excel.slicerCache": "TODO",
2321    "application/vnd.ms-excel.slicer+xml": "TODO",
2322    "application/vnd.ms-excel.slicerCache+xml": "TODO",
2323
2324    /* Sort Map */
2325    "application/vnd.ms-excel.wsSortMap": "TODO",
2326
2327    /* Table */
2328    "application/vnd.ms-excel.table": "TODO",
2329    "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml": "TODO",
2330
2331    /* Themes */
2332    "application/vnd.openxmlformats-officedocument.theme+xml": "themes",
2333
2334    /* Timeline */
2335    "application/vnd.ms-excel.Timeline+xml": "TODO", /* verify */
2336    "application/vnd.ms-excel.TimelineCache+xml": "TODO", /* verify */
2337
2338    /* VBA */
2339    "application/vnd.ms-office.vbaProject": "vba",
2340    "application/vnd.ms-office.vbaProjectSignature": "vba",
2341
2342    /* Volatile Dependencies */
2343    "application/vnd.ms-office.volatileDependencies": "TODO",
2344    "application/vnd.openxmlformats-officedocument.spreadsheetml.volatileDependencies+xml": "TODO",
2345
2346    /* Control Properties */
2347    "application/vnd.ms-excel.controlproperties+xml": "TODO",
2348
2349    /* Data Model */
2350    "application/vnd.openxmlformats-officedocument.model+data": "TODO",
2351
2352    /* Survey */
2353    "application/vnd.ms-excel.Survey+xml": "TODO",
2354
2355    /* Drawing */
2356    "application/vnd.openxmlformats-officedocument.drawing+xml": "TODO",
2357    "application/vnd.openxmlformats-officedocument.drawingml.chart+xml": "TODO",
2358    "application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml": "TODO",
2359    "application/vnd.openxmlformats-officedocument.drawingml.diagramColors+xml": "TODO",
2360    "application/vnd.openxmlformats-officedocument.drawingml.diagramData+xml": "TODO",
2361    "application/vnd.openxmlformats-officedocument.drawingml.diagramLayout+xml": "TODO",
2362    "application/vnd.openxmlformats-officedocument.drawingml.diagramStyle+xml": "TODO",
2363
2364    /* VML */
2365    "application/vnd.openxmlformats-officedocument.vmlDrawing": "TODO",
2366
2367    "application/vnd.openxmlformats-package.relationships+xml": "rels",
2368    "application/vnd.openxmlformats-officedocument.oleObject": "TODO",
2369
2370    "sheet": "js"
2371};
2372
2373var CT_LIST = (function(){
2374    var o = {
2375        workbooks: {
2376            xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml",
2377            xlsm: "application/vnd.ms-excel.sheet.macroEnabled.main+xml",
2378            xlsb: "application/vnd.ms-excel.sheet.binary.macroEnabled.main",
2379            xltx: "application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml"
2380        },
2381        strs: { /* Shared Strings */
2382            xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml",
2383            xlsb: "application/vnd.ms-excel.sharedStrings"
2384        },
2385        sheets: {
2386            xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml",
2387            xlsb: "application/vnd.ms-excel.worksheet"
2388        },
2389        styles: {/* Styles */
2390            xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml",
2391            xlsb: "application/vnd.ms-excel.styles"
2392        }
2393    };
2394    keys(o).forEach(function(k) { if(!o[k].xlsm) o[k].xlsm = o[k].xlsx; });
2395    keys(o).forEach(function(k){ keys(o[k]).forEach(function(v) { ct2type[o[k][v]] = k; }); });
2396    return o;
2397})();
2398
2399var type2ct = evert_arr(ct2type);
2400
2401XMLNS.CT = 'http://schemas.openxmlformats.org/package/2006/content-types';
2402
2403function parse_ct(data, opts) {
2404    var ctext = {};
2405    if(!data || !data.match) return data;
2406    var ct = { workbooks: [], sheets: [], calcchains: [], themes: [], styles: [],
2407        coreprops: [], extprops: [], custprops: [], strs:[], comments: [], vba: [],
2408        TODO:[], rels:[], xmlns: "" };
2409    (data.match(tagregex)||[]).forEach(function(x) {
2410        var y = parsexmltag(x);
2411        switch(y[0].replace(nsregex,"<")) {
2412            case '<?xml': break;
2413            case '<Types': ct.xmlns = y['xmlns' + (y[0].match(/<(\w+):/)||["",""])[1] ]; break;
2414            case '<Default': ctext[y.Extension] = y.ContentType; break;
2415            case '<Override':
2416                if(ct[ct2type[y.ContentType]] !== undefined) ct[ct2type[y.ContentType]].push(y.PartName);
2417                else if(opts.WTF) console.error(y);
2418                break;
2419        }
2420    });
2421    if(ct.xmlns !== XMLNS.CT) throw new Error("Unknown Namespace: " + ct.xmlns);
2422    ct.calcchain = ct.calcchains.length > 0 ? ct.calcchains[0] : "";
2423    ct.sst = ct.strs.length > 0 ? ct.strs[0] : "";
2424    ct.style = ct.styles.length > 0 ? ct.styles[0] : "";
2425    ct.defaults = ctext;
2426    delete ct.calcchains;
2427    return ct;
2428}
2429
2430var CTYPE_XML_ROOT = writextag('Types', null, {
2431    'xmlns': XMLNS.CT,
2432    'xmlns:xsd': XMLNS.xsd,
2433    'xmlns:xsi': XMLNS.xsi
2434});
2435
2436var CTYPE_DEFAULTS = [
2437    ['xml', 'application/xml'],
2438    ['bin', 'application/vnd.ms-excel.sheet.binary.macroEnabled.main'],
2439    ['rels', type2ct.rels[0]]
2440].map(function(x) {
2441    return writextag('Default', null, {'Extension':x[0], 'ContentType': x[1]});
2442});
2443
2444function write_ct(ct, opts) {
2445    var o = [], v;
2446    o[o.length] = (XML_HEADER);
2447    o[o.length] = (CTYPE_XML_ROOT);
2448    o = o.concat(CTYPE_DEFAULTS);
2449    var f1 = function(w) {
2450        if(ct[w] && ct[w].length > 0) {
2451            v = ct[w][0];
2452            o[o.length] = (writextag('Override', null, {
2453                'PartName': (v[0] == '/' ? "":"/") + v,
2454                'ContentType': CT_LIST[w][opts.bookType || 'xlsx']
2455            }));
2456        }
2457    };
2458    var f2 = function(w) {
2459        ct[w].forEach(function(v) {
2460            o[o.length] = (writextag('Override', null, {
2461                'PartName': (v[0] == '/' ? "":"/") + v,
2462                'ContentType': CT_LIST[w][opts.bookType || 'xlsx']
2463            }));
2464        });
2465    };
2466    var f3 = function(t) {
2467        (ct[t]||[]).forEach(function(v) {
2468            o[o.length] = (writextag('Override', null, {
2469                'PartName': (v[0] == '/' ? "":"/") + v,
2470                'ContentType': type2ct[t][0]
2471            }));
2472        });
2473    };
2474    f1('workbooks');
2475    f2('sheets');
2476    f3('themes');
2477    ['strs', 'styles'].forEach(f1);
2478    ['coreprops', 'extprops', 'custprops'].forEach(f3);
2479    if(o.length>2){ o[o.length] = ('</Types>'); o[1]=o[1].replace("/>",">"); }
2480    return o.join("");
2481}
2482/* 9.3.2 OPC Relationships Markup */
2483var RELS = {
2484    WB: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument",
2485    SHEET: "http://sheetjs.openxmlformats.org/officeDocument/2006/relationships/officeDocument"
2486};
2487
2488function parse_rels(data, currentFilePath) {
2489    if (!data) return data;
2490    if (currentFilePath.charAt(0) !== '/') {
2491        currentFilePath = '/'+currentFilePath;
2492    }
2493    var rels = {};
2494    var hash = {};
2495    var resolveRelativePathIntoAbsolute = function (to) {
2496        var toksFrom = currentFilePath.split('/');
2497        toksFrom.pop(); // folder path
2498        var toksTo = to.split('/');
2499        var reversed = [];
2500        while (toksTo.length !== 0) {
2501            var tokTo = toksTo.shift();
2502            if (tokTo === '..') {
2503                toksFrom.pop();
2504            } else if (tokTo !== '.') {
2505                toksFrom.push(tokTo);
2506            }
2507        }
2508        return toksFrom.join('/');
2509    };
2510
2511    data.match(tagregex).forEach(function(x) {
2512        var y = parsexmltag(x);
2513        /* 9.3.2.2 OPC_Relationships */
2514        if (y[0] === '<Relationship') {
2515            var rel = {}; rel.Type = y.Type; rel.Target = y.Target; rel.Id = y.Id; rel.TargetMode = y.TargetMode;
2516            var canonictarget = y.TargetMode === 'External' ? y.Target : resolveRelativePathIntoAbsolute(y.Target);
2517            rels[canonictarget] = rel;
2518            hash[y.Id] = rel;
2519        }
2520    });
2521    rels["!id"] = hash;
2522    return rels;
2523}
2524
2525XMLNS.RELS = 'http://schemas.openxmlformats.org/package/2006/relationships';
2526
2527var RELS_ROOT = writextag('Relationships', null, {
2528    //'xmlns:ns0': XMLNS.RELS,
2529    'xmlns': XMLNS.RELS
2530});
2531
2532/* TODO */
2533function write_rels(rels) {
2534    var o = [];
2535    o[o.length] = (XML_HEADER);
2536    o[o.length] = (RELS_ROOT);
2537    keys(rels['!id']).forEach(function(rid) { var rel = rels['!id'][rid];
2538        o[o.length] = (writextag('Relationship', null, rel));
2539    });
2540    if(o.length>2){ o[o.length] = ('</Relationships>'); o[1]=o[1].replace("/>",">"); }
2541    return o.join("");
2542}
2543/* ECMA-376 Part II 11.1 Core Properties Part */
2544/* [MS-OSHARED] 2.3.3.2.[1-2].1 (PIDSI/PIDDSI) */
2545var CORE_PROPS = [
2546    ["cp:category", "Category"],
2547    ["cp:contentStatus", "ContentStatus"],
2548    ["cp:keywords", "Keywords"],
2549    ["cp:lastModifiedBy", "LastAuthor"],
2550    ["cp:lastPrinted", "LastPrinted"],
2551    ["cp:revision", "RevNumber"],
2552    ["cp:version", "Version"],
2553    ["dc:creator", "Author"],
2554    ["dc:description", "Comments"],
2555    ["dc:identifier", "Identifier"],
2556    ["dc:language", "Language"],
2557    ["dc:subject", "Subject"],
2558    ["dc:title", "Title"],
2559    ["dcterms:created", "CreatedDate", 'date'],
2560    ["dcterms:modified", "ModifiedDate", 'date']
2561];
2562
2563XMLNS.CORE_PROPS = "http://schemas.openxmlformats.org/package/2006/metadata/core-properties";
2564RELS.CORE_PROPS  = 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties';
2565
2566var CORE_PROPS_REGEX = (function() {
2567    var r = new Array(CORE_PROPS.length);
2568    for(var i = 0; i < CORE_PROPS.length; ++i) {
2569        var f = CORE_PROPS[i];
2570        var g = "(?:"+ f[0].substr(0,f[0].indexOf(":")) +":)"+ f[0].substr(f[0].indexOf(":")+1);
2571        r[i] = new RegExp("<" + g + "[^>]*>(.*)<\/" + g + ">");
2572    }
2573    return r;
2574})();
2575
2576function parse_core_props(data) {
2577    var p = {};
2578
2579    for(var i = 0; i < CORE_PROPS.length; ++i) {
2580        var f = CORE_PROPS[i], cur = data.match(CORE_PROPS_REGEX[i]);
2581        if(cur != null && cur.length > 0) p[f[1]] = cur[1];
2582        if(f[2] === 'date' && p[f[1]]) p[f[1]] = new Date(p[f[1]]);
2583    }
2584
2585    return p;
2586}
2587
2588var CORE_PROPS_XML_ROOT = writextag('cp:coreProperties', null, {
2589    //'xmlns': XMLNS.CORE_PROPS,
2590    'xmlns:cp': XMLNS.CORE_PROPS,
2591    'xmlns:dc': XMLNS.dc,
2592    'xmlns:dcterms': XMLNS.dcterms,
2593    'xmlns:dcmitype': XMLNS.dcmitype,
2594    'xmlns:xsi': XMLNS.xsi
2595});
2596
2597function cp_doit(f, g, h, o, p) {
2598    if(p[f] != null || g == null || g === "") return;
2599    p[f] = g;
2600    o[o.length] = (h ? writextag(f,g,h) : writetag(f,g));
2601}
2602
2603function write_core_props(cp, opts) {
2604    var o = [XML_HEADER, CORE_PROPS_XML_ROOT], p = {};
2605    if(!cp) return o.join("");
2606
2607
2608    if(cp.CreatedDate != null) cp_doit("dcterms:created", typeof cp.CreatedDate === "string" ? cp.CreatedDate : write_w3cdtf(cp.CreatedDate, opts.WTF), {"xsi:type":"dcterms:W3CDTF"}, o, p);
2609    if(cp.ModifiedDate != null) cp_doit("dcterms:modified", typeof cp.ModifiedDate === "string" ? cp.ModifiedDate : write_w3cdtf(cp.ModifiedDate, opts.WTF), {"xsi:type":"dcterms:W3CDTF"}, o, p);
2610
2611    for(var i = 0; i != CORE_PROPS.length; ++i) { var f = CORE_PROPS[i]; cp_doit(f[0], cp[f[1]], null, o, p); }
2612    if(o.length>2){ o[o.length] = ('</cp:coreProperties>'); o[1]=o[1].replace("/>",">"); }
2613    return o.join("");
2614}
2615/* 15.2.12.3 Extended File Properties Part */
2616/* [MS-OSHARED] 2.3.3.2.[1-2].1 (PIDSI/PIDDSI) */
2617var EXT_PROPS = [
2618    ["Application", "Application", "string"],
2619    ["AppVersion", "AppVersion", "string"],
2620    ["Company", "Company", "string"],
2621    ["DocSecurity", "DocSecurity", "string"],
2622    ["Manager", "Manager", "string"],
2623    ["HyperlinksChanged", "HyperlinksChanged", "bool"],
2624    ["SharedDoc", "SharedDoc", "bool"],
2625    ["LinksUpToDate", "LinksUpToDate", "bool"],
2626    ["ScaleCrop", "ScaleCrop", "bool"],
2627    ["HeadingPairs", "HeadingPairs", "raw"],
2628    ["TitlesOfParts", "TitlesOfParts", "raw"]
2629];
2630
2631XMLNS.EXT_PROPS = "http://schemas.openxmlformats.org/officeDocument/2006/extended-properties";
2632RELS.EXT_PROPS  = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties';
2633
2634function parse_ext_props(data, p) {
2635    var q = {}; if(!p) p = {};
2636
2637    EXT_PROPS.forEach(function(f) {
2638        switch(f[2]) {
2639            case "string": p[f[1]] = (data.match(matchtag(f[0]))||[])[1]; break;
2640            case "bool": p[f[1]] = (data.match(matchtag(f[0]))||[])[1] === "true"; break;
2641            case "raw":
2642                var cur = data.match(new RegExp("<" + f[0] + "[^>]*>(.*)<\/" + f[0] + ">"));
2643                if(cur && cur.length > 0) q[f[1]] = cur[1];
2644                break;
2645        }
2646    });
2647
2648    if(q.HeadingPairs && q.TitlesOfParts) {
2649        var v = parseVector(q.HeadingPairs);
2650        var j = 0, widx = 0;
2651        for(var i = 0; i !== v.length; ++i) {
2652            switch(v[i].v) {
2653                case "Worksheets": widx = j; p.Worksheets = +(v[++i].v); break;
2654                case "Named Ranges": ++i; break; // TODO: Handle Named Ranges
2655            }
2656        }
2657        var parts = parseVector(q.TitlesOfParts).map(function(x) { return utf8read(x.v); });
2658        p.SheetNames = parts.slice(widx, widx + p.Worksheets);
2659    }
2660    return p;
2661}
2662
2663var EXT_PROPS_XML_ROOT = writextag('Properties', null, {
2664    'xmlns': XMLNS.EXT_PROPS,
2665    'xmlns:vt': XMLNS.vt
2666});
2667
2668function write_ext_props(cp, opts) {
2669    var o = [], p = {}, W = writextag;
2670    if(!cp) cp = {};
2671    cp.Application = "SheetJS";
2672    o[o.length] = (XML_HEADER);
2673    o[o.length] = (EXT_PROPS_XML_ROOT);
2674
2675    EXT_PROPS.forEach(function(f) {
2676        if(cp[f[1]] === undefined) return;
2677        var v;
2678        switch(f[2]) {
2679            case 'string': v = cp[f[1]]; break;
2680            case 'bool': v = cp[f[1]] ? 'true' : 'false'; break;
2681        }
2682        if(v !== undefined) o[o.length] = (W(f[0], v));
2683    });
2684
2685    /* TODO: HeadingPairs, TitlesOfParts */
2686    o[o.length] = (W('HeadingPairs', W('vt:vector', W('vt:variant', '<vt:lpstr>Worksheets</vt:lpstr>')+W('vt:variant', W('vt:i4', String(cp.Worksheets))), {size:2, baseType:"variant"})));
2687    o[o.length] = (W('TitlesOfParts', W('vt:vector', cp.SheetNames.map(function(s) { return "<vt:lpstr>" + s + "</vt:lpstr>"; }).join(""), {size: cp.Worksheets, baseType:"lpstr"})));
2688    if(o.length>2){ o[o.length] = ('</Properties>'); o[1]=o[1].replace("/>",">"); }
2689    return o.join("");
2690}
2691/* 15.2.12.2 Custom File Properties Part */
2692XMLNS.CUST_PROPS = "http://schemas.openxmlformats.org/officeDocument/2006/custom-properties";
2693RELS.CUST_PROPS  = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties';
2694
2695var custregex = /<[^>]+>[^<]*/g;
2696function parse_cust_props(data, opts) {
2697    var p = {}, name;
2698    var m = data.match(custregex);
2699    if(m) for(var i = 0; i != m.length; ++i) {
2700        var x = m[i], y = parsexmltag(x);
2701        switch(y[0]) {
2702            case '<?xml': break;
2703            case '<Properties':
2704                if(y.xmlns !== XMLNS.CUST_PROPS) throw "unrecognized xmlns " + y.xmlns;
2705                if(y.xmlnsvt && y.xmlnsvt !== XMLNS.vt) throw "unrecognized vt " + y.xmlnsvt;
2706                break;
2707            case '<property': name = y.name; break;
2708            case '</property>': name = null; break;
2709            default: if (x.indexOf('<vt:') === 0) {
2710                var toks = x.split('>');
2711                var type = toks[0].substring(4), text = toks[1];
2712                /* 22.4.2.32 (CT_Variant). Omit the binary types from 22.4 (Variant Types) */
2713                switch(type) {
2714                    case 'lpstr': case 'lpwstr': case 'bstr': case 'lpwstr':
2715                        p[name] = unescapexml(text);
2716                        break;
2717                    case 'bool':
2718                        p[name] = parsexmlbool(text, '<vt:bool>');
2719                        break;
2720                    case 'i1': case 'i2': case 'i4': case 'i8': case 'int': case 'uint':
2721                        p[name] = parseInt(text, 10);
2722                        break;
2723                    case 'r4': case 'r8': case 'decimal':
2724                        p[name] = parseFloat(text);
2725                        break;
2726                    case 'filetime': case 'date':
2727                        p[name] = new Date(text);
2728                        break;
2729                    case 'cy': case 'error':
2730                        p[name] = unescapexml(text);
2731                        break;
2732                    default:
2733                        if(typeof console !== 'undefined') console.warn('Unexpected', x, type, toks);
2734                }
2735            } else if(x.substr(0,2) === "</") {
2736            } else if(opts.WTF) throw new Error(x);
2737        }
2738    }
2739    return p;
2740}
2741
2742var CUST_PROPS_XML_ROOT = writextag('Properties', null, {
2743    'xmlns': XMLNS.CUST_PROPS,
2744    'xmlns:vt': XMLNS.vt
2745});
2746
2747function write_cust_props(cp, opts) {
2748    var o = [XML_HEADER, CUST_PROPS_XML_ROOT];
2749    if(!cp) return o.join("");
2750    var pid = 1;
2751    keys(cp).forEach(function custprop(k) { ++pid;
2752        o[o.length] = (writextag('property', write_vt(cp[k]), {
2753            'fmtid': '{D5CDD505-2E9C-101B-9397-08002B2CF9AE}',
2754            'pid': pid,
2755            'name': k
2756        }));
2757    });
2758    if(o.length>2){ o[o.length] = '</Properties>'; o[1]=o[1].replace("/>",">"); }
2759    return o.join("");
2760}
2761function xlml_set_prop(Props, tag, val) {
2762    /* TODO: Normalize the properties */
2763    switch(tag) {
2764        case 'Description': tag = 'Comments'; break;
2765    }
2766    Props[tag] = val;
2767}
2768
2769/* [MS-DTYP] 2.3.3 FILETIME */
2770/* [MS-OLEDS] 2.1.3 FILETIME (Packet Version) */
2771/* [MS-OLEPS] 2.8 FILETIME (Packet Version) */
2772function parse_FILETIME(blob) {
2773    var dwLowDateTime = blob.read_shift(4), dwHighDateTime = blob.read_shift(4);
2774    return new Date(((dwHighDateTime/1e7*Math.pow(2,32) + dwLowDateTime/1e7) - 11644473600)*1000).toISOString().replace(/\.000/,"");
2775}
2776
2777/* [MS-OSHARED] 2.3.3.1.4 Lpstr */
2778function parse_lpstr(blob, type, pad) {
2779    var str = blob.read_shift(0, 'lpstr');
2780    if(pad) blob.l += (4 - ((str.length+1) & 3)) & 3;
2781    return str;
2782}
2783
2784/* [MS-OSHARED] 2.3.3.1.6 Lpwstr */
2785function parse_lpwstr(blob, type, pad) {
2786    var str = blob.read_shift(0, 'lpwstr');
2787    if(pad) blob.l += (4 - ((str.length+1) & 3)) & 3;
2788    return str;
2789}
2790
2791
2792/* [MS-OSHARED] 2.3.3.1.11 VtString */
2793/* [MS-OSHARED] 2.3.3.1.12 VtUnalignedString */
2794function parse_VtStringBase(blob, stringType, pad) {
2795    if(stringType === 0x1F /*VT_LPWSTR*/) return parse_lpwstr(blob);
2796    return parse_lpstr(blob, stringType, pad);
2797}
2798
2799function parse_VtString(blob, t, pad) { return parse_VtStringBase(blob, t, pad === false ? 0: 4); }
2800function parse_VtUnalignedString(blob, t) { if(!t) throw new Error("dafuq?"); return parse_VtStringBase(blob, t, 0); }
2801
2802/* [MS-OSHARED] 2.3.3.1.9 VtVecUnalignedLpstrValue */
2803function parse_VtVecUnalignedLpstrValue(blob) {
2804    var length = blob.read_shift(4);
2805    var ret = [];
2806    for(var i = 0; i != length; ++i) ret[i] = blob.read_shift(0, 'lpstr');
2807    return ret;
2808}
2809
2810/* [MS-OSHARED] 2.3.3.1.10 VtVecUnalignedLpstr */
2811function parse_VtVecUnalignedLpstr(blob) {
2812    return parse_VtVecUnalignedLpstrValue(blob);
2813}
2814
2815/* [MS-OSHARED] 2.3.3.1.13 VtHeadingPair */
2816function parse_VtHeadingPair(blob) {
2817    var headingString = parse_TypedPropertyValue(blob, VT_USTR);
2818    var headerParts = parse_TypedPropertyValue(blob, VT_I4);
2819    return [headingString, headerParts];
2820}
2821
2822/* [MS-OSHARED] 2.3.3.1.14 VtVecHeadingPairValue */
2823function parse_VtVecHeadingPairValue(blob) {
2824    var cElements = blob.read_shift(4);
2825    var out = [];
2826    for(var i = 0; i != cElements / 2; ++i) out.push(parse_VtHeadingPair(blob));
2827    return out;
2828}
2829
2830/* [MS-OSHARED] 2.3.3.1.15 VtVecHeadingPair */
2831function parse_VtVecHeadingPair(blob) {
2832    // NOTE: When invoked, wType & padding were already consumed
2833    return parse_VtVecHeadingPairValue(blob);
2834}
2835
2836/* [MS-OLEPS] 2.18.1 Dictionary (uses 2.17, 2.16) */
2837function parse_dictionary(blob,CodePage) {
2838    var cnt = blob.read_shift(4);
2839    var dict = {};
2840    for(var j = 0; j != cnt; ++j) {
2841        var pid = blob.read_shift(4);
2842        var len = blob.read_shift(4);
2843        dict[pid] = blob.read_shift(len, (CodePage === 0x4B0 ?'utf16le':'utf8')).replace(chr0,'').replace(chr1,'!');
2844    }
2845    if(blob.l & 3) blob.l = (blob.l>>2+1)<<2;
2846    return dict;
2847}
2848
2849/* [MS-OLEPS] 2.9 BLOB */
2850function parse_BLOB(blob) {
2851    var size = blob.read_shift(4);
2852    var bytes = blob.slice(blob.l,blob.l+size);
2853    if(size & 3 > 0) blob.l += (4 - (size & 3)) & 3;
2854    return bytes;
2855}
2856
2857/* [MS-OLEPS] 2.11 ClipboardData */
2858function parse_ClipboardData(blob) {
2859    // TODO
2860    var o = {};
2861    o.Size = blob.read_shift(4);
2862    //o.Format = blob.read_shift(4);
2863    blob.l += o.Size;
2864    return o;
2865}
2866
2867/* [MS-OLEPS] 2.14 Vector and Array Property Types */
2868function parse_VtVector(blob, cb) {
2869    /* [MS-OLEPS] 2.14.2 VectorHeader */
2870/*  var Length = blob.read_shift(4);
2871    var o = [];
2872    for(var i = 0; i != Length; ++i) {
2873        o.push(cb(blob));
2874    }
2875    return o;*/
2876}
2877
2878/* [MS-OLEPS] 2.15 TypedPropertyValue */
2879function parse_TypedPropertyValue(blob, type, _opts) {
2880    var t = blob.read_shift(2), ret, opts = _opts||{};
2881    blob.l += 2;
2882    if(type !== VT_VARIANT)
2883    if(t !== type && VT_CUSTOM.indexOf(type)===-1) throw new Error('Expected type ' + type + ' saw ' + t);
2884    switch(type === VT_VARIANT ? t : type) {
2885        case 0x02 /*VT_I2*/: ret = blob.read_shift(2, 'i'); if(!opts.raw) blob.l += 2; return ret;
2886        case 0x03 /*VT_I4*/: ret = blob.read_shift(4, 'i'); return ret;
2887        case 0x0B /*VT_BOOL*/: return blob.read_shift(4) !== 0x0;
2888        case 0x13 /*VT_UI4*/: ret = blob.read_shift(4); return ret;
2889        case 0x1E /*VT_LPSTR*/: return parse_lpstr(blob, t, 4).replace(chr0,'');
2890        case 0x1F /*VT_LPWSTR*/: return parse_lpwstr(blob);
2891        case 0x40 /*VT_FILETIME*/: return parse_FILETIME(blob);
2892        case 0x41 /*VT_BLOB*/: return parse_BLOB(blob);
2893        case 0x47 /*VT_CF*/: return parse_ClipboardData(blob);
2894        case 0x50 /*VT_STRING*/: return parse_VtString(blob, t, !opts.raw && 4).replace(chr0,'');
2895        case 0x51 /*VT_USTR*/: return parse_VtUnalignedString(blob, t, 4).replace(chr0,'');
2896        case 0x100C /*VT_VECTOR|VT_VARIANT*/: return parse_VtVecHeadingPair(blob);
2897        case 0x101E /*VT_LPSTR*/: return parse_VtVecUnalignedLpstr(blob);
2898        default: throw new Error("TypedPropertyValue unrecognized type " + type + " " + t);
2899    }
2900}
2901/* [MS-OLEPS] 2.14.2 VectorHeader */
2902/*function parse_VTVectorVariant(blob) {
2903    var Length = blob.read_shift(4);
2904
2905    if(Length & 1 !== 0) throw new Error("VectorHeader Length=" + Length + " must be even");
2906    var o = [];
2907    for(var i = 0; i != Length; ++i) {
2908        o.push(parse_TypedPropertyValue(blob, VT_VARIANT));
2909    }
2910    return o;
2911}*/
2912
2913/* [MS-OLEPS] 2.20 PropertySet */
2914function parse_PropertySet(blob, PIDSI) {
2915    var start_addr = blob.l;
2916    var size = blob.read_shift(4);
2917    var NumProps = blob.read_shift(4);
2918    var Props = [], i = 0;
2919    var CodePage = 0;
2920    var Dictionary = -1, DictObj;
2921    for(i = 0; i != NumProps; ++i) {
2922        var PropID = blob.read_shift(4);
2923        var Offset = blob.read_shift(4);
2924        Props[i] = [PropID, Offset + start_addr];
2925    }
2926    var PropH = {};
2927    for(i = 0; i != NumProps; ++i) {
2928        if(blob.l !== Props[i][1]) {
2929            var fail = true;
2930            if(i>0 && PIDSI) switch(PIDSI[Props[i-1][0]].t) {
2931                case 0x02 /*VT_I2*/: if(blob.l +2 === Props[i][1]) { blob.l+=2; fail = false; } break;
2932                case 0x50 /*VT_STRING*/: if(blob.l <= Props[i][1]) { blob.l=Props[i][1]; fail = false; } break;
2933                case 0x100C /*VT_VECTOR|VT_VARIANT*/: if(blob.l <= Props[i][1]) { blob.l=Props[i][1]; fail = false; } break;
2934            }
2935            if(!PIDSI && blob.l <= Props[i][1]) { fail=false; blob.l = Props[i][1]; }
2936            if(fail) throw new Error("Read Error: Expected address " + Props[i][1] + ' at ' + blob.l + ' :' + i);
2937        }
2938        if(PIDSI) {
2939            var piddsi = PIDSI[Props[i][0]];
2940            PropH[piddsi.n] = parse_TypedPropertyValue(blob, piddsi.t, {raw:true});
2941            if(piddsi.p === 'version') PropH[piddsi.n] = String(PropH[piddsi.n] >> 16) + "." + String(PropH[piddsi.n] & 0xFFFF);
2942            if(piddsi.n == "CodePage") switch(PropH[piddsi.n]) {
2943                case 0: PropH[piddsi.n] = 1252;
2944                    /* falls through */
2945                case 10000: // OSX Roman
2946                case 1252: // Windows Latin
2947
2948                case 874: // SB Windows Thai
2949                case 1250: // SB Windows Central Europe
2950                case 1251: // SB Windows Cyrillic
2951                case 1253: // SB Windows Greek
2952                case 1254: // SB Windows Turkish
2953                case 1255: // SB Windows Hebrew
2954                case 1256: // SB Windows Arabic
2955                case 1257: // SB Windows Baltic
2956                case 1258: // SB Windows Vietnam
2957
2958                case 932: // DB Windows Japanese Shift-JIS
2959                case 936: // DB Windows Simplified Chinese GBK
2960                case 949: // DB Windows Korean
2961                case 950: // DB Windows Traditional Chinese Big5
2962
2963                case 1200: // UTF16LE
2964                case 1201: // UTF16BE
2965                case 65000: case -536: // UTF-7
2966                case 65001: case -535: // UTF-8
2967                    set_cp(CodePage = PropH[piddsi.n]); break;
2968                default: throw new Error("Unsupported CodePage: " + PropH[piddsi.n]);
2969            }
2970        } else {
2971            if(Props[i][0] === 0x1) {
2972                CodePage = PropH.CodePage = parse_TypedPropertyValue(blob, VT_I2);
2973                set_cp(CodePage);
2974                if(Dictionary !== -1) {
2975                    var oldpos = blob.l;
2976                    blob.l = Props[Dictionary][1];
2977                    DictObj = parse_dictionary(blob,CodePage);
2978                    blob.l = oldpos;
2979                }
2980            } else if(Props[i][0] === 0) {
2981                if(CodePage === 0) { Dictionary = i; blob.l = Props[i+1][1]; continue; }
2982                DictObj = parse_dictionary(blob,CodePage);
2983            } else {
2984                var name = DictObj[Props[i][0]];
2985                var val;
2986                /* [MS-OSHARED] 2.3.3.2.3.1.2 + PROPVARIANT */
2987                switch(blob[blob.l]) {
2988                    case 0x41 /*VT_BLOB*/: blob.l += 4; val = parse_BLOB(blob); break;
2989                    case 0x1E /*VT_LPSTR*/: blob.l += 4; val = parse_VtString(blob, blob[blob.l-4]); break;
2990                    case 0x1F /*VT_LPWSTR*/: blob.l += 4; val = parse_VtString(blob, blob[blob.l-4]); break;
2991                    case 0x03 /*VT_I4*/: blob.l += 4; val = blob.read_shift(4, 'i'); break;
2992                    case 0x13 /*VT_UI4*/: blob.l += 4; val = blob.read_shift(4); break;
2993                    case 0x05 /*VT_R8*/: blob.l += 4; val = blob.read_shift(8, 'f'); break;
2994                    case 0x0B /*VT_BOOL*/: blob.l += 4; val = parsebool(blob, 4); break;
2995                    case 0x40 /*VT_FILETIME*/: blob.l += 4; val = new Date(parse_FILETIME(blob)); break;
2996                    default: throw new Error("unparsed value: " + blob[blob.l]);
2997                }
2998                PropH[name] = val;
2999            }
3000        }
3001    }
3002    blob.l = start_addr + size; /* step ahead to skip padding */
3003    return PropH;
3004}
3005
3006/* [MS-OLEPS] 2.21 PropertySetStream */
3007function parse_PropertySetStream(file, PIDSI) {
3008    var blob = file.content;
3009    prep_blob(blob, 0);
3010
3011    var NumSets, FMTID0, FMTID1, Offset0, Offset1;
3012    blob.chk('feff', 'Byte Order: ');
3013
3014    var vers = blob.read_shift(2); // TODO: check version
3015    var SystemIdentifier = blob.read_shift(4);
3016    blob.chk(CFB.utils.consts.HEADER_CLSID, 'CLSID: ');
3017    NumSets = blob.read_shift(4);
3018    if(NumSets !== 1 && NumSets !== 2) throw "Unrecognized #Sets: " + NumSets;
3019    FMTID0 = blob.read_shift(16); Offset0 = blob.read_shift(4);
3020
3021    if(NumSets === 1 && Offset0 !== blob.l) throw "Length mismatch";
3022    else if(NumSets === 2) { FMTID1 = blob.read_shift(16); Offset1 = blob.read_shift(4); }
3023    var PSet0 = parse_PropertySet(blob, PIDSI);
3024
3025    var rval = { SystemIdentifier: SystemIdentifier };
3026    for(var y in PSet0) rval[y] = PSet0[y];
3027    //rval.blob = blob;
3028    rval.FMTID = FMTID0;
3029    //rval.PSet0 = PSet0;
3030    if(NumSets === 1) return rval;
3031    if(blob.l !== Offset1) throw "Length mismatch 2: " + blob.l + " !== " + Offset1;
3032    var PSet1;
3033    try { PSet1 = parse_PropertySet(blob, null); } catch(e) { }
3034    for(y in PSet1) rval[y] = PSet1[y];
3035    rval.FMTID = [FMTID0, FMTID1]; // TODO: verify FMTID0/1
3036    return rval;
3037}
3038
3039
3040function parsenoop2(blob, length) { blob.read_shift(length); return null; }
3041
3042function parslurp(blob, length, cb) {
3043    var arr = [], target = blob.l + length;
3044    while(blob.l < target) arr.push(cb(blob, target - blob.l));
3045    if(target !== blob.l) throw new Error("Slurp error");
3046    return arr;
3047}
3048
3049function parslurp2(blob, length, cb) {
3050    var arr = [], target = blob.l + length, len = blob.read_shift(2);
3051    while(len-- !== 0) arr.push(cb(blob, target - blob.l));
3052    if(target !== blob.l) throw new Error("Slurp error");
3053    return arr;
3054}
3055
3056function parsebool(blob, length) { return blob.read_shift(length) === 0x1; }
3057
3058function parseuint16(blob) { return blob.read_shift(2, 'u'); }
3059function parseuint16a(blob, length) { return parslurp(blob,length,parseuint16);}
3060
3061/* --- 2.5 Structures --- */
3062
3063/* [MS-XLS] 2.5.14 Boolean */
3064var parse_Boolean = parsebool;
3065
3066/* [MS-XLS] 2.5.10 Bes (boolean or error) */
3067function parse_Bes(blob) {
3068    var v = blob.read_shift(1), t = blob.read_shift(1);
3069    return t === 0x01 ? v : v === 0x01;
3070}
3071
3072/* [MS-XLS] 2.5.240 ShortXLUnicodeString */
3073function parse_ShortXLUnicodeString(blob, length, opts) {
3074    var cch = blob.read_shift(1);
3075    var width = 1, encoding = 'sbcs-cont';
3076    var cp = current_codepage;
3077    if(opts && opts.biff >= 8) current_codepage = 1200;
3078    if(opts === undefined || opts.biff !== 5) {
3079        var fHighByte = blob.read_shift(1);
3080        if(fHighByte) { width = 2; encoding = 'dbcs-cont'; }
3081    }
3082    var o = cch ? blob.read_shift(cch, encoding) : "";
3083    current_codepage = cp;
3084    return o;
3085}
3086
3087/* 2.5.293 XLUnicodeRichExtendedString */
3088function parse_XLUnicodeRichExtendedString(blob) {
3089    var cp = current_codepage;
3090    current_codepage = 1200;
3091    var cch = blob.read_shift(2), flags = blob.read_shift(1);
3092    var fHighByte = flags & 0x1, fExtSt = flags & 0x4, fRichSt = flags & 0x8;
3093    var width = 1 + (flags & 0x1); // 0x0 -> utf8, 0x1 -> dbcs
3094    var cRun, cbExtRst;
3095    var z = {};
3096    if(fRichSt) cRun = blob.read_shift(2);
3097    if(fExtSt) cbExtRst = blob.read_shift(4);
3098    var encoding = (flags & 0x1) ? 'dbcs-cont' : 'sbcs-cont';
3099    var msg = cch === 0 ? "" : blob.read_shift(cch, encoding);
3100    if(fRichSt) blob.l += 4 * cRun; //TODO: parse this
3101    if(fExtSt) blob.l += cbExtRst; //TODO: parse this
3102    z.t = msg;
3103    if(!fRichSt) { z.raw = "<t>" + z.t + "</t>"; z.r = z.t; }
3104    current_codepage = cp;
3105    return z;
3106}
3107
3108/* 2.5.296 XLUnicodeStringNoCch */
3109function parse_XLUnicodeStringNoCch(blob, cch, opts) {
3110    var retval;
3111    var fHighByte = blob.read_shift(1);
3112    if(fHighByte===0) { retval = blob.read_shift(cch, 'sbcs-cont'); }
3113    else { retval = blob.read_shift(cch, 'dbcs-cont'); }
3114    return retval;
3115}
3116
3117/* 2.5.294 XLUnicodeString */
3118function parse_XLUnicodeString(blob, length, opts) {
3119    var cch = blob.read_shift(opts !== undefined && opts.biff > 0 && opts.biff < 8 ? 1 : 2);
3120    if(cch === 0) { blob.l++; return ""; }
3121    return parse_XLUnicodeStringNoCch(blob, cch, opts);
3122}
3123/* BIFF5 override */
3124function parse_XLUnicodeString2(blob, length, opts) {
3125    if(opts.biff !== 5 && opts.biff !== 2) return parse_XLUnicodeString(blob, length, opts);
3126    var cch = blob.read_shift(1);
3127    if(cch === 0) { blob.l++; return ""; }
3128    return blob.read_shift(cch, 'sbcs-cont');
3129}
3130
3131/* [MS-XLS] 2.5.61 ControlInfo */
3132var parse_ControlInfo = parsenoop;
3133
3134/* [MS-OSHARED] 2.3.7.6 URLMoniker TODO: flags */
3135var parse_URLMoniker = function(blob, length) {
3136    var len = blob.read_shift(4), start = blob.l;
3137    var extra = false;
3138    if(len > 24) {
3139        /* look ahead */
3140        blob.l += len - 24;
3141        if(blob.read_shift(16) === "795881f43b1d7f48af2c825dc4852763") extra = true;
3142        blob.l = start;
3143    }
3144    var url = blob.read_shift((extra?len-24:len)>>1, 'utf16le').replace(chr0,"");
3145    if(extra) blob.l += 24;
3146    return url;
3147};
3148
3149/* [MS-OSHARED] 2.3.7.8 FileMoniker TODO: all fields */
3150var parse_FileMoniker = function(blob, length) {
3151    var cAnti = blob.read_shift(2);
3152    var ansiLength = blob.read_shift(4);
3153    var ansiPath = blob.read_shift(ansiLength, 'cstr');
3154    var endServer = blob.read_shift(2);
3155    var versionNumber = blob.read_shift(2);
3156    var cbUnicodePathSize = blob.read_shift(4);
3157    if(cbUnicodePathSize === 0) return ansiPath.replace(/\\/g,"/");
3158    var cbUnicodePathBytes = blob.read_shift(4);
3159    var usKeyValue = blob.read_shift(2);
3160    var unicodePath = blob.read_shift(cbUnicodePathBytes>>1, 'utf16le').replace(chr0,"");
3161    return unicodePath;
3162};
3163
3164/* [MS-OSHARED] 2.3.7.2 HyperlinkMoniker TODO: all the monikers */
3165var parse_HyperlinkMoniker = function(blob, length) {
3166    var clsid = blob.read_shift(16); length -= 16;
3167    switch(clsid) {
3168        case "e0c9ea79f9bace118c8200aa004ba90b": return parse_URLMoniker(blob, length);
3169        case "0303000000000000c000000000000046": return parse_FileMoniker(blob, length);
3170        default: throw "unsupported moniker " + clsid;
3171    }
3172};
3173
3174/* [MS-OSHARED] 2.3.7.9 HyperlinkString */
3175var parse_HyperlinkString = function(blob, length) {
3176    var len = blob.read_shift(4);
3177    var o = blob.read_shift(len, 'utf16le').replace(chr0, "");
3178    return o;
3179};
3180
3181/* [MS-OSHARED] 2.3.7.1 Hyperlink Object TODO: unify params with XLSX */
3182var parse_Hyperlink = function(blob, length) {
3183    var end = blob.l + length;
3184    var sVer = blob.read_shift(4);
3185    if(sVer !== 2) throw new Error("Unrecognized streamVersion: " + sVer);
3186    var flags = blob.read_shift(2);
3187    blob.l += 2;
3188    var displayName, targetFrameName, moniker, oleMoniker, location, guid, fileTime;
3189    if(flags & 0x0010) displayName = parse_HyperlinkString(blob, end - blob.l);
3190    if(flags & 0x0080) targetFrameName = parse_HyperlinkString(blob, end - blob.l);
3191    if((flags & 0x0101) === 0x0101) moniker = parse_HyperlinkString(blob, end - blob.l);
3192    if((flags & 0x0101) === 0x0001) oleMoniker = parse_HyperlinkMoniker(blob, end - blob.l);
3193    if(flags & 0x0008) location = parse_HyperlinkString(blob, end - blob.l);
3194    if(flags & 0x0020) guid = blob.read_shift(16);
3195    if(flags & 0x0040) fileTime = parse_FILETIME(blob, 8);
3196    blob.l = end;
3197    var target = (targetFrameName||moniker||oleMoniker);
3198    if(location) target+="#"+location;
3199    return {Target: target};
3200};
3201
3202/* 2.5.178 LongRGBA */
3203function parse_LongRGBA(blob, length) { var r = blob.read_shift(1), g = blob.read_shift(1), b = blob.read_shift(1), a = blob.read_shift(1); return [r,g,b,a]; }
3204
3205/* 2.5.177 LongRGB */
3206function parse_LongRGB(blob, length) { var x = parse_LongRGBA(blob, length); x[3] = 0; return x; }
3207
3208
3209/* --- MS-XLS --- */
3210
3211/* 2.5.19 */
3212function parse_XLSCell(blob, length) {
3213    var rw = blob.read_shift(2); // 0-indexed
3214    var col = blob.read_shift(2);
3215    var ixfe = blob.read_shift(2);
3216    return {r:rw, c:col, ixfe:ixfe};
3217}
3218
3219/* 2.5.134 */
3220function parse_frtHeader(blob) {
3221    var rt = blob.read_shift(2);
3222    var flags = blob.read_shift(2); // TODO: parse these flags
3223    blob.l += 8;
3224    return {type: rt, flags: flags};
3225}
3226
3227
3228
3229function parse_OptXLUnicodeString(blob, length, opts) { return length === 0 ? "" : parse_XLUnicodeString2(blob, length, opts); }
3230
3231/* 2.5.158 */
3232var HIDEOBJENUM = ['SHOWALL', 'SHOWPLACEHOLDER', 'HIDEALL'];
3233var parse_HideObjEnum = parseuint16;
3234
3235/* 2.5.344 */
3236function parse_XTI(blob, length) {
3237    var iSupBook = blob.read_shift(2), itabFirst = blob.read_shift(2,'i'), itabLast = blob.read_shift(2,'i');
3238    return [iSupBook, itabFirst, itabLast];
3239}
3240
3241/* 2.5.218 */
3242function parse_RkRec(blob, length) {
3243    var ixfe = blob.read_shift(2);
3244    var RK = parse_RkNumber(blob);
3245    //console.log("::", ixfe, RK,";;");
3246    return [ixfe, RK];
3247}
3248
3249/* 2.5.1 */
3250function parse_AddinUdf(blob, length) {
3251    blob.l += 4; length -= 4;
3252    var l = blob.l + length;
3253    var udfName = parse_ShortXLUnicodeString(blob, length);
3254    var cb = blob.read_shift(2);
3255    l -= blob.l;
3256    if(cb !== l) throw "Malformed AddinUdf: padding = " + l + " != " + cb;
3257    blob.l += cb;
3258    return udfName;
3259}
3260
3261/* 2.5.209 TODO: Check sizes */
3262function parse_Ref8U(blob, length) {
3263    var rwFirst = blob.read_shift(2);
3264    var rwLast = blob.read_shift(2);
3265    var colFirst = blob.read_shift(2);
3266    var colLast = blob.read_shift(2);
3267    return {s:{c:colFirst, r:rwFirst}, e:{c:colLast,r:rwLast}};
3268}
3269
3270/* 2.5.211 */
3271function parse_RefU(blob, length) {
3272    var rwFirst = blob.read_shift(2);
3273    var rwLast = blob.read_shift(2);
3274    var colFirst = blob.read_shift(1);
3275    var colLast = blob.read_shift(1);
3276    return {s:{c:colFirst, r:rwFirst}, e:{c:colLast,r:rwLast}};
3277}
3278
3279/* 2.5.207 */
3280var parse_Ref = parse_RefU;
3281
3282/* 2.5.143 */
3283function parse_FtCmo(blob, length) {
3284    blob.l += 4;
3285    var ot = blob.read_shift(2);
3286    var id = blob.read_shift(2);
3287    var flags = blob.read_shift(2);
3288    blob.l+=12;
3289    return [id, ot, flags];
3290}
3291
3292/* 2.5.149 */
3293function parse_FtNts(blob, length) {
3294    var out = {};
3295    blob.l += 4;
3296    blob.l += 16; // GUID TODO
3297    out.fSharedNote = blob.read_shift(2);
3298    blob.l += 4;
3299    return out;
3300}
3301
3302/* 2.5.142 */
3303function parse_FtCf(blob, length) {
3304    var out = {};
3305    blob.l += 4;
3306    blob.cf = blob.read_shift(2);
3307    return out;
3308}
3309
3310/* 2.5.140 - 2.5.154 and friends */
3311var FtTab = {
3312    0x15: parse_FtCmo,
3313    0x13: parsenoop,                                /* FtLbsData */
3314    0x12: function(blob, length) { blob.l += 12; }, /* FtCblsData */
3315    0x11: function(blob, length) { blob.l += 8; },  /* FtRboData */
3316    0x10: parsenoop,                                /* FtEdoData */
3317    0x0F: parsenoop,                                /* FtGboData */
3318    0x0D: parse_FtNts,                              /* FtNts */
3319    0x0C: function(blob, length) { blob.l += 24; }, /* FtSbs */
3320    0x0B: function(blob, length) { blob.l += 10; }, /* FtRbo */
3321    0x0A: function(blob, length) { blob.l += 16; }, /* FtCbls */
3322    0x09: parsenoop,                                /* FtPictFmla */
3323    0x08: function(blob, length) { blob.l += 6; },  /* FtPioGrbit */
3324    0x07: parse_FtCf,                               /* FtCf */
3325    0x06: function(blob, length) { blob.l += 6; },  /* FtGmo */
3326    0x04: parsenoop,                                /* FtMacro */
3327    0x00: function(blob, length) { blob.l += 4; }   /* FtEnding */
3328};
3329function parse_FtArray(blob, length, ot) {
3330    var s = blob.l;
3331    var fts = [];
3332    while(blob.l < s + length) {
3333        var ft = blob.read_shift(2);
3334        blob.l-=2;
3335        try {
3336            fts.push(FtTab[ft](blob, s + length - blob.l));
3337        } catch(e) { blob.l = s + length; return fts; }
3338    }
3339    if(blob.l != s + length) blob.l = s + length; //throw "bad Object Ft-sequence";
3340    return fts;
3341}
3342
3343/* 2.5.129 */
3344var parse_FontIndex = parseuint16;
3345
3346/* --- 2.4 Records --- */
3347
3348/* 2.4.21 */
3349function parse_BOF(blob, length) {
3350    var o = {};
3351    o.BIFFVer = blob.read_shift(2); length -= 2;
3352    switch(o.BIFFVer) {
3353        case 0x0600: /* BIFF8 */
3354        case 0x0500: /* BIFF5 */
3355        case 0x0002: case 0x0007: /* BIFF2 */
3356            break;
3357        default: throw "Unexpected BIFF Ver " + o.BIFFVer;
3358    }
3359    blob.read_shift(length);
3360    return o;
3361}
3362
3363
3364/* 2.4.146 */
3365function parse_InterfaceHdr(blob, length) {
3366    if(length === 0) return 0x04b0;
3367    var q;
3368    if((q=blob.read_shift(2))!==0x04b0) throw 'InterfaceHdr codePage ' + q;
3369    return 0x04b0;
3370}
3371
3372
3373/* 2.4.349 */
3374function parse_WriteAccess(blob, length, opts) {
3375    if(opts.enc) { blob.l += length; return ""; }
3376    var l = blob.l;
3377    // TODO: make sure XLUnicodeString doesnt overrun
3378    var UserName = parse_XLUnicodeString(blob, 0, opts);
3379    blob.read_shift(length + l - blob.l);
3380    return UserName;
3381}
3382
3383/* 2.4.28 */
3384function parse_BoundSheet8(blob, length, opts) {
3385    var pos = blob.read_shift(4);
3386    var hidden = blob.read_shift(1) >> 6;
3387    var dt = blob.read_shift(1);
3388    switch(dt) {
3389        case 0: dt = 'Worksheet'; break;
3390        case 1: dt = 'Macrosheet'; break;
3391        case 2: dt = 'Chartsheet'; break;
3392        case 6: dt = 'VBAModule'; break;
3393    }
3394    var name = parse_ShortXLUnicodeString(blob, 0, opts);
3395    if(name.length === 0) name = "Sheet1";
3396    return { pos:pos, hs:hidden, dt:dt, name:name };
3397}
3398
3399/* 2.4.265 TODO */
3400function parse_SST(blob, length) {
3401    var cnt = blob.read_shift(4);
3402    var ucnt = blob.read_shift(4);
3403    var strs = [];
3404    for(var i = 0; i != ucnt; ++i) {
3405        strs.push(parse_XLUnicodeRichExtendedString(blob));
3406    }
3407    strs.Count = cnt; strs.Unique = ucnt;
3408    return strs;
3409}
3410
3411/* 2.4.107 */
3412function parse_ExtSST(blob, length) {
3413    var extsst = {};
3414    extsst.dsst = blob.read_shift(2);
3415    blob.l += length-2;
3416    return extsst;
3417}
3418
3419
3420/* 2.4.221 TODO*/
3421function parse_Row(blob, length) {
3422    var rw = blob.read_shift(2), col = blob.read_shift(2), Col = blob.read_shift(2), rht = blob.read_shift(2);
3423    blob.read_shift(4); // reserved(2), unused(2)
3424    var flags = blob.read_shift(1); // various flags
3425    blob.read_shift(1); // reserved
3426    blob.read_shift(2); //ixfe, other flags
3427    return {r:rw, c:col, cnt:Col-col};
3428}
3429
3430
3431/* 2.4.125 */
3432function parse_ForceFullCalculation(blob, length) {
3433    var header = parse_frtHeader(blob);
3434    if(header.type != 0x08A3) throw "Invalid Future Record " + header.type;
3435    var fullcalc = blob.read_shift(4);
3436    return fullcalc !== 0x0;
3437}
3438
3439
3440var parse_CompressPictures = parsenoop2; /* 2.4.55 Not interesting */
3441
3442
3443
3444/* 2.4.215 rt */
3445function parse_RecalcId(blob, length) {
3446    blob.read_shift(2);
3447    return blob.read_shift(4);
3448}
3449
3450/* 2.4.87 */
3451function parse_DefaultRowHeight (blob, length) {
3452    var f = blob.read_shift(2), miyRw;
3453    miyRw = blob.read_shift(2); // flags & 0x02 -> hidden, else empty
3454    var fl = {Unsynced:f&1,DyZero:(f&2)>>1,ExAsc:(f&4)>>2,ExDsc:(f&8)>>3};
3455    return [fl, miyRw];
3456}
3457
3458/* 2.4.345 TODO */
3459function parse_Window1(blob, length) {
3460    var xWn = blob.read_shift(2), yWn = blob.read_shift(2), dxWn = blob.read_shift(2), dyWn = blob.read_shift(2);
3461    var flags = blob.read_shift(2), iTabCur = blob.read_shift(2), iTabFirst = blob.read_shift(2);
3462    var ctabSel = blob.read_shift(2), wTabRatio = blob.read_shift(2);
3463    return { Pos: [xWn, yWn], Dim: [dxWn, dyWn], Flags: flags, CurTab: iTabCur,
3464        FirstTab: iTabFirst, Selected: ctabSel, TabRatio: wTabRatio };
3465}
3466
3467/* 2.4.122 TODO */
3468function parse_Font(blob, length, opts) {
3469    blob.l += 14;
3470    var name = parse_ShortXLUnicodeString(blob, 0, opts);
3471    return name;
3472}
3473
3474/* 2.4.149 */
3475function parse_LabelSst(blob, length) {
3476    var cell = parse_XLSCell(blob);
3477    cell.isst = blob.read_shift(4);
3478    return cell;
3479}
3480
3481/* 2.4.148 */
3482function parse_Label(blob, length, opts) {
3483    var cell = parse_XLSCell(blob, 6);
3484    var str = parse_XLUnicodeString(blob, length-6, opts);
3485    cell.val = str;
3486    return cell;
3487}
3488
3489/* 2.4.126 Number Formats */
3490function parse_Format(blob, length, opts) {
3491    var ifmt = blob.read_shift(2);
3492    var fmtstr = parse_XLUnicodeString2(blob, 0, opts);
3493    return [ifmt, fmtstr];
3494}
3495
3496/* 2.4.90 */
3497function parse_Dimensions(blob, length) {
3498    var w = length === 10 ? 2 : 4;
3499    var r = blob.read_shift(w), R = blob.read_shift(w),
3500        c = blob.read_shift(2), C = blob.read_shift(2);
3501    blob.l += 2;
3502    return {s: {r:r, c:c}, e: {r:R, c:C}};
3503}
3504
3505/* 2.4.220 */
3506function parse_RK(blob, length) {
3507    var rw = blob.read_shift(2), col = blob.read_shift(2);
3508    var rkrec = parse_RkRec(blob);
3509    return {r:rw, c:col, ixfe:rkrec[0], rknum:rkrec[1]};
3510}
3511
3512/* 2.4.175 */
3513function parse_MulRk(blob, length) {
3514    var target = blob.l + length - 2;
3515    var rw = blob.read_shift(2), col = blob.read_shift(2);
3516    var rkrecs = [];
3517    while(blob.l < target) rkrecs.push(parse_RkRec(blob));
3518    if(blob.l !== target) throw "MulRK read error";
3519    var lastcol = blob.read_shift(2);
3520    if(rkrecs.length != lastcol - col + 1) throw "MulRK length mismatch";
3521    return {r:rw, c:col, C:lastcol, rkrec:rkrecs};
3522}
3523
3524/* 2.5.20 2.5.249 TODO */
3525function parse_CellStyleXF(blob, length, style) {
3526    var o = {};
3527    var a = blob.read_shift(4), b = blob.read_shift(4);
3528    var c = blob.read_shift(4), d = blob.read_shift(2);
3529    o.patternType = XLSFillPattern[c >> 26];
3530    o.icvFore = d & 0x7F;
3531    o.icvBack = (d >> 7) & 0x7F;
3532    return o;
3533}
3534function parse_CellXF(blob, length) {return parse_CellStyleXF(blob,length,0);}
3535function parse_StyleXF(blob, length) {return parse_CellStyleXF(blob,length,1);}
3536
3537/* 2.4.353 TODO: actually do this right */
3538function parse_XF(blob, length) {
3539    var o = {};
3540    o.ifnt = blob.read_shift(2); o.ifmt = blob.read_shift(2); o.flags = blob.read_shift(2);
3541    o.fStyle = (o.flags >> 2) & 0x01;
3542    length -= 6;
3543    o.data = parse_CellStyleXF(blob, length, o.fStyle);
3544    return o;
3545}
3546
3547/* 2.4.134 */
3548function parse_Guts(blob, length) {
3549    blob.l += 4;
3550    var out = [blob.read_shift(2), blob.read_shift(2)];
3551    if(out[0] !== 0) out[0]--;
3552    if(out[1] !== 0) out[1]--;
3553    if(out[0] > 7 || out[1] > 7) throw "Bad Gutters: " + out;
3554    return out;
3555}
3556
3557/* 2.4.24 */
3558function parse_BoolErr(blob, length) {
3559    var cell = parse_XLSCell(blob, 6);
3560    var val = parse_Bes(blob, 2);
3561    cell.val = val;
3562    cell.t = (val === true || val === false) ? 'b' : 'e';
3563    return cell;
3564}
3565
3566/* 2.4.180 Number */
3567function parse_Number(blob, length) {
3568    var cell = parse_XLSCell(blob, 6);
3569    var xnum = parse_Xnum(blob, 8);
3570    cell.val = xnum;
3571    return cell;
3572}
3573
3574var parse_XLHeaderFooter = parse_OptXLUnicodeString; // TODO: parse 2.4.136
3575
3576/* 2.4.271 */
3577function parse_SupBook(blob, length, opts) {
3578    var end = blob.l + length;
3579    var ctab = blob.read_shift(2);
3580    var cch = blob.read_shift(2);
3581    var virtPath;
3582    if(cch >=0x01 && cch <=0xff) virtPath = parse_XLUnicodeStringNoCch(blob, cch);
3583    var rgst = blob.read_shift(end - blob.l);
3584    opts.sbcch = cch;
3585    return [cch, ctab, virtPath, rgst];
3586}
3587
3588/* 2.4.105 TODO */
3589function parse_ExternName(blob, length, opts) {
3590    var flags = blob.read_shift(2);
3591    var body;
3592    var o = {
3593        fBuiltIn: flags & 0x01,
3594        fWantAdvise: (flags >>> 1) & 0x01,
3595        fWantPict: (flags >>> 2) & 0x01,
3596        fOle: (flags >>> 3) & 0x01,
3597        fOleLink: (flags >>> 4) & 0x01,
3598        cf: (flags >>> 5) & 0x3FF,
3599        fIcon: flags >>> 15 & 0x01
3600    };
3601    if(opts.sbcch === 0x3A01) body = parse_AddinUdf(blob, length-2);
3602    //else throw new Error("unsupported SupBook cch: " + opts.sbcch);
3603    o.body = body || blob.read_shift(length-2);
3604    return o;
3605}
3606
3607/* 2.4.150 TODO */
3608function parse_Lbl(blob, length, opts) {
3609    if(opts.biff < 8) return parse_Label(blob, length, opts);
3610    var target = blob.l + length;
3611    var flags = blob.read_shift(2);
3612    var chKey = blob.read_shift(1);
3613    var cch = blob.read_shift(1);
3614    var cce = blob.read_shift(2);
3615    blob.l += 2;
3616    var itab = blob.read_shift(2);
3617    blob.l += 4;
3618    var name = parse_XLUnicodeStringNoCch(blob, cch, opts);
3619    var rgce = parse_NameParsedFormula(blob, target - blob.l, opts, cce);
3620    return {
3621        chKey: chKey,
3622        Name: name,
3623        rgce: rgce
3624    };
3625}
3626
3627/* 2.4.106 TODO: verify supbook manipulation */
3628function parse_ExternSheet(blob, length, opts) {
3629    if(opts.biff < 8) return parse_ShortXLUnicodeString(blob, length, opts);
3630    var o = parslurp2(blob,length,parse_XTI);
3631    var oo = [];
3632    if(opts.sbcch === 0x0401) {
3633        for(var i = 0; i != o.length; ++i) oo.push(opts.snames[o[i][1]]);
3634        return oo;
3635    }
3636    else return o;
3637}
3638
3639/* 2.4.260 */
3640function parse_ShrFmla(blob, length, opts) {
3641    var ref = parse_RefU(blob, 6);
3642    blob.l++;
3643    var cUse = blob.read_shift(1);
3644    length -= 8;
3645    return [parse_SharedParsedFormula(blob, length, opts), cUse];
3646}
3647
3648/* 2.4.4 TODO */
3649function parse_Array(blob, length, opts) {
3650    var ref = parse_Ref(blob, 6);
3651    blob.l += 6; length -= 12; /* TODO: fAlwaysCalc */
3652    return [ref, parse_ArrayParsedFormula(blob, length, opts, ref)];
3653}
3654
3655/* 2.4.173 */
3656function parse_MTRSettings(blob, length) {
3657    var fMTREnabled = blob.read_shift(4) !== 0x00;
3658    var fUserSetThreadCount = blob.read_shift(4) !== 0x00;
3659    var cUserThreadCount = blob.read_shift(4);
3660    return [fMTREnabled, fUserSetThreadCount, cUserThreadCount];
3661}
3662
3663/* 2.5.186 TODO: BIFF5 */
3664function parse_NoteSh(blob, length, opts) {
3665    if(opts.biff < 8) return;
3666    var row = blob.read_shift(2), col = blob.read_shift(2);
3667    var flags = blob.read_shift(2), idObj = blob.read_shift(2);
3668    var stAuthor = parse_XLUnicodeString2(blob, 0, opts);
3669    if(opts.biff < 8) blob.read_shift(1);
3670    return [{r:row,c:col}, stAuthor, idObj, flags];
3671}
3672
3673/* 2.4.179 */
3674function parse_Note(blob, length, opts) {
3675    /* TODO: Support revisions */
3676    return parse_NoteSh(blob, length, opts);
3677}
3678
3679/* 2.4.168 */
3680function parse_MergeCells(blob, length) {
3681    var merges = [];
3682    var cmcs = blob.read_shift(2);
3683    while (cmcs--) merges.push(parse_Ref8U(blob,length));
3684    return merges;
3685}
3686
3687/* 2.4.181 TODO: parse all the things! */
3688function parse_Obj(blob, length) {
3689    var cmo = parse_FtCmo(blob, 22); // id, ot, flags
3690    var fts = parse_FtArray(blob, length-22, cmo[1]);
3691    return { cmo: cmo, ft:fts };
3692}
3693
3694/* 2.4.329 TODO: parse properly */
3695function parse_TxO(blob, length, opts) {
3696    var s = blob.l;
3697try {
3698    blob.l += 4;
3699    var ot = (opts.lastobj||{cmo:[0,0]}).cmo[1];
3700    var controlInfo;
3701    if([0,5,7,11,12,14].indexOf(ot) == -1) blob.l += 6;
3702    else controlInfo = parse_ControlInfo(blob, 6, opts);
3703    var cchText = blob.read_shift(2);
3704    var cbRuns = blob.read_shift(2);
3705    var ifntEmpty = parse_FontIndex(blob, 2);
3706    var len = blob.read_shift(2);
3707    blob.l += len;
3708    //var fmla = parse_ObjFmla(blob, s + length - blob.l);
3709
3710    var texts = "";
3711    for(var i = 1; i < blob.lens.length-1; ++i) {
3712        if(blob.l-s != blob.lens[i]) throw "TxO: bad continue record";
3713        var hdr = blob[blob.l];
3714        var t = parse_XLUnicodeStringNoCch(blob, blob.lens[i+1]-blob.lens[i]-1);
3715        texts += t;
3716        if(texts.length >= (hdr ? cchText : 2*cchText)) break;
3717    }
3718    if(texts.length !== cchText && texts.length !== cchText*2) {
3719        throw "cchText: " + cchText + " != " + texts.length;
3720    }
3721
3722    blob.l = s + length;
3723    /* 2.5.272 TxORuns */
3724//  var rgTxoRuns = [];
3725//  for(var j = 0; j != cbRuns/8-1; ++j) blob.l += 8;
3726//  var cchText2 = blob.read_shift(2);
3727//  if(cchText2 !== cchText) throw "TxOLastRun mismatch: " + cchText2 + " " + cchText;
3728//  blob.l += 6;
3729//  if(s + length != blob.l) throw "TxO " + (s + length) + ", at " + blob.l;
3730    return { t: texts };
3731} catch(e) { blob.l = s + length; return { t: texts||"" }; }
3732}
3733
3734/* 2.4.140 */
3735var parse_HLink = function(blob, length) {
3736    var ref = parse_Ref8U(blob, 8);
3737    blob.l += 16; /* CLSID */
3738    var hlink = parse_Hyperlink(blob, length-24);
3739    return [ref, hlink];
3740};
3741
3742/* 2.4.141 */
3743var parse_HLinkTooltip = function(blob, length) {
3744    var end = blob.l + length;
3745    blob.read_shift(2);
3746    var ref = parse_Ref8U(blob, 8);
3747    var wzTooltip = blob.read_shift((length-10)/2, 'dbcs-cont');
3748    wzTooltip = wzTooltip.replace(chr0,"");
3749    return [ref, wzTooltip];
3750};
3751
3752/* 2.4.63 */
3753function parse_Country(blob, length) {
3754    var o = [], d;
3755    d = blob.read_shift(2); o[0] = CountryEnum[d] || d;
3756    d = blob.read_shift(2); o[1] = CountryEnum[d] || d;
3757    return o;
3758}
3759
3760/* 2.4.50 ClrtClient */
3761function parse_ClrtClient(blob, length) {
3762    var ccv = blob.read_shift(2);
3763    var o = [];
3764    while(ccv-->0) o.push(parse_LongRGB(blob, 8));
3765    return o;
3766}
3767
3768/* 2.4.188 */
3769function parse_Palette(blob, length) {
3770    var ccv = blob.read_shift(2);
3771    var o = [];
3772    while(ccv-->0) o.push(parse_LongRGB(blob, 8));
3773    return o;
3774}
3775
3776/* 2.4.354 */
3777function parse_XFCRC(blob, length) {
3778    blob.l += 2;
3779    var o = {cxfs:0, crc:0};
3780    o.cxfs = blob.read_shift(2);
3781    o.crc = blob.read_shift(4);
3782    return o;
3783}
3784
3785
3786var parse_Style = parsenoop;
3787var parse_StyleExt = parsenoop;
3788
3789var parse_ColInfo = parsenoop;
3790
3791var parse_Window2 = parsenoop;
3792
3793
3794var parse_Backup = parsebool; /* 2.4.14 */
3795var parse_Blank = parse_XLSCell; /* 2.4.20 Just the cell */
3796var parse_BottomMargin = parse_Xnum; /* 2.4.27 */
3797var parse_BuiltInFnGroupCount = parseuint16; /* 2.4.30 0x0E or 0x10 but excel 2011 generates 0x11? */
3798var parse_CalcCount = parseuint16; /* 2.4.31 #Iterations */
3799var parse_CalcDelta = parse_Xnum; /* 2.4.32 */
3800var parse_CalcIter = parsebool;  /* 2.4.33 1=iterative calc */
3801var parse_CalcMode = parseuint16; /* 2.4.34 0=manual, 1=auto (def), 2=table */
3802var parse_CalcPrecision = parsebool; /* 2.4.35 */
3803var parse_CalcRefMode = parsenoop2; /* 2.4.36 */
3804var parse_CalcSaveRecalc = parsebool; /* 2.4.37 */
3805var parse_CodePage = parseuint16; /* 2.4.52 */
3806var parse_Compat12 = parsebool; /* 2.4.54 true = no compatibility check */
3807var parse_Date1904 = parsebool; /* 2.4.77 - 1=1904,0=1900 */
3808var parse_DefColWidth = parseuint16; /* 2.4.89 */
3809var parse_DSF = parsenoop2; /* 2.4.94 -- MUST be ignored */
3810var parse_EntExU2 = parsenoop2; /* 2.4.102 -- Explicitly says to ignore */
3811var parse_EOF = parsenoop2; /* 2.4.103 */
3812var parse_Excel9File = parsenoop2; /* 2.4.104 -- Optional and unused */
3813var parse_FeatHdr = parsenoop2; /* 2.4.112 */
3814var parse_FontX = parseuint16; /* 2.4.123 */
3815var parse_Footer = parse_XLHeaderFooter; /* 2.4.124 */
3816var parse_GridSet = parseuint16; /* 2.4.132, =1 */
3817var parse_HCenter = parsebool; /* 2.4.135 sheet centered horizontal on print */
3818var parse_Header = parse_XLHeaderFooter; /* 2.4.136 */
3819var parse_HideObj = parse_HideObjEnum; /* 2.4.139 */
3820var parse_InterfaceEnd = parsenoop2; /* 2.4.145 -- noop */
3821var parse_LeftMargin = parse_Xnum; /* 2.4.151 */
3822var parse_Mms = parsenoop2; /* 2.4.169 -- Explicitly says to ignore */
3823var parse_ObjProtect = parsebool; /* 2.4.183 -- must be 1 if present */
3824var parse_Password = parseuint16; /* 2.4.191 */
3825var parse_PrintGrid = parsebool; /* 2.4.202 */
3826var parse_PrintRowCol = parsebool; /* 2.4.203 */
3827var parse_PrintSize = parseuint16; /* 2.4.204 0:3 */
3828var parse_Prot4Rev = parsebool; /* 2.4.205 */
3829var parse_Prot4RevPass = parseuint16; /* 2.4.206 */
3830var parse_Protect = parsebool; /* 2.4.207 */
3831var parse_RefreshAll = parsebool; /* 2.4.217 -- must be 0 if not template */
3832var parse_RightMargin = parse_Xnum; /* 2.4.219 */
3833var parse_RRTabId = parseuint16a; /* 2.4.241 */
3834var parse_ScenarioProtect = parsebool; /* 2.4.245 */
3835var parse_Scl = parseuint16a; /* 2.4.247 num, den */
3836var parse_String = parse_XLUnicodeString; /* 2.4.268 */
3837var parse_SxBool = parsebool; /* 2.4.274 */
3838var parse_TopMargin = parse_Xnum; /* 2.4.328 */
3839var parse_UsesELFs = parsebool; /* 2.4.337 -- should be 0 */
3840var parse_VCenter = parsebool; /* 2.4.342 */
3841var parse_WinProtect = parsebool; /* 2.4.347 */
3842var parse_WriteProtect = parsenoop; /* 2.4.350 empty record */
3843
3844
3845/* ---- */
3846var parse_VerticalPageBreaks = parsenoop;
3847var parse_HorizontalPageBreaks = parsenoop;
3848var parse_Selection = parsenoop;
3849var parse_Continue = parsenoop;
3850var parse_Pane = parsenoop;
3851var parse_Pls = parsenoop;
3852var parse_DCon = parsenoop;
3853var parse_DConRef = parsenoop;
3854var parse_DConName = parsenoop;
3855var parse_XCT = parsenoop;
3856var parse_CRN = parsenoop;
3857var parse_FileSharing = parsenoop;
3858var parse_Uncalced = parsenoop;
3859var parse_Template = parsenoop;
3860var parse_Intl = parsenoop;
3861var parse_WsBool = parsenoop;
3862var parse_Sort = parsenoop;
3863var parse_Sync = parsenoop;
3864var parse_LPr = parsenoop;
3865var parse_DxGCol = parsenoop;
3866var parse_FnGroupName = parsenoop;
3867var parse_FilterMode = parsenoop;
3868var parse_AutoFilterInfo = parsenoop;
3869var parse_AutoFilter = parsenoop;
3870var parse_Setup = parsenoop;
3871var parse_ScenMan = parsenoop;
3872var parse_SCENARIO = parsenoop;
3873var parse_SxView = parsenoop;
3874var parse_Sxvd = parsenoop;
3875var parse_SXVI = parsenoop;
3876var parse_SxIvd = parsenoop;
3877var parse_SXLI = parsenoop;
3878var parse_SXPI = parsenoop;
3879var parse_DocRoute = parsenoop;
3880var parse_RecipName = parsenoop;
3881var parse_MulBlank = parsenoop;
3882var parse_SXDI = parsenoop;
3883var parse_SXDB = parsenoop;
3884var parse_SXFDB = parsenoop;
3885var parse_SXDBB = parsenoop;
3886var parse_SXNum = parsenoop;
3887var parse_SxErr = parsenoop;
3888var parse_SXInt = parsenoop;
3889var parse_SXString = parsenoop;
3890var parse_SXDtr = parsenoop;
3891var parse_SxNil = parsenoop;
3892var parse_SXTbl = parsenoop;
3893var parse_SXTBRGIITM = parsenoop;
3894var parse_SxTbpg = parsenoop;
3895var parse_ObProj = parsenoop;
3896var parse_SXStreamID = parsenoop;
3897var parse_DBCell = parsenoop;
3898var parse_SXRng = parsenoop;
3899var parse_SxIsxoper = parsenoop;
3900var parse_BookBool = parsenoop;
3901var parse_DbOrParamQry = parsenoop;
3902var parse_OleObjectSize = parsenoop;
3903var parse_SXVS = parsenoop;
3904var parse_BkHim = parsenoop;
3905var parse_MsoDrawingGroup = parsenoop;
3906var parse_MsoDrawing = parsenoop;
3907var parse_MsoDrawingSelection = parsenoop;
3908var parse_PhoneticInfo = parsenoop;
3909var parse_SxRule = parsenoop;
3910var parse_SXEx = parsenoop;
3911var parse_SxFilt = parsenoop;
3912var parse_SxDXF = parsenoop;
3913var parse_SxItm = parsenoop;
3914var parse_SxName = parsenoop;
3915var parse_SxSelect = parsenoop;
3916var parse_SXPair = parsenoop;
3917var parse_SxFmla = parsenoop;
3918var parse_SxFormat = parsenoop;
3919var parse_SXVDEx = parsenoop;
3920var parse_SXFormula = parsenoop;
3921var parse_SXDBEx = parsenoop;
3922var parse_RRDInsDel = parsenoop;
3923var parse_RRDHead = parsenoop;
3924var parse_RRDChgCell = parsenoop;
3925var parse_RRDRenSheet = parsenoop;
3926var parse_RRSort = parsenoop;
3927var parse_RRDMove = parsenoop;
3928var parse_RRFormat = parsenoop;
3929var parse_RRAutoFmt = parsenoop;
3930var parse_RRInsertSh = parsenoop;
3931var parse_RRDMoveBegin = parsenoop;
3932var parse_RRDMoveEnd = parsenoop;
3933var parse_RRDInsDelBegin = parsenoop;
3934var parse_RRDInsDelEnd = parsenoop;
3935var parse_RRDConflict = parsenoop;
3936var parse_RRDDefName = parsenoop;
3937var parse_RRDRstEtxp = parsenoop;
3938var parse_LRng = parsenoop;
3939var parse_CUsr = parsenoop;
3940var parse_CbUsr = parsenoop;
3941var parse_UsrInfo = parsenoop;
3942var parse_UsrExcl = parsenoop;
3943var parse_FileLock = parsenoop;
3944var parse_RRDInfo = parsenoop;
3945var parse_BCUsrs = parsenoop;
3946var parse_UsrChk = parsenoop;
3947var parse_UserBView = parsenoop;
3948var parse_UserSViewBegin = parsenoop; // overloaded
3949var parse_UserSViewEnd = parsenoop;
3950var parse_RRDUserView = parsenoop;
3951var parse_Qsi = parsenoop;
3952var parse_CondFmt = parsenoop;
3953var parse_CF = parsenoop;
3954var parse_DVal = parsenoop;
3955var parse_DConBin = parsenoop;
3956var parse_Lel = parsenoop;
3957var parse_XLSCodeName = parse_XLUnicodeString;
3958var parse_SXFDBType = parsenoop;
3959var parse_ObNoMacros = parsenoop;
3960var parse_Dv = parsenoop;
3961var parse_Index = parsenoop;
3962var parse_Table = parsenoop;
3963var parse_BigName = parsenoop;
3964var parse_ContinueBigName = parsenoop;
3965var parse_WebPub = parsenoop;
3966var parse_QsiSXTag = parsenoop;
3967var parse_DBQueryExt = parsenoop;
3968var parse_ExtString = parsenoop;
3969var parse_TxtQry = parsenoop;
3970var parse_Qsir = parsenoop;
3971var parse_Qsif = parsenoop;
3972var parse_RRDTQSIF = parsenoop;
3973var parse_OleDbConn = parsenoop;
3974var parse_WOpt = parsenoop;
3975var parse_SXViewEx = parsenoop;
3976var parse_SXTH = parsenoop;
3977var parse_SXPIEx = parsenoop;
3978var parse_SXVDTEx = parsenoop;
3979var parse_SXViewEx9 = parsenoop;
3980var parse_ContinueFrt = parsenoop;
3981var parse_RealTimeData = parsenoop;
3982var parse_ChartFrtInfo = parsenoop;
3983var parse_FrtWrapper = parsenoop;
3984var parse_StartBlock = parsenoop;
3985var parse_EndBlock = parsenoop;
3986var parse_StartObject = parsenoop;
3987var parse_EndObject = parsenoop;
3988var parse_CatLab = parsenoop;
3989var parse_YMult = parsenoop;
3990var parse_SXViewLink = parsenoop;
3991var parse_PivotChartBits = parsenoop;
3992var parse_FrtFontList = parsenoop;
3993var parse_SheetExt = parsenoop;
3994var parse_BookExt = parsenoop;
3995var parse_SXAddl = parsenoop;
3996var parse_CrErr = parsenoop;
3997var parse_HFPicture = parsenoop;
3998var parse_Feat = parsenoop;
3999var parse_DataLabExt = parsenoop;
4000var parse_DataLabExtContents = parsenoop;
4001var parse_CellWatch = parsenoop;
4002var parse_FeatHdr11 = parsenoop;
4003var parse_Feature11 = parsenoop;
4004var parse_DropDownObjIds = parsenoop;
4005var parse_ContinueFrt11 = parsenoop;
4006var parse_DConn = parsenoop;
4007var parse_List12 = parsenoop;
4008var parse_Feature12 = parsenoop;
4009var parse_CondFmt12 = parsenoop;
4010var parse_CF12 = parsenoop;
4011var parse_CFEx = parsenoop;
4012var parse_AutoFilter12 = parsenoop;
4013var parse_ContinueFrt12 = parsenoop;
4014var parse_MDTInfo = parsenoop;
4015var parse_MDXStr = parsenoop;
4016var parse_MDXTuple = parsenoop;
4017var parse_MDXSet = parsenoop;
4018var parse_MDXProp = parsenoop;
4019var parse_MDXKPI = parsenoop;
4020var parse_MDB = parsenoop;
4021var parse_PLV = parsenoop;
4022var parse_DXF = parsenoop;
4023var parse_TableStyles = parsenoop;
4024var parse_TableStyle = parsenoop;
4025var parse_TableStyleElement = parsenoop;
4026var parse_NamePublish = parsenoop;
4027var parse_NameCmt = parsenoop;
4028var parse_SortData = parsenoop;
4029var parse_GUIDTypeLib = parsenoop;
4030var parse_FnGrp12 = parsenoop;
4031var parse_NameFnGrp12 = parsenoop;
4032var parse_HeaderFooter = parsenoop;
4033var parse_CrtLayout12 = parsenoop;
4034var parse_CrtMlFrt = parsenoop;
4035var parse_CrtMlFrtContinue = parsenoop;
4036var parse_ShapePropsStream = parsenoop;
4037var parse_TextPropsStream = parsenoop;
4038var parse_RichTextStream = parsenoop;
4039var parse_CrtLayout12A = parsenoop;
4040var parse_Units = parsenoop;
4041var parse_Chart = parsenoop;
4042var parse_Series = parsenoop;
4043var parse_DataFormat = parsenoop;
4044var parse_LineFormat = parsenoop;
4045var parse_MarkerFormat = parsenoop;
4046var parse_AreaFormat = parsenoop;
4047var parse_PieFormat = parsenoop;
4048var parse_AttachedLabel = parsenoop;
4049var parse_SeriesText = parsenoop;
4050var parse_ChartFormat = parsenoop;
4051var parse_Legend = parsenoop;
4052var parse_SeriesList = parsenoop;
4053var parse_Bar = parsenoop;
4054var parse_Line = parsenoop;
4055var parse_Pie = parsenoop;
4056var parse_Area = parsenoop;
4057var parse_Scatter = parsenoop;
4058var parse_CrtLine = parsenoop;
4059var parse_Axis = parsenoop;
4060var parse_Tick = parsenoop;
4061var parse_ValueRange = parsenoop;
4062var parse_CatSerRange = parsenoop;
4063var parse_AxisLine = parsenoop;
4064var parse_CrtLink = parsenoop;
4065var parse_DefaultText = parsenoop;
4066var parse_Text = parsenoop;
4067var parse_ObjectLink = parsenoop;
4068var parse_Frame = parsenoop;
4069var parse_Begin = parsenoop;
4070var parse_End = parsenoop;
4071var parse_PlotArea = parsenoop;
4072var parse_Chart3d = parsenoop;
4073var parse_PicF = parsenoop;
4074var parse_DropBar = parsenoop;
4075var parse_Radar = parsenoop;
4076var parse_Surf = parsenoop;
4077var parse_RadarArea = parsenoop;
4078var parse_AxisParent = parsenoop;
4079var parse_LegendException = parsenoop;
4080var parse_ShtProps = parsenoop;
4081var parse_SerToCrt = parsenoop;
4082var parse_AxesUsed = parsenoop;
4083var parse_SBaseRef = parsenoop;
4084var parse_SerParent = parsenoop;
4085var parse_SerAuxTrend = parsenoop;
4086var parse_IFmtRecord = parsenoop;
4087var parse_Pos = parsenoop;
4088var parse_AlRuns = parsenoop;
4089var parse_BRAI = parsenoop;
4090var parse_SerAuxErrBar = parsenoop;
4091var parse_SerFmt = parsenoop;
4092var parse_Chart3DBarShape = parsenoop;
4093var parse_Fbi = parsenoop;
4094var parse_BopPop = parsenoop;
4095var parse_AxcExt = parsenoop;
4096var parse_Dat = parsenoop;
4097var parse_PlotGrowth = parsenoop;
4098var parse_SIIndex = parsenoop;
4099var parse_GelFrame = parsenoop;
4100var parse_BopPopCustom = parsenoop;
4101var parse_Fbi2 = parsenoop;
4102
4103/* --- Specific to versions before BIFF8 --- */
4104function parse_BIFF5String(blob) {
4105    var len = blob.read_shift(1);
4106    return blob.read_shift(len, 'sbcs-cont');
4107}
4108
4109/* BIFF2_??? where ??? is the name from [XLS] */
4110function parse_BIFF2STR(blob, length, opts) {
4111    var cell = parse_XLSCell(blob, 6);
4112    ++blob.l;
4113    var str = parse_XLUnicodeString2(blob, length-7, opts);
4114    cell.val = str;
4115    return cell;
4116}
4117
4118function parse_BIFF2NUM(blob, length, opts) {
4119    var cell = parse_XLSCell(blob, 6);
4120    ++blob.l;
4121    var num = parse_Xnum(blob, 8);
4122    cell.val = num;
4123    return cell;
4124}
4125
4126/* 18.4.1 charset to codepage mapping */
4127var CS2CP = {
4128    0:    1252, /* ANSI */
4129    1:   65001, /* DEFAULT */
4130    2:   65001, /* SYMBOL */
4131    77:  10000, /* MAC */
4132    128:   932, /* SHIFTJIS */
4133    129:   949, /* HANGUL */
4134    130:  1361, /* JOHAB */
4135    134:   936, /* GB2312 */
4136    136:   950, /* CHINESEBIG5 */
4137    161:  1253, /* GREEK */
4138    162:  1254, /* TURKISH */
4139    163:  1258, /* VIETNAMESE */
4140    177:  1255, /* HEBREW */
4141    178:  1256, /* ARABIC */
4142    186:  1257, /* BALTIC */
4143    204:  1251, /* RUSSIAN */
4144    222:   874, /* THAI */
4145    238:  1250, /* EASTEUROPE */
4146    255:  1252, /* OEM */
4147    69:   6969  /* MISC */
4148};
4149
4150/* Parse a list of <r> tags */
4151var parse_rs = (function parse_rs_factory() {
4152    var tregex = matchtag("t"), rpregex = matchtag("rPr"), rregex = /<r>/g, rend = /<\/r>/, nlregex = /\r\n/g;
4153    /* 18.4.7 rPr CT_RPrElt */
4154    var parse_rpr = function parse_rpr(rpr, intro, outro) {
4155        var font = {}, cp = 65001;
4156        var m = rpr.match(tagregex), i = 0;
4157        if(m) for(;i!=m.length; ++i) {
4158            var y = parsexmltag(m[i]);
4159            switch(y[0]) {
4160                /* 18.8.12 condense CT_BooleanProperty */
4161                /* ** not required . */
4162                case '<condense': break;
4163                /* 18.8.17 extend CT_BooleanProperty */
4164                /* ** not required . */
4165                case '<extend': break;
4166                /* 18.8.36 shadow CT_BooleanProperty */
4167                /* ** not required . */
4168                case '<shadow':
4169                    /* falls through */
4170                case '<shadow/>': break;
4171
4172                /* 18.4.1 charset CT_IntProperty TODO */
4173                case '<charset':
4174                    if(y.val == '1') break;
4175                    cp = CS2CP[parseInt(y.val, 10)];
4176                    break;
4177
4178                /* 18.4.2 outline CT_BooleanProperty TODO */
4179                case '<outline':
4180                    /* falls through */
4181                case '<outline/>': break;
4182
4183                /* 18.4.5 rFont CT_FontName */
4184                case '<rFont': font.name = y.val; break;
4185
4186                /* 18.4.11 sz CT_FontSize */
4187                case '<sz': font.sz = y.val; break;
4188
4189                /* 18.4.10 strike CT_BooleanProperty */
4190                case '<strike':
4191                    if(!y.val) break;
4192                    /* falls through */
4193                case '<strike/>': font.strike = 1; break;
4194                case '</strike>': break;
4195
4196                /* 18.4.13 u CT_UnderlineProperty */
4197                case '<u':
4198                    if(!y.val) break;
4199                    /* falls through */
4200                case '<u/>': font.u = 1; break;
4201                case '</u>': break;
4202
4203                /* 18.8.2 b */
4204                case '<b':
4205                    if(!y.val) break;
4206                    /* falls through */
4207                case '<b/>': font.b = 1; break;
4208                case '</b>': break;
4209
4210                /* 18.8.26 i */
4211                case '<i':
4212                    if(!y.val) break;
4213                    /* falls through */
4214                case '<i/>': font.i = 1; break;
4215                case '</i>': break;
4216
4217                /* 18.3.1.15 color CT_Color TODO: tint, theme, auto, indexed */
4218                case '<color':
4219                    if(y.rgb) font.color = y.rgb.substr(2,6);
4220                    break;
4221
4222                /* 18.8.18 family ST_FontFamily */
4223                case '<family': font.family = y.val; break;
4224
4225                /* 18.4.14 vertAlign CT_VerticalAlignFontProperty TODO */
4226                case '<vertAlign': break;
4227
4228                /* 18.8.35 scheme CT_FontScheme TODO */
4229                case '<scheme': break;
4230
4231                default:
4232                    if(y[0].charCodeAt(1) !== 47) throw 'Unrecognized rich format ' + y[0];
4233            }
4234        }
4235        /* TODO: These should be generated styles, not inline */
4236        var style = [];
4237        if(font.b) style.push("font-weight: bold;");
4238        if(font.i) style.push("font-style: italic;");
4239        intro.push('<span style="' + style.join("") + '">');
4240        outro.push("</span>");
4241        return cp;
4242    };
4243
4244    /* 18.4.4 r CT_RElt */
4245    function parse_r(r) {
4246        var terms = [[],"",[]];
4247        /* 18.4.12 t ST_Xstring */
4248        var t = r.match(tregex), cp = 65001;
4249        if(!isval(t)) return "";
4250        terms[1] = t[1];
4251
4252        var rpr = r.match(rpregex);
4253        if(isval(rpr)) cp = parse_rpr(rpr[1], terms[0], terms[2]);
4254
4255        return terms[0].join("") + terms[1].replace(nlregex,'<br/>') + terms[2].join("");
4256    }
4257    return function parse_rs(rs) {
4258        return rs.replace(rregex,"").split(rend).map(parse_r).join("");
4259    };
4260})();
4261
4262/* 18.4.8 si CT_Rst */
4263var sitregex = /<t[^>]*>([^<]*)<\/t>/g, sirregex = /<r>/;
4264function parse_si(x, opts) {
4265    var html = opts ? opts.cellHTML : true;
4266    var z = {};
4267    if(!x) return null;
4268    var y;
4269    /* 18.4.12 t ST_Xstring (Plaintext String) */
4270    if(x.charCodeAt(1) === 116) {
4271        z.t = utf8read(unescapexml(x.substr(x.indexOf(">")+1).split(/<\/t>/)[0]));
4272        z.r = x;
4273        if(html) z.h = z.t;
4274    }
4275    /* 18.4.4 r CT_RElt (Rich Text Run) */
4276    else if((y = x.match(sirregex))) {
4277        z.r = x;
4278        z.t = utf8read(unescapexml(x.match(sitregex).join("").replace(tagregex,"")));
4279        if(html) z.h = parse_rs(x);
4280    }
4281    /* 18.4.3 phoneticPr CT_PhoneticPr (TODO: needed for Asian support) */
4282    /* 18.4.6 rPh CT_PhoneticRun (TODO: needed for Asian support) */
4283    return z;
4284}
4285
4286/* 18.4 Shared String Table */
4287var sstr0 = /<sst([^>]*)>([\s\S]*)<\/sst>/;
4288var sstr1 = /<(?:si|sstItem)>/g;
4289var sstr2 = /<\/(?:si|sstItem)>/;
4290function parse_sst_xml(data, opts) {
4291    var s = [], ss;
4292    /* 18.4.9 sst CT_Sst */
4293    var sst = data.match(sstr0);
4294    if(isval(sst)) {
4295        ss = sst[2].replace(sstr1,"").split(sstr2);
4296        for(var i = 0; i != ss.length; ++i) {
4297            var o = parse_si(ss[i], opts);
4298            if(o != null) s[s.length] = o;
4299        }
4300        sst = parsexmltag(sst[1]); s.Count = sst.count; s.Unique = sst.uniqueCount;
4301    }
4302    return s;
4303}
4304
4305RELS.SST = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings";
4306var straywsregex = /^\s|\s$|[\t\n\r]/;
4307function write_sst_xml(sst, opts) {
4308    if(!opts.bookSST) return "";
4309    var o = [XML_HEADER];
4310    o[o.length] = (writextag('sst', null, {
4311        xmlns: XMLNS.main[0],
4312        count: sst.Count,
4313        uniqueCount: sst.Unique
4314    }));
4315    for(var i = 0; i != sst.length; ++i) { if(sst[i] == null) continue;
4316        var s = sst[i];
4317        var sitag = "<si>";
4318        if(s.r) sitag += s.r;
4319        else {
4320            sitag += "<t";
4321            if(s.t.match(straywsregex)) sitag += ' xml:space="preserve"';
4322            sitag += ">" + escapexml(s.t) + "</t>";
4323        }
4324        sitag += "</si>";
4325        o[o.length] = (sitag);
4326    }
4327    if(o.length>2){ o[o.length] = ('</sst>'); o[1]=o[1].replace("/>",">"); }
4328    return o.join("");
4329}
4330/* [MS-XLSB] 2.4.219 BrtBeginSst */
4331function parse_BrtBeginSst(data, length) {
4332    return [data.read_shift(4), data.read_shift(4)];
4333}
4334
4335/* [MS-XLSB] 2.1.7.45 Shared Strings */
4336function parse_sst_bin(data, opts) {
4337    var s = [];
4338    var pass = false;
4339    recordhopper(data, function hopper_sst(val, R, RT) {
4340        switch(R.n) {
4341            case 'BrtBeginSst': s.Count = val[0]; s.Unique = val[1]; break;
4342            case 'BrtSSTItem': s.push(val); break;
4343            case 'BrtEndSst': return true;
4344            /* TODO: produce a test case with a future record */
4345            case 'BrtFRTBegin': pass = true; break;
4346            case 'BrtFRTEnd': pass = false; break;
4347            default: if(!pass || opts.WTF) throw new Error("Unexpected record " + RT + " " + R.n);
4348        }
4349    });
4350    return s;
4351}
4352
4353function write_BrtBeginSst(sst, o) {
4354    if(!o) o = new_buf(8);
4355    o.write_shift(4, sst.Count);
4356    o.write_shift(4, sst.Unique);
4357    return o;
4358}
4359
4360var write_BrtSSTItem = write_RichStr;
4361
4362function write_sst_bin(sst, opts) {
4363    var ba = buf_array();
4364    write_record(ba, "BrtBeginSst", write_BrtBeginSst(sst));
4365    for(var i = 0; i < sst.length; ++i) write_record(ba, "BrtSSTItem", write_BrtSSTItem(sst[i]));
4366    write_record(ba, "BrtEndSst");
4367    return ba.end();
4368}
4369function _JS2ANSI(str) { if(typeof cptable !== 'undefined') return cptable.utils.encode(1252, str); return str.split("").map(function(x) { return x.charCodeAt(0); }); }
4370
4371/* [MS-OFFCRYPTO] 2.1.4 Version */
4372function parse_Version(blob, length) {
4373    var o = {};
4374    o.Major = blob.read_shift(2);
4375    o.Minor = blob.read_shift(2);
4376    return o;
4377}
4378/* [MS-OFFCRYPTO] 2.3.2 Encryption Header */
4379function parse_EncryptionHeader(blob, length) {
4380    var o = {};
4381    o.Flags = blob.read_shift(4);
4382
4383    // Check if SizeExtra is 0x00000000
4384    var tmp = blob.read_shift(4);
4385    if(tmp !== 0) throw 'Unrecognized SizeExtra: ' + tmp;
4386
4387    o.AlgID = blob.read_shift(4);
4388    switch(o.AlgID) {
4389        case 0: case 0x6801: case 0x660E: case 0x660F: case 0x6610: break;
4390        default: throw 'Unrecognized encryption algorithm: ' + o.AlgID;
4391    }
4392    parsenoop(blob, length-12);
4393    return o;
4394}
4395
4396/* [MS-OFFCRYPTO] 2.3.3 Encryption Verifier */
4397function parse_EncryptionVerifier(blob, length) {
4398    return parsenoop(blob, length);
4399}
4400/* [MS-OFFCRYPTO] 2.3.5.1 RC4 CryptoAPI Encryption Header */
4401function parse_RC4CryptoHeader(blob, length) {
4402    var o = {};
4403    var vers = o.EncryptionVersionInfo = parse_Version(blob, 4); length -= 4;
4404    if(vers.Minor != 2) throw 'unrecognized minor version code: ' + vers.Minor;
4405    if(vers.Major > 4 || vers.Major < 2) throw 'unrecognized major version code: ' + vers.Major;
4406    o.Flags = blob.read_shift(4); length -= 4;
4407    var sz = blob.read_shift(4); length -= 4;
4408    o.EncryptionHeader = parse_EncryptionHeader(blob, sz); length -= sz;
4409    o.EncryptionVerifier = parse_EncryptionVerifier(blob, length);
4410    return o;
4411}
4412/* [MS-OFFCRYPTO] 2.3.6.1 RC4 Encryption Header */
4413function parse_RC4Header(blob, length) {
4414    var o = {};
4415    var vers = o.EncryptionVersionInfo = parse_Version(blob, 4); length -= 4;
4416    if(vers.Major != 1 || vers.Minor != 1) throw 'unrecognized version code ' + vers.Major + ' : ' + vers.Minor;
4417    o.Salt = blob.read_shift(16);
4418    o.EncryptedVerifier = blob.read_shift(16);
4419    o.EncryptedVerifierHash = blob.read_shift(16);
4420    return o;
4421}
4422
4423/* [MS-OFFCRYPTO] 2.3.7.1 Binary Document Password Verifier Derivation */
4424function crypto_CreatePasswordVerifier_Method1(Password) {
4425    var Verifier = 0x0000, PasswordArray;
4426    var PasswordDecoded = _JS2ANSI(Password);
4427    var len = PasswordDecoded.length + 1, i, PasswordByte;
4428    var Intermediate1, Intermediate2, Intermediate3;
4429    PasswordArray = new_raw_buf(len);
4430    PasswordArray[0] = PasswordDecoded.length;
4431    for(i = 1; i != len; ++i) PasswordArray[i] = PasswordDecoded[i-1];
4432    for(i = len-1; i >= 0; --i) {
4433        PasswordByte = PasswordArray[i];
4434        Intermediate1 = ((Verifier & 0x4000) === 0x0000) ? 0 : 1;
4435        Intermediate2 = (Verifier << 1) & 0x7FFF;
4436        Intermediate3 = Intermediate1 | Intermediate2;
4437        Verifier = Intermediate3 ^ PasswordByte;
4438    }
4439    return Verifier ^ 0xCE4B;
4440}
4441
4442/* [MS-OFFCRYPTO] 2.3.7.2 Binary Document XOR Array Initialization */
4443var crypto_CreateXorArray_Method1 = (function() {
4444    var PadArray = [0xBB, 0xFF, 0xFF, 0xBA, 0xFF, 0xFF, 0xB9, 0x80, 0x00, 0xBE, 0x0F, 0x00, 0xBF, 0x0F, 0x00];
4445    var InitialCode = [0xE1F0, 0x1D0F, 0xCC9C, 0x84C0, 0x110C, 0x0E10, 0xF1CE, 0x313E, 0x1872, 0xE139, 0xD40F, 0x84F9, 0x280C, 0xA96A, 0x4EC3];
4446    var XorMatrix = [0xAEFC, 0x4DD9, 0x9BB2, 0x2745, 0x4E8A, 0x9D14, 0x2A09, 0x7B61, 0xF6C2, 0xFDA5, 0xEB6B, 0xC6F7, 0x9DCF, 0x2BBF, 0x4563, 0x8AC6, 0x05AD, 0x0B5A, 0x16B4, 0x2D68, 0x5AD0, 0x0375, 0x06EA, 0x0DD4, 0x1BA8, 0x3750, 0x6EA0, 0xDD40, 0xD849, 0xA0B3, 0x5147, 0xA28E, 0x553D, 0xAA7A, 0x44D5, 0x6F45, 0xDE8A, 0xAD35, 0x4A4B, 0x9496, 0x390D, 0x721A, 0xEB23, 0xC667, 0x9CEF, 0x29FF, 0x53FE, 0xA7FC, 0x5FD9, 0x47D3, 0x8FA6, 0x0F6D, 0x1EDA, 0x3DB4, 0x7B68, 0xF6D0, 0xB861, 0x60E3, 0xC1C6, 0x93AD, 0x377B, 0x6EF6, 0xDDEC, 0x45A0, 0x8B40, 0x06A1, 0x0D42, 0x1A84, 0x3508, 0x6A10, 0xAA51, 0x4483, 0x8906, 0x022D, 0x045A, 0x08B4, 0x1168, 0x76B4, 0xED68, 0xCAF1, 0x85C3, 0x1BA7, 0x374E, 0x6E9C, 0x3730, 0x6E60, 0xDCC0, 0xA9A1, 0x4363, 0x86C6, 0x1DAD, 0x3331, 0x6662, 0xCCC4, 0x89A9, 0x0373, 0x06E6, 0x0DCC, 0x1021, 0x2042, 0x4084, 0x8108, 0x1231, 0x2462, 0x48C4];
4447    var Ror = function(Byte) { return ((Byte/2) | (Byte*128)) & 0xFF; };
4448    var XorRor = function(byte1, byte2) { return Ror(byte1 ^ byte2); };
4449    var CreateXorKey_Method1 = function(Password) {
4450        var XorKey = InitialCode[Password.length - 1];
4451        var CurrentElement = 0x68;
4452        for(var i = Password.length-1; i >= 0; --i) {
4453            var Char = Password[i];
4454            for(var j = 0; j != 7; ++j) {
4455                if(Char & 0x40) XorKey ^= XorMatrix[CurrentElement];
4456                Char *= 2; --CurrentElement;
4457            }
4458        }
4459        return XorKey;
4460    };
4461    return function(password) {
4462        var Password = _JS2ANSI(password);
4463        var XorKey = CreateXorKey_Method1(Password);
4464        var Index = Password.length;
4465        var ObfuscationArray = new_raw_buf(16);
4466        for(var i = 0; i != 16; ++i) ObfuscationArray[i] = 0x00;
4467        var Temp, PasswordLastChar, PadIndex;
4468        if((Index & 1) === 1) {
4469            Temp = XorKey >> 8;
4470            ObfuscationArray[Index] = XorRor(PadArray[0], Temp);
4471            --Index;
4472            Temp = XorKey & 0xFF;
4473            PasswordLastChar = Password[Password.length - 1];
4474            ObfuscationArray[Index] = XorRor(PasswordLastChar, Temp);
4475        }
4476        while(Index > 0) {
4477            --Index;
4478            Temp = XorKey >> 8;
4479            ObfuscationArray[Index] = XorRor(Password[Index], Temp);
4480            --Index;
4481            Temp = XorKey & 0xFF;
4482            ObfuscationArray[Index] = XorRor(Password[Index], Temp);
4483        }
4484        Index = 15;
4485        PadIndex = 15 - Password.length;
4486        while(PadIndex > 0) {
4487            Temp = XorKey >> 8;
4488            ObfuscationArray[Index] = XorRor(PadArray[PadIndex], Temp);
4489            --Index;
4490            --PadIndex;
4491            Temp = XorKey & 0xFF;
4492            ObfuscationArray[Index] = XorRor(Password[Index], Temp);
4493            --Index;
4494            --PadIndex;
4495        }
4496        return ObfuscationArray;
4497    };
4498})();
4499
4500/* [MS-OFFCRYPTO] 2.3.7.3 Binary Document XOR Data Transformation Method 1 */
4501var crypto_DecryptData_Method1 = function(password, Data, XorArrayIndex, XorArray, O) {
4502    /* If XorArray is set, use it; if O is not set, make changes in-place */
4503    if(!O) O = Data;
4504    if(!XorArray) XorArray = crypto_CreateXorArray_Method1(password);
4505    var Index, Value;
4506    for(Index = 0; Index != Data.length; ++Index) {
4507        Value = Data[Index];
4508        Value ^= XorArray[XorArrayIndex];
4509        Value = ((Value>>5) | (Value<<3)) & 0xFF;
4510        O[Index] = Value;
4511        ++XorArrayIndex;
4512    }
4513    return [O, XorArrayIndex, XorArray];
4514};
4515
4516var crypto_MakeXorDecryptor = function(password) {
4517    var XorArrayIndex = 0, XorArray = crypto_CreateXorArray_Method1(password);
4518    return function(Data) {
4519        var O = crypto_DecryptData_Method1(null, Data, XorArrayIndex, XorArray);
4520        XorArrayIndex = O[1];
4521        return O[0];
4522    };
4523};
4524
4525/* 2.5.343 */
4526function parse_XORObfuscation(blob, length, opts, out) {
4527    var o = { key: parseuint16(blob), verificationBytes: parseuint16(blob) };
4528    if(opts.password) o.verifier = crypto_CreatePasswordVerifier_Method1(opts.password);
4529    out.valid = o.verificationBytes === o.verifier;
4530    if(out.valid) out.insitu_decrypt = crypto_MakeXorDecryptor(opts.password);
4531    return o;
4532}
4533
4534/* 2.4.117 */
4535function parse_FilePassHeader(blob, length, oo) {
4536    var o = oo || {}; o.Info = blob.read_shift(2); blob.l -= 2;
4537    if(o.Info === 1) o.Data = parse_RC4Header(blob, length);
4538    else o.Data = parse_RC4CryptoHeader(blob, length);
4539    return o;
4540}
4541function parse_FilePass(blob, length, opts) {
4542    var o = { Type: blob.read_shift(2) }; /* wEncryptionType */
4543    if(o.Type) parse_FilePassHeader(blob, length-2, o);
4544    else parse_XORObfuscation(blob, length-2, opts, o);
4545    return o;
4546}
4547
4548
4549function hex2RGB(h) {
4550    var o = h.substr(h[0]==="#"?1:0,6);
4551    return [parseInt(o.substr(0,2),16),parseInt(o.substr(0,2),16),parseInt(o.substr(0,2),16)];
4552}
4553function rgb2Hex(rgb) {
4554    for(var i=0,o=1; i!=3; ++i) o = o*256 + (rgb[i]>255?255:rgb[i]<0?0:rgb[i]);
4555    return o.toString(16).toUpperCase().substr(1);
4556}
4557
4558function rgb2HSL(rgb) {
4559    var R = rgb[0]/255, G = rgb[1]/255, B=rgb[2]/255;
4560    var M = Math.max(R, G, B), m = Math.min(R, G, B), C = M - m;
4561    if(C === 0) return [0, 0, R];
4562
4563    var H6 = 0, S = 0, L2 = (M + m);
4564    S = C / (L2 > 1 ? 2 - L2 : L2);
4565    switch(M){
4566        case R: H6 = ((G - B) / C + 6)%6; break;
4567        case G: H6 = ((B - R) / C + 2); break;
4568        case B: H6 = ((R - G) / C + 4); break;
4569    }
4570    return [H6 / 6, S, L2 / 2];
4571}
4572
4573function hsl2RGB(hsl){
4574    var H = hsl[0], S = hsl[1], L = hsl[2];
4575    var C = S * 2 * (L < 0.5 ? L : 1 - L), m = L - C/2;
4576    var rgb = [m,m,m], h6 = 6*H;
4577
4578    var X;
4579    if(S !== 0) switch(h6|0) {
4580        case 0: case 6: X = C * h6; rgb[0] += C; rgb[1] += X; break;
4581        case 1: X = C * (2 - h6);   rgb[0] += X; rgb[1] += C; break;
4582        case 2: X = C * (h6 - 2);   rgb[1] += C; rgb[2] += X; break;
4583        case 3: X = C * (4 - h6);   rgb[1] += X; rgb[2] += C; break;
4584        case 4: X = C * (h6 - 4);   rgb[2] += C; rgb[0] += X; break;
4585        case 5: X = C * (6 - h6);   rgb[2] += X; rgb[0] += C; break;
4586    }
4587    for(var i = 0; i != 3; ++i) rgb[i] = Math.round(rgb[i]*255);
4588    return rgb;
4589}
4590
4591/* 18.8.3 bgColor tint algorithm */
4592function rgb_tint(hex, tint) {
4593    if(tint === 0) return hex;
4594    var hsl = rgb2HSL(hex2RGB(hex));
4595    if (tint < 0) hsl[2] = hsl[2] * (1 + tint);
4596    else hsl[2] = 1 - (1 - hsl[2]) * (1 - tint);
4597    return rgb2Hex(hsl2RGB(hsl));
4598}
4599
4600/* 18.3.1.13 width calculations */
4601var DEF_MDW = 7, MAX_MDW = 15, MIN_MDW = 1, MDW = DEF_MDW;
4602function width2px(width) { return (( width + ((128/MDW)|0)/256 )* MDW )|0; }
4603function px2char(px) { return (((px - 5)/MDW * 100 + 0.5)|0)/100; }
4604function char2width(chr) { return (((chr * MDW + 5)/MDW*256)|0)/256; }
4605function cycle_width(collw) { return char2width(px2char(width2px(collw))); }
4606function find_mdw(collw, coll) {
4607    if(cycle_width(collw) != collw) {
4608        for(MDW=DEF_MDW; MDW>MIN_MDW; --MDW) if(cycle_width(collw) === collw) break;
4609        if(MDW === MIN_MDW) for(MDW=DEF_MDW+1; MDW<MAX_MDW; ++MDW) if(cycle_width(collw) === collw) break;
4610        if(MDW === MAX_MDW) MDW = DEF_MDW;
4611    }
4612}
4613
4614/* [MS-EXSPXML3] 2.4.54 ST_enmPattern */
4615var XLMLPatternTypeMap = {
4616    "None": "none",
4617    "Solid": "solid",
4618    "Gray50": "mediumGray",
4619    "Gray75": "darkGray",
4620    "Gray25": "lightGray",
4621    "HorzStripe": "darkHorizontal",
4622    "VertStripe": "darkVertical",
4623    "ReverseDiagStripe": "darkDown",
4624    "DiagStripe": "darkUp",
4625    "DiagCross": "darkGrid",
4626    "ThickDiagCross": "darkTrellis",
4627    "ThinHorzStripe": "lightHorizontal",
4628    "ThinVertStripe": "lightVertical",
4629    "ThinReverseDiagStripe": "lightDown",
4630    "ThinHorzCross": "lightGrid"
4631};
4632
4633var styles = {}; // shared styles
4634
4635var themes = {}; // shared themes
4636
4637/* 18.8.21 fills CT_Fills */
4638function parse_fills(t, opts) {
4639    styles.Fills = [];
4640    var fill = {};
4641    t[0].match(tagregex).forEach(function(x) {
4642        var y = parsexmltag(x);
4643        switch(y[0]) {
4644            case '<fills': case '<fills>': case '</fills>': break;
4645
4646            /* 18.8.20 fill CT_Fill */
4647            case '<fill>': break;
4648            case '</fill>': styles.Fills.push(fill); fill = {}; break;
4649
4650            /* 18.8.32 patternFill CT_PatternFill */
4651            case '<patternFill':
4652                if(y.patternType) fill.patternType = y.patternType;
4653                break;
4654            case '<patternFill/>': case '</patternFill>': break;
4655
4656            /* 18.8.3 bgColor CT_Color */
4657            case '<bgColor':
4658                if(!fill.bgColor) fill.bgColor = {};
4659                if(y.indexed) fill.bgColor.indexed = parseInt(y.indexed, 10);
4660                if(y.theme) fill.bgColor.theme = parseInt(y.theme, 10);
4661                if(y.tint) fill.bgColor.tint = parseFloat(y.tint);
4662                /* Excel uses ARGB strings */
4663                if(y.rgb) fill.bgColor.rgb = y.rgb.substring(y.rgb.length - 6);
4664                break;
4665            case '<bgColor/>': case '</bgColor>': break;
4666
4667            /* 18.8.19 fgColor CT_Color */
4668            case '<fgColor':
4669                if(!fill.fgColor) fill.fgColor = {};
4670                if(y.theme) fill.fgColor.theme = parseInt(y.theme, 10);
4671                if(y.tint) fill.fgColor.tint = parseFloat(y.tint);
4672                /* Excel uses ARGB strings */
4673                if(y.rgb) fill.fgColor.rgb = y.rgb.substring(y.rgb.length - 6);
4674                break;
4675            case '<fgColor/>': case '</fgColor>': break;
4676
4677            default: if(opts.WTF) throw 'unrecognized ' + y[0] + ' in fills';
4678        }
4679    });
4680}
4681
4682/* 18.8.31 numFmts CT_NumFmts */
4683function parse_numFmts(t, opts) {
4684    styles.NumberFmt = [];
4685    var k = keys(SSF._table);
4686    for(var i=0; i < k.length; ++i) styles.NumberFmt[k[i]] = SSF._table[k[i]];
4687    var m = t[0].match(tagregex);
4688    for(i=0; i < m.length; ++i) {
4689        var y = parsexmltag(m[i]);
4690        switch(y[0]) {
4691            case '<numFmts': case '</numFmts>': case '<numFmts/>': case '<numFmts>': break;
4692            case '<numFmt': {
4693                var f=unescapexml(utf8read(y.formatCode)), j=parseInt(y.numFmtId,10);
4694                styles.NumberFmt[j] = f; if(j>0) SSF.load(f,j);
4695            } break;
4696            default: if(opts.WTF) throw 'unrecognized ' + y[0] + ' in numFmts';
4697        }
4698    }
4699}
4700
4701function write_numFmts(NF, opts) {
4702    var o = ["<numFmts>"];
4703    [[5,8],[23,26],[41,44],[63,66],[164,392]].forEach(function(r) {
4704        for(var i = r[0]; i <= r[1]; ++i) if(NF[i] !== undefined) o[o.length] = (writextag('numFmt',null,{numFmtId:i,formatCode:escapexml(NF[i])}));
4705    });
4706    if(o.length === 1) return "";
4707    o[o.length] = ("</numFmts>");
4708    o[0] = writextag('numFmts', null, { count:o.length-2 }).replace("/>", ">");
4709    return o.join("");
4710}
4711
4712/* 18.8.10 cellXfs CT_CellXfs */
4713function parse_cellXfs(t, opts) {
4714    styles.CellXf = [];
4715    t[0].match(tagregex).forEach(function(x) {
4716        var y = parsexmltag(x);
4717        switch(y[0]) {
4718            case '<cellXfs': case '<cellXfs>': case '<cellXfs/>': case '</cellXfs>': break;
4719
4720            /* 18.8.45 xf CT_Xf */
4721            case '<xf': delete y[0];
4722                if(y.numFmtId) y.numFmtId = parseInt(y.numFmtId, 10);
4723                if(y.fillId) y.fillId = parseInt(y.fillId, 10);
4724                styles.CellXf.push(y); break;
4725            case '</xf>': break;
4726
4727            /* 18.8.1 alignment CT_CellAlignment */
4728            case '<alignment': case '<alignment/>': break;
4729
4730            /* 18.8.33 protection CT_CellProtection */
4731            case '<protection': case '</protection>': case '<protection/>': break;
4732
4733            case '<extLst': case '</extLst>': break;
4734            case '<ext': break;
4735            default: if(opts.WTF) throw 'unrecognized ' + y[0] + ' in cellXfs';
4736        }
4737    });
4738}
4739
4740function write_cellXfs(cellXfs) {
4741    var o = [];
4742    o[o.length] = (writextag('cellXfs',null));
4743    cellXfs.forEach(function(c) { o[o.length] = (writextag('xf', null, c)); });
4744    o[o.length] = ("</cellXfs>");
4745    if(o.length === 2) return "";
4746    o[0] = writextag('cellXfs',null, {count:o.length-2}).replace("/>",">");
4747    return o.join("");
4748}
4749
4750/* 18.8 Styles CT_Stylesheet*/
4751var parse_sty_xml= (function make_pstyx() {
4752var numFmtRegex = /<numFmts([^>]*)>.*<\/numFmts>/;
4753var cellXfRegex = /<cellXfs([^>]*)>.*<\/cellXfs>/;
4754var fillsRegex = /<fills([^>]*)>.*<\/fills>/;
4755
4756return function parse_sty_xml(data, opts) {
4757    /* 18.8.39 styleSheet CT_Stylesheet */
4758    var t;
4759
4760    /* numFmts CT_NumFmts ? */
4761    if((t=data.match(numFmtRegex))) parse_numFmts(t, opts);
4762
4763    /* fonts CT_Fonts ? */
4764    /*if((t=data.match(/<fonts([^>]*)>.*<\/fonts>/))) parse_fonts(t, opts);*/
4765
4766    /* fills CT_Fills */
4767    if((t=data.match(fillsRegex))) parse_fills(t, opts);
4768
4769    /* borders CT_Borders ? */
4770    /* cellStyleXfs CT_CellStyleXfs ? */
4771
4772    /* cellXfs CT_CellXfs ? */
4773    if((t=data.match(cellXfRegex))) parse_cellXfs(t, opts);
4774
4775    /* dxfs CT_Dxfs ? */
4776    /* tableStyles CT_TableStyles ? */
4777    /* colors CT_Colors ? */
4778    /* extLst CT_ExtensionList ? */
4779
4780    return styles;
4781};
4782})();
4783
4784var STYLES_XML_ROOT = writextag('styleSheet', null, {
4785    'xmlns': XMLNS.main[0],
4786    'xmlns:vt': XMLNS.vt
4787});
4788
4789RELS.STY = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles";
4790
4791function write_sty_xml(wb, opts) {
4792    var o = [XML_HEADER, STYLES_XML_ROOT], w;
4793    if((w = write_numFmts(wb.SSF)) != null) o[o.length] = w;
4794    o[o.length] = ('<fonts count="1"><font><sz val="12"/><color theme="1"/><name val="Calibri"/><family val="2"/><scheme val="minor"/></font></fonts>');
4795    o[o.length] = ('<fills count="2"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill></fills>');
4796    o[o.length] = ('<borders count="1"><border><left/><right/><top/><bottom/><diagonal/></border></borders>');
4797    o[o.length] = ('<cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0"/></cellStyleXfs>');
4798    if((w = write_cellXfs(opts.cellXfs))) o[o.length] = (w);
4799    o[o.length] = ('<cellStyles count="1"><cellStyle name="Normal" xfId="0" builtinId="0"/></cellStyles>');
4800    o[o.length] = ('<dxfs count="0"/>');
4801    o[o.length] = ('<tableStyles count="0" defaultTableStyle="TableStyleMedium9" defaultPivotStyle="PivotStyleMedium4"/>');
4802
4803    if(o.length>2){ o[o.length] = ('</styleSheet>'); o[1]=o[1].replace("/>",">"); }
4804    return o.join("");
4805}
4806/* [MS-XLSB] 2.4.651 BrtFmt */
4807function parse_BrtFmt(data, length) {
4808    var ifmt = data.read_shift(2);
4809    var stFmtCode = parse_XLWideString(data,length-2);
4810    return [ifmt, stFmtCode];
4811}
4812
4813/* [MS-XLSB] 2.4.653 BrtFont TODO */
4814function parse_BrtFont(data, length) {
4815    var out = {flags:{}};
4816    out.dyHeight = data.read_shift(2);
4817    out.grbit = parse_FontFlags(data, 2);
4818    out.bls = data.read_shift(2);
4819    out.sss = data.read_shift(2);
4820    out.uls = data.read_shift(1);
4821    out.bFamily = data.read_shift(1);
4822    out.bCharSet = data.read_shift(1);
4823    data.l++;
4824    out.brtColor = parse_BrtColor(data, 8);
4825    out.bFontScheme = data.read_shift(1);
4826    out.name = parse_XLWideString(data, length - 21);
4827
4828    out.flags.Bold = out.bls === 0x02BC;
4829    out.flags.Italic = out.grbit.fItalic;
4830    out.flags.Strikeout = out.grbit.fStrikeout;
4831    out.flags.Outline = out.grbit.fOutline;
4832    out.flags.Shadow = out.grbit.fShadow;
4833    out.flags.Condense = out.grbit.fCondense;
4834    out.flags.Extend = out.grbit.fExtend;
4835    out.flags.Sub = out.sss & 0x2;
4836    out.flags.Sup = out.sss & 0x1;
4837    return out;
4838}
4839
4840/* [MS-XLSB] 2.4.816 BrtXF */
4841function parse_BrtXF(data, length) {
4842    var ixfeParent = data.read_shift(2);
4843    var ifmt = data.read_shift(2);
4844    parsenoop(data, length-4);
4845    return {ixfe:ixfeParent, ifmt:ifmt };
4846}
4847
4848/* [MS-XLSB] 2.1.7.50 Styles */
4849function parse_sty_bin(data, opts) {
4850    styles.NumberFmt = [];
4851    for(var y in SSF._table) styles.NumberFmt[y] = SSF._table[y];
4852
4853    styles.CellXf = [];
4854    var state = ""; /* TODO: this should be a stack */
4855    var pass = false;
4856    recordhopper(data, function hopper_sty(val, R, RT) {
4857        switch(R.n) {
4858            case 'BrtFmt':
4859                styles.NumberFmt[val[0]] = val[1]; SSF.load(val[1], val[0]);
4860                break;
4861            case 'BrtFont': break; /* TODO */
4862            case 'BrtKnownFonts': break; /* TODO */
4863            case 'BrtFill': break; /* TODO */
4864            case 'BrtBorder': break; /* TODO */
4865            case 'BrtXF':
4866                if(state === "CELLXFS") {
4867                    styles.CellXf.push(val);
4868                }
4869                break; /* TODO */
4870            case 'BrtStyle': break; /* TODO */
4871            case 'BrtDXF': break; /* TODO */
4872            case 'BrtMRUColor': break; /* TODO */
4873            case 'BrtIndexedColor': break; /* TODO */
4874            case 'BrtBeginStyleSheet': break;
4875            case 'BrtEndStyleSheet': break;
4876            case 'BrtBeginTableStyle': break;
4877            case 'BrtTableStyleElement': break;
4878            case 'BrtEndTableStyle': break;
4879            case 'BrtBeginFmts': state = "FMTS"; break;
4880            case 'BrtEndFmts': state = ""; break;
4881            case 'BrtBeginFonts': state = "FONTS"; break;
4882            case 'BrtEndFonts': state = ""; break;
4883            case 'BrtACBegin': state = "ACFONTS"; break;
4884            case 'BrtACEnd': state = ""; break;
4885            case 'BrtBeginFills': state = "FILLS"; break;
4886            case 'BrtEndFills': state = ""; break;
4887            case 'BrtBeginBorders': state = "BORDERS"; break;
4888            case 'BrtEndBorders': state = ""; break;
4889            case 'BrtBeginCellStyleXFs': state = "CELLSTYLEXFS"; break;
4890            case 'BrtEndCellStyleXFs': state = ""; break;
4891            case 'BrtBeginCellXFs': state = "CELLXFS"; break;
4892            case 'BrtEndCellXFs': state = ""; break;
4893            case 'BrtBeginStyles': state = "STYLES"; break;
4894            case 'BrtEndStyles': state = ""; break;
4895            case 'BrtBeginDXFs': state = "DXFS"; break;
4896            case 'BrtEndDXFs': state = ""; break;
4897            case 'BrtBeginTableStyles': state = "TABLESTYLES"; break;
4898            case 'BrtEndTableStyles': state = ""; break;
4899            case 'BrtBeginColorPalette': state = "COLORPALETTE"; break;
4900            case 'BrtEndColorPalette': state = ""; break;
4901            case 'BrtBeginIndexedColors': state = "INDEXEDCOLORS"; break;
4902            case 'BrtEndIndexedColors': state = ""; break;
4903            case 'BrtBeginMRUColors': state = "MRUCOLORS"; break;
4904            case 'BrtEndMRUColors': state = ""; break;
4905            case 'BrtFRTBegin': pass = true; break;
4906            case 'BrtFRTEnd': pass = false; break;
4907            case 'BrtBeginStyleSheetExt14': break;
4908            case 'BrtBeginSlicerStyles': break;
4909            case 'BrtEndSlicerStyles': break;
4910            case 'BrtBeginTimelineStylesheetExt15': break;
4911            case 'BrtEndTimelineStylesheetExt15': break;
4912            case 'BrtBeginTimelineStyles': break;
4913            case 'BrtEndTimelineStyles': break;
4914            case 'BrtEndStyleSheetExt14': break;
4915            default: if(!pass || opts.WTF) throw new Error("Unexpected record " + RT + " " + R.n);
4916        }
4917    });
4918    return styles;
4919}
4920
4921/* [MS-XLSB] 2.1.7.50 Styles */
4922function write_sty_bin(data, opts) {
4923    var ba = buf_array();
4924    write_record(ba, "BrtBeginStyleSheet");
4925    /* [FMTS] */
4926    /* [FONTS] */
4927    /* [FILLS] */
4928    /* [BORDERS] */
4929    /* CELLSTYLEXFS */
4930    /* CELLXFS*/
4931    /* STYLES */
4932    /* DXFS */
4933    /* TABLESTYLES */
4934    /* [COLORPALETTE] */
4935    /* FRTSTYLESHEET*/
4936    write_record(ba, "BrtEndStyleSheet");
4937    return ba.end();
4938}
4939RELS.THEME = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme";
4940
4941/* 20.1.6.2 clrScheme CT_ColorScheme */
4942function parse_clrScheme(t, opts) {
4943    themes.themeElements.clrScheme = [];
4944    var color = {};
4945    t[0].match(tagregex).forEach(function(x) {
4946        var y = parsexmltag(x);
4947        switch(y[0]) {
4948            case '<a:clrScheme': case '</a:clrScheme>': break;
4949
4950            /* 20.1.2.3.32 srgbClr CT_SRgbColor */
4951            case '<a:srgbClr': color.rgb = y.val; break;
4952
4953            /* 20.1.2.3.33 sysClr CT_SystemColor */
4954            case '<a:sysClr': color.rgb = y.lastClr; break;
4955
4956            /* 20.1.4.1.9 dk1 (Dark 1) */
4957            case '<a:dk1>':
4958            case '</a:dk1>':
4959            /* 20.1.4.1.10 dk2 (Dark 2) */
4960            case '<a:dk2>':
4961            case '</a:dk2>':
4962            /* 20.1.4.1.22 lt1 (Light 1) */
4963            case '<a:lt1>':
4964            case '</a:lt1>':
4965            /* 20.1.4.1.23 lt2 (Light 2) */
4966            case '<a:lt2>':
4967            case '</a:lt2>':
4968            /* 20.1.4.1.1 accent1 (Accent 1) */
4969            case '<a:accent1>':
4970            case '</a:accent1>':
4971            /* 20.1.4.1.2 accent2 (Accent 2) */
4972            case '<a:accent2>':
4973            case '</a:accent2>':
4974            /* 20.1.4.1.3 accent3 (Accent 3) */
4975            case '<a:accent3>':
4976            case '</a:accent3>':
4977            /* 20.1.4.1.4 accent4 (Accent 4) */
4978            case '<a:accent4>':
4979            case '</a:accent4>':
4980            /* 20.1.4.1.5 accent5 (Accent 5) */
4981            case '<a:accent5>':
4982            case '</a:accent5>':
4983            /* 20.1.4.1.6 accent6 (Accent 6) */
4984            case '<a:accent6>':
4985            case '</a:accent6>':
4986            /* 20.1.4.1.19 hlink (Hyperlink) */
4987            case '<a:hlink>':
4988            case '</a:hlink>':
4989            /* 20.1.4.1.15 folHlink (Followed Hyperlink) */
4990            case '<a:folHlink>':
4991            case '</a:folHlink>':
4992                if (y[0][1] === '/') {
4993                    themes.themeElements.clrScheme.push(color);
4994                    color = {};
4995                } else {
4996                    color.name = y[0].substring(3, y[0].length - 1);
4997                }
4998                break;
4999
5000            default: if(opts.WTF) throw 'unrecognized ' + y[0] + ' in clrScheme';
5001        }
5002    });
5003}
5004
5005/* 20.1.4.1.18 fontScheme CT_FontScheme */
5006function parse_fontScheme(t, opts) { }
5007
5008/* 20.1.4.1.15 fmtScheme CT_StyleMatrix */
5009function parse_fmtScheme(t, opts) { }
5010
5011var clrsregex = /<a:clrScheme([^>]*)>[^\u2603]*<\/a:clrScheme>/;
5012var fntsregex = /<a:fontScheme([^>]*)>[^\u2603]*<\/a:fontScheme>/;
5013var fmtsregex = /<a:fmtScheme([^>]*)>[^\u2603]*<\/a:fmtScheme>/;
5014
5015/* 20.1.6.10 themeElements CT_BaseStyles */
5016function parse_themeElements(data, opts) {
5017    themes.themeElements = {};
5018
5019    var t;
5020
5021    [
5022        /* clrScheme CT_ColorScheme */
5023        ['clrScheme', clrsregex, parse_clrScheme],
5024        /* fontScheme CT_FontScheme */
5025        ['fontScheme', fntsregex, parse_fontScheme],
5026        /* fmtScheme CT_StyleMatrix */
5027        ['fmtScheme', fmtsregex, parse_fmtScheme]
5028    ].forEach(function(m) {
5029        if(!(t=data.match(m[1]))) throw m[0] + ' not found in themeElements';
5030        m[2](t, opts);
5031    });
5032}
5033
5034var themeltregex = /<a:themeElements([^>]*)>[^\u2603]*<\/a:themeElements>/;
5035
5036/* 14.2.7 Theme Part */
5037function parse_theme_xml(data, opts) {
5038    /* 20.1.6.9 theme CT_OfficeStyleSheet */
5039    if(!data || data.length === 0) return themes;
5040
5041    var t;
5042
5043    /* themeElements CT_BaseStyles */
5044    if(!(t=data.match(themeltregex))) throw 'themeElements not found in theme';
5045    parse_themeElements(t[0], opts);
5046
5047    return themes;
5048}
5049
5050function write_theme() { return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="Office Theme"><a:themeElements><a:clrScheme name="Office"><a:dk1><a:sysClr val="windowText" lastClr="000000"/></a:dk1><a:lt1><a:sysClr val="window" lastClr="FFFFFF"/></a:lt1><a:dk2><a:srgbClr val="1F497D"/></a:dk2><a:lt2><a:srgbClr val="EEECE1"/></a:lt2><a:accent1><a:srgbClr val="4F81BD"/></a:accent1><a:accent2><a:srgbClr val="C0504D"/></a:accent2><a:accent3><a:srgbClr val="9BBB59"/></a:accent3><a:accent4><a:srgbClr val="8064A2"/></a:accent4><a:accent5><a:srgbClr val="4BACC6"/></a:accent5><a:accent6><a:srgbClr val="F79646"/></a:accent6><a:hlink><a:srgbClr val="0000FF"/></a:hlink><a:folHlink><a:srgbClr val="800080"/></a:folHlink></a:clrScheme><a:fontScheme name="Office"><a:majorFont><a:latin typeface="Cambria"/><a:ea typeface=""/><a:cs typeface=""/><a:font script="Jpan" typeface="MS Pゴシック"/><a:font script="Hang" typeface="맑은 고딕"/><a:font script="Hans" typeface="宋体"/><a:font script="Hant" typeface="新細明體"/><a:font script="Arab" typeface="Times New Roman"/><a:font script="Hebr" typeface="Times New Roman"/><a:font script="Thai" typeface="Tahoma"/><a:font script="Ethi" typeface="Nyala"/><a:font script="Beng" typeface="Vrinda"/><a:font script="Gujr" typeface="Shruti"/><a:font script="Khmr" typeface="MoolBoran"/><a:font script="Knda" typeface="Tunga"/><a:font script="Guru" typeface="Raavi"/><a:font script="Cans" typeface="Euphemia"/><a:font script="Cher" typeface="Plantagenet Cherokee"/><a:font script="Yiii" typeface="Microsoft Yi Baiti"/><a:font script="Tibt" typeface="Microsoft Himalaya"/><a:font script="Thaa" typeface="MV Boli"/><a:font script="Deva" typeface="Mangal"/><a:font script="Telu" typeface="Gautami"/><a:font script="Taml" typeface="Latha"/><a:font script="Syrc" typeface="Estrangelo Edessa"/><a:font script="Orya" typeface="Kalinga"/><a:font script="Mlym" typeface="Kartika"/><a:font script="Laoo" typeface="DokChampa"/><a:font script="Sinh" typeface="Iskoola Pota"/><a:font script="Mong" typeface="Mongolian Baiti"/><a:font script="Viet" typeface="Times New Roman"/><a:font script="Uigh" typeface="Microsoft Uighur"/><a:font script="Geor" typeface="Sylfaen"/></a:majorFont><a:minorFont><a:latin typeface="Calibri"/><a:ea typeface=""/><a:cs typeface=""/><a:font script="Jpan" typeface="MS Pゴシック"/><a:font script="Hang" typeface="맑은 고딕"/><a:font script="Hans" typeface="宋体"/><a:font script="Hant" typeface="新細明體"/><a:font script="Arab" typeface="Arial"/><a:font script="Hebr" typeface="Arial"/><a:font script="Thai" typeface="Tahoma"/><a:font script="Ethi" typeface="Nyala"/><a:font script="Beng" typeface="Vrinda"/><a:font script="Gujr" typeface="Shruti"/><a:font script="Khmr" typeface="DaunPenh"/><a:font script="Knda" typeface="Tunga"/><a:font script="Guru" typeface="Raavi"/><a:font script="Cans" typeface="Euphemia"/><a:font script="Cher" typeface="Plantagenet Cherokee"/><a:font script="Yiii" typeface="Microsoft Yi Baiti"/><a:font script="Tibt" typeface="Microsoft Himalaya"/><a:font script="Thaa" typeface="MV Boli"/><a:font script="Deva" typeface="Mangal"/><a:font script="Telu" typeface="Gautami"/><a:font script="Taml" typeface="Latha"/><a:font script="Syrc" typeface="Estrangelo Edessa"/><a:font script="Orya" typeface="Kalinga"/><a:font script="Mlym" typeface="Kartika"/><a:font script="Laoo" typeface="DokChampa"/><a:font script="Sinh" typeface="Iskoola Pota"/><a:font script="Mong" typeface="Mongolian Baiti"/><a:font script="Viet" typeface="Arial"/><a:font script="Uigh" typeface="Microsoft Uighur"/><a:font script="Geor" typeface="Sylfaen"/></a:minorFont></a:fontScheme><a:fmtScheme name="Office"><a:fillStyleLst><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:gradFill rotWithShape="1"><a:gsLst><a:gs pos="0"><a:schemeClr val="phClr"><a:tint val="50000"/><a:satMod val="300000"/></a:schemeClr></a:gs><a:gs pos="35000"><a:schemeClr val="phClr"><a:tint val="37000"/><a:satMod val="300000"/></a:schemeClr></a:gs><a:gs pos="100000"><a:schemeClr val="phClr"><a:tint val="15000"/><a:satMod val="350000"/></a:schemeClr></a:gs></a:gsLst><a:lin ang="16200000" scaled="1"/></a:gradFill><a:gradFill rotWithShape="1"><a:gsLst><a:gs pos="0"><a:schemeClr val="phClr"><a:tint val="100000"/><a:shade val="100000"/><a:satMod val="130000"/></a:schemeClr></a:gs><a:gs pos="100000"><a:schemeClr val="phClr"><a:tint val="50000"/><a:shade val="100000"/><a:satMod val="350000"/></a:schemeClr></a:gs></a:gsLst><a:lin ang="16200000" scaled="0"/></a:gradFill></a:fillStyleLst><a:lnStyleLst><a:ln w="9525" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"><a:shade val="95000"/><a:satMod val="105000"/></a:schemeClr></a:solidFill><a:prstDash val="solid"/></a:ln><a:ln w="25400" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/></a:ln><a:ln w="38100" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/></a:ln></a:lnStyleLst><a:effectStyleLst><a:effectStyle><a:effectLst><a:outerShdw blurRad="40000" dist="20000" dir="5400000" rotWithShape="0"><a:srgbClr val="000000"><a:alpha val="38000"/></a:srgbClr></a:outerShdw></a:effectLst></a:effectStyle><a:effectStyle><a:effectLst><a:outerShdw blurRad="40000" dist="23000" dir="5400000" rotWithShape="0"><a:srgbClr val="000000"><a:alpha val="35000"/></a:srgbClr></a:outerShdw></a:effectLst></a:effectStyle><a:effectStyle><a:effectLst><a:outerShdw blurRad="40000" dist="23000" dir="5400000" rotWithShape="0"><a:srgbClr val="000000"><a:alpha val="35000"/></a:srgbClr></a:outerShdw></a:effectLst><a:scene3d><a:camera prst="orthographicFront"><a:rot lat="0" lon="0" rev="0"/></a:camera><a:lightRig rig="threePt" dir="t"><a:rot lat="0" lon="0" rev="1200000"/></a:lightRig></a:scene3d><a:sp3d><a:bevelT w="63500" h="25400"/></a:sp3d></a:effectStyle></a:effectStyleLst><a:bgFillStyleLst><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:gradFill rotWithShape="1"><a:gsLst><a:gs pos="0"><a:schemeClr val="phClr"><a:tint val="40000"/><a:satMod val="350000"/></a:schemeClr></a:gs><a:gs pos="40000"><a:schemeClr val="phClr"><a:tint val="45000"/><a:shade val="99000"/><a:satMod val="350000"/></a:schemeClr></a:gs><a:gs pos="100000"><a:schemeClr val="phClr"><a:shade val="20000"/><a:satMod val="255000"/></a:schemeClr></a:gs></a:gsLst><a:path path="circle"><a:fillToRect l="50000" t="-80000" r="50000" b="180000"/></a:path></a:gradFill><a:gradFill rotWithShape="1"><a:gsLst><a:gs pos="0"><a:schemeClr val="phClr"><a:tint val="80000"/><a:satMod val="300000"/></a:schemeClr></a:gs><a:gs pos="100000"><a:schemeClr val="phClr"><a:shade val="30000"/><a:satMod val="200000"/></a:schemeClr></a:gs></a:gsLst><a:path path="circle"><a:fillToRect l="50000" t="50000" r="50000" b="50000"/></a:path></a:gradFill></a:bgFillStyleLst></a:fmtScheme></a:themeElements><a:objectDefaults><a:spDef><a:spPr/><a:bodyPr/><a:lstStyle/><a:style><a:lnRef idx="1"><a:schemeClr val="accent1"/></a:lnRef><a:fillRef idx="3"><a:schemeClr val="accent1"/></a:fillRef><a:effectRef idx="2"><a:schemeClr val="accent1"/></a:effectRef><a:fontRef idx="minor"><a:schemeClr val="lt1"/></a:fontRef></a:style></a:spDef><a:lnDef><a:spPr/><a:bodyPr/><a:lstStyle/><a:style><a:lnRef idx="2"><a:schemeClr val="accent1"/></a:lnRef><a:fillRef idx="0"><a:schemeClr val="accent1"/></a:fillRef><a:effectRef idx="1"><a:schemeClr val="accent1"/></a:effectRef><a:fontRef idx="minor"><a:schemeClr val="tx1"/></a:fontRef></a:style></a:lnDef></a:objectDefaults><a:extraClrSchemeLst/></a:theme>'; }
5051/* [MS-XLS] 2.4.326 TODO: payload is a zip file */
5052function parse_Theme(blob, length) {
5053    var dwThemeVersion = blob.read_shift(4);
5054    if(dwThemeVersion === 124226) return;
5055    blob.l += length-4;
5056}
5057
5058/* 2.5.49 */
5059function parse_ColorTheme(blob, length) { return blob.read_shift(4); }
5060
5061/* 2.5.155 */
5062function parse_FullColorExt(blob, length) {
5063    var o = {};
5064    o.xclrType = blob.read_shift(2);
5065    o.nTintShade = blob.read_shift(2);
5066    switch(o.xclrType) {
5067        case 0: blob.l += 4; break;
5068        case 1: o.xclrValue = parse_IcvXF(blob, 4); break;
5069        case 2: o.xclrValue = parse_LongRGBA(blob, 4); break;
5070        case 3: o.xclrValue = parse_ColorTheme(blob, 4); break;
5071        case 4: blob.l += 4; break;
5072    }
5073    blob.l += 8;
5074    return o;
5075}
5076
5077/* 2.5.164 TODO: read 7 bits*/
5078function parse_IcvXF(blob, length) {
5079    return parsenoop(blob, length);
5080}
5081
5082/* 2.5.280 */
5083function parse_XFExtGradient(blob, length) {
5084    return parsenoop(blob, length);
5085}
5086
5087/* 2.5.108 */
5088function parse_ExtProp(blob, length) {
5089    var extType = blob.read_shift(2);
5090    var cb = blob.read_shift(2);
5091    var o = [extType];
5092    switch(extType) {
5093        case 0x04: case 0x05: case 0x07: case 0x08:
5094        case 0x09: case 0x0A: case 0x0B: case 0x0D:
5095            o[1] = parse_FullColorExt(blob, cb); break;
5096        case 0x06: o[1] = parse_XFExtGradient(blob, cb); break;
5097        case 0x0E: case 0x0F: o[1] = blob.read_shift(cb === 5 ? 1 : 2); break;
5098        default: throw new Error("Unrecognized ExtProp type: " + extType + " " + cb);
5099    }
5100    return o;
5101}
5102
5103/* 2.4.355 */
5104function parse_XFExt(blob, length) {
5105    var end = blob.l + length;
5106    blob.l += 2;
5107    var ixfe = blob.read_shift(2);
5108    blob.l += 2;
5109    var cexts = blob.read_shift(2);
5110    var ext = [];
5111    while(cexts-- > 0) ext.push(parse_ExtProp(blob, end-blob.l));
5112    return {ixfe:ixfe, ext:ext};
5113}
5114
5115/* xf is an XF, see parse_XFExt for xfext */
5116function update_xfext(xf, xfext) {
5117    xfext.forEach(function(xfe) {
5118        switch(xfe[0]) { /* 2.5.108 extPropData */
5119            case 0x04: break; /* foreground color */
5120            case 0x05: break; /* background color */
5121            case 0x07: case 0x08: case 0x09: case 0x0a: break;
5122            case 0x0d: break; /* text color */
5123            case 0x0e: break; /* font scheme */
5124            default: throw "bafuq" + xfe[0].toString(16);
5125        }
5126    });
5127}
5128
5129/* 18.6 Calculation Chain */
5130function parse_cc_xml(data, opts) {
5131    var d = [];
5132    var l = 0, i = 1;
5133    (data.match(tagregex)||[]).forEach(function(x) {
5134        var y = parsexmltag(x);
5135        switch(y[0]) {
5136            case '<?xml': break;
5137            /* 18.6.2  calcChain CT_CalcChain 1 */
5138            case '<calcChain': case '<calcChain>': case '</calcChain>': break;
5139            /* 18.6.1  c CT_CalcCell 1 */
5140            case '<c': delete y[0]; if(y.i) i = y.i; else y.i = i; d.push(y); break;
5141        }
5142    });
5143    return d;
5144}
5145
5146function write_cc_xml(data, opts) { }
5147/* [MS-XLSB] 2.6.4.1 */
5148function parse_BrtCalcChainItem$(data, length) {
5149    var out = {};
5150    out.i = data.read_shift(4);
5151    var cell = {};
5152    cell.r = data.read_shift(4);
5153    cell.c = data.read_shift(4);
5154    out.r = encode_cell(cell);
5155    var flags = data.read_shift(1);
5156    if(flags & 0x2) out.l = '1';
5157    if(flags & 0x8) out.a = '1';
5158    return out;
5159}
5160
5161/* 18.6 Calculation Chain */
5162function parse_cc_bin(data, opts) {
5163    var out = [];
5164    var pass = false;
5165    recordhopper(data, function hopper_cc(val, R, RT) {
5166        switch(R.n) {
5167            case 'BrtCalcChainItem$': out.push(val); break;
5168            case 'BrtBeginCalcChain$': break;
5169            case 'BrtEndCalcChain$': break;
5170            default: if(!pass || opts.WTF) throw new Error("Unexpected record " + RT + " " + R.n);
5171        }
5172    });
5173    return out;
5174}
5175
5176function write_cc_bin(data, opts) { }
5177
5178function parse_comments(zip, dirComments, sheets, sheetRels, opts) {
5179    for(var i = 0; i != dirComments.length; ++i) {
5180        var canonicalpath=dirComments[i];
5181        var comments=parse_cmnt(getzipdata(zip, canonicalpath.replace(/^\//,''), true), canonicalpath, opts);
5182        if(!comments || !comments.length) continue;
5183        // find the sheets targeted by these comments
5184        var sheetNames = keys(sheets);
5185        for(var j = 0; j != sheetNames.length; ++j) {
5186            var sheetName = sheetNames[j];
5187            var rels = sheetRels[sheetName];
5188            if(rels) {
5189                var rel = rels[canonicalpath];
5190                if(rel) insertCommentsIntoSheet(sheetName, sheets[sheetName], comments);
5191            }
5192        }
5193    }
5194}
5195
5196function insertCommentsIntoSheet(sheetName, sheet, comments) {
5197    comments.forEach(function(comment) {
5198        var cell = sheet[comment.ref];
5199        if (!cell) {
5200            cell = {};
5201            sheet[comment.ref] = cell;
5202            var range = safe_decode_range(sheet["!ref"]||"BDWGO1000001:A1");
5203            var thisCell = decode_cell(comment.ref);
5204            if(range.s.r > thisCell.r) range.s.r = thisCell.r;
5205            if(range.e.r < thisCell.r) range.e.r = thisCell.r;
5206            if(range.s.c > thisCell.c) range.s.c = thisCell.c;
5207            if(range.e.c < thisCell.c) range.e.c = thisCell.c;
5208            var encoded = encode_range(range);
5209            if (encoded !== sheet["!ref"]) sheet["!ref"] = encoded;
5210        }
5211
5212        if (!cell.c) cell.c = [];
5213        var o = {a: comment.author, t: comment.t, r: comment.r};
5214        if(comment.h) o.h = comment.h;
5215        cell.c.push(o);
5216    });
5217}
5218
5219/* 18.7.3 CT_Comment */
5220function parse_comments_xml(data, opts) {
5221    if(data.match(/<(?:\w+:)?comments *\/>/)) return [];
5222    var authors = [];
5223    var commentList = [];
5224    data.match(/<(?:\w+:)?authors>([^\u2603]*)<\/(?:\w+:)?authors>/)[1].split(/<\/\w*:?author>/).forEach(function(x) {
5225        if(x === "" || x.trim() === "") return;
5226        authors.push(x.match(/<(?:\w+:)?author[^>]*>(.*)/)[1]);
5227    });
5228    (data.match(/<(?:\w+:)?commentList>([^\u2603]*)<\/(?:\w+:)?commentList>/)||["",""])[1].split(/<\/\w*:?comment>/).forEach(function(x, index) {
5229        if(x === "" || x.trim() === "") return;
5230        var y = parsexmltag(x.match(/<(?:\w+:)?comment[^>]*>/)[0]);
5231        var comment = { author: y.authorId && authors[y.authorId] ? authors[y.authorId] : undefined, ref: y.ref, guid: y.guid };
5232        var cell = decode_cell(y.ref);
5233        if(opts.sheetRows && opts.sheetRows <= cell.r) return;
5234        var textMatch = x.match(/<text>([^\u2603]*)<\/text>/);
5235        if (!textMatch || !textMatch[1]) return; // a comment may contain an empty text tag.
5236        var rt = parse_si(textMatch[1]);
5237        comment.r = rt.r;
5238        comment.t = rt.t;
5239        if(opts.cellHTML) comment.h = rt.h;
5240        commentList.push(comment);
5241    });
5242    return commentList;
5243}
5244
5245function write_comments_xml(data, opts) { }
5246/* [MS-XLSB] 2.4.28 BrtBeginComment */
5247function parse_BrtBeginComment(data, length) {
5248    var out = {};
5249    out.iauthor = data.read_shift(4);
5250    var rfx = parse_UncheckedRfX(data, 16);
5251    out.rfx = rfx.s;
5252    out.ref = encode_cell(rfx.s);
5253    data.l += 16; /*var guid = parse_GUID(data); */
5254    return out;
5255}
5256
5257/* [MS-XLSB] 2.4.324 BrtCommentAuthor */
5258var parse_BrtCommentAuthor = parse_XLWideString;
5259
5260/* [MS-XLSB] 2.4.325 BrtCommentText */
5261var parse_BrtCommentText = parse_RichStr;
5262
5263/* [MS-XLSB] 2.1.7.8 Comments */
5264function parse_comments_bin(data, opts) {
5265    var out = [];
5266    var authors = [];
5267    var c = {};
5268    var pass = false;
5269    recordhopper(data, function hopper_cmnt(val, R, RT) {
5270        switch(R.n) {
5271            case 'BrtCommentAuthor': authors.push(val); break;
5272            case 'BrtBeginComment': c = val; break;
5273            case 'BrtCommentText': c.t = val.t; c.h = val.h; c.r = val.r; break;
5274            case 'BrtEndComment':
5275                c.author = authors[c.iauthor];
5276                delete c.iauthor;
5277                if(opts.sheetRows && opts.sheetRows <= c.rfx.r) break;
5278                delete c.rfx; out.push(c); break;
5279            case 'BrtBeginComments': break;
5280            case 'BrtEndComments': break;
5281            case 'BrtBeginCommentAuthors': break;
5282            case 'BrtEndCommentAuthors': break;
5283            case 'BrtBeginCommentList': break;
5284            case 'BrtEndCommentList': break;
5285            default: if(!pass || opts.WTF) throw new Error("Unexpected record " + RT + " " + R.n);
5286        }
5287    });
5288    return out;
5289}
5290
5291function write_comments_bin(data, opts) { }
5292/* TODO: it will be useful to parse the function str */
5293var rc_to_a1 = (function(){
5294    var rcregex = /(^|[^A-Za-z])R(\[?)(-?\d+|)\]?C(\[?)(-?\d+|)\]?/g;
5295    var rcbase;
5296    function rcfunc($$,$1,$2,$3,$4,$5) {
5297        var R = $3.length>0?parseInt($3,10)|0:0, C = $5.length>0?parseInt($5,10)|0:0;
5298        if(C<0 && $4.length === 0) C=0;
5299        if($4.length > 0) C += rcbase.c;
5300        if($2.length > 0) R += rcbase.r;
5301        return $1 + encode_col(C) + encode_row(R);
5302    }
5303    return function rc_to_a1(fstr, base) {
5304        rcbase = base;
5305        return fstr.replace(rcregex, rcfunc);
5306    };
5307})();
5308
5309/* --- formula references point to MS-XLS --- */
5310/* Small helpers */
5311function parseread(l) { return function(blob, length) { blob.l+=l; return; }; }
5312function parseread1(blob, length) { blob.l+=1; return; }
5313
5314/* Rgce Helpers */
5315
5316/* 2.5.51 */
5317function parse_ColRelU(blob, length) {
5318    var c = blob.read_shift(2);
5319    return [c & 0x3FFF, (c >> 14) & 1, (c >> 15) & 1];
5320}
5321
5322/* 2.5.198.105 */
5323function parse_RgceArea(blob, length) {
5324    var r=blob.read_shift(2), R=blob.read_shift(2);
5325    var c=parse_ColRelU(blob, 2);
5326    var C=parse_ColRelU(blob, 2);
5327    return { s:{r:r, c:c[0], cRel:c[1], rRel:c[2]}, e:{r:R, c:C[0], cRel:C[1], rRel:C[2]} };
5328}
5329
5330/* 2.5.198.105 TODO */
5331function parse_RgceAreaRel(blob, length) {
5332    var r=blob.read_shift(2), R=blob.read_shift(2);
5333    var c=parse_ColRelU(blob, 2);
5334    var C=parse_ColRelU(blob, 2);
5335    return { s:{r:r, c:c[0], cRel:c[1], rRel:c[2]}, e:{r:R, c:C[0], cRel:C[1], rRel:C[2]} };
5336}
5337
5338/* 2.5.198.109 */
5339function parse_RgceLoc(blob, length) {
5340    var r = blob.read_shift(2);
5341    var c = parse_ColRelU(blob, 2);
5342    return {r:r, c:c[0], cRel:c[1], rRel:c[2]};
5343}
5344
5345/* 2.5.198.111 */
5346function parse_RgceLocRel(blob, length) {
5347    var r = blob.read_shift(2);
5348    var cl = blob.read_shift(2);
5349    var cRel = (cl & 0x8000) >> 15, rRel = (cl & 0x4000) >> 14;
5350    cl &= 0x3FFF;
5351    if(cRel !== 0) while(cl >= 0x100) cl -= 0x100;
5352    return {r:r,c:cl,cRel:cRel,rRel:rRel};
5353}
5354
5355/* Ptg Tokens */
5356
5357/* 2.5.198.27 */
5358function parse_PtgArea(blob, length) {
5359    var type = (blob[blob.l++] & 0x60) >> 5;
5360    var area = parse_RgceArea(blob, 8);
5361    return [type, area];
5362}
5363
5364/* 2.5.198.28 */
5365function parse_PtgArea3d(blob, length) {
5366    var type = (blob[blob.l++] & 0x60) >> 5;
5367    var ixti = blob.read_shift(2);
5368    var area = parse_RgceArea(blob, 8);
5369    return [type, ixti, area];
5370}
5371
5372/* 2.5.198.29 */
5373function parse_PtgAreaErr(blob, length) {
5374    var type = (blob[blob.l++] & 0x60) >> 5;
5375    blob.l += 8;
5376    return [type];
5377}
5378/* 2.5.198.30 */
5379function parse_PtgAreaErr3d(blob, length) {
5380    var type = (blob[blob.l++] & 0x60) >> 5;
5381    var ixti = blob.read_shift(2);
5382    blob.l += 8;
5383    return [type, ixti];
5384}
5385
5386/* 2.5.198.31 */
5387function parse_PtgAreaN(blob, length) {
5388    var type = (blob[blob.l++] & 0x60) >> 5;
5389    var area = parse_RgceAreaRel(blob, 8);
5390    return [type, area];
5391}
5392
5393/* 2.5.198.32 -- ignore this and look in PtgExtraArray for shape + values */
5394function parse_PtgArray(blob, length) {
5395    var type = (blob[blob.l++] & 0x60) >> 5;
5396    blob.l += 7;
5397    return [type];
5398}
5399
5400/* 2.5.198.33 */
5401function parse_PtgAttrBaxcel(blob, length) {
5402    var bitSemi = blob[blob.l+1] & 0x01; /* 1 = volatile */
5403    var bitBaxcel = 1;
5404    blob.l += 4;
5405    return [bitSemi, bitBaxcel];
5406}
5407
5408/* 2.5.198.34 */
5409function parse_PtgAttrChoose(blob, length) {
5410    blob.l +=2;
5411    var offset = blob.read_shift(2);
5412    var o = [];
5413    /* offset is 1 less than the number of elements */
5414    for(var i = 0; i <= offset; ++i) o.push(blob.read_shift(2));
5415    return o;
5416}
5417
5418/* 2.5.198.35 */
5419function parse_PtgAttrGoto(blob, length) {
5420    var bitGoto = (blob[blob.l+1] & 0xFF) ? 1 : 0;
5421    blob.l += 2;
5422    return [bitGoto, blob.read_shift(2)];
5423}
5424
5425/* 2.5.198.36 */
5426function parse_PtgAttrIf(blob, length) {
5427    var bitIf = (blob[blob.l+1] & 0xFF) ? 1 : 0;
5428    blob.l += 2;
5429    return [bitIf, blob.read_shift(2)];
5430}
5431
5432/* 2.5.198.37 */
5433function parse_PtgAttrSemi(blob, length) {
5434    var bitSemi = (blob[blob.l+1] & 0xFF) ? 1 : 0;
5435    blob.l += 4;
5436    return [bitSemi];
5437}
5438
5439/* 2.5.198.40 (used by PtgAttrSpace and PtgAttrSpaceSemi) */
5440function parse_PtgAttrSpaceType(blob, length) {
5441    var type = blob.read_shift(1), cch = blob.read_shift(1);
5442    return [type, cch];
5443}
5444
5445/* 2.5.198.38 */
5446function parse_PtgAttrSpace(blob, length) {
5447    blob.read_shift(2);
5448    return parse_PtgAttrSpaceType(blob, 2);
5449}
5450
5451/* 2.5.198.39 */
5452function parse_PtgAttrSpaceSemi(blob, length) {
5453    blob.read_shift(2);
5454    return parse_PtgAttrSpaceType(blob, 2);
5455}
5456
5457/* 2.5.198.84 TODO */
5458function parse_PtgRef(blob, length) {
5459    var ptg = blob[blob.l] & 0x1F;
5460    var type = (blob[blob.l] & 0x60)>>5;
5461    blob.l += 1;
5462    var loc = parse_RgceLoc(blob,4);
5463    return [type, loc];
5464}
5465
5466/* 2.5.198.88 TODO */
5467function parse_PtgRefN(blob, length) {
5468    var ptg = blob[blob.l] & 0x1F;
5469    var type = (blob[blob.l] & 0x60)>>5;
5470    blob.l += 1;
5471    var loc = parse_RgceLocRel(blob,4);
5472    return [type, loc];
5473}
5474
5475/* 2.5.198.85 TODO */
5476function parse_PtgRef3d(blob, length) {
5477    var ptg = blob[blob.l] & 0x1F;
5478    var type = (blob[blob.l] & 0x60)>>5;
5479    blob.l += 1;
5480    var ixti = blob.read_shift(2); // XtiIndex
5481    var loc = parse_RgceLoc(blob,4);
5482    return [type, ixti, loc];
5483}
5484
5485
5486/* 2.5.198.62 TODO */
5487function parse_PtgFunc(blob, length) {
5488    var ptg = blob[blob.l] & 0x1F;
5489    var type = (blob[blob.l] & 0x60)>>5;
5490    blob.l += 1;
5491    var iftab = blob.read_shift(2);
5492    return [FtabArgc[iftab], Ftab[iftab]];
5493}
5494/* 2.5.198.63 TODO */
5495function parse_PtgFuncVar(blob, length) {
5496    blob.l++;
5497    var cparams = blob.read_shift(1), tab = parsetab(blob);
5498    return [cparams, (tab[0] === 0 ? Ftab : Cetab)[tab[1]]];
5499}
5500
5501function parsetab(blob, length) {
5502    return [blob[blob.l+1]>>7, blob.read_shift(2) & 0x7FFF];
5503}
5504
5505/* 2.5.198.41 */
5506var parse_PtgAttrSum = parseread(4);
5507/* 2.5.198.43 */
5508var parse_PtgConcat = parseread1;
5509
5510/* 2.5.198.58 */
5511function parse_PtgExp(blob, length) {
5512    blob.l++;
5513    var row = blob.read_shift(2);
5514    var col = blob.read_shift(2);
5515    return [row, col];
5516}
5517
5518/* 2.5.198.57 */
5519function parse_PtgErr(blob, length) { blob.l++; return BErr[blob.read_shift(1)]; }
5520
5521/* 2.5.198.66 TODO */
5522function parse_PtgInt(blob, length) { blob.l++; return blob.read_shift(2); }
5523
5524/* 2.5.198.42 */
5525function parse_PtgBool(blob, length) { blob.l++; return blob.read_shift(1)!==0;}
5526
5527/* 2.5.198.79 */
5528function parse_PtgNum(blob, length) { blob.l++; return parse_Xnum(blob, 8); }
5529
5530/* 2.5.198.89 */
5531function parse_PtgStr(blob, length) { blob.l++; return parse_ShortXLUnicodeString(blob); }
5532
5533/* 2.5.192.112 + 2.5.192.11{3,4,5,6,7} */
5534function parse_SerAr(blob) {
5535    var val = [];
5536    switch((val[0] = blob.read_shift(1))) {
5537        /* 2.5.192.113 */
5538        case 0x04: /* SerBool -- boolean */
5539            val[1] = parsebool(blob, 1) ? 'TRUE' : 'FALSE';
5540            blob.l += 7; break;
5541        /* 2.5.192.114 */
5542        case 0x10: /* SerErr -- error */
5543            val[1] = BErr[blob[blob.l]];
5544            blob.l += 8; break;
5545        /* 2.5.192.115 */
5546        case 0x00: /* SerNil -- honestly, I'm not sure how to reproduce this */
5547            blob.l += 8; break;
5548        /* 2.5.192.116 */
5549        case 0x01: /* SerNum -- Xnum */
5550            val[1] = parse_Xnum(blob, 8); break;
5551        /* 2.5.192.117 */
5552        case 0x02: /* SerStr -- XLUnicodeString (<256 chars) */
5553            val[1] = parse_XLUnicodeString(blob); break;
5554        // default: throw "Bad SerAr: " + val[0]; /* Unreachable */
5555    }
5556    return val;
5557}
5558
5559/* 2.5.198.61 */
5560function parse_PtgExtraMem(blob, cce) {
5561    var count = blob.read_shift(2);
5562    var out = [];
5563    for(var i = 0; i != count; ++i) out.push(parse_Ref8U(blob, 8));
5564    return out;
5565}
5566
5567/* 2.5.198.59 */
5568function parse_PtgExtraArray(blob) {
5569    var cols = 1 + blob.read_shift(1); //DColByteU
5570    var rows = 1 + blob.read_shift(2); //DRw
5571    for(var i = 0, o=[]; i != rows && (o[i] = []); ++i)
5572        for(var j = 0; j != cols; ++j) o[i][j] = parse_SerAr(blob);
5573    return o;
5574}
5575
5576/* 2.5.198.76 */
5577function parse_PtgName(blob, length) {
5578    var type = (blob.read_shift(1) >>> 5) & 0x03;
5579    var nameindex = blob.read_shift(4);
5580    return [type, 0, nameindex];
5581}
5582
5583/* 2.5.198.77 */
5584function parse_PtgNameX(blob, length) {
5585    var type = (blob.read_shift(1) >>> 5) & 0x03;
5586    var ixti = blob.read_shift(2); // XtiIndex
5587    var nameindex = blob.read_shift(4);
5588    return [type, ixti, nameindex];
5589}
5590
5591/* 2.5.198.70 */
5592function parse_PtgMemArea(blob, length) {
5593    var type = (blob.read_shift(1) >>> 5) & 0x03;
5594    blob.l += 4;
5595    var cce = blob.read_shift(2);
5596    return [type, cce];
5597}
5598
5599/* 2.5.198.72 */
5600function parse_PtgMemFunc(blob, length) {
5601    var type = (blob.read_shift(1) >>> 5) & 0x03;
5602    var cce = blob.read_shift(2);
5603    return [type, cce];
5604}
5605
5606
5607/* 2.5.198.86 */
5608function parse_PtgRefErr(blob, length) {
5609    var type = (blob.read_shift(1) >>> 5) & 0x03;
5610    blob.l += 4;
5611    return [type];
5612}
5613
5614/* 2.5.198.26 */
5615var parse_PtgAdd = parseread1;
5616/* 2.5.198.45 */
5617var parse_PtgDiv = parseread1;
5618/* 2.5.198.56 */
5619var parse_PtgEq = parseread1;
5620/* 2.5.198.64 */
5621var parse_PtgGe = parseread1;
5622/* 2.5.198.65 */
5623var parse_PtgGt = parseread1;
5624/* 2.5.198.67 */
5625var parse_PtgIsect = parseread1;
5626/* 2.5.198.68 */
5627var parse_PtgLe = parseread1;
5628/* 2.5.198.69 */
5629var parse_PtgLt = parseread1;
5630/* 2.5.198.74 */
5631var parse_PtgMissArg = parseread1;
5632/* 2.5.198.75 */
5633var parse_PtgMul = parseread1;
5634/* 2.5.198.78 */
5635var parse_PtgNe = parseread1;
5636/* 2.5.198.80 */
5637var parse_PtgParen = parseread1;
5638/* 2.5.198.81 */
5639var parse_PtgPercent = parseread1;
5640/* 2.5.198.82 */
5641var parse_PtgPower = parseread1;
5642/* 2.5.198.83 */
5643var parse_PtgRange = parseread1;
5644/* 2.5.198.90 */
5645var parse_PtgSub = parseread1;
5646/* 2.5.198.93 */
5647var parse_PtgUminus = parseread1;
5648/* 2.5.198.94 */
5649var parse_PtgUnion = parseread1;
5650/* 2.5.198.95 */
5651var parse_PtgUplus = parseread1;
5652
5653/* 2.5.198.71 */
5654var parse_PtgMemErr = parsenoop;
5655/* 2.5.198.73 */
5656var parse_PtgMemNoMem = parsenoop;
5657/* 2.5.198.87 */
5658var parse_PtgRefErr3d = parsenoop;
5659/* 2.5.198.92 */
5660var parse_PtgTbl = parsenoop;
5661
5662/* 2.5.198.25 */
5663var PtgTypes = {
5664    0x01: { n:'PtgExp', f:parse_PtgExp },
5665    0x02: { n:'PtgTbl', f:parse_PtgTbl },
5666    0x03: { n:'PtgAdd', f:parse_PtgAdd },
5667    0x04: { n:'PtgSub', f:parse_PtgSub },
5668    0x05: { n:'PtgMul', f:parse_PtgMul },
5669    0x06: { n:'PtgDiv', f:parse_PtgDiv },
5670    0x07: { n:'PtgPower', f:parse_PtgPower },
5671    0x08: { n:'PtgConcat', f:parse_PtgConcat },
5672    0x09: { n:'PtgLt', f:parse_PtgLt },
5673    0x0A: { n:'PtgLe', f:parse_PtgLe },
5674    0x0B: { n:'PtgEq', f:parse_PtgEq },
5675    0x0C: { n:'PtgGe', f:parse_PtgGe },
5676    0x0D: { n:'PtgGt', f:parse_PtgGt },
5677    0x0E: { n:'PtgNe', f:parse_PtgNe },
5678    0x0F: { n:'PtgIsect', f:parse_PtgIsect },
5679    0x10: { n:'PtgUnion', f:parse_PtgUnion },
5680    0x11: { n:'PtgRange', f:parse_PtgRange },
5681    0x12: { n:'PtgUplus', f:parse_PtgUplus },
5682    0x13: { n:'PtgUminus', f:parse_PtgUminus },
5683    0x14: { n:'PtgPercent', f:parse_PtgPercent },
5684    0x15: { n:'PtgParen', f:parse_PtgParen },
5685    0x16: { n:'PtgMissArg', f:parse_PtgMissArg },
5686    0x17: { n:'PtgStr', f:parse_PtgStr },
5687    0x1C: { n:'PtgErr', f:parse_PtgErr },
5688    0x1D: { n:'PtgBool', f:parse_PtgBool },
5689    0x1E: { n:'PtgInt', f:parse_PtgInt },
5690    0x1F: { n:'PtgNum', f:parse_PtgNum },
5691    0x20: { n:'PtgArray', f:parse_PtgArray },
5692    0x21: { n:'PtgFunc', f:parse_PtgFunc },
5693    0x22: { n:'PtgFuncVar', f:parse_PtgFuncVar },
5694    0x23: { n:'PtgName', f:parse_PtgName },
5695    0x24: { n:'PtgRef', f:parse_PtgRef },
5696    0x25: { n:'PtgArea', f:parse_PtgArea },
5697    0x26: { n:'PtgMemArea', f:parse_PtgMemArea },
5698    0x27: { n:'PtgMemErr', f:parse_PtgMemErr },
5699    0x28: { n:'PtgMemNoMem', f:parse_PtgMemNoMem },
5700    0x29: { n:'PtgMemFunc', f:parse_PtgMemFunc },
5701    0x2A: { n:'PtgRefErr', f:parse_PtgRefErr },
5702    0x2B: { n:'PtgAreaErr', f:parse_PtgAreaErr },
5703    0x2C: { n:'PtgRefN', f:parse_PtgRefN },
5704    0x2D: { n:'PtgAreaN', f:parse_PtgAreaN },
5705    0x39: { n:'PtgNameX', f:parse_PtgNameX },
5706    0x3A: { n:'PtgRef3d', f:parse_PtgRef3d },
5707    0x3B: { n:'PtgArea3d', f:parse_PtgArea3d },
5708    0x3C: { n:'PtgRefErr3d', f:parse_PtgRefErr3d },
5709    0x3D: { n:'PtgAreaErr3d', f:parse_PtgAreaErr3d },
5710    0xFF: {}
5711};
5712/* These are duplicated in the PtgTypes table */
5713var PtgDupes = {
5714    0x40: 0x20, 0x60: 0x20,
5715    0x41: 0x21, 0x61: 0x21,
5716    0x42: 0x22, 0x62: 0x22,
5717    0x43: 0x23, 0x63: 0x23,
5718    0x44: 0x24, 0x64: 0x24,
5719    0x45: 0x25, 0x65: 0x25,
5720    0x46: 0x26, 0x66: 0x26,
5721    0x47: 0x27, 0x67: 0x27,
5722    0x48: 0x28, 0x68: 0x28,
5723    0x49: 0x29, 0x69: 0x29,
5724    0x4A: 0x2A, 0x6A: 0x2A,
5725    0x4B: 0x2B, 0x6B: 0x2B,
5726    0x4C: 0x2C, 0x6C: 0x2C,
5727    0x4D: 0x2D, 0x6D: 0x2D,
5728    0x59: 0x39, 0x79: 0x39,
5729    0x5A: 0x3A, 0x7A: 0x3A,
5730    0x5B: 0x3B, 0x7B: 0x3B,
5731    0x5C: 0x3C, 0x7C: 0x3C,
5732    0x5D: 0x3D, 0x7D: 0x3D
5733};
5734(function(){for(var y in PtgDupes) PtgTypes[y] = PtgTypes[PtgDupes[y]];})();
5735
5736var Ptg18 = {};
5737var Ptg19 = {
5738    0x01: { n:'PtgAttrSemi', f:parse_PtgAttrSemi },
5739    0x02: { n:'PtgAttrIf', f:parse_PtgAttrIf },
5740    0x04: { n:'PtgAttrChoose', f:parse_PtgAttrChoose },
5741    0x08: { n:'PtgAttrGoto', f:parse_PtgAttrGoto },
5742    0x10: { n:'PtgAttrSum', f:parse_PtgAttrSum },
5743    0x20: { n:'PtgAttrBaxcel', f:parse_PtgAttrBaxcel },
5744    0x40: { n:'PtgAttrSpace', f:parse_PtgAttrSpace },
5745    0x41: { n:'PtgAttrSpaceSemi', f:parse_PtgAttrSpaceSemi },
5746    0xFF: {}
5747};
5748
5749/* 2.4.127 TODO */
5750function parse_Formula(blob, length, opts) {
5751    var cell = parse_XLSCell(blob, 6);
5752    var val = parse_FormulaValue(blob,8);
5753    var flags = blob.read_shift(1);
5754    blob.read_shift(1);
5755    var chn = blob.read_shift(4);
5756    var cbf = "";
5757    if(opts.biff === 5) blob.l += length-20;
5758    else cbf = parse_XLSCellParsedFormula(blob, length-20, opts);
5759    return {cell:cell, val:val[0], formula:cbf, shared: (flags >> 3) & 1, tt:val[1]};
5760}
5761
5762/* 2.5.133 TODO: how to emit empty strings? */
5763function parse_FormulaValue(blob) {
5764    var b;
5765    if(__readUInt16LE(blob,blob.l + 6) !== 0xFFFF) return [parse_Xnum(blob),'n'];
5766    switch(blob[blob.l]) {
5767        case 0x00: blob.l += 8; return ["String", 's'];
5768        case 0x01: b = blob[blob.l+2] === 0x1; blob.l += 8; return [b,'b'];
5769        case 0x02: b = blob[blob.l+2]; blob.l += 8; return [b,'e'];
5770        case 0x03: blob.l += 8; return ["",'s'];
5771    }
5772}
5773
5774/* 2.5.198.103 */
5775function parse_RgbExtra(blob, length, rgce, opts) {
5776    if(opts.biff < 8) return parsenoop(blob, length);
5777    var target = blob.l + length;
5778    var o = [];
5779    for(var i = 0; i !== rgce.length; ++i) {
5780        switch(rgce[i][0]) {
5781            case 'PtgArray': /* PtgArray -> PtgExtraArray */
5782                rgce[i][1] = parse_PtgExtraArray(blob);
5783                o.push(rgce[i][1]);
5784                break;
5785            case 'PtgMemArea': /* PtgMemArea -> PtgExtraMem */
5786                rgce[i][2] = parse_PtgExtraMem(blob, rgce[i][1]);
5787                o.push(rgce[i][2]);
5788                break;
5789            default: break;
5790        }
5791    }
5792    length = target - blob.l;
5793    if(length !== 0) o.push(parsenoop(blob, length));
5794    return o;
5795}
5796
5797/* 2.5.198.21 */
5798function parse_NameParsedFormula(blob, length, opts, cce) {
5799    var target = blob.l + length;
5800    var rgce = parse_Rgce(blob, cce);
5801    var rgcb;
5802    if(target !== blob.l) rgcb = parse_RgbExtra(blob, target - blob.l, rgce, opts);
5803    return [rgce, rgcb];
5804}
5805
5806/* 2.5.198.3 TODO */
5807function parse_XLSCellParsedFormula(blob, length, opts) {
5808    var target = blob.l + length;
5809    var rgcb, cce = blob.read_shift(2); // length of rgce
5810    if(cce == 0xFFFF) return [[],parsenoop(blob, length-2)];
5811    var rgce = parse_Rgce(blob, cce);
5812    if(length !== cce + 2) rgcb = parse_RgbExtra(blob, length - cce - 2, rgce, opts);
5813    return [rgce, rgcb];
5814}
5815
5816/* 2.5.198.118 TODO */
5817function parse_SharedParsedFormula(blob, length, opts) {
5818    var target = blob.l + length;
5819    var rgcb, cce = blob.read_shift(2); // length of rgce
5820    var rgce = parse_Rgce(blob, cce);
5821    if(cce == 0xFFFF) return [[],parsenoop(blob, length-2)];
5822    if(length !== cce + 2) rgcb = parse_RgbExtra(blob, target - cce - 2, rgce, opts);
5823    return [rgce, rgcb];
5824}
5825
5826/* 2.5.198.1 TODO */
5827function parse_ArrayParsedFormula(blob, length, opts, ref) {
5828    var target = blob.l + length;
5829    var rgcb, cce = blob.read_shift(2); // length of rgce
5830    if(cce == 0xFFFF) return [[],parsenoop(blob, length-2)];
5831    var rgce = parse_Rgce(blob, cce);
5832    if(length !== cce + 2) rgcb = parse_RgbExtra(blob, target - cce - 2, rgce, opts);
5833    return [rgce, rgcb];
5834}
5835
5836/* 2.5.198.104 */
5837function parse_Rgce(blob, length) {
5838    var target = blob.l + length;
5839    var R, id, ptgs = [];
5840    while(target != blob.l) {
5841        length = target - blob.l;
5842        id = blob[blob.l];
5843        R = PtgTypes[id];
5844        //console.log("ptg", id, R)
5845        if(id === 0x18 || id === 0x19) {
5846            id = blob[blob.l + 1];
5847            R = (id === 0x18 ? Ptg18 : Ptg19)[id];
5848        }
5849        if(!R || !R.f) { ptgs.push(parsenoop(blob, length)); }
5850        else { ptgs.push([R.n, R.f(blob, length)]); }
5851    }
5852    return ptgs;
5853}
5854
5855function mapper(x) { return x.map(function f2(y) { return y[1];}).join(",");}
5856
5857/* 2.2.2 + Magic TODO */
5858function stringify_formula(formula, range, cell, supbooks, opts) {
5859    if(opts !== undefined && opts.biff === 5) return "BIFF5??";
5860    var _range = range !== undefined ? range : {s:{c:0, r:0}};
5861    var stack = [], e1, e2, type, c, ixti, nameidx, r;
5862    if(!formula[0] || !formula[0][0]) return "";
5863    //console.log("--",cell,formula[0])
5864    for(var ff = 0, fflen = formula[0].length; ff < fflen; ++ff) {
5865        var f = formula[0][ff];
5866        //console.log("++",f, stack)
5867        switch(f[0]) {
5868        /* 2.2.2.1 Unary Operator Tokens */
5869            /* 2.5.198.93 */
5870            case 'PtgUminus': stack.push("-" + stack.pop()); break;
5871            /* 2.5.198.95 */
5872            case 'PtgUplus': stack.push("+" + stack.pop()); break;
5873            /* 2.5.198.81 */
5874            case 'PtgPercent': stack.push(stack.pop() + "%"); break;
5875
5876        /* 2.2.2.1 Binary Value Operator Token */
5877            /* 2.5.198.26 */
5878            case 'PtgAdd':
5879                e1 = stack.pop(); e2 = stack.pop();
5880                stack.push(e2+"+"+e1);
5881                break;
5882            /* 2.5.198.90 */
5883            case 'PtgSub':
5884                e1 = stack.pop(); e2 = stack.pop();
5885                stack.push(e2+"-"+e1);
5886                break;
5887            /* 2.5.198.75 */
5888            case 'PtgMul':
5889                e1 = stack.pop(); e2 = stack.pop();
5890                stack.push(e2+"*"+e1);
5891                break;
5892            /* 2.5.198.45 */
5893            case 'PtgDiv':
5894                e1 = stack.pop(); e2 = stack.pop();
5895                stack.push(e2+"/"+e1);
5896                break;
5897            /* 2.5.198.82 */
5898            case 'PtgPower':
5899                e1 = stack.pop(); e2 = stack.pop();
5900                stack.push(e2+"^"+e1);
5901                break;
5902            /* 2.5.198.43 */
5903            case 'PtgConcat':
5904                e1 = stack.pop(); e2 = stack.pop();
5905                stack.push(e2+"&"+e1);
5906                break;
5907            /* 2.5.198.69 */
5908            case 'PtgLt':
5909                e1 = stack.pop(); e2 = stack.pop();
5910                stack.push(e2+"<"+e1);
5911                break;
5912            /* 2.5.198.68 */
5913            case 'PtgLe':
5914                e1 = stack.pop(); e2 = stack.pop();
5915                stack.push(e2+"<="+e1);
5916                break;
5917            /* 2.5.198.56 */
5918            case 'PtgEq':
5919                e1 = stack.pop(); e2 = stack.pop();
5920                stack.push(e2+"="+e1);
5921                break;
5922            /* 2.5.198.64 */
5923            case 'PtgGe':
5924                e1 = stack.pop(); e2 = stack.pop();
5925                stack.push(e2+">="+e1);
5926                break;
5927            /* 2.5.198.65 */
5928            case 'PtgGt':
5929                e1 = stack.pop(); e2 = stack.pop();
5930                stack.push(e2+">"+e1);
5931                break;
5932            /* 2.5.198.78 */
5933            case 'PtgNe':
5934                e1 = stack.pop(); e2 = stack.pop();
5935                stack.push(e2+"<>"+e1);
5936                break;
5937
5938        /* 2.2.2.1 Binary Reference Operator Token */
5939            /* 2.5.198.67 */
5940            case 'PtgIsect':
5941                e1 = stack.pop(); e2 = stack.pop();
5942                stack.push(e2+" "+e1);
5943                break;
5944            case 'PtgUnion':
5945                e1 = stack.pop(); e2 = stack.pop();
5946                stack.push(e2+","+e1);
5947                break;
5948            case 'PtgRange': break;
5949
5950        /* 2.2.2.3 Control Tokens "can be ignored" */
5951            /* 2.5.198.34 */
5952            case 'PtgAttrChoose': break;
5953            /* 2.5.198.35 */
5954            case 'PtgAttrGoto': break;
5955            /* 2.5.198.36 */
5956            case 'PtgAttrIf': break;
5957
5958
5959            /* 2.5.198.84 */
5960            case 'PtgRef':
5961                type = f[1][0]; c = shift_cell_xls(decode_cell(encode_cell(f[1][1])), _range);
5962                stack.push(encode_cell(c));
5963                break;
5964            /* 2.5.198.88 */
5965            case 'PtgRefN':
5966                type = f[1][0]; c = shift_cell_xls(decode_cell(encode_cell(f[1][1])), cell);
5967                stack.push(encode_cell(c));
5968                break;
5969            case 'PtgRef3d': // TODO: lots of stuff
5970                type = f[1][0]; ixti = f[1][1]; c = shift_cell_xls(f[1][2], _range);
5971                stack.push(supbooks[1][ixti+1]+"!"+encode_cell(c));
5972                break;
5973
5974        /* Function Call */
5975            /* 2.5.198.62 */
5976            case 'PtgFunc':
5977            /* 2.5.198.63 */
5978            case 'PtgFuncVar':
5979                /* f[1] = [argc, func] */
5980                var argc = f[1][0], func = f[1][1];
5981                if(!argc) argc = 0;
5982                var args = stack.slice(-argc);
5983                stack.length -= argc;
5984                if(func === 'User') func = args.shift();
5985                stack.push(func + "(" + args.join(",") + ")");
5986                break;
5987
5988            /* 2.5.198.42 */
5989            case 'PtgBool': stack.push(f[1] ? "TRUE" : "FALSE"); break;
5990            /* 2.5.198.66 */
5991            case 'PtgInt': stack.push(f[1]); break;
5992            /* 2.5.198.79 TODO: precision? */
5993            case 'PtgNum': stack.push(String(f[1])); break;
5994            /* 2.5.198.89 */
5995            case 'PtgStr': stack.push('"' + f[1] + '"'); break;
5996            /* 2.5.198.57 */
5997            case 'PtgErr': stack.push(f[1]); break;
5998            /* 2.5.198.27 TODO: fixed points */
5999            case 'PtgArea':
6000                type = f[1][0]; r = shift_range_xls(f[1][1], _range);
6001                stack.push(encode_range(r));
6002                break;
6003            /* 2.5.198.28 */
6004            case 'PtgArea3d': // TODO: lots of stuff
6005                type = f[1][0]; ixti = f[1][1]; r = f[1][2];
6006                stack.push(supbooks[1][ixti+1]+"!"+encode_range(r));
6007                break;
6008            /* 2.5.198.41 */
6009            case 'PtgAttrSum':
6010                stack.push("SUM(" + stack.pop() + ")");
6011                break;
6012
6013        /* Expression Prefixes */
6014            /* 2.5.198.37 */
6015            case 'PtgAttrSemi': break;
6016
6017            /* 2.5.97.60 TODO: do something different for revisions */
6018            case 'PtgName':
6019                /* f[1] = type, 0, nameindex */
6020                nameidx = f[1][2];
6021                var lbl = supbooks[0][nameidx];
6022                var name = lbl.Name;
6023                if(name in XLSXFutureFunctions) name = XLSXFutureFunctions[name];
6024                stack.push(name);
6025                break;
6026
6027            /* 2.5.97.61 TODO: do something different for revisions */
6028            case 'PtgNameX':
6029                /* f[1] = type, ixti, nameindex */
6030                var bookidx = f[1][1]; nameidx = f[1][2]; var externbook;
6031                /* TODO: Properly handle missing values */
6032                if(supbooks[bookidx+1]) externbook = supbooks[bookidx+1][nameidx];
6033                else if(supbooks[bookidx-1]) externbook = supbooks[bookidx-1][nameidx];
6034                if(!externbook) externbook = {body: "??NAMEX??"};
6035                stack.push(externbook.body);
6036                break;
6037
6038        /* 2.2.2.4 Display Tokens */
6039            /* 2.5.198.80 */
6040            case 'PtgParen': stack.push('(' + stack.pop() + ')'); break;
6041
6042            /* 2.5.198.86 */
6043            case 'PtgRefErr': stack.push('#REF!'); break;
6044
6045        /* */
6046            /* 2.5.198.58 TODO */
6047            case 'PtgExp':
6048                c = {c:f[1][1],r:f[1][0]};
6049                var q = {c: cell.c, r:cell.r};
6050                if(supbooks.sharedf[encode_cell(c)]) {
6051                    var parsedf = (supbooks.sharedf[encode_cell(c)]);
6052                    stack.push(stringify_formula(parsedf, _range, q, supbooks, opts));
6053                }
6054                else {
6055                    var fnd = false;
6056                    for(e1=0;e1!=supbooks.arrayf.length; ++e1) {
6057                        /* TODO: should be something like range_has */
6058                        e2 = supbooks.arrayf[e1];
6059                        if(c.c < e2[0].s.c || c.c > e2[0].e.c) continue;
6060                        if(c.r < e2[0].s.r || c.r > e2[0].e.r) continue;
6061                        stack.push(stringify_formula(e2[1], _range, q, supbooks, opts));
6062                    }
6063                    if(!fnd) stack.push(f[1]);
6064                }
6065                break;
6066
6067            /* 2.5.198.32 TODO */
6068            case 'PtgArray':
6069                stack.push("{" + f[1].map(mapper).join(";") + "}");
6070                break;
6071
6072        /* 2.2.2.5 Mem Tokens */
6073            /* 2.5.198.70 TODO: confirm this is a non-display */
6074            case 'PtgMemArea':
6075                //stack.push("(" + f[2].map(encode_range).join(",") + ")");
6076                break;
6077
6078            /* 2.5.198.38 TODO */
6079            case 'PtgAttrSpace': break;
6080
6081            /* 2.5.198.92 TODO */
6082            case 'PtgTbl': break;
6083
6084            /* 2.5.198.71 */
6085            case 'PtgMemErr': break;
6086
6087            /* 2.5.198.74 */
6088            case 'PtgMissArg':
6089                stack.push("");
6090                break;
6091
6092            /* 2.5.198.29 TODO */
6093            case 'PtgAreaErr': break;
6094
6095            /* 2.5.198.31 TODO */
6096            case 'PtgAreaN': stack.push(""); break;
6097
6098            /* 2.5.198.87 TODO */
6099            case 'PtgRefErr3d': break;
6100
6101            /* 2.5.198.72 TODO */
6102            case 'PtgMemFunc': break;
6103
6104            default: throw 'Unrecognized Formula Token: ' + f;
6105        }
6106        //console.log("::",f, stack)
6107    }
6108    //console.log("--",stack);
6109    return stack[0];
6110}
6111
6112/* [MS-XLSB] 2.5.97.4 CellParsedFormula TODO: use similar logic to js-xls */
6113function parse_XLSBCellParsedFormula(data, length) {
6114    var cce = data.read_shift(4);
6115    return parsenoop(data, length-4);
6116}
6117/* [MS-XLS] 2.5.198.44 */
6118var PtgDataType = {
6119    0x1: "REFERENCE", // reference to range
6120    0x2: "VALUE", // single value
6121    0x3: "ARRAY" // array of values
6122};
6123
6124/* [MS-XLS] 2.5.198.4 */
6125var Cetab = {
6126    0x0000: 'BEEP',
6127    0x0001: 'OPEN',
6128    0x0002: 'OPEN.LINKS',
6129    0x0003: 'CLOSE.ALL',
6130    0x0004: 'SAVE',
6131    0x0005: 'SAVE.AS',
6132    0x0006: 'FILE.DELETE',
6133    0x0007: 'PAGE.SETUP',
6134    0x0008: 'PRINT',
6135    0x0009: 'PRINTER.SETUP',
6136    0x000A: 'QUIT',
6137    0x000B: 'NEW.WINDOW',
6138    0x000C: 'ARRANGE.ALL',
6139    0x000D: 'WINDOW.SIZE',
6140    0x000E: 'WINDOW.MOVE',
6141    0x000F: 'FULL',
6142    0x0010: 'CLOSE',
6143    0x0011: 'RUN',
6144    0x0016: 'SET.PRINT.AREA',
6145    0x0017: 'SET.PRINT.TITLES',
6146    0x0018: 'SET.PAGE.BREAK',
6147    0x0019: 'REMOVE.PAGE.BREAK',
6148    0x001A: 'FONT',
6149    0x001B: 'DISPLAY',
6150    0x001C: 'PROTECT.DOCUMENT',
6151    0x001D: 'PRECISION',
6152    0x001E: 'A1.R1C1',
6153    0x001F: 'CALCULATE.NOW',
6154    0x0020: 'CALCULATION',
6155    0x0022: 'DATA.FIND',
6156    0x0023: 'EXTRACT',
6157    0x0024: 'DATA.DELETE',
6158    0x0025: 'SET.DATABASE',
6159    0x0026: 'SET.CRITERIA',
6160    0x0027: 'SORT',
6161    0x0028: 'DATA.SERIES',
6162    0x0029: 'TABLE',
6163    0x002A: 'FORMAT.NUMBER',
6164    0x002B: 'ALIGNMENT',
6165    0x002C: 'STYLE',
6166    0x002D: 'BORDER',
6167    0x002E: 'CELL.PROTECTION',
6168    0x002F: 'COLUMN.WIDTH',
6169    0x0030: 'UNDO',
6170    0x0031: 'CUT',
6171    0x0032: 'COPY',
6172    0x0033: 'PASTE',
6173    0x0034: 'CLEAR',
6174    0x0035: 'PASTE.SPECIAL',
6175    0x0036: 'EDIT.DELETE',
6176    0x0037: 'INSERT',
6177    0x0038: 'FILL.RIGHT',
6178    0x0039: 'FILL.DOWN',
6179    0x003D: 'DEFINE.NAME',
6180    0x003E: 'CREATE.NAMES',
6181    0x003F: 'FORMULA.GOTO',
6182    0x0040: 'FORMULA.FIND',
6183    0x0041: 'SELECT.LAST.CELL',
6184    0x0042: 'SHOW.ACTIVE.CELL',
6185    0x0043: 'GALLERY.AREA',
6186    0x0044: 'GALLERY.BAR',
6187    0x0045: 'GALLERY.COLUMN',
6188    0x0046: 'GALLERY.LINE',
6189    0x0047: 'GALLERY.PIE',
6190    0x0048: 'GALLERY.SCATTER',
6191    0x0049: 'COMBINATION',
6192    0x004A: 'PREFERRED',
6193    0x004B: 'ADD.OVERLAY',
6194    0x004C: 'GRIDLINES',
6195    0x004D: 'SET.PREFERRED',
6196    0x004E: 'AXES',
6197    0x004F: 'LEGEND',
6198    0x0050: 'ATTACH.TEXT',
6199    0x0051: 'ADD.ARROW',
6200    0x0052: 'SELECT.CHART',
6201    0x0053: 'SELECT.PLOT.AREA',
6202    0x0054: 'PATTERNS',
6203    0x0055: 'MAIN.CHART',
6204    0x0056: 'OVERLAY',
6205    0x0057: 'SCALE',
6206    0x0058: 'FORMAT.LEGEND',
6207    0x0059: 'FORMAT.TEXT',
6208    0x005A: 'EDIT.REPEAT',
6209    0x005B: 'PARSE',
6210    0x005C: 'JUSTIFY',
6211    0x005D: 'HIDE',
6212    0x005E: 'UNHIDE',
6213    0x005F: 'WORKSPACE',
6214    0x0060: 'FORMULA',
6215    0x0061: 'FORMULA.FILL',
6216    0x0062: 'FORMULA.ARRAY',
6217    0x0063: 'DATA.FIND.NEXT',
6218    0x0064: 'DATA.FIND.PREV',
6219    0x0065: 'FORMULA.FIND.NEXT',
6220    0x0066: 'FORMULA.FIND.PREV',
6221    0x0067: 'ACTIVATE',
6222    0x0068: 'ACTIVATE.NEXT',
6223    0x0069: 'ACTIVATE.PREV',
6224    0x006A: 'UNLOCKED.NEXT',
6225    0x006B: 'UNLOCKED.PREV',
6226    0x006C: 'COPY.PICTURE',
6227    0x006D: 'SELECT',
6228    0x006E: 'DELETE.NAME',
6229    0x006F: 'DELETE.FORMAT',
6230    0x0070: 'VLINE',
6231    0x0071: 'HLINE',
6232    0x0072: 'VPAGE',
6233    0x0073: 'HPAGE',
6234    0x0074: 'VSCROLL',
6235    0x0075: 'HSCROLL',
6236    0x0076: 'ALERT',
6237    0x0077: 'NEW',
6238    0x0078: 'CANCEL.COPY',
6239    0x0079: 'SHOW.CLIPBOARD',
6240    0x007A: 'MESSAGE',
6241    0x007C: 'PASTE.LINK',
6242    0x007D: 'APP.ACTIVATE',
6243    0x007E: 'DELETE.ARROW',
6244    0x007F: 'ROW.HEIGHT',
6245    0x0080: 'FORMAT.MOVE',
6246    0x0081: 'FORMAT.SIZE',
6247    0x0082: 'FORMULA.REPLACE',
6248    0x0083: 'SEND.KEYS',
6249    0x0084: 'SELECT.SPECIAL',
6250    0x0085: 'APPLY.NAMES',
6251    0x0086: 'REPLACE.FONT',
6252    0x0087: 'FREEZE.PANES',
6253    0x0088: 'SHOW.INFO',
6254    0x0089: 'SPLIT',
6255    0x008A: 'ON.WINDOW',
6256    0x008B: 'ON.DATA',
6257    0x008C: 'DISABLE.INPUT',
6258    0x008E: 'OUTLINE',
6259    0x008F: 'LIST.NAMES',
6260    0x0090: 'FILE.CLOSE',
6261    0x0091: 'SAVE.WORKBOOK',
6262    0x0092: 'DATA.FORM',
6263    0x0093: 'COPY.CHART',
6264    0x0094: 'ON.TIME',
6265    0x0095: 'WAIT',
6266    0x0096: 'FORMAT.FONT',
6267    0x0097: 'FILL.UP',
6268    0x0098: 'FILL.LEFT',
6269    0x0099: 'DELETE.OVERLAY',
6270    0x009B: 'SHORT.MENUS',
6271    0x009F: 'SET.UPDATE.STATUS',
6272    0x00A1: 'COLOR.PALETTE',
6273    0x00A2: 'DELETE.STYLE',
6274    0x00A3: 'WINDOW.RESTORE',
6275    0x00A4: 'WINDOW.MAXIMIZE',
6276    0x00A6: 'CHANGE.LINK',
6277    0x00A7: 'CALCULATE.DOCUMENT',
6278    0x00A8: 'ON.KEY',
6279    0x00A9: 'APP.RESTORE',
6280    0x00AA: 'APP.MOVE',
6281    0x00AB: 'APP.SIZE',
6282    0x00AC: 'APP.MINIMIZE',
6283    0x00AD: 'APP.MAXIMIZE',
6284    0x00AE: 'BRING.TO.FRONT',
6285    0x00AF: 'SEND.TO.BACK',
6286    0x00B9: 'MAIN.CHART.TYPE',
6287    0x00BA: 'OVERLAY.CHART.TYPE',
6288    0x00BB: 'SELECT.END',
6289    0x00BC: 'OPEN.MAIL',
6290    0x00BD: 'SEND.MAIL',
6291    0x00BE: 'STANDARD.FONT',
6292    0x00BF: 'CONSOLIDATE',
6293    0x00C0: 'SORT.SPECIAL',
6294    0x00C1: 'GALLERY.3D.AREA',
6295    0x00C2: 'GALLERY.3D.COLUMN',
6296    0x00C3: 'GALLERY.3D.LINE',
6297    0x00C4: 'GALLERY.3D.PIE',
6298    0x00C5: 'VIEW.3D',
6299    0x00C6: 'GOAL.SEEK',
6300    0x00C7: 'WORKGROUP',
6301    0x00C8: 'FILL.GROUP',
6302    0x00C9: 'UPDATE.LINK',
6303    0x00CA: 'PROMOTE',
6304    0x00CB: 'DEMOTE',
6305    0x00CC: 'SHOW.DETAIL',
6306    0x00CE: 'UNGROUP',
6307    0x00CF: 'OBJECT.PROPERTIES',
6308    0x00D0: 'SAVE.NEW.OBJECT',
6309    0x00D1: 'SHARE',
6310    0x00D2: 'SHARE.NAME',
6311    0x00D3: 'DUPLICATE',
6312    0x00D4: 'APPLY.STYLE',
6313    0x00D5: 'ASSIGN.TO.OBJECT',
6314    0x00D6: 'OBJECT.PROTECTION',
6315    0x00D7: 'HIDE.OBJECT',
6316    0x00D8: 'SET.EXTRACT',
6317    0x00D9: 'CREATE.PUBLISHER',
6318    0x00DA: 'SUBSCRIBE.TO',
6319    0x00DB: 'ATTRIBUTES',
6320    0x00DC: 'SHOW.TOOLBAR',
6321    0x00DE: 'PRINT.PREVIEW',
6322    0x00DF: 'EDIT.COLOR',
6323    0x00E0: 'SHOW.LEVELS',
6324    0x00E1: 'FORMAT.MAIN',
6325    0x00E2: 'FORMAT.OVERLAY',
6326    0x00E3: 'ON.RECALC',
6327    0x00E4: 'EDIT.SERIES',
6328    0x00E5: 'DEFINE.STYLE',
6329    0x00F0: 'LINE.PRINT',
6330    0x00F3: 'ENTER.DATA',
6331    0x00F9: 'GALLERY.RADAR',
6332    0x00FA: 'MERGE.STYLES',
6333    0x00FB: 'EDITION.OPTIONS',
6334    0x00FC: 'PASTE.PICTURE',
6335    0x00FD: 'PASTE.PICTURE.LINK',
6336    0x00FE: 'SPELLING',
6337    0x0100: 'ZOOM',
6338    0x0103: 'INSERT.OBJECT',
6339    0x0104: 'WINDOW.MINIMIZE',
6340    0x0109: 'SOUND.NOTE',
6341    0x010A: 'SOUND.PLAY',
6342    0x010B: 'FORMAT.SHAPE',
6343    0x010C: 'EXTEND.POLYGON',
6344    0x010D: 'FORMAT.AUTO',
6345    0x0110: 'GALLERY.3D.BAR',
6346    0x0111: 'GALLERY.3D.SURFACE',
6347    0x0112: 'FILL.AUTO',
6348    0x0114: 'CUSTOMIZE.TOOLBAR',
6349    0x0115: 'ADD.TOOL',
6350    0x0116: 'EDIT.OBJECT',
6351    0x0117: 'ON.DOUBLECLICK',
6352    0x0118: 'ON.ENTRY',
6353    0x0119: 'WORKBOOK.ADD',
6354    0x011A: 'WORKBOOK.MOVE',
6355    0x011B: 'WORKBOOK.COPY',
6356    0x011C: 'WORKBOOK.OPTIONS',
6357    0x011D: 'SAVE.WORKSPACE',
6358    0x0120: 'CHART.WIZARD',
6359    0x0121: 'DELETE.TOOL',
6360    0x0122: 'MOVE.TOOL',
6361    0x0123: 'WORKBOOK.SELECT',
6362    0x0124: 'WORKBOOK.ACTIVATE',
6363    0x0125: 'ASSIGN.TO.TOOL',
6364    0x0127: 'COPY.TOOL',
6365    0x0128: 'RESET.TOOL',
6366    0x0129: 'CONSTRAIN.NUMERIC',
6367    0x012A: 'PASTE.TOOL',
6368    0x012E: 'WORKBOOK.NEW',
6369    0x0131: 'SCENARIO.CELLS',
6370    0x0132: 'SCENARIO.DELETE',
6371    0x0133: 'SCENARIO.ADD',
6372    0x0134: 'SCENARIO.EDIT',
6373    0x0135: 'SCENARIO.SHOW',
6374    0x0136: 'SCENARIO.SHOW.NEXT',
6375    0x0137: 'SCENARIO.SUMMARY',
6376    0x0138: 'PIVOT.TABLE.WIZARD',
6377    0x0139: 'PIVOT.FIELD.PROPERTIES',
6378    0x013A: 'PIVOT.FIELD',
6379    0x013B: 'PIVOT.ITEM',
6380    0x013C: 'PIVOT.ADD.FIELDS',
6381    0x013E: 'OPTIONS.CALCULATION',
6382    0x013F: 'OPTIONS.EDIT',
6383    0x0140: 'OPTIONS.VIEW',
6384    0x0141: 'ADDIN.MANAGER',
6385    0x0142: 'MENU.EDITOR',
6386    0x0143: 'ATTACH.TOOLBARS',
6387    0x0144: 'VBAActivate',
6388    0x0145: 'OPTIONS.CHART',
6389    0x0148: 'VBA.INSERT.FILE',
6390    0x014A: 'VBA.PROCEDURE.DEFINITION',
6391    0x0150: 'ROUTING.SLIP',
6392    0x0152: 'ROUTE.DOCUMENT',
6393    0x0153: 'MAIL.LOGON',
6394    0x0156: 'INSERT.PICTURE',
6395    0x0157: 'EDIT.TOOL',
6396    0x0158: 'GALLERY.DOUGHNUT',
6397    0x015E: 'CHART.TREND',
6398    0x0160: 'PIVOT.ITEM.PROPERTIES',
6399    0x0162: 'WORKBOOK.INSERT',
6400    0x0163: 'OPTIONS.TRANSITION',
6401    0x0164: 'OPTIONS.GENERAL',
6402    0x0172: 'FILTER.ADVANCED',
6403    0x0175: 'MAIL.ADD.MAILER',
6404    0x0176: 'MAIL.DELETE.MAILER',
6405    0x0177: 'MAIL.REPLY',
6406    0x0178: 'MAIL.REPLY.ALL',
6407    0x0179: 'MAIL.FORWARD',
6408    0x017A: 'MAIL.NEXT.LETTER',
6409    0x017B: 'DATA.LABEL',
6410    0x017C: 'INSERT.TITLE',
6411    0x017D: 'FONT.PROPERTIES',
6412    0x017E: 'MACRO.OPTIONS',
6413    0x017F: 'WORKBOOK.HIDE',
6414    0x0180: 'WORKBOOK.UNHIDE',
6415    0x0181: 'WORKBOOK.DELETE',
6416    0x0182: 'WORKBOOK.NAME',
6417    0x0184: 'GALLERY.CUSTOM',
6418    0x0186: 'ADD.CHART.AUTOFORMAT',
6419    0x0187: 'DELETE.CHART.AUTOFORMAT',
6420    0x0188: 'CHART.ADD.DATA',
6421    0x0189: 'AUTO.OUTLINE',
6422    0x018A: 'TAB.ORDER',
6423    0x018B: 'SHOW.DIALOG',
6424    0x018C: 'SELECT.ALL',
6425    0x018D: 'UNGROUP.SHEETS',
6426    0x018E: 'SUBTOTAL.CREATE',
6427    0x018F: 'SUBTOTAL.REMOVE',
6428    0x0190: 'RENAME.OBJECT',
6429    0x019C: 'WORKBOOK.SCROLL',
6430    0x019D: 'WORKBOOK.NEXT',
6431    0x019E: 'WORKBOOK.PREV',
6432    0x019F: 'WORKBOOK.TAB.SPLIT',
6433    0x01A0: 'FULL.SCREEN',
6434    0x01A1: 'WORKBOOK.PROTECT',
6435    0x01A4: 'SCROLLBAR.PROPERTIES',
6436    0x01A5: 'PIVOT.SHOW.PAGES',
6437    0x01A6: 'TEXT.TO.COLUMNS',
6438    0x01A7: 'FORMAT.CHARTTYPE',
6439    0x01A8: 'LINK.FORMAT',
6440    0x01A9: 'TRACER.DISPLAY',
6441    0x01AE: 'TRACER.NAVIGATE',
6442    0x01AF: 'TRACER.CLEAR',
6443    0x01B0: 'TRACER.ERROR',
6444    0x01B1: 'PIVOT.FIELD.GROUP',
6445    0x01B2: 'PIVOT.FIELD.UNGROUP',
6446    0x01B3: 'CHECKBOX.PROPERTIES',
6447    0x01B4: 'LABEL.PROPERTIES',
6448    0x01B5: 'LISTBOX.PROPERTIES',
6449    0x01B6: 'EDITBOX.PROPERTIES',
6450    0x01B7: 'PIVOT.REFRESH',
6451    0x01B8: 'LINK.COMBO',
6452    0x01B9: 'OPEN.TEXT',
6453    0x01BA: 'HIDE.DIALOG',
6454    0x01BB: 'SET.DIALOG.FOCUS',
6455    0x01BC: 'ENABLE.OBJECT',
6456    0x01BD: 'PUSHBUTTON.PROPERTIES',
6457    0x01BE: 'SET.DIALOG.DEFAULT',
6458    0x01BF: 'FILTER',
6459    0x01C0: 'FILTER.SHOW.ALL',
6460    0x01C1: 'CLEAR.OUTLINE',
6461    0x01C2: 'FUNCTION.WIZARD',
6462    0x01C3: 'ADD.LIST.ITEM',
6463    0x01C4: 'SET.LIST.ITEM',
6464    0x01C5: 'REMOVE.LIST.ITEM',
6465    0x01C6: 'SELECT.LIST.ITEM',
6466    0x01C7: 'SET.CONTROL.VALUE',
6467    0x01C8: 'SAVE.COPY.AS',
6468    0x01CA: 'OPTIONS.LISTS.ADD',
6469    0x01CB: 'OPTIONS.LISTS.DELETE',
6470    0x01CC: 'SERIES.AXES',
6471    0x01CD: 'SERIES.X',
6472    0x01CE: 'SERIES.Y',
6473    0x01CF: 'ERRORBAR.X',
6474    0x01D0: 'ERRORBAR.Y',
6475    0x01D1: 'FORMAT.CHART',
6476    0x01D2: 'SERIES.ORDER',
6477    0x01D3: 'MAIL.LOGOFF',
6478    0x01D4: 'CLEAR.ROUTING.SLIP',
6479    0x01D5: 'APP.ACTIVATE.MICROSOFT',
6480    0x01D6: 'MAIL.EDIT.MAILER',
6481    0x01D7: 'ON.SHEET',
6482    0x01D8: 'STANDARD.WIDTH',
6483    0x01D9: 'SCENARIO.MERGE',
6484    0x01DA: 'SUMMARY.INFO',
6485    0x01DB: 'FIND.FILE',
6486    0x01DC: 'ACTIVE.CELL.FONT',
6487    0x01DD: 'ENABLE.TIPWIZARD',
6488    0x01DE: 'VBA.MAKE.ADDIN',
6489    0x01E0: 'INSERTDATATABLE',
6490    0x01E1: 'WORKGROUP.OPTIONS',
6491    0x01E2: 'MAIL.SEND.MAILER',
6492    0x01E5: 'AUTOCORRECT',
6493    0x01E9: 'POST.DOCUMENT',
6494    0x01EB: 'PICKLIST',
6495    0x01ED: 'VIEW.SHOW',
6496    0x01EE: 'VIEW.DEFINE',
6497    0x01EF: 'VIEW.DELETE',
6498    0x01FD: 'SHEET.BACKGROUND',
6499    0x01FE: 'INSERT.MAP.OBJECT',
6500    0x01FF: 'OPTIONS.MENONO',
6501    0x0205: 'MSOCHECKS',
6502    0x0206: 'NORMAL',
6503    0x0207: 'LAYOUT',
6504    0x0208: 'RM.PRINT.AREA',
6505    0x0209: 'CLEAR.PRINT.AREA',
6506    0x020A: 'ADD.PRINT.AREA',
6507    0x020B: 'MOVE.BRK',
6508    0x0221: 'HIDECURR.NOTE',
6509    0x0222: 'HIDEALL.NOTES',
6510    0x0223: 'DELETE.NOTE',
6511    0x0224: 'TRAVERSE.NOTES',
6512    0x0225: 'ACTIVATE.NOTES',
6513    0x026C: 'PROTECT.REVISIONS',
6514    0x026D: 'UNPROTECT.REVISIONS',
6515    0x0287: 'OPTIONS.ME',
6516    0x028D: 'WEB.PUBLISH',
6517    0x029B: 'NEWWEBQUERY',
6518    0x02A1: 'PIVOT.TABLE.CHART',
6519    0x02F1: 'OPTIONS.SAVE',
6520    0x02F3: 'OPTIONS.SPELL',
6521    0x0328: 'HIDEALL.INKANNOTS'
6522};
6523
6524/* [MS-XLS] 2.5.198.17 */
6525var Ftab = {
6526    0x0000: 'COUNT',
6527    0x0001: 'IF',
6528    0x0002: 'ISNA',
6529    0x0003: 'ISERROR',
6530    0x0004: 'SUM',
6531    0x0005: 'AVERAGE',
6532    0x0006: 'MIN',
6533    0x0007: 'MAX',
6534    0x0008: 'ROW',
6535    0x0009: 'COLUMN',
6536    0x000A: 'NA',
6537    0x000B: 'NPV',
6538    0x000C: 'STDEV',
6539    0x000D: 'DOLLAR',
6540    0x000E: 'FIXED',
6541    0x000F: 'SIN',
6542    0x0010: 'COS',
6543    0x0011: 'TAN',
6544    0x0012: 'ATAN',
6545    0x0013: 'PI',
6546    0x0014: 'SQRT',
6547    0x0015: 'EXP',
6548    0x0016: 'LN',
6549    0x0017: 'LOG10',
6550    0x0018: 'ABS',
6551    0x0019: 'INT',
6552    0x001A: 'SIGN',
6553    0x001B: 'ROUND',
6554    0x001C: 'LOOKUP',
6555    0x001D: 'INDEX',
6556    0x001E: 'REPT',
6557    0x001F: 'MID',
6558    0x0020: 'LEN',
6559    0x0021: 'VALUE',
6560    0x0022: 'TRUE',
6561    0x0023: 'FALSE',
6562    0x0024: 'AND',
6563    0x0025: 'OR',
6564    0x0026: 'NOT',
6565    0x0027: 'MOD',
6566    0x0028: 'DCOUNT',
6567    0x0029: 'DSUM',
6568    0x002A: 'DAVERAGE',
6569    0x002B: 'DMIN',
6570    0x002C: 'DMAX',
6571    0x002D: 'DSTDEV',
6572    0x002E: 'VAR',
6573    0x002F: 'DVAR',
6574    0x0030: 'TEXT',
6575    0x0031: 'LINEST',
6576    0x0032: 'TREND',
6577    0x0033: 'LOGEST',
6578    0x0034: 'GROWTH',
6579    0x0035: 'GOTO',
6580    0x0036: 'HALT',
6581    0x0037: 'RETURN',
6582    0x0038: 'PV',
6583    0x0039: 'FV',
6584    0x003A: 'NPER',
6585    0x003B: 'PMT',
6586    0x003C: 'RATE',
6587    0x003D: 'MIRR',
6588    0x003E: 'IRR',
6589    0x003F: 'RAND',
6590    0x0040: 'MATCH',
6591    0x0041: 'DATE',
6592    0x0042: 'TIME',
6593    0x0043: 'DAY',
6594    0x0044: 'MONTH',
6595    0x0045: 'YEAR',
6596    0x0046: 'WEEKDAY',
6597    0x0047: 'HOUR',
6598    0x0048: 'MINUTE',
6599    0x0049: 'SECOND',
6600    0x004A: 'NOW',
6601    0x004B: 'AREAS',
6602    0x004C: 'ROWS',
6603    0x004D: 'COLUMNS',
6604    0x004E: 'OFFSET',
6605    0x004F: 'ABSREF',
6606    0x0050: 'RELREF',
6607    0x0051: 'ARGUMENT',
6608    0x0052: 'SEARCH',
6609    0x0053: 'TRANSPOSE',
6610    0x0054: 'ERROR',
6611    0x0055: 'STEP',
6612    0x0056: 'TYPE',
6613    0x0057: 'ECHO',
6614    0x0058: 'SET.NAME',
6615    0x0059: 'CALLER',
6616    0x005A: 'DEREF',
6617    0x005B: 'WINDOWS',
6618    0x005C: 'SERIES',
6619    0x005D: 'DOCUMENTS',
6620    0x005E: 'ACTIVE.CELL',
6621    0x005F: 'SELECTION',
6622    0x0060: 'RESULT',
6623    0x0061: 'ATAN2',
6624    0x0062: 'ASIN',
6625    0x0063: 'ACOS',
6626    0x0064: 'CHOOSE',
6627    0x0065: 'HLOOKUP',
6628    0x0066: 'VLOOKUP',
6629    0x0067: 'LINKS',
6630    0x0068: 'INPUT',
6631    0x0069: 'ISREF',
6632    0x006A: 'GET.FORMULA',
6633    0x006B: 'GET.NAME',
6634    0x006C: 'SET.VALUE',
6635    0x006D: 'LOG',
6636    0x006E: 'EXEC',
6637    0x006F: 'CHAR',
6638    0x0070: 'LOWER',
6639    0x0071: 'UPPER',
6640    0x0072: 'PROPER',
6641    0x0073: 'LEFT',
6642    0x0074: 'RIGHT',
6643    0x0075: 'EXACT',
6644    0x0076: 'TRIM',
6645    0x0077: 'REPLACE',
6646    0x0078: 'SUBSTITUTE',
6647    0x0079: 'CODE',
6648    0x007A: 'NAMES',
6649    0x007B: 'DIRECTORY',
6650    0x007C: 'FIND',
6651    0x007D: 'CELL',
6652    0x007E: 'ISERR',
6653    0x007F: 'ISTEXT',
6654    0x0080: 'ISNUMBER',
6655    0x0081: 'ISBLANK',
6656    0x0082: 'T',
6657    0x0083: 'N',
6658    0x0084: 'FOPEN',
6659    0x0085: 'FCLOSE',
6660    0x0086: 'FSIZE',
6661    0x0087: 'FREADLN',
6662    0x0088: 'FREAD',
6663    0x0089: 'FWRITELN',
6664    0x008A: 'FWRITE',
6665    0x008B: 'FPOS',
6666    0x008C: 'DATEVALUE',
6667    0x008D: 'TIMEVALUE',
6668    0x008E: 'SLN',
6669    0x008F: 'SYD',
6670    0x0090: 'DDB',
6671    0x0091: 'GET.DEF',
6672    0x0092: 'REFTEXT',
6673    0x0093: 'TEXTREF',
6674    0x0094: 'INDIRECT',
6675    0x0095: 'REGISTER',
6676    0x0096: 'CALL',
6677    0x0097: 'ADD.BAR',
6678    0x0098: 'ADD.MENU',
6679    0x0099: 'ADD.COMMAND',
6680    0x009A: 'ENABLE.COMMAND',
6681    0x009B: 'CHECK.COMMAND',
6682    0x009C: 'RENAME.COMMAND',
6683    0x009D: 'SHOW.BAR',
6684    0x009E: 'DELETE.MENU',
6685    0x009F: 'DELETE.COMMAND',
6686    0x00A0: 'GET.CHART.ITEM',
6687    0x00A1: 'DIALOG.BOX',
6688    0x00A2: 'CLEAN',
6689    0x00A3: 'MDETERM',
6690    0x00A4: 'MINVERSE',
6691    0x00A5: 'MMULT',
6692    0x00A6: 'FILES',
6693    0x00A7: 'IPMT',
6694    0x00A8: 'PPMT',
6695    0x00A9: 'COUNTA',
6696    0x00AA: 'CANCEL.KEY',
6697    0x00AB: 'FOR',
6698    0x00AC: 'WHILE',
6699    0x00AD: 'BREAK',
6700    0x00AE: 'NEXT',
6701    0x00AF: 'INITIATE',
6702    0x00B0: 'REQUEST',
6703    0x00B1: 'POKE',
6704    0x00B2: 'EXECUTE',
6705    0x00B3: 'TERMINATE',
6706    0x00B4: 'RESTART',
6707    0x00B5: 'HELP',
6708    0x00B6: 'GET.BAR',
6709    0x00B7: 'PRODUCT',
6710    0x00B8: 'FACT',
6711    0x00B9: 'GET.CELL',
6712    0x00BA: 'GET.WORKSPACE',
6713    0x00BB: 'GET.WINDOW',
6714    0x00BC: 'GET.DOCUMENT',
6715    0x00BD: 'DPRODUCT',
6716    0x00BE: 'ISNONTEXT',
6717    0x00BF: 'GET.NOTE',
6718    0x00C0: 'NOTE',
6719    0x00C1: 'STDEVP',
6720    0x00C2: 'VARP',
6721    0x00C3: 'DSTDEVP',
6722    0x00C4: 'DVARP',
6723    0x00C5: 'TRUNC',
6724    0x00C6: 'ISLOGICAL',
6725    0x00C7: 'DCOUNTA',
6726    0x00C8: 'DELETE.BAR',
6727    0x00C9: 'UNREGISTER',
6728    0x00CC: 'USDOLLAR',
6729    0x00CD: 'FINDB',
6730    0x00CE: 'SEARCHB',
6731    0x00CF: 'REPLACEB',
6732    0x00D0: 'LEFTB',
6733    0x00D1: 'RIGHTB',
6734    0x00D2: 'MIDB',
6735    0x00D3: 'LENB',
6736    0x00D4: 'ROUNDUP',
6737    0x00D5: 'ROUNDDOWN',
6738    0x00D6: 'ASC',
6739    0x00D7: 'DBCS',
6740    0x00D8: 'RANK',
6741    0x00DB: 'ADDRESS',
6742    0x00DC: 'DAYS360',
6743    0x00DD: 'TODAY',
6744    0x00DE: 'VDB',
6745    0x00DF: 'ELSE',
6746    0x00E0: 'ELSE.IF',
6747    0x00E1: 'END.IF',
6748    0x00E2: 'FOR.CELL',
6749    0x00E3: 'MEDIAN',
6750    0x00E4: 'SUMPRODUCT',
6751    0x00E5: 'SINH',
6752    0x00E6: 'COSH',
6753    0x00E7: 'TANH',
6754    0x00E8: 'ASINH',
6755    0x00E9: 'ACOSH',
6756    0x00EA: 'ATANH',
6757    0x00EB: 'DGET',
6758    0x00EC: 'CREATE.OBJECT',
6759    0x00ED: 'VOLATILE',
6760    0x00EE: 'LAST.ERROR',
6761    0x00EF: 'CUSTOM.UNDO',
6762    0x00F0: 'CUSTOM.REPEAT',
6763    0x00F1: 'FORMULA.CONVERT',
6764    0x00F2: 'GET.LINK.INFO',
6765    0x00F3: 'TEXT.BOX',
6766    0x00F4: 'INFO',
6767    0x00F5: 'GROUP',
6768    0x00F6: 'GET.OBJECT',
6769    0x00F7: 'DB',
6770    0x00F8: 'PAUSE',
6771    0x00FB: 'RESUME',
6772    0x00FC: 'FREQUENCY',
6773    0x00FD: 'ADD.TOOLBAR',
6774    0x00FE: 'DELETE.TOOLBAR',
6775    0x00FF: 'User',
6776    0x0100: 'RESET.TOOLBAR',
6777    0x0101: 'EVALUATE',
6778    0x0102: 'GET.TOOLBAR',
6779    0x0103: 'GET.TOOL',
6780    0x0104: 'SPELLING.CHECK',
6781    0x0105: 'ERROR.TYPE',
6782    0x0106: 'APP.TITLE',
6783    0x0107: 'WINDOW.TITLE',
6784    0x0108: 'SAVE.TOOLBAR',
6785    0x0109: 'ENABLE.TOOL',
6786    0x010A: 'PRESS.TOOL',
6787    0x010B: 'REGISTER.ID',
6788    0x010C: 'GET.WORKBOOK',
6789    0x010D: 'AVEDEV',
6790    0x010E: 'BETADIST',
6791    0x010F: 'GAMMALN',
6792    0x0110: 'BETAINV',
6793    0x0111: 'BINOMDIST',
6794    0x0112: 'CHIDIST',
6795    0x0113: 'CHIINV',
6796    0x0114: 'COMBIN',
6797    0x0115: 'CONFIDENCE',
6798    0x0116: 'CRITBINOM',
6799    0x0117: 'EVEN',
6800    0x0118: 'EXPONDIST',
6801    0x0119: 'FDIST',
6802    0x011A: 'FINV',
6803    0x011B: 'FISHER',
6804    0x011C: 'FISHERINV',
6805    0x011D: 'FLOOR',
6806    0x011E: 'GAMMADIST',
6807    0x011F: 'GAMMAINV',
6808    0x0120: 'CEILING',
6809    0x0121: 'HYPGEOMDIST',
6810    0x0122: 'LOGNORMDIST',
6811    0x0123: 'LOGINV',
6812    0x0124: 'NEGBINOMDIST',
6813    0x0125: 'NORMDIST',
6814    0x0126: 'NORMSDIST',
6815    0x0127: 'NORMINV',
6816    0x0128: 'NORMSINV',
6817    0x0129: 'STANDARDIZE',
6818    0x012A: 'ODD',
6819    0x012B: 'PERMUT',
6820    0x012C: 'POISSON',
6821    0x012D: 'TDIST',
6822    0x012E: 'WEIBULL',
6823    0x012F: 'SUMXMY2',
6824    0x0130: 'SUMX2MY2',
6825    0x0131: 'SUMX2PY2',
6826    0x0132: 'CHITEST',
6827    0x0133: 'CORREL',
6828    0x0134: 'COVAR',
6829    0x0135: 'FORECAST',
6830    0x0136: 'FTEST',
6831    0x0137: 'INTERCEPT',
6832    0x0138: 'PEARSON',
6833    0x0139: 'RSQ',
6834    0x013A: 'STEYX',
6835    0x013B: 'SLOPE',
6836    0x013C: 'TTEST',
6837    0x013D: 'PROB',
6838    0x013E: 'DEVSQ',
6839    0x013F: 'GEOMEAN',
6840    0x0140: 'HARMEAN',
6841    0x0141: 'SUMSQ',
6842    0x0142: 'KURT',
6843    0x0143: 'SKEW',
6844    0x0144: 'ZTEST',
6845    0x0145: 'LARGE',
6846    0x0146: 'SMALL',
6847    0x0147: 'QUARTILE',
6848    0x0148: 'PERCENTILE',
6849    0x0149: 'PERCENTRANK',
6850    0x014A: 'MODE',
6851    0x014B: 'TRIMMEAN',
6852    0x014C: 'TINV',
6853    0x014E: 'MOVIE.COMMAND',
6854    0x014F: 'GET.MOVIE',
6855    0x0150: 'CONCATENATE',
6856    0x0151: 'POWER',
6857    0x0152: 'PIVOT.ADD.DATA',
6858    0x0153: 'GET.PIVOT.TABLE',
6859    0x0154: 'GET.PIVOT.FIELD',
6860    0x0155: 'GET.PIVOT.ITEM',
6861    0x0156: 'RADIANS',
6862    0x0157: 'DEGREES',
6863    0x0158: 'SUBTOTAL',
6864    0x0159: 'SUMIF',
6865    0x015A: 'COUNTIF',
6866    0x015B: 'COUNTBLANK',
6867    0x015C: 'SCENARIO.GET',
6868    0x015D: 'OPTIONS.LISTS.GET',
6869    0x015E: 'ISPMT',
6870    0x015F: 'DATEDIF',
6871    0x0160: 'DATESTRING',
6872    0x0161: 'NUMBERSTRING',
6873    0x0162: 'ROMAN',
6874    0x0163: 'OPEN.DIALOG',
6875    0x0164: 'SAVE.DIALOG',
6876    0x0165: 'VIEW.GET',
6877    0x0166: 'GETPIVOTDATA',
6878    0x0167: 'HYPERLINK',
6879    0x0168: 'PHONETIC',
6880    0x0169: 'AVERAGEA',
6881    0x016A: 'MAXA',
6882    0x016B: 'MINA',
6883    0x016C: 'STDEVPA',
6884    0x016D: 'VARPA',
6885    0x016E: 'STDEVA',
6886    0x016F: 'VARA',
6887    0x0170: 'BAHTTEXT',
6888    0x0171: 'THAIDAYOFWEEK',
6889    0x0172: 'THAIDIGIT',
6890    0x0173: 'THAIMONTHOFYEAR',
6891    0x0174: 'THAINUMSOUND',
6892    0x0175: 'THAINUMSTRING',
6893    0x0176: 'THAISTRINGLENGTH',
6894    0x0177: 'ISTHAIDIGIT',
6895    0x0178: 'ROUNDBAHTDOWN',
6896    0x0179: 'ROUNDBAHTUP',
6897    0x017A: 'THAIYEAR',
6898    0x017B: 'RTD'
6899};
6900var FtabArgc = {
6901    0x0002: 1, /* ISNA */
6902    0x0003: 1, /* ISERROR */
6903    0x000F: 1, /* SIN */
6904    0x0010: 1, /* COS */
6905    0x0011: 1, /* TAN */
6906    0x0012: 1, /* ATAN */
6907    0x0014: 1, /* SQRT */
6908    0x0015: 1, /* EXP */
6909    0x0016: 1, /* LN */
6910    0x0017: 1, /* LOG10 */
6911    0x0018: 1, /* ABS */
6912    0x0019: 1, /* INT */
6913    0x001A: 1, /* SIGN */
6914    0x001B: 2, /* ROUND */
6915    0x001E: 2, /* REPT */
6916    0x001F: 3, /* MID */
6917    0x0020: 1, /* LEN */
6918    0x0021: 1, /* VALUE */
6919    0x0026: 1, /* NOT */
6920    0x0027: 2, /* MOD */
6921    0x0028: 3, /* DCOUNT */
6922    0x0029: 3, /* DSUM */
6923    0x002A: 3, /* DAVERAGE */
6924    0x002B: 3, /* DMIN */
6925    0x002C: 3, /* DMAX */
6926    0x002D: 3, /* DSTDEV */
6927    0x002F: 3, /* DVAR */
6928    0x0030: 2, /* TEXT */
6929    0x0035: 1, /* GOTO */
6930    0x003D: 3, /* MIRR */
6931    0x0041: 3, /* DATE */
6932    0x0042: 3, /* TIME */
6933    0x0043: 1, /* DAY */
6934    0x0044: 1, /* MONTH */
6935    0x0045: 1, /* YEAR */
6936    0x0047: 1, /* HOUR */
6937    0x0048: 1, /* MINUTE */
6938    0x0049: 1, /* SECOND */
6939    0x004B: 1, /* AREAS */
6940    0x004C: 1, /* ROWS */
6941    0x004D: 1, /* COLUMNS */
6942    0x004F: 2, /* ABSREF */
6943    0x0050: 2, /* RELREF */
6944    0x0053: 1, /* TRANSPOSE */
6945    0x0056: 1, /* TYPE */
6946    0x005A: 1, /* DEREF */
6947    0x0061: 2, /* ATAN2 */
6948    0x0062: 1, /* ASIN */
6949    0x0063: 1, /* ACOS */
6950    0x0069: 1, /* ISREF */
6951    0x006F: 1, /* CHAR */
6952    0x0070: 1, /* LOWER */
6953    0x0071: 1, /* UPPER */
6954    0x0072: 1, /* PROPER */
6955    0x0075: 2, /* EXACT */
6956    0x0076: 1, /* TRIM */
6957    0x0077: 4, /* REPLACE */
6958    0x0079: 1, /* CODE */
6959    0x007E: 1, /* ISERR */
6960    0x007F: 1, /* ISTEXT */
6961    0x0080: 1, /* ISNUMBER */
6962    0x0081: 1, /* ISBLANK */
6963    0x0082: 1, /* T */
6964    0x0083: 1, /* N */
6965    0x0085: 1, /* FCLOSE */
6966    0x0086: 1, /* FSIZE */
6967    0x0087: 1, /* FREADLN */
6968    0x0088: 2, /* FREAD */
6969    0x0089: 2, /* FWRITELN */
6970    0x008A: 2, /* FWRITE */
6971    0x008C: 1, /* DATEVALUE */
6972    0x008D: 1, /* TIMEVALUE */
6973    0x008E: 3, /* SLN */
6974    0x008F: 4, /* SYD */
6975    0x00A2: 1, /* CLEAN */
6976    0x00A3: 1, /* MDETERM */
6977    0x00A4: 1, /* MINVERSE */
6978    0x00A5: 2, /* MMULT */
6979    0x00AC: 1, /* WHILE */
6980    0x00AF: 2, /* INITIATE */
6981    0x00B0: 2, /* REQUEST */
6982    0x00B1: 3, /* POKE */
6983    0x00B2: 2, /* EXECUTE */
6984    0x00B3: 1, /* TERMINATE */
6985    0x00B8: 1, /* FACT */
6986    0x00BD: 3, /* DPRODUCT */
6987    0x00BE: 1, /* ISNONTEXT */
6988    0x00C3: 3, /* DSTDEVP */
6989    0x00C4: 3, /* DVARP */
6990    0x00C6: 1, /* ISLOGICAL */
6991    0x00C7: 3, /* DCOUNTA */
6992    0x00C9: 1, /* UNREGISTER */
6993    0x00CF: 4, /* REPLACEB */
6994    0x00D2: 3, /* MIDB */
6995    0x00D3: 1, /* LENB */
6996    0x00D4: 2, /* ROUNDUP */
6997    0x00D5: 2, /* ROUNDDOWN */
6998    0x00D6: 1, /* ASC */
6999    0x00D7: 1, /* DBCS */
7000    0x00E5: 1, /* SINH */
7001    0x00E6: 1, /* COSH */
7002    0x00E7: 1, /* TANH */
7003    0x00E8: 1, /* ASINH */
7004    0x00E9: 1, /* ACOSH */
7005    0x00EA: 1, /* ATANH */
7006    0x00EB: 3, /* DGET */
7007    0x00F4: 1, /* INFO */
7008    0x00FC: 2, /* FREQUENCY */
7009    0x0101: 1, /* EVALUATE */
7010    0x0105: 1, /* ERROR.TYPE */
7011    0x010F: 1, /* GAMMALN */
7012    0x0111: 4, /* BINOMDIST */
7013    0x0112: 2, /* CHIDIST */
7014    0x0113: 2, /* CHIINV */
7015    0x0114: 2, /* COMBIN */
7016    0x0115: 3, /* CONFIDENCE */
7017    0x0116: 3, /* CRITBINOM */
7018    0x0117: 1, /* EVEN */
7019    0x0118: 3, /* EXPONDIST */
7020    0x0119: 3, /* FDIST */
7021    0x011A: 3, /* FINV */
7022    0x011B: 1, /* FISHER */
7023    0x011C: 1, /* FISHERINV */
7024    0x011D: 2, /* FLOOR */
7025    0x011E: 4, /* GAMMADIST */
7026    0x011F: 3, /* GAMMAINV */
7027    0x0120: 2, /* CEILING */
7028    0x0121: 4, /* HYPGEOMDIST */
7029    0x0122: 3, /* LOGNORMDIST */
7030    0x0123: 3, /* LOGINV */
7031    0x0124: 3, /* NEGBINOMDIST */
7032    0x0125: 4, /* NORMDIST */
7033    0x0126: 1, /* NORMSDIST */
7034    0x0127: 3, /* NORMINV */
7035    0x0128: 1, /* NORMSINV */
7036    0x0129: 3, /* STANDARDIZE */
7037    0x012A: 1, /* ODD */
7038    0x012B: 2, /* PERMUT */
7039    0x012C: 3, /* POISSON */
7040    0x012D: 3, /* TDIST */
7041    0x012E: 4, /* WEIBULL */
7042    0x012F: 2, /* SUMXMY2 */
7043    0x0130: 2, /* SUMX2MY2 */
7044    0x0131: 2, /* SUMX2PY2 */
7045    0x0132: 2, /* CHITEST */
7046    0x0133: 2, /* CORREL */
7047    0x0134: 2, /* COVAR */
7048    0x0135: 3, /* FORECAST */
7049    0x0136: 2, /* FTEST */
7050    0x0137: 2, /* INTERCEPT */
7051    0x0138: 2, /* PEARSON */
7052    0x0139: 2, /* RSQ */
7053    0x013A: 2, /* STEYX */
7054    0x013B: 2, /* SLOPE */
7055    0x013C: 4, /* TTEST */
7056    0x0145: 2, /* LARGE */
7057    0x0146: 2, /* SMALL */
7058    0x0147: 2, /* QUARTILE */
7059    0x0148: 2, /* PERCENTILE */
7060    0x014B: 2, /* TRIMMEAN */
7061    0x014C: 2, /* TINV */
7062    0x0151: 2, /* POWER */
7063    0x0156: 1, /* RADIANS */
7064    0x0157: 1, /* DEGREES */
7065    0x015A: 2, /* COUNTIF */
7066    0x015B: 1, /* COUNTBLANK */
7067    0x015E: 4, /* ISPMT */
7068    0x015F: 3, /* DATEDIF */
7069    0x0160: 1, /* DATESTRING */
7070    0x0161: 2, /* NUMBERSTRING */
7071    0x0168: 1, /* PHONETIC */
7072    0x0170: 1, /* BAHTTEXT */
7073    0x0171: 1, /* THAIDAYOFWEEK */
7074    0x0172: 1, /* THAIDIGIT */
7075    0x0173: 1, /* THAIMONTHOFYEAR */
7076    0x0174: 1, /* THAINUMSOUND */
7077    0x0175: 1, /* THAINUMSTRING */
7078    0x0176: 1, /* THAISTRINGLENGTH */
7079    0x0177: 1, /* ISTHAIDIGIT */
7080    0x0178: 1, /* ROUNDBAHTDOWN */
7081    0x0179: 1, /* ROUNDBAHTUP */
7082    0x017A: 1, /* THAIYEAR */
7083    0xFFFF: 0
7084};
7085/* [MS-XLSX] 2.2.3 Functions */
7086var XLSXFutureFunctions = {
7087    "_xlfn.ACOT": "ACOT",
7088    "_xlfn.ACOTH": "ACOTH",
7089    "_xlfn.AGGREGATE": "AGGREGATE",
7090    "_xlfn.ARABIC": "ARABIC",
7091    "_xlfn.AVERAGEIF": "AVERAGEIF",
7092    "_xlfn.AVERAGEIFS": "AVERAGEIFS",
7093    "_xlfn.BASE": "BASE",
7094    "_xlfn.BETA.DIST": "BETA.DIST",
7095    "_xlfn.BETA.INV": "BETA.INV",
7096    "_xlfn.BINOM.DIST": "BINOM.DIST",
7097    "_xlfn.BINOM.DIST.RANGE": "BINOM.DIST.RANGE",
7098    "_xlfn.BINOM.INV": "BINOM.INV",
7099    "_xlfn.BITAND": "BITAND",
7100    "_xlfn.BITLSHIFT": "BITLSHIFT",
7101    "_xlfn.BITOR": "BITOR",
7102    "_xlfn.BITRSHIFT": "BITRSHIFT",
7103    "_xlfn.BITXOR": "BITXOR",
7104    "_xlfn.CEILING.MATH": "CEILING.MATH",
7105    "_xlfn.CEILING.PRECISE": "CEILING.PRECISE",
7106    "_xlfn.CHISQ.DIST": "CHISQ.DIST",
7107    "_xlfn.CHISQ.DIST.RT": "CHISQ.DIST.RT",
7108    "_xlfn.CHISQ.INV": "CHISQ.INV",
7109    "_xlfn.CHISQ.INV.RT": "CHISQ.INV.RT",
7110    "_xlfn.CHISQ.TEST": "CHISQ.TEST",
7111    "_xlfn.COMBINA": "COMBINA",
7112    "_xlfn.CONFIDENCE.NORM": "CONFIDENCE.NORM",
7113    "_xlfn.CONFIDENCE.T": "CONFIDENCE.T",
7114    "_xlfn.COT": "COT",
7115    "_xlfn.COTH": "COTH",
7116    "_xlfn.COUNTIFS": "COUNTIFS",
7117    "_xlfn.COVARIANCE.P": "COVARIANCE.P",
7118    "_xlfn.COVARIANCE.S": "COVARIANCE.S",
7119    "_xlfn.CSC": "CSC",
7120    "_xlfn.CSCH": "CSCH",
7121    "_xlfn.DAYS": "DAYS",
7122    "_xlfn.DECIMAL": "DECIMAL",
7123    "_xlfn.ECMA.CEILING": "ECMA.CEILING",
7124    "_xlfn.ERF.PRECISE": "ERF.PRECISE",
7125    "_xlfn.ERFC.PRECISE": "ERFC.PRECISE",
7126    "_xlfn.EXPON.DIST": "EXPON.DIST",
7127    "_xlfn.F.DIST": "F.DIST",
7128    "_xlfn.F.DIST.RT": "F.DIST.RT",
7129    "_xlfn.F.INV": "F.INV",
7130    "_xlfn.F.INV.RT": "F.INV.RT",
7131    "_xlfn.F.TEST": "F.TEST",
7132    "_xlfn.FILTERXML": "FILTERXML",
7133    "_xlfn.FLOOR.MATH": "FLOOR.MATH",
7134    "_xlfn.FLOOR.PRECISE": "FLOOR.PRECISE",
7135    "_xlfn.FORMULATEXT": "FORMULATEXT",
7136    "_xlfn.GAMMA": "GAMMA",
7137    "_xlfn.GAMMA.DIST": "GAMMA.DIST",
7138    "_xlfn.GAMMA.INV": "GAMMA.INV",
7139    "_xlfn.GAMMALN.PRECISE": "GAMMALN.PRECISE",
7140    "_xlfn.GAUSS": "GAUSS",
7141    "_xlfn.HYPGEOM.DIST": "HYPGEOM.DIST",
7142    "_xlfn.IFNA": "IFNA",
7143    "_xlfn.IFERROR": "IFERROR",
7144    "_xlfn.IMCOSH": "IMCOSH",
7145    "_xlfn.IMCOT": "IMCOT",
7146    "_xlfn.IMCSC": "IMCSC",
7147    "_xlfn.IMCSCH": "IMCSCH",
7148    "_xlfn.IMSEC": "IMSEC",
7149    "_xlfn.IMSECH": "IMSECH",
7150    "_xlfn.IMSINH": "IMSINH",
7151    "_xlfn.IMTAN": "IMTAN",
7152    "_xlfn.ISFORMULA": "ISFORMULA",
7153    "_xlfn.ISO.CEILING": "ISO.CEILING",
7154    "_xlfn.ISOWEEKNUM": "ISOWEEKNUM",
7155    "_xlfn.LOGNORM.DIST": "LOGNORM.DIST",
7156    "_xlfn.LOGNORM.INV": "LOGNORM.INV",
7157    "_xlfn.MODE.MULT": "MODE.MULT",
7158    "_xlfn.MODE.SNGL": "MODE.SNGL",
7159    "_xlfn.MUNIT": "MUNIT",
7160    "_xlfn.NEGBINOM.DIST": "NEGBINOM.DIST",
7161    "_xlfn.NETWORKDAYS.INTL": "NETWORKDAYS.INTL",
7162    "_xlfn.NIGBINOM": "NIGBINOM",
7163    "_xlfn.NORM.DIST": "NORM.DIST",
7164    "_xlfn.NORM.INV": "NORM.INV",
7165    "_xlfn.NORM.S.DIST": "NORM.S.DIST",
7166    "_xlfn.NORM.S.INV": "NORM.S.INV",
7167    "_xlfn.NUMBERVALUE": "NUMBERVALUE",
7168    "_xlfn.PDURATION": "PDURATION",
7169    "_xlfn.PERCENTILE.EXC": "PERCENTILE.EXC",
7170    "_xlfn.PERCENTILE.INC": "PERCENTILE.INC",
7171    "_xlfn.PERCENTRANK.EXC": "PERCENTRANK.EXC",
7172    "_xlfn.PERCENTRANK.INC": "PERCENTRANK.INC",
7173    "_xlfn.PERMUTATIONA": "PERMUTATIONA",
7174    "_xlfn.PHI": "PHI",
7175    "_xlfn.POISSON.DIST": "POISSON.DIST",
7176    "_xlfn.QUARTILE.EXC": "QUARTILE.EXC",
7177    "_xlfn.QUARTILE.INC": "QUARTILE.INC",
7178    "_xlfn.QUERYSTRING": "QUERYSTRING",
7179    "_xlfn.RANK.AVG": "RANK.AVG",
7180    "_xlfn.RANK.EQ": "RANK.EQ",
7181    "_xlfn.RRI": "RRI",
7182    "_xlfn.SEC": "SEC",
7183    "_xlfn.SECH": "SECH",
7184    "_xlfn.SHEET": "SHEET",
7185    "_xlfn.SHEETS": "SHEETS",
7186    "_xlfn.SKEW.P": "SKEW.P",
7187    "_xlfn.STDEV.P": "STDEV.P",
7188    "_xlfn.STDEV.S": "STDEV.S",
7189    "_xlfn.SUMIFS": "SUMIFS",
7190    "_xlfn.T.DIST": "T.DIST",
7191    "_xlfn.T.DIST.2T": "T.DIST.2T",
7192    "_xlfn.T.DIST.RT": "T.DIST.RT",
7193    "_xlfn.T.INV": "T.INV",
7194    "_xlfn.T.INV.2T": "T.INV.2T",
7195    "_xlfn.T.TEST": "T.TEST",
7196    "_xlfn.UNICHAR": "UNICHAR",
7197    "_xlfn.UNICODE": "UNICODE",
7198    "_xlfn.VAR.P": "VAR.P",
7199    "_xlfn.VAR.S": "VAR.S",
7200    "_xlfn.WEBSERVICE": "WEBSERVICE",
7201    "_xlfn.WEIBULL.DIST": "WEIBULL.DIST",
7202    "_xlfn.WORKDAY.INTL": "WORKDAY.INTL",
7203    "_xlfn.XOR": "XOR",
7204    "_xlfn.Z.TEST": "Z.TEST"
7205};
7206
7207var strs = {}; // shared strings
7208var _ssfopts = {}; // spreadsheet formatting options
7209
7210RELS.WS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet";
7211
7212function get_sst_id(sst, str) {
7213    for(var i = 0, len = sst.length; i < len; ++i) if(sst[i].t === str) { sst.Count ++; return i; }
7214    sst[len] = {t:str}; sst.Count ++; sst.Unique ++; return len;
7215}
7216
7217function get_cell_style(styles, cell, opts) {
7218    var z = opts.revssf[cell.z != null ? cell.z : "General"];
7219    for(var i = 0, len = styles.length; i != len; ++i) if(styles[i].numFmtId === z) return i;
7220    styles[len] = {
7221        numFmtId:z,
7222        fontId:0,
7223        fillId:0,
7224        borderId:0,
7225        xfId:0,
7226        applyNumberFormat:1
7227    };
7228    return len;
7229}
7230
7231function safe_format(p, fmtid, fillid, opts) {
7232    try {
7233        if(p.t === 'e') p.w = p.w || BErr[p.v];
7234        else if(fmtid === 0) {
7235            if(p.t === 'n') {
7236                if((p.v|0) === p.v) p.w = SSF._general_int(p.v,_ssfopts);
7237                else p.w = SSF._general_num(p.v,_ssfopts);
7238            }
7239            else if(p.t === 'd') {
7240                var dd = datenum(p.v);
7241                if((dd|0) === dd) p.w = SSF._general_int(dd,_ssfopts);
7242                else p.w = SSF._general_num(dd,_ssfopts);
7243            }
7244            else if(p.v === undefined) return "";
7245            else p.w = SSF._general(p.v,_ssfopts);
7246        }
7247        else if(p.t === 'd') p.w = SSF.format(fmtid,datenum(p.v),_ssfopts);
7248        else p.w = SSF.format(fmtid,p.v,_ssfopts);
7249        if(opts.cellNF) p.z = SSF._table[fmtid];
7250    } catch(e) { if(opts.WTF) throw e; }
7251    if(fillid) try {
7252        p.s = styles.Fills[fillid];
7253        if (p.s.fgColor && p.s.fgColor.theme) {
7254            p.s.fgColor.rgb = rgb_tint(themes.themeElements.clrScheme[p.s.fgColor.theme].rgb, p.s.fgColor.tint || 0);
7255            if(opts.WTF) p.s.fgColor.raw_rgb = themes.themeElements.clrScheme[p.s.fgColor.theme].rgb;
7256        }
7257        if (p.s.bgColor && p.s.bgColor.theme) {
7258            p.s.bgColor.rgb = rgb_tint(themes.themeElements.clrScheme[p.s.bgColor.theme].rgb, p.s.bgColor.tint || 0);
7259            if(opts.WTF) p.s.bgColor.raw_rgb = themes.themeElements.clrScheme[p.s.bgColor.theme].rgb;
7260        }
7261    } catch(e) { if(opts.WTF) throw e; }
7262}
7263function parse_ws_xml_dim(ws, s) {
7264    var d = safe_decode_range(s);
7265    if(d.s.r<=d.e.r && d.s.c<=d.e.c && d.s.r>=0 && d.s.c>=0) ws["!ref"] = encode_range(d);
7266}
7267var mergecregex = /<mergeCell ref="[A-Z0-9:]+"\s*\/>/g;
7268var sheetdataregex = /<(?:\w+:)?sheetData>([^\u2603]*)<\/(?:\w+:)?sheetData>/;
7269var hlinkregex = /<hyperlink[^>]*\/>/g;
7270var dimregex = /"(\w*:\w*)"/;
7271var colregex = /<col[^>]*\/>/g;
7272/* 18.3 Worksheets */
7273function parse_ws_xml(data, opts, rels) {
7274    if(!data) return data;
7275    /* 18.3.1.99 worksheet CT_Worksheet */
7276    var s = {};
7277
7278    /* 18.3.1.35 dimension CT_SheetDimension ? */
7279    var ridx = data.indexOf("<dimension");
7280    if(ridx > 0) {
7281        var ref = data.substr(ridx,50).match(dimregex);
7282        if(ref != null) parse_ws_xml_dim(s, ref[1]);
7283    }
7284
7285    /* 18.3.1.55 mergeCells CT_MergeCells */
7286    var mergecells = [];
7287    if(data.indexOf("</mergeCells>")!==-1) {
7288        var merges = data.match(mergecregex);
7289        for(ridx = 0; ridx != merges.length; ++ridx)
7290            mergecells[ridx] = safe_decode_range(merges[ridx].substr(merges[ridx].indexOf("\"")+1));
7291    }
7292
7293    /* 18.3.1.17 cols CT_Cols */
7294    var columns = [];
7295    if(opts.cellStyles && data.indexOf("</cols>")!==-1) {
7296        /* 18.3.1.13 col CT_Col */
7297        var cols = data.match(colregex);
7298        parse_ws_xml_cols(columns, cols);
7299    }
7300
7301    var refguess = {s: {r:1000000, c:1000000}, e: {r:0, c:0} };
7302
7303    /* 18.3.1.80 sheetData CT_SheetData ? */
7304    var mtch=data.match(sheetdataregex);
7305    if(mtch) parse_ws_xml_data(mtch[1], s, opts, refguess);
7306
7307    /* 18.3.1.48 hyperlinks CT_Hyperlinks */
7308    if(data.indexOf("</hyperlinks>")!==-1) parse_ws_xml_hlinks(s, data.match(hlinkregex), rels);
7309
7310    if(!s["!ref"] && refguess.e.c >= refguess.s.c && refguess.e.r >= refguess.s.r) s["!ref"] = encode_range(refguess);
7311    if(opts.sheetRows > 0 && s["!ref"]) {
7312        var tmpref = safe_decode_range(s["!ref"]);
7313        if(opts.sheetRows < +tmpref.e.r) {
7314            tmpref.e.r = opts.sheetRows - 1;
7315            if(tmpref.e.r > refguess.e.r) tmpref.e.r = refguess.e.r;
7316            if(tmpref.e.r < tmpref.s.r) tmpref.s.r = tmpref.e.r;
7317            if(tmpref.e.c > refguess.e.c) tmpref.e.c = refguess.e.c;
7318            if(tmpref.e.c < tmpref.s.c) tmpref.s.c = tmpref.e.c;
7319            s["!fullref"] = s["!ref"];
7320            s["!ref"] = encode_range(tmpref);
7321        }
7322    }
7323    if(mergecells.length > 0) s["!merges"] = mergecells;
7324    if(columns.length > 0) s["!cols"] = columns;
7325    return s;
7326}
7327
7328function write_ws_xml_merges(merges) {
7329    if(merges.length == 0) return "";
7330    var o = '<mergeCells count="' + merges.length + '">';
7331    for(var i = 0; i != merges.length; ++i) o += '<mergeCell ref="' + encode_range(merges[i]) + '"/>';
7332    return o + '</mergeCells>';
7333}
7334
7335function parse_ws_xml_hlinks(s, data, rels) {
7336    for(var i = 0; i != data.length; ++i) {
7337        var val = parsexmltag(data[i], true);
7338        if(!val.ref) return;
7339        var rel = rels ? rels['!id'][val.id] : null;
7340        if(rel) {
7341            val.Target = rel.Target;
7342            if(val.location) val.Target += "#"+val.location;
7343            val.Rel = rel;
7344        } else {
7345            val.Target = val.location;
7346            rel = {Target: val.location, TargetMode: 'Internal'};
7347            val.Rel = rel;
7348        }
7349        var rng = safe_decode_range(val.ref);
7350        for(var R=rng.s.r;R<=rng.e.r;++R) for(var C=rng.s.c;C<=rng.e.c;++C) {
7351            var addr = encode_cell({c:C,r:R});
7352            if(!s[addr]) s[addr] = {t:"stub",v:undefined};
7353            s[addr].l = val;
7354        }
7355    }
7356}
7357
7358function parse_ws_xml_cols(columns, cols) {
7359    var seencol = false;
7360    for(var coli = 0; coli != cols.length; ++coli) {
7361        var coll = parsexmltag(cols[coli], true);
7362        var colm=parseInt(coll.min, 10)-1, colM=parseInt(coll.max,10)-1;
7363        delete coll.min; delete coll.max;
7364        if(!seencol && coll.width) { seencol = true; find_mdw(+coll.width, coll); }
7365        if(coll.width) {
7366            coll.wpx = width2px(+coll.width);
7367            coll.wch = px2char(coll.wpx);
7368            coll.MDW = MDW;
7369        }
7370        while(colm <= colM) columns[colm++] = coll;
7371    }
7372}
7373
7374function write_ws_xml_cols(ws, cols) {
7375    var o = ["<cols>"], col, width;
7376    for(var i = 0; i != cols.length; ++i) {
7377        if(!(col = cols[i])) continue;
7378        var p = {min:i+1,max:i+1};
7379        /* wch (chars), wpx (pixels) */
7380        width = -1;
7381        if(col.wpx) width = px2char(col.wpx);
7382        else if(col.wch) width = col.wch;
7383        if(width > -1) { p.width = char2width(width); p.customWidth= 1; }
7384        o[o.length] = (writextag('col', null, p));
7385    }
7386    o[o.length] = "</cols>";
7387    return o.join("");
7388}
7389
7390function write_ws_xml_cell(cell, ref, ws, opts, idx, wb) {
7391    if(cell.v === undefined) return "";
7392    var vv = "";
7393    var oldt = cell.t, oldv = cell.v;
7394    switch(cell.t) {
7395        case 'b': vv = cell.v ? "1" : "0"; break;
7396        case 'n': vv = ''+cell.v; break;
7397        case 'e': vv = BErr[cell.v]; break;
7398        case 'd':
7399            if(opts.cellDates) vv = new Date(cell.v).toISOString();
7400            else {
7401                cell.t = 'n';
7402                vv = ''+(cell.v = datenum(cell.v));
7403                if(typeof cell.z === 'undefined') cell.z = SSF._table[14];
7404            }
7405            break;
7406        default: vv = cell.v; break;
7407    }
7408    var v = writetag('v', escapexml(vv)), o = {r:ref};
7409    /* TODO: cell style */
7410    var os = get_cell_style(opts.cellXfs, cell, opts);
7411    if(os !== 0) o.s = os;
7412    switch(cell.t) {
7413        case 'n': break;
7414        case 'd': o.t = "d"; break;
7415        case 'b': o.t = "b"; break;
7416        case 'e': o.t = "e"; break;
7417        default:
7418            if(opts.bookSST) {
7419                v = writetag('v', ''+get_sst_id(opts.Strings, cell.v));
7420                o.t = "s"; break;
7421            }
7422            o.t = "str"; break;
7423    }
7424    if(cell.t != oldt) { cell.t = oldt; cell.v = oldv; }
7425    return writextag('c', v, o);
7426}
7427
7428var parse_ws_xml_data = (function parse_ws_xml_data_factory() {
7429    var cellregex = /<(?:\w+:)?c[ >]/, rowregex = /<\/(?:\w+:)?row>/;
7430    var rregex = /r=["']([^"']*)["']/, isregex = /<is>([\S\s]*?)<\/is>/;
7431    var match_v = matchtag("v"), match_f = matchtag("f");
7432
7433return function parse_ws_xml_data(sdata, s, opts, guess) {
7434    var ri = 0, x = "", cells = [], cref = [], idx = 0, i=0, cc=0, d="", p;
7435    var tag, tagr = 0, tagc = 0;
7436    var sstr;
7437    var fmtid = 0, fillid = 0, do_format = Array.isArray(styles.CellXf), cf;
7438    for(var marr = sdata.split(rowregex), mt = 0, marrlen = marr.length; mt != marrlen; ++mt) {
7439        x = marr[mt].trim();
7440        var xlen = x.length;
7441        if(xlen === 0) continue;
7442
7443        /* 18.3.1.73 row CT_Row */
7444        for(ri = 0; ri < xlen; ++ri) if(x.charCodeAt(ri) === 62) break; ++ri;
7445        tag = parsexmltag(x.substr(0,ri), true);
7446        /* SpreadSheetGear uses implicit r/c */
7447        tagr = typeof tag.r !== 'undefined' ? parseInt(tag.r, 10) : tagr+1; tagc = -1;
7448        if(opts.sheetRows && opts.sheetRows < tagr) continue;
7449        if(guess.s.r > tagr - 1) guess.s.r = tagr - 1;
7450        if(guess.e.r < tagr - 1) guess.e.r = tagr - 1;
7451
7452        /* 18.3.1.4 c CT_Cell */
7453        cells = x.substr(ri).split(cellregex);
7454        for(ri = typeof tag.r === 'undefined' ? 0 : 1; ri != cells.length; ++ri) {
7455            x = cells[ri].trim();
7456            if(x.length === 0) continue;
7457            cref = x.match(rregex); idx = ri; i=0; cc=0;
7458            x = "<c " + (x.substr(0,1)=="<"?">":"") + x;
7459            if(cref !== null && cref.length === 2) {
7460                idx = 0; d=cref[1];
7461                for(i=0; i != d.length; ++i) {
7462                    if((cc=d.charCodeAt(i)-64) < 1 || cc > 26) break;
7463                    idx = 26*idx + cc;
7464                }
7465                --idx;
7466                tagc = idx;
7467            } else ++tagc;
7468            for(i = 0; i != x.length; ++i) if(x.charCodeAt(i) === 62) break; ++i;
7469            tag = parsexmltag(x.substr(0,i), true);
7470            if(!tag.r) tag.r = utils.encode_cell({r:tagr-1, c:tagc});
7471            d = x.substr(i);
7472            p = {t:""};
7473
7474            if((cref=d.match(match_v))!== null && cref[1] !== '') p.v=unescapexml(cref[1]);
7475            if(opts.cellFormula && (cref=d.match(match_f))!== null) p.f=unescapexml(cref[1]);
7476
7477            /* SCHEMA IS ACTUALLY INCORRECT HERE.  IF A CELL HAS NO T, EMIT "" */
7478            if(tag.t === undefined && p.v === undefined) {
7479                if(!opts.sheetStubs) continue;
7480                p.t = "stub";
7481            }
7482            else p.t = tag.t || "n";
7483            if(guess.s.c > idx) guess.s.c = idx;
7484            if(guess.e.c < idx) guess.e.c = idx;
7485            /* 18.18.11 t ST_CellType */
7486            switch(p.t) {
7487                case 'n': p.v = parseFloat(p.v); break;
7488                case 's':
7489                    sstr = strs[parseInt(p.v, 10)];
7490                    p.v = sstr.t;
7491                    p.r = sstr.r;
7492                    if(opts.cellHTML) p.h = sstr.h;
7493                    break;
7494                case 'str':
7495                    p.t = "s";
7496                    p.v = (p.v!=null) ? utf8read(p.v) : '';
7497                    if(opts.cellHTML) p.h = p.v;
7498                    break;
7499                case 'inlineStr':
7500                    cref = d.match(isregex);
7501                    p.t = 's';
7502                    if(cref !== null) { sstr = parse_si(cref[1]); p.v = sstr.t; } else p.v = "";
7503                    break; // inline string
7504                case 'b': p.v = parsexmlbool(p.v); break;
7505                case 'd':
7506                    if(!opts.cellDates) { p.v = datenum(p.v); p.t = 'n'; }
7507                    break;
7508                /* error string in .v, number in .v */
7509                case 'e': p.w = p.v; p.v = RBErr[p.v]; break;
7510            }
7511            /* formatting */
7512            fmtid = fillid = 0;
7513            if(do_format && tag.s !== undefined) {
7514                cf = styles.CellXf[tag.s];
7515                if(cf != null) {
7516                    if(cf.numFmtId != null) fmtid = cf.numFmtId;
7517                    if(opts.cellStyles && cf.fillId != null) fillid = cf.fillId;
7518                }
7519            }
7520            safe_format(p, fmtid, fillid, opts);
7521            s[tag.r] = p;
7522        }
7523    }
7524}; })();
7525
7526function write_ws_xml_data(ws, opts, idx, wb) {
7527    var o = [], r = [], range = safe_decode_range(ws['!ref']), cell, ref, rr = "", cols = [], R, C;
7528    for(C = range.s.c; C <= range.e.c; ++C) cols[C] = encode_col(C);
7529    for(R = range.s.r; R <= range.e.r; ++R) {
7530        r = [];
7531        rr = encode_row(R);
7532        for(C = range.s.c; C <= range.e.c; ++C) {
7533            ref = cols[C] + rr;
7534            if(ws[ref] === undefined) continue;
7535            if((cell = write_ws_xml_cell(ws[ref], ref, ws, opts, idx, wb)) != null) r.push(cell);
7536        }
7537        if(r.length > 0) o[o.length] = (writextag('row', r.join(""), {r:rr}));
7538    }
7539    return o.join("");
7540}
7541
7542var WS_XML_ROOT = writextag('worksheet', null, {
7543    'xmlns': XMLNS.main[0],
7544    'xmlns:r': XMLNS.r
7545});
7546
7547function write_ws_xml(idx, opts, wb) {
7548    var o = [XML_HEADER, WS_XML_ROOT];
7549    var s = wb.SheetNames[idx], sidx = 0, rdata = "";
7550    var ws = wb.Sheets[s];
7551    if(ws === undefined) ws = {};
7552    var ref = ws['!ref']; if(ref === undefined) ref = 'A1';
7553    o[o.length] = (writextag('dimension', null, {'ref': ref}));
7554
7555    if(ws['!cols'] !== undefined && ws['!cols'].length > 0) o[o.length] = (write_ws_xml_cols(ws, ws['!cols']));
7556    o[sidx = o.length] = '<sheetData/>';
7557    if(ws['!ref'] !== undefined) {
7558        rdata = write_ws_xml_data(ws, opts, idx, wb);
7559        if(rdata.length > 0) o[o.length] = (rdata);
7560    }
7561    if(o.length>sidx+1) { o[o.length] = ('</sheetData>'); o[sidx]=o[sidx].replace("/>",">"); }
7562
7563    if(ws['!merges'] !== undefined && ws['!merges'].length > 0) o[o.length] = (write_ws_xml_merges(ws['!merges']));
7564
7565    if(o.length>2) { o[o.length] = ('</worksheet>'); o[1]=o[1].replace("/>",">"); }
7566    return o.join("");
7567}
7568
7569/* [MS-XLSB] 2.4.718 BrtRowHdr */
7570function parse_BrtRowHdr(data, length) {
7571    var z = [];
7572    z.r = data.read_shift(4);
7573    data.l += length-4;
7574    return z;
7575}
7576
7577/* [MS-XLSB] 2.4.812 BrtWsDim */
7578var parse_BrtWsDim = parse_UncheckedRfX;
7579var write_BrtWsDim = write_UncheckedRfX;
7580
7581/* [MS-XLSB] 2.4.815 BrtWsProp */
7582function parse_BrtWsProp(data, length) {
7583    var z = {};
7584    /* TODO: pull flags */
7585    data.l += 19;
7586    z.name = parse_XLSBCodeName(data, length - 19);
7587    return z;
7588}
7589
7590/* [MS-XLSB] 2.4.303 BrtCellBlank */
7591function parse_BrtCellBlank(data, length) {
7592    var cell = parse_XLSBCell(data);
7593    return [cell];
7594}
7595function write_BrtCellBlank(cell, val, o) {
7596    if(o == null) o = new_buf(8);
7597    return write_XLSBCell(val, o);
7598}
7599
7600
7601/* [MS-XLSB] 2.4.304 BrtCellBool */
7602function parse_BrtCellBool(data, length) {
7603    var cell = parse_XLSBCell(data);
7604    var fBool = data.read_shift(1);
7605    return [cell, fBool, 'b'];
7606}
7607
7608/* [MS-XLSB] 2.4.305 BrtCellError */
7609function parse_BrtCellError(data, length) {
7610    var cell = parse_XLSBCell(data);
7611    var fBool = data.read_shift(1);
7612    return [cell, fBool, 'e'];
7613}
7614
7615/* [MS-XLSB] 2.4.308 BrtCellIsst */
7616function parse_BrtCellIsst(data, length) {
7617    var cell = parse_XLSBCell(data);
7618    var isst = data.read_shift(4);
7619    return [cell, isst, 's'];
7620}
7621
7622/* [MS-XLSB] 2.4.310 BrtCellReal */
7623function parse_BrtCellReal(data, length) {
7624    var cell = parse_XLSBCell(data);
7625    var value = parse_Xnum(data);
7626    return [cell, value, 'n'];
7627}
7628
7629/* [MS-XLSB] 2.4.311 BrtCellRk */
7630function parse_BrtCellRk(data, length) {
7631    var cell = parse_XLSBCell(data);
7632    var value = parse_RkNumber(data);
7633    return [cell, value, 'n'];
7634}
7635
7636/* [MS-XLSB] 2.4.314 BrtCellSt */
7637function parse_BrtCellSt(data, length) {
7638    var cell = parse_XLSBCell(data);
7639    var value = parse_XLWideString(data);
7640    return [cell, value, 'str'];
7641}
7642
7643/* [MS-XLSB] 2.4.647 BrtFmlaBool */
7644function parse_BrtFmlaBool(data, length, opts) {
7645    var cell = parse_XLSBCell(data);
7646    var value = data.read_shift(1);
7647    var o = [cell, value, 'b'];
7648    if(opts.cellFormula) {
7649        var formula = parse_XLSBCellParsedFormula(data, length-9);
7650        o[3] = ""; /* TODO */
7651    }
7652    else data.l += length-9;
7653    return o;
7654}
7655
7656/* [MS-XLSB] 2.4.648 BrtFmlaError */
7657function parse_BrtFmlaError(data, length, opts) {
7658    var cell = parse_XLSBCell(data);
7659    var value = data.read_shift(1);
7660    var o = [cell, value, 'e'];
7661    if(opts.cellFormula) {
7662        var formula = parse_XLSBCellParsedFormula(data, length-9);
7663        o[3] = ""; /* TODO */
7664    }
7665    else data.l += length-9;
7666    return o;
7667}
7668
7669/* [MS-XLSB] 2.4.649 BrtFmlaNum */
7670function parse_BrtFmlaNum(data, length, opts) {
7671    var cell = parse_XLSBCell(data);
7672    var value = parse_Xnum(data);
7673    var o = [cell, value, 'n'];
7674    if(opts.cellFormula) {
7675        var formula = parse_XLSBCellParsedFormula(data, length - 16);
7676        o[3] = ""; /* TODO */
7677    }
7678    else data.l += length-16;
7679    return o;
7680}
7681
7682/* [MS-XLSB] 2.4.650 BrtFmlaString */
7683function parse_BrtFmlaString(data, length, opts) {
7684    var start = data.l;
7685    var cell = parse_XLSBCell(data);
7686    var value = parse_XLWideString(data);
7687    var o = [cell, value, 'str'];
7688    if(opts.cellFormula) {
7689        var formula = parse_XLSBCellParsedFormula(data, start + length - data.l);
7690    }
7691    else data.l = start + length;
7692    return o;
7693}
7694
7695/* [MS-XLSB] 2.4.676 BrtMergeCell */
7696var parse_BrtMergeCell = parse_UncheckedRfX;
7697
7698/* [MS-XLSB] 2.4.656 BrtHLink */
7699function parse_BrtHLink(data, length, opts) {
7700    var end = data.l + length;
7701    var rfx = parse_UncheckedRfX(data, 16);
7702    var relId = parse_XLNullableWideString(data);
7703    var loc = parse_XLWideString(data);
7704    var tooltip = parse_XLWideString(data);
7705    var display = parse_XLWideString(data);
7706    data.l = end;
7707    return {rfx:rfx, relId:relId, loc:loc, tooltip:tooltip, display:display};
7708}
7709
7710/* [MS-XLSB] 2.1.7.61 Worksheet */
7711function parse_ws_bin(data, opts, rels) {
7712    if(!data) return data;
7713    if(!rels) rels = {'!id':{}};
7714    var s = {};
7715
7716    var ref;
7717    var refguess = {s: {r:1000000, c:1000000}, e: {r:0, c:0} };
7718
7719    var pass = false, end = false;
7720    var row, p, cf, R, C, addr, sstr, rr;
7721    var mergecells = [];
7722    recordhopper(data, function ws_parse(val, R) {
7723        if(end) return;
7724        switch(R.n) {
7725            case 'BrtWsDim': ref = val; break;
7726            case 'BrtRowHdr':
7727                row = val;
7728                if(opts.sheetRows && opts.sheetRows <= row.r) end=true;
7729                rr = encode_row(row.r);
7730                break;
7731
7732            case 'BrtFmlaBool':
7733            case 'BrtFmlaError':
7734            case 'BrtFmlaNum':
7735            case 'BrtFmlaString':
7736            case 'BrtCellBool':
7737            case 'BrtCellError':
7738            case 'BrtCellIsst':
7739            case 'BrtCellReal':
7740            case 'BrtCellRk':
7741            case 'BrtCellSt':
7742                p = {t:val[2]};
7743                switch(val[2]) {
7744                    case 'n': p.v = val[1]; break;
7745                    case 's': sstr = strs[val[1]]; p.v = sstr.t; p.r = sstr.r; break;
7746                    case 'b': p.v = val[1] ? true : false; break;
7747                    case 'e': p.v = val[1]; p.w = BErr[p.v]; break;
7748                    case 'str': p.t = 's'; p.v = utf8read(val[1]); break;
7749                }
7750                if(opts.cellFormula && val.length > 3) p.f = val[3];
7751                if((cf = styles.CellXf[val[0].iStyleRef])) safe_format(p,cf.ifmt,null,opts);
7752                s[encode_col(C=val[0].c) + rr] = p;
7753                if(refguess.s.r > row.r) refguess.s.r = row.r;
7754                if(refguess.s.c > C) refguess.s.c = C;
7755                if(refguess.e.r < row.r) refguess.e.r = row.r;
7756                if(refguess.e.c < C) refguess.e.c = C;
7757                break;
7758
7759            case 'BrtCellBlank': if(!opts.sheetStubs) break;
7760                p = {t:'s',v:undefined};
7761                s[encode_col(C=val[0].c) + rr] = p;
7762                if(refguess.s.r > row.r) refguess.s.r = row.r;
7763                if(refguess.s.c > C) refguess.s.c = C;
7764                if(refguess.e.r < row.r) refguess.e.r = row.r;
7765                if(refguess.e.c < C) refguess.e.c = C;
7766                break;
7767
7768            /* Merge Cells */
7769            case 'BrtBeginMergeCells': break;
7770            case 'BrtEndMergeCells': break;
7771            case 'BrtMergeCell': mergecells.push(val); break;
7772
7773            case 'BrtHLink':
7774                var rel = rels['!id'][val.relId];
7775                if(rel) {
7776                    val.Target = rel.Target;
7777                    if(val.loc) val.Target += "#"+val.loc;
7778                    val.Rel = rel;
7779                }
7780                for(R=val.rfx.s.r;R<=val.rfx.e.r;++R) for(C=val.rfx.s.c;C<=val.rfx.e.c;++C) {
7781                    addr = encode_cell({c:C,r:R});
7782                    if(!s[addr]) s[addr] = {t:'s',v:undefined};
7783                    s[addr].l = val;
7784                }
7785                break;
7786
7787            case 'BrtArrFmla': break; // TODO
7788            case 'BrtShrFmla': break; // TODO
7789            case 'BrtBeginSheet': break;
7790            case 'BrtWsProp': break; // TODO
7791            case 'BrtSheetCalcProp': break; // TODO
7792            case 'BrtBeginWsViews': break; // TODO
7793            case 'BrtBeginWsView': break; // TODO
7794            case 'BrtPane': break; // TODO
7795            case 'BrtSel': break; // TODO
7796            case 'BrtEndWsView': break; // TODO
7797            case 'BrtEndWsViews': break; // TODO
7798            case 'BrtACBegin': break; // TODO
7799            case 'BrtRwDescent': break; // TODO
7800            case 'BrtACEnd': break; // TODO
7801            case 'BrtWsFmtInfoEx14': break; // TODO
7802            case 'BrtWsFmtInfo': break; // TODO
7803            case 'BrtBeginColInfos': break; // TODO
7804            case 'BrtColInfo': break; // TODO
7805            case 'BrtEndColInfos': break; // TODO
7806            case 'BrtBeginSheetData': break; // TODO
7807            case 'BrtEndSheetData': break; // TODO
7808            case 'BrtSheetProtection': break; // TODO
7809            case 'BrtPrintOptions': break; // TODO
7810            case 'BrtMargins': break; // TODO
7811            case 'BrtPageSetup': break; // TODO
7812            case 'BrtFRTBegin': pass = true; break;
7813            case 'BrtFRTEnd': pass = false; break;
7814            case 'BrtEndSheet': break; // TODO
7815            case 'BrtDrawing': break; // TODO
7816            case 'BrtLegacyDrawing': break; // TODO
7817            case 'BrtLegacyDrawingHF': break; // TODO
7818            case 'BrtPhoneticInfo': break; // TODO
7819            case 'BrtBeginHeaderFooter': break; // TODO
7820            case 'BrtEndHeaderFooter': break; // TODO
7821            case 'BrtBrk': break; // TODO
7822            case 'BrtBeginRwBrk': break; // TODO
7823            case 'BrtEndRwBrk': break; // TODO
7824            case 'BrtBeginColBrk': break; // TODO
7825            case 'BrtEndColBrk': break; // TODO
7826            case 'BrtBeginUserShViews': break; // TODO
7827            case 'BrtBeginUserShView': break; // TODO
7828            case 'BrtEndUserShView': break; // TODO
7829            case 'BrtEndUserShViews': break; // TODO
7830            case 'BrtBkHim': break; // TODO
7831            case 'BrtBeginOleObjects': break; // TODO
7832            case 'BrtOleObject': break; // TODO
7833            case 'BrtEndOleObjects': break; // TODO
7834            case 'BrtBeginListParts': break; // TODO
7835            case 'BrtListPart': break; // TODO
7836            case 'BrtEndListParts': break; // TODO
7837            case 'BrtBeginSortState': break; // TODO
7838            case 'BrtBeginSortCond': break; // TODO
7839            case 'BrtEndSortCond': break; // TODO
7840            case 'BrtEndSortState': break; // TODO
7841            case 'BrtBeginConditionalFormatting': break; // TODO
7842            case 'BrtEndConditionalFormatting': break; // TODO
7843            case 'BrtBeginCFRule': break; // TODO
7844            case 'BrtEndCFRule': break; // TODO
7845            case 'BrtBeginDVals': break; // TODO
7846            case 'BrtDVal': break; // TODO
7847            case 'BrtEndDVals': break; // TODO
7848            case 'BrtRangeProtection': break; // TODO
7849            case 'BrtBeginDCon': break; // TODO
7850            case 'BrtEndDCon': break; // TODO
7851            case 'BrtBeginDRefs': break;
7852            case 'BrtDRef': break;
7853            case 'BrtEndDRefs': break;
7854
7855            /* ActiveX */
7856            case 'BrtBeginActiveXControls': break;
7857            case 'BrtActiveX': break;
7858            case 'BrtEndActiveXControls': break;
7859
7860            /* AutoFilter */
7861            case 'BrtBeginAFilter': break;
7862            case 'BrtEndAFilter': break;
7863            case 'BrtBeginFilterColumn': break;
7864            case 'BrtBeginFilters': break;
7865            case 'BrtFilter': break;
7866            case 'BrtEndFilters': break;
7867            case 'BrtEndFilterColumn': break;
7868            case 'BrtDynamicFilter': break;
7869            case 'BrtTop10Filter': break;
7870            case 'BrtBeginCustomFilters': break;
7871            case 'BrtCustomFilter': break;
7872            case 'BrtEndCustomFilters': break;
7873
7874            /* Smart Tags */
7875            case 'BrtBeginSmartTags': break;
7876            case 'BrtBeginCellSmartTags': break;
7877            case 'BrtBeginCellSmartTag': break;
7878            case 'BrtCellSmartTagProperty': break;
7879            case 'BrtEndCellSmartTag': break;
7880            case 'BrtEndCellSmartTags': break;
7881            case 'BrtEndSmartTags': break;
7882
7883            /* Cell Watch */
7884            case 'BrtBeginCellWatches': break;
7885            case 'BrtCellWatch': break;
7886            case 'BrtEndCellWatches': break;
7887
7888            /* Table */
7889            case 'BrtTable': break;
7890
7891            /* Ignore Cell Errors */
7892            case 'BrtBeginCellIgnoreECs': break;
7893            case 'BrtCellIgnoreEC': break;
7894            case 'BrtEndCellIgnoreECs': break;
7895
7896            default: if(!pass || opts.WTF) throw new Error("Unexpected record " + R.n);
7897        }
7898    }, opts);
7899    if(!s["!ref"] && (refguess.s.r < 1000000 || ref.e.r > 0 || ref.e.c > 0 || ref.s.r > 0 || ref.s.c > 0)) s["!ref"] = encode_range(ref);
7900    if(opts.sheetRows && s["!ref"]) {
7901        var tmpref = safe_decode_range(s["!ref"]);
7902        if(opts.sheetRows < +tmpref.e.r) {
7903            tmpref.e.r = opts.sheetRows - 1;
7904            if(tmpref.e.r > refguess.e.r) tmpref.e.r = refguess.e.r;
7905            if(tmpref.e.r < tmpref.s.r) tmpref.s.r = tmpref.e.r;
7906            if(tmpref.e.c > refguess.e.c) tmpref.e.c = refguess.e.c;
7907            if(tmpref.e.c < tmpref.s.c) tmpref.s.c = tmpref.e.c;
7908            s["!fullref"] = s["!ref"];
7909            s["!ref"] = encode_range(tmpref);
7910        }
7911    }
7912    if(mergecells.length > 0) s["!merges"] = mergecells;
7913    return s;
7914}
7915
7916/* TODO: something useful -- this is a stub */
7917function write_ws_bin_cell(ba, cell, R, C, opts) {
7918    if(cell.v === undefined) return "";
7919    var vv = "";
7920    switch(cell.t) {
7921        case 'b': vv = cell.v ? "1" : "0"; break;
7922        case 'n': case 'e': vv = ''+cell.v; break;
7923        default: vv = cell.v; break;
7924    }
7925    var o = {r:R, c:C};
7926    /* TODO: cell style */
7927    o.s = get_cell_style(opts.cellXfs, cell, opts);
7928    switch(cell.t) {
7929        case 's': case 'str':
7930            if(opts.bookSST) {
7931                vv = get_sst_id(opts.Strings, cell.v);
7932                o.t = "s"; break;
7933            }
7934            o.t = "str"; break;
7935        case 'n': break;
7936        case 'b': o.t = "b"; break;
7937        case 'e': o.t = "e"; break;
7938    }
7939    write_record(ba, "BrtCellBlank", write_BrtCellBlank(cell, o));
7940}
7941
7942function write_CELLTABLE(ba, ws, idx, opts, wb) {
7943    var range = safe_decode_range(ws['!ref'] || "A1"), ref, rr = "", cols = [];
7944    write_record(ba, 'BrtBeginSheetData');
7945    for(var R = range.s.r; R <= range.e.r; ++R) {
7946        rr = encode_row(R);
7947        /* [ACCELLTABLE] */
7948        /* BrtRowHdr */
7949        for(var C = range.s.c; C <= range.e.c; ++C) {
7950            /* *16384CELL */
7951            if(R === range.s.r) cols[C] = encode_col(C);
7952            ref = cols[C] + rr;
7953            if(!ws[ref]) continue;
7954            /* write cell */
7955            write_ws_bin_cell(ba, ws[ref], R, C, opts);
7956        }
7957    }
7958    write_record(ba, 'BrtEndSheetData');
7959}
7960
7961function write_ws_bin(idx, opts, wb) {
7962    var ba = buf_array();
7963    var s = wb.SheetNames[idx], ws = wb.Sheets[s] || {};
7964    var r = safe_decode_range(ws['!ref'] || "A1");
7965    write_record(ba, "BrtBeginSheet");
7966    /* [BrtWsProp] */
7967    write_record(ba, "BrtWsDim", write_BrtWsDim(r));
7968    /* [WSVIEWS2] */
7969    /* [WSFMTINFO] */
7970    /* *COLINFOS */
7971    write_CELLTABLE(ba, ws, idx, opts, wb);
7972    /* [BrtSheetCalcProp] */
7973    /* [[BrtSheetProtectionIso] BrtSheetProtection] */
7974    /* *([BrtRangeProtectionIso] BrtRangeProtection) */
7975    /* [SCENMAN] */
7976    /* [AUTOFILTER] */
7977    /* [SORTSTATE] */
7978    /* [DCON] */
7979    /* [USERSHVIEWS] */
7980    /* [MERGECELLS] */
7981    /* [BrtPhoneticInfo] */
7982    /* *CONDITIONALFORMATTING */
7983    /* [DVALS] */
7984    /* *BrtHLink */
7985    /* [BrtPrintOptions] */
7986    /* [BrtMargins] */
7987    /* [BrtPageSetup] */
7988    /* [HEADERFOOTER] */
7989    /* [RWBRK] */
7990    /* [COLBRK] */
7991    /* *BrtBigName */
7992    /* [CELLWATCHES] */
7993    /* [IGNOREECS] */
7994    /* [SMARTTAGS] */
7995    /* [BrtDrawing] */
7996    /* [BrtLegacyDrawing] */
7997    /* [BrtLegacyDrawingHF] */
7998    /* [BrtBkHim] */
7999    /* [OLEOBJECTS] */
8000    /* [ACTIVEXCONTROLS] */
8001    /* [WEBPUBITEMS] */
8002    /* [LISTPARTS] */
8003    /* FRTWORKSHEET */
8004    write_record(ba, "BrtEndSheet");
8005    return ba.end();
8006}
8007/* 18.2.28 (CT_WorkbookProtection) Defaults */
8008var WBPropsDef = [
8009    ['allowRefreshQuery', '0'],
8010    ['autoCompressPictures', '1'],
8011    ['backupFile', '0'],
8012    ['checkCompatibility', '0'],
8013    ['codeName', ''],
8014    ['date1904', '0'],
8015    ['dateCompatibility', '1'],
8016    //['defaultThemeVersion', '0'],
8017    ['filterPrivacy', '0'],
8018    ['hidePivotFieldList', '0'],
8019    ['promptedSolutions', '0'],
8020    ['publishItems', '0'],
8021    ['refreshAllConnections', false],
8022    ['saveExternalLinkValues', '1'],
8023    ['showBorderUnselectedTables', '1'],
8024    ['showInkAnnotation', '1'],
8025    ['showObjects', 'all'],
8026    ['showPivotChartFilter', '0']
8027    //['updateLinks', 'userSet']
8028];
8029
8030/* 18.2.30 (CT_BookView) Defaults */
8031var WBViewDef = [
8032    ['activeTab', '0'],
8033    ['autoFilterDateGrouping', '1'],
8034    ['firstSheet', '0'],
8035    ['minimized', '0'],
8036    ['showHorizontalScroll', '1'],
8037    ['showSheetTabs', '1'],
8038    ['showVerticalScroll', '1'],
8039    ['tabRatio', '600'],
8040    ['visibility', 'visible']
8041    //window{Height,Width}, {x,y}Window
8042];
8043
8044/* 18.2.19 (CT_Sheet) Defaults */
8045var SheetDef = [
8046    ['state', 'visible']
8047];
8048
8049/* 18.2.2  (CT_CalcPr) Defaults */
8050var CalcPrDef = [
8051    ['calcCompleted', 'true'],
8052    ['calcMode', 'auto'],
8053    ['calcOnSave', 'true'],
8054    ['concurrentCalc', 'true'],
8055    ['fullCalcOnLoad', 'false'],
8056    ['fullPrecision', 'true'],
8057    ['iterate', 'false'],
8058    ['iterateCount', '100'],
8059    ['iterateDelta', '0.001'],
8060    ['refMode', 'A1']
8061];
8062
8063/* 18.2.3 (CT_CustomWorkbookView) Defaults */
8064var CustomWBViewDef = [
8065    ['autoUpdate', 'false'],
8066    ['changesSavedWin', 'false'],
8067    ['includeHiddenRowCol', 'true'],
8068    ['includePrintSettings', 'true'],
8069    ['maximized', 'false'],
8070    ['minimized', 'false'],
8071    ['onlySync', 'false'],
8072    ['personalView', 'false'],
8073    ['showComments', 'commIndicator'],
8074    ['showFormulaBar', 'true'],
8075    ['showHorizontalScroll', 'true'],
8076    ['showObjects', 'all'],
8077    ['showSheetTabs', 'true'],
8078    ['showStatusbar', 'true'],
8079    ['showVerticalScroll', 'true'],
8080    ['tabRatio', '600'],
8081    ['xWindow', '0'],
8082    ['yWindow', '0']
8083];
8084
8085function push_defaults_array(target, defaults) {
8086    for(var j = 0; j != target.length; ++j) { var w = target[j];
8087        for(var i=0; i != defaults.length; ++i) { var z = defaults[i];
8088            if(w[z[0]] == null) w[z[0]] = z[1];
8089        }
8090    }
8091}
8092function push_defaults(target, defaults) {
8093    for(var i = 0; i != defaults.length; ++i) { var z = defaults[i];
8094        if(target[z[0]] == null) target[z[0]] = z[1];
8095    }
8096}
8097
8098function parse_wb_defaults(wb) {
8099    push_defaults(wb.WBProps, WBPropsDef);
8100    push_defaults(wb.CalcPr, CalcPrDef);
8101
8102    push_defaults_array(wb.WBView, WBViewDef);
8103    push_defaults_array(wb.Sheets, SheetDef);
8104
8105    _ssfopts.date1904 = parsexmlbool(wb.WBProps.date1904, 'date1904');
8106}
8107/* 18.2 Workbook */
8108var wbnsregex = /<\w+:workbook/;
8109function parse_wb_xml(data, opts) {
8110    var wb = { AppVersion:{}, WBProps:{}, WBView:[], Sheets:[], CalcPr:{}, xmlns: "" };
8111    var pass = false, xmlns = "xmlns";
8112    data.match(tagregex).forEach(function xml_wb(x) {
8113        var y = parsexmltag(x);
8114        switch(strip_ns(y[0])) {
8115            case '<?xml': break;
8116
8117            /* 18.2.27 workbook CT_Workbook 1 */
8118            case '<workbook':
8119                if(x.match(wbnsregex)) xmlns = "xmlns" + x.match(/<(\w+):/)[1];
8120                wb.xmlns = y[xmlns];
8121                break;
8122            case '</workbook>': break;
8123
8124            /* 18.2.13 fileVersion CT_FileVersion ? */
8125            case '<fileVersion': delete y[0]; wb.AppVersion = y; break;
8126            case '<fileVersion/>': break;
8127
8128            /* 18.2.12 fileSharing CT_FileSharing ? */
8129            case '<fileSharing': case '<fileSharing/>': break;
8130
8131            /* 18.2.28 workbookPr CT_WorkbookPr ? */
8132            case '<workbookPr': delete y[0]; wb.WBProps = y; break;
8133            case '<workbookPr/>': delete y[0]; wb.WBProps = y; break;
8134
8135            /* 18.2.29 workbookProtection CT_WorkbookProtection ? */
8136            case '<workbookProtection': break;
8137            case '<workbookProtection/>': break;
8138
8139            /* 18.2.1  bookViews CT_BookViews ? */
8140            case '<bookViews>': case '</bookViews>': break;
8141            /* 18.2.30   workbookView CT_BookView + */
8142            case '<workbookView': delete y[0]; wb.WBView.push(y); break;
8143
8144            /* 18.2.20 sheets CT_Sheets 1 */
8145            case '<sheets>': case '</sheets>': break; // aggregate sheet
8146            /* 18.2.19   sheet CT_Sheet + */
8147            case '<sheet': delete y[0]; y.name = utf8read(y.name); wb.Sheets.push(y); break;
8148
8149            /* 18.2.15 functionGroups CT_FunctionGroups ? */
8150            case '<functionGroups': case '<functionGroups/>': break;
8151            /* 18.2.14   functionGroup CT_FunctionGroup + */
8152            case '<functionGroup': break;
8153
8154            /* 18.2.9  externalReferences CT_ExternalReferences ? */
8155            case '<externalReferences': case '</externalReferences>': case '<externalReferences>': break;
8156            /* 18.2.8    externalReference CT_ExternalReference + */
8157            case '<externalReference': break;
8158
8159            /* 18.2.6  definedNames CT_DefinedNames ? */
8160            case '<definedNames/>': break;
8161            case '<definedNames>': case '<definedNames': pass=true; break;
8162            case '</definedNames>': pass=false; break;
8163            /* 18.2.5    definedName CT_DefinedName + */
8164            case '<definedName': case '<definedName/>': case '</definedName>': break;
8165
8166            /* 18.2.2  calcPr CT_CalcPr ? */
8167            case '<calcPr': delete y[0]; wb.CalcPr = y; break;
8168            case '<calcPr/>': delete y[0]; wb.CalcPr = y; break;
8169
8170            /* 18.2.16 oleSize CT_OleSize ? (ref required) */
8171            case '<oleSize': break;
8172
8173            /* 18.2.4  customWorkbookViews CT_CustomWorkbookViews ? */
8174            case '<customWorkbookViews>': case '</customWorkbookViews>': case '<customWorkbookViews': break;
8175            /* 18.2.3    customWorkbookView CT_CustomWorkbookView + */
8176            case '<customWorkbookView': case '</customWorkbookView>': break;
8177
8178            /* 18.2.18 pivotCaches CT_PivotCaches ? */
8179            case '<pivotCaches>': case '</pivotCaches>': case '<pivotCaches': break;
8180            /* 18.2.17 pivotCache CT_PivotCache ? */
8181            case '<pivotCache': break;
8182
8183            /* 18.2.21 smartTagPr CT_SmartTagPr ? */
8184            case '<smartTagPr': case '<smartTagPr/>': break;
8185
8186            /* 18.2.23 smartTagTypes CT_SmartTagTypes ? */
8187            case '<smartTagTypes': case '<smartTagTypes>': case '</smartTagTypes>': break;
8188            /* 18.2.22   smartTagType CT_SmartTagType ? */
8189            case '<smartTagType': break;
8190
8191            /* 18.2.24 webPublishing CT_WebPublishing ? */
8192            case '<webPublishing': case '<webPublishing/>': break;
8193
8194            /* 18.2.11 fileRecoveryPr CT_FileRecoveryPr ? */
8195            case '<fileRecoveryPr': case '<fileRecoveryPr/>': break;
8196
8197            /* 18.2.26 webPublishObjects CT_WebPublishObjects ? */
8198            case '<webPublishObjects>': case '<webPublishObjects': case '</webPublishObjects>': break;
8199            /* 18.2.25 webPublishObject CT_WebPublishObject ? */
8200            case '<webPublishObject': break;
8201
8202            /* 18.2.10 extLst CT_ExtensionList ? */
8203            case '<extLst>': case '</extLst>': case '<extLst/>': break;
8204            /* 18.2.7    ext CT_Extension + */
8205            case '<ext': pass=true; break; //TODO: check with versions of excel
8206            case '</ext>': pass=false; break;
8207
8208            /* Others */
8209            case '<ArchID': break;
8210            case '<AlternateContent': pass=true; break;
8211            case '</AlternateContent>': pass=false; break;
8212
8213            default: if(!pass && opts.WTF) throw 'unrecognized ' + y[0] + ' in workbook';
8214        }
8215    });
8216    if(XMLNS.main.indexOf(wb.xmlns) === -1) throw new Error("Unknown Namespace: " + wb.xmlns);
8217
8218    parse_wb_defaults(wb);
8219
8220    return wb;
8221}
8222
8223var WB_XML_ROOT = writextag('workbook', null, {
8224    'xmlns': XMLNS.main[0],
8225    //'xmlns:mx': XMLNS.mx,
8226    //'xmlns:s': XMLNS.main[0],
8227    'xmlns:r': XMLNS.r
8228});
8229
8230function safe1904(wb) {
8231    /* TODO: store date1904 somewhere else */
8232    try { return parsexmlbool(wb.Workbook.WBProps.date1904) ? "true" : "false"; } catch(e) { return "false"; }
8233}
8234
8235function write_wb_xml(wb, opts) {
8236    var o = [XML_HEADER];
8237    o[o.length] = WB_XML_ROOT;
8238    o[o.length] = (writextag('workbookPr', null, {date1904:safe1904(wb)}));
8239    o[o.length] = "<sheets>";
8240    for(var i = 0; i != wb.SheetNames.length; ++i)
8241        o[o.length] = (writextag('sheet',null,{name:wb.SheetNames[i].substr(0,31), sheetId:""+(i+1), "r:id":"rId"+(i+1)}));
8242    o[o.length] = "</sheets>";
8243    if(o.length>2){ o[o.length] = '</workbook>'; o[1]=o[1].replace("/>",">"); }
8244    return o.join("");
8245}
8246/* [MS-XLSB] 2.4.301 BrtBundleSh */
8247function parse_BrtBundleSh(data, length) {
8248    var z = {};
8249    z.hsState = data.read_shift(4); //ST_SheetState
8250    z.iTabID = data.read_shift(4);
8251    z.strRelID = parse_RelID(data,length-8);
8252    z.name = parse_XLWideString(data);
8253    return z;
8254}
8255function write_BrtBundleSh(data, o) {
8256    if(!o) o = new_buf(127);
8257    o.write_shift(4, data.hsState);
8258    o.write_shift(4, data.iTabID);
8259    write_RelID(data.strRelID, o);
8260    write_XLWideString(data.name.substr(0,31), o);
8261    return o;
8262}
8263
8264/* [MS-XLSB] 2.4.807 BrtWbProp */
8265function parse_BrtWbProp(data, length) {
8266    data.read_shift(4);
8267    var dwThemeVersion = data.read_shift(4);
8268    var strName = (length > 8) ? parse_XLWideString(data) : "";
8269    return [dwThemeVersion, strName];
8270}
8271function write_BrtWbProp(data, o) {
8272    if(!o) o = new_buf(8);
8273    o.write_shift(4, 0);
8274    o.write_shift(4, 0);
8275    return o;
8276}
8277
8278function parse_BrtFRTArchID$(data, length) {
8279    var o = {};
8280    data.read_shift(4);
8281    o.ArchID = data.read_shift(4);
8282    data.l += length - 8;
8283    return o;
8284}
8285
8286/* [MS-XLSB] 2.1.7.60 Workbook */
8287function parse_wb_bin(data, opts) {
8288    var wb = { AppVersion:{}, WBProps:{}, WBView:[], Sheets:[], CalcPr:{}, xmlns: "" };
8289    var pass = false, z;
8290
8291    recordhopper(data, function hopper_wb(val, R) {
8292        switch(R.n) {
8293            case 'BrtBundleSh': wb.Sheets.push(val); break;
8294
8295            case 'BrtBeginBook': break;
8296            case 'BrtFileVersion': break;
8297            case 'BrtWbProp': break;
8298            case 'BrtACBegin': break;
8299            case 'BrtAbsPath15': break;
8300            case 'BrtACEnd': break;
8301            case 'BrtWbFactoid': break;
8302            /*case 'BrtBookProtectionIso': break;*/
8303            case 'BrtBookProtection': break;
8304            case 'BrtBeginBookViews': break;
8305            case 'BrtBookView': break;
8306            case 'BrtEndBookViews': break;
8307            case 'BrtBeginBundleShs': break;
8308            case 'BrtEndBundleShs': break;
8309            case 'BrtBeginFnGroup': break;
8310            case 'BrtEndFnGroup': break;
8311            case 'BrtBeginExternals': break;
8312            case 'BrtSupSelf': break;
8313            case 'BrtSupBookSrc': break;
8314            case 'BrtExternSheet': break;
8315            case 'BrtEndExternals': break;
8316            case 'BrtName': break;
8317            case 'BrtCalcProp': break;
8318            case 'BrtUserBookView': break;
8319            case 'BrtBeginPivotCacheIDs': break;
8320            case 'BrtBeginPivotCacheID': break;
8321            case 'BrtEndPivotCacheID': break;
8322            case 'BrtEndPivotCacheIDs': break;
8323            case 'BrtWebOpt': break;
8324            case 'BrtFileRecover': break;
8325            case 'BrtFileSharing': break;
8326            /*case 'BrtBeginWebPubItems': break;
8327            case 'BrtBeginWebPubItem': break;
8328            case 'BrtEndWebPubItem': break;
8329            case 'BrtEndWebPubItems': break;*/
8330
8331            /* Smart Tags */
8332            case 'BrtBeginSmartTagTypes': break;
8333            case 'BrtSmartTagType': break;
8334            case 'BrtEndSmartTagTypes': break;
8335
8336            case 'BrtFRTBegin': pass = true; break;
8337            case 'BrtFRTArchID$': break;
8338            case 'BrtWorkBookPr15': break;
8339            case 'BrtFRTEnd': pass = false; break;
8340            case 'BrtEndBook': break;
8341            default: if(!pass || opts.WTF) throw new Error("Unexpected record " + R.n);
8342        }
8343    });
8344
8345    parse_wb_defaults(wb);
8346
8347    return wb;
8348}
8349
8350/* [MS-XLSB] 2.1.7.60 Workbook */
8351function write_BUNDLESHS(ba, wb, opts) {
8352    write_record(ba, "BrtBeginBundleShs");
8353    for(var idx = 0; idx != wb.SheetNames.length; ++idx) {
8354        var d = { hsState: 0, iTabID: idx+1, strRelID: 'rId' + (idx+1), name: wb.SheetNames[idx] };
8355        write_record(ba, "BrtBundleSh", write_BrtBundleSh(d));
8356    }
8357    write_record(ba, "BrtEndBundleShs");
8358}
8359
8360/* [MS-XLSB] 2.4.643 BrtFileVersion */
8361function write_BrtFileVersion(data, o) {
8362    if(!o) o = new_buf(127);
8363    for(var i = 0; i != 4; ++i) o.write_shift(4, 0);
8364    write_XLWideString("SheetJS", o);
8365    write_XLWideString(XLSX.version, o);
8366    write_XLWideString(XLSX.version, o);
8367    write_XLWideString("7262", o);
8368    o.length = o.l;
8369    return o;
8370}
8371
8372/* [MS-XLSB] 2.1.7.60 Workbook */
8373function write_BOOKVIEWS(ba, wb, opts) {
8374    write_record(ba, "BrtBeginBookViews");
8375    /* 1*(BrtBookView *FRT) */
8376    write_record(ba, "BrtEndBookViews");
8377}
8378
8379/* [MS-XLSB] 2.4.302 BrtCalcProp */
8380function write_BrtCalcProp(data, o) {
8381    if(!o) o = new_buf(26);
8382    o.write_shift(4,0); /* force recalc */
8383    o.write_shift(4,1);
8384    o.write_shift(4,0);
8385    write_Xnum(0, o);
8386    o.write_shift(-4, 1023);
8387    o.write_shift(1, 0x33);
8388    o.write_shift(1, 0x00);
8389    return o;
8390}
8391
8392function write_BrtFileRecover(data, o) {
8393    if(!o) o = new_buf(1);
8394    o.write_shift(1,0);
8395    return o;
8396}
8397
8398/* [MS-XLSB] 2.1.7.60 Workbook */
8399function write_wb_bin(wb, opts) {
8400    var ba = buf_array();
8401    write_record(ba, "BrtBeginBook");
8402    write_record(ba, "BrtFileVersion", write_BrtFileVersion());
8403    /* [[BrtFileSharingIso] BrtFileSharing] */
8404    write_record(ba, "BrtWbProp", write_BrtWbProp());
8405    /* [ACABSPATH] */
8406    /* [[BrtBookProtectionIso] BrtBookProtection] */
8407    write_BOOKVIEWS(ba, wb, opts);
8408    write_BUNDLESHS(ba, wb, opts);
8409    /* [FNGROUP] */
8410    /* [EXTERNALS] */
8411    /* *BrtName */
8412    write_record(ba, "BrtCalcProp", write_BrtCalcProp());
8413    /* [BrtOleSize] */
8414    /* *(BrtUserBookView *FRT) */
8415    /* [PIVOTCACHEIDS] */
8416    /* [BrtWbFactoid] */
8417    /* [SMARTTAGTYPES] */
8418    /* [BrtWebOpt] */
8419    write_record(ba, "BrtFileRecover", write_BrtFileRecover());
8420    /* [WEBPUBITEMS] */
8421    /* [CRERRS] */
8422    /* FRTWORKBOOK */
8423    write_record(ba, "BrtEndBook");
8424
8425    return ba.end();
8426}
8427function parse_wb(data, name, opts) {
8428    return (name.substr(-4)===".bin" ? parse_wb_bin : parse_wb_xml)(data, opts);
8429}
8430
8431function parse_ws(data, name, opts, rels) {
8432    return (name.substr(-4)===".bin" ? parse_ws_bin : parse_ws_xml)(data, opts, rels);
8433}
8434
8435function parse_sty(data, name, opts) {
8436    return (name.substr(-4)===".bin" ? parse_sty_bin : parse_sty_xml)(data, opts);
8437}
8438
8439function parse_theme(data, name, opts) {
8440    return parse_theme_xml(data, opts);
8441}
8442
8443function parse_sst(data, name, opts) {
8444    return (name.substr(-4)===".bin" ? parse_sst_bin : parse_sst_xml)(data, opts);
8445}
8446
8447function parse_cmnt(data, name, opts) {
8448    return (name.substr(-4)===".bin" ? parse_comments_bin : parse_comments_xml)(data, opts);
8449}
8450
8451function parse_cc(data, name, opts) {
8452    return (name.substr(-4)===".bin" ? parse_cc_bin : parse_cc_xml)(data, opts);
8453}
8454
8455function write_wb(wb, name, opts) {
8456    return (name.substr(-4)===".bin" ? write_wb_bin : write_wb_xml)(wb, opts);
8457}
8458
8459function write_ws(data, name, opts, wb) {
8460    return (name.substr(-4)===".bin" ? write_ws_bin : write_ws_xml)(data, opts, wb);
8461}
8462
8463function write_sty(data, name, opts) {
8464    return (name.substr(-4)===".bin" ? write_sty_bin : write_sty_xml)(data, opts);
8465}
8466
8467function write_sst(data, name, opts) {
8468    return (name.substr(-4)===".bin" ? write_sst_bin : write_sst_xml)(data, opts);
8469}
8470/*
8471function write_cmnt(data, name, opts) {
8472    return (name.substr(-4)===".bin" ? write_comments_bin : write_comments_xml)(data, opts);
8473}
8474
8475function write_cc(data, name, opts) {
8476    return (name.substr(-4)===".bin" ? write_cc_bin : write_cc_xml)(data, opts);
8477}
8478*/
8479var attregexg2=/([\w:]+)=((?:")([^"]*)(?:")|(?:')([^']*)(?:'))/g;
8480var attregex2=/([\w:]+)=((?:")(?:[^"]*)(?:")|(?:')(?:[^']*)(?:'))/;
8481var _chr = function(c) { return String.fromCharCode(c); };
8482function xlml_parsexmltag(tag, skip_root) {
8483    var words = tag.split(/\s+/);
8484    var z = []; if(!skip_root) z[0] = words[0];
8485    if(words.length === 1) return z;
8486    var m = tag.match(attregexg2), y, j, w, i;
8487    if(m) for(i = 0; i != m.length; ++i) {
8488        y = m[i].match(attregex2);
8489        if((j=y[1].indexOf(":")) === -1) z[y[1]] = y[2].substr(1,y[2].length-2);
8490        else {
8491            if(y[1].substr(0,6) === "xmlns:") w = "xmlns"+y[1].substr(6);
8492            else w = y[1].substr(j+1);
8493            z[w] = y[2].substr(1,y[2].length-2);
8494        }
8495    }
8496    return z;
8497}
8498function xlml_parsexmltagobj(tag) {
8499    var words = tag.split(/\s+/);
8500    var z = {};
8501    if(words.length === 1) return z;
8502    var m = tag.match(attregexg2), y, j, w, i;
8503    if(m) for(i = 0; i != m.length; ++i) {
8504        y = m[i].match(attregex2);
8505        if((j=y[1].indexOf(":")) === -1) z[y[1]] = y[2].substr(1,y[2].length-2);
8506        else {
8507            if(y[1].substr(0,6) === "xmlns:") w = "xmlns"+y[1].substr(6);
8508            else w = y[1].substr(j+1);
8509            z[w] = y[2].substr(1,y[2].length-2);
8510        }
8511    }
8512    return z;
8513}
8514
8515// ----
8516
8517function xlml_format(format, value) {
8518    var fmt = XLMLFormatMap[format] || unescapexml(format);
8519    if(fmt === "General") return SSF._general(value);
8520    return SSF.format(fmt, value);
8521}
8522
8523function xlml_set_custprop(Custprops, Rn, cp, val) {
8524    switch((cp[0].match(/dt:dt="([\w.]+)"/)||["",""])[1]) {
8525        case "boolean": val = parsexmlbool(val); break;
8526        case "i2": case "int": val = parseInt(val, 10); break;
8527        case "r4": case "float": val = parseFloat(val); break;
8528        case "date": case "dateTime.tz": val = new Date(val); break;
8529        case "i8": case "string": case "fixed": case "uuid": case "bin.base64": break;
8530        default: throw "bad custprop:" + cp[0];
8531    }
8532    Custprops[unescapexml(Rn[3])] = val;
8533}
8534
8535function safe_format_xlml(cell, nf, o) {
8536    try {
8537        if(cell.t === 'e') { cell.w = cell.w || BErr[cell.v]; }
8538        else if(nf === "General") {
8539            if(cell.t === 'n') {
8540                if((cell.v|0) === cell.v) cell.w = SSF._general_int(cell.v);
8541                else cell.w = SSF._general_num(cell.v);
8542            }
8543            else cell.w = SSF._general(cell.v);
8544        }
8545        else cell.w = xlml_format(nf||"General", cell.v);
8546        if(o.cellNF) cell.z = XLMLFormatMap[nf]||nf||"General";
8547    } catch(e) { if(o.WTF) throw e; }
8548}
8549
8550function process_style_xlml(styles, stag, opts) {
8551    if(opts.cellStyles) {
8552        if(stag.Interior) {
8553            var I = stag.Interior;
8554            if(I.Pattern) I.patternType = XLMLPatternTypeMap[I.Pattern] || I.Pattern;
8555        }
8556    }
8557    styles[stag.ID] = stag;
8558}
8559
8560/* TODO: there must exist some form of OSP-blessed spec */
8561function parse_xlml_data(xml, ss, data, cell, base, styles, csty, row, o) {
8562    var nf = "General", sid = cell.StyleID, S = {}; o = o || {};
8563    var interiors = [];
8564    if(sid === undefined && row) sid = row.StyleID;
8565    if(sid === undefined && csty) sid = csty.StyleID;
8566    while(styles[sid] !== undefined) {
8567        if(styles[sid].nf) nf = styles[sid].nf;
8568        if(styles[sid].Interior) interiors.push(styles[sid].Interior);
8569        if(!styles[sid].Parent) break;
8570        sid = styles[sid].Parent;
8571    }
8572    switch(data.Type) {
8573        case 'Boolean':
8574            cell.t = 'b';
8575            cell.v = parsexmlbool(xml);
8576            break;
8577        case 'String':
8578            cell.t = 's'; cell.r = xlml_fixstr(unescapexml(xml));
8579            cell.v = xml.indexOf("<") > -1 ? ss : cell.r;
8580            break;
8581        case 'DateTime':
8582            cell.v = (Date.parse(xml) - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
8583            if(cell.v !== cell.v) cell.v = unescapexml(xml);
8584            else if(cell.v >= 1 && cell.v<60) cell.v = cell.v -1;
8585            if(!nf || nf == "General") nf = "yyyy-mm-dd";
8586            /* falls through */
8587        case 'Number':
8588            if(cell.v === undefined) cell.v=+xml;
8589            if(!cell.t) cell.t = 'n';
8590            break;
8591        case 'Error': cell.t = 'e'; cell.v = RBErr[xml]; cell.w = xml; break;
8592        default: cell.t = 's'; cell.v = xlml_fixstr(ss); break;
8593    }
8594    safe_format_xlml(cell, nf, o);
8595    if(o.cellFormula != null && cell.Formula) {
8596        cell.f = rc_to_a1(unescapexml(cell.Formula), base);
8597        cell.Formula = undefined;
8598    }
8599    if(o.cellStyles) {
8600        interiors.forEach(function(x) {
8601            if(!S.patternType && x.patternType) S.patternType = x.patternType;
8602        });
8603        cell.s = S;
8604    }
8605    cell.ixfe = cell.StyleID !== undefined ? cell.StyleID : 'Default';
8606}
8607
8608function xlml_clean_comment(comment) {
8609    comment.t = comment.v;
8610    comment.v = comment.w = comment.ixfe = undefined;
8611}
8612
8613function xlml_normalize(d) {
8614    if(has_buf && Buffer.isBuffer(d)) return d.toString('utf8');
8615    if(typeof d === 'string') return d;
8616    throw "badf";
8617}
8618
8619/* TODO: Everything */
8620var xlmlregex = /<(\/?)([a-z0-9]*:|)(\w+)[^>]*>/mg;
8621function parse_xlml_xml(d, opts) {
8622    var str = xlml_normalize(d);
8623    var Rn;
8624    var state = [], tmp;
8625    var sheets = {}, sheetnames = [], cursheet = {}, sheetname = "";
8626    var table = {}, cell = {}, row = {}, dtag, didx;
8627    var c = 0, r = 0;
8628    var refguess = {s: {r:1000000, c:1000000}, e: {r:0, c:0} };
8629    var styles = {}, stag = {};
8630    var ss = "", fidx = 0;
8631    var mergecells = [];
8632    var Props = {}, Custprops = {}, pidx = 0, cp = {};
8633    var comments = [], comment = {};
8634    var cstys = [], csty;
8635    xlmlregex.lastIndex = 0;
8636    while((Rn = xlmlregex.exec(str))) switch(Rn[3]) {
8637        case 'Data':
8638            if(state[state.length-1][1]) break;
8639            if(Rn[1]==='/') parse_xlml_data(str.slice(didx, Rn.index), ss, dtag, state[state.length-1][0]=="Comment"?comment:cell, {c:c,r:r}, styles, cstys[c], row, opts);
8640            else { ss = ""; dtag = xlml_parsexmltag(Rn[0]); didx = Rn.index + Rn[0].length; }
8641            break;
8642        case 'Cell':
8643            if(Rn[1]==='/'){
8644                if(comments.length > 0) cell.c = comments;
8645                if((!opts.sheetRows || opts.sheetRows > r) && cell.v !== undefined) cursheet[encode_col(c) + encode_row(r)] = cell;
8646                if(cell.HRef) {
8647                    cell.l = {Target:cell.HRef, tooltip:cell.HRefScreenTip};
8648                    cell.HRef = cell.HRefScreenTip = undefined;
8649                }
8650                if(cell.MergeAcross || cell.MergeDown) {
8651                    var cc = c + (parseInt(cell.MergeAcross,10)|0);
8652                    var rr = r + (parseInt(cell.MergeDown,10)|0);
8653                    mergecells.push({s:{c:c,r:r},e:{c:cc,r:rr}});
8654                }
8655                ++c;
8656                if(cell.MergeAcross) c += +cell.MergeAcross;
8657            } else {
8658                cell = xlml_parsexmltagobj(Rn[0]);
8659                if(cell.Index) c = +cell.Index - 1;
8660                if(c < refguess.s.c) refguess.s.c = c;
8661                if(c > refguess.e.c) refguess.e.c = c;
8662                if(Rn[0].substr(-2) === "/>") ++c;
8663                comments = [];
8664            }
8665            break;
8666        case 'Row':
8667            if(Rn[1]==='/' || Rn[0].substr(-2) === "/>") {
8668                if(r < refguess.s.r) refguess.s.r = r;
8669                if(r > refguess.e.r) refguess.e.r = r;
8670                if(Rn[0].substr(-2) === "/>") {
8671                    row = xlml_parsexmltag(Rn[0]);
8672                    if(row.Index) r = +row.Index - 1;
8673                }
8674                c = 0; ++r;
8675            } else {
8676                row = xlml_parsexmltag(Rn[0]);
8677                if(row.Index) r = +row.Index - 1;
8678            }
8679            break;
8680        case 'Worksheet': /* TODO: read range from FullRows/FullColumns */
8681            if(Rn[1]==='/'){
8682                if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp;
8683                sheetnames.push(sheetname);
8684                if(refguess.s.r <= refguess.e.r && refguess.s.c <= refguess.e.c) cursheet["!ref"] = encode_range(refguess);
8685                if(mergecells.length) cursheet["!merges"] = mergecells;
8686                sheets[sheetname] = cursheet;
8687            } else {
8688                refguess = {s: {r:1000000, c:1000000}, e: {r:0, c:0} };
8689                r = c = 0;
8690                state.push([Rn[3], false]);
8691                tmp = xlml_parsexmltag(Rn[0]);
8692                sheetname = tmp.Name;
8693                cursheet = {};
8694                mergecells = [];
8695            }
8696            break;
8697        case 'Table':
8698            if(Rn[1]==='/'){if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp;}
8699            else if(Rn[0].slice(-2) == "/>") break;
8700            else {
8701                table = xlml_parsexmltag(Rn[0]);
8702                state.push([Rn[3], false]);
8703                cstys = [];
8704            }
8705            break;
8706
8707        case 'Style':
8708            if(Rn[1]==='/') process_style_xlml(styles, stag, opts);
8709            else stag = xlml_parsexmltag(Rn[0]);
8710            break;
8711
8712        case 'NumberFormat':
8713            stag.nf = xlml_parsexmltag(Rn[0]).Format || "General";
8714            break;
8715
8716        case 'Column':
8717            if(state[state.length-1][0] !== 'Table') break;
8718            csty = xlml_parsexmltag(Rn[0]);
8719            cstys[(csty.Index-1||cstys.length)] = csty;
8720            for(var i = 0; i < +csty.Span; ++i) cstys[cstys.length] = csty;
8721            break;
8722
8723        case 'NamedRange': break;
8724        case 'NamedCell': break;
8725        case 'B': break;
8726        case 'I': break;
8727        case 'U': break;
8728        case 'S': break;
8729        case 'Sub': break;
8730        case 'Sup': break;
8731        case 'Span': break;
8732        case 'Border': break;
8733        case 'Alignment': break;
8734        case 'Borders': break;
8735        case 'Font':
8736            if(Rn[0].substr(-2) === "/>") break;
8737            else if(Rn[1]==="/") ss += str.slice(fidx, Rn.index);
8738            else fidx = Rn.index + Rn[0].length;
8739            break;
8740        case 'Interior':
8741            if(!opts.cellStyles) break;
8742            stag.Interior = xlml_parsexmltag(Rn[0]);
8743            break;
8744        case 'Protection': break;
8745
8746        case 'Author':
8747        case 'Title':
8748        case 'Description':
8749        case 'Created':
8750        case 'Keywords':
8751        case 'Subject':
8752        case 'Category':
8753        case 'Company':
8754        case 'LastAuthor':
8755        case 'LastSaved':
8756        case 'LastPrinted':
8757        case 'Version':
8758        case 'Revision':
8759        case 'TotalTime':
8760        case 'HyperlinkBase':
8761        case 'Manager':
8762            if(Rn[0].substr(-2) === "/>") break;
8763            else if(Rn[1]==="/") xlml_set_prop(Props, Rn[3], str.slice(pidx, Rn.index));
8764            else pidx = Rn.index + Rn[0].length;
8765            break;
8766        case 'Paragraphs': break;
8767
8768        case 'Styles':
8769        case 'Workbook':
8770            if(Rn[1]==='/'){if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp;}
8771            else state.push([Rn[3], false]);
8772            break;
8773
8774        case 'Comment':
8775            if(Rn[1]==='/'){
8776                if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp;
8777                xlml_clean_comment(comment);
8778                comments.push(comment);
8779            } else {
8780                state.push([Rn[3], false]);
8781                tmp = xlml_parsexmltag(Rn[0]);
8782                comment = {a:tmp.Author};
8783            }
8784            break;
8785
8786        case 'Name': break;
8787
8788        case 'ComponentOptions':
8789        case 'DocumentProperties':
8790        case 'CustomDocumentProperties':
8791        case 'OfficeDocumentSettings':
8792        case 'PivotTable':
8793        case 'PivotCache':
8794        case 'Names':
8795        case 'MapInfo':
8796        case 'PageBreaks':
8797        case 'QueryTable':
8798        case 'DataValidation':
8799        case 'AutoFilter':
8800        case 'Sorting':
8801        case 'Schema':
8802        case 'data':
8803        case 'ConditionalFormatting':
8804        case 'SmartTagType':
8805        case 'SmartTags':
8806        case 'ExcelWorkbook':
8807        case 'WorkbookOptions':
8808        case 'WorksheetOptions':
8809            if(Rn[1]==='/'){if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp;}
8810            else if(Rn[0].charAt(Rn[0].length-2) !== '/') state.push([Rn[3], true]);
8811            break;
8812
8813        default:
8814            var seen = true;
8815            switch(state[state.length-1][0]) {
8816                /* OfficeDocumentSettings */
8817                case 'OfficeDocumentSettings': switch(Rn[3]) {
8818                    case 'AllowPNG': break;
8819                    case 'RemovePersonalInformation': break;
8820                    case 'DownloadComponents': break;
8821                    case 'LocationOfComponents': break;
8822                    case 'Colors': break;
8823                    case 'Color': break;
8824                    case 'Index': break;
8825                    case 'RGB': break;
8826                    case 'PixelsPerInch': break;
8827                    case 'TargetScreenSize': break;
8828                    case 'ReadOnlyRecommended': break;
8829                    default: seen = false;
8830                } break;
8831
8832                /* ComponentOptions */
8833                case 'ComponentOptions': switch(Rn[3]) {
8834                    case 'Toolbar': break;
8835                    case 'HideOfficeLogo': break;
8836                    case 'SpreadsheetAutoFit': break;
8837                    case 'Label': break;
8838                    case 'Caption': break;
8839                    case 'MaxHeight': break;
8840                    case 'MaxWidth': break;
8841                    case 'NextSheetNumber': break;
8842                    default: seen = false;
8843                } break;
8844
8845                /* ExcelWorkbook */
8846                case 'ExcelWorkbook': switch(Rn[3]) {
8847                    case 'WindowHeight': break;
8848                    case 'WindowWidth': break;
8849                    case 'WindowTopX': break;
8850                    case 'WindowTopY': break;
8851                    case 'TabRatio': break;
8852                    case 'ProtectStructure': break;
8853                    case 'ProtectWindows': break;
8854                    case 'ActiveSheet': break;
8855                    case 'DisplayInkNotes': break;
8856                    case 'FirstVisibleSheet': break;
8857                    case 'SupBook': break;
8858                    case 'SheetName': break;
8859                    case 'SheetIndex': break;
8860                    case 'SheetIndexFirst': break;
8861                    case 'SheetIndexLast': break;
8862                    case 'Dll': break;
8863                    case 'AcceptLabelsInFormulas': break;
8864                    case 'DoNotSaveLinkValues': break;
8865                    case 'Date1904': break;
8866                    case 'Iteration': break;
8867                    case 'MaxIterations': break;
8868                    case 'MaxChange': break;
8869                    case 'Path': break;
8870                    case 'Xct': break;
8871                    case 'Count': break;
8872                    case 'SelectedSheets': break;
8873                    case 'Calculation': break;
8874                    case 'Uncalced': break;
8875                    case 'StartupPrompt': break;
8876                    case 'Crn': break;
8877                    case 'ExternName': break;
8878                    case 'Formula': break;
8879                    case 'ColFirst': break;
8880                    case 'ColLast': break;
8881                    case 'WantAdvise': break;
8882                    case 'Boolean': break;
8883                    case 'Error': break;
8884                    case 'Text': break;
8885                    case 'OLE': break;
8886                    case 'NoAutoRecover': break;
8887                    case 'PublishObjects': break;
8888                    case 'DoNotCalculateBeforeSave': break;
8889                    case 'Number': break;
8890                    case 'RefModeR1C1': break;
8891                    case 'EmbedSaveSmartTags': break;
8892                    default: seen = false;
8893                } break;
8894
8895                /* WorkbookOptions */
8896                case 'WorkbookOptions': switch(Rn[3]) {
8897                    case 'OWCVersion': break;
8898                    case 'Height': break;
8899                    case 'Width': break;
8900                    default: seen = false;
8901                } break;
8902
8903                /* WorksheetOptions */
8904                case 'WorksheetOptions': switch(Rn[3]) {
8905                    case 'Unsynced': break;
8906                    case 'Visible': break;
8907                    case 'Print': break;
8908                    case 'Panes': break;
8909                    case 'Scale': break;
8910                    case 'Pane': break;
8911                    case 'Number': break;
8912                    case 'Layout': break;
8913                    case 'Header': break;
8914                    case 'Footer': break;
8915                    case 'PageSetup': break;
8916                    case 'PageMargins': break;
8917                    case 'Selected': break;
8918                    case 'ProtectObjects': break;
8919                    case 'EnableSelection': break;
8920                    case 'ProtectScenarios': break;
8921                    case 'ValidPrinterInfo': break;
8922                    case 'HorizontalResolution': break;
8923                    case 'VerticalResolution': break;
8924                    case 'NumberofCopies': break;
8925                    case 'ActiveRow': break;
8926                    case 'ActiveCol': break;
8927                    case 'ActivePane': break;
8928                    case 'TopRowVisible': break;
8929                    case 'TopRowBottomPane': break;
8930                    case 'LeftColumnVisible': break;
8931                    case 'LeftColumnRightPane': break;
8932                    case 'FitToPage': break;
8933                    case 'RangeSelection': break;
8934                    case 'PaperSizeIndex': break;
8935                    case 'PageLayoutZoom': break;
8936                    case 'PageBreakZoom': break;
8937                    case 'FilterOn': break;
8938                    case 'DoNotDisplayGridlines': break;
8939                    case 'SplitHorizontal': break;
8940                    case 'SplitVertical': break;
8941                    case 'FreezePanes': break;
8942                    case 'FrozenNoSplit': break;
8943                    case 'FitWidth': break;
8944                    case 'FitHeight': break;
8945                    case 'CommentsLayout': break;
8946                    case 'Zoom': break;
8947                    case 'LeftToRight': break;
8948                    case 'Gridlines': break;
8949                    case 'AllowSort': break;
8950                    case 'AllowFilter': break;
8951                    case 'AllowInsertRows': break;
8952                    case 'AllowDeleteRows': break;
8953                    case 'AllowInsertCols': break;
8954                    case 'AllowDeleteCols': break;
8955                    case 'AllowInsertHyperlinks': break;
8956                    case 'AllowFormatCells': break;
8957                    case 'AllowSizeCols': break;
8958                    case 'AllowSizeRows': break;
8959                    case 'NoSummaryRowsBelowDetail': break;
8960                    case 'TabColorIndex': break;
8961                    case 'DoNotDisplayHeadings': break;
8962                    case 'ShowPageLayoutZoom': break;
8963                    case 'NoSummaryColumnsRightDetail': break;
8964                    case 'BlackAndWhite': break;
8965                    case 'DoNotDisplayZeros': break;
8966                    case 'DisplayPageBreak': break;
8967                    case 'RowColHeadings': break;
8968                    case 'DoNotDisplayOutline': break;
8969                    case 'NoOrientation': break;
8970                    case 'AllowUsePivotTables': break;
8971                    case 'ZeroHeight': break;
8972                    case 'ViewableRange': break;
8973                    case 'Selection': break;
8974                    case 'ProtectContents': break;
8975                    default: seen = false;
8976                } break;
8977
8978                /* PivotTable */
8979                case 'PivotTable': case 'PivotCache': switch(Rn[3]) {
8980                    case 'ImmediateItemsOnDrop': break;
8981                    case 'ShowPageMultipleItemLabel': break;
8982                    case 'CompactRowIndent': break;
8983                    case 'Location': break;
8984                    case 'PivotField': break;
8985                    case 'Orientation': break;
8986                    case 'LayoutForm': break;
8987                    case 'LayoutSubtotalLocation': break;
8988                    case 'LayoutCompactRow': break;
8989                    case 'Position': break;
8990                    case 'PivotItem': break;
8991                    case 'DataType': break;
8992                    case 'DataField': break;
8993                    case 'SourceName': break;
8994                    case 'ParentField': break;
8995                    case 'PTLineItems': break;
8996                    case 'PTLineItem': break;
8997                    case 'CountOfSameItems': break;
8998                    case 'Item': break;
8999                    case 'ItemType': break;
9000                    case 'PTSource': break;
9001                    case 'CacheIndex': break;
9002                    case 'ConsolidationReference': break;
9003                    case 'FileName': break;
9004                    case 'Reference': break;
9005                    case 'NoColumnGrand': break;
9006                    case 'NoRowGrand': break;
9007                    case 'BlankLineAfterItems': break;
9008                    case 'Hidden': break;
9009                    case 'Subtotal': break;
9010                    case 'BaseField': break;
9011                    case 'MapChildItems': break;
9012                    case 'Function': break;
9013                    case 'RefreshOnFileOpen': break;
9014                    case 'PrintSetTitles': break;
9015                    case 'MergeLabels': break;
9016                    case 'DefaultVersion': break;
9017                    case 'RefreshName': break;
9018                    case 'RefreshDate': break;
9019                    case 'RefreshDateCopy': break;
9020                    case 'VersionLastRefresh': break;
9021                    case 'VersionLastUpdate': break;
9022                    case 'VersionUpdateableMin': break;
9023                    case 'VersionRefreshableMin': break;
9024                    case 'Calculation': break;
9025                    default: seen = false;
9026                } break;
9027
9028                /* PageBreaks */
9029                case 'PageBreaks': switch(Rn[3]) {
9030                    case 'ColBreaks': break;
9031                    case 'ColBreak': break;
9032                    case 'RowBreaks': break;
9033                    case 'RowBreak': break;
9034                    case 'ColStart': break;
9035                    case 'ColEnd': break;
9036                    case 'RowEnd': break;
9037                    default: seen = false;
9038                } break;
9039
9040                /* AutoFilter */
9041                case 'AutoFilter': switch(Rn[3]) {
9042                    case 'AutoFilterColumn': break;
9043                    case 'AutoFilterCondition': break;
9044                    case 'AutoFilterAnd': break;
9045                    case 'AutoFilterOr': break;
9046                    default: seen = false;
9047                } break;
9048
9049                /* QueryTable */
9050                case 'QueryTable': switch(Rn[3]) {
9051                    case 'Id': break;
9052                    case 'AutoFormatFont': break;
9053                    case 'AutoFormatPattern': break;
9054                    case 'QuerySource': break;
9055                    case 'QueryType': break;
9056                    case 'EnableRedirections': break;
9057                    case 'RefreshedInXl9': break;
9058                    case 'URLString': break;
9059                    case 'HTMLTables': break;
9060                    case 'Connection': break;
9061                    case 'CommandText': break;
9062                    case 'RefreshInfo': break;
9063                    case 'NoTitles': break;
9064                    case 'NextId': break;
9065                    case 'ColumnInfo': break;
9066                    case 'OverwriteCells': break;
9067                    case 'DoNotPromptForFile': break;
9068                    case 'TextWizardSettings': break;
9069                    case 'Source': break;
9070                    case 'Number': break;
9071                    case 'Decimal': break;
9072                    case 'ThousandSeparator': break;
9073                    case 'TrailingMinusNumbers': break;
9074                    case 'FormatSettings': break;
9075                    case 'FieldType': break;
9076                    case 'Delimiters': break;
9077                    case 'Tab': break;
9078                    case 'Comma': break;
9079                    case 'AutoFormatName': break;
9080                    case 'VersionLastEdit': break;
9081                    case 'VersionLastRefresh': break;
9082                    default: seen = false;
9083                } break;
9084
9085                /* Sorting */
9086                case 'Sorting':
9087                /* ConditionalFormatting */
9088                case 'ConditionalFormatting':
9089                /* DataValidation */
9090                case 'DataValidation': switch(Rn[3]) {
9091                    case 'Range': break;
9092                    case 'Type': break;
9093                    case 'Min': break;
9094                    case 'Max': break;
9095                    case 'Sort': break;
9096                    case 'Descending': break;
9097                    case 'Order': break;
9098                    case 'CaseSensitive': break;
9099                    case 'Value': break;
9100                    case 'ErrorStyle': break;
9101                    case 'ErrorMessage': break;
9102                    case 'ErrorTitle': break;
9103                    case 'CellRangeList': break;
9104                    case 'InputMessage': break;
9105                    case 'InputTitle': break;
9106                    case 'ComboHide': break;
9107                    case 'InputHide': break;
9108                    case 'Condition': break;
9109                    case 'Qualifier': break;
9110                    case 'UseBlank': break;
9111                    case 'Value1': break;
9112                    case 'Value2': break;
9113                    case 'Format': break;
9114                    default: seen = false;
9115                } break;
9116
9117                /* MapInfo (schema) */
9118                case 'MapInfo': case 'Schema': case 'data': switch(Rn[3]) {
9119                    case 'Map': break;
9120                    case 'Entry': break;
9121                    case 'Range': break;
9122                    case 'XPath': break;
9123                    case 'Field': break;
9124                    case 'XSDType': break;
9125                    case 'FilterOn': break;
9126                    case 'Aggregate': break;
9127                    case 'ElementType': break;
9128                    case 'AttributeType': break;
9129                /* These are from xsd (XML Schema Definition) */
9130                    case 'schema':
9131                    case 'element':
9132                    case 'complexType':
9133                    case 'datatype':
9134                    case 'all':
9135                    case 'attribute':
9136                    case 'extends': break;
9137
9138                    case 'row': break;
9139                    default: seen = false;
9140                } break;
9141
9142                /* SmartTags (can be anything) */
9143                case 'SmartTags': break;
9144
9145                default: seen = false; break;
9146            }
9147            if(seen) break;
9148            /* CustomDocumentProperties */
9149            if(!state[state.length-1][1]) throw 'Unrecognized tag: ' + Rn[3] + "|" + state.join("|");
9150            if(state[state.length-1][0]==='CustomDocumentProperties') {
9151                if(Rn[0].substr(-2) === "/>") break;
9152                else if(Rn[1]==="/") xlml_set_custprop(Custprops, Rn, cp, str.slice(pidx, Rn.index));
9153                else { cp = Rn; pidx = Rn.index + Rn[0].length; }
9154                break;
9155            }
9156            if(opts.WTF) throw 'Unrecognized tag: ' + Rn[3] + "|" + state.join("|");
9157    }
9158    var out = {};
9159    if(!opts.bookSheets && !opts.bookProps) out.Sheets = sheets;
9160    out.SheetNames = sheetnames;
9161    out.SSF = SSF.get_table();
9162    out.Props = Props;
9163    out.Custprops = Custprops;
9164    return out;
9165}
9166
9167function parse_xlml(data, opts) {
9168    fix_read_opts(opts=opts||{});
9169    switch(opts.type||"base64") {
9170        case "base64": return parse_xlml_xml(Base64.decode(data), opts);
9171        case "binary": case "buffer": case "file": return parse_xlml_xml(data, opts);
9172        case "array": return parse_xlml_xml(data.map(_chr).join(""), opts);
9173    }
9174}
9175
9176function write_xlml(wb, opts) { }
9177
9178/* [MS-OLEDS] 2.3.8 CompObjStream */
9179function parse_compobj(obj) {
9180    var v = {};
9181    var o = obj.content;
9182
9183    /* [MS-OLEDS] 2.3.7 CompObjHeader -- All fields MUST be ignored */
9184    var l = 28, m;
9185    m = __lpstr(o, l);
9186    l += 4 + __readUInt32LE(o,l);
9187    v.UserType = m;
9188
9189    /* [MS-OLEDS] 2.3.1 ClipboardFormatOrAnsiString */
9190    m = __readUInt32LE(o,l); l+= 4;
9191    switch(m) {
9192        case 0x00000000: break;
9193        case 0xffffffff: case 0xfffffffe: l+=4; break;
9194        default:
9195            if(m > 0x190) throw new Error("Unsupported Clipboard: " + m.toString(16));
9196            l += m;
9197    }
9198
9199    m = __lpstr(o, l); l += m.length === 0 ? 0 : 5 + m.length; v.Reserved1 = m;
9200
9201    if((m = __readUInt32LE(o,l)) !== 0x71b2e9f4) return v;
9202    throw "Unsupported Unicode Extension";
9203}
9204
9205/* 2.4.58 Continue logic */
9206function slurp(R, blob, length, opts) {
9207    var l = length;
9208    var bufs = [];
9209    var d = blob.slice(blob.l,blob.l+l);
9210    if(opts && opts.enc && opts.enc.insitu_decrypt) switch(R.n) {
9211    case 'BOF': case 'FilePass': case 'FileLock': case 'InterfaceHdr': case 'RRDInfo': case 'RRDHead': case 'UsrExcl': break;
9212    default:
9213        if(d.length === 0) break;
9214        opts.enc.insitu_decrypt(d);
9215    }
9216    bufs.push(d);
9217    blob.l += l;
9218    var next = (XLSRecordEnum[__readUInt16LE(blob,blob.l)]);
9219    while(next != null && next.n === 'Continue') {
9220        l = __readUInt16LE(blob,blob.l+2);
9221        bufs.push(blob.slice(blob.l+4,blob.l+4+l));
9222        blob.l += 4+l;
9223        next = (XLSRecordEnum[__readUInt16LE(blob, blob.l)]);
9224    }
9225    var b = bconcat(bufs);
9226    prep_blob(b, 0);
9227    var ll = 0; b.lens = [];
9228    for(var j = 0; j < bufs.length; ++j) { b.lens.push(ll); ll += bufs[j].length; }
9229    return R.f(b, b.length, opts);
9230}
9231
9232function safe_format_xf(p, opts, date1904) {
9233    if(!p.XF) return;
9234    try {
9235        var fmtid = p.XF.ifmt||0;
9236        if(p.t === 'e') { p.w = p.w || BErr[p.v]; }
9237        else if(fmtid === 0) {
9238            if(p.t === 'n') {
9239                if((p.v|0) === p.v) p.w = SSF._general_int(p.v);
9240                else p.w = SSF._general_num(p.v);
9241            }
9242            else p.w = SSF._general(p.v);
9243        }
9244        else p.w = SSF.format(fmtid,p.v, {date1904:date1904||false});
9245        if(opts.cellNF) p.z = SSF._table[fmtid];
9246    } catch(e) { if(opts.WTF) throw e; }
9247}
9248
9249function make_cell(val, ixfe, t) {
9250    return {v:val, ixfe:ixfe, t:t};
9251}
9252
9253// 2.3.2
9254function parse_workbook(blob, options) {
9255    var wb = {opts:{}};
9256    var Sheets = {};
9257    var out = {};
9258    var Directory = {};
9259    var found_sheet = false;
9260    var range = {};
9261    var last_formula = null;
9262    var sst = [];
9263    var cur_sheet = "";
9264    var Preamble = {};
9265    var lastcell, last_cell, cc, cmnt, rng, rngC, rngR;
9266    var shared_formulae = {};
9267    var array_formulae = []; /* TODO: something more clever */
9268    var temp_val;
9269    var country;
9270    var cell_valid = true;
9271    var XFs = []; /* XF records */
9272    var palette = [];
9273    var get_rgb = function getrgb(icv) {
9274        if(icv < 8) return XLSIcv[icv];
9275        if(icv < 64) return palette[icv-8] || XLSIcv[icv];
9276        return XLSIcv[icv];
9277    };
9278    var process_cell_style = function pcs(cell, line) {
9279        var xfd = line.XF.data;
9280        if(!xfd || !xfd.patternType) return;
9281        line.s = {};
9282        line.s.patternType = xfd.patternType;
9283        var t;
9284        if((t = rgb2Hex(get_rgb(xfd.icvFore)))) { line.s.fgColor = {rgb:t}; }
9285        if((t = rgb2Hex(get_rgb(xfd.icvBack)))) { line.s.bgColor = {rgb:t}; }
9286    };
9287    var addcell = function addcell(cell, line, options) {
9288        if(!cell_valid) return;
9289        if(options.cellStyles && line.XF && line.XF.data) process_cell_style(cell, line);
9290        lastcell = cell;
9291        last_cell = encode_cell(cell);
9292        if(range.s) {
9293            if(cell.r < range.s.r) range.s.r = cell.r;
9294            if(cell.c < range.s.c) range.s.c = cell.c;
9295        }
9296        if(range.e) {
9297            if(cell.r + 1 > range.e.r) range.e.r = cell.r + 1;
9298            if(cell.c + 1 > range.e.c) range.e.c = cell.c + 1;
9299        }
9300        if(options.sheetRows && lastcell.r >= options.sheetRows) cell_valid = false;
9301        else out[last_cell] = line;
9302    };
9303    var opts = {
9304        enc: false, // encrypted
9305        sbcch: 0, // cch in the preceding SupBook
9306        snames: [], // sheetnames
9307        sharedf: shared_formulae, // shared formulae by address
9308        arrayf: array_formulae, // array formulae array
9309        rrtabid: [], // RRTabId
9310        lastuser: "", // Last User from WriteAccess
9311        biff: 8, // BIFF version
9312        codepage: 0, // CP from CodePage record
9313        winlocked: 0, // fLockWn from WinProtect
9314        wtf: false
9315    };
9316    if(options.password) opts.password = options.password;
9317    var mergecells = [];
9318    var objects = [];
9319    var supbooks = [[]]; // 1-indexed, will hold extern names
9320    var sbc = 0, sbci = 0, sbcli = 0;
9321    supbooks.SheetNames = opts.snames;
9322    supbooks.sharedf = opts.sharedf;
9323    supbooks.arrayf = opts.arrayf;
9324    var last_Rn = '';
9325    var file_depth = 0; /* TODO: make a real stack */
9326
9327    /* explicit override for some broken writers */
9328    opts.codepage = 1200;
9329    set_cp(1200);
9330
9331    while(blob.l < blob.length - 1) {
9332        var s = blob.l;
9333        var RecordType = blob.read_shift(2);
9334        if(RecordType === 0 && last_Rn === 'EOF') break;
9335        var length = (blob.l === blob.length ? 0 : blob.read_shift(2)), y;
9336        var R = XLSRecordEnum[RecordType];
9337        if(R && R.f) {
9338            if(options.bookSheets) {
9339                if(last_Rn === 'BoundSheet8' && R.n !== 'BoundSheet8') break;
9340            }
9341            last_Rn = R.n;
9342            if(R.r === 2 || R.r == 12) {
9343                var rt = blob.read_shift(2); length -= 2;
9344                if(!opts.enc && rt !== RecordType) throw "rt mismatch";
9345                if(R.r == 12){ blob.l += 10; length -= 10; } // skip FRT
9346            }
9347            //console.error(R,blob.l,length,blob.length);
9348            var val;
9349            if(R.n === 'EOF') val = R.f(blob, length, opts);
9350            else val = slurp(R, blob, length, opts);
9351            var Rn = R.n;
9352            /* BIFF5 overrides */
9353            if(opts.biff === 5 || opts.biff === 2) switch(Rn) {
9354                case 'Lbl': Rn = 'Label'; break;
9355            }
9356            /* nested switch statements to workaround V8 128 limit */
9357            switch(Rn) {
9358                /* Workbook Options */
9359                case 'Date1904': wb.opts.Date1904 = val; break;
9360                case 'WriteProtect': wb.opts.WriteProtect = true; break;
9361                case 'FilePass':
9362                    if(!opts.enc) blob.l = 0;
9363                    opts.enc = val;
9364                    if(opts.WTF) console.error(val);
9365                    if(!options.password) throw new Error("File is password-protected");
9366                    if(val.Type !== 0) throw new Error("Encryption scheme unsupported");
9367                    if(!val.valid) throw new Error("Password is incorrect");
9368                    break;
9369                case 'WriteAccess': opts.lastuser = val; break;
9370                case 'FileSharing': break; //TODO
9371                case 'CodePage':
9372                    /* overrides based on test cases */
9373                    if(val === 0x5212) val = 1200;
9374                    else if(val === 0x8001) val = 1252;
9375                    opts.codepage = val;
9376                    set_cp(val);
9377                    break;
9378                case 'RRTabId': opts.rrtabid = val; break;
9379                case 'WinProtect': opts.winlocked = val; break;
9380                case 'Template': break; // TODO
9381                case 'RefreshAll': wb.opts.RefreshAll = val; break;
9382                case 'BookBool': break; // TODO
9383                case 'UsesELFs': /* if(val) console.error("Unsupported ELFs"); */ break;
9384                case 'MTRSettings': {
9385                    if(val[0] && val[1]) throw "Unsupported threads: " + val;
9386                } break; // TODO: actually support threads
9387                case 'CalcCount': wb.opts.CalcCount = val; break;
9388                case 'CalcDelta': wb.opts.CalcDelta = val; break;
9389                case 'CalcIter': wb.opts.CalcIter = val; break;
9390                case 'CalcMode': wb.opts.CalcMode = val; break;
9391                case 'CalcPrecision': wb.opts.CalcPrecision = val; break;
9392                case 'CalcSaveRecalc': wb.opts.CalcSaveRecalc = val; break;
9393                case 'CalcRefMode': opts.CalcRefMode = val; break; // TODO: implement R1C1
9394                case 'Uncalced': break;
9395                case 'ForceFullCalculation': wb.opts.FullCalc = val; break;
9396                case 'WsBool': break; // TODO
9397                case 'XF': XFs.push(val); break;
9398                case 'ExtSST': break; // TODO
9399                case 'BookExt': break; // TODO
9400                case 'RichTextStream': break;
9401                case 'BkHim': break;
9402
9403                case 'SupBook': supbooks[++sbc] = [val]; sbci = 0; break;
9404                case 'ExternName': supbooks[sbc][++sbci] = val; break;
9405                case 'Index': break; // TODO
9406                case 'Lbl': supbooks[0][++sbcli] = val; break;
9407                case 'ExternSheet': supbooks[sbc] = supbooks[sbc].concat(val); sbci += val.length; break;
9408
9409                case 'Protect': out["!protect"] = val; break; /* for sheet or book */
9410                case 'Password': if(val !== 0 && opts.WTF) console.error("Password verifier: " + val); break;
9411                case 'Prot4Rev': case 'Prot4RevPass': break; /*TODO: Revision Control*/
9412
9413                case 'BoundSheet8': {
9414                    Directory[val.pos] = val;
9415                    opts.snames.push(val.name);
9416                } break;
9417                case 'EOF': {
9418                    if(--file_depth) break;
9419                    if(range.e) {
9420                        out["!range"] = range;
9421                        if(range.e.r > 0 && range.e.c > 0) {
9422                            range.e.r--; range.e.c--;
9423                            out["!ref"] = encode_range(range);
9424                            range.e.r++; range.e.c++;
9425                        }
9426                        if(mergecells.length > 0) out["!merges"] = mergecells;
9427                        if(objects.length > 0) out["!objects"] = objects;
9428                    }
9429                    if(cur_sheet === "") Preamble = out; else Sheets[cur_sheet] = out;
9430                    out = {};
9431                } break;
9432                case 'BOF': {
9433                    if(opts.biff !== 8);
9434                    else if(val.BIFFVer === 0x0500) opts.biff = 5;
9435                    else if(val.BIFFVer === 0x0002) opts.biff = 2;
9436                    else if(val.BIFFVer === 0x0007) opts.biff = 2;
9437                    if(file_depth++) break;
9438                    cell_valid = true;
9439                    out = {};
9440                    if(opts.biff === 2) {
9441                        if(cur_sheet === "") cur_sheet = "Sheet1";
9442                        range = {s:{r:0,c:0},e:{r:0,c:0}};
9443                    }
9444                    else cur_sheet = (Directory[s] || {name:""}).name;
9445                    mergecells = [];
9446                    objects = [];
9447                } break;
9448                case 'Number': case 'BIFF2NUM': {
9449                    temp_val = {ixfe: val.ixfe, XF: XFs[val.ixfe], v:val.val, t:'n'};
9450                    if(temp_val.XF) safe_format_xf(temp_val, options, wb.opts.Date1904);
9451                    addcell({c:val.c, r:val.r}, temp_val, options);
9452                } break;
9453                case 'BoolErr': {
9454                    temp_val = {ixfe: val.ixfe, XF: XFs[val.ixfe], v:val.val, t:val.t};
9455                    if(temp_val.XF) safe_format_xf(temp_val, options, wb.opts.Date1904);
9456                    addcell({c:val.c, r:val.r}, temp_val, options);
9457                } break;
9458                case 'RK': {
9459                    temp_val = {ixfe: val.ixfe, XF: XFs[val.ixfe], v:val.rknum, t:'n'};
9460                    if(temp_val.XF) safe_format_xf(temp_val, options, wb.opts.Date1904);
9461                    addcell({c:val.c, r:val.r}, temp_val, options);
9462                } break;
9463                case 'MulRk': {
9464                    for(var j = val.c; j <= val.C; ++j) {
9465                        var ixfe = val.rkrec[j-val.c][0];
9466                        temp_val= {ixfe:ixfe, XF:XFs[ixfe], v:val.rkrec[j-val.c][1], t:'n'};
9467                        if(temp_val.XF) safe_format_xf(temp_val, options, wb.opts.Date1904);
9468                        addcell({c:j, r:val.r}, temp_val, options);
9469                    }
9470                } break;
9471                case 'Formula': {
9472                    switch(val.val) {
9473                        case 'String': last_formula = val; break;
9474                        case 'Array Formula': throw "Array Formula unsupported";
9475                        default:
9476                            temp_val = {v:val.val, ixfe:val.cell.ixfe, t:val.tt};
9477                            temp_val.XF = XFs[temp_val.ixfe];
9478                            if(options.cellFormula) temp_val.f = "="+stringify_formula(val.formula,range,val.cell,supbooks, opts);
9479                            if(temp_val.XF) safe_format_xf(temp_val, options, wb.opts.Date1904);
9480                            addcell(val.cell, temp_val, options);
9481                            last_formula = val;
9482                    }
9483                } break;
9484                case 'String': {
9485                    if(last_formula) {
9486                        last_formula.val = val;
9487                        temp_val = {v:last_formula.val, ixfe:last_formula.cell.ixfe, t:'s'};
9488                        temp_val.XF = XFs[temp_val.ixfe];
9489                        if(options.cellFormula) temp_val.f = "="+stringify_formula(last_formula.formula, range, last_formula.cell, supbooks, opts);
9490                        if(temp_val.XF) safe_format_xf(temp_val, options, wb.opts.Date1904);
9491                        addcell(last_formula.cell, temp_val, options);
9492                        last_formula = null;
9493                    }
9494                } break;
9495                case 'Array': {
9496                    array_formulae.push(val);
9497                } break;
9498                case 'ShrFmla': {
9499                    if(!cell_valid) break;
9500                    //if(options.cellFormula) out[last_cell].f = stringify_formula(val[0], range, lastcell, supbooks, opts);
9501                    /* TODO: capture range */
9502                    shared_formulae[encode_cell(last_formula.cell)]= val[0];
9503                } break;
9504                case 'LabelSst':
9505                    //temp_val={v:sst[val.isst].t, ixfe:val.ixfe, t:'s'};
9506                    temp_val=make_cell(sst[val.isst].t, val.ixfe, 's');
9507                    temp_val.XF = XFs[temp_val.ixfe];
9508                    if(temp_val.XF) safe_format_xf(temp_val, options, wb.opts.Date1904);
9509                    addcell({c:val.c, r:val.r}, temp_val, options);
9510                    break;
9511                case 'Label': case 'BIFF2STR':
9512                    /* Some writers erroneously write Label */
9513                    temp_val=make_cell(val.val, val.ixfe, 's');
9514                    temp_val.XF = XFs[temp_val.ixfe];
9515                    if(temp_val.XF) safe_format_xf(temp_val, options, wb.opts.Date1904);
9516                    addcell({c:val.c, r:val.r}, temp_val, options);
9517                    break;
9518                case 'Dimensions': {
9519                    if(file_depth === 1) range = val; /* TODO: stack */
9520                } break;
9521                case 'SST': {
9522                    sst = val;
9523                } break;
9524                case 'Format': { /* val = [id, fmt] */
9525                    SSF.load(val[1], val[0]);
9526                } break;
9527
9528                case 'MergeCells': mergecells = mergecells.concat(val); break;
9529
9530                case 'Obj': objects[val.cmo[0]] = opts.lastobj = val; break;
9531                case 'TxO': opts.lastobj.TxO = val; break;
9532
9533                case 'HLink': {
9534                    for(rngR = val[0].s.r; rngR <= val[0].e.r; ++rngR)
9535                        for(rngC = val[0].s.c; rngC <= val[0].e.c; ++rngC)
9536                            if(out[encode_cell({c:rngC,r:rngR})])
9537                                out[encode_cell({c:rngC,r:rngR})].l = val[1];
9538                } break;
9539                case 'HLinkTooltip': {
9540                    for(rngR = val[0].s.r; rngR <= val[0].e.r; ++rngR)
9541                        for(rngC = val[0].s.c; rngC <= val[0].e.c; ++rngC)
9542                            if(out[encode_cell({c:rngC,r:rngR})])
9543                                out[encode_cell({c:rngC,r:rngR})].l.tooltip = val[1];
9544                } break;
9545
9546                /* Comments */
9547                case 'Note': {
9548                    if(opts.biff <= 5 && opts.biff >= 2) break; /* TODO: BIFF5 */
9549                    cc = out[encode_cell(val[0])];
9550                    var noteobj = objects[val[2]];
9551                    if(!cc) break;
9552                    if(!cc.c) cc.c = [];
9553                    cmnt = {a:val[1],t:noteobj.TxO.t};
9554                    cc.c.push(cmnt);
9555                } break;
9556
9557                default: switch(R.n) { /* nested */
9558                case 'ClrtClient': break;
9559                case 'XFExt': update_xfext(XFs[val.ixfe], val.ext); break;
9560
9561                case 'NameCmt': break;
9562                case 'Header': break; // TODO
9563                case 'Footer': break; // TODO
9564                case 'HCenter': break; // TODO
9565                case 'VCenter': break; // TODO
9566                case 'Pls': break; // TODO
9567                case 'Setup': break; // TODO
9568                case 'DefColWidth': break; // TODO
9569                case 'GCW': break;
9570                case 'LHRecord': break;
9571                case 'ColInfo': break; // TODO
9572                case 'Row': break; // TODO
9573                case 'DBCell': break; // TODO
9574                case 'MulBlank': break; // TODO
9575                case 'EntExU2': break; // TODO
9576                case 'SxView': break; // TODO
9577                case 'Sxvd': break; // TODO
9578                case 'SXVI': break; // TODO
9579                case 'SXVDEx': break; // TODO
9580                case 'SxIvd': break; // TODO
9581                case 'SXDI': break; // TODO
9582                case 'SXLI': break; // TODO
9583                case 'SXEx': break; // TODO
9584                case 'QsiSXTag': break; // TODO
9585                case 'Selection': break;
9586                case 'Feat': break;
9587                case 'FeatHdr': case 'FeatHdr11': break;
9588                case 'Feature11': case 'Feature12': case 'List12': break;
9589                case 'Blank': break;
9590                case 'Country': country = val; break;
9591                case 'RecalcId': break;
9592                case 'DefaultRowHeight': case 'DxGCol': break; // TODO: htmlify
9593                case 'Fbi': case 'Fbi2': case 'GelFrame': break;
9594                case 'Font': break; // TODO
9595                case 'XFCRC': break; // TODO
9596                case 'Style': break; // TODO
9597                case 'StyleExt': break; // TODO
9598                case 'Palette': palette = val; break; // TODO
9599                case 'Theme': break; // TODO
9600                /* Protection */
9601                case 'ScenarioProtect': break;
9602                case 'ObjProtect': break;
9603
9604                /* Conditional Formatting */
9605                case 'CondFmt12': break;
9606
9607                /* Table */
9608                case 'Table': break; // TODO
9609                case 'TableStyles': break; // TODO
9610                case 'TableStyle': break; // TODO
9611                case 'TableStyleElement': break; // TODO
9612
9613                /* PivotTable */
9614                case 'SXStreamID': break; // TODO
9615                case 'SXVS': break; // TODO
9616                case 'DConRef': break; // TODO
9617                case 'SXAddl': break; // TODO
9618                case 'DConBin': break; // TODO
9619                case 'DConName': break; // TODO
9620                case 'SXPI': break; // TODO
9621                case 'SxFormat': break; // TODO
9622                case 'SxSelect': break; // TODO
9623                case 'SxRule': break; // TODO
9624                case 'SxFilt': break; // TODO
9625                case 'SxItm': break; // TODO
9626                case 'SxDXF': break; // TODO
9627
9628                /* Scenario Manager */
9629                case 'ScenMan': break;
9630
9631                /* Data Consolidation */
9632                case 'DCon': break;
9633
9634                /* Watched Cell */
9635                case 'CellWatch': break;
9636
9637                /* Print Settings */
9638                case 'PrintRowCol': break;
9639                case 'PrintGrid': break;
9640                case 'PrintSize': break;
9641
9642                case 'XCT': break;
9643                case 'CRN': break;
9644
9645                case 'Scl': {
9646                    //console.log("Zoom Level:", val[0]/val[1],val);
9647                } break;
9648                case 'SheetExt': {
9649
9650                } break;
9651                case 'SheetExtOptional': {
9652
9653                } break;
9654
9655                /* VBA */
9656                case 'ObNoMacros': {
9657
9658                } break;
9659                case 'ObProj': {
9660
9661                } break;
9662                case 'CodeName': {
9663
9664                } break;
9665                case 'GUIDTypeLib': {
9666
9667                } break;
9668
9669                case 'WOpt': break; // TODO: WTF?
9670                case 'PhoneticInfo': break;
9671
9672                case 'OleObjectSize': break;
9673
9674                /* Differential Formatting */
9675                case 'DXF': case 'DXFN': case 'DXFN12': case 'DXFN12List': case 'DXFN12NoCB': break;
9676
9677                /* Data Validation */
9678                case 'Dv': case 'DVal': break;
9679
9680                /* Data Series */
9681                case 'BRAI': case 'Series': case 'SeriesText': break;
9682
9683                /* Data Connection */
9684                case 'DConn': break;
9685                case 'DbOrParamQry': break;
9686                case 'DBQueryExt': break;
9687
9688                /* Formatting */
9689                case 'IFmtRecord': break;
9690                case 'CondFmt': case 'CF': case 'CF12': case 'CFEx': break;
9691
9692                /* Explicitly Ignored */
9693                case 'Excel9File': break;
9694                case 'Units': break;
9695                case 'InterfaceHdr': case 'Mms': case 'InterfaceEnd': case 'DSF': case 'BuiltInFnGroupCount':
9696                /* View Stuff */
9697                case 'Window1': case 'Window2': case 'HideObj': case 'GridSet': case 'Guts':
9698                case 'UserBView': case 'UserSViewBegin': case 'UserSViewEnd':
9699                case 'Pane': break;
9700                default: switch(R.n) { /* nested */
9701                /* Chart */
9702                case 'Dat':
9703                case 'Begin': case 'End':
9704                case 'StartBlock': case 'EndBlock':
9705                case 'Frame': case 'Area':
9706                case 'Axis': case 'AxisLine': case 'Tick': break;
9707                case 'AxesUsed':
9708                case 'CrtLayout12': case 'CrtLayout12A': case 'CrtLink': case 'CrtLine': case 'CrtMlFrt': case 'CrtMlFrtContinue': break;
9709                case 'LineFormat': case 'AreaFormat':
9710                case 'Chart': case 'Chart3d': case 'Chart3DBarShape': case 'ChartFormat': case 'ChartFrtInfo': break;
9711                case 'PlotArea': case 'PlotGrowth': break;
9712                case 'SeriesList': case 'SerParent': case 'SerAuxTrend': break;
9713                case 'DataFormat': case 'SerToCrt': case 'FontX': break;
9714                case 'CatSerRange': case 'AxcExt': case 'SerFmt': break;
9715                case 'ShtProps': break;
9716                case 'DefaultText': case 'Text': case 'CatLab': break;
9717                case 'DataLabExtContents': break;
9718                case 'Legend': case 'LegendException': break;
9719                case 'Pie': case 'Scatter': break;
9720                case 'PieFormat': case 'MarkerFormat': break;
9721                case 'StartObject': case 'EndObject': break;
9722                case 'AlRuns': case 'ObjectLink': break;
9723                case 'SIIndex': break;
9724                case 'AttachedLabel': case 'YMult': break;
9725
9726                /* Chart Group */
9727                case 'Line': case 'Bar': break;
9728                case 'Surf': break;
9729
9730                /* Axis Group */
9731                case 'AxisParent': break;
9732                case 'Pos': break;
9733                case 'ValueRange': break;
9734
9735                /* Pivot Chart */
9736                case 'SXViewEx9': break; // TODO
9737                case 'SXViewLink': break;
9738                case 'PivotChartBits': break;
9739                case 'SBaseRef': break;
9740                case 'TextPropsStream': break;
9741
9742                /* Chart Misc */
9743                case 'LnExt': break;
9744                case 'MkrExt': break;
9745                case 'CrtCoopt': break;
9746
9747                /* Query Table */
9748                case 'Qsi': case 'Qsif': case 'Qsir': case 'QsiSXTag': break;
9749                case 'TxtQry': break;
9750
9751                /* Filter */
9752                case 'FilterMode': break;
9753                case 'AutoFilter': case 'AutoFilterInfo': break;
9754                case 'AutoFilter12': break;
9755                case 'DropDownObjIds': break;
9756                case 'Sort': break;
9757                case 'SortData': break;
9758
9759                /* Drawing */
9760                case 'ShapePropsStream': break;
9761                case 'MsoDrawing': case 'MsoDrawingGroup': case 'MsoDrawingSelection': break;
9762                case 'ImData': break;
9763                /* Pub Stuff */
9764                case 'WebPub': case 'AutoWebPub':
9765
9766                /* Print Stuff */
9767                case 'RightMargin': case 'LeftMargin': case 'TopMargin': case 'BottomMargin':
9768                case 'HeaderFooter': case 'HFPicture': case 'PLV':
9769                case 'HorizontalPageBreaks': case 'VerticalPageBreaks':
9770                /* Behavioral */
9771                case 'Backup': case 'CompressPictures': case 'Compat12': break;
9772
9773                /* Should not Happen */
9774                case 'Continue': case 'ContinueFrt12': break;
9775
9776                /* Future Records */
9777                case 'FrtFontList': case 'FrtWrapper': break;
9778
9779                /* BIFF5 records */
9780                case 'ExternCount': break;
9781                case 'RString': break;
9782                case 'TabIdConf': case 'Radar': case 'RadarArea': case 'DropBar': case 'Intl': case 'CoordList': case 'SerAuxErrBar': break;
9783
9784                default: switch(R.n) { /* nested */
9785                /* Miscellaneous */
9786                case 'SCENARIO': case 'DConBin': case 'PicF': case 'DataLabExt':
9787                case 'Lel': case 'BopPop': case 'BopPopCustom': case 'RealTimeData':
9788                case 'Name': break;
9789                default: if(options.WTF) throw 'Unrecognized Record ' + R.n;
9790            }}}}
9791        } else blob.l += length;
9792    }
9793    var sheetnamesraw = opts.biff === 2 ? ['Sheet1'] : Object.keys(Directory).sort(function(a,b) { return Number(a) - Number(b); }).map(function(x){return Directory[x].name;});
9794    var sheetnames = sheetnamesraw.slice();
9795    wb.Directory=sheetnamesraw;
9796    wb.SheetNames=sheetnamesraw;
9797    if(!options.bookSheets) wb.Sheets=Sheets;
9798    wb.Preamble=Preamble;
9799    wb.Strings = sst;
9800    wb.SSF = SSF.get_table();
9801    if(opts.enc) wb.Encryption = opts.enc;
9802    wb.Metadata = {};
9803    if(country !== undefined) wb.Metadata.Country = country;
9804    return wb;
9805}
9806
9807function parse_xlscfb(cfb, options) {
9808if(!options) options = {};
9809fix_read_opts(options);
9810reset_cp();
9811var CompObj, Summary, Workbook;
9812if(cfb.find) {
9813    CompObj = cfb.find('!CompObj');
9814    Summary = cfb.find('!SummaryInformation');
9815    Workbook = cfb.find('/Workbook');
9816} else {
9817    prep_blob(cfb, 0);
9818    Workbook = {content: cfb};
9819}
9820
9821if(!Workbook) Workbook = cfb.find('/Book');
9822var CompObjP, SummaryP, WorkbookP;
9823
9824if(CompObj) CompObjP = parse_compobj(CompObj);
9825if(options.bookProps && !options.bookSheets) WorkbookP = {};
9826else {
9827    if(Workbook) WorkbookP = parse_workbook(Workbook.content, options, !!Workbook.find);
9828    else throw new Error("Cannot find Workbook stream");
9829}
9830
9831if(cfb.find) parse_props(cfb);
9832
9833var props = {};
9834for(var y in cfb.Summary) props[y] = cfb.Summary[y];
9835for(y in cfb.DocSummary) props[y] = cfb.DocSummary[y];
9836WorkbookP.Props = WorkbookP.Custprops = props; /* TODO: split up properties */
9837if(options.bookFiles) WorkbookP.cfb = cfb;
9838WorkbookP.CompObjP = CompObjP;
9839return WorkbookP;
9840}
9841
9842/* TODO: WTF */
9843function parse_props(cfb) {
9844    /* [MS-OSHARED] 2.3.3.2.2 Document Summary Information Property Set */
9845    var DSI = cfb.find('!DocumentSummaryInformation');
9846    if(DSI) try { cfb.DocSummary = parse_PropertySetStream(DSI, DocSummaryPIDDSI); } catch(e) {}
9847
9848    /* [MS-OSHARED] 2.3.3.2.1 Summary Information Property Set*/
9849    var SI = cfb.find('!SummaryInformation');
9850    if(SI) try { cfb.Summary = parse_PropertySetStream(SI, SummaryPIDSI); } catch(e) {}
9851}
9852
9853/* [MS-XLSB] 2.3 Record Enumeration */
9854var XLSBRecordEnum = {
9855    0x0000: { n:"BrtRowHdr", f:parse_BrtRowHdr },
9856    0x0001: { n:"BrtCellBlank", f:parse_BrtCellBlank },
9857    0x0002: { n:"BrtCellRk", f:parse_BrtCellRk },
9858    0x0003: { n:"BrtCellError", f:parse_BrtCellError },
9859    0x0004: { n:"BrtCellBool", f:parse_BrtCellBool },
9860    0x0005: { n:"BrtCellReal", f:parse_BrtCellReal },
9861    0x0006: { n:"BrtCellSt", f:parse_BrtCellSt },
9862    0x0007: { n:"BrtCellIsst", f:parse_BrtCellIsst },
9863    0x0008: { n:"BrtFmlaString", f:parse_BrtFmlaString },
9864    0x0009: { n:"BrtFmlaNum", f:parse_BrtFmlaNum },
9865    0x000A: { n:"BrtFmlaBool", f:parse_BrtFmlaBool },
9866    0x000B: { n:"BrtFmlaError", f:parse_BrtFmlaError },
9867    0x0010: { n:"BrtFRTArchID$", f:parse_BrtFRTArchID$ },
9868    0x0013: { n:"BrtSSTItem", f:parse_RichStr },
9869    0x0014: { n:"BrtPCDIMissing", f:parsenoop },
9870    0x0015: { n:"BrtPCDINumber", f:parsenoop },
9871    0x0016: { n:"BrtPCDIBoolean", f:parsenoop },
9872    0x0017: { n:"BrtPCDIError", f:parsenoop },
9873    0x0018: { n:"BrtPCDIString", f:parsenoop },
9874    0x0019: { n:"BrtPCDIDatetime", f:parsenoop },
9875    0x001A: { n:"BrtPCDIIndex", f:parsenoop },
9876    0x001B: { n:"BrtPCDIAMissing", f:parsenoop },
9877    0x001C: { n:"BrtPCDIANumber", f:parsenoop },
9878    0x001D: { n:"BrtPCDIABoolean", f:parsenoop },
9879    0x001E: { n:"BrtPCDIAError", f:parsenoop },
9880    0x001F: { n:"BrtPCDIAString", f:parsenoop },
9881    0x0020: { n:"BrtPCDIADatetime", f:parsenoop },
9882    0x0021: { n:"BrtPCRRecord", f:parsenoop },
9883    0x0022: { n:"BrtPCRRecordDt", f:parsenoop },
9884    0x0023: { n:"BrtFRTBegin", f:parsenoop },
9885    0x0024: { n:"BrtFRTEnd", f:parsenoop },
9886    0x0025: { n:"BrtACBegin", f:parsenoop },
9887    0x0026: { n:"BrtACEnd", f:parsenoop },
9888    0x0027: { n:"BrtName", f:parsenoop },
9889    0x0028: { n:"BrtIndexRowBlock", f:parsenoop },
9890    0x002A: { n:"BrtIndexBlock", f:parsenoop },
9891    0x002B: { n:"BrtFont", f:parse_BrtFont },
9892    0x002C: { n:"BrtFmt", f:parse_BrtFmt },
9893    0x002D: { n:"BrtFill", f:parsenoop },
9894    0x002E: { n:"BrtBorder", f:parsenoop },
9895    0x002F: { n:"BrtXF", f:parse_BrtXF },
9896    0x0030: { n:"BrtStyle", f:parsenoop },
9897    0x0031: { n:"BrtCellMeta", f:parsenoop },
9898    0x0032: { n:"BrtValueMeta", f:parsenoop },
9899    0x0033: { n:"BrtMdb", f:parsenoop },
9900    0x0034: { n:"BrtBeginFmd", f:parsenoop },
9901    0x0035: { n:"BrtEndFmd", f:parsenoop },
9902    0x0036: { n:"BrtBeginMdx", f:parsenoop },
9903    0x0037: { n:"BrtEndMdx", f:parsenoop },
9904    0x0038: { n:"BrtBeginMdxTuple", f:parsenoop },
9905    0x0039: { n:"BrtEndMdxTuple", f:parsenoop },
9906    0x003A: { n:"BrtMdxMbrIstr", f:parsenoop },
9907    0x003B: { n:"BrtStr", f:parsenoop },
9908    0x003C: { n:"BrtColInfo", f:parsenoop },
9909    0x003E: { n:"BrtCellRString", f:parsenoop },
9910    0x003F: { n:"BrtCalcChainItem$", f:parse_BrtCalcChainItem$ },
9911    0x0040: { n:"BrtDVal", f:parsenoop },
9912    0x0041: { n:"BrtSxvcellNum", f:parsenoop },
9913    0x0042: { n:"BrtSxvcellStr", f:parsenoop },
9914    0x0043: { n:"BrtSxvcellBool", f:parsenoop },
9915    0x0044: { n:"BrtSxvcellErr", f:parsenoop },
9916    0x0045: { n:"BrtSxvcellDate", f:parsenoop },
9917    0x0046: { n:"BrtSxvcellNil", f:parsenoop },
9918    0x0080: { n:"BrtFileVersion", f:parsenoop },
9919    0x0081: { n:"BrtBeginSheet", f:parsenoop },
9920    0x0082: { n:"BrtEndSheet", f:parsenoop },
9921    0x0083: { n:"BrtBeginBook", f:parsenoop, p:0 },
9922    0x0084: { n:"BrtEndBook", f:parsenoop },
9923    0x0085: { n:"BrtBeginWsViews", f:parsenoop },
9924    0x0086: { n:"BrtEndWsViews", f:parsenoop },
9925    0x0087: { n:"BrtBeginBookViews", f:parsenoop },
9926    0x0088: { n:"BrtEndBookViews", f:parsenoop },
9927    0x0089: { n:"BrtBeginWsView", f:parsenoop },
9928    0x008A: { n:"BrtEndWsView", f:parsenoop },
9929    0x008B: { n:"BrtBeginCsViews", f:parsenoop },
9930    0x008C: { n:"BrtEndCsViews", f:parsenoop },
9931    0x008D: { n:"BrtBeginCsView", f:parsenoop },
9932    0x008E: { n:"BrtEndCsView", f:parsenoop },
9933    0x008F: { n:"BrtBeginBundleShs", f:parsenoop },
9934    0x0090: { n:"BrtEndBundleShs", f:parsenoop },
9935    0x0091: { n:"BrtBeginSheetData", f:parsenoop },
9936    0x0092: { n:"BrtEndSheetData", f:parsenoop },
9937    0x0093: { n:"BrtWsProp", f:parse_BrtWsProp },
9938    0x0094: { n:"BrtWsDim", f:parse_BrtWsDim, p:16 },
9939    0x0097: { n:"BrtPane", f:parsenoop },
9940    0x0098: { n:"BrtSel", f:parsenoop },
9941    0x0099: { n:"BrtWbProp", f:parse_BrtWbProp },
9942    0x009A: { n:"BrtWbFactoid", f:parsenoop },
9943    0x009B: { n:"BrtFileRecover", f:parsenoop },
9944    0x009C: { n:"BrtBundleSh", f:parse_BrtBundleSh },
9945    0x009D: { n:"BrtCalcProp", f:parsenoop },
9946    0x009E: { n:"BrtBookView", f:parsenoop },
9947    0x009F: { n:"BrtBeginSst", f:parse_BrtBeginSst },
9948    0x00A0: { n:"BrtEndSst", f:parsenoop },
9949    0x00A1: { n:"BrtBeginAFilter", f:parsenoop },
9950    0x00A2: { n:"BrtEndAFilter", f:parsenoop },
9951    0x00A3: { n:"BrtBeginFilterColumn", f:parsenoop },
9952    0x00A4: { n:"BrtEndFilterColumn", f:parsenoop },
9953    0x00A5: { n:"BrtBeginFilters", f:parsenoop },
9954    0x00A6: { n:"BrtEndFilters", f:parsenoop },
9955    0x00A7: { n:"BrtFilter", f:parsenoop },
9956    0x00A8: { n:"BrtColorFilter", f:parsenoop },
9957    0x00A9: { n:"BrtIconFilter", f:parsenoop },
9958    0x00AA: { n:"BrtTop10Filter", f:parsenoop },
9959    0x00AB: { n:"BrtDynamicFilter", f:parsenoop },
9960    0x00AC: { n:"BrtBeginCustomFilters", f:parsenoop },
9961    0x00AD: { n:"BrtEndCustomFilters", f:parsenoop },
9962    0x00AE: { n:"BrtCustomFilter", f:parsenoop },
9963    0x00AF: { n:"BrtAFilterDateGroupItem", f:parsenoop },
9964    0x00B0: { n:"BrtMergeCell", f:parse_BrtMergeCell },
9965    0x00B1: { n:"BrtBeginMergeCells", f:parsenoop },
9966    0x00B2: { n:"BrtEndMergeCells", f:parsenoop },
9967    0x00B3: { n:"BrtBeginPivotCacheDef", f:parsenoop },
9968    0x00B4: { n:"BrtEndPivotCacheDef", f:parsenoop },
9969    0x00B5: { n:"BrtBeginPCDFields", f:parsenoop },
9970    0x00B6: { n:"BrtEndPCDFields", f:parsenoop },
9971    0x00B7: { n:"BrtBeginPCDField", f:parsenoop },
9972    0x00B8: { n:"BrtEndPCDField", f:parsenoop },
9973    0x00B9: { n:"BrtBeginPCDSource", f:parsenoop },
9974    0x00BA: { n:"BrtEndPCDSource", f:parsenoop },
9975    0x00BB: { n:"BrtBeginPCDSRange", f:parsenoop },
9976    0x00BC: { n:"BrtEndPCDSRange", f:parsenoop },
9977    0x00BD: { n:"BrtBeginPCDFAtbl", f:parsenoop },
9978    0x00BE: { n:"BrtEndPCDFAtbl", f:parsenoop },
9979    0x00BF: { n:"BrtBeginPCDIRun", f:parsenoop },
9980    0x00C0: { n:"BrtEndPCDIRun", f:parsenoop },
9981    0x00C1: { n:"BrtBeginPivotCacheRecords", f:parsenoop },
9982    0x00C2: { n:"BrtEndPivotCacheRecords", f:parsenoop },
9983    0x00C3: { n:"BrtBeginPCDHierarchies", f:parsenoop },
9984    0x00C4: { n:"BrtEndPCDHierarchies", f:parsenoop },
9985    0x00C5: { n:"BrtBeginPCDHierarchy", f:parsenoop },
9986    0x00C6: { n:"BrtEndPCDHierarchy", f:parsenoop },
9987    0x00C7: { n:"BrtBeginPCDHFieldsUsage", f:parsenoop },
9988    0x00C8: { n:"BrtEndPCDHFieldsUsage", f:parsenoop },
9989    0x00C9: { n:"BrtBeginExtConnection", f:parsenoop },
9990    0x00CA: { n:"BrtEndExtConnection", f:parsenoop },
9991    0x00CB: { n:"BrtBeginECDbProps", f:parsenoop },
9992    0x00CC: { n:"BrtEndECDbProps", f:parsenoop },
9993    0x00CD: { n:"BrtBeginECOlapProps", f:parsenoop },
9994    0x00CE: { n:"BrtEndECOlapProps", f:parsenoop },
9995    0x00CF: { n:"BrtBeginPCDSConsol", f:parsenoop },
9996    0x00D0: { n:"BrtEndPCDSConsol", f:parsenoop },
9997    0x00D1: { n:"BrtBeginPCDSCPages", f:parsenoop },
9998    0x00D2: { n:"BrtEndPCDSCPages", f:parsenoop },
9999    0x00D3: { n:"BrtBeginPCDSCPage", f:parsenoop },
10000    0x00D4: { n:"BrtEndPCDSCPage", f:parsenoop },
10001    0x00D5: { n:"BrtBeginPCDSCPItem", f:parsenoop },
10002    0x00D6: { n:"BrtEndPCDSCPItem", f:parsenoop },
10003    0x00D7: { n:"BrtBeginPCDSCSets", f:parsenoop },
10004    0x00D8: { n:"BrtEndPCDSCSets", f:parsenoop },
10005    0x00D9: { n:"BrtBeginPCDSCSet", f:parsenoop },
10006    0x00DA: { n:"BrtEndPCDSCSet", f:parsenoop },
10007    0x00DB: { n:"BrtBeginPCDFGroup", f:parsenoop },
10008    0x00DC: { n:"BrtEndPCDFGroup", f:parsenoop },
10009    0x00DD: { n:"BrtBeginPCDFGItems", f:parsenoop },
10010    0x00DE: { n:"BrtEndPCDFGItems", f:parsenoop },
10011    0x00DF: { n:"BrtBeginPCDFGRange", f:parsenoop },
10012    0x00E0: { n:"BrtEndPCDFGRange", f:parsenoop },
10013    0x00E1: { n:"BrtBeginPCDFGDiscrete", f:parsenoop },
10014    0x00E2: { n:"BrtEndPCDFGDiscrete", f:parsenoop },
10015    0x00E3: { n:"BrtBeginPCDSDTupleCache", f:parsenoop },
10016    0x00E4: { n:"BrtEndPCDSDTupleCache", f:parsenoop },
10017    0x00E5: { n:"BrtBeginPCDSDTCEntries", f:parsenoop },
10018    0x00E6: { n:"BrtEndPCDSDTCEntries", f:parsenoop },
10019    0x00E7: { n:"BrtBeginPCDSDTCEMembers", f:parsenoop },
10020    0x00E8: { n:"BrtEndPCDSDTCEMembers", f:parsenoop },
10021    0x00E9: { n:"BrtBeginPCDSDTCEMember", f:parsenoop },
10022    0x00EA: { n:"BrtEndPCDSDTCEMember", f:parsenoop },
10023    0x00EB: { n:"BrtBeginPCDSDTCQueries", f:parsenoop },
10024    0x00EC: { n:"BrtEndPCDSDTCQueries", f:parsenoop },
10025    0x00ED: { n:"BrtBeginPCDSDTCQuery", f:parsenoop },
10026    0x00EE: { n:"BrtEndPCDSDTCQuery", f:parsenoop },
10027    0x00EF: { n:"BrtBeginPCDSDTCSets", f:parsenoop },
10028    0x00F0: { n:"BrtEndPCDSDTCSets", f:parsenoop },
10029    0x00F1: { n:"BrtBeginPCDSDTCSet", f:parsenoop },
10030    0x00F2: { n:"BrtEndPCDSDTCSet", f:parsenoop },
10031    0x00F3: { n:"BrtBeginPCDCalcItems", f:parsenoop },
10032    0x00F4: { n:"BrtEndPCDCalcItems", f:parsenoop },
10033    0x00F5: { n:"BrtBeginPCDCalcItem", f:parsenoop },
10034    0x00F6: { n:"BrtEndPCDCalcItem", f:parsenoop },
10035    0x00F7: { n:"BrtBeginPRule", f:parsenoop },
10036    0x00F8: { n:"BrtEndPRule", f:parsenoop },
10037    0x00F9: { n:"BrtBeginPRFilters", f:parsenoop },
10038    0x00FA: { n:"BrtEndPRFilters", f:parsenoop },
10039    0x00FB: { n:"BrtBeginPRFilter", f:parsenoop },
10040    0x00FC: { n:"BrtEndPRFilter", f:parsenoop },
10041    0x00FD: { n:"BrtBeginPNames", f:parsenoop },
10042    0x00FE: { n:"BrtEndPNames", f:parsenoop },
10043    0x00FF: { n:"BrtBeginPName", f:parsenoop },
10044    0x0100: { n:"BrtEndPName", f:parsenoop },
10045    0x0101: { n:"BrtBeginPNPairs", f:parsenoop },
10046    0x0102: { n:"BrtEndPNPairs", f:parsenoop },
10047    0x0103: { n:"BrtBeginPNPair", f:parsenoop },
10048    0x0104: { n:"BrtEndPNPair", f:parsenoop },
10049    0x0105: { n:"BrtBeginECWebProps", f:parsenoop },
10050    0x0106: { n:"BrtEndECWebProps", f:parsenoop },
10051    0x0107: { n:"BrtBeginEcWpTables", f:parsenoop },
10052    0x0108: { n:"BrtEndECWPTables", f:parsenoop },
10053    0x0109: { n:"BrtBeginECParams", f:parsenoop },
10054    0x010A: { n:"BrtEndECParams", f:parsenoop },
10055    0x010B: { n:"BrtBeginECParam", f:parsenoop },
10056    0x010C: { n:"BrtEndECParam", f:parsenoop },
10057    0x010D: { n:"BrtBeginPCDKPIs", f:parsenoop },
10058    0x010E: { n:"BrtEndPCDKPIs", f:parsenoop },
10059    0x010F: { n:"BrtBeginPCDKPI", f:parsenoop },
10060    0x0110: { n:"BrtEndPCDKPI", f:parsenoop },
10061    0x0111: { n:"BrtBeginDims", f:parsenoop },
10062    0x0112: { n:"BrtEndDims", f:parsenoop },
10063    0x0113: { n:"BrtBeginDim", f:parsenoop },
10064    0x0114: { n:"BrtEndDim", f:parsenoop },
10065    0x0115: { n:"BrtIndexPartEnd", f:parsenoop },
10066    0x0116: { n:"BrtBeginStyleSheet", f:parsenoop },
10067    0x0117: { n:"BrtEndStyleSheet", f:parsenoop },
10068    0x0118: { n:"BrtBeginSXView", f:parsenoop },
10069    0x0119: { n:"BrtEndSXVI", f:parsenoop },
10070    0x011A: { n:"BrtBeginSXVI", f:parsenoop },
10071    0x011B: { n:"BrtBeginSXVIs", f:parsenoop },
10072    0x011C: { n:"BrtEndSXVIs", f:parsenoop },
10073    0x011D: { n:"BrtBeginSXVD", f:parsenoop },
10074    0x011E: { n:"BrtEndSXVD", f:parsenoop },
10075    0x011F: { n:"BrtBeginSXVDs", f:parsenoop },
10076    0x0120: { n:"BrtEndSXVDs", f:parsenoop },
10077    0x0121: { n:"BrtBeginSXPI", f:parsenoop },
10078    0x0122: { n:"BrtEndSXPI", f:parsenoop },
10079    0x0123: { n:"BrtBeginSXPIs", f:parsenoop },
10080    0x0124: { n:"BrtEndSXPIs", f:parsenoop },
10081    0x0125: { n:"BrtBeginSXDI", f:parsenoop },
10082    0x0126: { n:"BrtEndSXDI", f:parsenoop },
10083    0x0127: { n:"BrtBeginSXDIs", f:parsenoop },
10084    0x0128: { n:"BrtEndSXDIs", f:parsenoop },
10085    0x0129: { n:"BrtBeginSXLI", f:parsenoop },
10086    0x012A: { n:"BrtEndSXLI", f:parsenoop },
10087    0x012B: { n:"BrtBeginSXLIRws", f:parsenoop },
10088    0x012C: { n:"BrtEndSXLIRws", f:parsenoop },
10089    0x012D: { n:"BrtBeginSXLICols", f:parsenoop },
10090    0x012E: { n:"BrtEndSXLICols", f:parsenoop },
10091    0x012F: { n:"BrtBeginSXFormat", f:parsenoop },
10092    0x0130: { n:"BrtEndSXFormat", f:parsenoop },
10093    0x0131: { n:"BrtBeginSXFormats", f:parsenoop },
10094    0x0132: { n:"BrtEndSxFormats", f:parsenoop },
10095    0x0133: { n:"BrtBeginSxSelect", f:parsenoop },
10096    0x0134: { n:"BrtEndSxSelect", f:parsenoop },
10097    0x0135: { n:"BrtBeginISXVDRws", f:parsenoop },
10098    0x0136: { n:"BrtEndISXVDRws", f:parsenoop },
10099    0x0137: { n:"BrtBeginISXVDCols", f:parsenoop },
10100    0x0138: { n:"BrtEndISXVDCols", f:parsenoop },
10101    0x0139: { n:"BrtEndSXLocation", f:parsenoop },
10102    0x013A: { n:"BrtBeginSXLocation", f:parsenoop },
10103    0x013B: { n:"BrtEndSXView", f:parsenoop },
10104    0x013C: { n:"BrtBeginSXTHs", f:parsenoop },
10105    0x013D: { n:"BrtEndSXTHs", f:parsenoop },
10106    0x013E: { n:"BrtBeginSXTH", f:parsenoop },
10107    0x013F: { n:"BrtEndSXTH", f:parsenoop },
10108    0x0140: { n:"BrtBeginISXTHRws", f:parsenoop },
10109    0x0141: { n:"BrtEndISXTHRws", f:parsenoop },
10110    0x0142: { n:"BrtBeginISXTHCols", f:parsenoop },
10111    0x0143: { n:"BrtEndISXTHCols", f:parsenoop },
10112    0x0144: { n:"BrtBeginSXTDMPS", f:parsenoop },
10113    0x0145: { n:"BrtEndSXTDMPs", f:parsenoop },
10114    0x0146: { n:"BrtBeginSXTDMP", f:parsenoop },
10115    0x0147: { n:"BrtEndSXTDMP", f:parsenoop },
10116    0x0148: { n:"BrtBeginSXTHItems", f:parsenoop },
10117    0x0149: { n:"BrtEndSXTHItems", f:parsenoop },
10118    0x014A: { n:"BrtBeginSXTHItem", f:parsenoop },
10119    0x014B: { n:"BrtEndSXTHItem", f:parsenoop },
10120    0x014C: { n:"BrtBeginMetadata", f:parsenoop },
10121    0x014D: { n:"BrtEndMetadata", f:parsenoop },
10122    0x014E: { n:"BrtBeginEsmdtinfo", f:parsenoop },
10123    0x014F: { n:"BrtMdtinfo", f:parsenoop },
10124    0x0150: { n:"BrtEndEsmdtinfo", f:parsenoop },
10125    0x0151: { n:"BrtBeginEsmdb", f:parsenoop },
10126    0x0152: { n:"BrtEndEsmdb", f:parsenoop },
10127    0x0153: { n:"BrtBeginEsfmd", f:parsenoop },
10128    0x0154: { n:"BrtEndEsfmd", f:parsenoop },
10129    0x0155: { n:"BrtBeginSingleCells", f:parsenoop },
10130    0x0156: { n:"BrtEndSingleCells", f:parsenoop },
10131    0x0157: { n:"BrtBeginList", f:parsenoop },
10132    0x0158: { n:"BrtEndList", f:parsenoop },
10133    0x0159: { n:"BrtBeginListCols", f:parsenoop },
10134    0x015A: { n:"BrtEndListCols", f:parsenoop },
10135    0x015B: { n:"BrtBeginListCol", f:parsenoop },
10136    0x015C: { n:"BrtEndListCol", f:parsenoop },
10137    0x015D: { n:"BrtBeginListXmlCPr", f:parsenoop },
10138    0x015E: { n:"BrtEndListXmlCPr", f:parsenoop },
10139    0x015F: { n:"BrtListCCFmla", f:parsenoop },
10140    0x0160: { n:"BrtListTrFmla", f:parsenoop },
10141    0x0161: { n:"BrtBeginExternals", f:parsenoop },
10142    0x0162: { n:"BrtEndExternals", f:parsenoop },
10143    0x0163: { n:"BrtSupBookSrc", f:parsenoop },
10144    0x0165: { n:"BrtSupSelf", f:parsenoop },
10145    0x0166: { n:"BrtSupSame", f:parsenoop },
10146    0x0167: { n:"BrtSupTabs", f:parsenoop },
10147    0x0168: { n:"BrtBeginSupBook", f:parsenoop },
10148    0x0169: { n:"BrtPlaceholderName", f:parsenoop },
10149    0x016A: { n:"BrtExternSheet", f:parsenoop },
10150    0x016B: { n:"BrtExternTableStart", f:parsenoop },
10151    0x016C: { n:"BrtExternTableEnd", f:parsenoop },
10152    0x016E: { n:"BrtExternRowHdr", f:parsenoop },
10153    0x016F: { n:"BrtExternCellBlank", f:parsenoop },
10154    0x0170: { n:"BrtExternCellReal", f:parsenoop },
10155    0x0171: { n:"BrtExternCellBool", f:parsenoop },
10156    0x0172: { n:"BrtExternCellError", f:parsenoop },
10157    0x0173: { n:"BrtExternCellString", f:parsenoop },
10158    0x0174: { n:"BrtBeginEsmdx", f:parsenoop },
10159    0x0175: { n:"BrtEndEsmdx", f:parsenoop },
10160    0x0176: { n:"BrtBeginMdxSet", f:parsenoop },
10161    0x0177: { n:"BrtEndMdxSet", f:parsenoop },
10162    0x0178: { n:"BrtBeginMdxMbrProp", f:parsenoop },
10163    0x0179: { n:"BrtEndMdxMbrProp", f:parsenoop },
10164    0x017A: { n:"BrtBeginMdxKPI", f:parsenoop },
10165    0x017B: { n:"BrtEndMdxKPI", f:parsenoop },
10166    0x017C: { n:"BrtBeginEsstr", f:parsenoop },
10167    0x017D: { n:"BrtEndEsstr", f:parsenoop },
10168    0x017E: { n:"BrtBeginPRFItem", f:parsenoop },
10169    0x017F: { n:"BrtEndPRFItem", f:parsenoop },
10170    0x0180: { n:"BrtBeginPivotCacheIDs", f:parsenoop },
10171    0x0181: { n:"BrtEndPivotCacheIDs", f:parsenoop },
10172    0x0182: { n:"BrtBeginPivotCacheID", f:parsenoop },
10173    0x0183: { n:"BrtEndPivotCacheID", f:parsenoop },
10174    0x0184: { n:"BrtBeginISXVIs", f:parsenoop },
10175    0x0185: { n:"BrtEndISXVIs", f:parsenoop },
10176    0x0186: { n:"BrtBeginColInfos", f:parsenoop },
10177    0x0187: { n:"BrtEndColInfos", f:parsenoop },
10178    0x0188: { n:"BrtBeginRwBrk", f:parsenoop },
10179    0x0189: { n:"BrtEndRwBrk", f:parsenoop },
10180    0x018A: { n:"BrtBeginColBrk", f:parsenoop },
10181    0x018B: { n:"BrtEndColBrk", f:parsenoop },
10182    0x018C: { n:"BrtBrk", f:parsenoop },
10183    0x018D: { n:"BrtUserBookView", f:parsenoop },
10184    0x018E: { n:"BrtInfo", f:parsenoop },
10185    0x018F: { n:"BrtCUsr", f:parsenoop },
10186    0x0190: { n:"BrtUsr", f:parsenoop },
10187    0x0191: { n:"BrtBeginUsers", f:parsenoop },
10188    0x0193: { n:"BrtEOF", f:parsenoop },
10189    0x0194: { n:"BrtUCR", f:parsenoop },
10190    0x0195: { n:"BrtRRInsDel", f:parsenoop },
10191    0x0196: { n:"BrtRREndInsDel", f:parsenoop },
10192    0x0197: { n:"BrtRRMove", f:parsenoop },
10193    0x0198: { n:"BrtRREndMove", f:parsenoop },
10194    0x0199: { n:"BrtRRChgCell", f:parsenoop },
10195    0x019A: { n:"BrtRREndChgCell", f:parsenoop },
10196    0x019B: { n:"BrtRRHeader", f:parsenoop },
10197    0x019C: { n:"BrtRRUserView", f:parsenoop },
10198    0x019D: { n:"BrtRRRenSheet", f:parsenoop },
10199    0x019E: { n:"BrtRRInsertSh", f:parsenoop },
10200    0x019F: { n:"BrtRRDefName", f:parsenoop },
10201    0x01A0: { n:"BrtRRNote", f:parsenoop },
10202    0x01A1: { n:"BrtRRConflict", f:parsenoop },
10203    0x01A2: { n:"BrtRRTQSIF", f:parsenoop },
10204    0x01A3: { n:"BrtRRFormat", f:parsenoop },
10205    0x01A4: { n:"BrtRREndFormat", f:parsenoop },
10206    0x01A5: { n:"BrtRRAutoFmt", f:parsenoop },
10207    0x01A6: { n:"BrtBeginUserShViews", f:parsenoop },
10208    0x01A7: { n:"BrtBeginUserShView", f:parsenoop },
10209    0x01A8: { n:"BrtEndUserShView", f:parsenoop },
10210    0x01A9: { n:"BrtEndUserShViews", f:parsenoop },
10211    0x01AA: { n:"BrtArrFmla", f:parsenoop },
10212    0x01AB: { n:"BrtShrFmla", f:parsenoop },
10213    0x01AC: { n:"BrtTable", f:parsenoop },
10214    0x01AD: { n:"BrtBeginExtConnections", f:parsenoop },
10215    0x01AE: { n:"BrtEndExtConnections", f:parsenoop },
10216    0x01AF: { n:"BrtBeginPCDCalcMems", f:parsenoop },
10217    0x01B0: { n:"BrtEndPCDCalcMems", f:parsenoop },
10218    0x01B1: { n:"BrtBeginPCDCalcMem", f:parsenoop },
10219    0x01B2: { n:"BrtEndPCDCalcMem", f:parsenoop },
10220    0x01B3: { n:"BrtBeginPCDHGLevels", f:parsenoop },
10221    0x01B4: { n:"BrtEndPCDHGLevels", f:parsenoop },
10222    0x01B5: { n:"BrtBeginPCDHGLevel", f:parsenoop },
10223    0x01B6: { n:"BrtEndPCDHGLevel", f:parsenoop },
10224    0x01B7: { n:"BrtBeginPCDHGLGroups", f:parsenoop },
10225    0x01B8: { n:"BrtEndPCDHGLGroups", f:parsenoop },
10226    0x01B9: { n:"BrtBeginPCDHGLGroup", f:parsenoop },
10227    0x01BA: { n:"BrtEndPCDHGLGroup", f:parsenoop },
10228    0x01BB: { n:"BrtBeginPCDHGLGMembers", f:parsenoop },
10229    0x01BC: { n:"BrtEndPCDHGLGMembers", f:parsenoop },
10230    0x01BD: { n:"BrtBeginPCDHGLGMember", f:parsenoop },
10231    0x01BE: { n:"BrtEndPCDHGLGMember", f:parsenoop },
10232    0x01BF: { n:"BrtBeginQSI", f:parsenoop },
10233    0x01C0: { n:"BrtEndQSI", f:parsenoop },
10234    0x01C1: { n:"BrtBeginQSIR", f:parsenoop },
10235    0x01C2: { n:"BrtEndQSIR", f:parsenoop },
10236    0x01C3: { n:"BrtBeginDeletedNames", f:parsenoop },
10237    0x01C4: { n:"BrtEndDeletedNames", f:parsenoop },
10238    0x01C5: { n:"BrtBeginDeletedName", f:parsenoop },
10239    0x01C6: { n:"BrtEndDeletedName", f:parsenoop },
10240    0x01C7: { n:"BrtBeginQSIFs", f:parsenoop },
10241    0x01C8: { n:"BrtEndQSIFs", f:parsenoop },
10242    0x01C9: { n:"BrtBeginQSIF", f:parsenoop },
10243    0x01CA: { n:"BrtEndQSIF", f:parsenoop },
10244    0x01CB: { n:"BrtBeginAutoSortScope", f:parsenoop },
10245    0x01CC: { n:"BrtEndAutoSortScope", f:parsenoop },
10246    0x01CD: { n:"BrtBeginConditionalFormatting", f:parsenoop },
10247    0x01CE: { n:"BrtEndConditionalFormatting", f:parsenoop },
10248    0x01CF: { n:"BrtBeginCFRule", f:parsenoop },
10249    0x01D0: { n:"BrtEndCFRule", f:parsenoop },
10250    0x01D1: { n:"BrtBeginIconSet", f:parsenoop },
10251    0x01D2: { n:"BrtEndIconSet", f:parsenoop },
10252    0x01D3: { n:"BrtBeginDatabar", f:parsenoop },
10253    0x01D4: { n:"BrtEndDatabar", f:parsenoop },
10254    0x01D5: { n:"BrtBeginColorScale", f:parsenoop },
10255    0x01D6: { n:"BrtEndColorScale", f:parsenoop },
10256    0x01D7: { n:"BrtCFVO", f:parsenoop },
10257    0x01D8: { n:"BrtExternValueMeta", f:parsenoop },
10258    0x01D9: { n:"BrtBeginColorPalette", f:parsenoop },
10259    0x01DA: { n:"BrtEndColorPalette", f:parsenoop },
10260    0x01DB: { n:"BrtIndexedColor", f:parsenoop },
10261    0x01DC: { n:"BrtMargins", f:parsenoop },
10262    0x01DD: { n:"BrtPrintOptions", f:parsenoop },
10263    0x01DE: { n:"BrtPageSetup", f:parsenoop },
10264    0x01DF: { n:"BrtBeginHeaderFooter", f:parsenoop },
10265    0x01E0: { n:"BrtEndHeaderFooter", f:parsenoop },
10266    0x01E1: { n:"BrtBeginSXCrtFormat", f:parsenoop },
10267    0x01E2: { n:"BrtEndSXCrtFormat", f:parsenoop },
10268    0x01E3: { n:"BrtBeginSXCrtFormats", f:parsenoop },
10269    0x01E4: { n:"BrtEndSXCrtFormats", f:parsenoop },
10270    0x01E5: { n:"BrtWsFmtInfo", f:parsenoop },
10271    0x01E6: { n:"BrtBeginMgs", f:parsenoop },
10272    0x01E7: { n:"BrtEndMGs", f:parsenoop },
10273    0x01E8: { n:"BrtBeginMGMaps", f:parsenoop },
10274    0x01E9: { n:"BrtEndMGMaps", f:parsenoop },
10275    0x01EA: { n:"BrtBeginMG", f:parsenoop },
10276    0x01EB: { n:"BrtEndMG", f:parsenoop },
10277    0x01EC: { n:"BrtBeginMap", f:parsenoop },
10278    0x01ED: { n:"BrtEndMap", f:parsenoop },
10279    0x01EE: { n:"BrtHLink", f:parse_BrtHLink },
10280    0x01EF: { n:"BrtBeginDCon", f:parsenoop },
10281    0x01F0: { n:"BrtEndDCon", f:parsenoop },
10282    0x01F1: { n:"BrtBeginDRefs", f:parsenoop },
10283    0x01F2: { n:"BrtEndDRefs", f:parsenoop },
10284    0x01F3: { n:"BrtDRef", f:parsenoop },
10285    0x01F4: { n:"BrtBeginScenMan", f:parsenoop },
10286    0x01F5: { n:"BrtEndScenMan", f:parsenoop },
10287    0x01F6: { n:"BrtBeginSct", f:parsenoop },
10288    0x01F7: { n:"BrtEndSct", f:parsenoop },
10289    0x01F8: { n:"BrtSlc", f:parsenoop },
10290    0x01F9: { n:"BrtBeginDXFs", f:parsenoop },
10291    0x01FA: { n:"BrtEndDXFs", f:parsenoop },
10292    0x01FB: { n:"BrtDXF", f:parsenoop },
10293    0x01FC: { n:"BrtBeginTableStyles", f:parsenoop },
10294    0x01FD: { n:"BrtEndTableStyles", f:parsenoop },
10295    0x01FE: { n:"BrtBeginTableStyle", f:parsenoop },
10296    0x01FF: { n:"BrtEndTableStyle", f:parsenoop },
10297    0x0200: { n:"BrtTableStyleElement", f:parsenoop },
10298    0x0201: { n:"BrtTableStyleClient", f:parsenoop },
10299    0x0202: { n:"BrtBeginVolDeps", f:parsenoop },
10300    0x0203: { n:"BrtEndVolDeps", f:parsenoop },
10301    0x0204: { n:"BrtBeginVolType", f:parsenoop },
10302    0x0205: { n:"BrtEndVolType", f:parsenoop },
10303    0x0206: { n:"BrtBeginVolMain", f:parsenoop },
10304    0x0207: { n:"BrtEndVolMain", f:parsenoop },
10305    0x0208: { n:"BrtBeginVolTopic", f:parsenoop },
10306    0x0209: { n:"BrtEndVolTopic", f:parsenoop },
10307    0x020A: { n:"BrtVolSubtopic", f:parsenoop },
10308    0x020B: { n:"BrtVolRef", f:parsenoop },
10309    0x020C: { n:"BrtVolNum", f:parsenoop },
10310    0x020D: { n:"BrtVolErr", f:parsenoop },
10311    0x020E: { n:"BrtVolStr", f:parsenoop },
10312    0x020F: { n:"BrtVolBool", f:parsenoop },
10313    0x0210: { n:"BrtBeginCalcChain$", f:parsenoop },
10314    0x0211: { n:"BrtEndCalcChain$", f:parsenoop },
10315    0x0212: { n:"BrtBeginSortState", f:parsenoop },
10316    0x0213: { n:"BrtEndSortState", f:parsenoop },
10317    0x0214: { n:"BrtBeginSortCond", f:parsenoop },
10318    0x0215: { n:"BrtEndSortCond", f:parsenoop },
10319    0x0216: { n:"BrtBookProtection", f:parsenoop },
10320    0x0217: { n:"BrtSheetProtection", f:parsenoop },
10321    0x0218: { n:"BrtRangeProtection", f:parsenoop },
10322    0x0219: { n:"BrtPhoneticInfo", f:parsenoop },
10323    0x021A: { n:"BrtBeginECTxtWiz", f:parsenoop },
10324    0x021B: { n:"BrtEndECTxtWiz", f:parsenoop },
10325    0x021C: { n:"BrtBeginECTWFldInfoLst", f:parsenoop },
10326    0x021D: { n:"BrtEndECTWFldInfoLst", f:parsenoop },
10327    0x021E: { n:"BrtBeginECTwFldInfo", f:parsenoop },
10328    0x0224: { n:"BrtFileSharing", f:parsenoop },
10329    0x0225: { n:"BrtOleSize", f:parsenoop },
10330    0x0226: { n:"BrtDrawing", f:parsenoop },
10331    0x0227: { n:"BrtLegacyDrawing", f:parsenoop },
10332    0x0228: { n:"BrtLegacyDrawingHF", f:parsenoop },
10333    0x0229: { n:"BrtWebOpt", f:parsenoop },
10334    0x022A: { n:"BrtBeginWebPubItems", f:parsenoop },
10335    0x022B: { n:"BrtEndWebPubItems", f:parsenoop },
10336    0x022C: { n:"BrtBeginWebPubItem", f:parsenoop },
10337    0x022D: { n:"BrtEndWebPubItem", f:parsenoop },
10338    0x022E: { n:"BrtBeginSXCondFmt", f:parsenoop },
10339    0x022F: { n:"BrtEndSXCondFmt", f:parsenoop },
10340    0x0230: { n:"BrtBeginSXCondFmts", f:parsenoop },
10341    0x0231: { n:"BrtEndSXCondFmts", f:parsenoop },
10342    0x0232: { n:"BrtBkHim", f:parsenoop },
10343    0x0234: { n:"BrtColor", f:parsenoop },
10344    0x0235: { n:"BrtBeginIndexedColors", f:parsenoop },
10345    0x0236: { n:"BrtEndIndexedColors", f:parsenoop },
10346    0x0239: { n:"BrtBeginMRUColors", f:parsenoop },
10347    0x023A: { n:"BrtEndMRUColors", f:parsenoop },
10348    0x023C: { n:"BrtMRUColor", f:parsenoop },
10349    0x023D: { n:"BrtBeginDVals", f:parsenoop },
10350    0x023E: { n:"BrtEndDVals", f:parsenoop },
10351    0x0241: { n:"BrtSupNameStart", f:parsenoop },
10352    0x0242: { n:"BrtSupNameValueStart", f:parsenoop },
10353    0x0243: { n:"BrtSupNameValueEnd", f:parsenoop },
10354    0x0244: { n:"BrtSupNameNum", f:parsenoop },
10355    0x0245: { n:"BrtSupNameErr", f:parsenoop },
10356    0x0246: { n:"BrtSupNameSt", f:parsenoop },
10357    0x0247: { n:"BrtSupNameNil", f:parsenoop },
10358    0x0248: { n:"BrtSupNameBool", f:parsenoop },
10359    0x0249: { n:"BrtSupNameFmla", f:parsenoop },
10360    0x024A: { n:"BrtSupNameBits", f:parsenoop },
10361    0x024B: { n:"BrtSupNameEnd", f:parsenoop },
10362    0x024C: { n:"BrtEndSupBook", f:parsenoop },
10363    0x024D: { n:"BrtCellSmartTagProperty", f:parsenoop },
10364    0x024E: { n:"BrtBeginCellSmartTag", f:parsenoop },
10365    0x024F: { n:"BrtEndCellSmartTag", f:parsenoop },
10366    0x0250: { n:"BrtBeginCellSmartTags", f:parsenoop },
10367    0x0251: { n:"BrtEndCellSmartTags", f:parsenoop },
10368    0x0252: { n:"BrtBeginSmartTags", f:parsenoop },
10369    0x0253: { n:"BrtEndSmartTags", f:parsenoop },
10370    0x0254: { n:"BrtSmartTagType", f:parsenoop },
10371    0x0255: { n:"BrtBeginSmartTagTypes", f:parsenoop },
10372    0x0256: { n:"BrtEndSmartTagTypes", f:parsenoop },
10373    0x0257: { n:"BrtBeginSXFilters", f:parsenoop },
10374    0x0258: { n:"BrtEndSXFilters", f:parsenoop },
10375    0x0259: { n:"BrtBeginSXFILTER", f:parsenoop },
10376    0x025A: { n:"BrtEndSXFilter", f:parsenoop },
10377    0x025B: { n:"BrtBeginFills", f:parsenoop },
10378    0x025C: { n:"BrtEndFills", f:parsenoop },
10379    0x025D: { n:"BrtBeginCellWatches", f:parsenoop },
10380    0x025E: { n:"BrtEndCellWatches", f:parsenoop },
10381    0x025F: { n:"BrtCellWatch", f:parsenoop },
10382    0x0260: { n:"BrtBeginCRErrs", f:parsenoop },
10383    0x0261: { n:"BrtEndCRErrs", f:parsenoop },
10384    0x0262: { n:"BrtCrashRecErr", f:parsenoop },
10385    0x0263: { n:"BrtBeginFonts", f:parsenoop },
10386    0x0264: { n:"BrtEndFonts", f:parsenoop },
10387    0x0265: { n:"BrtBeginBorders", f:parsenoop },
10388    0x0266: { n:"BrtEndBorders", f:parsenoop },
10389    0x0267: { n:"BrtBeginFmts", f:parsenoop },
10390    0x0268: { n:"BrtEndFmts", f:parsenoop },
10391    0x0269: { n:"BrtBeginCellXFs", f:parsenoop },
10392    0x026A: { n:"BrtEndCellXFs", f:parsenoop },
10393    0x026B: { n:"BrtBeginStyles", f:parsenoop },
10394    0x026C: { n:"BrtEndStyles", f:parsenoop },
10395    0x0271: { n:"BrtBigName", f:parsenoop },
10396    0x0272: { n:"BrtBeginCellStyleXFs", f:parsenoop },
10397    0x0273: { n:"BrtEndCellStyleXFs", f:parsenoop },
10398    0x0274: { n:"BrtBeginComments", f:parsenoop },
10399    0x0275: { n:"BrtEndComments", f:parsenoop },
10400    0x0276: { n:"BrtBeginCommentAuthors", f:parsenoop },
10401    0x0277: { n:"BrtEndCommentAuthors", f:parsenoop },
10402    0x0278: { n:"BrtCommentAuthor", f:parse_BrtCommentAuthor },
10403    0x0279: { n:"BrtBeginCommentList", f:parsenoop },
10404    0x027A: { n:"BrtEndCommentList", f:parsenoop },
10405    0x027B: { n:"BrtBeginComment", f:parse_BrtBeginComment},
10406    0x027C: { n:"BrtEndComment", f:parsenoop },
10407    0x027D: { n:"BrtCommentText", f:parse_BrtCommentText },
10408    0x027E: { n:"BrtBeginOleObjects", f:parsenoop },
10409    0x027F: { n:"BrtOleObject", f:parsenoop },
10410    0x0280: { n:"BrtEndOleObjects", f:parsenoop },
10411    0x0281: { n:"BrtBeginSxrules", f:parsenoop },
10412    0x0282: { n:"BrtEndSxRules", f:parsenoop },
10413    0x0283: { n:"BrtBeginActiveXControls", f:parsenoop },
10414    0x0284: { n:"BrtActiveX", f:parsenoop },
10415    0x0285: { n:"BrtEndActiveXControls", f:parsenoop },
10416    0x0286: { n:"BrtBeginPCDSDTCEMembersSortBy", f:parsenoop },
10417    0x0288: { n:"BrtBeginCellIgnoreECs", f:parsenoop },
10418    0x0289: { n:"BrtCellIgnoreEC", f:parsenoop },
10419    0x028A: { n:"BrtEndCellIgnoreECs", f:parsenoop },
10420    0x028B: { n:"BrtCsProp", f:parsenoop },
10421    0x028C: { n:"BrtCsPageSetup", f:parsenoop },
10422    0x028D: { n:"BrtBeginUserCsViews", f:parsenoop },
10423    0x028E: { n:"BrtEndUserCsViews", f:parsenoop },
10424    0x028F: { n:"BrtBeginUserCsView", f:parsenoop },
10425    0x0290: { n:"BrtEndUserCsView", f:parsenoop },
10426    0x0291: { n:"BrtBeginPcdSFCIEntries", f:parsenoop },
10427    0x0292: { n:"BrtEndPCDSFCIEntries", f:parsenoop },
10428    0x0293: { n:"BrtPCDSFCIEntry", f:parsenoop },
10429    0x0294: { n:"BrtBeginListParts", f:parsenoop },
10430    0x0295: { n:"BrtListPart", f:parsenoop },
10431    0x0296: { n:"BrtEndListParts", f:parsenoop },
10432    0x0297: { n:"BrtSheetCalcProp", f:parsenoop },
10433    0x0298: { n:"BrtBeginFnGroup", f:parsenoop },
10434    0x0299: { n:"BrtFnGroup", f:parsenoop },
10435    0x029A: { n:"BrtEndFnGroup", f:parsenoop },
10436    0x029B: { n:"BrtSupAddin", f:parsenoop },
10437    0x029C: { n:"BrtSXTDMPOrder", f:parsenoop },
10438    0x029D: { n:"BrtCsProtection", f:parsenoop },
10439    0x029F: { n:"BrtBeginWsSortMap", f:parsenoop },
10440    0x02A0: { n:"BrtEndWsSortMap", f:parsenoop },
10441    0x02A1: { n:"BrtBeginRRSort", f:parsenoop },
10442    0x02A2: { n:"BrtEndRRSort", f:parsenoop },
10443    0x02A3: { n:"BrtRRSortItem", f:parsenoop },
10444    0x02A4: { n:"BrtFileSharingIso", f:parsenoop },
10445    0x02A5: { n:"BrtBookProtectionIso", f:parsenoop },
10446    0x02A6: { n:"BrtSheetProtectionIso", f:parsenoop },
10447    0x02A7: { n:"BrtCsProtectionIso", f:parsenoop },
10448    0x02A8: { n:"BrtRangeProtectionIso", f:parsenoop },
10449    0x0400: { n:"BrtRwDescent", f:parsenoop },
10450    0x0401: { n:"BrtKnownFonts", f:parsenoop },
10451    0x0402: { n:"BrtBeginSXTupleSet", f:parsenoop },
10452    0x0403: { n:"BrtEndSXTupleSet", f:parsenoop },
10453    0x0404: { n:"BrtBeginSXTupleSetHeader", f:parsenoop },
10454    0x0405: { n:"BrtEndSXTupleSetHeader", f:parsenoop },
10455    0x0406: { n:"BrtSXTupleSetHeaderItem", f:parsenoop },
10456    0x0407: { n:"BrtBeginSXTupleSetData", f:parsenoop },
10457    0x0408: { n:"BrtEndSXTupleSetData", f:parsenoop },
10458    0x0409: { n:"BrtBeginSXTupleSetRow", f:parsenoop },
10459    0x040A: { n:"BrtEndSXTupleSetRow", f:parsenoop },
10460    0x040B: { n:"BrtSXTupleSetRowItem", f:parsenoop },
10461    0x040C: { n:"BrtNameExt", f:parsenoop },
10462    0x040D: { n:"BrtPCDH14", f:parsenoop },
10463    0x040E: { n:"BrtBeginPCDCalcMem14", f:parsenoop },
10464    0x040F: { n:"BrtEndPCDCalcMem14", f:parsenoop },
10465    0x0410: { n:"BrtSXTH14", f:parsenoop },
10466    0x0411: { n:"BrtBeginSparklineGroup", f:parsenoop },
10467    0x0412: { n:"BrtEndSparklineGroup", f:parsenoop },
10468    0x0413: { n:"BrtSparkline", f:parsenoop },
10469    0x0414: { n:"BrtSXDI14", f:parsenoop },
10470    0x0415: { n:"BrtWsFmtInfoEx14", f:parsenoop },
10471    0x0416: { n:"BrtBeginConditionalFormatting14", f:parsenoop },
10472    0x0417: { n:"BrtEndConditionalFormatting14", f:parsenoop },
10473    0x0418: { n:"BrtBeginCFRule14", f:parsenoop },
10474    0x0419: { n:"BrtEndCFRule14", f:parsenoop },
10475    0x041A: { n:"BrtCFVO14", f:parsenoop },
10476    0x041B: { n:"BrtBeginDatabar14", f:parsenoop },
10477    0x041C: { n:"BrtBeginIconSet14", f:parsenoop },
10478    0x041D: { n:"BrtDVal14", f:parsenoop },
10479    0x041E: { n:"BrtBeginDVals14", f:parsenoop },
10480    0x041F: { n:"BrtColor14", f:parsenoop },
10481    0x0420: { n:"BrtBeginSparklines", f:parsenoop },
10482    0x0421: { n:"BrtEndSparklines", f:parsenoop },
10483    0x0422: { n:"BrtBeginSparklineGroups", f:parsenoop },
10484    0x0423: { n:"BrtEndSparklineGroups", f:parsenoop },
10485    0x0425: { n:"BrtSXVD14", f:parsenoop },
10486    0x0426: { n:"BrtBeginSxview14", f:parsenoop },
10487    0x0427: { n:"BrtEndSxview14", f:parsenoop },
10488    0x042A: { n:"BrtBeginPCD14", f:parsenoop },
10489    0x042B: { n:"BrtEndPCD14", f:parsenoop },
10490    0x042C: { n:"BrtBeginExtConn14", f:parsenoop },
10491    0x042D: { n:"BrtEndExtConn14", f:parsenoop },
10492    0x042E: { n:"BrtBeginSlicerCacheIDs", f:parsenoop },
10493    0x042F: { n:"BrtEndSlicerCacheIDs", f:parsenoop },
10494    0x0430: { n:"BrtBeginSlicerCacheID", f:parsenoop },
10495    0x0431: { n:"BrtEndSlicerCacheID", f:parsenoop },
10496    0x0433: { n:"BrtBeginSlicerCache", f:parsenoop },
10497    0x0434: { n:"BrtEndSlicerCache", f:parsenoop },
10498    0x0435: { n:"BrtBeginSlicerCacheDef", f:parsenoop },
10499    0x0436: { n:"BrtEndSlicerCacheDef", f:parsenoop },
10500    0x0437: { n:"BrtBeginSlicersEx", f:parsenoop },
10501    0x0438: { n:"BrtEndSlicersEx", f:parsenoop },
10502    0x0439: { n:"BrtBeginSlicerEx", f:parsenoop },
10503    0x043A: { n:"BrtEndSlicerEx", f:parsenoop },
10504    0x043B: { n:"BrtBeginSlicer", f:parsenoop },
10505    0x043C: { n:"BrtEndSlicer", f:parsenoop },
10506    0x043D: { n:"BrtSlicerCachePivotTables", f:parsenoop },
10507    0x043E: { n:"BrtBeginSlicerCacheOlapImpl", f:parsenoop },
10508    0x043F: { n:"BrtEndSlicerCacheOlapImpl", f:parsenoop },
10509    0x0440: { n:"BrtBeginSlicerCacheLevelsData", f:parsenoop },
10510    0x0441: { n:"BrtEndSlicerCacheLevelsData", f:parsenoop },
10511    0x0442: { n:"BrtBeginSlicerCacheLevelData", f:parsenoop },
10512    0x0443: { n:"BrtEndSlicerCacheLevelData", f:parsenoop },
10513    0x0444: { n:"BrtBeginSlicerCacheSiRanges", f:parsenoop },
10514    0x0445: { n:"BrtEndSlicerCacheSiRanges", f:parsenoop },
10515    0x0446: { n:"BrtBeginSlicerCacheSiRange", f:parsenoop },
10516    0x0447: { n:"BrtEndSlicerCacheSiRange", f:parsenoop },
10517    0x0448: { n:"BrtSlicerCacheOlapItem", f:parsenoop },
10518    0x0449: { n:"BrtBeginSlicerCacheSelections", f:parsenoop },
10519    0x044A: { n:"BrtSlicerCacheSelection", f:parsenoop },
10520    0x044B: { n:"BrtEndSlicerCacheSelections", f:parsenoop },
10521    0x044C: { n:"BrtBeginSlicerCacheNative", f:parsenoop },
10522    0x044D: { n:"BrtEndSlicerCacheNative", f:parsenoop },
10523    0x044E: { n:"BrtSlicerCacheNativeItem", f:parsenoop },
10524    0x044F: { n:"BrtRangeProtection14", f:parsenoop },
10525    0x0450: { n:"BrtRangeProtectionIso14", f:parsenoop },
10526    0x0451: { n:"BrtCellIgnoreEC14", f:parsenoop },
10527    0x0457: { n:"BrtList14", f:parsenoop },
10528    0x0458: { n:"BrtCFIcon", f:parsenoop },
10529    0x0459: { n:"BrtBeginSlicerCachesPivotCacheIDs", f:parsenoop },
10530    0x045A: { n:"BrtEndSlicerCachesPivotCacheIDs", f:parsenoop },
10531    0x045B: { n:"BrtBeginSlicers", f:parsenoop },
10532    0x045C: { n:"BrtEndSlicers", f:parsenoop },
10533    0x045D: { n:"BrtWbProp14", f:parsenoop },
10534    0x045E: { n:"BrtBeginSXEdit", f:parsenoop },
10535    0x045F: { n:"BrtEndSXEdit", f:parsenoop },
10536    0x0460: { n:"BrtBeginSXEdits", f:parsenoop },
10537    0x0461: { n:"BrtEndSXEdits", f:parsenoop },
10538    0x0462: { n:"BrtBeginSXChange", f:parsenoop },
10539    0x0463: { n:"BrtEndSXChange", f:parsenoop },
10540    0x0464: { n:"BrtBeginSXChanges", f:parsenoop },
10541    0x0465: { n:"BrtEndSXChanges", f:parsenoop },
10542    0x0466: { n:"BrtSXTupleItems", f:parsenoop },
10543    0x0468: { n:"BrtBeginSlicerStyle", f:parsenoop },
10544    0x0469: { n:"BrtEndSlicerStyle", f:parsenoop },
10545    0x046A: { n:"BrtSlicerStyleElement", f:parsenoop },
10546    0x046B: { n:"BrtBeginStyleSheetExt14", f:parsenoop },
10547    0x046C: { n:"BrtEndStyleSheetExt14", f:parsenoop },
10548    0x046D: { n:"BrtBeginSlicerCachesPivotCacheID", f:parsenoop },
10549    0x046E: { n:"BrtEndSlicerCachesPivotCacheID", f:parsenoop },
10550    0x046F: { n:"BrtBeginConditionalFormattings", f:parsenoop },
10551    0x0470: { n:"BrtEndConditionalFormattings", f:parsenoop },
10552    0x0471: { n:"BrtBeginPCDCalcMemExt", f:parsenoop },
10553    0x0472: { n:"BrtEndPCDCalcMemExt", f:parsenoop },
10554    0x0473: { n:"BrtBeginPCDCalcMemsExt", f:parsenoop },
10555    0x0474: { n:"BrtEndPCDCalcMemsExt", f:parsenoop },
10556    0x0475: { n:"BrtPCDField14", f:parsenoop },
10557    0x0476: { n:"BrtBeginSlicerStyles", f:parsenoop },
10558    0x0477: { n:"BrtEndSlicerStyles", f:parsenoop },
10559    0x0478: { n:"BrtBeginSlicerStyleElements", f:parsenoop },
10560    0x0479: { n:"BrtEndSlicerStyleElements", f:parsenoop },
10561    0x047A: { n:"BrtCFRuleExt", f:parsenoop },
10562    0x047B: { n:"BrtBeginSXCondFmt14", f:parsenoop },
10563    0x047C: { n:"BrtEndSXCondFmt14", f:parsenoop },
10564    0x047D: { n:"BrtBeginSXCondFmts14", f:parsenoop },
10565    0x047E: { n:"BrtEndSXCondFmts14", f:parsenoop },
10566    0x0480: { n:"BrtBeginSortCond14", f:parsenoop },
10567    0x0481: { n:"BrtEndSortCond14", f:parsenoop },
10568    0x0482: { n:"BrtEndDVals14", f:parsenoop },
10569    0x0483: { n:"BrtEndIconSet14", f:parsenoop },
10570    0x0484: { n:"BrtEndDatabar14", f:parsenoop },
10571    0x0485: { n:"BrtBeginColorScale14", f:parsenoop },
10572    0x0486: { n:"BrtEndColorScale14", f:parsenoop },
10573    0x0487: { n:"BrtBeginSxrules14", f:parsenoop },
10574    0x0488: { n:"BrtEndSxrules14", f:parsenoop },
10575    0x0489: { n:"BrtBeginPRule14", f:parsenoop },
10576    0x048A: { n:"BrtEndPRule14", f:parsenoop },
10577    0x048B: { n:"BrtBeginPRFilters14", f:parsenoop },
10578    0x048C: { n:"BrtEndPRFilters14", f:parsenoop },
10579    0x048D: { n:"BrtBeginPRFilter14", f:parsenoop },
10580    0x048E: { n:"BrtEndPRFilter14", f:parsenoop },
10581    0x048F: { n:"BrtBeginPRFItem14", f:parsenoop },
10582    0x0490: { n:"BrtEndPRFItem14", f:parsenoop },
10583    0x0491: { n:"BrtBeginCellIgnoreECs14", f:parsenoop },
10584    0x0492: { n:"BrtEndCellIgnoreECs14", f:parsenoop },
10585    0x0493: { n:"BrtDxf14", f:parsenoop },
10586    0x0494: { n:"BrtBeginDxF14s", f:parsenoop },
10587    0x0495: { n:"BrtEndDxf14s", f:parsenoop },
10588    0x0499: { n:"BrtFilter14", f:parsenoop },
10589    0x049A: { n:"BrtBeginCustomFilters14", f:parsenoop },
10590    0x049C: { n:"BrtCustomFilter14", f:parsenoop },
10591    0x049D: { n:"BrtIconFilter14", f:parsenoop },
10592    0x049E: { n:"BrtPivotCacheConnectionName", f:parsenoop },
10593    0x0800: { n:"BrtBeginDecoupledPivotCacheIDs", f:parsenoop },
10594    0x0801: { n:"BrtEndDecoupledPivotCacheIDs", f:parsenoop },
10595    0x0802: { n:"BrtDecoupledPivotCacheID", f:parsenoop },
10596    0x0803: { n:"BrtBeginPivotTableRefs", f:parsenoop },
10597    0x0804: { n:"BrtEndPivotTableRefs", f:parsenoop },
10598    0x0805: { n:"BrtPivotTableRef", f:parsenoop },
10599    0x0806: { n:"BrtSlicerCacheBookPivotTables", f:parsenoop },
10600    0x0807: { n:"BrtBeginSxvcells", f:parsenoop },
10601    0x0808: { n:"BrtEndSxvcells", f:parsenoop },
10602    0x0809: { n:"BrtBeginSxRow", f:parsenoop },
10603    0x080A: { n:"BrtEndSxRow", f:parsenoop },
10604    0x080C: { n:"BrtPcdCalcMem15", f:parsenoop },
10605    0x0813: { n:"BrtQsi15", f:parsenoop },
10606    0x0814: { n:"BrtBeginWebExtensions", f:parsenoop },
10607    0x0815: { n:"BrtEndWebExtensions", f:parsenoop },
10608    0x0816: { n:"BrtWebExtension", f:parsenoop },
10609    0x0817: { n:"BrtAbsPath15", f:parsenoop },
10610    0x0818: { n:"BrtBeginPivotTableUISettings", f:parsenoop },
10611    0x0819: { n:"BrtEndPivotTableUISettings", f:parsenoop },
10612    0x081B: { n:"BrtTableSlicerCacheIDs", f:parsenoop },
10613    0x081C: { n:"BrtTableSlicerCacheID", f:parsenoop },
10614    0x081D: { n:"BrtBeginTableSlicerCache", f:parsenoop },
10615    0x081E: { n:"BrtEndTableSlicerCache", f:parsenoop },
10616    0x081F: { n:"BrtSxFilter15", f:parsenoop },
10617    0x0820: { n:"BrtBeginTimelineCachePivotCacheIDs", f:parsenoop },
10618    0x0821: { n:"BrtEndTimelineCachePivotCacheIDs", f:parsenoop },
10619    0x0822: { n:"BrtTimelineCachePivotCacheID", f:parsenoop },
10620    0x0823: { n:"BrtBeginTimelineCacheIDs", f:parsenoop },
10621    0x0824: { n:"BrtEndTimelineCacheIDs", f:parsenoop },
10622    0x0825: { n:"BrtBeginTimelineCacheID", f:parsenoop },
10623    0x0826: { n:"BrtEndTimelineCacheID", f:parsenoop },
10624    0x0827: { n:"BrtBeginTimelinesEx", f:parsenoop },
10625    0x0828: { n:"BrtEndTimelinesEx", f:parsenoop },
10626    0x0829: { n:"BrtBeginTimelineEx", f:parsenoop },
10627    0x082A: { n:"BrtEndTimelineEx", f:parsenoop },
10628    0x082B: { n:"BrtWorkBookPr15", f:parsenoop },
10629    0x082C: { n:"BrtPCDH15", f:parsenoop },
10630    0x082D: { n:"BrtBeginTimelineStyle", f:parsenoop },
10631    0x082E: { n:"BrtEndTimelineStyle", f:parsenoop },
10632    0x082F: { n:"BrtTimelineStyleElement", f:parsenoop },
10633    0x0830: { n:"BrtBeginTimelineStylesheetExt15", f:parsenoop },
10634    0x0831: { n:"BrtEndTimelineStylesheetExt15", f:parsenoop },
10635    0x0832: { n:"BrtBeginTimelineStyles", f:parsenoop },
10636    0x0833: { n:"BrtEndTimelineStyles", f:parsenoop },
10637    0x0834: { n:"BrtBeginTimelineStyleElements", f:parsenoop },
10638    0x0835: { n:"BrtEndTimelineStyleElements", f:parsenoop },
10639    0x0836: { n:"BrtDxf15", f:parsenoop },
10640    0x0837: { n:"BrtBeginDxfs15", f:parsenoop },
10641    0x0838: { n:"brtEndDxfs15", f:parsenoop },
10642    0x0839: { n:"BrtSlicerCacheHideItemsWithNoData", f:parsenoop },
10643    0x083A: { n:"BrtBeginItemUniqueNames", f:parsenoop },
10644    0x083B: { n:"BrtEndItemUniqueNames", f:parsenoop },
10645    0x083C: { n:"BrtItemUniqueName", f:parsenoop },
10646    0x083D: { n:"BrtBeginExtConn15", f:parsenoop },
10647    0x083E: { n:"BrtEndExtConn15", f:parsenoop },
10648    0x083F: { n:"BrtBeginOledbPr15", f:parsenoop },
10649    0x0840: { n:"BrtEndOledbPr15", f:parsenoop },
10650    0x0841: { n:"BrtBeginDataFeedPr15", f:parsenoop },
10651    0x0842: { n:"BrtEndDataFeedPr15", f:parsenoop },
10652    0x0843: { n:"BrtTextPr15", f:parsenoop },
10653    0x0844: { n:"BrtRangePr15", f:parsenoop },
10654    0x0845: { n:"BrtDbCommand15", f:parsenoop },
10655    0x0846: { n:"BrtBeginDbTables15", f:parsenoop },
10656    0x0847: { n:"BrtEndDbTables15", f:parsenoop },
10657    0x0848: { n:"BrtDbTable15", f:parsenoop },
10658    0x0849: { n:"BrtBeginDataModel", f:parsenoop },
10659    0x084A: { n:"BrtEndDataModel", f:parsenoop },
10660    0x084B: { n:"BrtBeginModelTables", f:parsenoop },
10661    0x084C: { n:"BrtEndModelTables", f:parsenoop },
10662    0x084D: { n:"BrtModelTable", f:parsenoop },
10663    0x084E: { n:"BrtBeginModelRelationships", f:parsenoop },
10664    0x084F: { n:"BrtEndModelRelationships", f:parsenoop },
10665    0x0850: { n:"BrtModelRelationship", f:parsenoop },
10666    0x0851: { n:"BrtBeginECTxtWiz15", f:parsenoop },
10667    0x0852: { n:"BrtEndECTxtWiz15", f:parsenoop },
10668    0x0853: { n:"BrtBeginECTWFldInfoLst15", f:parsenoop },
10669    0x0854: { n:"BrtEndECTWFldInfoLst15", f:parsenoop },
10670    0x0855: { n:"BrtBeginECTWFldInfo15", f:parsenoop },
10671    0x0856: { n:"BrtFieldListActiveItem", f:parsenoop },
10672    0x0857: { n:"BrtPivotCacheIdVersion", f:parsenoop },
10673    0x0858: { n:"BrtSXDI15", f:parsenoop },
10674    0xFFFF: { n:"", f:parsenoop }
10675};
10676
10677var evert_RE = evert_key(XLSBRecordEnum, 'n');
10678
10679/* [MS-XLS] 2.3 Record Enumeration */
10680var XLSRecordEnum = {
10681    0x0003: { n:"BIFF2NUM", f:parse_BIFF2NUM },
10682    0x0004: { n:"BIFF2STR", f:parse_BIFF2STR },
10683    0x0006: { n:"Formula", f:parse_Formula },
10684    0x0009: { n:'BOF', f:parse_BOF },
10685    0x000a: { n:'EOF', f:parse_EOF },
10686    0x000c: { n:"CalcCount", f:parse_CalcCount },
10687    0x000d: { n:"CalcMode", f:parse_CalcMode },
10688    0x000e: { n:"CalcPrecision", f:parse_CalcPrecision },
10689    0x000f: { n:"CalcRefMode", f:parse_CalcRefMode },
10690    0x0010: { n:"CalcDelta", f:parse_CalcDelta },
10691    0x0011: { n:"CalcIter", f:parse_CalcIter },
10692    0x0012: { n:"Protect", f:parse_Protect },
10693    0x0013: { n:"Password", f:parse_Password },
10694    0x0014: { n:"Header", f:parse_Header },
10695    0x0015: { n:"Footer", f:parse_Footer },
10696    0x0017: { n:"ExternSheet", f:parse_ExternSheet },
10697    0x0018: { n:"Lbl", f:parse_Lbl },
10698    0x0019: { n:"WinProtect", f:parse_WinProtect },
10699    0x001a: { n:"VerticalPageBreaks", f:parse_VerticalPageBreaks },
10700    0x001b: { n:"HorizontalPageBreaks", f:parse_HorizontalPageBreaks },
10701    0x001c: { n:"Note", f:parse_Note },
10702    0x001d: { n:"Selection", f:parse_Selection },
10703    0x0022: { n:"Date1904", f:parse_Date1904 },
10704    0x0023: { n:"ExternName", f:parse_ExternName },
10705    0x0026: { n:"LeftMargin", f:parse_LeftMargin },
10706    0x0027: { n:"RightMargin", f:parse_RightMargin },
10707    0x0028: { n:"TopMargin", f:parse_TopMargin },
10708    0x0029: { n:"BottomMargin", f:parse_BottomMargin },
10709    0x002a: { n:"PrintRowCol", f:parse_PrintRowCol },
10710    0x002b: { n:"PrintGrid", f:parse_PrintGrid },
10711    0x002f: { n:"FilePass", f:parse_FilePass },
10712    0x0031: { n:"Font", f:parse_Font },
10713    0x0033: { n:"PrintSize", f:parse_PrintSize },
10714    0x003c: { n:"Continue", f:parse_Continue },
10715    0x003d: { n:"Window1", f:parse_Window1 },
10716    0x0040: { n:"Backup", f:parse_Backup },
10717    0x0041: { n:"Pane", f:parse_Pane },
10718    0x0042: { n:'CodePage', f:parse_CodePage },
10719    0x004d: { n:"Pls", f:parse_Pls },
10720    0x0050: { n:"DCon", f:parse_DCon },
10721    0x0051: { n:"DConRef", f:parse_DConRef },
10722    0x0052: { n:"DConName", f:parse_DConName },
10723    0x0055: { n:"DefColWidth", f:parse_DefColWidth },
10724    0x0059: { n:"XCT", f:parse_XCT },
10725    0x005a: { n:"CRN", f:parse_CRN },
10726    0x005b: { n:"FileSharing", f:parse_FileSharing },
10727    0x005c: { n:'WriteAccess', f:parse_WriteAccess },
10728    0x005d: { n:"Obj", f:parse_Obj },
10729    0x005e: { n:"Uncalced", f:parse_Uncalced },
10730    0x005f: { n:"CalcSaveRecalc", f:parse_CalcSaveRecalc },
10731    0x0060: { n:"Template", f:parse_Template },
10732    0x0061: { n:"Intl", f:parse_Intl },
10733    0x0063: { n:"ObjProtect", f:parse_ObjProtect },
10734    0x007d: { n:"ColInfo", f:parse_ColInfo },
10735    0x0080: { n:"Guts", f:parse_Guts },
10736    0x0081: { n:"WsBool", f:parse_WsBool },
10737    0x0082: { n:"GridSet", f:parse_GridSet },
10738    0x0083: { n:"HCenter", f:parse_HCenter },
10739    0x0084: { n:"VCenter", f:parse_VCenter },
10740    0x0085: { n:'BoundSheet8', f:parse_BoundSheet8 },
10741    0x0086: { n:"WriteProtect", f:parse_WriteProtect },
10742    0x008c: { n:"Country", f:parse_Country },
10743    0x008d: { n:"HideObj", f:parse_HideObj },
10744    0x0090: { n:"Sort", f:parse_Sort },
10745    0x0092: { n:"Palette", f:parse_Palette },
10746    0x0097: { n:"Sync", f:parse_Sync },
10747    0x0098: { n:"LPr", f:parse_LPr },
10748    0x0099: { n:"DxGCol", f:parse_DxGCol },
10749    0x009a: { n:"FnGroupName", f:parse_FnGroupName },
10750    0x009b: { n:"FilterMode", f:parse_FilterMode },
10751    0x009c: { n:"BuiltInFnGroupCount", f:parse_BuiltInFnGroupCount },
10752    0x009d: { n:"AutoFilterInfo", f:parse_AutoFilterInfo },
10753    0x009e: { n:"AutoFilter", f:parse_AutoFilter },
10754    0x00a0: { n:"Scl", f:parse_Scl },
10755    0x00a1: { n:"Setup", f:parse_Setup },
10756    0x00ae: { n:"ScenMan", f:parse_ScenMan },
10757    0x00af: { n:"SCENARIO", f:parse_SCENARIO },
10758    0x00b0: { n:"SxView", f:parse_SxView },
10759    0x00b1: { n:"Sxvd", f:parse_Sxvd },
10760    0x00b2: { n:"SXVI", f:parse_SXVI },
10761    0x00b4: { n:"SxIvd", f:parse_SxIvd },
10762    0x00b5: { n:"SXLI", f:parse_SXLI },
10763    0x00b6: { n:"SXPI", f:parse_SXPI },
10764    0x00b8: { n:"DocRoute", f:parse_DocRoute },
10765    0x00b9: { n:"RecipName", f:parse_RecipName },
10766    0x00bd: { n:"MulRk", f:parse_MulRk },
10767    0x00be: { n:"MulBlank", f:parse_MulBlank },
10768    0x00c1: { n:'Mms', f:parse_Mms },
10769    0x00c5: { n:"SXDI", f:parse_SXDI },
10770    0x00c6: { n:"SXDB", f:parse_SXDB },
10771    0x00c7: { n:"SXFDB", f:parse_SXFDB },
10772    0x00c8: { n:"SXDBB", f:parse_SXDBB },
10773    0x00c9: { n:"SXNum", f:parse_SXNum },
10774    0x00ca: { n:"SxBool", f:parse_SxBool },
10775    0x00cb: { n:"SxErr", f:parse_SxErr },
10776    0x00cc: { n:"SXInt", f:parse_SXInt },
10777    0x00cd: { n:"SXString", f:parse_SXString },
10778    0x00ce: { n:"SXDtr", f:parse_SXDtr },
10779    0x00cf: { n:"SxNil", f:parse_SxNil },
10780    0x00d0: { n:"SXTbl", f:parse_SXTbl },
10781    0x00d1: { n:"SXTBRGIITM", f:parse_SXTBRGIITM },
10782    0x00d2: { n:"SxTbpg", f:parse_SxTbpg },
10783    0x00d3: { n:"ObProj", f:parse_ObProj },
10784    0x00d5: { n:"SXStreamID", f:parse_SXStreamID },
10785    0x00d7: { n:"DBCell", f:parse_DBCell },
10786    0x00d8: { n:"SXRng", f:parse_SXRng },
10787    0x00d9: { n:"SxIsxoper", f:parse_SxIsxoper },
10788    0x00da: { n:"BookBool", f:parse_BookBool },
10789    0x00dc: { n:"DbOrParamQry", f:parse_DbOrParamQry },
10790    0x00dd: { n:"ScenarioProtect", f:parse_ScenarioProtect },
10791    0x00de: { n:"OleObjectSize", f:parse_OleObjectSize },
10792    0x00e0: { n:"XF", f:parse_XF },
10793    0x00e1: { n:'InterfaceHdr', f:parse_InterfaceHdr },
10794    0x00e2: { n:'InterfaceEnd', f:parse_InterfaceEnd },
10795    0x00e3: { n:"SXVS", f:parse_SXVS },
10796    0x00e5: { n:"MergeCells", f:parse_MergeCells },
10797    0x00e9: { n:"BkHim", f:parse_BkHim },
10798    0x00eb: { n:"MsoDrawingGroup", f:parse_MsoDrawingGroup },
10799    0x00ec: { n:"MsoDrawing", f:parse_MsoDrawing },
10800    0x00ed: { n:"MsoDrawingSelection", f:parse_MsoDrawingSelection },
10801    0x00ef: { n:"PhoneticInfo", f:parse_PhoneticInfo },
10802    0x00f0: { n:"SxRule", f:parse_SxRule },
10803    0x00f1: { n:"SXEx", f:parse_SXEx },
10804    0x00f2: { n:"SxFilt", f:parse_SxFilt },
10805    0x00f4: { n:"SxDXF", f:parse_SxDXF },
10806    0x00f5: { n:"SxItm", f:parse_SxItm },
10807    0x00f6: { n:"SxName", f:parse_SxName },
10808    0x00f7: { n:"SxSelect", f:parse_SxSelect },
10809    0x00f8: { n:"SXPair", f:parse_SXPair },
10810    0x00f9: { n:"SxFmla", f:parse_SxFmla },
10811    0x00fb: { n:"SxFormat", f:parse_SxFormat },
10812    0x00fc: { n:"SST", f:parse_SST },
10813    0x00fd: { n:"LabelSst", f:parse_LabelSst },
10814    0x00ff: { n:"ExtSST", f:parse_ExtSST },
10815    0x0100: { n:"SXVDEx", f:parse_SXVDEx },
10816    0x0103: { n:"SXFormula", f:parse_SXFormula },
10817    0x0122: { n:"SXDBEx", f:parse_SXDBEx },
10818    0x0137: { n:"RRDInsDel", f:parse_RRDInsDel },
10819    0x0138: { n:"RRDHead", f:parse_RRDHead },
10820    0x013b: { n:"RRDChgCell", f:parse_RRDChgCell },
10821    0x013d: { n:"RRTabId", f:parse_RRTabId },
10822    0x013e: { n:"RRDRenSheet", f:parse_RRDRenSheet },
10823    0x013f: { n:"RRSort", f:parse_RRSort },
10824    0x0140: { n:"RRDMove", f:parse_RRDMove },
10825    0x014a: { n:"RRFormat", f:parse_RRFormat },
10826    0x014b: { n:"RRAutoFmt", f:parse_RRAutoFmt },
10827    0x014d: { n:"RRInsertSh", f:parse_RRInsertSh },
10828    0x014e: { n:"RRDMoveBegin", f:parse_RRDMoveBegin },
10829    0x014f: { n:"RRDMoveEnd", f:parse_RRDMoveEnd },
10830    0x0150: { n:"RRDInsDelBegin", f:parse_RRDInsDelBegin },
10831    0x0151: { n:"RRDInsDelEnd", f:parse_RRDInsDelEnd },
10832    0x0152: { n:"RRDConflict", f:parse_RRDConflict },
10833    0x0153: { n:"RRDDefName", f:parse_RRDDefName },
10834    0x0154: { n:"RRDRstEtxp", f:parse_RRDRstEtxp },
10835    0x015f: { n:"LRng", f:parse_LRng },
10836    0x0160: { n:"UsesELFs", f:parse_UsesELFs },
10837    0x0161: { n:"DSF", f:parse_DSF },
10838    0x0191: { n:"CUsr", f:parse_CUsr },
10839    0x0192: { n:"CbUsr", f:parse_CbUsr },
10840    0x0193: { n:"UsrInfo", f:parse_UsrInfo },
10841    0x0194: { n:"UsrExcl", f:parse_UsrExcl },
10842    0x0195: { n:"FileLock", f:parse_FileLock },
10843    0x0196: { n:"RRDInfo", f:parse_RRDInfo },
10844    0x0197: { n:"BCUsrs", f:parse_BCUsrs },
10845    0x0198: { n:"UsrChk", f:parse_UsrChk },
10846    0x01a9: { n:"UserBView", f:parse_UserBView },
10847    0x01aa: { n:"UserSViewBegin", f:parse_UserSViewBegin },
10848    0x01ab: { n:"UserSViewEnd", f:parse_UserSViewEnd },
10849    0x01ac: { n:"RRDUserView", f:parse_RRDUserView },
10850    0x01ad: { n:"Qsi", f:parse_Qsi },
10851    0x01ae: { n:"SupBook", f:parse_SupBook },
10852    0x01af: { n:"Prot4Rev", f:parse_Prot4Rev },
10853    0x01b0: { n:"CondFmt", f:parse_CondFmt },
10854    0x01b1: { n:"CF", f:parse_CF },
10855    0x01b2: { n:"DVal", f:parse_DVal },
10856    0x01b5: { n:"DConBin", f:parse_DConBin },
10857    0x01b6: { n:"TxO", f:parse_TxO },
10858    0x01b7: { n:"RefreshAll", f:parse_RefreshAll },
10859    0x01b8: { n:"HLink", f:parse_HLink },
10860    0x01b9: { n:"Lel", f:parse_Lel },
10861    0x01ba: { n:"CodeName", f:parse_XLSCodeName },
10862    0x01bb: { n:"SXFDBType", f:parse_SXFDBType },
10863    0x01bc: { n:"Prot4RevPass", f:parse_Prot4RevPass },
10864    0x01bd: { n:"ObNoMacros", f:parse_ObNoMacros },
10865    0x01be: { n:"Dv", f:parse_Dv },
10866    0x01c0: { n:"Excel9File", f:parse_Excel9File },
10867    0x01c1: { n:"RecalcId", f:parse_RecalcId, r:2},
10868    0x01c2: { n:"EntExU2", f:parse_EntExU2 },
10869    0x0200: { n:"Dimensions", f:parse_Dimensions },
10870    0x0201: { n:"Blank", f:parse_Blank },
10871    0x0203: { n:"Number", f:parse_Number },
10872    0x0204: { n:"Label", f:parse_Label },
10873    0x0205: { n:"BoolErr", f:parse_BoolErr },
10874    0x0207: { n:"String", f:parse_String },
10875    0x0208: { n:'Row', f:parse_Row },
10876    0x020b: { n:"Index", f:parse_Index },
10877    0x0221: { n:"Array", f:parse_Array },
10878    0x0225: { n:"DefaultRowHeight", f:parse_DefaultRowHeight },
10879    0x0236: { n:"Table", f:parse_Table },
10880    0x023e: { n:"Window2", f:parse_Window2 },
10881    0x027e: { n:"RK", f:parse_RK },
10882    0x0293: { n:"Style", f:parse_Style },
10883    0x0418: { n:"BigName", f:parse_BigName },
10884    0x041e: { n:"Format", f:parse_Format },
10885    0x043c: { n:"ContinueBigName", f:parse_ContinueBigName },
10886    0x04bc: { n:"ShrFmla", f:parse_ShrFmla },
10887    0x0800: { n:"HLinkTooltip", f:parse_HLinkTooltip },
10888    0x0801: { n:"WebPub", f:parse_WebPub },
10889    0x0802: { n:"QsiSXTag", f:parse_QsiSXTag },
10890    0x0803: { n:"DBQueryExt", f:parse_DBQueryExt },
10891    0x0804: { n:"ExtString", f:parse_ExtString },
10892    0x0805: { n:"TxtQry", f:parse_TxtQry },
10893    0x0806: { n:"Qsir", f:parse_Qsir },
10894    0x0807: { n:"Qsif", f:parse_Qsif },
10895    0x0808: { n:"RRDTQSIF", f:parse_RRDTQSIF },
10896    0x0809: { n:'BOF', f:parse_BOF },
10897    0x080a: { n:"OleDbConn", f:parse_OleDbConn },
10898    0x080b: { n:"WOpt", f:parse_WOpt },
10899    0x080c: { n:"SXViewEx", f:parse_SXViewEx },
10900    0x080d: { n:"SXTH", f:parse_SXTH },
10901    0x080e: { n:"SXPIEx", f:parse_SXPIEx },
10902    0x080f: { n:"SXVDTEx", f:parse_SXVDTEx },
10903    0x0810: { n:"SXViewEx9", f:parse_SXViewEx9 },
10904    0x0812: { n:"ContinueFrt", f:parse_ContinueFrt },
10905    0x0813: { n:"RealTimeData", f:parse_RealTimeData },
10906    0x0850: { n:"ChartFrtInfo", f:parse_ChartFrtInfo },
10907    0x0851: { n:"FrtWrapper", f:parse_FrtWrapper },
10908    0x0852: { n:"StartBlock", f:parse_StartBlock },
10909    0x0853: { n:"EndBlock", f:parse_EndBlock },
10910    0x0854: { n:"StartObject", f:parse_StartObject },
10911    0x0855: { n:"EndObject", f:parse_EndObject },
10912    0x0856: { n:"CatLab", f:parse_CatLab },
10913    0x0857: { n:"YMult", f:parse_YMult },
10914    0x0858: { n:"SXViewLink", f:parse_SXViewLink },
10915    0x0859: { n:"PivotChartBits", f:parse_PivotChartBits },
10916    0x085a: { n:"FrtFontList", f:parse_FrtFontList },
10917    0x0862: { n:"SheetExt", f:parse_SheetExt },
10918    0x0863: { n:"BookExt", f:parse_BookExt, r:12},
10919    0x0864: { n:"SXAddl", f:parse_SXAddl },
10920    0x0865: { n:"CrErr", f:parse_CrErr },
10921    0x0866: { n:"HFPicture", f:parse_HFPicture },
10922    0x0867: { n:'FeatHdr', f:parse_FeatHdr },
10923    0x0868: { n:"Feat", f:parse_Feat },
10924    0x086a: { n:"DataLabExt", f:parse_DataLabExt },
10925    0x086b: { n:"DataLabExtContents", f:parse_DataLabExtContents },
10926    0x086c: { n:"CellWatch", f:parse_CellWatch },
10927    0x0871: { n:"FeatHdr11", f:parse_FeatHdr11 },
10928    0x0872: { n:"Feature11", f:parse_Feature11 },
10929    0x0874: { n:"DropDownObjIds", f:parse_DropDownObjIds },
10930    0x0875: { n:"ContinueFrt11", f:parse_ContinueFrt11 },
10931    0x0876: { n:"DConn", f:parse_DConn },
10932    0x0877: { n:"List12", f:parse_List12 },
10933    0x0878: { n:"Feature12", f:parse_Feature12 },
10934    0x0879: { n:"CondFmt12", f:parse_CondFmt12 },
10935    0x087a: { n:"CF12", f:parse_CF12 },
10936    0x087b: { n:"CFEx", f:parse_CFEx },
10937    0x087c: { n:"XFCRC", f:parse_XFCRC, r:12 },
10938    0x087d: { n:"XFExt", f:parse_XFExt, r:12 },
10939    0x087e: { n:"AutoFilter12", f:parse_AutoFilter12 },
10940    0x087f: { n:"ContinueFrt12", f:parse_ContinueFrt12 },
10941    0x0884: { n:"MDTInfo", f:parse_MDTInfo },
10942    0x0885: { n:"MDXStr", f:parse_MDXStr },
10943    0x0886: { n:"MDXTuple", f:parse_MDXTuple },
10944    0x0887: { n:"MDXSet", f:parse_MDXSet },
10945    0x0888: { n:"MDXProp", f:parse_MDXProp },
10946    0x0889: { n:"MDXKPI", f:parse_MDXKPI },
10947    0x088a: { n:"MDB", f:parse_MDB },
10948    0x088b: { n:"PLV", f:parse_PLV },
10949    0x088c: { n:"Compat12", f:parse_Compat12, r:12 },
10950    0x088d: { n:"DXF", f:parse_DXF },
10951    0x088e: { n:"TableStyles", f:parse_TableStyles, r:12 },
10952    0x088f: { n:"TableStyle", f:parse_TableStyle },
10953    0x0890: { n:"TableStyleElement", f:parse_TableStyleElement },
10954    0x0892: { n:"StyleExt", f:parse_StyleExt },
10955    0x0893: { n:"NamePublish", f:parse_NamePublish },
10956    0x0894: { n:"NameCmt", f:parse_NameCmt },
10957    0x0895: { n:"SortData", f:parse_SortData },
10958    0x0896: { n:"Theme", f:parse_Theme, r:12 },
10959    0x0897: { n:"GUIDTypeLib", f:parse_GUIDTypeLib },
10960    0x0898: { n:"FnGrp12", f:parse_FnGrp12 },
10961    0x0899: { n:"NameFnGrp12", f:parse_NameFnGrp12 },
10962    0x089a: { n:"MTRSettings", f:parse_MTRSettings, r:12 },
10963    0x089b: { n:"CompressPictures", f:parse_CompressPictures },
10964    0x089c: { n:"HeaderFooter", f:parse_HeaderFooter },
10965    0x089d: { n:"CrtLayout12", f:parse_CrtLayout12 },
10966    0x089e: { n:"CrtMlFrt", f:parse_CrtMlFrt },
10967    0x089f: { n:"CrtMlFrtContinue", f:parse_CrtMlFrtContinue },
10968    0x08a3: { n:"ForceFullCalculation", f:parse_ForceFullCalculation },
10969    0x08a4: { n:"ShapePropsStream", f:parse_ShapePropsStream },
10970    0x08a5: { n:"TextPropsStream", f:parse_TextPropsStream },
10971    0x08a6: { n:"RichTextStream", f:parse_RichTextStream },
10972    0x08a7: { n:"CrtLayout12A", f:parse_CrtLayout12A },
10973    0x1001: { n:"Units", f:parse_Units },
10974    0x1002: { n:"Chart", f:parse_Chart },
10975    0x1003: { n:"Series", f:parse_Series },
10976    0x1006: { n:"DataFormat", f:parse_DataFormat },
10977    0x1007: { n:"LineFormat", f:parse_LineFormat },
10978    0x1009: { n:"MarkerFormat", f:parse_MarkerFormat },
10979    0x100a: { n:"AreaFormat", f:parse_AreaFormat },
10980    0x100b: { n:"PieFormat", f:parse_PieFormat },
10981    0x100c: { n:"AttachedLabel", f:parse_AttachedLabel },
10982    0x100d: { n:"SeriesText", f:parse_SeriesText },
10983    0x1014: { n:"ChartFormat", f:parse_ChartFormat },
10984    0x1015: { n:"Legend", f:parse_Legend },
10985    0x1016: { n:"SeriesList", f:parse_SeriesList },
10986    0x1017: { n:"Bar", f:parse_Bar },
10987    0x1018: { n:"Line", f:parse_Line },
10988    0x1019: { n:"Pie", f:parse_Pie },
10989    0x101a: { n:"Area", f:parse_Area },
10990    0x101b: { n:"Scatter", f:parse_Scatter },
10991    0x101c: { n:"CrtLine", f:parse_CrtLine },
10992    0x101d: { n:"Axis", f:parse_Axis },
10993    0x101e: { n:"Tick", f:parse_Tick },
10994    0x101f: { n:"ValueRange", f:parse_ValueRange },
10995    0x1020: { n:"CatSerRange", f:parse_CatSerRange },
10996    0x1021: { n:"AxisLine", f:parse_AxisLine },
10997    0x1022: { n:"CrtLink", f:parse_CrtLink },
10998    0x1024: { n:"DefaultText", f:parse_DefaultText },
10999    0x1025: { n:"Text", f:parse_Text },
11000    0x1026: { n:"FontX", f:parse_FontX },
11001    0x1027: { n:"ObjectLink", f:parse_ObjectLink },
11002    0x1032: { n:"Frame", f:parse_Frame },
11003    0x1033: { n:"Begin", f:parse_Begin },
11004    0x1034: { n:"End", f:parse_End },
11005    0x1035: { n:"PlotArea", f:parse_PlotArea },
11006    0x103a: { n:"Chart3d", f:parse_Chart3d },
11007    0x103c: { n:"PicF", f:parse_PicF },
11008    0x103d: { n:"DropBar", f:parse_DropBar },
11009    0x103e: { n:"Radar", f:parse_Radar },
11010    0x103f: { n:"Surf", f:parse_Surf },
11011    0x1040: { n:"RadarArea", f:parse_RadarArea },
11012    0x1041: { n:"AxisParent", f:parse_AxisParent },
11013    0x1043: { n:"LegendException", f:parse_LegendException },
11014    0x1044: { n:"ShtProps", f:parse_ShtProps },
11015    0x1045: { n:"SerToCrt", f:parse_SerToCrt },
11016    0x1046: { n:"AxesUsed", f:parse_AxesUsed },
11017    0x1048: { n:"SBaseRef", f:parse_SBaseRef },
11018    0x104a: { n:"SerParent", f:parse_SerParent },
11019    0x104b: { n:"SerAuxTrend", f:parse_SerAuxTrend },
11020    0x104e: { n:"IFmtRecord", f:parse_IFmtRecord },
11021    0x104f: { n:"Pos", f:parse_Pos },
11022    0x1050: { n:"AlRuns", f:parse_AlRuns },
11023    0x1051: { n:"BRAI", f:parse_BRAI },
11024    0x105b: { n:"SerAuxErrBar", f:parse_SerAuxErrBar },
11025    0x105c: { n:"ClrtClient", f:parse_ClrtClient },
11026    0x105d: { n:"SerFmt", f:parse_SerFmt },
11027    0x105f: { n:"Chart3DBarShape", f:parse_Chart3DBarShape },
11028    0x1060: { n:"Fbi", f:parse_Fbi },
11029    0x1061: { n:"BopPop", f:parse_BopPop },
11030    0x1062: { n:"AxcExt", f:parse_AxcExt },
11031    0x1063: { n:"Dat", f:parse_Dat },
11032    0x1064: { n:"PlotGrowth", f:parse_PlotGrowth },
11033    0x1065: { n:"SIIndex", f:parse_SIIndex },
11034    0x1066: { n:"GelFrame", f:parse_GelFrame },
11035    0x1067: { n:"BopPopCustom", f:parse_BopPopCustom },
11036    0x1068: { n:"Fbi2", f:parse_Fbi2 },
11037
11038    /* These are specified in an older version of the spec */
11039    0x0016: { n:"ExternCount", f:parsenoop },
11040    0x007e: { n:"RK", f:parsenoop }, /* Not necessarily same as 0x027e */
11041    0x007f: { n:"ImData", f:parsenoop },
11042    0x0087: { n:"Addin", f:parsenoop },
11043    0x0088: { n:"Edg", f:parsenoop },
11044    0x0089: { n:"Pub", f:parsenoop },
11045    0x0091: { n:"Sub", f:parsenoop },
11046    0x0094: { n:"LHRecord", f:parsenoop },
11047    0x0095: { n:"LHNGraph", f:parsenoop },
11048    0x0096: { n:"Sound", f:parsenoop },
11049    0x00a9: { n:"CoordList", f:parsenoop },
11050    0x00ab: { n:"GCW", f:parsenoop },
11051    0x00bc: { n:"ShrFmla", f:parsenoop }, /* Not necessarily same as 0x04bc */
11052    0x00c2: { n:"AddMenu", f:parsenoop },
11053    0x00c3: { n:"DelMenu", f:parsenoop },
11054    0x00d6: { n:"RString", f:parsenoop },
11055    0x00df: { n:"UDDesc", f:parsenoop },
11056    0x00ea: { n:"TabIdConf", f:parsenoop },
11057    0x0162: { n:"XL5Modify", f:parsenoop },
11058    0x01a5: { n:"FileSharing2", f:parsenoop },
11059    0x0218: { n:"Name", f:parsenoop },
11060    0x0223: { n:"ExternName", f:parse_ExternName },
11061    0x0231: { n:"Font", f:parsenoop },
11062    0x0406: { n:"Formula", f:parse_Formula },
11063    0x086d: { n:"FeatInfo", f:parsenoop },
11064    0x0873: { n:"FeatInfo11", f:parsenoop },
11065    0x0881: { n:"SXAddl12", f:parsenoop },
11066    0x08c0: { n:"AutoWebPub", f:parsenoop },
11067    0x08c1: { n:"ListObj", f:parsenoop },
11068    0x08c2: { n:"ListField", f:parsenoop },
11069    0x08c3: { n:"ListDV", f:parsenoop },
11070    0x08c4: { n:"ListCondFmt", f:parsenoop },
11071    0x08c5: { n:"ListCF", f:parsenoop },
11072    0x08c6: { n:"FMQry", f:parsenoop },
11073    0x08c7: { n:"FMSQry", f:parsenoop },
11074    0x08c8: { n:"PLV", f:parsenoop }, /* supposedly PLV for Excel 11 */
11075    0x08c9: { n:"LnExt", f:parsenoop },
11076    0x08ca: { n:"MkrExt", f:parsenoop },
11077    0x08cb: { n:"CrtCoopt", f:parsenoop },
11078
11079    0x0000: {}
11080};
11081
11082
11083/* Helper function to call out to ODS parser */
11084function parse_ods(zip, opts) {
11085    if(typeof module !== "undefined" && typeof require !== 'undefined' && typeof ODS === 'undefined') ODS = require('./od' + 's');
11086    if(typeof ODS === 'undefined' || !ODS.parse_ods) throw new Error("Unsupported ODS");
11087    return ODS.parse_ods(zip, opts);
11088}
11089function fix_opts_func(defaults) {
11090    return function fix_opts(opts) {
11091        for(var i = 0; i != defaults.length; ++i) {
11092            var d = defaults[i];
11093            if(opts[d[0]] === undefined) opts[d[0]] = d[1];
11094            if(d[2] === 'n') opts[d[0]] = Number(opts[d[0]]);
11095        }
11096    };
11097}
11098
11099var fix_read_opts = fix_opts_func([
11100    ['cellNF', false], /* emit cell number format string as .z */
11101    ['cellHTML', true], /* emit html string as .h */
11102    ['cellFormula', true], /* emit formulae as .f */
11103    ['cellStyles', false], /* emits style/theme as .s */
11104    ['cellDates', false], /* emit date cells with type `d` */
11105
11106    ['sheetStubs', false], /* emit empty cells */
11107    ['sheetRows', 0, 'n'], /* read n rows (0 = read all rows) */
11108
11109    ['bookDeps', false], /* parse calculation chains */
11110    ['bookSheets', false], /* only try to get sheet names (no Sheets) */
11111    ['bookProps', false], /* only try to get properties (no Sheets) */
11112    ['bookFiles', false], /* include raw file structure (keys, files, cfb) */
11113    ['bookVBA', false], /* include vba raw data (vbaraw) */
11114
11115    ['password',''], /* password */
11116    ['WTF', false] /* WTF mode (throws errors) */
11117]);
11118
11119
11120var fix_write_opts = fix_opts_func([
11121    ['cellDates', false], /* write date cells with type `d` */
11122
11123    ['bookSST', false], /* Generate Shared String Table */
11124
11125    ['bookType', 'xlsx'], /* Type of workbook (xlsx/m/b) */
11126
11127    ['WTF', false] /* WTF mode (throws errors) */
11128]);
11129function safe_parse_wbrels(wbrels, sheets) {
11130    if(!wbrels) return 0;
11131    try {
11132        wbrels = sheets.map(function pwbr(w) { return [w.name, wbrels['!id'][w.id].Target]; });
11133    } catch(e) { return null; }
11134    return !wbrels || wbrels.length === 0 ? null : wbrels;
11135}
11136
11137function safe_parse_ws(zip, path, relsPath, sheet, sheetRels, sheets, opts) {
11138    try {
11139        sheetRels[sheet]=parse_rels(getzipdata(zip, relsPath, true), path);
11140        sheets[sheet]=parse_ws(getzipdata(zip, path),path,opts,sheetRels[sheet]);
11141    } catch(e) { if(opts.WTF) throw e; }
11142}
11143
11144var nodirs = function nodirs(x){return x.substr(-1) != '/';};
11145function parse_zip(zip, opts) {
11146    make_ssf(SSF);
11147    opts = opts || {};
11148    fix_read_opts(opts);
11149    reset_cp();
11150
11151    /* OpenDocument Part 3 Section 2.2.1 OpenDocument Package */
11152    if(safegetzipfile(zip, 'META-INF/manifest.xml')) return parse_ods(zip, opts);
11153
11154    var entries = keys(zip.files).filter(nodirs).sort();
11155    var dir = parse_ct(getzipdata(zip, '[Content_Types].xml'), opts);
11156    var xlsb = false;
11157    var sheets, binname;
11158    if(dir.workbooks.length === 0) {
11159        binname = "xl/workbook.xml";
11160        if(getzipdata(zip,binname, true)) dir.workbooks.push(binname);
11161    }
11162    if(dir.workbooks.length === 0) {
11163        binname = "xl/workbook.bin";
11164        if(!getzipfile(zip,binname,true)) throw new Error("Could not find workbook");
11165        dir.workbooks.push(binname);
11166        xlsb = true;
11167    }
11168    if(dir.workbooks[0].substr(-3) == "bin") xlsb = true;
11169    if(xlsb) set_cp(1200);
11170
11171    if(!opts.bookSheets && !opts.bookProps) {
11172        strs = [];
11173        if(dir.sst) strs=parse_sst(getzipdata(zip, dir.sst.replace(/^\//,'')), dir.sst, opts);
11174
11175        styles = {};
11176        if(dir.style) styles = parse_sty(getzipdata(zip, dir.style.replace(/^\//,'')),dir.style, opts);
11177
11178        themes = {};
11179        if(opts.cellStyles && dir.themes.length) themes = parse_theme(getzipdata(zip, dir.themes[0].replace(/^\//,''), true),dir.themes[0], opts);
11180    }
11181
11182    var wb = parse_wb(getzipdata(zip, dir.workbooks[0].replace(/^\//,'')), dir.workbooks[0], opts);
11183
11184    var props = {}, propdata = "";
11185
11186    if(dir.coreprops.length !== 0) {
11187        propdata = getzipdata(zip, dir.coreprops[0].replace(/^\//,''), true);
11188        if(propdata) props = parse_core_props(propdata);
11189        if(dir.extprops.length !== 0) {
11190            propdata = getzipdata(zip, dir.extprops[0].replace(/^\//,''), true);
11191            if(propdata) parse_ext_props(propdata, props);
11192        }
11193    }
11194
11195    var custprops = {};
11196    if(!opts.bookSheets || opts.bookProps) {
11197        if (dir.custprops.length !== 0) {
11198            propdata = getzipdata(zip, dir.custprops[0].replace(/^\//,''), true);
11199            if(propdata) custprops = parse_cust_props(propdata, opts);
11200        }
11201    }
11202
11203    var out = {};
11204    if(opts.bookSheets || opts.bookProps) {
11205        if(props.Worksheets && props.SheetNames.length > 0) sheets=props.SheetNames;
11206        else if(wb.Sheets) sheets = wb.Sheets.map(function pluck(x){ return x.name; });
11207        if(opts.bookProps) { out.Props = props; out.Custprops = custprops; }
11208        if(typeof sheets !== 'undefined') out.SheetNames = sheets;
11209        if(opts.bookSheets ? out.SheetNames : opts.bookProps) return out;
11210    }
11211    sheets = {};
11212
11213    var deps = {};
11214    if(opts.bookDeps && dir.calcchain) deps=parse_cc(getzipdata(zip, dir.calcchain.replace(/^\//,'')),dir.calcchain,opts);
11215
11216    var i=0;
11217    var sheetRels = {};
11218    var path, relsPath;
11219    if(!props.Worksheets) {
11220        var wbsheets = wb.Sheets;
11221        props.Worksheets = wbsheets.length;
11222        props.SheetNames = [];
11223        for(var j = 0; j != wbsheets.length; ++j) {
11224            props.SheetNames[j] = wbsheets[j].name;
11225        }
11226    }
11227
11228    var wbext = xlsb ? "bin" : "xml";
11229    var wbrelsfile = 'xl/_rels/workbook.' + wbext + '.rels';
11230    var wbrels = parse_rels(getzipdata(zip, wbrelsfile, true), wbrelsfile);
11231    if(wbrels) wbrels = safe_parse_wbrels(wbrels, wb.Sheets);
11232    /* Numbers iOS hack */
11233    var nmode = (getzipdata(zip,"xl/worksheets/sheet.xml",true))?1:0;
11234    for(i = 0; i != props.Worksheets; ++i) {
11235        if(wbrels) path = 'xl/' + (wbrels[i][1]).replace(/[\/]?xl\//, "");
11236        else {
11237            path = 'xl/worksheets/sheet'+(i+1-nmode)+"." + wbext;
11238            path = path.replace(/sheet0\./,"sheet.");
11239        }
11240        relsPath = path.replace(/^(.*)(\/)([^\/]*)$/, "$1/_rels/$3.rels");
11241        safe_parse_ws(zip, path, relsPath, props.SheetNames[i], sheetRels, sheets, opts);
11242    }
11243
11244    if(dir.comments) parse_comments(zip, dir.comments, sheets, sheetRels, opts);
11245
11246    out = {
11247        Directory: dir,
11248        Workbook: wb,
11249        Props: props,
11250        Custprops: custprops,
11251        Deps: deps,
11252        Sheets: sheets,
11253        SheetNames: props.SheetNames,
11254        Strings: strs,
11255        Styles: styles,
11256        Themes: themes,
11257        SSF: SSF.get_table()
11258    };
11259    if(opts.bookFiles) {
11260        out.keys = entries;
11261        out.files = zip.files;
11262    }
11263    if(opts.bookVBA) {
11264        if(dir.vba.length > 0) out.vbaraw = getzipdata(zip,dir.vba[0],true);
11265        else if(dir.defaults.bin === 'application/vnd.ms-office.vbaProject') out.vbaraw = getzipdata(zip,'xl/vbaProject.bin',true);
11266    }
11267    return out;
11268}
11269function add_rels(rels, rId, f, type, relobj) {
11270    if(!relobj) relobj = {};
11271    if(!rels['!id']) rels['!id'] = {};
11272    relobj.Id = 'rId' + rId;
11273    relobj.Type = type;
11274    relobj.Target = f;
11275    if(rels['!id'][relobj.Id]) throw new Error("Cannot rewrite rId " + rId);
11276    rels['!id'][relobj.Id] = relobj;
11277    rels[('/' + relobj.Target).replace("//","/")] = relobj;
11278}
11279
11280function write_zip(wb, opts) {
11281    if(wb && !wb.SSF) {
11282        wb.SSF = SSF.get_table();
11283    }
11284    if(wb && wb.SSF) {
11285        make_ssf(SSF); SSF.load_table(wb.SSF);
11286        opts.revssf = evert_num(wb.SSF); opts.revssf[wb.SSF[65535]] = 0;
11287    }
11288    opts.rels = {}; opts.wbrels = {};
11289    opts.Strings = []; opts.Strings.Count = 0; opts.Strings.Unique = 0;
11290    var wbext = opts.bookType == "xlsb" ? "bin" : "xml";
11291    var ct = { workbooks: [], sheets: [], calcchains: [], themes: [], styles: [],
11292        coreprops: [], extprops: [], custprops: [], strs:[], comments: [], vba: [],
11293        TODO:[], rels:[], xmlns: "" };
11294    fix_write_opts(opts = opts || {});
11295    var zip = new jszip();
11296    var f = "", rId = 0;
11297
11298    opts.cellXfs = [];
11299    get_cell_style(opts.cellXfs, {}, {revssf:{"General":0}});
11300
11301    f = "docProps/core.xml";
11302    zip.file(f, write_core_props(wb.Props, opts));
11303    ct.coreprops.push(f);
11304    add_rels(opts.rels, 2, f, RELS.CORE_PROPS);
11305
11306    f = "docProps/app.xml";
11307    if(!wb.Props) wb.Props = {};
11308    wb.Props.SheetNames = wb.SheetNames;
11309    wb.Props.Worksheets = wb.SheetNames.length;
11310    zip.file(f, write_ext_props(wb.Props, opts));
11311    ct.extprops.push(f);
11312    add_rels(opts.rels, 3, f, RELS.EXT_PROPS);
11313
11314    if(wb.Custprops !== wb.Props && keys(wb.Custprops||{}).length > 0) {
11315        f = "docProps/custom.xml";
11316        zip.file(f, write_cust_props(wb.Custprops, opts));
11317        ct.custprops.push(f);
11318        add_rels(opts.rels, 4, f, RELS.CUST_PROPS);
11319    }
11320
11321    f = "xl/workbook." + wbext;
11322    zip.file(f, write_wb(wb, f, opts));
11323    ct.workbooks.push(f);
11324    add_rels(opts.rels, 1, f, RELS.WB);
11325
11326    for(rId=1;rId <= wb.SheetNames.length; ++rId) {
11327        f = "xl/worksheets/sheet" + rId + "." + wbext;
11328        zip.file(f, write_ws(rId-1, f, opts, wb));
11329        ct.sheets.push(f);
11330        add_rels(opts.wbrels, rId, "worksheets/sheet" + rId + "." + wbext, RELS.WS);
11331    }
11332
11333    if(opts.Strings != null && opts.Strings.length > 0) {
11334        f = "xl/sharedStrings." + wbext;
11335        zip.file(f, write_sst(opts.Strings, f, opts));
11336        ct.strs.push(f);
11337        add_rels(opts.wbrels, ++rId, "sharedStrings." + wbext, RELS.SST);
11338    }
11339
11340    /* TODO: something more intelligent with themes */
11341
11342    f = "xl/theme/theme1.xml";
11343    zip.file(f, write_theme());
11344    ct.themes.push(f);
11345    add_rels(opts.wbrels, ++rId, "theme/theme1.xml", RELS.THEME);
11346
11347    /* TODO: something more intelligent with styles */
11348
11349    f = "xl/styles." + wbext;
11350    zip.file(f, write_sty(wb, f, opts));
11351    ct.styles.push(f);
11352    add_rels(opts.wbrels, ++rId, "styles." + wbext, RELS.STY);
11353
11354    zip.file("[Content_Types].xml", write_ct(ct, opts));
11355    zip.file('_rels/.rels', write_rels(opts.rels));
11356    zip.file('xl/_rels/workbook.' + wbext + '.rels', write_rels(opts.wbrels));
11357    return zip;
11358}
11359function firstbyte(f,o) {
11360    switch((o||{}).type || "base64") {
11361        case 'buffer': return f[0];
11362        case 'base64': return Base64.decode(f.substr(0,12)).charCodeAt(0);
11363        case 'binary': return f.charCodeAt(0);
11364        case 'array': return f[0];
11365        default: throw new Error("Unrecognized type " + o.type);
11366    }
11367}
11368
11369function read_zip(data, opts) {
11370    var zip, d = data;
11371    var o = opts||{};
11372    if(!o.type) o.type = (has_buf && Buffer.isBuffer(data)) ? "buffer" : "base64";
11373    switch(o.type) {
11374        case "base64": zip = new jszip(d, { base64:true }); break;
11375        case "binary": case "array": zip = new jszip(d, { base64:false }); break;
11376        case "buffer": zip = new jszip(d); break;
11377        case "file": zip=new jszip(d=_fs.readFileSync(data)); break;
11378        default: throw new Error("Unrecognized type " + o.type);
11379    }
11380    return parse_zip(zip, o);
11381}
11382
11383function readSync(data, opts) {
11384    var zip, d = data, isfile = false, n;
11385    var o = opts||{};
11386    if(!o.type) o.type = (has_buf && Buffer.isBuffer(data)) ? "buffer" : "base64";
11387    if(o.type == "file") { isfile = true; o.type = "buffer"; d = _fs.readFileSync(data); }
11388    switch((n = firstbyte(d, o))) {
11389        case 0xD0:
11390            if(isfile) o.type = "file";
11391            return parse_xlscfb(CFB.read(data, o), o);
11392        case 0x09: return parse_xlscfb(s2a(o.type === 'base64' ? Base64.decode(data) : data), o);
11393        case 0x3C: return parse_xlml(d, o);
11394        case 0x50:
11395            if(isfile) o.type = "file";
11396            return read_zip(data, opts);
11397        default: throw new Error("Unsupported file " + n);
11398    }
11399}
11400
11401function readFileSync(data, opts) {
11402    var o = opts||{}; o.type = 'file';
11403    return readSync(data, o);
11404}
11405function write_zip_type(wb, opts) {
11406    var o = opts||{};
11407    var z = write_zip(wb, o);
11408    switch(o.type) {
11409        case "base64": return z.generate({type:"base64"});
11410        case "binary": return z.generate({type:"string"});
11411        case "buffer": return z.generate({type:"nodebuffer"});
11412        case "file": return _fs.writeFileSync(o.file, z.generate({type:"nodebuffer"}));
11413        default: throw new Error("Unrecognized type " + o.type);
11414    }
11415}
11416
11417function writeSync(wb, opts) {
11418    var o = opts||{};
11419    switch(o.bookType) {
11420        case 'xml': return write_xlml(wb, o);
11421        default: return write_zip_type(wb, o);
11422    }
11423}
11424
11425function writeFileSync(wb, filename, opts) {
11426    var o = opts||{}; o.type = 'file';
11427    o.file = filename;
11428    switch(o.file.substr(-5).toLowerCase()) {
11429        case '.xlsx': o.bookType = 'xlsx'; break;
11430        case '.xlsm': o.bookType = 'xlsm'; break;
11431        case '.xlsb': o.bookType = 'xlsb'; break;
11432    default: switch(o.file.substr(-4).toLowerCase()) {
11433        case '.xls': o.bookType = 'xls'; break;
11434        case '.xml': o.bookType = 'xml'; break;
11435    }}
11436    return writeSync(wb, o);
11437}
11438
11439function decode_row(rowstr) { return parseInt(unfix_row(rowstr),10) - 1; }
11440function encode_row(row) { return "" + (row + 1); }
11441function fix_row(cstr) { return cstr.replace(/([A-Z]|^)(\d+)$/,"$1$$$2"); }
11442function unfix_row(cstr) { return cstr.replace(/\$(\d+)$/,"$1"); }
11443
11444function decode_col(colstr) { var c = unfix_col(colstr), d = 0, i = 0; for(; i !== c.length; ++i) d = 26*d + c.charCodeAt(i) - 64; return d - 1; }
11445function encode_col(col) { var s=""; for(++col; col; col=Math.floor((col-1)/26)) s = String.fromCharCode(((col-1)%26) + 65) + s; return s; }
11446function fix_col(cstr) { return cstr.replace(/^([A-Z])/,"$$$1"); }
11447function unfix_col(cstr) { return cstr.replace(/^\$([A-Z])/,"$1"); }
11448
11449function split_cell(cstr) { return cstr.replace(/(\$?[A-Z]*)(\$?\d*)/,"$1,$2").split(","); }
11450function decode_cell(cstr) { var splt = split_cell(cstr); return { c:decode_col(splt[0]), r:decode_row(splt[1]) }; }
11451function encode_cell(cell) { return encode_col(cell.c) + encode_row(cell.r); }
11452function fix_cell(cstr) { return fix_col(fix_row(cstr)); }
11453function unfix_cell(cstr) { return unfix_col(unfix_row(cstr)); }
11454function decode_range(range) { var x =range.split(":").map(decode_cell); return {s:x[0],e:x[x.length-1]}; }
11455function encode_range(cs,ce) {
11456    if(ce === undefined || typeof ce === 'number') return encode_range(cs.s, cs.e);
11457    if(typeof cs !== 'string') cs = encode_cell(cs); if(typeof ce !== 'string') ce = encode_cell(ce);
11458    return cs == ce ? cs : cs + ":" + ce;
11459}
11460
11461function safe_decode_range(range) {
11462    var o = {s:{c:0,r:0},e:{c:0,r:0}};
11463    var idx = 0, i = 0, cc = 0;
11464    var len = range.length;
11465    for(idx = 0; i < len; ++i) {
11466        if((cc=range.charCodeAt(i)-64) < 1 || cc > 26) break;
11467        idx = 26*idx + cc;
11468    }
11469    o.s.c = --idx;
11470
11471    for(idx = 0; i < len; ++i) {
11472        if((cc=range.charCodeAt(i)-48) < 0 || cc > 9) break;
11473        idx = 10*idx + cc;
11474    }
11475    o.s.r = --idx;
11476
11477    if(i === len || range.charCodeAt(++i) === 58) { o.e.c=o.s.c; o.e.r=o.s.r; return o; }
11478
11479    for(idx = 0; i != len; ++i) {
11480        if((cc=range.charCodeAt(i)-64) < 1 || cc > 26) break;
11481        idx = 26*idx + cc;
11482    }
11483    o.e.c = --idx;
11484
11485    for(idx = 0; i != len; ++i) {
11486        if((cc=range.charCodeAt(i)-48) < 0 || cc > 9) break;
11487        idx = 10*idx + cc;
11488    }
11489    o.e.r = --idx;
11490    return o;
11491}
11492
11493function safe_format_cell(cell, v) {
11494    if(cell.z !== undefined) try { return (cell.w = SSF.format(cell.z, v)); } catch(e) { }
11495    if(!cell.XF) return v;
11496    try { return (cell.w = SSF.format(cell.XF.ifmt||0, v)); } catch(e) { return ''+v; }
11497}
11498
11499function format_cell(cell, v) {
11500    if(cell == null || cell.t == null) return "";
11501    if(cell.w !== undefined) return cell.w;
11502    if(v === undefined) return safe_format_cell(cell, cell.v);
11503    return safe_format_cell(cell, v);
11504}
11505
11506function sheet_to_json(sheet, opts){
11507    var val, row, range, header = 0, offset = 1, r, hdr = [], isempty, R, C, v;
11508    var o = opts != null ? opts : {};
11509    var raw = o.raw;
11510    if(sheet == null || sheet["!ref"] == null) return [];
11511    range = o.range !== undefined ? o.range : sheet["!ref"];
11512    if(o.header === 1) header = 1;
11513    else if(o.header === "A") header = 2;
11514    else if(Array.isArray(o.header)) header = 3;
11515    switch(typeof range) {
11516        case 'string': r = safe_decode_range(range); break;
11517        case 'number': r = safe_decode_range(sheet["!ref"]); r.s.r = range; break;
11518        default: r = range;
11519    }
11520    if(header > 0) offset = 0;
11521    var rr = encode_row(r.s.r);
11522    var cols = new Array(r.e.c-r.s.c+1);
11523    var out = new Array(r.e.r-r.s.r-offset+1);
11524    var outi = 0;
11525    for(C = r.s.c; C <= r.e.c; ++C) {
11526        cols[C] = encode_col(C);
11527        val = sheet[cols[C] + rr];
11528        switch(header) {
11529            case 1: hdr[C] = C; break;
11530            case 2: hdr[C] = cols[C]; break;
11531            case 3: hdr[C] = o.header[C - r.s.c]; break;
11532            default:
11533                if(val === undefined) continue;
11534                hdr[C] = format_cell(val);
11535        }
11536    }
11537
11538    for (R = r.s.r + offset; R <= r.e.r; ++R) {
11539        rr = encode_row(R);
11540        isempty = true;
11541        if(header === 1) row = [];
11542        else {
11543            row = {};
11544            if(Object.defineProperty) Object.defineProperty(row, '__rowNum__', {value:R, enumerable:false});
11545            else row.__rowNum__ = R;
11546        }
11547        for (C = r.s.c; C <= r.e.c; ++C) {
11548            val = sheet[cols[C] + rr];
11549            if(val === undefined || val.t === undefined) continue;
11550            v = val.v;
11551            switch(val.t){
11552                case 'e': continue;
11553                case 's': break;
11554                case 'b': case 'n': break;
11555                default: throw 'unrecognized type ' + val.t;
11556            }
11557            if(v !== undefined) {
11558                row[hdr[C]] = raw ? v : format_cell(val,v);
11559                isempty = false;
11560            }
11561        }
11562        if(isempty === false || header === 1) out[outi++] = row;
11563    }
11564    out.length = outi;
11565    return out;
11566}
11567
11568function sheet_to_row_object_array(sheet, opts) { return sheet_to_json(sheet, opts != null ? opts : {}); }
11569
11570function sheet_to_csv(sheet, opts) {
11571    var out = "", txt = "", qreg = /"/g;
11572    var o = opts == null ? {} : opts;
11573    if(sheet == null || sheet["!ref"] == null) return "";
11574    var r = safe_decode_range(sheet["!ref"]);
11575    var FS = o.FS !== undefined ? o.FS : ",", fs = FS.charCodeAt(0);
11576    var RS = o.RS !== undefined ? o.RS : "\n", rs = RS.charCodeAt(0);
11577    var row = "", rr = "", cols = [];
11578    var i = 0, cc = 0, val;
11579    var R = 0, C = 0;
11580    for(C = r.s.c; C <= r.e.c; ++C) cols[C] = encode_col(C);
11581    for(R = r.s.r; R <= r.e.r; ++R) {
11582        row = "";
11583        rr = encode_row(R);
11584        for(C = r.s.c; C <= r.e.c; ++C) {
11585            val = sheet[cols[C] + rr];
11586            txt = val !== undefined ? ''+format_cell(val) : "";
11587            for(i = 0, cc = 0; i !== txt.length; ++i) if((cc = txt.charCodeAt(i)) === fs || cc === rs || cc === 34) {
11588                txt = "\"" + txt.replace(qreg, '""') + "\""; break; }
11589            row += (C === r.s.c ? "" : FS) + txt;
11590        }
11591        out += row + RS;
11592    }
11593    return out;
11594}
11595var make_csv = sheet_to_csv;
11596
11597function sheet_to_formulae(sheet) {
11598    var cmds, y = "", x, val="";
11599    if(sheet == null || sheet["!ref"] == null) return "";
11600    var r = safe_decode_range(sheet['!ref']), rr = "", cols = [], C;
11601    cmds = new Array((r.e.r-r.s.r+1)*(r.e.c-r.s.c+1));
11602    var i = 0;
11603    for(C = r.s.c; C <= r.e.c; ++C) cols[C] = encode_col(C);
11604    for(var R = r.s.r; R <= r.e.r; ++R) {
11605        rr = encode_row(R);
11606        for(C = r.s.c; C <= r.e.c; ++C) {
11607            y = cols[C] + rr;
11608            x = sheet[y];
11609            val = "";
11610            if(x === undefined) continue;
11611            if(x.f != null) val = x.f;
11612            else if(x.w !== undefined) val = "'" + x.w;
11613            else if(x.v === undefined) continue;
11614            else val = ""+x.v;
11615            cmds[i++] = y + "=" + val;
11616        }
11617    }
11618    cmds.length = i;
11619    return cmds;
11620}
11621
11622var utils = {
11623    encode_col: encode_col,
11624    encode_row: encode_row,
11625    encode_cell: encode_cell,
11626    encode_range: encode_range,
11627    decode_col: decode_col,
11628    decode_row: decode_row,
11629    split_cell: split_cell,
11630    decode_cell: decode_cell,
11631    decode_range: decode_range,
11632    format_cell: format_cell,
11633    get_formulae: sheet_to_formulae,
11634    make_csv: sheet_to_csv,
11635    make_json: sheet_to_json,
11636    make_formulae: sheet_to_formulae,
11637    sheet_to_csv: sheet_to_csv,
11638    sheet_to_json: sheet_to_json,
11639    sheet_to_formulae: sheet_to_formulae,
11640    sheet_to_row_object_array: sheet_to_row_object_array
11641};
11642XLSX.parse_xlscfb = parse_xlscfb;
11643XLSX.parse_zip = parse_zip;
11644XLSX.read = readSync; //xlsread
11645XLSX.readFile = readFileSync; //readFile
11646XLSX.readFileSync = readFileSync;
11647XLSX.write = writeSync;
11648XLSX.writeFile = writeFileSync;
11649XLSX.writeFileSync = writeFileSync;
11650XLSX.utils = utils;
11651XLSX.CFB = CFB;
11652XLSX.SSF = SSF;
11653})(typeof exports !== 'undefined' ? exports : XLSX);
11654var XLS = XLSX;