/*! xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
/* vim: set ts=2: */
/*exported XLSX */
/*global global, exports, module, require:false, process:false, Buffer:false, ArrayBuffer:false */
var XLSX = {};
function make_xlsx_lib(XLSX) {
    XLSX.version = '0.14.1';
    var current_codepage = 1200,
        current_ansi = 1252;
    /*global cptable:true, window */
    if (typeof module !== "undefined" && typeof require !== 'undefined') {
        if (typeof cptable === 'undefined') {
            if (typeof global !== 'undefined') global.cptable = require('./dist/cpexcel.js');
            else if (typeof window !== 'undefined') window.cptable = require('./dist/cpexcel.js');
        }
    }
    var VALID_ANSI = [874, 932, 936, 949, 950];
    for (var i = 0; i <= 8; ++i) VALID_ANSI.push(1250 + i);
    /* ECMA-376 Part I 18.4.1 charset to codepage mapping */
    var CS2CP = ({
        0: 1252,
        /* ANSI */
        1: 65001,
        /* DEFAULT */
        2: 65001,
        /* SYMBOL */
        77: 10000,
        /* MAC */
        128: 932,
        /* SHIFTJIS */
        129: 949,
        /* HANGUL */
        130: 1361,
        /* JOHAB */
        134: 936,
        /* GB2312 */
        136: 950,
        /* CHINESEBIG5 */
        161: 1253,
        /* GREEK */
        162: 1254,
        /* TURKISH */
        163: 1258,
        /* VIETNAMESE */
        177: 1255,
        /* HEBREW */
        178: 1256,
        /* ARABIC */
        186: 1257,
        /* BALTIC */
        204: 1251,
        /* RUSSIAN */
        222: 874,
        /* THAI */
        238: 1250,
        /* EASTEUROPE */
        255: 1252,
        /* OEM */
        69: 6969 /* MISC */
    });
    var set_ansi = function (cp) {
        if (VALID_ANSI.indexOf(cp) == -1) return;
        current_ansi = CS2CP[0] = cp;
    };
    function reset_ansi() {
        set_ansi(1252);
    }
    var set_cp = function (cp) {
        current_codepage = cp;
        set_ansi(cp);
    };
    function reset_cp() {
        set_cp(1200);
        reset_ansi();
    }
    function char_codes(data) {
        var o = [];
        for (var i = 0, len = data.length; i < len; ++i) o[i] = data.charCodeAt(i);
        return o;
    }
    function utf16leread(data) {
        var o = [];
        for (var i = 0; i < (data.length >> 1); ++i) o[i] = String.fromCharCode(data.charCodeAt(2 * i) + (data.charCodeAt(2 * i + 1) << 8));
        return o.join("");
    }
    function utf16beread(data) {
        var o = [];
        for (var i = 0; i < (data.length >> 1); ++i) o[i] = String.fromCharCode(data.charCodeAt(2 * i + 1) + (data.charCodeAt(2 * i) << 8));
        return o.join("");
    }
    var debom = function (data) {
        var c1 = data.charCodeAt(0),
            c2 = data.charCodeAt(1);
        if (c1 == 0xFF && c2 == 0xFE) return utf16leread(data.slice(2));
        if (c1 == 0xFE && c2 == 0xFF) return utf16beread(data.slice(2));
        if (c1 == 0xFEFF) return data.slice(1);
        return data;
    };
    var _getchar = function _gc1(x) {
        return String.fromCharCode(x);
    };
    if (typeof cptable !== 'undefined') {
        set_cp = function (cp) {
            current_codepage = cp;
        };
        debom = function (data) {
            if (data.charCodeAt(0) === 0xFF && data.charCodeAt(1) === 0xFE) {
                return cptable.utils.decode(1200, char_codes(data.slice(2)));
            }
            return data;
        };
        _getchar = function _gc2(x) {
            if (current_codepage === 1200) return String.fromCharCode(x);
            return cptable.utils.decode(current_codepage, [x & 255, x >> 8])[0];
        };
    }
    var DENSE = null;
    var DIF_XL = true;
    var Base64 = (function make_b64() {
        var map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
        return {
            encode: function (input) {
                var o = "";
                var c1 = 0,
                    c2 = 0,
                    c3 = 0,
                    e1 = 0,
                    e2 = 0,
                    e3 = 0,
                    e4 = 0;
                for (var i = 0; i < input.length;) {
                    c1 = input.charCodeAt(i++);
                    e1 = (c1 >> 2);
                    c2 = input.charCodeAt(i++);
                    e2 = ((c1 & 3) << 4) | (c2 >> 4);
                    c3 = input.charCodeAt(i++);
                    e3 = ((c2 & 15) << 2) | (c3 >> 6);
                    e4 = (c3 & 63);
                    if (isNaN(c2)) {
                        e3 = e4 = 64;
                    } else if (isNaN(c3)) {
                        e4 = 64;
                    }
                    o += map.charAt(e1) + map.charAt(e2) + map.charAt(e3) + map.charAt(e4);
                }
                return o;
            },
            decode: function b64_decode(input) {
                var o = "";
                var c1 = 0,
                    c2 = 0,
                    c3 = 0,
                    e1 = 0,
                    e2 = 0,
                    e3 = 0,
                    e4 = 0;
                input = input.replace(/[^\w\+\/\=]/g, "");
                for (var i = 0; i < input.length;) {
                    e1 = map.indexOf(input.charAt(i++));
                    e2 = map.indexOf(input.charAt(i++));
                    c1 = (e1 << 2) | (e2 >> 4);
                    o += String.fromCharCode(c1);
                    e3 = map.indexOf(input.charAt(i++));
                    c2 = ((e2 & 15) << 4) | (e3 >> 2);
                    if (e3 !== 64) {
                        o += String.fromCharCode(c2);
                    }
                    e4 = map.indexOf(input.charAt(i++));
                    c3 = ((e3 & 3) << 6) | e4;
                    if (e4 !== 64) {
                        o += String.fromCharCode(c3);
                    }
                }
                return o;
            }
        };
    })();
    var has_buf = (typeof Buffer !== 'undefined' && typeof process !== 'undefined' && typeof process.versions !== 'undefined' && !!process.versions.node);
    var Buffer_from = function () {};
    if (typeof Buffer !== 'undefined') {
        var nbfs = !Buffer.from;
        if (!nbfs) try {
            Buffer.from("foo", "utf8");
        } catch (e) {
            nbfs = true;
        }
        Buffer_from = nbfs ? function (buf, enc) {
            return (enc) ? new Buffer(buf, enc) : new Buffer(buf);
        } : Buffer.from.bind(Buffer);
        // $FlowIgnore
        if (!Buffer.alloc) Buffer.alloc = function (n) {
            return new Buffer(n);
        };
        // $FlowIgnore
        if (!Buffer.allocUnsafe) Buffer.allocUnsafe = function (n) {
            return new Buffer(n);
        };
    }
    function new_raw_buf(len) {
        /* jshint -W056 */
        return has_buf ? Buffer.alloc(len) : new Array(len);
        /* jshint +W056 */
    }
    function new_unsafe_buf(len) {
        /* jshint -W056 */
        return has_buf ? Buffer.allocUnsafe(len) : new Array(len);
        /* jshint +W056 */
    }
    var s2a = function s2a(s) {
        // $FlowIgnore
        if (has_buf) return Buffer_from(s, "binary");
        return s.split("").map(function (x) {
            return x.charCodeAt(0) & 0xff;
        });
    };
    function s2ab(s) {
        if (typeof ArrayBuffer === 'undefined') return s2a(s);
        var buf = new ArrayBuffer(s.length),
            view = new Uint8Array(buf);
        for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
        return buf;
    }
    function a2s(data) {
        if (Array.isArray(data)) return data.map(_chr).join("");
        var o = [];
        for (var i = 0; i < data.length; ++i) o[i] = _chr(data[i]);
        return o.join("");
    }
    function a2u(data) {
        if (typeof Uint8Array === 'undefined') throw new Error("Unsupported");
        return new Uint8Array(data);
    }
    function ab2a(data) {
        if (typeof ArrayBuffer == 'undefined') throw new Error("Unsupported");
        if (data instanceof ArrayBuffer) return ab2a(new Uint8Array(data));
        var o = new Array(data.length);
        for (var i = 0; i < data.length; ++i) o[i] = data[i];
        return o;
    }
    var bconcat = function (bufs) {
        return [].concat.apply([], bufs);
    };
    var chr0 = /\u0000/g,
        chr1 = /[\u0001-\u0006]/g;
    /* ssf.js (C) 2013-present SheetJS -- http://sheetjs.com */
    /*jshint -W041 */
    var SSF = ({});
    var make_ssf = function make_ssf(SSF) {
        SSF.version = '0.10.2';
        function _strrev(x) {
            var o = "",
                i = x.length - 1;
            while (i >= 0) o += x.charAt(i--);
            return o;
        }
        function fill(c, l) {
            var o = "";
            while (o.length < l) o += c;
            return o;
        }
        function pad0(v, d) {
            var t = "" + v;
            return t.length >= d ? t : fill('0', d - t.length) + t;
        }
        function pad_(v, d) {
            var t = "" + v;
            return t.length >= d ? t : fill(' ', d - t.length) + t;
        }
        function rpad_(v, d) {
            var t = "" + v;
            return t.length >= d ? t : t + fill(' ', d - t.length);
        }
        function pad0r1(v, d) {
            var t = "" + Math.round(v);
            return t.length >= d ? t : fill('0', d - t.length) + t;
        }
        function pad0r2(v, d) {
            var t = "" + v;
            return t.length >= d ? t : fill('0', d - t.length) + t;
        }
        var p2_32 = Math.pow(2, 32);
        function pad0r(v, d) {
            if (v > p2_32 || v < -p2_32) return pad0r1(v, d);
            var i = Math.round(v);
            return pad0r2(i, d);
        }
        function isgeneral(s, i) {
            i = i || 0;
            return s.length >= 7 + i && (s.charCodeAt(i) | 32) === 103 && (s.charCodeAt(i + 1) | 32) === 101 && (s.charCodeAt(i + 2) | 32) === 110 && (s.charCodeAt(i + 3) | 32) === 101 && (s.charCodeAt(i + 4) | 32) === 114 && (s.charCodeAt(i + 5) | 32) === 97 && (s.charCodeAt(i + 6) | 32) === 108;
        }
        var days = [
            ['Sun', 'Sunday'],
            ['Mon', 'Monday'],
            ['Tue', 'Tuesday'],
            ['Wed', 'Wednesday'],
            ['Thu', 'Thursday'],
            ['Fri', 'Friday'],
            ['Sat', 'Saturday']
        ];
        var months = [
            ['J', 'Jan', 'January'],
            ['F', 'Feb', 'February'],
            ['M', 'Mar', 'March'],
            ['A', 'Apr', 'April'],
            ['M', 'May', 'May'],
            ['J', 'Jun', 'June'],
            ['J', 'Jul', 'July'],
            ['A', 'Aug', 'August'],
            ['S', 'Sep', 'September'],
            ['O', 'Oct', 'October'],
            ['N', 'Nov', 'November'],
            ['D', 'Dec', 'December']
        ];
        function init_table(t) {
            t[0] = 'General';
            t[1] = '0';
            t[2] = '0.00';
            t[3] = '#,##0';
            t[4] = '#,##0.00';
            t[9] = '0%';
            t[10] = '0.00%';
            t[11] = '0.00E+00';
            t[12] = '# ?/?';
            t[13] = '# ??/??';
            t[14] = 'm/d/yy';
            t[15] = 'd-mmm-yy';
            t[16] = 'd-mmm';
            t[17] = 'mmm-yy';
            t[18] = 'h:mm AM/PM';
            t[19] = 'h:mm:ss AM/PM';
            t[20] = 'h:mm';
            t[21] = 'h:mm:ss';
            t[22] = 'm/d/yy h:mm';
            t[37] = '#,##0 ;(#,##0)';
            t[38] = '#,##0 ;[Red](#,##0)';
            t[39] = '#,##0.00;(#,##0.00)';
            t[40] = '#,##0.00;[Red](#,##0.00)';
            t[45] = 'mm:ss';
            t[46] = '[h]:mm:ss';
            t[47] = 'mmss.0';
            t[48] = '##0.0E+0';
            t[49] = '@';
            t[56] = '"上午/下午 "hh"時"mm"分"ss"秒 "';
            t[65535] = 'General';
        }
        var table_fmt = {};
        init_table(table_fmt);
        function frac(x, D, mixed) {
            var sgn = x < 0 ? -1 : 1;
            var B = x * sgn;
            var P_2 = 0,
                P_1 = 1,
                P = 0;
            var Q_2 = 1,
                Q_1 = 0,
                Q = 0;
            var A = Math.floor(B);
            while (Q_1 < D) {
                A = Math.floor(B);
                P = A * P_1 + P_2;
                Q = A * Q_1 + Q_2;
                if ((B - A) < 0.00000005) break;
                B = 1 / (B - A);
                P_2 = P_1;
                P_1 = P;
                Q_2 = Q_1;
                Q_1 = Q;
            }
            if (Q > D) {
                if (Q_1 > D) {
                    Q = Q_2;
                    P = P_2;
                } else {
                    Q = Q_1;
                    P = P_1;
                }
            }
            if (!mixed) return [0, sgn * P, Q];
            var q = Math.floor(sgn * P / Q);
            return [q, sgn * P - q * Q, Q];
        }
        function parse_date_code(v, opts, b2) {
            if (v > 2958465 || v < 0) return null;
            var date = (v | 0),
                time = Math.floor(86400 * (v - date)),
                dow = 0;
            var dout = [];
            var out = {
                D: date,
                T: time,
                u: 86400 * (v - date) - time,
                y: 0,
                m: 0,
                d: 0,
                H: 0,
                M: 0,
                S: 0,
                q: 0
            };
            if (Math.abs(out.u) < 1e-6) out.u = 0;
            if (opts && opts.date1904) date += 1462;
            if (out.u > 0.9999) {
                out.u = 0;
                if (++time == 86400) {
                    out.T = time = 0;
                    ++date;
                    ++out.D;
                }
            }
            if (date === 60) {
                dout = b2 ? [1317, 10, 29] : [1900, 2, 29];
                dow = 3;
            } else if (date === 0) {
                dout = b2 ? [1317, 8, 29] : [1900, 1, 0];
                dow = 6;
            } else {
                if (date > 60) --date;
                /* 1 = Jan 1 1900 in Gregorian */
                var d = new Date(1900, 0, 1);
                d.setDate(d.getDate() + date - 1);
                dout = [d.getFullYear(), d.getMonth() + 1, d.getDate()];
                dow = d.getDay();
                if (date < 60) dow = (dow + 6) % 7;
                if (b2) dow = fix_hijri(d, dout);
            }
            out.y = dout[0];
            out.m = dout[1];
            out.d = dout[2];
            out.S = time % 60;
            time = Math.floor(time / 60);
            out.M = time % 60;
            time = Math.floor(time / 60);
            out.H = time;
            out.q = dow;
            return out;
        }
        SSF.parse_date_code = parse_date_code;
        var basedate = new Date(1899, 11, 31, 0, 0, 0);
        var dnthresh = basedate.getTime();
        var base1904 = new Date(1900, 2, 1, 0, 0, 0);
        function datenum_local(v, date1904) {
            var epoch = v.getTime();
            if (date1904) epoch -= 1461 * 24 * 60 * 60 * 1000;
            else if (v >= base1904) epoch += 24 * 60 * 60 * 1000;
            return (epoch - (dnthresh + (v.getTimezoneOffset() - basedate.getTimezoneOffset()) * 60000)) / (24 * 60 * 60 * 1000);
        }
        function general_fmt_int(v) {
            return v.toString(10);
        }
        SSF._general_int = general_fmt_int;
        var general_fmt_num = (function make_general_fmt_num() {
            var gnr1 = /\.(\d*[1-9])0+$/,
                gnr2 = /\.0*$/,
                gnr4 = /\.(\d*[1-9])0+/,
                gnr5 = /\.0*[Ee]/,
                gnr6 = /(E[+-])(\d)$/;
            function gfn2(v) {
                var w = (v < 0 ? 12 : 11);
                var o = gfn5(v.toFixed(12));
                if (o.length <= w) return o;
                o = v.toPrecision(10);
                if (o.length <= w) return o;
                return v.toExponential(5);
            }
            function gfn3(v) {
                var o = v.toFixed(11).replace(gnr1, ".$1");
                if (o.length > (v < 0 ? 12 : 11)) o = v.toPrecision(6);
                return o;
            }
            function gfn4(o) {
                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");
                return o;
            }
            function gfn5(o) {
                return o.indexOf(".") > -1 ? o.replace(gnr2, "").replace(gnr1, ".$1") : o;
            }
            return function general_fmt_num(v) {
                var V = Math.floor(Math.log(Math.abs(v)) * Math.LOG10E),
                    o;
                if (V >= -4 && V <= -1) o = v.toPrecision(10 + V);
                else if (Math.abs(V) <= 9) o = gfn2(v);
                else if (V === 10) o = v.toFixed(10).substr(0, 12);
                else o = gfn3(v);
                return gfn5(gfn4(o));
            };
        })();
        SSF._general_num = general_fmt_num;
        function general_fmt(v, opts) {
            switch (typeof v) {
                case 'string':
                    return v;
                case 'boolean':
                    return v ? "TRUE" : "FALSE";
                case 'number':
                    return (v | 0) === v ? general_fmt_int(v) : general_fmt_num(v);
                case 'undefined':
                    return "";
                case 'object':
                    if (v == null) return "";
                    if (v instanceof Date) return format(14, datenum_local(v, opts && opts.date1904), opts);
            }
            throw new Error("unsupported value in General format: " + v);
        }
        SSF._general = general_fmt;
        function fix_hijri() {
            return 0;
        }
        /*jshint -W086 */
        function write_date(type, fmt, val, ss0) {
            var o = "",
                ss = 0,
                tt = 0,
                y = val.y,
                out, outl = 0;
            switch (type) {
                case 98:
                    /* 'b' buddhist year */
                    y = val.y + 543;
                    /* falls through */
                case 121:
                    /* 'y' year */
                    switch (fmt.length) {
                        case 1:
                        case 2:
                            out = y % 100;
                            outl = 2;
                            break;
                        default:
                            out = y % 10000;
                            outl = 4;
                            break;
                    }
                    break;
                case 109:
                    /* 'm' month */
                    switch (fmt.length) {
                        case 1:
                        case 2:
                            out = val.m;
                            outl = fmt.length;
                            break;
                        case 3:
                            return months[val.m - 1][1];
                        case 5:
                            return months[val.m - 1][0];
                        default:
                            return months[val.m - 1][2];
                    }
                    break;
                case 100:
                    /* 'd' day */
                    switch (fmt.length) {
                        case 1:
                        case 2:
                            out = val.d;
                            outl = fmt.length;
                            break;
                        case 3:
                            return days[val.q][0];
                        default:
                            return days[val.q][1];
                    }
                    break;
                case 104:
                    /* 'h' 12-hour */
                    switch (fmt.length) {
                        case 1:
                        case 2:
                            out = 1 + (val.H + 11) % 12;
                            outl = fmt.length;
                            break;
                        default:
                            throw 'bad hour format: ' + fmt;
                    }
                    break;
                case 72:
                    /* 'H' 24-hour */
                    switch (fmt.length) {
                        case 1:
                        case 2:
                            out = val.H;
                            outl = fmt.length;
                            break;
                        default:
                            throw 'bad hour format: ' + fmt;
                    }
                    break;
                case 77:
                    /* 'M' minutes */
                    switch (fmt.length) {
                        case 1:
                        case 2:
                            out = val.M;
                            outl = fmt.length;
                            break;
                        default:
                            throw 'bad minute format: ' + fmt;
                    }
                    break;
                case 115:
                    /* 's' seconds */
                    if (fmt != 's' && fmt != 'ss' && fmt != '.0' && fmt != '.00' && fmt != '.000') throw 'bad second format: ' + fmt;
                    if (val.u === 0 && (fmt == "s" || fmt == "ss")) return pad0(val.S, fmt.length);
                    if (ss0 >= 2) tt = ss0 === 3 ? 1000 : 100;
                    else tt = ss0 === 1 ? 10 : 1;
                    ss = Math.round((tt) * (val.S + val.u));
                    if (ss >= 60 * tt) ss = 0;
                    if (fmt === 's') return ss === 0 ? "0" : "" + ss / tt;
                    o = pad0(ss, 2 + ss0);
                    if (fmt === 'ss') return o.substr(0, 2);
                    return "." + o.substr(2, fmt.length - 1);
                case 90:
                    /* 'Z' absolute time */
                    switch (fmt) {
                        case '[h]':
                        case '[hh]':
                            out = val.D * 24 + val.H;
                            break;
                        case '[m]':
                        case '[mm]':
                            out = (val.D * 24 + val.H) * 60 + val.M;
                            break;
                        case '[s]':
                        case '[ss]':
                            out = ((val.D * 24 + val.H) * 60 + val.M) * 60 + Math.round(val.S + val.u);
                            break;
                        default:
                            throw 'bad abstime format: ' + fmt;
                    }
                    outl = fmt.length === 3 ? 1 : 2;
                    break;
                case 101:
                    /* 'e' era */
                    out = y;
                    outl = 1;
            }
            if (outl > 0) return pad0(out, outl);
            else return "";
        }
        /*jshint +W086 */
        function commaify(s) {
            var w = 3;
            if (s.length <= w) return s;
            var j = (s.length % w),
                o = s.substr(0, j);
            for (; j != s.length; j += w) o += (o.length > 0 ? "," : "") + s.substr(j, w);
            return o;
        }
        var write_num = (function make_write_num() {
            var pct1 = /%/g;
            function write_num_pct(type, fmt, val) {
                var sfmt = fmt.replace(pct1, ""),
                    mul = fmt.length - sfmt.length;
                return write_num(type, sfmt, val * Math.pow(10, 2 * mul)) + fill("%", mul);
            }
            function write_num_cm(type, fmt, val) {
                var idx = fmt.length - 1;
                while (fmt.charCodeAt(idx - 1) === 44) --idx;
                return write_num(type, fmt.substr(0, idx), val / Math.pow(10, 3 * (fmt.length - idx)));
            }
            function write_num_exp(fmt, val) {
                var o;
                var idx = fmt.indexOf("E") - fmt.indexOf(".") - 1;
                if (fmt.match(/^#+0.0E\+0$/)) {
                    if (val == 0) return "0.0E+0";
                    else if (val < 0) return "-" + write_num_exp(fmt, -val);
                    var period = fmt.indexOf(".");
                    if (period === -1) period = fmt.indexOf('E');
                    var ee = Math.floor(Math.log(val) * Math.LOG10E) % period;
                    if (ee < 0) ee += period;
                    o = (val / Math.pow(10, ee)).toPrecision(idx + 1 + (period + ee) % period);
                    if (o.indexOf("e") === -1) {
                        var fakee = Math.floor(Math.log(val) * Math.LOG10E);
                        if (o.indexOf(".") === -1) o = o.charAt(0) + "." + o.substr(1) + "E+" + (fakee - o.length + ee);
                        else o += "E+" + (fakee - ee);
                        while (o.substr(0, 2) === "0.") {
                            o = o.charAt(0) + o.substr(2, period) + "." + o.substr(2 + period);
                            o = o.replace(/^0+([1-9])/, "$1").replace(/^0+\./, "0.");
                        }
                        o = o.replace(/\+-/, "-");
                    }
                    o = o.replace(/^([+-]?)(\d*)\.(\d*)[Ee]/, function ($$, $1, $2, $3) {
                        return $1 + $2 + $3.substr(0, (period + ee) % period) + "." + $3.substr(ee) + "E";
                    });
                } else o = val.toExponential(idx);
                if (fmt.match(/E\+00$/) && o.match(/e[+-]\d$/)) o = o.substr(0, o.length - 1) + "0" + o.charAt(o.length - 1);
                if (fmt.match(/E\-/) && o.match(/e\+/)) o = o.replace(/e\+/, "e");
                return o.replace("e", "E");
            }
            var frac1 = /# (\?+)( ?)\/( ?)(\d+)/;
            function write_num_f1(r, aval, sign) {
                var den = parseInt(r[4], 10),
                    rr = Math.round(aval * den),
                    base = Math.floor(rr / den);
                var myn = (rr - base * den),
                    myd = den;
                return sign + (base === 0 ? "" : "" + base) + " " + (myn === 0 ? fill(" ", r[1].length + 1 + r[4].length) : pad_(myn, r[1].length) + r[2] + "/" + r[3] + pad0(myd, r[4].length));
            }
            function write_num_f2(r, aval, sign) {
                return sign + (aval === 0 ? "" : "" + aval) + fill(" ", r[1].length + 2 + r[4].length);
            }
            var dec1 = /^#*0*\.([0#]+)/;
            var closeparen = /\).*[0#]/;
            var phone = /\(###\) ###\\?-####/;
            function hashq(str) {
                var o = "",
                    cc;
                for (var i = 0; i != str.length; ++i) switch ((cc = str.charCodeAt(i))) {
                    case 35:
                        break;
                    case 63:
                        o += " ";
                        break;
                    case 48:
                        o += "0";
                        break;
                    default:
                        o += String.fromCharCode(cc);
                }
                return o;
            }
            function rnd(val, d) {
                var dd = Math.pow(10, d);
                return "" + (Math.round(val * dd) / dd);
            }
            function dec(val, d) {
                if (d < ('' + Math.round((val - Math.floor(val)) * Math.pow(10, d))).length) {
                    return 0;
                }
                return Math.round((val - Math.floor(val)) * Math.pow(10, d));
            }
            function carry(val, d) {
                if (d < ('' + Math.round((val - Math.floor(val)) * Math.pow(10, d))).length) {
                    return 1;
                }
                return 0;
            }
            function flr(val) {
                if (val < 2147483647 && val > -2147483648) return "" + (val >= 0 ? (val | 0) : (val - 1 | 0));
                return "" + Math.floor(val);
            }
            function write_num_flt(type, fmt, val) {
                if (type.charCodeAt(0) === 40 && !fmt.match(closeparen)) {
                    var ffmt = fmt.replace(/\( */, "").replace(/ \)/, "").replace(/\)/, "");
                    if (val >= 0) return write_num_flt('n', ffmt, val);
                    return '(' + write_num_flt('n', ffmt, -val) + ')';
                }
                if (fmt.charCodeAt(fmt.length - 1) === 44) return write_num_cm(type, fmt, val);
                if (fmt.indexOf('%') !== -1) return write_num_pct(type, fmt, val);
                if (fmt.indexOf('E') !== -1) return write_num_exp(fmt, val);
                if (fmt.charCodeAt(0) === 36) return "$" + write_num_flt(type, fmt.substr(fmt.charAt(1) == ' ' ? 2 : 1), val);
                var o;
                var r, ri, ff, aval = Math.abs(val),
                    sign = val < 0 ? "-" : "";
                if (fmt.match(/^00+$/)) return sign + pad0r(aval, fmt.length);
                if (fmt.match(/^[#?]+$/)) {
                    o = pad0r(val, 0);
                    if (o === "0") o = "";
                    return o.length > fmt.length ? o : hashq(fmt.substr(0, fmt.length - o.length)) + o;
                }
                if ((r = fmt.match(frac1))) return write_num_f1(r, aval, sign);
                if (fmt.match(/^#+0+$/)) return sign + pad0r(aval, fmt.length - fmt.indexOf("0"));
                if ((r = fmt.match(dec1))) {
                    o = rnd(val, r[1].length).replace(/^([^\.]+)$/, "$1." + hashq(r[1])).replace(/\.$/, "." + hashq(r[1])).replace(/\.(\d*)$/, function ($$, $1) {
                        return "." + $1 + fill("0", hashq(r[1]).length - $1.length);
                    });
                    return fmt.indexOf("0.") !== -1 ? o : o.replace(/^0\./, ".");
                }
                fmt = fmt.replace(/^#+([0.])/, "$1");
                if ((r = fmt.match(/^(0*)\.(#*)$/))) {
                    return sign + rnd(aval, r[2].length).replace(/\.(\d*[1-9])0*$/, ".$1").replace(/^(-?\d*)$/, "$1.").replace(/^0\./, r[1].length ? "0." : ".");
                }
                if ((r = fmt.match(/^#{1,3},##0(\.?)$/))) return sign + commaify(pad0r(aval, 0));
                if ((r = fmt.match(/^#,##0\.([#0]*0)$/))) {
                    return val < 0 ? "-" + write_num_flt(type, fmt, -val) : commaify("" + (Math.floor(val) + carry(val, r[1].length))) + "." + pad0(dec(val, r[1].length), r[1].length);
                }
                if ((r = fmt.match(/^#,#*,#0/))) return write_num_flt(type, fmt.replace(/^#,#*,/, ""), val);
                if ((r = fmt.match(/^([0#]+)(\\?-([0#]+))+$/))) {
                    o = _strrev(write_num_flt(type, fmt.replace(/[\\-]/g, ""), val));
                    ri = 0;
                    return _strrev(_strrev(fmt.replace(/\\/g, "")).replace(/[0#]/g, function (x) {
                        return ri < o.length ? o.charAt(ri++) : x === '0' ? '0' : "";
                    }));
                }
                if (fmt.match(phone)) {
                    o = write_num_flt(type, "##########", val);
                    return "(" + o.substr(0, 3) + ") " + o.substr(3, 3) + "-" + o.substr(6);
                }
                var oa = "";
                if ((r = fmt.match(/^([#0?]+)( ?)\/( ?)([#0?]+)/))) {
                    ri = Math.min(r[4].length, 7);
                    ff = frac(aval, Math.pow(10, ri) - 1, false);
                    o = "" + sign;
                    oa = write_num("n", r[1], ff[1]);
                    if (oa.charAt(oa.length - 1) == " ") oa = oa.substr(0, oa.length - 1) + "0";
                    o += oa + r[2] + "/" + r[3];
                    oa = rpad_(ff[2], ri);
                    if (oa.length < r[4].length) oa = hashq(r[4].substr(r[4].length - oa.length)) + oa;
                    o += oa;
                    return o;
                }
                if ((r = fmt.match(/^# ([#0?]+)( ?)\/( ?)([#0?]+)/))) {
                    ri = Math.min(Math.max(r[1].length, r[4].length), 7);
                    ff = frac(aval, Math.pow(10, ri) - 1, true);
                    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));
                }
                if ((r = fmt.match(/^[#0?]+$/))) {
                    o = pad0r(val, 0);
                    if (fmt.length <= o.length) return o;
                    return hashq(fmt.substr(0, fmt.length - o.length)) + o;
                }
                if ((r = fmt.match(/^([#0?]+)\.([#0]+)$/))) {
                    o = "" + val.toFixed(Math.min(r[2].length, 10)).replace(/([^0])0+$/, "$1");
                    ri = o.indexOf(".");
                    var lres = fmt.indexOf(".") - ri,
                        rres = fmt.length - o.length - lres;
                    return hashq(fmt.substr(0, lres) + o + fmt.substr(fmt.length - rres));
                }
                if ((r = fmt.match(/^00,000\.([#0]*0)$/))) {
                    ri = dec(val, r[1].length);
                    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);
                }
                switch (fmt) {
                    case "###,##0.00":
                        return write_num_flt(type, "#,##0.00", val);
                    case "###,###":
                    case "##,###":
                    case "#,###":
                        var x = commaify(pad0r(aval, 0));
                        return x !== "0" ? sign + x : "";
                    case "###,###.00":
                        return write_num_flt(type, "###,##0.00", val).replace(/^0\./, ".");
                    case "#,###.00":
                        return write_num_flt(type, "#,##0.00", val).replace(/^0\./, ".");
                    default:
                }
                throw new Error("unsupported format |" + fmt + "|");
            }
            function write_num_cm2(type, fmt, val) {
                var idx = fmt.length - 1;
                while (fmt.charCodeAt(idx - 1) === 44) --idx;
                return write_num(type, fmt.substr(0, idx), val / Math.pow(10, 3 * (fmt.length - idx)));
            }
            function write_num_pct2(type, fmt, val) {
                var sfmt = fmt.replace(pct1, ""),
                    mul = fmt.length - sfmt.length;
                return write_num(type, sfmt, val * Math.pow(10, 2 * mul)) + fill("%", mul);
            }
            function write_num_exp2(fmt, val) {
                var o;
                var idx = fmt.indexOf("E") - fmt.indexOf(".") - 1;
                if (fmt.match(/^#+0.0E\+0$/)) {
                    if (val == 0) return "0.0E+0";
                    else if (val < 0) return "-" + write_num_exp2(fmt, -val);
                    var period = fmt.indexOf(".");
                    if (period === -1) period = fmt.indexOf('E');
                    var ee = Math.floor(Math.log(val) * Math.LOG10E) % period;
                    if (ee < 0) ee += period;
                    o = (val / Math.pow(10, ee)).toPrecision(idx + 1 + (period + ee) % period);
                    if (!o.match(/[Ee]/)) {
                        var fakee = Math.floor(Math.log(val) * Math.LOG10E);
                        if (o.indexOf(".") === -1) o = o.charAt(0) + "." + o.substr(1) + "E+" + (fakee - o.length + ee);
                        else o += "E+" + (fakee - ee);
                        o = o.replace(/\+-/, "-");
                    }
                    o = o.replace(/^([+-]?)(\d*)\.(\d*)[Ee]/, function ($$, $1, $2, $3) {
                        return $1 + $2 + $3.substr(0, (period + ee) % period) + "." + $3.substr(ee) + "E";
                    });
                } else o = val.toExponential(idx);
                if (fmt.match(/E\+00$/) && o.match(/e[+-]\d$/)) o = o.substr(0, o.length - 1) + "0" + o.charAt(o.length - 1);
                if (fmt.match(/E\-/) && o.match(/e\+/)) o = o.replace(/e\+/, "e");
                return o.replace("e", "E");
            }
            function write_num_int(type, fmt, val) {
                if (type.charCodeAt(0) === 40 && !fmt.match(closeparen)) {
                    var ffmt = fmt.replace(/\( */, "").replace(/ \)/, "").replace(/\)/, "");
                    if (val >= 0) return write_num_int('n', ffmt, val);
                    return '(' + write_num_int('n', ffmt, -val) + ')';
                }
                if (fmt.charCodeAt(fmt.length - 1) === 44) return write_num_cm2(type, fmt, val);
                if (fmt.indexOf('%') !== -1) return write_num_pct2(type, fmt, val);
                if (fmt.indexOf('E') !== -1) return write_num_exp2(fmt, val);
                if (fmt.charCodeAt(0) === 36) return "$" + write_num_int(type, fmt.substr(fmt.charAt(1) == ' ' ? 2 : 1), val);
                var o;
                var r, ri, ff, aval = Math.abs(val),
                    sign = val < 0 ? "-" : "";
                if (fmt.match(/^00+$/)) return sign + pad0(aval, fmt.length);
                if (fmt.match(/^[#?]+$/)) {
                    o = ("" + val);
                    if (val === 0) o = "";
                    return o.length > fmt.length ? o : hashq(fmt.substr(0, fmt.length - o.length)) + o;
                }
                if ((r = fmt.match(frac1))) return write_num_f2(r, aval, sign);
                if (fmt.match(/^#+0+$/)) return sign + pad0(aval, fmt.length - fmt.indexOf("0"));
                if ((r = fmt.match(dec1))) {
                    o = ("" + val).replace(/^([^\.]+)$/, "$1." + hashq(r[1])).replace(/\.$/, "." + hashq(r[1]));
                    o = o.replace(/\.(\d*)$/, function ($$, $1) {
                        return "." + $1 + fill("0", hashq(r[1]).length - $1.length);
                    });
                    return fmt.indexOf("0.") !== -1 ? o : o.replace(/^0\./, ".");
                }
                fmt = fmt.replace(/^#+([0.])/, "$1");
                if ((r = fmt.match(/^(0*)\.(#*)$/))) {
                    return sign + ("" + aval).replace(/\.(\d*[1-9])0*$/, ".$1").replace(/^(-?\d*)$/, "$1.").replace(/^0\./, r[1].length ? "0." : ".");
                }
                if ((r = fmt.match(/^#{1,3},##0(\.?)$/))) return sign + commaify(("" + aval));
                if ((r = fmt.match(/^#,##0\.([#0]*0)$/))) {
                    return val < 0 ? "-" + write_num_int(type, fmt, -val) : commaify(("" + val)) + "." + fill('0', r[1].length);
                }
                if ((r = fmt.match(/^#,#*,#0/))) return write_num_int(type, fmt.replace(/^#,#*,/, ""), val);
                if ((r = fmt.match(/^([0#]+)(\\?-([0#]+))+$/))) {
                    o = _strrev(write_num_int(type, fmt.replace(/[\\-]/g, ""), val));
                    ri = 0;
                    return _strrev(_strrev(fmt.replace(/\\/g, "")).replace(/[0#]/g, function (x) {
                        return ri < o.length ? o.charAt(ri++) : x === '0' ? '0' : "";
                    }));
                }
                if (fmt.match(phone)) {
                    o = write_num_int(type, "##########", val);
                    return "(" + o.substr(0, 3) + ") " + o.substr(3, 3) + "-" + o.substr(6);
                }
                var oa = "";
                if ((r = fmt.match(/^([#0?]+)( ?)\/( ?)([#0?]+)/))) {
                    ri = Math.min(r[4].length, 7);
                    ff = frac(aval, Math.pow(10, ri) - 1, false);
                    o = "" + sign;
                    oa = write_num("n", r[1], ff[1]);
                    if (oa.charAt(oa.length - 1) == " ") oa = oa.substr(0, oa.length - 1) + "0";
                    o += oa + r[2] + "/" + r[3];
                    oa = rpad_(ff[2], ri);
                    if (oa.length < r[4].length) oa = hashq(r[4].substr(r[4].length - oa.length)) + oa;
                    o += oa;
                    return o;
                }
                if ((r = fmt.match(/^# ([#0?]+)( ?)\/( ?)([#0?]+)/))) {
                    ri = Math.min(Math.max(r[1].length, r[4].length), 7);
                    ff = frac(aval, Math.pow(10, ri) - 1, true);
                    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));
                }
                if ((r = fmt.match(/^[#0?]+$/))) {
                    o = "" + val;
                    if (fmt.length <= o.length) return o;
                    return hashq(fmt.substr(0, fmt.length - o.length)) + o;
                }
                if ((r = fmt.match(/^([#0]+)\.([#0]+)$/))) {
                    o = "" + val.toFixed(Math.min(r[2].length, 10)).replace(/([^0])0+$/, "$1");
                    ri = o.indexOf(".");
                    var lres = fmt.indexOf(".") - ri,
                        rres = fmt.length - o.length - lres;
                    return hashq(fmt.substr(0, lres) + o + fmt.substr(fmt.length - rres));
                }
                if ((r = fmt.match(/^00,000\.([#0]*0)$/))) {
                    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);
                }
                switch (fmt) {
                    case "###,###":
                    case "##,###":
                    case "#,###":
                        var x = commaify("" + aval);
                        return x !== "0" ? sign + x : "";
                    default:
                        if (fmt.match(/\.[0#?]*$/)) return write_num_int(type, fmt.slice(0, fmt.lastIndexOf(".")), val) + hashq(fmt.slice(fmt.lastIndexOf(".")));
                }
                throw new Error("unsupported format |" + fmt + "|");
            }
            return function write_num(type, fmt, val) {
                return (val | 0) === val ? write_num_int(type, fmt, val) : write_num_flt(type, fmt, val);
            };
        })();
        function split_fmt(fmt) {
            var out = [];
            var in_str = false /*, cc*/ ;
            for (var i = 0, j = 0; i < fmt.length; ++i) switch (( /*cc=*/ fmt.charCodeAt(i))) {
                case 34:
                    /* '"' */
                    in_str = !in_str;
                    break;
                case 95:
                case 42:
                case 92:
                    /* '_' '*' '\\' */
                    ++i;
                    break;
                case 59:
                    /* ';' */
                    out[out.length] = fmt.substr(j, i - j);
                    j = i + 1;
            }
            out[out.length] = fmt.substr(j);
            if (in_str === true) throw new Error("Format |" + fmt + "| unterminated string ");
            return out;
        }
        SSF._split = split_fmt;
        var abstime = /\[[HhMmSs]*\]/;
        function fmt_is_date(fmt) {
            var i = 0,
                /*cc = 0,*/ c = "",
                o = "";
            while (i < fmt.length) {
                switch ((c = fmt.charAt(i))) {
                    case 'G':
                        if (isgeneral(fmt, i)) i += 6;
                        i++;
                        break;
                    case '"':
                        for (;
                            ( /*cc=*/ fmt.charCodeAt(++i)) !== 34 && i < fmt.length;) ++i;
                        ++i;
                        break;
                    case '\\':
                        i += 2;
                        break;
                    case '_':
                        i += 2;
                        break;
                    case '@':
                        ++i;
                        break;
                    case 'B':
                    case 'b':
                        if (fmt.charAt(i + 1) === "1" || fmt.charAt(i + 1) === "2") return true;
                        /* falls through */
                    case 'M':
                    case 'D':
                    case 'Y':
                    case 'H':
                    case 'S':
                    case 'E':
                        /* falls through */
                    case 'm':
                    case 'd':
                    case 'y':
                    case 'h':
                    case 's':
                    case 'e':
                    case 'g':
                        return true;
                    case 'A':
                    case 'a':
                        if (fmt.substr(i, 3).toUpperCase() === "A/P") return true;
                        if (fmt.substr(i, 5).toUpperCase() === "AM/PM") return true;
                        ++i;
                        break;
                    case '[':
                        o = c;
                        while (fmt.charAt(i++) !== ']' && i < fmt.length) o += fmt.charAt(i);
                        if (o.match(abstime)) return true;
                        break;
                    case '.':
                        /* falls through */
                    case '0':
                    case '#':
                        while (i < fmt.length && ("0#?.,E+-%".indexOf(c = fmt.charAt(++i)) > -1 || (c == '\\' && fmt.charAt(i + 1) == "-" && "0#".indexOf(fmt.charAt(i + 2)) > -1))) { /* empty */ }
                        break;
                    case '?':
                        while (fmt.charAt(++i) === c) { /* empty */ }
                        break;
                    case '*':
                        ++i;
                        if (fmt.charAt(i) == ' ' || fmt.charAt(i) == '*') ++i;
                        break;
                    case '(':
                    case ')':
                        ++i;
                        break;
                    case '1':
                    case '2':
                    case '3':
                    case '4':
                    case '5':
                    case '6':
                    case '7':
                    case '8':
                    case '9':
                        while (i < fmt.length && "0123456789".indexOf(fmt.charAt(++i)) > -1) { /* empty */ }
                        break;
                    case ' ':
                        ++i;
                        break;
                    default:
                        ++i;
                        break;
                }
            }
            return false;
        }
        SSF.is_date = fmt_is_date;
        function eval_fmt(fmt, v, opts, flen) {
            var out = [],
                o = "",
                i = 0,
                c = "",
                lst = 't',
                dt, j, cc;
            var hr = 'H';
            /* Tokenize */
            while (i < fmt.length) {
                switch ((c = fmt.charAt(i))) {
                    case 'G':
                        /* General */
                        if (!isgeneral(fmt, i)) throw new Error('unrecognized character ' + c + ' in ' + fmt);
                        out[out.length] = {
                            t: 'G',
                            v: 'General'
                        };
                        i += 7;
                        break;
                    case '"':
                        /* Literal text */
                        for (o = "";
                            (cc = fmt.charCodeAt(++i)) !== 34 && i < fmt.length;) o += String.fromCharCode(cc);
                        out[out.length] = {
                            t: 't',
                            v: o
                        };
                        ++i;
                        break;
                    case '\\':
                        var w = fmt.charAt(++i),
                            t = (w === "(" || w === ")") ? w : 't';
                        out[out.length] = {
                            t: t,
                            v: w
                        };
                        ++i;
                        break;
                    case '_':
                        out[out.length] = {
                            t: 't',
                            v: " "
                        };
                        i += 2;
                        break;
                    case '@':
                        /* Text Placeholder */
                        out[out.length] = {
                            t: 'T',
                            v: v
                        };
                        ++i;
                        break;
                    case 'B':
                    case 'b':
                        if (fmt.charAt(i + 1) === "1" || fmt.charAt(i + 1) === "2") {
                            if (dt == null) {
                                dt = parse_date_code(v, opts, fmt.charAt(i + 1) === "2");
                                if (dt == null) return "";
                            }
                            out[out.length] = {
                                t: 'X',
                                v: fmt.substr(i, 2)
                            };
                            lst = c;
                            i += 2;
                            break;
                        }
                        /* falls through */
                    case 'M':
                    case 'D':
                    case 'Y':
                    case 'H':
                    case 'S':
                    case 'E':
                        c = c.toLowerCase();
                        /* falls through */
                    case 'm':
                    case 'd':
                    case 'y':
                    case 'h':
                    case 's':
                    case 'e':
                    case 'g':
                        if (v < 0) return "";
                        if (dt == null) {
                            dt = parse_date_code(v, opts);
                            if (dt == null) return "";
                        }
                        o = c;
                        while (++i < fmt.length && fmt.charAt(i).toLowerCase() === c) o += c;
                        if (c === 'm' && lst.toLowerCase() === 'h') c = 'M';
                        if (c === 'h') c = hr;
                        out[out.length] = {
                            t: c,
                            v: o
                        };
                        lst = c;
                        break;
                    case 'A':
                    case 'a':
                        var q = {
                            t: c,
                            v: c
                        };
                        if (dt == null) dt = parse_date_code(v, opts);
                        if (fmt.substr(i, 3).toUpperCase() === "A/P") {
                            if (dt != null) q.v = dt.H >= 12 ? "P" : "A";
                            q.t = 'T';
                            hr = 'h';
                            i += 3;
                        } else if (fmt.substr(i, 5).toUpperCase() === "AM/PM") {
                            if (dt != null) q.v = dt.H >= 12 ? "PM" : "AM";
                            q.t = 'T';
                            i += 5;
                            hr = 'h';
                        } else {
                            q.t = "t";
                            ++i;
                        }
                        if (dt == null && q.t === 'T') return "";
                        out[out.length] = q;
                        lst = c;
                        break;
                    case '[':
                        o = c;
                        while (fmt.charAt(i++) !== ']' && i < fmt.length) o += fmt.charAt(i);
                        if (o.slice(-1) !== ']') throw 'unterminated "[" block: |' + o + '|';
                        if (o.match(abstime)) {
                            if (dt == null) {
                                dt = parse_date_code(v, opts);
                                if (dt == null) return "";
                            }
                            out[out.length] = {
                                t: 'Z',
                                v: o.toLowerCase()
                            };
                            lst = o.charAt(1);
                        } else if (o.indexOf("$") > -1) {
                            o = (o.match(/\$([^-\[\]]*)/) || [])[1] || "$";
                            if (!fmt_is_date(fmt)) out[out.length] = {
                                t: 't',
                                v: o
                            };
                        }
                        break;
                        /* Numbers */
                    case '.':
                        if (dt != null) {
                            o = c;
                            while (++i < fmt.length && (c = fmt.charAt(i)) === "0") o += c;
                            out[out.length] = {
                                t: 's',
                                v: o
                            };
                            break;
                        }
                        /* falls through */
                    case '0':
                    case '#':
                        o = c;
                        while ((++i < fmt.length && "0#?.,E+-%".indexOf(c = fmt.charAt(i)) > -1) || (c == '\\' && fmt.charAt(i + 1) == "-" && i < fmt.length - 2 && "0#".indexOf(fmt.charAt(i + 2)) > -1)) o += c;
                        out[out.length] = {
                            t: 'n',
                            v: o
                        };
                        break;
                    case '?':
                        o = c;
                        while (fmt.charAt(++i) === c) o += c;
                        out[out.length] = {
                            t: c,
                            v: o
                        };
                        lst = c;
                        break;
                    case '*':
                        ++i;
                        if (fmt.charAt(i) == ' ' || fmt.charAt(i) == '*') ++i;
                        break; // **
                    case '(':
                    case ')':
                        out[out.length] = {
                            t: (flen === 1 ? 't' : c),
                            v: c
                        };
                        ++i;
                        break;
                    case '1':
                    case '2':
                    case '3':
                    case '4':
                    case '5':
                    case '6':
                    case '7':
                    case '8':
                    case '9':
                        o = c;
                        while (i < fmt.length && "0123456789".indexOf(fmt.charAt(++i)) > -1) o += fmt.charAt(i);
                        out[out.length] = {
                            t: 'D',
                            v: o
                        };
                        break;
                    case ' ':
                        out[out.length] = {
                            t: c,
                            v: c
                        };
                        ++i;
                        break;
                    default:
                        if (",$-+/():!^&'~{}<>=€acfijklopqrtuvwxzP".indexOf(c) === -1) throw new Error('unrecognized character ' + c + ' in ' + fmt);
                        out[out.length] = {
                            t: 't',
                            v: c
                        };
                        ++i;
                        break;
                }
            }
            var bt = 0,
                ss0 = 0,
                ssm;
            for (i = out.length - 1, lst = 't'; i >= 0; --i) {
                switch (out[i].t) {
                    case 'h':
                    case 'H':
                        out[i].t = hr;
                        lst = 'h';
                        if (bt < 1) bt = 1;
                        break;
                    case 's':
                        if ((ssm = out[i].v.match(/\.0+$/))) ss0 = Math.max(ss0, ssm[0].length - 1);
                        if (bt < 3) bt = 3;
                        /* falls through */
                    case 'd':
                    case 'y':
                    case 'M':
                    case 'e':
                        lst = out[i].t;
                        break;
                    case 'm':
                        if (lst === 's') {
                            out[i].t = 'M';
                            if (bt < 2) bt = 2;
                        }
                        break;
                    case 'X':
                        /*if(out[i].v === "B2");*/
                        break;
                    case 'Z':
                        if (bt < 1 && out[i].v.match(/[Hh]/)) bt = 1;
                        if (bt < 2 && out[i].v.match(/[Mm]/)) bt = 2;
                        if (bt < 3 && out[i].v.match(/[Ss]/)) bt = 3;
                }
            }
            switch (bt) {
                case 0:
                    break;
                case 1:
                    if (dt.u >= 0.5) {
                        dt.u = 0;
                        ++dt.S;
                    }
                    if (dt.S >= 60) {
                        dt.S = 0;
                        ++dt.M;
                    }
                    if (dt.M >= 60) {
                        dt.M = 0;
                        ++dt.H;
                    }
                    break;
                case 2:
                    if (dt.u >= 0.5) {
                        dt.u = 0;
                        ++dt.S;
                    }
                    if (dt.S >= 60) {
                        dt.S = 0;
                        ++dt.M;
                    }
                    break;
            }
            /* replace fields */
            var nstr = "",
                jj;
            for (i = 0; i < out.length; ++i) {
                switch (out[i].t) {
                    case 't':
                    case 'T':
                    case ' ':
                    case 'D':
                        break;
                    case 'X':
                        out[i].v = "";
                        out[i].t = ";";
                        break;
                    case 'd':
                    case 'm':
                    case 'y':
                    case 'h':
                    case 'H':
                    case 'M':
                    case 's':
                    case 'e':
                    case 'b':
                    case 'Z':
                        out[i].v = write_date(out[i].t.charCodeAt(0), out[i].v, dt, ss0);
                        out[i].t = 't';
                        break;
                    case 'n':
                    case '(':
                    case '?':
                        jj = i + 1;
                        while (out[jj] != null && (
                                (c = out[jj].t) === "?" || c === "D" ||
                                ((c === " " || c === "t") && out[jj + 1] != null && (out[jj + 1].t === '?' || out[jj + 1].t === "t" && out[jj + 1].v === '/')) ||
                                (out[i].t === '(' && (c === ' ' || c === 'n' || c === ')')) ||
                                (c === 't' && (out[jj].v === '/' || out[jj].v === ' ' && out[jj + 1] != null && out[jj + 1].t == '?'))
                            )) {
                            out[i].v += out[jj].v;
                            out[jj] = {
                                v: "",
                                t: ";"
                            };
                            ++jj;
                        }
                        nstr += out[i].v;
                        i = jj - 1;
                        break;
                    case 'G':
                        out[i].t = 't';
                        out[i].v = general_fmt(v, opts);
                        break;
                }
            }
            var vv = "",
                myv, ostr;
            if (nstr.length > 0) {
                if (nstr.charCodeAt(0) == 40) /* '(' */ {
                    myv = (v < 0 && nstr.charCodeAt(0) === 45 ? -v : v);
                    ostr = write_num('(', nstr, myv);
                } else {
                    myv = (v < 0 && flen > 1 ? -v : v);
                    ostr = write_num('n', nstr, myv);
                    if (myv < 0 && out[0] && out[0].t == 't') {
                        ostr = ostr.substr(1);
                        out[0].v = "-" + out[0].v;
                    }
                }
                jj = ostr.length - 1;
                var decpt = out.length;
                for (i = 0; i < out.length; ++i)
                    if (out[i] != null && out[i].t != 't' && out[i].v.indexOf(".") > -1) {
                        decpt = i;
                        break;
                    }
                var lasti = out.length;
                if (decpt === out.length && ostr.indexOf("E") === -1) {
                    for (i = out.length - 1; i >= 0; --i) {
                        if (out[i] == null || 'n?('.indexOf(out[i].t) === -1) continue;
                        if (jj >= out[i].v.length - 1) {
                            jj -= out[i].v.length;
                            out[i].v = ostr.substr(jj + 1, out[i].v.length);
                        } else if (jj < 0) out[i].v = "";
                        else {
                            out[i].v = ostr.substr(0, jj + 1);
                            jj = -1;
                        }
                        out[i].t = 't';
                        lasti = i;
                    }
                    if (jj >= 0 && lasti < out.length) out[lasti].v = ostr.substr(0, jj + 1) + out[lasti].v;
                } else if (decpt !== out.length && ostr.indexOf("E") === -1) {
                    jj = ostr.indexOf(".") - 1;
                    for (i = decpt; i >= 0; --i) {
                        if (out[i] == null || 'n?('.indexOf(out[i].t) === -1) continue;
                        j = out[i].v.indexOf(".") > -1 && i === decpt ? out[i].v.indexOf(".") - 1 : out[i].v.length - 1;
                        vv = out[i].v.substr(j + 1);
                        for (; j >= 0; --j) {
                            if (jj >= 0 && (out[i].v.charAt(j) === "0" || out[i].v.charAt(j) === "#")) vv = ostr.charAt(jj--) + vv;
                        }
                        out[i].v = vv;
                        out[i].t = 't';
                        lasti = i;
                    }
                    if (jj >= 0 && lasti < out.length) out[lasti].v = ostr.substr(0, jj + 1) + out[lasti].v;
                    jj = ostr.indexOf(".") + 1;
                    for (i = decpt; i < out.length; ++i) {
                        if (out[i] == null || ('n?('.indexOf(out[i].t) === -1 && i !== decpt)) continue;
                        j = out[i].v.indexOf(".") > -1 && i === decpt ? out[i].v.indexOf(".") + 1 : 0;
                        vv = out[i].v.substr(0, j);
                        for (; j < out[i].v.length; ++j) {
                            if (jj < ostr.length) vv += ostr.charAt(jj++);
                        }
                        out[i].v = vv;
                        out[i].t = 't';
                        lasti = i;
                    }
                }
            }
            for (i = 0; i < out.length; ++i)
                if (out[i] != null && 'n(?'.indexOf(out[i].t) > -1) {
                    myv = (flen > 1 && v < 0 && i > 0 && out[i - 1].v === "-" ? -v : v);
                    out[i].v = write_num(out[i].t, out[i].v, myv);
                    out[i].t = 't';
                }
            var retval = "";
            for (i = 0; i !== out.length; ++i)
                if (out[i] != null) retval += out[i].v;
            return retval;
        }
        SSF._eval = eval_fmt;
        var cfregex = /\[[=<>]/;
        var cfregex2 = /\[(=|>[=]?|<[>=]?)(-?\d+(?:\.\d*)?)\]/;
        function chkcond(v, rr) {
            if (rr == null) return false;
            var thresh = parseFloat(rr[2]);
            switch (rr[1]) {
                case "=":
                    if (v == thresh) return true;
                    break;
                case ">":
                    if (v > thresh) return true;
                    break;
                case "<":
                    if (v < thresh) return true;
                    break;
                case "<>":
                    if (v != thresh) return true;
                    break;
                case ">=":
                    if (v >= thresh) return true;
                    break;
                case "<=":
                    if (v <= thresh) return true;
                    break;
            }
            return false;
        }
        function choose_fmt(f, v) {
            var fmt = split_fmt(f);
            var l = fmt.length,
                lat = fmt[l - 1].indexOf("@");
            if (l < 4 && lat > -1) --l;
            if (fmt.length > 4) throw new Error("cannot find right format for |" + fmt.join("|") + "|");
            if (typeof v !== "number") return [4, fmt.length === 4 || lat > -1 ? fmt[fmt.length - 1] : "@"];
            switch (fmt.length) {
                case 1:
                    fmt = lat > -1 ? ["General", "General", "General", fmt[0]] : [fmt[0], fmt[0], fmt[0], "@"];
                    break;
                case 2:
                    fmt = lat > -1 ? [fmt[0], fmt[0], fmt[0], fmt[1]] : [fmt[0], fmt[1], fmt[0], "@"];
                    break;
                case 3:
                    fmt = lat > -1 ? [fmt[0], fmt[1], fmt[0], fmt[2]] : [fmt[0], fmt[1], fmt[2], "@"];
                    break;
                case 4:
                    break;
            }
            var ff = v > 0 ? fmt[0] : v < 0 ? fmt[1] : fmt[2];
            if (fmt[0].indexOf("[") === -1 && fmt[1].indexOf("[") === -1) return [l, ff];
            if (fmt[0].match(cfregex) != null || fmt[1].match(cfregex) != null) {
                var m1 = fmt[0].match(cfregex2);
                var m2 = fmt[1].match(cfregex2);
                return chkcond(v, m1) ? [l, fmt[0]] : chkcond(v, m2) ? [l, fmt[1]] : [l, fmt[m1 != null && m2 != null ? 2 : 1]];
            }
            return [l, ff];
        }
        function format(fmt, v, o) {
            if (o == null) o = {};
            var sfmt = "";
            switch (typeof fmt) {
                case "string":
                    if (fmt == "m/d/yy" && o.dateNF) sfmt = o.dateNF;
                    else sfmt = fmt;
                    break;
                case "number":
                    if (fmt == 14 && o.dateNF) sfmt = o.dateNF;
                    else sfmt = (o.table != null ? (o.table) : table_fmt)[fmt];
                    break;
            }
            if (isgeneral(sfmt, 0)) return general_fmt(v, o);
            if (v instanceof Date) v = datenum_local(v, o.date1904);
            var f = choose_fmt(sfmt, v);
            if (isgeneral(f[1])) return general_fmt(v, o);
            if (v === true) v = "TRUE";
            else if (v === false) v = "FALSE";
            else if (v === "" || v == null) return "";
            return eval_fmt(f[1], v, o, f[0]);
        }
        function load_entry(fmt, idx) {
            if (typeof idx != 'number') {
                idx = +idx || -1;
                for (var i = 0; i < 0x0188; ++i) {
                    if (table_fmt[i] == undefined) {
                        if (idx < 0) idx = i;
                        continue;
                    }
                    if (table_fmt[i] == fmt) {
                        idx = i;
                        break;
                    }
                }
                if (idx < 0) idx = 0x187;
            }
            table_fmt[idx] = fmt;
            return idx;
        }
        SSF.load = load_entry;
        SSF._table = table_fmt;
        SSF.get_table = function get_table() {
            return table_fmt;
        };
        SSF.load_table = function load_table(tbl) {
            for (var i = 0; i != 0x0188; ++i)
                if (tbl[i] !== undefined) load_entry(tbl[i], i);
        };
        SSF.init_table = init_table;
        SSF.format = format;
    };
    make_ssf(SSF);
    /* map from xlml named formats to SSF TODO: localize */
    var XLMLFormatMap /*{[string]:string}*/ = ({
        "General Number": "General",
        "General Date": SSF._table[22],
        "Long Date": "dddd, mmmm dd, yyyy",
        "Medium Date": SSF._table[15],
        "Short Date": SSF._table[14],
        "Long Time": SSF._table[19],
        "Medium Time": SSF._table[18],
        "Short Time": SSF._table[20],
        "Currency": '"$"#,##0.00_);[Red]\\("$"#,##0.00\\)',
        "Fixed": SSF._table[2],
        "Standard": SSF._table[4],
        "Percent": SSF._table[10],
        "Scientific": SSF._table[11],
        "Yes/No": '"Yes";"Yes";"No";@',
        "True/False": '"True";"True";"False";@',
        "On/Off": '"Yes";"Yes";"No";@'
    });
    var SSFImplicit /*{[number]:string}*/ = ({
        "5": '"$"#,##0_);\\("$"#,##0\\)',
        "6": '"$"#,##0_);[Red]\\("$"#,##0\\)',
        "7": '"$"#,##0.00_);\\("$"#,##0.00\\)',
        "8": '"$"#,##0.00_);[Red]\\("$"#,##0.00\\)',
        "23": 'General',
        "24": 'General',
        "25": 'General',
        "26": 'General',
        "27": 'm/d/yy',
        "28": 'm/d/yy',
        "29": 'm/d/yy',
        "30": 'm/d/yy',
        "31": 'm/d/yy',
        "32": 'h:mm:ss',
        "33": 'h:mm:ss',
        "34": 'h:mm:ss',
        "35": 'h:mm:ss',
        "36": 'm/d/yy',
        "41": '_(* #,##0_);_(* \(#,##0\);_(* "-"_);_(@_)',
        "42": '_("$"* #,##0_);_("$"* \(#,##0\);_("$"* "-"_);_(@_)',
        "43": '_(* #,##0.00_);_(* \(#,##0.00\);_(* "-"??_);_(@_)',
        "44": '_("$"* #,##0.00_);_("$"* \(#,##0.00\);_("$"* "-"??_);_(@_)',
        "50": 'm/d/yy',
        "51": 'm/d/yy',
        "52": 'm/d/yy',
        "53": 'm/d/yy',
        "54": 'm/d/yy',
        "55": 'm/d/yy',
        "56": 'm/d/yy',
        "57": 'm/d/yy',
        "58": 'm/d/yy',
        "59": '0',
        "60": '0.00',
        "61": '#,##0',
        "62": '#,##0.00',
        "63": '"$"#,##0_);\\("$"#,##0\\)',
        "64": '"$"#,##0_);[Red]\\("$"#,##0\\)',
        "65": '"$"#,##0.00_);\\("$"#,##0.00\\)',
        "66": '"$"#,##0.00_);[Red]\\("$"#,##0.00\\)',
        "67": '0%',
        "68": '0.00%',
        "69": '# ?/?',
        "70": '# ??/??',
        "71": 'm/d/yy',
        "72": 'm/d/yy',
        "73": 'd-mmm-yy',
        "74": 'd-mmm',
        "75": 'mmm-yy',
        "76": 'h:mm',
        "77": 'h:mm:ss',
        "78": 'm/d/yy h:mm',
        "79": 'mm:ss',
        "80": '[h]:mm:ss',
        "81": 'mmss.0'
    });
    /* dateNF parse TODO: move to SSF */
    var dateNFregex = /[dD]+|[mM]+|[yYeE]+|[Hh]+|[Ss]+/g;
    function dateNF_regex(dateNF) {
        var fmt = typeof dateNF == "number" ? SSF._table[dateNF] : dateNF;
        fmt = fmt.replace(dateNFregex, "(\\d+)");
        return new RegExp("^" + fmt + "$");
    }
    function dateNF_fix(str, dateNF, match) {
        var Y = -1,
            m = -1,
            d = -1,
            H = -1,
            M = -1,
            S = -1;
        (dateNF.match(dateNFregex) || []).forEach(function (n, i) {
            var v = parseInt(match[i + 1], 10);
            switch (n.toLowerCase().charAt(0)) {
                case 'y':
                    Y = v;
                    break;
                case 'd':
                    d = v;
                    break;
                case 'h':
                    H = v;
                    break;
                case 's':
                    S = v;
                    break;
                case 'm':
                    if (H >= 0) M = v;
                    else m = v;
                    break;
            }
        });
        if (S >= 0 && M == -1 && m >= 0) {
            M = m;
            m = -1;
        }
        var datestr = (("" + (Y >= 0 ? Y : new Date().getFullYear())).slice(-4) + "-" + ("00" + (m >= 1 ? m : 1)).slice(-2) + "-" + ("00" + (d >= 1 ? d : 1)).slice(-2));
        if (datestr.length == 7) datestr = "0" + datestr;
        if (datestr.length == 8) datestr = "20" + datestr;
        var timestr = (("00" + (H >= 0 ? H : 0)).slice(-2) + ":" + ("00" + (M >= 0 ? M : 0)).slice(-2) + ":" + ("00" + (S >= 0 ? S : 0)).slice(-2));
        if (H == -1 && M == -1 && S == -1) return datestr;
        if (Y == -1 && m == -1 && d == -1) return timestr;
        return datestr + "T" + timestr;
    }
    var DO_NOT_EXPORT_CFB = true;
    /* cfb.js (C) 2013-present SheetJS -- http://sheetjs.com */
    /* vim: set ts=2: */
    /*jshint eqnull:true */
    /*exported CFB */
    /*global module, require:false, process:false, Buffer:false, Uint8Array:false, Uint16Array:false */
    /* crc32.js (C) 2014-present SheetJS -- http://sheetjs.com */
    /* vim: set ts=2: */
    /*exported CRC32 */
    var CRC32;
    (function (factory) {
        /*jshint ignore:start */
        /*eslint-disable */
        factory(CRC32 = {});
        /*eslint-enable */
        /*jshint ignore:end */
    }(function (CRC32) {
        CRC32.version = '1.2.0';
        /* see perf/crc32table.js */
        /*global Int32Array */
        function signed_crc_table() {
            var c = 0,
                table = new Array(256);
            for (var n = 0; n != 256; ++n) {
                c = n;
                c = ((c & 1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
                c = ((c & 1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
                c = ((c & 1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
                c = ((c & 1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
                c = ((c & 1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
                c = ((c & 1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
                c = ((c & 1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
                c = ((c & 1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1));
                table[n] = c;
            }
            return typeof Int32Array !== 'undefined' ? new Int32Array(table) : table;
        }
        var T = signed_crc_table();
        function crc32_bstr(bstr, seed) {
            var C = seed ^ -1,
                L = bstr.length - 1;
            for (var i = 0; i < L;) {
                C = (C >>> 8) ^ T[(C ^ bstr.charCodeAt(i++)) & 0xFF];
                C = (C >>> 8) ^ T[(C ^ bstr.charCodeAt(i++)) & 0xFF];
            }
            if (i === L) C = (C >>> 8) ^ T[(C ^ bstr.charCodeAt(i)) & 0xFF];
            return C ^ -1;
        }
        function crc32_buf(buf, seed) {
            if (buf.length > 10000) return crc32_buf_8(buf, seed);
            var C = seed ^ -1,
                L = buf.length - 3;
            for (var i = 0; i < L;) {
                C = (C >>> 8) ^ T[(C ^ buf[i++]) & 0xFF];
                C = (C >>> 8) ^ T[(C ^ buf[i++]) & 0xFF];
                C = (C >>> 8) ^ T[(C ^ buf[i++]) & 0xFF];
                C = (C >>> 8) ^ T[(C ^ buf[i++]) & 0xFF];
            }
            while (i < L + 3) C = (C >>> 8) ^ T[(C ^ buf[i++]) & 0xFF];
            return C ^ -1;
        }
        function crc32_buf_8(buf, seed) {
            var C = seed ^ -1,
                L = buf.length - 7;
            for (var i = 0; i < L;) {
                C = (C >>> 8) ^ T[(C ^ buf[i++]) & 0xFF];
                C = (C >>> 8) ^ T[(C ^ buf[i++]) & 0xFF];
                C = (C >>> 8) ^ T[(C ^ buf[i++]) & 0xFF];
                C = (C >>> 8) ^ T[(C ^ buf[i++]) & 0xFF];
                C = (C >>> 8) ^ T[(C ^ buf[i++]) & 0xFF];
                C = (C >>> 8) ^ T[(C ^ buf[i++]) & 0xFF];
                C = (C >>> 8) ^ T[(C ^ buf[i++]) & 0xFF];
                C = (C >>> 8) ^ T[(C ^ buf[i++]) & 0xFF];
            }
            while (i < L + 7) C = (C >>> 8) ^ T[(C ^ buf[i++]) & 0xFF];
            return C ^ -1;
        }
        function crc32_str(str, seed) {
            var C = seed ^ -1;
            for (var i = 0, L = str.length, c, d; i < L;) {
                c = str.charCodeAt(i++);
                if (c < 0x80) {
                    C = (C >>> 8) ^ T[(C ^ c) & 0xFF];
                } else if (c < 0x800) {
                    C = (C >>> 8) ^ T[(C ^ (192 | ((c >> 6) & 31))) & 0xFF];
                    C = (C >>> 8) ^ T[(C ^ (128 | (c & 63))) & 0xFF];
                } else if (c >= 0xD800 && c < 0xE000) {
                    c = (c & 1023) + 64;
                    d = str.charCodeAt(i++) & 1023;
                    C = (C >>> 8) ^ T[(C ^ (240 | ((c >> 8) & 7))) & 0xFF];
                    C = (C >>> 8) ^ T[(C ^ (128 | ((c >> 2) & 63))) & 0xFF];
                    C = (C >>> 8) ^ T[(C ^ (128 | ((d >> 6) & 15) | ((c & 3) << 4))) & 0xFF];
                    C = (C >>> 8) ^ T[(C ^ (128 | (d & 63))) & 0xFF];
                } else {
                    C = (C >>> 8) ^ T[(C ^ (224 | ((c >> 12) & 15))) & 0xFF];
                    C = (C >>> 8) ^ T[(C ^ (128 | ((c >> 6) & 63))) & 0xFF];
                    C = (C >>> 8) ^ T[(C ^ (128 | (c & 63))) & 0xFF];
                }
            }
            return C ^ -1;
        }
        CRC32.table = T;
        CRC32.bstr = crc32_bstr;
        CRC32.buf = crc32_buf;
        CRC32.str = crc32_str;
    }));
    /* [MS-CFB] v20171201 */
    var CFB = (function _CFB() {
        var exports = {};
        exports.version = '1.1.0';
        /* [MS-CFB] 2.6.4 */
        function namecmp(l, r) {
            var L = l.split("/"),
                R = r.split("/");
            for (var i = 0, c = 0, Z = Math.min(L.length, R.length); i < Z; ++i) {
                if ((c = L[i].length - R[i].length)) return c;
                if (L[i] != R[i]) return L[i] < R[i] ? -1 : 1;
            }
            return L.length - R.length;
        }
        function dirname(p) {
            if (p.charAt(p.length - 1) == "/") return (p.slice(0, -1).indexOf("/") === -1) ? p : dirname(p.slice(0, -1));
            var c = p.lastIndexOf("/");
            return (c === -1) ? p : p.slice(0, c + 1);
        }
        function filename(p) {
            if (p.charAt(p.length - 1) == "/") return filename(p.slice(0, -1));
            var c = p.lastIndexOf("/");
            return (c === -1) ? p : p.slice(c + 1);
        }
        /* -------------------------------------------------------------------------- */
        /* DOS Date format:
           high|YYYYYYYm.mmmddddd.HHHHHMMM.MMMSSSSS|low
           add 1980 to stored year
           stored second should be doubled
        */
        /* write JS date to buf as a DOS date */
        function write_dos_date(buf, date) {
            if (typeof date === "string") date = new Date(date);
            var hms = date.getHours();
            hms = hms << 6 | date.getMinutes();
            hms = hms << 5 | (date.getSeconds() >>> 1);
            buf.write_shift(2, hms);
            var ymd = (date.getFullYear() - 1980);
            ymd = ymd << 4 | (date.getMonth() + 1);
            ymd = ymd << 5 | date.getDate();
            buf.write_shift(2, ymd);
        }
        /* read four bytes from buf and interpret as a DOS date */
        function parse_dos_date(buf) {
            var hms = buf.read_shift(2) & 0xFFFF;
            var ymd = buf.read_shift(2) & 0xFFFF;
            var val = new Date();
            var d = ymd & 0x1F;
            ymd >>>= 5;
            var m = ymd & 0x0F;
            ymd >>>= 4;
            val.setMilliseconds(0);
            val.setFullYear(ymd + 1980);
            val.setMonth(m - 1);
            val.setDate(d);
            var S = hms & 0x1F;
            hms >>>= 5;
            var M = hms & 0x3F;
            hms >>>= 6;
            val.setHours(hms);
            val.setMinutes(M);
            val.setSeconds(S << 1);
            return val;
        }
        function parse_extra_field(blob) {
            prep_blob(blob, 0);
            var o = {};
            var flags = 0;
            while (blob.l <= blob.length - 4) {
                var type = blob.read_shift(2);
                var sz = blob.read_shift(2),
                    tgt = blob.l + sz;
                var p = {};
                switch (type) {
                    /* UNIX-style Timestamps */
                    case 0x5455:
                        {
                            flags = blob.read_shift(1);
                            if (flags & 1) p.mtime = blob.read_shift(4);
                            /* for some reason, CD flag corresponds to LFH */
                            if (sz > 5) {
                                if (flags & 2) p.atime = blob.read_shift(4);
                                if (flags & 4) p.ctime = blob.read_shift(4);
                            }
                            if (p.mtime) p.mt = new Date(p.mtime * 1000);
                        }
                        break;
                }
                blob.l = tgt;
                o[type] = p;
            }
            return o;
        }
        var fs;
        function get_fs() {
            return fs || (fs = require('fs'));
        }
        function parse(file, options) {
            if (file[0] == 0x50 && file[1] == 0x4b) return parse_zip(file, options);
            if (file.length < 512) throw new Error("CFB file size " + file.length + " < 512");
            var mver = 3;
            var ssz = 512;
            var nmfs = 0; // number of mini FAT sectors
            var difat_sec_cnt = 0;
            var dir_start = 0;
            var minifat_start = 0;
            var difat_start = 0;
            var fat_addrs = []; // locations of FAT sectors
            /* [MS-CFB] 2.2 Compound File Header */
            var blob = file.slice(0, 512);
            prep_blob(blob, 0);
            /* major version */
            var mv = check_get_mver(blob);
            mver = mv[0];
            switch (mver) {
                case 3:
                    ssz = 512;
                    break;
                case 4:
                    ssz = 4096;
                    break;
                case 0:
                    if (mv[1] == 0) return parse_zip(file, options);
                    /* falls through */
                default:
                    throw new Error("Major Version: Expected 3 or 4 saw " + mver);
            }
            /* reprocess header */
            if (ssz !== 512) {
                blob = file.slice(0, ssz);
                prep_blob(blob, 28 /* blob.l */ );
            }
            /* Save header for final object */
            var header = file.slice(0, ssz);
            check_shifts(blob, mver);
            // Number of Directory Sectors
            var dir_cnt = blob.read_shift(4, 'i');
            if (mver === 3 && dir_cnt !== 0) throw new Error('# Directory Sectors: Expected 0 saw ' + dir_cnt);
            // Number of FAT Sectors
            blob.l += 4;
            // First Directory Sector Location
            dir_start = blob.read_shift(4, 'i');
            // Transaction Signature
            blob.l += 4;
            // Mini Stream Cutoff Size
            blob.chk('00100000', 'Mini Stream Cutoff Size: ');
            // First Mini FAT Sector Location
            minifat_start = blob.read_shift(4, 'i');
            // Number of Mini FAT Sectors
            nmfs = blob.read_shift(4, 'i');
            // First DIFAT sector location
            difat_start = blob.read_shift(4, 'i');
            // Number of DIFAT Sectors
            difat_sec_cnt = blob.read_shift(4, 'i');
            // Grab FAT Sector Locations
            for (var q = -1, j = 0; j < 109; ++j) { /* 109 = (512 - blob.l)>>>2; */
                q = blob.read_shift(4, 'i');
                if (q < 0) break;
                fat_addrs[j] = q;
            }
            /** Break the file up into sectors */
            var sectors = sectorify(file, ssz);
            sleuth_fat(difat_start, difat_sec_cnt, sectors, ssz, fat_addrs);
            /** Chains */
            var sector_list = make_sector_list(sectors, dir_start, fat_addrs, ssz);
            sector_list[dir_start].name = "!Directory";
            if (nmfs > 0 && minifat_start !== ENDOFCHAIN) sector_list[minifat_start].name = "!MiniFAT";
            sector_list[fat_addrs[0]].name = "!FAT";
            sector_list.fat_addrs = fat_addrs;
            sector_list.ssz = ssz;
            /* [MS-CFB] 2.6.1 Compound File Directory Entry */
            var files = {},
                Paths = [],
                FileIndex = [],
                FullPaths = [];
            read_directory(dir_start, sector_list, sectors, Paths, nmfs, files, FileIndex, minifat_start);
            build_full_paths(FileIndex, FullPaths, Paths);
            Paths.shift();
            var o = {
                FileIndex: FileIndex,
                FullPaths: FullPaths
            };
            // $FlowIgnore
            if (options && options.raw) o.raw = {
                header: header,
                sectors: sectors
            };
            return o;
        } // parse
        /* [MS-CFB] 2.2 Compound File Header -- read up to major version */
        function check_get_mver(blob) {
            if (blob[blob.l] == 0x50 && blob[blob.l + 1] == 0x4b) return [0, 0];
            // header signature 8
            blob.chk(HEADER_SIGNATURE, 'Header Signature: ');
            // clsid 16
            blob.chk(HEADER_CLSID, 'CLSID: ');
            // minor version 2
            var mver = blob.read_shift(2, 'u');
            return [blob.read_shift(2, 'u'), mver];
        }
        function check_shifts(blob, mver) {
            var shift = 0x09;
            // Byte Order
            //blob.chk('feff', 'Byte Order: '); // note: some writers put 0xffff
            blob.l += 2;
            // Sector Shift
            switch ((shift = blob.read_shift(2))) {
                case 0x09:
                    if (mver != 3) throw new Error('Sector Shift: Expected 9 saw ' + shift);
                    break;
                case 0x0c:
                    if (mver != 4) throw new Error('Sector Shift: Expected 12 saw ' + shift);
                    break;
                default:
                    throw new Error('Sector Shift: Expected 9 or 12 saw ' + shift);
            }
            // Mini Sector Shift
            blob.chk('0600', 'Mini Sector Shift: ');
            // Reserved
            blob.chk('000000000000', 'Reserved: ');
        }
        /** Break the file up into sectors */
        function sectorify(file, ssz) {
            var nsectors = Math.ceil(file.length / ssz) - 1;
            var sectors = [];
            for (var i = 1; i < nsectors; ++i) sectors[i - 1] = file.slice(i * ssz, (i + 1) * ssz);
            sectors[nsectors - 1] = file.slice(nsectors * ssz);
            return sectors;
        }
        /* [MS-CFB] 2.6.4 Red-Black Tree */
        function build_full_paths(FI, FP, Paths) {
            var i = 0,
                L = 0,
                R = 0,
                C = 0,
                j = 0,
                pl = Paths.length;
            var dad = [],
                q = [];
            for (; i < pl; ++i) {
                dad[i] = q[i] = i;
                FP[i] = Paths[i];
            }
            for (; j < q.length; ++j) {
                i = q[j];
                L = FI[i].L;
                R = FI[i].R;
                C = FI[i].C;
                if (dad[i] === i) {
                    if (L !== -1 /*NOSTREAM*/ && dad[L] !== L) dad[i] = dad[L];
                    if (R !== -1 && dad[R] !== R) dad[i] = dad[R];
                }
                if (C !== -1 /*NOSTREAM*/ ) dad[C] = i;
                if (L !== -1) {
                    dad[L] = dad[i];
                    if (q.lastIndexOf(L) < j) q.push(L);
                }
                if (R !== -1) {
                    dad[R] = dad[i];
                    if (q.lastIndexOf(R) < j) q.push(R);
                }
            }
            for (i = 1; i < pl; ++i)
                if (dad[i] === i) {
                    if (R !== -1 /*NOSTREAM*/ && dad[R] !== R) dad[i] = dad[R];
                    else if (L !== -1 && dad[L] !== L) dad[i] = dad[L];
                }
            for (i = 1; i < pl; ++i) {
                if (FI[i].type === 0 /* unknown */ ) continue;
                j = dad[i];
                if (j === 0) FP[i] = FP[0] + "/" + FP[i];
                else
                    while (j !== 0 && j !== dad[j]) {
                        FP[i] = FP[j] + "/" + FP[i];
                        j = dad[j];
                    }
                dad[i] = 0;
            }
            FP[0] += "/";
            for (i = 1; i < pl; ++i) {
                if (FI[i].type !== 2 /* stream */ ) FP[i] += "/";
            }
        }
        function get_mfat_entry(entry, payload, mini) {
            var start = entry.start,
                size = entry.size;
            //return (payload.slice(start*MSSZ, start*MSSZ + size));
            var o = [];
            var idx = start;
            while (mini && size > 0 && idx >= 0) {
                o.push(payload.slice(idx * MSSZ, idx * MSSZ + MSSZ));
                size -= MSSZ;
                idx = __readInt32LE(mini, idx * 4);
            }
            if (o.length === 0) return (new_buf(0));
            return (bconcat(o).slice(0, entry.size));
        }
        /** Chase down the rest of the DIFAT chain to build a comprehensive list
            DIFAT chains by storing the next sector number as the last 32 bits */
        function sleuth_fat(idx, cnt, sectors, ssz, fat_addrs) {
            var q = ENDOFCHAIN;
            if (idx === ENDOFCHAIN) {
                if (cnt !== 0) throw new Error("DIFAT chain shorter than expected");
            } else if (idx !== -1 /*FREESECT*/ ) {
                var sector = sectors[idx],
                    m = (ssz >>> 2) - 1;
                if (!sector) return;
                for (var i = 0; i < m; ++i) {
                    if ((q = __readInt32LE(sector, i * 4)) === ENDOFCHAIN) break;
                    fat_addrs.push(q);
                }
                sleuth_fat(__readInt32LE(sector, ssz - 4), cnt - 1, sectors, ssz, fat_addrs);
            }
        }
        /** Follow the linked list of sectors for a given starting point */
        function get_sector_list(sectors, start, fat_addrs, ssz, chkd) {
            var buf = [],
                buf_chain = [];
            if (!chkd) chkd = [];
            var modulus = ssz - 1,
                j = 0,
                jj = 0;
            for (j = start; j >= 0;) {
                chkd[j] = true;
                buf[buf.length] = j;
                buf_chain.push(sectors[j]);
                var addr = fat_addrs[Math.floor(j * 4 / ssz)];
                jj = ((j * 4) & modulus);
                if (ssz < 4 + jj) throw new Error("FAT boundary crossed: " + j + " 4 " + ssz);
                if (!sectors[addr]) break;
                j = __readInt32LE(sectors[addr], jj);
            }
            return {
                nodes: buf,
                data: __toBuffer([buf_chain])
            };
        }
        /** Chase down the sector linked lists */
        function make_sector_list(sectors, dir_start, fat_addrs, ssz) {
            var sl = sectors.length,
                sector_list = ([]);
            var chkd = [],
                buf = [],
                buf_chain = [];
            var modulus = ssz - 1,
                i = 0,
                j = 0,
                k = 0,
                jj = 0;
            for (i = 0; i < sl; ++i) {
                buf = ([]);
                k = (i + dir_start);
                if (k >= sl) k -= sl;
                if (chkd[k]) continue;
                buf_chain = [];
                for (j = k; j >= 0;) {
                    chkd[j] = true;
                    buf[buf.length] = j;
                    buf_chain.push(sectors[j]);
                    var addr = fat_addrs[Math.floor(j * 4 / ssz)];
                    jj = ((j * 4) & modulus);
                    if (ssz < 4 + jj) throw new Error("FAT boundary crossed: " + j + " 4 " + ssz);
                    if (!sectors[addr]) break;
                    j = __readInt32LE(sectors[addr], jj);
                }
                sector_list[k] = ({
                    nodes: buf,
                    data: __toBuffer([buf_chain])
                });
            }
            return sector_list;
        }
        /* [MS-CFB] 2.6.1 Compound File Directory Entry */
        function read_directory(dir_start, sector_list, sectors, Paths, nmfs, files, FileIndex, mini) {
            var minifat_store = 0,
                pl = (Paths.length ? 2 : 0);
            var sector = sector_list[dir_start].data;
            var i = 0,
                namelen = 0,
                name;
            for (; i < sector.length; i += 128) {
                var blob = sector.slice(i, i + 128);
                prep_blob(blob, 64);
                namelen = blob.read_shift(2);
                name = __utf16le(blob, 0, namelen - pl);
                Paths.push(name);
                var o = ({
                    name: name,
                    type: blob.read_shift(1),
                    color: blob.read_shift(1),
                    L: blob.read_shift(4, 'i'),
                    R: blob.read_shift(4, 'i'),
                    C: blob.read_shift(4, 'i'),
                    clsid: blob.read_shift(16),
                    state: blob.read_shift(4, 'i'),
                    start: 0,
                    size: 0
                });
                var ctime = blob.read_shift(2) + blob.read_shift(2) + blob.read_shift(2) + blob.read_shift(2);
                if (ctime !== 0) o.ct = read_date(blob, blob.l - 8);
                var mtime = blob.read_shift(2) + blob.read_shift(2) + blob.read_shift(2) + blob.read_shift(2);
                if (mtime !== 0) o.mt = read_date(blob, blob.l - 8);
                o.start = blob.read_shift(4, 'i');
                o.size = blob.read_shift(4, 'i');
                if (o.size < 0 && o.start < 0) {
                    o.size = o.type = 0;
                    o.start = ENDOFCHAIN;
                    o.name = "";
                }
                if (o.type === 5) { /* root */
                    minifat_store = o.start;
                    if (nmfs > 0 && minifat_store !== ENDOFCHAIN) sector_list[minifat_store].name = "!StreamData";
                    /*minifat_size = o.size;*/
                } else if (o.size >= 4096 /* MSCSZ */ ) {
                    o.storage = 'fat';
                    if (sector_list[o.start] === undefined) sector_list[o.start] = get_sector_list(sectors, o.start, sector_list.fat_addrs, sector_list.ssz);
                    sector_list[o.start].name = o.name;
                    o.content = (sector_list[o.start].data.slice(0, o.size));
                } else {
                    o.storage = 'minifat';
                    if (o.size < 0) o.size = 0;
                    else if (minifat_store !== ENDOFCHAIN && o.start !== ENDOFCHAIN && sector_list[minifat_store]) {
                        o.content = get_mfat_entry(o, sector_list[minifat_store].data, (sector_list[mini] || {}).data);
                    }
                }
                if (o.content) prep_blob(o.content, 0);
                files[name] = o;
                FileIndex.push(o);
            }
        }
        function read_date(blob, offset) {
            return new Date((((__readUInt32LE(blob, offset + 4) / 1e7) * Math.pow(2, 32) + __readUInt32LE(blob, offset) / 1e7) - 11644473600) * 1000);
        }
        function read_file(filename, options) {
            get_fs();
            return parse(fs.readFileSync(filename), options);
        }
        function read(blob, options) {
            switch (options && options.type || "base64") {
                case "file":
                    return read_file(blob, options);
                case "base64":
                    return parse(s2a(Base64.decode(blob)), options);
                case "binary":
                    return parse(s2a(blob), options);
            }
            return parse(blob, options);
        }
        function init_cfb(cfb, opts) {
            var o = opts || {},
                root = o.root || "Root Entry";
            if (!cfb.FullPaths) cfb.FullPaths = [];
            if (!cfb.FileIndex) cfb.FileIndex = [];
            if (cfb.FullPaths.length !== cfb.FileIndex.length) throw new Error("inconsistent CFB structure");
            if (cfb.FullPaths.length === 0) {
                cfb.FullPaths[0] = root + "/";
                cfb.FileIndex[0] = ({
                    name: root,
                    type: 5
                });
            }
            if (o.CLSID) cfb.FileIndex[0].clsid = o.CLSID;
            seed_cfb(cfb);
        }
        function seed_cfb(cfb) {
            var nm = "\u0001Sh33tJ5";
            if (CFB.find(cfb, "/" + nm)) return;
            var p = new_buf(4);
            p[0] = 55;
            p[1] = p[3] = 50;
            p[2] = 54;
            cfb.FileIndex.push(({
                name: nm,
                type: 2,
                content: p,
                size: 4,
                L: 69,
                R: 69,
                C: 69
            }));
            cfb.FullPaths.push(cfb.FullPaths[0] + nm);
            rebuild_cfb(cfb);
        }
        function rebuild_cfb(cfb, f) {
            init_cfb(cfb);
            var gc = false,
                s = false;
            for (var i = cfb.FullPaths.length - 1; i >= 0; --i) {
                var _file = cfb.FileIndex[i];
                switch (_file.type) {
                    case 0:
                        if (s) gc = true;
                        else {
                            cfb.FileIndex.pop();
                            cfb.FullPaths.pop();
                        }
                        break;
                    case 1:
                    case 2:
                    case 5:
                        s = true;
                        if (isNaN(_file.R * _file.L * _file.C)) gc = true;
                        if (_file.R > -1 && _file.L > -1 && _file.R == _file.L) gc = true;
                        break;
                    default:
                        gc = true;
                        break;
                }
            }
            if (!gc && !f) return;
            var now = new Date(1987, 1, 19),
                j = 0;
            var data = [];
            for (i = 0; i < cfb.FullPaths.length; ++i) {
                if (cfb.FileIndex[i].type === 0) continue;
                data.push([cfb.FullPaths[i], cfb.FileIndex[i]]);
            }
            for (i = 0; i < data.length; ++i) {
                var dad = dirname(data[i][0]);
                s = false;
                for (j = 0; j < data.length; ++j)
                    if (data[j][0] === dad) s = true;
                if (!s) data.push([dad, ({
                    name: filename(dad).replace("/", ""),
                    type: 1,
                    clsid: HEADER_CLSID,
                    ct: now,
                    mt: now,
                    content: null
                })]);
            }
            data.sort(function (x, y) {
                return namecmp(x[0], y[0]);
            });
            cfb.FullPaths = [];
            cfb.FileIndex = [];
            for (i = 0; i < data.length; ++i) {
                cfb.FullPaths[i] = data[i][0];
                cfb.FileIndex[i] = data[i][1];
            }
            for (i = 0; i < data.length; ++i) {
                var elt = cfb.FileIndex[i];
                var nm = cfb.FullPaths[i];
                elt.name = filename(nm).replace("/", "");
                elt.L = elt.R = elt.C = -(elt.color = 1);
                elt.size = elt.content ? elt.content.length : 0;
                elt.start = 0;
                elt.clsid = (elt.clsid || HEADER_CLSID);
                if (i === 0) {
                    elt.C = data.length > 1 ? 1 : -1;
                    elt.size = 0;
                    elt.type = 5;
                } else if (nm.slice(-1) == "/") {
                    for (j = i + 1; j < data.length; ++j)
                        if (dirname(cfb.FullPaths[j]) == nm) break;
                    elt.C = j >= data.length ? -1 : j;
                    for (j = i + 1; j < data.length; ++j)
                        if (dirname(cfb.FullPaths[j]) == dirname(nm)) break;
                    elt.R = j >= data.length ? -1 : j;
                    elt.type = 1;
                } else {
                    if (dirname(cfb.FullPaths[i + 1] || "") == dirname(nm)) elt.R = i + 1;
                    elt.type = 2;
                }
            }
        }
        function _write(cfb, options) {
            var _opts = options || {};
            rebuild_cfb(cfb);
            if (_opts.fileType == 'zip') return write_zip(cfb, _opts);
            var L = (function (cfb) {
                var mini_size = 0,
                    fat_size = 0;
                for (var i = 0; i < cfb.FileIndex.length; ++i) {
                    var file = cfb.FileIndex[i];
                    if (!file.content) continue;
                    var flen = file.content.length;
                    if (flen > 0) {
                        if (flen < 0x1000) mini_size += (flen + 0x3F) >> 6;
                        else fat_size += (flen + 0x01FF) >> 9;
                    }
                }
                var dir_cnt = (cfb.FullPaths.length + 3) >> 2;
                var mini_cnt = (mini_size + 7) >> 3;
                var mfat_cnt = (mini_size + 0x7F) >> 7;
                var fat_base = mini_cnt + fat_size + dir_cnt + mfat_cnt;
                var fat_cnt = (fat_base + 0x7F) >> 7;
                var difat_cnt = fat_cnt <= 109 ? 0 : Math.ceil((fat_cnt - 109) / 0x7F);
                while (((fat_base + fat_cnt + difat_cnt + 0x7F) >> 7) > fat_cnt) difat_cnt = ++fat_cnt <= 109 ? 0 : Math.ceil((fat_cnt - 109) / 0x7F);
                var L = [1, difat_cnt, fat_cnt, mfat_cnt, dir_cnt, fat_size, mini_size, 0];
                cfb.FileIndex[0].size = mini_size << 6;
                L[7] = (cfb.FileIndex[0].start = L[0] + L[1] + L[2] + L[3] + L[4] + L[5]) + ((L[6] + 7) >> 3);
                return L;
            })(cfb);
            var o = new_buf(L[7] << 9);
            var i = 0,
                T = 0; {
                for (i = 0; i < 8; ++i) o.write_shift(1, HEADER_SIG[i]);
                for (i = 0; i < 8; ++i) o.write_shift(2, 0);
                o.write_shift(2, 0x003E);
                o.write_shift(2, 0x0003);
                o.write_shift(2, 0xFFFE);
                o.write_shift(2, 0x0009);
                o.write_shift(2, 0x0006);
                for (i = 0; i < 3; ++i) o.write_shift(2, 0);
                o.write_shift(4, 0);
                o.write_shift(4, L[2]);
                o.write_shift(4, L[0] + L[1] + L[2] + L[3] - 1);
                o.write_shift(4, 0);
                o.write_shift(4, 1 << 12);
                o.write_shift(4, L[3] ? L[0] + L[1] + L[2] - 1 : ENDOFCHAIN);
                o.write_shift(4, L[3]);
                o.write_shift(-4, L[1] ? L[0] - 1 : ENDOFCHAIN);
                o.write_shift(4, L[1]);
                for (i = 0; i < 109; ++i) o.write_shift(-4, i < L[2] ? L[1] + i : -1);
            }
            if (L[1]) {
                for (T = 0; T < L[1]; ++T) {
                    for (; i < 236 + T * 127; ++i) o.write_shift(-4, i < L[2] ? L[1] + i : -1);
                    o.write_shift(-4, T === L[1] - 1 ? ENDOFCHAIN : T + 1);
                }
            }
            var chainit = function (w) {
                for (T += w; i < T - 1; ++i) o.write_shift(-4, i + 1);
                if (w) {
                    ++i;
                    o.write_shift(-4, ENDOFCHAIN);
                }
            };
            T = i = 0;
            for (T += L[1]; i < T; ++i) o.write_shift(-4, consts.DIFSECT);
            for (T += L[2]; i < T; ++i) o.write_shift(-4, consts.FATSECT);
            chainit(L[3]);
            chainit(L[4]);
            var j = 0,
                flen = 0;
            var file = cfb.FileIndex[0];
            for (; j < cfb.FileIndex.length; ++j) {
                file = cfb.FileIndex[j];
                if (!file.content) continue;
                flen = file.content.length;
                if (flen < 0x1000) continue;
                file.start = T;
                chainit((flen + 0x01FF) >> 9);
            }
            chainit((L[6] + 7) >> 3);
            while (o.l & 0x1FF) o.write_shift(-4, consts.ENDOFCHAIN);
            T = i = 0;
            for (j = 0; j < cfb.FileIndex.length; ++j) {
                file = cfb.FileIndex[j];
                if (!file.content) continue;
                flen = file.content.length;
                if (!flen || flen >= 0x1000) continue;
                file.start = T;
                chainit((flen + 0x3F) >> 6);
            }
            while (o.l & 0x1FF) o.write_shift(-4, consts.ENDOFCHAIN);
            for (i = 0; i < L[4] << 2; ++i) {
                var nm = cfb.FullPaths[i];
                if (!nm || nm.length === 0) {
                    for (j = 0; j < 17; ++j) o.write_shift(4, 0);
                    for (j = 0; j < 3; ++j) o.write_shift(4, -1);
                    for (j = 0; j < 12; ++j) o.write_shift(4, 0);
                    continue;
                }
                file = cfb.FileIndex[i];
                if (i === 0) file.start = file.size ? file.start - 1 : ENDOFCHAIN;
                var _nm = (i === 0 && _opts.root) || file.name;
                flen = 2 * (_nm.length + 1);
                o.write_shift(64, _nm, "utf16le");
                o.write_shift(2, flen);
                o.write_shift(1, file.type);
                o.write_shift(1, file.color);
                o.write_shift(-4, file.L);
                o.write_shift(-4, file.R);
                o.write_shift(-4, file.C);
                if (!file.clsid)
                    for (j = 0; j < 4; ++j) o.write_shift(4, 0);
                else o.write_shift(16, file.clsid, "hex");
                o.write_shift(4, file.state || 0);
                o.write_shift(4, 0);
                o.write_shift(4, 0);
                o.write_shift(4, 0);
                o.write_shift(4, 0);
                o.write_shift(4, file.start);
                o.write_shift(4, file.size);
                o.write_shift(4, 0);
            }
            for (i = 1; i < cfb.FileIndex.length; ++i) {
                file = cfb.FileIndex[i];
                if (file.size >= 0x1000) {
                    o.l = (file.start + 1) << 9;
                    for (j = 0; j < file.size; ++j) o.write_shift(1, file.content[j]);
                    for (; j & 0x1FF; ++j) o.write_shift(1, 0);
                }
            }
            for (i = 1; i < cfb.FileIndex.length; ++i) {
                file = cfb.FileIndex[i];
                if (file.size > 0 && file.size < 0x1000) {
                    for (j = 0; j < file.size; ++j) o.write_shift(1, file.content[j]);
                    for (; j & 0x3F; ++j) o.write_shift(1, 0);
                }
            }
            while (o.l < o.length) o.write_shift(1, 0);
            return o;
        }
        /* [MS-CFB] 2.6.4 (Unicode 3.0.1 case conversion) */
        function find(cfb, path) {
            var UCFullPaths = cfb.FullPaths.map(function (x) {
                return x.toUpperCase();
            });
            var UCPaths = UCFullPaths.map(function (x) {
                var y = x.split("/");
                return y[y.length - (x.slice(-1) == "/" ? 2 : 1)];
            });
            var k = false;
            if (path.charCodeAt(0) === 47 /* "/" */ ) {
                k = true;
                path = UCFullPaths[0].slice(0, -1) + path;
            } else k = path.indexOf("/") !== -1;
            var UCPath = path.toUpperCase();
            var w = k === true ? UCFullPaths.indexOf(UCPath) : UCPaths.indexOf(UCPath);
            if (w !== -1) return cfb.FileIndex[w];
            var m = !UCPath.match(chr1);
            UCPath = UCPath.replace(chr0, '');
            if (m) UCPath = UCPath.replace(chr1, '!');
            for (w = 0; w < UCFullPaths.length; ++w) {
                if ((m ? UCFullPaths[w].replace(chr1, '!') : UCFullPaths[w]).replace(chr0, '') == UCPath) return cfb.FileIndex[w];
                if ((m ? UCPaths[w].replace(chr1, '!') : UCPaths[w]).replace(chr0, '') == UCPath) return cfb.FileIndex[w];
            }
            return null;
        }
        /** CFB Constants */
        var MSSZ = 64; /* Mini Sector Size = 1<<6 */
        //var MSCSZ = 4096; /* Mini Stream Cutoff Size */
        /* 2.1 Compound File Sector Numbers and Types */
        var ENDOFCHAIN = -2;
        /* 2.2 Compound File Header */
        var HEADER_SIGNATURE = 'd0cf11e0a1b11ae1';
        var HEADER_SIG = [0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1];
        var HEADER_CLSID = '00000000000000000000000000000000';
        var consts = {
            /* 2.1 Compund File Sector Numbers and Types */
            MAXREGSECT: -6,
            DIFSECT: -4,
            FATSECT: -3,
            ENDOFCHAIN: ENDOFCHAIN,
            FREESECT: -1,
            /* 2.2 Compound File Header */
            HEADER_SIGNATURE: HEADER_SIGNATURE,
            HEADER_MINOR_VERSION: '3e00',
            MAXREGSID: -6,
            NOSTREAM: -1,
            HEADER_CLSID: HEADER_CLSID,
            /* 2.6.1 Compound File Directory Entry */
            EntryTypes: ['unknown', 'storage', 'stream', 'lockbytes', 'property', 'root']
        };
        function write_file(cfb, filename, options) {
            get_fs();
            var o = _write(cfb, options);
            fs.writeFileSync(filename, o);
        }
        function a2s(o) {
            var out = new Array(o.length);
            for (var i = 0; i < o.length; ++i) out[i] = String.fromCharCode(o[i]);
            return out.join("");
        }
        function write(cfb, options) {
            var o = _write(cfb, options);
            switch (options && options.type) {
                case "file":
                    get_fs();
                    fs.writeFileSync(options.filename, (o));
                    return o;
                case "binary":
                    return a2s(o);
                case "base64":
                    return Base64.encode(a2s(o));
            }
            return o;
        }
        /* node < 8.1 zlib does not expose bytesRead, so default to pure JS */
        var _zlib;
        function use_zlib(zlib) {
            try {
                var InflateRaw = zlib.InflateRaw;
                var InflRaw = new InflateRaw();
                InflRaw._processChunk(new Uint8Array([3, 0]), InflRaw._finishFlushFlag);
                if (InflRaw.bytesRead) _zlib = zlib;
                else throw new Error("zlib does not expose bytesRead");
            } catch (e) {
                console.error("cannot use native zlib: " + (e.message || e));
            }
        }
        function _inflateRawSync(payload, usz) {
            if (!_zlib) return _inflate(payload, usz);
            var InflateRaw = _zlib.InflateRaw;
            var InflRaw = new InflateRaw();
            var out = InflRaw._processChunk(payload.slice(payload.l), InflRaw._finishFlushFlag);
            payload.l += InflRaw.bytesRead;
            return out;
        }
        function _deflateRawSync(payload) {
            return _zlib ? _zlib.deflateRawSync(payload) : _deflate(payload);
        }
        var CLEN_ORDER = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15];
        /*  LEN_ID = [ 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285 ]; */
        var LEN_LN = [3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258];
        /*  DST_ID = [  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13,  14,  15,  16,  17,  18,  19,   20,   21,   22,   23,   24,   25,   26,    27,    28,    29 ]; */
        var DST_LN = [1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577];
        function bit_swap_8(n) {
            var t = (((((n << 1) | (n << 11)) & 0x22110) | (((n << 5) | (n << 15)) & 0x88440)));
            return ((t >> 16) | (t >> 8) | t) & 0xFF;
        }
        var use_typed_arrays = typeof Uint8Array !== 'undefined';
        var bitswap8 = use_typed_arrays ? new Uint8Array(1 << 8) : [];
        for (var q = 0; q < (1 << 8); ++q) bitswap8[q] = bit_swap_8(q);
        function bit_swap_n(n, b) {
            var rev = bitswap8[n & 0xFF];
            if (b <= 8) return rev >>> (8 - b);
            rev = (rev << 8) | bitswap8[(n >> 8) & 0xFF];
            if (b <= 16) return rev >>> (16 - b);
            rev = (rev << 8) | bitswap8[(n >> 16) & 0xFF];
            return rev >>> (24 - b);
        }
        /* helpers for unaligned bit reads */
        function read_bits_2(buf, bl) {
            var w = (bl & 7),
                h = (bl >>> 3);
            return ((buf[h] | (w <= 6 ? 0 : buf[h + 1] << 8)) >>> w) & 0x03;
        }
        function read_bits_3(buf, bl) {
            var w = (bl & 7),
                h = (bl >>> 3);
            return ((buf[h] | (w <= 5 ? 0 : buf[h + 1] << 8)) >>> w) & 0x07;
        }
        function read_bits_4(buf, bl) {
            var w = (bl & 7),
                h = (bl >>> 3);
            return ((buf[h] | (w <= 4 ? 0 : buf[h + 1] << 8)) >>> w) & 0x0F;
        }
        function read_bits_5(buf, bl) {
            var w = (bl & 7),
                h = (bl >>> 3);
            return ((buf[h] | (w <= 3 ? 0 : buf[h + 1] << 8)) >>> w) & 0x1F;
        }
        function read_bits_7(buf, bl) {
            var w = (bl & 7),
                h = (bl >>> 3);
            return ((buf[h] | (w <= 1 ? 0 : buf[h + 1] << 8)) >>> w) & 0x7F;
        }
        /* works up to n = 3 * 8 + 1 = 25 */
        function read_bits_n(buf, bl, n) {
            var w = (bl & 7),
                h = (bl >>> 3),
                f = ((1 << n) - 1);
            var v = buf[h] >>> w;
            if (n < 8 - w) return v & f;
            v |= buf[h + 1] << (8 - w);
            if (n < 16 - w) return v & f;
            v |= buf[h + 2] << (16 - w);
            if (n < 24 - w) return v & f;
            v |= buf[h + 3] << (24 - w);
            return v & f;
        }
        /* until ArrayBuffer#realloc is a thing, fake a realloc */
        function realloc(b, sz) {
            var L = b.length,
                M = 2 * L > sz ? 2 * L : sz + 5,
                i = 0;
            if (L >= sz) return b;
            if (has_buf) {
                var o = new_unsafe_buf(M);
                // $FlowIgnore
                if (b.copy) b.copy(o);
                else
                    for (; i < b.length; ++i) o[i] = b[i];
                return o;
            } else if (use_typed_arrays) {
                var a = new Uint8Array(M);
                if (a.set) a.set(b);
                else
                    for (; i < b.length; ++i) a[i] = b[i];
                return a;
            }
            b.length = M;
            return b;
        }
        /* zero-filled arrays for older browsers */
        function zero_fill_array(n) {
            var o = new Array(n);
            for (var i = 0; i < n; ++i) o[i] = 0;
            return o;
        }
        var _deflate = (function () {
            var _deflateRaw = (function () {
                return function deflateRaw(data, out) {
                    var boff = 0;
                    while (boff < data.length) {
                        var L = Math.min(0xFFFF, data.length - boff);
                        var h = boff + L == data.length;
                        /* TODO: this is only type 0 stored */
                        out.write_shift(1, +h);
                        out.write_shift(2, L);
                        out.write_shift(2, (~L) & 0xFFFF);
                        while (L-- > 0) out[out.l++] = data[boff++];
                    }
                    return out.l;
                };
            })();
            return function (data) {
                var buf = new_buf(50 + Math.floor(data.length * 1.1));
                var off = _deflateRaw(data, buf);
                return buf.slice(0, off);
            };
        })();
        /* modified inflate function also moves original read head */
        /* build tree (used for literals and lengths) */
        function build_tree(clens, cmap, MAX) {
            var maxlen = 1,
                w = 0,
                i = 0,
                j = 0,
                ccode = 0,
                L = clens.length;
            var bl_count = use_typed_arrays ? new Uint16Array(32) : zero_fill_array(32);
            for (i = 0; i < 32; ++i) bl_count[i] = 0;
            for (i = L; i < MAX; ++i) clens[i] = 0;
            L = clens.length;
            var ctree = use_typed_arrays ? new Uint16Array(L) : zero_fill_array(L); // []
            /* build code tree */
            for (i = 0; i < L; ++i) {
                bl_count[(w = clens[i])]++;
                if (maxlen < w) maxlen = w;
                ctree[i] = 0;
            }
            bl_count[0] = 0;
            for (i = 1; i <= maxlen; ++i) bl_count[i + 16] = (ccode = (ccode + bl_count[i - 1]) << 1);
            for (i = 0; i < L; ++i) {
                ccode = clens[i];
                if (ccode != 0) ctree[i] = bl_count[ccode + 16]++;
            }
            /* cmap[maxlen + 4 bits] = (off&15) + (lit<<4) reverse mapping */
            var cleni = 0;
            for (i = 0; i < L; ++i) {
                cleni = clens[i];
                if (cleni != 0) {
                    ccode = bit_swap_n(ctree[i], maxlen) >> (maxlen - cleni);
                    for (j = (1 << (maxlen + 4 - cleni)) - 1; j >= 0; --j)
                        cmap[ccode | (j << cleni)] = (cleni & 15) | (i << 4);
                }
            }
            return maxlen;
        }
        var fix_lmap = use_typed_arrays ? new Uint16Array(512) : zero_fill_array(512);
        var fix_dmap = use_typed_arrays ? new Uint16Array(32) : zero_fill_array(32);
        if (!use_typed_arrays) {
            for (var i = 0; i < 512; ++i) fix_lmap[i] = 0;
            for (i = 0; i < 32; ++i) fix_dmap[i] = 0;
        }
        (function () {
            var dlens = [];
            var i = 0;
            for (; i < 32; i++) dlens.push(5);
            build_tree(dlens, fix_dmap, 32);
            var clens = [];
            i = 0;
            for (; i <= 143; i++) clens.push(8);
            for (; i <= 255; i++) clens.push(9);
            for (; i <= 279; i++) clens.push(7);
            for (; i <= 287; i++) clens.push(8);
            build_tree(clens, fix_lmap, 288);
        })();
        var dyn_lmap = use_typed_arrays ? new Uint16Array(32768) : zero_fill_array(32768);
        var dyn_dmap = use_typed_arrays ? new Uint16Array(32768) : zero_fill_array(32768);
        var dyn_cmap = use_typed_arrays ? new Uint16Array(128) : zero_fill_array(128);
        var dyn_len_1 = 1,
            dyn_len_2 = 1;
        /* 5.5.3 Expanding Huffman Codes */
        function dyn(data, boff) {
            /* nomenclature from RFC1951 refers to bit values; these are offset by the implicit constant */
            var _HLIT = read_bits_5(data, boff) + 257;
            boff += 5;
            var _HDIST = read_bits_5(data, boff) + 1;
            boff += 5;
            var _HCLEN = read_bits_4(data, boff) + 4;
            boff += 4;
            var w = 0;
            /* grab and store code lengths */
            var clens = use_typed_arrays ? new Uint8Array(19) : zero_fill_array(19);
            var ctree = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
            var maxlen = 1;
            var bl_count = use_typed_arrays ? new Uint8Array(8) : zero_fill_array(8);
            var next_code = use_typed_arrays ? new Uint8Array(8) : zero_fill_array(8);
            var L = clens.length; /* 19 */
            for (var i = 0; i < _HCLEN; ++i) {
                clens[CLEN_ORDER[i]] = w = read_bits_3(data, boff);
                if (maxlen < w) maxlen = w;
                bl_count[w]++;
                boff += 3;
            }
            /* build code tree */
            var ccode = 0;
            bl_count[0] = 0;
            for (i = 1; i <= maxlen; ++i) next_code[i] = ccode = (ccode + bl_count[i - 1]) << 1;
            for (i = 0; i < L; ++i)
                if ((ccode = clens[i]) != 0) ctree[i] = next_code[ccode]++;
            /* cmap[7 bits from stream] = (off&7) + (lit<<3) */
            var cleni = 0;
            for (i = 0; i < L; ++i) {
                cleni = clens[i];
                if (cleni != 0) {
                    ccode = bitswap8[ctree[i]] >> (8 - cleni);
                    for (var j = (1 << (7 - cleni)) - 1; j >= 0; --j) dyn_cmap[ccode | (j << cleni)] = (cleni & 7) | (i << 3);
                }
            }
            /* read literal and dist codes at once */
            var hcodes = [];
            maxlen = 1;
            for (; hcodes.length < _HLIT + _HDIST;) {
                ccode = dyn_cmap[read_bits_7(data, boff)];
                boff += ccode & 7;
                switch ((ccode >>>= 3)) {
                    case 16:
                        w = 3 + read_bits_2(data, boff);
                        boff += 2;
                        ccode = hcodes[hcodes.length - 1];
                        while (w-- > 0) hcodes.push(ccode);
                        break;
                    case 17:
                        w = 3 + read_bits_3(data, boff);
                        boff += 3;
                        while (w-- > 0) hcodes.push(0);
                        break;
                    case 18:
                        w = 11 + read_bits_7(data, boff);
                        boff += 7;
                        while (w-- > 0) hcodes.push(0);
                        break;
                    default:
                        hcodes.push(ccode);
                        if (maxlen < ccode) maxlen = ccode;
                        break;
                }
            }
            /* build literal / length trees */
            var h1 = hcodes.slice(0, _HLIT),
                h2 = hcodes.slice(_HLIT);
            for (i = _HLIT; i < 286; ++i) h1[i] = 0;
            for (i = _HDIST; i < 30; ++i) h2[i] = 0;
            dyn_len_1 = build_tree(h1, dyn_lmap, 286);
            dyn_len_2 = build_tree(h2, dyn_dmap, 30);
            return boff;
        }
        /* return [ data, bytesRead ] */
        function inflate(data, usz) {
            /* shortcircuit for empty buffer [0x03, 0x00] */
            if (data[0] == 3 && !(data[1] & 0x3)) {
                return [new_raw_buf(usz), 2];
            }
            /* bit offset */
            var boff = 0;
            /* header includes final bit and type bits */
            var header = 0;
            var outbuf = new_unsafe_buf(usz ? usz : (1 << 18));
            var woff = 0;
            var OL = outbuf.length >>> 0;
            var max_len_1 = 0,
                max_len_2 = 0;
            while ((header & 1) == 0) {
                header = read_bits_3(data, boff);
                boff += 3;
                if ((header >>> 1) == 0) {
                    /* Stored block */
                    if (boff & 7) boff += 8 - (boff & 7);
                    /* 2 bytes sz, 2 bytes bit inverse */
                    var sz = data[boff >>> 3] | data[(boff >>> 3) + 1] << 8;
                    boff += 32;
                    /* push sz bytes */
                    if (!usz && OL < woff + sz) {
                        outbuf = realloc(outbuf, woff + sz);
                        OL = outbuf.length;
                    }
                    if (typeof data.copy === 'function') {
                        // $FlowIgnore
                        data.copy(outbuf, woff, boff >>> 3, (boff >>> 3) + sz);
                        woff += sz;
                        boff += 8 * sz;
                    } else
                        while (sz-- > 0) {
                            outbuf[woff++] = data[boff >>> 3];
                            boff += 8;
                        }
                    continue;
                } else if ((header >>> 1) == 1) {
                    /* Fixed Huffman */
                    max_len_1 = 9;
                    max_len_2 = 5;
                } else {
                    /* Dynamic Huffman */
                    boff = dyn(data, boff);
                    max_len_1 = dyn_len_1;
                    max_len_2 = dyn_len_2;
                }
                if (!usz && (OL < woff + 32767)) {
                    outbuf = realloc(outbuf, woff + 32767);
                    OL = outbuf.length;
                }
                for (;;) { // while(true) is apparently out of vogue in modern JS circles
                    /* ingest code and move read head */
                    var bits = read_bits_n(data, boff, max_len_1);
                    var code = (header >>> 1) == 1 ? fix_lmap[bits] : dyn_lmap[bits];
                    boff += code & 15;
                    code >>>= 4;
                    /* 0-255 are literals, 256 is end of block token, 257+ are copy tokens */
                    if (((code >>> 8) & 0xFF) === 0) outbuf[woff++] = code;
                    else if (code == 256) break;
                    else {
                        code -= 257;
                        var len_eb = (code < 8) ? 0 : ((code - 4) >> 2);
                        if (len_eb > 5) len_eb = 0;
                        var tgt = woff + LEN_LN[code];
                        /* length extra bits */
                        if (len_eb > 0) {
                            tgt += read_bits_n(data, boff, len_eb);
                            boff += len_eb;
                        }
                        /* dist code */
                        bits = read_bits_n(data, boff, max_len_2);
                        code = (header >>> 1) == 1 ? fix_dmap[bits] : dyn_dmap[bits];
                        boff += code & 15;
                        code >>>= 4;
                        var dst_eb = (code < 4 ? 0 : (code - 2) >> 1);
                        var dst = DST_LN[code];
                        /* dist extra bits */
                        if (dst_eb > 0) {
                            dst += read_bits_n(data, boff, dst_eb);
                            boff += dst_eb;
                        }
                        /* in the common case, manual byte copy is faster than TA set / Buffer copy */
                        if (!usz && OL < tgt) {
                            outbuf = realloc(outbuf, tgt);
                            OL = outbuf.length;
                        }
                        while (woff < tgt) {
                            outbuf[woff] = outbuf[woff - dst];
                            ++woff;
                        }
                    }
                }
            }
            return [usz ? outbuf : outbuf.slice(0, woff), (boff + 7) >>> 3];
        }
        function _inflate(payload, usz) {
            var data = payload.slice(payload.l || 0);
            var out = inflate(data, usz);
            payload.l += out[1];
            return out[0];
        }
        function warn_or_throw(wrn, msg) {
            if (wrn) {
                if (typeof console !== 'undefined') console.error(msg);
            } else throw new Error(msg);
        }
        function parse_zip(file, options) {
            var blob = file;
            prep_blob(blob, 0);
            var FileIndex = [],
                FullPaths = [];
            var o = {
                FileIndex: FileIndex,
                FullPaths: FullPaths
            };
            init_cfb(o, {
                root: options.root
            });
            /* find end of central directory, start just after signature */
            var i = blob.length - 4;
            while ((blob[i] != 0x50 || blob[i + 1] != 0x4b || blob[i + 2] != 0x05 || blob[i + 3] != 0x06) && i >= 0) --i;
            blob.l = i + 4;
            /* parse end of central directory */
            blob.l += 4;
            var fcnt = blob.read_shift(2);
            blob.l += 6;
            var start_cd = blob.read_shift(4);
            /* parse central directory */
            blob.l = start_cd;
            for (i = 0; i < fcnt; ++i) {
                /* trust local file header instead of CD entry */
                blob.l += 20;
                var csz = blob.read_shift(4);
                var usz = blob.read_shift(4);
                var namelen = blob.read_shift(2);
                var efsz = blob.read_shift(2);
                var fcsz = blob.read_shift(2);
                blob.l += 8;
                var offset = blob.read_shift(4);
                var EF = parse_extra_field(blob.slice(blob.l + namelen, blob.l + namelen + efsz));
                blob.l += namelen + efsz + fcsz;
                var L = blob.l;
                blob.l = offset + 4;
                parse_local_file(blob, csz, usz, o, EF);
                blob.l = L;
            }
            return o;
        }
        /* head starts just after local file header signature */
        function parse_local_file(blob, csz, usz, o, EF) {
            /* [local file header] */
            blob.l += 2;
            var flags = blob.read_shift(2);
            var meth = blob.read_shift(2);
            var date = parse_dos_date(blob);
            if (flags & 0x2041) throw new Error("Unsupported ZIP encryption");
            var crc32 = blob.read_shift(4);
            var _csz = blob.read_shift(4);
            var _usz = blob.read_shift(4);
            var namelen = blob.read_shift(2);
            var efsz = blob.read_shift(2);
            // TODO: flags & (1<<11) // UTF8
            var name = "";
            for (var i = 0; i < namelen; ++i) name += String.fromCharCode(blob[blob.l++]);
            if (efsz) {
                var ef = parse_extra_field(blob.slice(blob.l, blob.l + efsz));
                if ((ef[0x5455] || {}).mt) date = ef[0x5455].mt;
                if (((EF || {})[0x5455] || {}).mt) date = EF[0x5455].mt;
            }
            blob.l += efsz;
            /* [encryption header] */
            /* [file data] */
            var data = blob.slice(blob.l, blob.l + _csz);
            switch (meth) {
                case 8:
                    data = _inflateRawSync(blob, _usz);
                    break;
                case 0:
                    break;
                default:
                    throw new Error("Unsupported ZIP Compression method " + meth);
            }
            /* [data descriptor] */
            var wrn = false;
            if (flags & 8) {
                crc32 = blob.read_shift(4);
                if (crc32 == 0x08074b50) {
                    crc32 = blob.read_shift(4);
                    wrn = true;
                }
                _csz = blob.read_shift(4);
                _usz = blob.read_shift(4);
            }
            if (_csz != csz) warn_or_throw(wrn, "Bad compressed size: " + csz + " != " + _csz);
            if (_usz != usz) warn_or_throw(wrn, "Bad uncompressed size: " + usz + " != " + _usz);
            var _crc32 = CRC32.buf(data, 0);
            if (crc32 != _crc32) warn_or_throw(wrn, "Bad CRC32 checksum: " + crc32 + " != " + _crc32);
            cfb_add(o, name, data, {
                unsafe: true,
                mt: date
            });
        }
        function write_zip(cfb, options) {
            var _opts = options || {};
            var out = [],
                cdirs = [];
            var o = new_buf(1);
            var method = (_opts.compression ? 8 : 0),
                flags = 0;
            var desc = false;
            if (desc) flags |= 8;
            var i = 0,
                j = 0;
            var start_cd = 0,
                fcnt = 0;
            var root = cfb.FullPaths[0],
                fp = root,
                fi = cfb.FileIndex[0];
            var crcs = [];
            var sz_cd = 0;
            for (i = 1; i < cfb.FullPaths.length; ++i) {
                fp = cfb.FullPaths[i].slice(root.length);
                fi = cfb.FileIndex[i];
                if (!fi.size || !fi.content || fp == "\u0001Sh33tJ5") continue;
                var start = start_cd;
                /* TODO: CP437 filename */
                var namebuf = new_buf(fp.length);
                for (j = 0; j < fp.length; ++j) namebuf.write_shift(1, fp.charCodeAt(j) & 0x7F);
                namebuf = namebuf.slice(0, namebuf.l);
                crcs[fcnt] = CRC32.buf(fi.content, 0);
                var outbuf = fi.content;
                if (method == 8) outbuf = _deflateRawSync(outbuf);
                /* local file header */
                o = new_buf(30);
                o.write_shift(4, 0x04034b50);
                o.write_shift(2, 20);
                o.write_shift(2, flags);
                o.write_shift(2, method);
                /* TODO: last mod file time/date */
                if (fi.mt) write_dos_date(o, fi.mt);
                else o.write_shift(4, 0);
                o.write_shift(-4, (flags & 8) ? 0 : crcs[fcnt]);
                o.write_shift(4, (flags & 8) ? 0 : outbuf.length);
                o.write_shift(4, (flags & 8) ? 0 : fi.content.length);
                o.write_shift(2, namebuf.length);
                o.write_shift(2, 0);
                start_cd += o.length;
                out.push(o);
                start_cd += namebuf.length;
                out.push(namebuf);
                /* TODO: encryption header ? */
                start_cd += outbuf.length;
                out.push(outbuf);
                /* data descriptor */
                if (flags & 8) {
                    o = new_buf(12);
                    o.write_shift(-4, crcs[fcnt]);
                    o.write_shift(4, outbuf.length);
                    o.write_shift(4, fi.content.length);
                    start_cd += o.l;
                    out.push(o);
                }
                /* central directory */
                o = new_buf(46);
                o.write_shift(4, 0x02014b50);
                o.write_shift(2, 0);
                o.write_shift(2, 20);
                o.write_shift(2, flags);
                o.write_shift(2, method);
                o.write_shift(4, 0); /* TODO: last mod file time/date */
                o.write_shift(-4, crcs[fcnt]);
                o.write_shift(4, outbuf.length);
                o.write_shift(4, fi.content.length);
                o.write_shift(2, namebuf.length);
                o.write_shift(2, 0);
                o.write_shift(2, 0);
                o.write_shift(2, 0);
                o.write_shift(2, 0);
                o.write_shift(4, 0);
                o.write_shift(4, start);
                sz_cd += o.l;
                cdirs.push(o);
                sz_cd += namebuf.length;
                cdirs.push(namebuf);
                ++fcnt;
            }
            /* end of central directory */
            o = new_buf(22);
            o.write_shift(4, 0x06054b50);
            o.write_shift(2, 0);
            o.write_shift(2, 0);
            o.write_shift(2, fcnt);
            o.write_shift(2, fcnt);
            o.write_shift(4, sz_cd);
            o.write_shift(4, start_cd);
            o.write_shift(2, 0);
            return bconcat(([bconcat((out)), bconcat(cdirs), o]));
        }
        function cfb_new(opts) {
            var o = ({});
            init_cfb(o, opts);
            return o;
        }
        function cfb_add(cfb, name, content, opts) {
            var unsafe = opts && opts.unsafe;
            if (!unsafe) init_cfb(cfb);
            var file = !unsafe && CFB.find(cfb, name);
            if (!file) {
                var fpath = cfb.FullPaths[0];
                if (name.slice(0, fpath.length) == fpath) fpath = name;
                else {
                    if (fpath.slice(-1) != "/") fpath += "/";
                    fpath = (fpath + name).replace("//", "/");
                }
                file = ({
                    name: filename(name),
                    type: 2
                });
                cfb.FileIndex.push(file);
                cfb.FullPaths.push(fpath);
                if (!unsafe) CFB.utils.cfb_gc(cfb);
            }
            file.content = (content);
            file.size = content ? content.length : 0;
            if (opts) {
                if (opts.CLSID) file.clsid = opts.CLSID;
                if (opts.mt) file.mt = opts.mt;
                if (opts.ct) file.ct = opts.ct;
            }
            return file;
        }
        function cfb_del(cfb, name) {
            init_cfb(cfb);
            var file = CFB.find(cfb, name);
            if (file)
                for (var j = 0; j < cfb.FileIndex.length; ++j)
                    if (cfb.FileIndex[j] == file) {
                        cfb.FileIndex.splice(j, 1);
                        cfb.FullPaths.splice(j, 1);
                        return true;
                    }
            return false;
        }
        function cfb_mov(cfb, old_name, new_name) {
            init_cfb(cfb);
            var file = CFB.find(cfb, old_name);
            if (file)
                for (var j = 0; j < cfb.FileIndex.length; ++j)
                    if (cfb.FileIndex[j] == file) {
                        cfb.FileIndex[j].name = filename(new_name);
                        cfb.FullPaths[j] = new_name;
                        return true;
                    }
            return false;
        }
        function cfb_gc(cfb) {
            rebuild_cfb(cfb, true);
        }
        exports.find = find;
        exports.read = read;
        exports.parse = parse;
        exports.write = write;
        exports.writeFile = write_file;
        exports.utils = {
            cfb_new: cfb_new,
            cfb_add: cfb_add,
            cfb_del: cfb_del,
            cfb_mov: cfb_mov,
            cfb_gc: cfb_gc,
            ReadShift: ReadShift,
            CheckField: CheckField,
            prep_blob: prep_blob,
            bconcat: bconcat,
            use_zlib: use_zlib,
            _deflateRaw: _deflate,
            _inflateRaw: _inflate,
            consts: consts
        };
        return exports;
    })();
    if (typeof require !== 'undefined' && typeof module !== 'undefined' && typeof DO_NOT_EXPORT_CFB === 'undefined') {
        module.exports = CFB;
    }
    var _fs;
    if (typeof require !== 'undefined') try {
        _fs = require('fs');
    } catch (e) {}
    /* normalize data for blob ctor */
    function blobify(data) {
        if (typeof data === "string") return s2ab(data);
        if (Array.isArray(data)) return a2u(data);
        return data;
    }
    /* write or download file */
    function write_dl(fname, payload, enc) {
        /*global IE_SaveFile, Blob, navigator, saveAs, URL, document, File, chrome */
        if (typeof _fs !== 'undefined' && _fs.writeFileSync) return enc ? _fs.writeFileSync(fname, payload, enc) : _fs.writeFileSync(fname, payload);
        var data = (enc == "utf8") ? utf8write(payload) : payload;
        if (typeof IE_SaveFile !== 'undefined') return IE_SaveFile(data, fname);
        if (typeof Blob !== 'undefined') {
            var blob = new Blob([blobify(data)], {
                type: "application/octet-stream"
            });
            if (typeof navigator !== 'undefined' && navigator.msSaveBlob) return navigator.msSaveBlob(blob, fname);
            if (typeof saveAs !== 'undefined') return saveAs(blob, fname);
            if (typeof URL !== 'undefined' && typeof document !== 'undefined' && document.createElement && URL.createObjectURL) {
                var url = URL.createObjectURL(blob);
                if (typeof chrome === 'object' && typeof (chrome.downloads || {}).download == "function") {
                    if (URL.revokeObjectURL && typeof setTimeout !== 'undefined') setTimeout(function () {
                        URL.revokeObjectURL(url);
                    }, 60000);
                    return chrome.downloads.download({
                        url: url,
                        filename: fname,
                        saveAs: true
                    });
                }
                var a = document.createElement("a");
                if (a.download != null) {
                    a.download = fname;
                    a.href = url;
                    document.body.appendChild(a);
                    a.click();
                    document.body.removeChild(a);
                    if (URL.revokeObjectURL && typeof setTimeout !== 'undefined') setTimeout(function () {
                        URL.revokeObjectURL(url);
                    }, 60000);
                    return url;
                }
            }
        }
        // $FlowIgnore
        if (typeof $ !== 'undefined' && typeof File !== 'undefined' && typeof Folder !== 'undefined') try { // extendscript
            // $FlowIgnore
            var out = File(fname);
            out.open("w");
            out.encoding = "binary";
            if (Array.isArray(payload)) payload = a2s(payload);
            out.write(payload);
            out.close();
            return payload;
        } catch (e) {
            if (!e.message || !e.message.match(/onstruct/)) throw e;
        }
        throw new Error("cannot save file " + fname);
    }
    /* read binary data from file */
    function read_binary(path) {
        if (typeof _fs !== 'undefined') return _fs.readFileSync(path);
        // $FlowIgnore
        if (typeof $ !== 'undefined' && typeof File !== 'undefined' && typeof Folder !== 'undefined') try { // extendscript
            // $FlowIgnore
            var infile = File(path);
            infile.open("r");
            infile.encoding = "binary";
            var data = infile.read();
            infile.close();
            return data;
        } catch (e) {
            if (!e.message || !e.message.match(/onstruct/)) throw e;
        }
        throw new Error("Cannot access file " + path);
    }
    function keys(o) {
        var ks = Object.keys(o),
            o2 = [];
        for (var i = 0; i < ks.length; ++i)
            if (o.hasOwnProperty(ks[i])) o2.push(ks[i]);
        return o2;
    }
    function evert_key(obj, key) {
        var o = ([]),
            K = keys(obj);
        for (var i = 0; i !== K.length; ++i)
            if (o[obj[K[i]][key]] == null) o[obj[K[i]][key]] = K[i];
        return o;
    }
    function evert(obj) {
        var o = ([]),
            K = keys(obj);
        for (var i = 0; i !== K.length; ++i) o[obj[K[i]]] = K[i];
        return o;
    }
    function evert_num(obj) {
        var o = ([]),
            K = keys(obj);
        for (var i = 0; i !== K.length; ++i) o[obj[K[i]]] = parseInt(K[i], 10);
        return o;
    }
    function evert_arr(obj) {
        var o = ([]),
            K = keys(obj);
        for (var i = 0; i !== K.length; ++i) {
            if (o[obj[K[i]]] == null) o[obj[K[i]]] = [];
            o[obj[K[i]]].push(K[i]);
        }
        return o;
    }
    var basedate = new Date(1899, 11, 30, 0, 0, 0); // 2209161600000
    var dnthresh = basedate.getTime() + (new Date().getTimezoneOffset() - basedate.getTimezoneOffset()) * 60000;
    function datenum(v, date1904) {
        var epoch = v.getTime();
        if (date1904) epoch -= 1462 * 24 * 60 * 60 * 1000;
        return (epoch - dnthresh) / (24 * 60 * 60 * 1000);
    }
    function numdate(v) {
        var out = new Date();
        out.setTime(v * 24 * 60 * 60 * 1000 + dnthresh);
        return out;
    }
    /* ISO 8601 Duration */
    function parse_isodur(s) {
        var sec = 0,
            mt = 0,
            time = false;
        var m = s.match(/P([0-9\.]+Y)?([0-9\.]+M)?([0-9\.]+D)?T([0-9\.]+H)?([0-9\.]+M)?([0-9\.]+S)?/);
        if (!m) throw new Error("|" + s + "| is not an ISO8601 Duration");
        for (var i = 1; i != m.length; ++i) {
            if (!m[i]) continue;
            mt = 1;
            if (i > 3) time = true;
            switch (m[i].slice(m[i].length - 1)) {
                case 'Y':
                    throw new Error("Unsupported ISO Duration Field: " + m[i].slice(m[i].length - 1));
                case 'D':
                    mt *= 24;
                    /* falls through */
                case 'H':
                    mt *= 60;
                    /* falls through */
                case 'M':
                    if (!time) throw new Error("Unsupported ISO Duration Field: M");
                    else mt *= 60;
                    /* falls through */
                case 'S':
                    break;
            }
            sec += mt * parseInt(m[i], 10);
        }
        return sec;
    }
    var good_pd_date = new Date('2017-02-19T19:06:09.000Z');
    if (isNaN(good_pd_date.getFullYear())) good_pd_date = new Date('2/19/17');
    var good_pd = good_pd_date.getFullYear() == 2017;
    /* parses a date as a local date */
    function parseDate(str, fixdate) {
        var d = new Date(str);
        if (good_pd) {
            if (fixdate > 0) d.setTime(d.getTime() + d.getTimezoneOffset() * 60 * 1000);
            else if (fixdate < 0) d.setTime(d.getTime() - d.getTimezoneOffset() * 60 * 1000);
            return d;
        }
        if (str instanceof Date) return str;
        if (good_pd_date.getFullYear() == 1917 && !isNaN(d.getFullYear())) {
            var s = d.getFullYear();
            if (str.indexOf("" + s) > -1) return d;
            d.setFullYear(d.getFullYear() + 100);
            return d;
        }
        var n = str.match(/\d+/g) || ["2017", "2", "19", "0", "0", "0"];
        var out = new Date(+n[0], +n[1] - 1, +n[2], (+n[3] || 0), (+n[4] || 0), (+n[5] || 0));
        if (str.indexOf("Z") > -1) out = new Date(out.getTime() - out.getTimezoneOffset() * 60 * 1000);
        return out;
    }
    function cc2str(arr) {
        var o = "";
        for (var i = 0; i != arr.length; ++i) o += String.fromCharCode(arr[i]);
        return o;
    }
    function dup(o) {
        if (typeof JSON != 'undefined' && !Array.isArray(o)) return JSON.parse(JSON.stringify(o));
        if (typeof o != 'object' || o == null) return o;
        if (o instanceof Date) return new Date(o.getTime());
        var out = {};
        for (var k in o)
            if (o.hasOwnProperty(k)) out[k] = dup(o[k]);
        return out;
    }
    function fill(c, l) {
        var o = "";
        while (o.length < l) o += c;
        return o;
    }
    /* TODO: stress test */
    function fuzzynum(s) {
        var v = Number(s);
        if (!isNaN(v)) return v;
        var wt = 1;
        var ss = s.replace(/([\d]),([\d])/g, "$1$2").replace(/[$]/g, "").replace(/[%]/g, function () {
            wt *= 100;
            return "";
        });
        if (!isNaN(v = Number(ss))) return v / wt;
        ss = ss.replace(/[(](.*)[)]/, function ($$, $1) {
            wt = -wt;
            return $1;
        });
        if (!isNaN(v = Number(ss))) return v / wt;
        return v;
    }
    function fuzzydate(s) {
        var o = new Date(s),
            n = new Date(NaN);
        var y = o.getYear(),
            m = o.getMonth(),
            d = o.getDate();
        if (isNaN(d)) return n;
        if (y < 0 || y > 8099) return n;
        if ((m > 0 || d > 1) && y != 101) return o;
        if (s.toLowerCase().match(/jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec/)) return o;
        if (s.match(/[^-0-9:,\/\\]/)) return n;
        return o;
    }
    var safe_split_regex = "abacaba".split(/(:?b)/i).length == 5;
    function split_regex(str, re, def) {
        if (safe_split_regex || typeof re == "string") return str.split(re);
        var p = str.split(re),
            o = [p[0]];
        for (var i = 1; i < p.length; ++i) {
            o.push(def);
            o.push(p[i]);
        }
        return o;
    }
    function getdatastr(data) {
        if (!data) return null;
        if (data.data) return debom(data.data);
        if (data.asNodeBuffer && has_buf) return debom(data.asNodeBuffer().toString('binary'));
        if (data.asBinary) return debom(data.asBinary());
        if (data._data && data._data.getContent) return debom(cc2str(Array.prototype.slice.call(data._data.getContent(), 0)));
        return null;
    }
    function getdatabin(data) {
        if (!data) return null;
        if (data.data) return char_codes(data.data);
        if (data.asNodeBuffer && has_buf) return data.asNodeBuffer();
        if (data._data && data._data.getContent) {
            var o = data._data.getContent();
            if (typeof o == "string") return char_codes(o);
            return Array.prototype.slice.call(o);
        }
        return null;
    }
    function getdata(data) {
        return (data && data.name.slice(-4) === ".bin") ? getdatabin(data) : getdatastr(data);
    }
    /* Part 2 Section 10.1.2 "Mapping Content Types" Names are case-insensitive */
    /* OASIS does not comment on filename case sensitivity */
    function safegetzipfile(zip, file) {
        var k = keys(zip.files);
        var f = file.toLowerCase(),
            g = f.replace(/\//g, '\\');
        for (var i = 0; i < k.length; ++i) {
            var n = k[i].toLowerCase();
            if (f == n || g == n) return zip.files[k[i]];
        }
        return null;
    }
    function getzipfile(zip, file) {
        var o = safegetzipfile(zip, file);
        if (o == null) throw new Error("Cannot find file " + file + " in zip");
        return o;
    }
    function getzipdata(zip, file, safe) {
        if (!safe) return getdata(getzipfile(zip, file));
        if (!file) return null;
        try {
            return getzipdata(zip, file);
        } catch (e) {
            return null;
        }
    }
    function getzipstr(zip, file, safe) {
        if (!safe) return getdatastr(getzipfile(zip, file));
        if (!file) return null;
        try {
            return getzipstr(zip, file);
        } catch (e) {
            return null;
        }
    }
    function zipentries(zip) {
        var k = keys(zip.files),
            o = [];
        for (var i = 0; i < k.length; ++i)
            if (k[i].slice(-1) != '/') o.push(k[i]);
        return o.sort();
    }
    var jszip;
    /*global JSZipSync:true */
    if (typeof JSZipSync !== 'undefined') jszip = JSZipSync;
    if (typeof exports !== 'undefined') {
        if (typeof module !== 'undefined' && module.exports) {
            if (typeof jszip === 'undefined') jszip = require('./jszip.js');
        }
    }
    function resolve_path(path, base) {
        var result = base.split('/');
        if (base.slice(-1) != "/") result.pop(); // folder path
        var target = path.split('/');
        while (target.length !== 0) {
            var step = target.shift();
            if (step === '..') result.pop();
            else if (step !== '.') result.push(step);
        }
        return result.join('/');
    }
    var XML_HEADER = '\r\n';
    var attregexg = /([^"\s?>\/]+)\s*=\s*((?:")([^"]*)(?:")|(?:')([^']*)(?:')|([^'">\s]+))/g;
    var tagregex = /<[\/\?]?[a-zA-Z0-9:]+(?:\s+[^"\s?>\/]+\s*=\s*(?:"[^"]*"|'[^']*'|[^'">\s=]+))*\s?[\/\?]?>/g;
    if (!(XML_HEADER.match(tagregex))) tagregex = /<[^>]*>/g;
    var nsregex = /<\w*:/,
        nsregex2 = /<(\/?)\w+:/;
    function parsexmltag(tag, skip_root) {
        var z = ({});
        var eq = 0,
            c = 0;
        for (; eq !== tag.length; ++eq)
            if ((c = tag.charCodeAt(eq)) === 32 || c === 10 || c === 13) break;
        if (!skip_root) z[0] = tag.slice(0, eq);
        if (eq === tag.length) return z;
        var m = tag.match(attregexg),
            j = 0,
            v = "",
            i = 0,
            q = "",
            cc = "",
            quot = 1;
        if (m)
            for (i = 0; i != m.length; ++i) {
                cc = m[i];
                for (c = 0; c != cc.length; ++c)
                    if (cc.charCodeAt(c) === 61) break;
                q = cc.slice(0, c).trim();
                while (cc.charCodeAt(c + 1) == 32) ++c;
                quot = ((eq = cc.charCodeAt(c + 1)) == 34 || eq == 39) ? 1 : 0;
                v = cc.slice(c + 1 + quot, cc.length - quot);
                for (j = 0; j != q.length; ++j)
                    if (q.charCodeAt(j) === 58) break;
                if (j === q.length) {
                    if (q.indexOf("_") > 0) q = q.slice(0, q.indexOf("_")); // from ods
                    z[q] = v;
                    z[q.toLowerCase()] = v;
                } else {
                    var k = (j === 5 && q.slice(0, 5) === "xmlns" ? "xmlns" : "") + q.slice(j + 1);
                    if (z[k] && q.slice(j - 3, j) == "ext") continue; // from ods
                    z[k] = v;
                    z[k.toLowerCase()] = v;
                }
            }
        return z;
    }
    function strip_ns(x) {
        return x.replace(nsregex2, "<$1");
    }
    var encodings = {
        '"': '"',
        ''': "'",
        '>': '>',
        '<': '<',
        '&': '&'
    };
    var rencoding = evert(encodings);
    //var rencstr = "&<>'\"".split("");
    // TODO: CP remap (need to read file version to determine OS)
    var unescapexml = (function () {
        /* 22.4.2.4 bstr (Basic String) */
        var encregex = /&(?:quot|apos|gt|lt|amp|#x?([\da-fA-F]+));/g,
            coderegex = /_x([\da-fA-F]{4})_/g;
        return function unescapexml(text) {
            var s = text + '',
                i = s.indexOf(" -1 ? 16 : 10)) || $$;
            }).replace(coderegex, function (m, c) {
                return String.fromCharCode(parseInt(c, 16));
            });
            var j = s.indexOf("]]>");
            return unescapexml(s.slice(0, i)) + s.slice(i + 9, j) + unescapexml(s.slice(j + 3));
        };
    })();
    var decregex = /[&<>'"]/g,
        charegex = /[\u0000-\u0008\u000b-\u001f]/g;
    function escapexml(text) {
        var s = text + '';
        return s.replace(decregex, function (y) {
            return rencoding[y];
        }).replace(charegex, function (s) {
            return "_x" + ("000" + s.charCodeAt(0).toString(16)).slice(-4) + "_";
        });
    }
    function escapexmltag(text) {
        return escapexml(text).replace(/ /g, "_x0020_");
    }
    var htmlcharegex = /[\u0000-\u001f]/g;
    function escapehtml(text) {
        var s = text + '';
        return s.replace(decregex, function (y) {
            return rencoding[y];
        }).replace(/\n/g, "
").replace(htmlcharegex, function (s) {
            return "" + ("000" + s.charCodeAt(0).toString(16)).slice(-4) + ";";
        });
    }
    function escapexlml(text) {
        var s = text + '';
        return s.replace(decregex, function (y) {
            return rencoding[y];
        }).replace(htmlcharegex, function (s) {
            return "" + (s.charCodeAt(0).toString(16)).toUpperCase() + ";";
        });
    }
    /* TODO: handle codepages */
    var xlml_fixstr = (function () {
        var entregex = /(\d+);/g;
        function entrepl($$, $1) {
            return String.fromCharCode(parseInt($1, 10));
        }
        return function xlml_fixstr(str) {
            return str.replace(entregex, entrepl);
        };
    })();
    var xlml_unfixstr = (function () {
        return function xlml_unfixstr(str) {
            return str.replace(/(\r\n|[\r\n])/g, "\
");
        };
    })();
    function parsexmlbool(value) {
        switch (value) {
            case 1:
            case true:
            case '1':
            case 'true':
            case 'TRUE':
                return true;
                /* case '0': case 'false': case 'FALSE':*/
            default:
                return false;
        }
    }
    var utf8read = function utf8reada(orig) {
        var out = "",
            i = 0,
            c = 0,
            d = 0,
            e = 0,
            f = 0,
            w = 0;
        while (i < orig.length) {
            c = orig.charCodeAt(i++);
            if (c < 128) {
                out += String.fromCharCode(c);
                continue;
            }
            d = orig.charCodeAt(i++);
            if (c > 191 && c < 224) {
                f = ((c & 31) << 6);
                f |= (d & 63);
                out += String.fromCharCode(f);
                continue;
            }
            e = orig.charCodeAt(i++);
            if (c < 240) {
                out += String.fromCharCode(((c & 15) << 12) | ((d & 63) << 6) | (e & 63));
                continue;
            }
            f = orig.charCodeAt(i++);
            w = (((c & 7) << 18) | ((d & 63) << 12) | ((e & 63) << 6) | (f & 63)) - 65536;
            out += String.fromCharCode(0xD800 + ((w >>> 10) & 1023));
            out += String.fromCharCode(0xDC00 + (w & 1023));
        }
        return out;
    };
    var utf8write = function (orig) {
        var out = [],
            i = 0,
            c = 0,
            d = 0;
        while (i < orig.length) {
            c = orig.charCodeAt(i++);
            switch (true) {
                case c < 128:
                    out.push(String.fromCharCode(c));
                    break;
                case c < 2048:
                    out.push(String.fromCharCode(192 + (c >> 6)));
                    out.push(String.fromCharCode(128 + (c & 63)));
                    break;
                case c >= 55296 && c < 57344:
                    c -= 55296;
                    d = orig.charCodeAt(i++) - 56320 + (c << 10);
                    out.push(String.fromCharCode(240 + ((d >> 18) & 7)));
                    out.push(String.fromCharCode(144 + ((d >> 12) & 63)));
                    out.push(String.fromCharCode(128 + ((d >> 6) & 63)));
                    out.push(String.fromCharCode(128 + (d & 63)));
                    break;
                default:
                    out.push(String.fromCharCode(224 + (c >> 12)));
                    out.push(String.fromCharCode(128 + ((c >> 6) & 63)));
                    out.push(String.fromCharCode(128 + (c & 63)));
            }
        }
        return out.join("");
    };
    if (has_buf) {
        var utf8readb = function utf8readb(data) {
            var out = Buffer.alloc(2 * data.length),
                w, i, j = 1,
                k = 0,
                ww = 0,
                c;
            for (i = 0; i < data.length; i += j) {
                j = 1;
                if ((c = data.charCodeAt(i)) < 128) w = c;
                else if (c < 224) {
                    w = (c & 31) * 64 + (data.charCodeAt(i + 1) & 63);
                    j = 2;
                } else if (c < 240) {
                    w = (c & 15) * 4096 + (data.charCodeAt(i + 1) & 63) * 64 + (data.charCodeAt(i + 2) & 63);
                    j = 3;
                } else {
                    j = 4;
                    w = (c & 7) * 262144 + (data.charCodeAt(i + 1) & 63) * 4096 + (data.charCodeAt(i + 2) & 63) * 64 + (data.charCodeAt(i + 3) & 63);
                    w -= 65536;
                    ww = 0xD800 + ((w >>> 10) & 1023);
                    w = 0xDC00 + (w & 1023);
                }
                if (ww !== 0) {
                    out[k++] = ww & 255;
                    out[k++] = ww >>> 8;
                    ww = 0;
                }
                out[k++] = w % 256;
                out[k++] = w >>> 8;
            }
            return out.slice(0, k).toString('ucs2');
        };
        var corpus = "foo bar baz\u00e2\u0098\u0083\u00f0\u009f\u008d\u00a3";
        if (utf8read(corpus) == utf8readb(corpus)) utf8read = utf8readb;
        // $FlowIgnore
        var utf8readc = function utf8readc(data) {
            return Buffer_from(data, 'binary').toString('utf8');
        };
        if (utf8read(corpus) == utf8readc(corpus)) utf8read = utf8readc;
        // $FlowIgnore
        utf8write = function (data) {
            return Buffer_from(data, 'utf8').toString("binary");
        };
    }
    // matches ... extracts content
    var matchtag = (function () {
        var mtcache = ({});
        return function matchtag(f, g) {
            var t = f + "|" + (g || "");
            if (mtcache[t]) return mtcache[t];
            return (mtcache[t] = new RegExp('<(?:\\w+:)?' + f + '(?: xml:space="preserve")?(?:[^>]*)>([\\s\\S]*?)(?:\\w+:)?' + f + '>', ((g || ""))));
        };
    })();
    var htmldecode = (function () {
        var entities = [
            ['nbsp', ' '],
            ['middot', '·'],
            ['quot', '"'],
            ['apos', "'"],
            ['gt', '>'],
            ['lt', '<'],
            ['amp', '&']
        ].map(function (x) {
            return [new RegExp('&' + x[0] + ';', "g"), x[1]];
        });
        return function htmldecode(str) {
            var o = str.replace(/^[\t\n\r ]+/, "").replace(/[\t\n\r ]+$/, "").replace(/[\t\n\r ]+/g, " ").replace(/<\s*[bB][rR]\s*\/?>/g, "\n").replace(/<[^>]*>/g, "");
            for (var i = 0; i < entities.length; ++i) o = o.replace(entities[i][0], entities[i][1]);
            return o;
        };
    })();
    var vtregex = (function () {
        var vt_cache = {};
        return function vt_regex(bt) {
            if (vt_cache[bt] !== undefined) return vt_cache[bt];
            return (vt_cache[bt] = new RegExp("<(?:vt:)?" + bt + ">([\\s\\S]*?)(?:vt:)?" + bt + ">", 'g'));
        };
    })();
    var vtvregex = /<\/?(?:vt:)?variant>/g,
        vtmregex = /<(?:vt:)([^>]*)>([\s\S]*);
    function parseVector(data, opts) {
        var h = parsexmltag(data);
        var matches = data.match(vtregex(h.baseType)) || [];
        var res = [];
        if (matches.length != h.size) {
            if (opts.WTF) throw new Error("unexpected vector length " + matches.length + " != " + h.size);
            return res;
        }
        matches.forEach(function (x) {
            var v = x.replace(vtvregex, "").match(vtmregex);
            if (v) res.push({
                v: utf8read(v[2]),
                t: v[1]
            });
        });
        return res;
    }
    var wtregex = /(^\s|\s$|\n)/;
    function writetag(f, g) {
        return '<' + f + (g.match(wtregex) ? ' xml:space="preserve"' : "") + '>' + g + '' + f + '>';
    }
    function wxt_helper(h) {
        return keys(h).map(function (k) {
            return " " + k + '="' + h[k] + '"';
        }).join("");
    }
    function writextag(f, g, h) {
        return '<' + f + ((h != null) ? wxt_helper(h) : "") + ((g != null) ? (g.match(wtregex) ? ' xml:space="preserve"' : "") + '>' + g + '' + f : "/") + '>';
    }
    function write_w3cdtf(d, t) {
        try {
            return d.toISOString().replace(/\.\d*/, "");
        } catch (e) {
            if (t) throw e;
        }
        return "";
    }
    function write_vt(s) {
        switch (typeof s) {
            case 'string':
                return writextag('vt:lpwstr', s);
            case 'number':
                return writextag((s | 0) == s ? 'vt:i4' : 'vt:r8', String(s));
            case 'boolean':
                return writextag('vt:bool', s ? 'true' : 'false');
        }
        if (s instanceof Date) return writextag('vt:filetime', write_w3cdtf(s));
        throw new Error("Unable to serialize " + s);
    }
    var XMLNS = ({
        'dc': 'http://purl.org/dc/elements/1.1/',
        'dcterms': 'http://purl.org/dc/terms/',
        'dcmitype': 'http://purl.org/dc/dcmitype/',
        'mx': 'http://schemas.microsoft.com/office/mac/excel/2008/main',
        'r': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships',
        'sjs': 'http://schemas.openxmlformats.org/package/2006/sheetjs/core-properties',
        'vt': 'http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes',
        'xsi': 'http://www.w3.org/2001/XMLSchema-instance',
        'xsd': 'http://www.w3.org/2001/XMLSchema'
    });
    XMLNS.main = [
        'http://schemas.openxmlformats.org/spreadsheetml/2006/main',
        'http://purl.oclc.org/ooxml/spreadsheetml/main',
        'http://schemas.microsoft.com/office/excel/2006/main',
        'http://schemas.microsoft.com/office/excel/2006/2'
    ];
    var XLMLNS = ({
        'o': 'urn:schemas-microsoft-com:office:office',
        'x': 'urn:schemas-microsoft-com:office:excel',
        'ss': 'urn:schemas-microsoft-com:office:spreadsheet',
        'dt': 'uuid:C2F41010-65B3-11d1-A29F-00AA00C14882',
        'mv': 'http://macVmlSchemaUri',
        'v': 'urn:schemas-microsoft-com:vml',
        'html': 'http://www.w3.org/TR/REC-html40'
    });
    function read_double_le(b, idx) {
        var s = 1 - 2 * (b[idx + 7] >>> 7);
        var e = ((b[idx + 7] & 0x7f) << 4) + ((b[idx + 6] >>> 4) & 0x0f);
        var m = (b[idx + 6] & 0x0f);
        for (var i = 5; i >= 0; --i) m = m * 256 + b[idx + i];
        if (e == 0x7ff) return m == 0 ? (s * Infinity) : NaN;
        if (e == 0) e = -1022;
        else {
            e -= 1023;
            m += Math.pow(2, 52);
        }
        return s * Math.pow(2, e - 52) * m;
    }
    function write_double_le(b, v, idx) {
        var bs = ((((v < 0) || (1 / v == -Infinity)) ? 1 : 0) << 7),
            e = 0,
            m = 0;
        var av = bs ? (-v) : v;
        if (!isFinite(av)) {
            e = 0x7ff;
            m = isNaN(v) ? 0x6969 : 0;
        } else if (av == 0) e = m = 0;
        else {
            e = Math.floor(Math.log(av) / Math.LN2);
            m = av * Math.pow(2, 52 - e);
            if ((e <= -1023) && (!isFinite(m) || (m < Math.pow(2, 52)))) {
                e = -1022;
            } else {
                m -= Math.pow(2, 52);
                e += 1023;
            }
        }
        for (var i = 0; i <= 5; ++i, m /= 256) b[idx + i] = m & 0xff;
        b[idx + 6] = ((e & 0x0f) << 4) | (m & 0xf);
        b[idx + 7] = (e >> 4) | bs;
    }
    var __toBuffer = function (bufs) {
        var x = [],
            w = 10240;
        for (var i = 0; i < bufs[0].length; ++i)
            if (bufs[0][i])
                for (var j = 0, L = bufs[0][i].length; j < L; j += w) x.push.apply(x, bufs[0][i].slice(j, j + w));
        return x;
    };
    var ___toBuffer = __toBuffer;
    var __utf16le = function (b, s, e) {
        var ss = [];
        for (var i = s; i < e; i += 2) ss.push(String.fromCharCode(__readUInt16LE(b, i)));
        return ss.join("").replace(chr0, '');
    };
    var ___utf16le = __utf16le;
    var __hexlify = function (b, s, l) {
        var ss = [];
        for (var i = s; i < s + l; ++i) ss.push(("0" + b[i].toString(16)).slice(-2));
        return ss.join("");
    };
    var ___hexlify = __hexlify;
    var __utf8 = function (b, s, e) {
        var ss = [];
        for (var i = s; i < e; i++) ss.push(String.fromCharCode(__readUInt8(b, i)));
        return ss.join("");
    };
    var ___utf8 = __utf8;
    var __lpstr = function (b, i) {
        var len = __readUInt32LE(b, i);
        return len > 0 ? __utf8(b, i + 4, i + 4 + len - 1) : "";
    };
    var ___lpstr = __lpstr;
    var __cpstr = function (b, i) {
        var len = __readUInt32LE(b, i);
        return len > 0 ? __utf8(b, i + 4, i + 4 + len - 1) : "";
    };
    var ___cpstr = __cpstr;
    var __lpwstr = function (b, i) {
        var len = 2 * __readUInt32LE(b, i);
        return len > 0 ? __utf8(b, i + 4, i + 4 + len - 1) : "";
    };
    var ___lpwstr = __lpwstr;
    var __lpp4, ___lpp4;
    __lpp4 = ___lpp4 = function lpp4_(b, i) {
        var len = __readUInt32LE(b, i);
        return len > 0 ? __utf16le(b, i + 4, i + 4 + len) : "";
    };
    var __8lpp4 = function (b, i) {
        var len = __readUInt32LE(b, i);
        return len > 0 ? __utf8(b, i + 4, i + 4 + len) : "";
    };
    var ___8lpp4 = __8lpp4;
    var __double, ___double;
    __double = ___double = function (b, idx) {
        return read_double_le(b, idx);
    };
    var is_buf = function is_buf_a(a) {
        return Array.isArray(a);
    };
    if (has_buf) {
        __utf16le = function (b, s, e) {
            if (!Buffer.isBuffer(b)) return ___utf16le(b, s, e);
            return b.toString('utf16le', s, e).replace(chr0, '') /*.replace(chr1,'!')*/ ;
        };
        __hexlify = function (b, s, l) {
            return Buffer.isBuffer(b) ? b.toString('hex', s, s + l) : ___hexlify(b, s, l);
        };
        __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) : "";
        };
        __cpstr = function cpstr_b(b, i) {
            if (!Buffer.isBuffer(b)) return ___cpstr(b, i);
            var len = b.readUInt32LE(i);
            return len > 0 ? b.toString('utf8', i + 4, i + 4 + len - 1) : "";
        };
        __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);
        };
        __lpp4 = function lpp4_b(b, i) {
            if (!Buffer.isBuffer(b)) return ___lpp4(b, i);
            var len = b.readUInt32LE(i);
            return b.toString('utf16le', i + 4, i + 4 + len);
        };
        __8lpp4 = function lpp4_8b(b, i) {
            if (!Buffer.isBuffer(b)) return ___8lpp4(b, i);
            var len = b.readUInt32LE(i);
            return b.toString('utf8', i + 4, i + 4 + len);
        };
        __utf8 = function utf8_b(b, s, e) {
            return (Buffer.isBuffer(b)) ? b.toString('utf8', s, e) : ___utf8(b, s, e);
        };
        __toBuffer = function (bufs) {
            return (bufs[0].length > 0 && Buffer.isBuffer(bufs[0][0])) ? Buffer.concat(bufs[0]) : ___toBuffer(bufs);
        };
        bconcat = function (bufs) {
            return Buffer.isBuffer(bufs[0]) ? Buffer.concat(bufs) : [].concat.apply([], bufs);
        };
        __double = function double_(b, i) {
            if (Buffer.isBuffer(b)) return b.readDoubleLE(i);
            return ___double(b, i);
        };
        is_buf = function is_buf_b(a) {
            return Buffer.isBuffer(a) || Array.isArray(a);
        };
    }
    /* from js-xls */
    if (typeof cptable !== 'undefined') {
        __utf16le = function (b, s, e) {
            return cptable.utils.decode(1200, b.slice(s, e)).replace(chr0, '');
        };
        __utf8 = function (b, s, e) {
            return cptable.utils.decode(65001, b.slice(s, e));
        };
        __lpstr = function (b, i) {
            var len = __readUInt32LE(b, i);
            return len > 0 ? cptable.utils.decode(current_ansi, b.slice(i + 4, i + 4 + len - 1)) : "";
        };
        __cpstr = function (b, i) {
            var len = __readUInt32LE(b, i);
            return len > 0 ? cptable.utils.decode(current_codepage, b.slice(i + 4, i + 4 + len - 1)) : "";
        };
        __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)) : "";
        };
        __lpp4 = function (b, i) {
            var len = __readUInt32LE(b, i);
            return len > 0 ? cptable.utils.decode(1200, b.slice(i + 4, i + 4 + len)) : "";
        };
        __8lpp4 = function (b, i) {
            var len = __readUInt32LE(b, i);
            return len > 0 ? cptable.utils.decode(65001, b.slice(i + 4, i + 4 + len)) : "";
        };
    }
    var __readUInt8 = function (b, idx) {
        return b[idx];
    };
    var __readUInt16LE = function (b, idx) {
        return (b[idx + 1] * (1 << 8)) + b[idx];
    };
    var __readInt16LE = function (b, idx) {
        var u = (b[idx + 1] * (1 << 8)) + b[idx];
        return (u < 0x8000) ? u : ((0xffff - u + 1) * -1);
    };
    var __readUInt32LE = function (b, idx) {
        return b[idx + 3] * (1 << 24) + (b[idx + 2] << 16) + (b[idx + 1] << 8) + b[idx];
    };
    var __readInt32LE = function (b, idx) {
        return (b[idx + 3] << 24) | (b[idx + 2] << 16) | (b[idx + 1] << 8) | b[idx];
    };
    var __readInt32BE = function (b, idx) {
        return (b[idx] << 24) | (b[idx + 1] << 16) | (b[idx + 2] << 8) | b[idx + 3];
    };
    function ReadShift(size, t) {
        var o = "",
            oI, oR, oo = [],
            w, vv, i, loc;
        switch (t) {
            case 'dbcs':
                loc = this.l;
                if (has_buf && Buffer.isBuffer(this)) o = this.slice(this.l, this.l + 2 * size).toString("utf16le");
                else
                    for (i = 0; i < size; ++i) {
                        o += String.fromCharCode(__readUInt16LE(this, loc));
                        loc += 2;
                    }
                size *= 2;
                break;
            case 'utf8':
                o = __utf8(this, this.l, this.l + size);
                break;
            case 'utf16le':
                size *= 2;
                o = __utf16le(this, this.l, this.l + size);
                break;
            case 'wstr':
                if (typeof cptable !== 'undefined') o = cptable.utils.decode(current_codepage, this.slice(this.l, this.l + 2 * size));
                else return ReadShift.call(this, size, 'dbcs');
                size = 2 * size;
                break;
                /* [MS-OLEDS] 2.1.4 LengthPrefixedAnsiString */
            case 'lpstr-ansi':
                o = __lpstr(this, this.l);
                size = 4 + __readUInt32LE(this, this.l);
                break;
            case 'lpstr-cp':
                o = __cpstr(this, this.l);
                size = 4 + __readUInt32LE(this, this.l);
                break;
                /* [MS-OLEDS] 2.1.5 LengthPrefixedUnicodeString */
            case 'lpwstr':
                o = __lpwstr(this, this.l);
                size = 4 + 2 * __readUInt32LE(this, this.l);
                break;
                /* [MS-OFFCRYPTO] 2.1.2 Length-Prefixed Padded Unicode String (UNICODE-LP-P4) */
            case 'lpp4':
                size = 4 + __readUInt32LE(this, this.l);
                o = __lpp4(this, this.l);
                if (size & 0x02) size += 2;
                break;
                /* [MS-OFFCRYPTO] 2.1.3 Length-Prefixed UTF-8 String (UTF-8-LP-P4) */
            case '8lpp4':
                size = 4 + __readUInt32LE(this, this.l);
                o = __8lpp4(this, this.l);
                if (size & 0x03) size += 4 - (size & 0x03);
                break;
            case 'cstr':
                size = 0;
                o = "";
                while ((w = __readUInt8(this, this.l + size++)) !== 0) oo.push(_getchar(w));
                o = oo.join("");
                break;
            case '_wstr':
                size = 0;
                o = "";
                while ((w = __readUInt16LE(this, this.l + size)) !== 0) {
                    oo.push(_getchar(w));
                    size += 2;
                }
                size += 2;
                o = oo.join("");
                break;
                /* sbcs and dbcs support continue records in the SST way TODO codepages */
            case 'dbcs-cont':
                o = "";
                loc = this.l;
                for (i = 0; i < size; ++i) {
                    if (this.lens && this.lens.indexOf(loc) !== -1) {
                        w = __readUInt8(this, loc);
                        this.l = loc + 1;
                        vv = ReadShift.call(this, size - i, w ? 'dbcs-cont' : 'sbcs-cont');
                        return oo.join("") + vv;
                    }
                    oo.push(_getchar(__readUInt16LE(this, loc)));
                    loc += 2;
                }
                o = oo.join("");
                size *= 2;
                break;
            case 'cpstr':
                if (typeof cptable !== 'undefined') {
                    o = cptable.utils.decode(current_codepage, this.slice(this.l, this.l + size));
                    break;
                }
                /* falls through */
            case 'sbcs-cont':
                o = "";
                loc = this.l;
                for (i = 0; i != size; ++i) {
                    if (this.lens && this.lens.indexOf(loc) !== -1) {
                        w = __readUInt8(this, loc);
                        this.l = loc + 1;
                        vv = ReadShift.call(this, size - i, w ? 'dbcs-cont' : 'sbcs-cont');
                        return oo.join("") + vv;
                    }
                    oo.push(_getchar(__readUInt8(this, loc)));
                    loc += 1;
                }
                o = oo.join("");
                break;
            default:
                switch (size) {
                    case 1:
                        oI = __readUInt8(this, this.l);
                        this.l++;
                        return oI;
                    case 2:
                        oI = (t === 'i' ? __readInt16LE : __readUInt16LE)(this, this.l);
                        this.l += 2;
                        return oI;
                    case 4:
                    case -4:
                        if (t === 'i' || ((this[this.l + 3] & 0x80) === 0)) {
                            oI = ((size > 0) ? __readInt32LE : __readInt32BE)(this, this.l);
                            this.l += 4;
                            return oI;
                        } else {
                            oR = __readUInt32LE(this, this.l);
                            this.l += 4;
                        }
                        return oR;
                    case 8:
                    case -8:
                        if (t === 'f') {
                            if (size == 8) oR = __double(this, this.l);
                            else oR = __double([this[this.l + 7], this[this.l + 6], this[this.l + 5], this[this.l + 4], this[this.l + 3], this[this.l + 2], this[this.l + 1], this[this.l + 0]], 0);
                            this.l += 8;
                            return oR;
                        } else size = 8;
                        /* falls through */
                    case 16:
                        o = __hexlify(this, this.l, size);
                        break;
                }
        }
        this.l += size;
        return o;
    }
    var __writeUInt32LE = function (b, val, idx) {
        b[idx] = (val & 0xFF);
        b[idx + 1] = ((val >>> 8) & 0xFF);
        b[idx + 2] = ((val >>> 16) & 0xFF);
        b[idx + 3] = ((val >>> 24) & 0xFF);
    };
    var __writeInt32LE = function (b, val, idx) {
        b[idx] = (val & 0xFF);
        b[idx + 1] = ((val >> 8) & 0xFF);
        b[idx + 2] = ((val >> 16) & 0xFF);
        b[idx + 3] = ((val >> 24) & 0xFF);
    };
    var __writeUInt16LE = function (b, val, idx) {
        b[idx] = (val & 0xFF);
        b[idx + 1] = ((val >>> 8) & 0xFF);
    };
    function WriteShift(t, val, f) {
        var size = 0,
            i = 0;
        if (f === 'dbcs') {
            for (i = 0; i != val.length; ++i) __writeUInt16LE(this, val.charCodeAt(i), this.l + 2 * i);
            size = 2 * val.length;
        } else if (f === 'sbcs') {
            /* TODO: codepage */
            val = val.replace(/[^\x00-\x7F]/g, "_");
            for (i = 0; i != val.length; ++i) this[this.l + i] = (val.charCodeAt(i) & 0xFF);
            size = val.length;
        } else if (f === 'hex') {
            for (; i < t; ++i) {
                this[this.l++] = (parseInt(val.slice(2 * i, 2 * i + 2), 16) || 0);
            }
            return this;
        } else if (f === 'utf16le') {
            var end = Math.min(this.l + t, this.length);
            for (i = 0; i < Math.min(val.length, t); ++i) {
                var cc = val.charCodeAt(i);
                this[this.l++] = (cc & 0xff);
                this[this.l++] = (cc >> 8);
            }
            while (this.l < end) this[this.l++] = 0;
            return this;
        } else switch (t) {
            case 1:
                size = 1;
                this[this.l] = val & 0xFF;
                break;
            case 2:
                size = 2;
                this[this.l] = val & 0xFF;
                val >>>= 8;
                this[this.l + 1] = val & 0xFF;
                break;
            case 3:
                size = 3;
                this[this.l] = val & 0xFF;
                val >>>= 8;
                this[this.l + 1] = val & 0xFF;
                val >>>= 8;
                this[this.l + 2] = val & 0xFF;
                break;
            case 4:
                size = 4;
                __writeUInt32LE(this, val, this.l);
                break;
            case 8:
                size = 8;
                if (f === 'f') {
                    write_double_le(this, val, this.l);
                    break;
                }
                /* falls through */
            case 16:
                break;
            case -4:
                size = 4;
                __writeInt32LE(this, val, this.l);
                break;
        }
        this.l += size;
        return this;
    }
    function CheckField(hexstr, fld) {
        var m = __hexlify(this, this.l, hexstr.length >> 1);
        if (m !== hexstr) throw new Error(fld + 'Expected ' + hexstr + ' saw ' + m);
        this.l += hexstr.length >> 1;
    }
    function prep_blob(blob, pos) {
        blob.l = pos;
        blob.read_shift = ReadShift;
        blob.chk = CheckField;
        blob.write_shift = WriteShift;
    }
    function parsenoop(blob, length) {
        blob.l += length;
    }
    function new_buf(sz) {
        var o = new_raw_buf(sz);
        prep_blob(o, 0);
        return o;
    }
    /* [MS-XLSB] 2.1.4 Record */
    function recordhopper(data, cb, opts) {
        if (!data) return;
        var tmpbyte, cntbyte, length;
        prep_blob(data, data.l || 0);
        var L = data.length,
            RT = 0,
            tgt = 0;
        while (data.l < L) {
            RT = data.read_shift(1);
            if (RT & 0x80) RT = (RT & 0x7F) + ((data.read_shift(1) & 0x7F) << 7);
            var R = XLSBRecordEnum[RT] || XLSBRecordEnum[0xFFFF];
            tmpbyte = data.read_shift(1);
            length = tmpbyte & 0x7F;
            for (cntbyte = 1; cntbyte < 4 && (tmpbyte & 0x80); ++cntbyte) length += ((tmpbyte = data.read_shift(1)) & 0x7F) << (7 * cntbyte);
            tgt = data.l + length;
            var d = (R.f || parsenoop)(data, length, opts);
            data.l = tgt;
            if (cb(d, R.n, RT)) return;
        }
    }
    /* control buffer usage for fixed-length buffers */
    function buf_array() {
        var bufs = [],
            blksz = has_buf ? 256 : 2048;
        var newblk = function ba_newblk(sz) {
            var o = (new_buf(sz));
            prep_blob(o, 0);
            return o;
        };
        var curbuf = newblk(blksz);
        var endbuf = function ba_endbuf() {
            if (!curbuf) return;
            if (curbuf.length > curbuf.l) {
                curbuf = curbuf.slice(0, curbuf.l);
                curbuf.l = curbuf.length;
            }
            if (curbuf.length > 0) bufs.push(curbuf);
            curbuf = null;
        };
        var next = function ba_next(sz) {
            if (curbuf && (sz < (curbuf.length - curbuf.l))) return curbuf;
            endbuf();
            return (curbuf = newblk(Math.max(sz + 1, blksz)));
        };
        var end = function ba_end() {
            endbuf();
            return __toBuffer([bufs]);
        };
        var push = function ba_push(buf) {
            endbuf();
            curbuf = buf;
            if (curbuf.l == null) curbuf.l = curbuf.length;
            next(blksz);
        };
        return ({
            next: next,
            push: push,
            end: end,
            _bufs: bufs
        });
    }
    function write_record(ba, type, payload, length) {
        var t = +XLSBRE[type],
            l;
        if (isNaN(t)) return; // TODO: throw something here?
        if (!length) length = XLSBRecordEnum[t].p || (payload || []).length || 0;
        l = 1 + (t >= 0x80 ? 1 : 0) + 1 /* + length*/ ;
        if (length >= 0x80) ++l;
        if (length >= 0x4000) ++l;
        if (length >= 0x200000) ++l;
        var o = ba.next(l);
        if (t <= 0x7F) o.write_shift(1, t);
        else {
            o.write_shift(1, (t & 0x7F) + 0x80);
            o.write_shift(1, (t >> 7));
        }
        for (var i = 0; i != 4; ++i) {
            if (length >= 0x80) {
                o.write_shift(1, (length & 0x7F) + 0x80);
                length >>= 7;
            } else {
                o.write_shift(1, length);
                break;
            }
        }
        if (length > 0 && is_buf(payload)) ba.push(payload);
    }
    /* XLS ranges enforced */
    function shift_cell_xls(cell, tgt, opts) {
        var out = dup(cell);
        if (tgt.s) {
            if (out.cRel) out.c += tgt.s.c;
            if (out.rRel) out.r += tgt.s.r;
        } else {
            if (out.cRel) out.c += tgt.c;
            if (out.rRel) out.r += tgt.r;
        }
        if (!opts || opts.biff < 12) {
            while (out.c >= 0x100) out.c -= 0x100;
            while (out.r >= 0x10000) out.r -= 0x10000;
        }
        return out;
    }
    function shift_range_xls(cell, range, opts) {
        var out = dup(cell);
        out.s = shift_cell_xls(out.s, range.s, opts);
        out.e = shift_cell_xls(out.e, range.s, opts);
        return out;
    }
    function encode_cell_xls(c, biff) {
        if (c.cRel && c.c < 0) {
            c = dup(c);
            c.c += (biff > 8) ? 0x4000 : 0x100;
        }
        if (c.rRel && c.r < 0) {
            c = dup(c);
            c.r += (biff > 8) ? 0x100000 : ((biff > 5) ? 0x10000 : 0x4000);
        }
        var s = encode_cell(c);
        if (c.cRel === 0) s = fix_col(s);
        if (c.rRel === 0) s = fix_row(s);
        return s;
    }
    function encode_range_xls(r, opts) {
        if (r.s.r == 0 && !r.s.rRel) {
            if (r.e.r == (opts.biff >= 12 ? 0xFFFFF : (opts.biff >= 8 ? 0x10000 : 0x4000)) && !r.e.rRel) {
                return (r.s.cRel ? "" : "$") + encode_col(r.s.c) + ":" + (r.e.cRel ? "" : "$") + encode_col(r.e.c);
            }
        }
        if (r.s.c == 0 && !r.s.cRel) {
            if (r.e.c == (opts.biff >= 12 ? 0xFFFF : 0xFF) && !r.e.cRel) {
                return (r.s.rRel ? "" : "$") + encode_row(r.s.r) + ":" + (r.e.rRel ? "" : "$") + encode_row(r.e.r);
            }
        }
        return encode_cell_xls(r.s, opts.biff) + ":" + encode_cell_xls(r.e, opts.biff);
    }
    var OFFCRYPTO = {};
    var make_offcrypto = function (O, _crypto) {
        var crypto;
        if (typeof _crypto !== 'undefined') crypto = _crypto;
        else if (typeof require !== 'undefined') {
            try {
                crypto = require('crypto');
            } catch (e) {
                crypto = null;
            }
        }
        O.rc4 = function (key, data) {
            var S = new Array(256);
            var c = 0,
                i = 0,
                j = 0,
                t = 0;
            for (i = 0; i != 256; ++i) S[i] = i;
            for (i = 0; i != 256; ++i) {
                j = (j + S[i] + (key[i % key.length]).charCodeAt(0)) & 255;
                t = S[i];
                S[i] = S[j];
                S[j] = t;
            }
            // $FlowIgnore
            i = j = 0;
            var out = Buffer(data.length);
            for (c = 0; c != data.length; ++c) {
                i = (i + 1) & 255;
                j = (j + S[i]) % 256;
                t = S[i];
                S[i] = S[j];
                S[j] = t;
                out[c] = (data[c] ^ S[(S[i] + S[j]) & 255]);
            }
            return out;
        };
        O.md5 = function (hex) {
            if (!crypto) throw new Error("Unsupported crypto");
            return crypto.createHash('md5').update(hex).digest('hex');
        };
    };
    /*global crypto:true */
    make_offcrypto(OFFCRYPTO, typeof crypto !== "undefined" ? crypto : undefined);
    function decode_row(rowstr) {
        return parseInt(unfix_row(rowstr), 10) - 1;
    }
    function encode_row(row) {
        return "" + (row + 1);
    }
    function fix_row(cstr) {
        return cstr.replace(/([A-Z]|^)(\d+)$/, "$1$$$2");
    }
    function unfix_row(cstr) {
        return cstr.replace(/\$(\d+)$/, "$1");
    }
    function 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;
    }
    function encode_col(col) {
        var s = "";
        for (++col; col; col = Math.floor((col - 1) / 26)) s = String.fromCharCode(((col - 1) % 26) + 65) + s;
        return s;
    }
    function fix_col(cstr) {
        return cstr.replace(/^([A-Z])/, "$$$1");
    }
    function unfix_col(cstr) {
        return cstr.replace(/^\$([A-Z])/, "$1");
    }
    function split_cell(cstr) {
        return cstr.replace(/(\$?[A-Z]*)(\$?\d*)/, "$1,$2").split(",");
    }
    function decode_cell(cstr) {
        var splt = split_cell(cstr);
        return {
            c: decode_col(splt[0]),
            r: decode_row(splt[1])
        };
    }
    function encode_cell(cell) {
        return encode_col(cell.c) + encode_row(cell.r);
    }
    function decode_range(range) {
        var x = range.split(":").map(decode_cell);
        return {
            s: x[0],
            e: x[x.length - 1]
        };
    }
    function encode_range(cs, ce) {
        if (typeof ce === 'undefined' || typeof ce === 'number') {
            return encode_range(cs.s, cs.e);
        }
        if (typeof cs !== 'string') cs = encode_cell((cs));
        if (typeof ce !== 'string') ce = encode_cell((ce));
        return cs == ce ? cs : cs + ":" + ce;
    }
    function safe_decode_range(range) {
        var o = {
            s: {
                c: 0,
                r: 0
            },
            e: {
                c: 0,
                r: 0
            }
        };
        var idx = 0,
            i = 0,
            cc = 0;
        var len = range.length;
        for (idx = 0; i < len; ++i) {
            if ((cc = range.charCodeAt(i) - 64) < 1 || cc > 26) break;
            idx = 26 * idx + cc;
        }
        o.s.c = --idx;
        for (idx = 0; i < len; ++i) {
            if ((cc = range.charCodeAt(i) - 48) < 0 || cc > 9) break;
            idx = 10 * idx + cc;
        }
        o.s.r = --idx;
        if (i === len || range.charCodeAt(++i) === 58) {
            o.e.c = o.s.c;
            o.e.r = o.s.r;
            return o;
        }
        for (idx = 0; i != len; ++i) {
            if ((cc = range.charCodeAt(i) - 64) < 1 || cc > 26) break;
            idx = 26 * idx + cc;
        }
        o.e.c = --idx;
        for (idx = 0; i != len; ++i) {
            if ((cc = range.charCodeAt(i) - 48) < 0 || cc > 9) break;
            idx = 10 * idx + cc;
        }
        o.e.r = --idx;
        return o;
    }
    function safe_format_cell(cell, v) {
        var q = (cell.t == 'd' && v instanceof Date);
        if (cell.z != null) try {
            return (cell.w = SSF.format(cell.z, q ? datenum(v) : v));
        } catch (e) {}
        try {
            return (cell.w = SSF.format((cell.XF || {}).numFmtId || (q ? 14 : 0), q ? datenum(v) : v));
        } catch (e) {
            return '' + v;
        }
    }
    function format_cell(cell, v, o) {
        if (cell == null || cell.t == null || cell.t == 'z') return "";
        if (cell.w !== undefined) return cell.w;
        if (cell.t == 'd' && !cell.z && o && o.dateNF) cell.z = o.dateNF;
        if (v == undefined) return safe_format_cell(cell, cell.v);
        return safe_format_cell(cell, v);
    }
    function sheet_to_workbook(sheet, opts) {
        var n = opts && opts.sheet ? opts.sheet : "Sheet1";
        var sheets = {};
        sheets[n] = sheet;
        return {
            SheetNames: [n],
            Sheets: sheets
        };
    }
    function sheet_add_aoa(_ws, data, opts) {
        var o = opts || {};
        var dense = _ws ? Array.isArray(_ws) : o.dense;
        if (DENSE != null && dense == null) dense = DENSE;
        var ws = _ws || (dense ? ([]) : ({}));
        var _R = 0,
            _C = 0;
        if (ws && o.origin != null) {
            if (typeof o.origin == 'number') _R = o.origin;
            else {
                var _origin = typeof o.origin == "string" ? decode_cell(o.origin) : o.origin;
                _R = _origin.r;
                _C = _origin.c;
            }
        }
        var range = ({
            s: {
                c: 10000000,
                r: 10000000
            },
            e: {
                c: 0,
                r: 0
            }
        });
        if (ws['!ref']) {
            var _range = safe_decode_range(ws['!ref']);
            range.s.c = _range.s.c;
            range.s.r = _range.s.r;
            range.e.c = Math.max(range.e.c, _range.e.c);
            range.e.r = Math.max(range.e.r, _range.e.r);
            if (_R == -1) range.e.r = _R = _range.e.r + 1;
        }
        for (var R = 0; R != data.length; ++R) {
            if (!data[R]) continue;
            if (!Array.isArray(data[R])) throw new Error("aoa_to_sheet expects an array of arrays");
            for (var C = 0; C != data[R].length; ++C) {
                if (typeof data[R][C] === 'undefined') continue;
                var cell = ({
                    v: data[R][C]
                });
                var __R = _R + R,
                    __C = _C + C;
                if (range.s.r > __R) range.s.r = __R;
                if (range.s.c > __C) range.s.c = __C;
                if (range.e.r < __R) range.e.r = __R;
                if (range.e.c < __C) range.e.c = __C;
                if (data[R][C] && typeof data[R][C] === 'object' && !Array.isArray(data[R][C]) && !(data[R][C] instanceof Date)) cell = data[R][C];
                else {
                    if (Array.isArray(cell.v)) {
                        cell.f = data[R][C][1];
                        cell.v = cell.v[0];
                    }
                    if (cell.v === null) {
                        if (cell.f) cell.t = 'n';
                        else if (!o.cellStubs) continue;
                        else cell.t = 'z';
                    } else if (typeof cell.v === 'number') cell.t = 'n';
                    else if (typeof cell.v === 'boolean') cell.t = 'b';
                    else if (cell.v instanceof Date) {
                        cell.z = o.dateNF || SSF._table[14];
                        if (o.cellDates) {
                            cell.t = 'd';
                            cell.w = SSF.format(cell.z, datenum(cell.v));
                        } else {
                            cell.t = 'n';
                            cell.v = datenum(cell.v);
                            cell.w = SSF.format(cell.z, cell.v);
                        }
                    } else cell.t = 's';
                }
                if (dense) {
                    if (!ws[__R]) ws[__R] = [];
                    ws[__R][__C] = cell;
                } else {
                    var cell_ref = encode_cell(({
                        c: __C,
                        r: __R
                    }));
                    ws[cell_ref] = cell;
                }
            }
        }
        if (range.s.c < 10000000) ws['!ref'] = encode_range(range);
        return ws;
    }
    function aoa_to_sheet(data, opts) {
        return sheet_add_aoa(null, data, opts);
    }
    function write_UInt32LE(x, o) {
        if (!o) o = new_buf(4);
        o.write_shift(4, x);
        return o;
    }
    /* [MS-XLSB] 2.5.168 */
    function parse_XLWideString(data) {
        var cchCharacters = data.read_shift(4);
        return cchCharacters === 0 ? "" : data.read_shift(cchCharacters, 'dbcs');
    }
    function write_XLWideString(data, o) {
        var _null = false;
        if (o == null) {
            _null = true;
            o = new_buf(4 + 2 * data.length);
        }
        o.write_shift(4, data.length);
        if (data.length > 0) o.write_shift(0, data, 'dbcs');
        return _null ? o.slice(0, o.l) : o;
    }
    /* [MS-XLSB] 2.5.143 */
    function parse_StrRun(data) {
        return {
            ich: data.read_shift(2),
            ifnt: data.read_shift(2)
        };
    }
    function write_StrRun(run, o) {
        if (!o) o = new_buf(4);
        o.write_shift(2, run.ich || 0);
        o.write_shift(2, run.ifnt || 0);
        return o;
    }
    /* [MS-XLSB] 2.5.121 */
    function parse_RichStr(data, length) {
        var start = data.l;
        var flags = data.read_shift(1);
        var str = parse_XLWideString(data);
        var rgsStrRun = [];
        var z = ({
            t: str,
            h: str
        });
        if ((flags & 1) !== 0) { /* fRichStr */
            /* TODO: formatted string */
            var dwSizeStrRun = data.read_shift(4);
            for (var i = 0; i != dwSizeStrRun; ++i) rgsStrRun.push(parse_StrRun(data));
            z.r = rgsStrRun;
        } else z.r = [{
            ich: 0,
            ifnt: 0
        }];
        //if((flags & 2) !== 0) { /* fExtStr */
        //	/* TODO: phonetic string */
        //}
        data.l = start + length;
        return z;
    }
    function write_RichStr(str, o) {
        /* TODO: formatted string */
        var _null = false;
        if (o == null) {
            _null = true;
            o = new_buf(15 + 4 * str.t.length);
        }
        o.write_shift(1, 0);
        write_XLWideString(str.t, o);
        return _null ? o.slice(0, o.l) : o;
    }
    /* [MS-XLSB] 2.4.328 BrtCommentText (RichStr w/1 run) */
    var parse_BrtCommentText = parse_RichStr;
    function write_BrtCommentText(str, o) {
        /* TODO: formatted string */
        var _null = false;
        if (o == null) {
            _null = true;
            o = new_buf(23 + 4 * str.t.length);
        }
        o.write_shift(1, 1);
        write_XLWideString(str.t, o);
        o.write_shift(4, 1);
        write_StrRun({
            ich: 0,
            ifnt: 0
        }, o);
        return _null ? o.slice(0, o.l) : o;
    }
    /* [MS-XLSB] 2.5.9 */
    function parse_XLSBCell(data) {
        var col = data.read_shift(4);
        var iStyleRef = data.read_shift(2);
        iStyleRef += data.read_shift(1) << 16;
        data.l++; //var fPhShow = data.read_shift(1);
        return {
            c: col,
            iStyleRef: iStyleRef
        };
    }
    function write_XLSBCell(cell, o) {
        if (o == null) o = new_buf(8);
        o.write_shift(-4, cell.c);
        o.write_shift(3, cell.iStyleRef || cell.s);
        o.write_shift(1, 0); /* fPhShow */
        return o;
    }
    /* [MS-XLSB] 2.5.21 */
    var parse_XLSBCodeName = parse_XLWideString;
    var write_XLSBCodeName = write_XLWideString;
    /* [MS-XLSB] 2.5.166 */
    function parse_XLNullableWideString(data) {
        var cchCharacters = data.read_shift(4);
        return cchCharacters === 0 || cchCharacters === 0xFFFFFFFF ? "" : data.read_shift(cchCharacters, 'dbcs');
    }
    function write_XLNullableWideString(data, o) {
        var _null = false;
        if (o == null) {
            _null = true;
            o = new_buf(127);
        }
        o.write_shift(4, data.length > 0 ? data.length : 0xFFFFFFFF);
        if (data.length > 0) o.write_shift(0, data, 'dbcs');
        return _null ? o.slice(0, o.l) : o;
    }
    /* [MS-XLSB] 2.5.165 */
    var parse_XLNameWideString = parse_XLWideString;
    //var write_XLNameWideString = write_XLWideString;
    /* [MS-XLSB] 2.5.114 */
    var parse_RelID = parse_XLNullableWideString;
    var write_RelID = write_XLNullableWideString;
    /* [MS-XLS] 2.5.217 ; [MS-XLSB] 2.5.122 */
    function parse_RkNumber(data) {
        var b = data.slice(data.l, data.l + 4);
        var fX100 = (b[0] & 1),
            fInt = (b[0] & 2);
        data.l += 4;
        b[0] &= 0xFC; // b[0] &= ~3;
        var RK = fInt === 0 ? __double([0, 0, 0, 0, b[0], b[1], b[2], b[3]], 0) : __readInt32LE(b, 0) >> 2;
        return fX100 ? (RK / 100) : RK;
    }
    function write_RkNumber(data, o) {
        if (o == null) o = new_buf(4);
        var fX100 = 0,
            fInt = 0,
            d100 = data * 100;
        if ((data == (data | 0)) && (data >= -(1 << 29)) && (data < (1 << 29))) {
            fInt = 1;
        } else if ((d100 == (d100 | 0)) && (d100 >= -(1 << 29)) && (d100 < (1 << 29))) {
            fInt = 1;
            fX100 = 1;
        }
        if (fInt) o.write_shift(-4, ((fX100 ? d100 : data) << 2) + (fX100 + 2));
        else throw new Error("unsupported RkNumber " + data); // TODO
    }
    /* [MS-XLSB] 2.5.117 RfX */
    function parse_RfX(data) {
        var cell = ({
            s: {},
            e: {}
        });
        cell.s.r = data.read_shift(4);
        cell.e.r = data.read_shift(4);
        cell.s.c = data.read_shift(4);
        cell.e.c = data.read_shift(4);
        return cell;
    }
    function write_RfX(r, o) {
        if (!o) o = new_buf(16);
        o.write_shift(4, r.s.r);
        o.write_shift(4, r.e.r);
        o.write_shift(4, r.s.c);
        o.write_shift(4, r.e.c);
        return o;
    }
    /* [MS-XLSB] 2.5.153 UncheckedRfX */
    var parse_UncheckedRfX = parse_RfX;
    var write_UncheckedRfX = write_RfX;
    /* [MS-XLS] 2.5.342 ; [MS-XLSB] 2.5.171 */
    /* TODO: error checking, NaN and Infinity values are not valid Xnum */
    function parse_Xnum(data) {
        return data.read_shift(8, 'f');
    }
    function write_Xnum(data, o) {
        return (o || new_buf(8)).write_shift(8, data, 'f');
    }
    /* [MS-XLSB] 2.5.97.2 */
    var BErr = {
        0x00: "#NULL!",
        0x07: "#DIV/0!",
        0x0F: "#VALUE!",
        0x17: "#REF!",
        0x1D: "#NAME?",
        0x24: "#NUM!",
        0x2A: "#N/A",
        0x2B: "#GETTING_DATA",
        0xFF: "#WTF?"
    };
    var RBErr = evert_num(BErr);
    /* [MS-XLSB] 2.4.324 BrtColor */
    function parse_BrtColor(data) {
        var out = {};
        var d = data.read_shift(1);
        //var fValidRGB = d & 1;
        var xColorType = d >>> 1;
        var index = data.read_shift(1);
        var nTS = data.read_shift(2, 'i');
        var bR = data.read_shift(1);
        var bG = data.read_shift(1);
        var bB = data.read_shift(1);
        data.l++; //var bAlpha = data.read_shift(1);
        switch (xColorType) {
            case 0:
                out.auto = 1;
                break;
            case 1:
                out.index = index;
                var icv = XLSIcv[index];
                /* automatic pseudo index 81 */
                if (icv) out.rgb = rgb2Hex(icv);
                break;
            case 2:
                /* if(!fValidRGB) throw new Error("invalid"); */
                out.rgb = rgb2Hex([bR, bG, bB]);
                break;
            case 3:
                out.theme = index;
                break;
        }
        if (nTS != 0) out.tint = nTS > 0 ? nTS / 32767 : nTS / 32768;
        return out;
    }
    function write_BrtColor(color, o) {
        if (!o) o = new_buf(8);
        if (!color || color.auto) {
            o.write_shift(4, 0);
            o.write_shift(4, 0);
            return o;
        }
        if (color.index) {
            o.write_shift(1, 0x02);
            o.write_shift(1, color.index);
        } else if (color.theme) {
            o.write_shift(1, 0x06);
            o.write_shift(1, color.theme);
        } else {
            o.write_shift(1, 0x05);
            o.write_shift(1, 0);
        }
        var nTS = color.tint || 0;
        if (nTS > 0) nTS *= 32767;
        else if (nTS < 0) nTS *= 32768;
        o.write_shift(2, nTS);
        if (!color.rgb) {
            o.write_shift(2, 0);
            o.write_shift(1, 0);
            o.write_shift(1, 0);
        } else {
            var rgb = (color.rgb || 'FFFFFF');
            o.write_shift(1, parseInt(rgb.slice(0, 2), 16));
            o.write_shift(1, parseInt(rgb.slice(2, 4), 16));
            o.write_shift(1, parseInt(rgb.slice(4, 6), 16));
            o.write_shift(1, 0xFF);
        }
        return o;
    }
    /* [MS-XLSB] 2.5.52 */
    function parse_FontFlags(data) {
        var d = data.read_shift(1);
        data.l++;
        var out = {
            /* fBold: d & 0x01 */
            fItalic: d & 0x02,
            /* fUnderline: d & 0x04 */
            fStrikeout: d & 0x08,
            fOutline: d & 0x10,
            fShadow: d & 0x20,
            fCondense: d & 0x40,
            fExtend: d & 0x80
        };
        return out;
    }
    function write_FontFlags(font, o) {
        if (!o) o = new_buf(2);
        var grbit =
            (font.italic ? 0x02 : 0) |
            (font.strike ? 0x08 : 0) |
            (font.outline ? 0x10 : 0) |
            (font.shadow ? 0x20 : 0) |
            (font.condense ? 0x40 : 0) |
            (font.extend ? 0x80 : 0);
        o.write_shift(1, grbit);
        o.write_shift(1, 0);
        return o;
    }
    /* [MS-OLEDS] 2.3.1 and 2.3.2 */
    function parse_ClipboardFormatOrString(o, w) {
        // $FlowIgnore
        var ClipFmt = {
            2: "BITMAP",
            3: "METAFILEPICT",
            8: "DIB",
            14: "ENHMETAFILE"
        };
        var m = o.read_shift(4);
        switch (m) {
            case 0x00000000:
                return "";
            case 0xffffffff:
            case 0xfffffffe:
                return ClipFmt[o.read_shift(4)] || "";
        }
        if (m > 0x190) throw new Error("Unsupported Clipboard: " + m.toString(16));
        o.l -= 4;
        return o.read_shift(0, w == 1 ? "lpstr" : "lpwstr");
    }
    function parse_ClipboardFormatOrAnsiString(o) {
        return parse_ClipboardFormatOrString(o, 1);
    }
    function parse_ClipboardFormatOrUnicodeString(o) {
        return parse_ClipboardFormatOrString(o, 2);
    }
    /* [MS-OLEPS] 2.2 PropertyType */
    //var VT_EMPTY    = 0x0000;
    //var VT_NULL     = 0x0001;
    var VT_I2 = 0x0002;
    var VT_I4 = 0x0003;
    //var VT_R4       = 0x0004;
    //var VT_R8       = 0x0005;
    //var VT_CY       = 0x0006;
    //var VT_DATE     = 0x0007;
    //var VT_BSTR     = 0x0008;
    //var VT_ERROR    = 0x000A;
    var VT_BOOL = 0x000B;
    var VT_VARIANT = 0x000C;
    //var VT_DECIMAL  = 0x000E;
    //var VT_I1       = 0x0010;
    //var VT_UI1      = 0x0011;
    //var VT_UI2      = 0x0012;
    var VT_UI4 = 0x0013;
    //var VT_I8       = 0x0014;
    //var VT_UI8      = 0x0015;
    //var VT_INT      = 0x0016;
    //var VT_UINT     = 0x0017;
    var VT_LPSTR = 0x001E;
    //var VT_LPWSTR   = 0x001F;
    var VT_FILETIME = 0x0040;
    var VT_BLOB = 0x0041;
    //var VT_STREAM   = 0x0042;
    //var VT_STORAGE  = 0x0043;
    //var VT_STREAMED_Object  = 0x0044;
    //var VT_STORED_Object    = 0x0045;
    //var VT_BLOB_Object      = 0x0046;
    var VT_CF = 0x0047;
    //var VT_CLSID    = 0x0048;
    //var VT_VERSIONED_STREAM = 0x0049;
    var VT_VECTOR = 0x1000;
    //var VT_ARRAY    = 0x2000;
    var VT_STRING = 0x0050; // 2.3.3.1.11 VtString
    var VT_USTR = 0x0051; // 2.3.3.1.12 VtUnalignedString
    var VT_CUSTOM = [VT_STRING, VT_USTR];
    /* [MS-OSHARED] 2.3.3.2.2.1 Document Summary Information PIDDSI */
    var DocSummaryPIDDSI = {
        0x01: {
            n: 'CodePage',
            t: VT_I2
        },
        0x02: {
            n: 'Category',
            t: VT_STRING
        },
        0x03: {
            n: 'PresentationFormat',
            t: VT_STRING
        },
        0x04: {
            n: 'ByteCount',
            t: VT_I4
        },
        0x05: {
            n: 'LineCount',
            t: VT_I4
        },
        0x06: {
            n: 'ParagraphCount',
            t: VT_I4
        },
        0x07: {
            n: 'SlideCount',
            t: VT_I4
        },
        0x08: {
            n: 'NoteCount',
            t: VT_I4
        },
        0x09: {
            n: 'HiddenCount',
            t: VT_I4
        },
        0x0a: {
            n: 'MultimediaClipCount',
            t: VT_I4
        },
        0x0b: {
            n: 'ScaleCrop',
            t: VT_BOOL
        },
        0x0c: {
            n: 'HeadingPairs',
            t: VT_VECTOR | VT_VARIANT
        },
        0x0d: {
            n: 'TitlesOfParts',
            t: VT_VECTOR | VT_LPSTR
        },
        0x0e: {
            n: 'Manager',
            t: VT_STRING
        },
        0x0f: {
            n: 'Company',
            t: VT_STRING
        },
        0x10: {
            n: 'LinksUpToDate',
            t: VT_BOOL
        },
        0x11: {
            n: 'CharacterCount',
            t: VT_I4
        },
        0x13: {
            n: 'SharedDoc',
            t: VT_BOOL
        },
        0x16: {
            n: 'HyperlinksChanged',
            t: VT_BOOL
        },
        0x17: {
            n: 'AppVersion',
            t: VT_I4,
            p: 'version'
        },
        0x18: {
            n: 'DigSig',
            t: VT_BLOB
        },
        0x1A: {
            n: 'ContentType',
            t: VT_STRING
        },
        0x1B: {
            n: 'ContentStatus',
            t: VT_STRING
        },
        0x1C: {
            n: 'Language',
            t: VT_STRING
        },
        0x1D: {
            n: 'Version',
            t: VT_STRING
        },
        0xFF: {}
    };
    /* [MS-OSHARED] 2.3.3.2.1.1 Summary Information Property Set PIDSI */
    var SummaryPIDSI = {
        0x01: {
            n: 'CodePage',
            t: VT_I2
        },
        0x02: {
            n: 'Title',
            t: VT_STRING
        },
        0x03: {
            n: 'Subject',
            t: VT_STRING
        },
        0x04: {
            n: 'Author',
            t: VT_STRING
        },
        0x05: {
            n: 'Keywords',
            t: VT_STRING
        },
        0x06: {
            n: 'Comments',
            t: VT_STRING
        },
        0x07: {
            n: 'Template',
            t: VT_STRING
        },
        0x08: {
            n: 'LastAuthor',
            t: VT_STRING
        },
        0x09: {
            n: 'RevNumber',
            t: VT_STRING
        },
        0x0A: {
            n: 'EditTime',
            t: VT_FILETIME
        },
        0x0B: {
            n: 'LastPrinted',
            t: VT_FILETIME
        },
        0x0C: {
            n: 'CreatedDate',
            t: VT_FILETIME
        },
        0x0D: {
            n: 'ModifiedDate',
            t: VT_FILETIME
        },
        0x0E: {
            n: 'PageCount',
            t: VT_I4
        },
        0x0F: {
            n: 'WordCount',
            t: VT_I4
        },
        0x10: {
            n: 'CharCount',
            t: VT_I4
        },
        0x11: {
            n: 'Thumbnail',
            t: VT_CF
        },
        0x12: {
            n: 'Application',
            t: VT_STRING
        },
        0x13: {
            n: 'DocSecurity',
            t: VT_I4
        },
        0xFF: {}
    };
    /* [MS-OLEPS] 2.18 */
    var SpecialProperties = {
        0x80000000: {
            n: 'Locale',
            t: VT_UI4
        },
        0x80000003: {
            n: 'Behavior',
            t: VT_UI4
        },
        0x72627262: {}
    };
    (function () {
        for (var y in SpecialProperties)
            if (SpecialProperties.hasOwnProperty(y))
                DocSummaryPIDDSI[y] = SummaryPIDSI[y] = SpecialProperties[y];
    })();
    var DocSummaryRE = evert_key(DocSummaryPIDDSI, "n");
    var SummaryRE = evert_key(SummaryPIDSI, "n");
    /* [MS-XLS] 2.4.63 Country/Region codes */
    var CountryEnum = {
        0x0001: "US", // United States
        0x0002: "CA", // Canada
        0x0003: "", // Latin America (except Brazil)
        0x0007: "RU", // Russia
        0x0014: "EG", // Egypt
        0x001E: "GR", // Greece
        0x001F: "NL", // Netherlands
        0x0020: "BE", // Belgium
        0x0021: "FR", // France
        0x0022: "ES", // Spain
        0x0024: "HU", // Hungary
        0x0027: "IT", // Italy
        0x0029: "CH", // Switzerland
        0x002B: "AT", // Austria
        0x002C: "GB", // United Kingdom
        0x002D: "DK", // Denmark
        0x002E: "SE", // Sweden
        0x002F: "NO", // Norway
        0x0030: "PL", // Poland
        0x0031: "DE", // Germany
        0x0034: "MX", // Mexico
        0x0037: "BR", // Brazil
        0x003d: "AU", // Australia
        0x0040: "NZ", // New Zealand
        0x0042: "TH", // Thailand
        0x0051: "JP", // Japan
        0x0052: "KR", // Korea
        0x0054: "VN", // Viet Nam
        0x0056: "CN", // China
        0x005A: "TR", // Turkey
        0x0069: "JS", // Ramastan
        0x00D5: "DZ", // Algeria
        0x00D8: "MA", // Morocco
        0x00DA: "LY", // Libya
        0x015F: "PT", // Portugal
        0x0162: "IS", // Iceland
        0x0166: "FI", // Finland
        0x01A4: "CZ", // Czech Republic
        0x0376: "TW", // Taiwan
        0x03C1: "LB", // Lebanon
        0x03C2: "JO", // Jordan
        0x03C3: "SY", // Syria
        0x03C4: "IQ", // Iraq
        0x03C5: "KW", // Kuwait
        0x03C6: "SA", // Saudi Arabia
        0x03CB: "AE", // United Arab Emirates
        0x03CC: "IL", // Israel
        0x03CE: "QA", // Qatar
        0x03D5: "IR", // Iran
        0xFFFF: "US" // United States
    };
    /* [MS-XLS] 2.5.127 */
    var XLSFillPattern = [
        null,
        'solid',
        'mediumGray',
        'darkGray',
        'lightGray',
        'darkHorizontal',
        'darkVertical',
        'darkDown',
        'darkUp',
        'darkGrid',
        'darkTrellis',
        'lightHorizontal',
        'lightVertical',
        'lightDown',
        'lightUp',
        'lightGrid',
        'lightTrellis',
        'gray125',
        'gray0625'
    ];
    function rgbify(arr) {
        return arr.map(function (x) {
            return [(x >> 16) & 255, (x >> 8) & 255, x & 255];
        });
    }
    /* [MS-XLS] 2.5.161 */
    /* [MS-XLSB] 2.5.75 Icv */
    var XLSIcv = rgbify([
        /* Color Constants */
        0x000000,
        0xFFFFFF,
        0xFF0000,
        0x00FF00,
        0x0000FF,
        0xFFFF00,
        0xFF00FF,
        0x00FFFF,
        /* Overridable Defaults */
        0x000000,
        0xFFFFFF,
        0xFF0000,
        0x00FF00,
        0x0000FF,
        0xFFFF00,
        0xFF00FF,
        0x00FFFF,
        0x800000,
        0x008000,
        0x000080,
        0x808000,
        0x800080,
        0x008080,
        0xC0C0C0,
        0x808080,
        0x9999FF,
        0x993366,
        0xFFFFCC,
        0xCCFFFF,
        0x660066,
        0xFF8080,
        0x0066CC,
        0xCCCCFF,
        0x000080,
        0xFF00FF,
        0xFFFF00,
        0x00FFFF,
        0x800080,
        0x800000,
        0x008080,
        0x0000FF,
        0x00CCFF,
        0xCCFFFF,
        0xCCFFCC,
        0xFFFF99,
        0x99CCFF,
        0xFF99CC,
        0xCC99FF,
        0xFFCC99,
        0x3366FF,
        0x33CCCC,
        0x99CC00,
        0xFFCC00,
        0xFF9900,
        0xFF6600,
        0x666699,
        0x969696,
        0x003366,
        0x339966,
        0x003300,
        0x333300,
        0x993300,
        0x993366,
        0x333399,
        0x333333,
        /* Other entries to appease BIFF8/12 */
        0xFFFFFF, /* 0x40 icvForeground ?? */
        0x000000, /* 0x41 icvBackground ?? */
        0x000000, /* 0x42 icvFrame ?? */
        0x000000, /* 0x43 icv3D ?? */
        0x000000, /* 0x44 icv3DText ?? */
        0x000000, /* 0x45 icv3DHilite ?? */
        0x000000, /* 0x46 icv3DShadow ?? */
        0x000000, /* 0x47 icvHilite ?? */
        0x000000, /* 0x48 icvCtlText ?? */
        0x000000, /* 0x49 icvCtlScrl ?? */
        0x000000, /* 0x4A icvCtlInv ?? */
        0x000000, /* 0x4B icvCtlBody ?? */
        0x000000, /* 0x4C icvCtlFrame ?? */
        0x000000, /* 0x4D icvCtlFore ?? */
        0x000000, /* 0x4E icvCtlBack ?? */
        0x000000, /* 0x4F icvCtlNeutral */
        0x000000, /* 0x50 icvInfoBk ?? */
        0x000000 /* 0x51 icvInfoText ?? */
    ]);
    /* Parts enumerated in OPC spec, MS-XLSB and MS-XLSX */
    /* 12.3 Part Summary  */
    /* 14.2 Part Summary  */
    /* [MS-XLSX] 2.1 Part Enumerations ; [MS-XLSB] 2.1.7 Part Enumeration */
    var ct2type /*{[string]:string}*/ = ({
        /* Workbook */
        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml": "workbooks",
        /* Worksheet */
        "application/vnd.ms-excel.binIndexWs": "TODO",
        /* Binary Index */
        /* Macrosheet */
        "application/vnd.ms-excel.intlmacrosheet": "TODO",
        "application/vnd.ms-excel.binIndexMs": "TODO",
        /* Binary Index */
        /* File Properties */
        "application/vnd.openxmlformats-package.core-properties+xml": "coreprops",
        "application/vnd.openxmlformats-officedocument.custom-properties+xml": "custprops",
        "application/vnd.openxmlformats-officedocument.extended-properties+xml": "extprops",
        /* Custom Data Properties */
        "application/vnd.openxmlformats-officedocument.customXmlProperties+xml": "TODO",
        "application/vnd.openxmlformats-officedocument.spreadsheetml.customProperty": "TODO",
        /* PivotTable */
        "application/vnd.ms-excel.pivotTable": "TODO",
        "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml": "TODO",
        /* Chart Colors */
        "application/vnd.ms-office.chartcolorstyle+xml": "TODO",
        /* Chart Style */
        "application/vnd.ms-office.chartstyle+xml": "TODO",
        /* Calculation Chain */
        "application/vnd.ms-excel.calcChain": "calcchains",
        "application/vnd.openxmlformats-officedocument.spreadsheetml.calcChain+xml": "calcchains",
        /* Printer Settings */
        "application/vnd.openxmlformats-officedocument.spreadsheetml.printerSettings": "TODO",
        /* ActiveX */
        "application/vnd.ms-office.activeX": "TODO",
        "application/vnd.ms-office.activeX+xml": "TODO",
        /* Custom Toolbars */
        "application/vnd.ms-excel.attachedToolbars": "TODO",
        /* External Data Connections */
        "application/vnd.ms-excel.connections": "TODO",
        "application/vnd.openxmlformats-officedocument.spreadsheetml.connections+xml": "TODO",
        /* External Links */
        "application/vnd.ms-excel.externalLink": "links",
        "application/vnd.openxmlformats-officedocument.spreadsheetml.externalLink+xml": "links",
        /* Metadata */
        "application/vnd.ms-excel.sheetMetadata": "TODO",
        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheetMetadata+xml": "TODO",
        /* PivotCache */
        "application/vnd.ms-excel.pivotCacheDefinition": "TODO",
        "application/vnd.ms-excel.pivotCacheRecords": "TODO",
        "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml": "TODO",
        "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml": "TODO",
        /* Query Table */
        "application/vnd.ms-excel.queryTable": "TODO",
        "application/vnd.openxmlformats-officedocument.spreadsheetml.queryTable+xml": "TODO",
        /* Shared Workbook */
        "application/vnd.ms-excel.userNames": "TODO",
        "application/vnd.ms-excel.revisionHeaders": "TODO",
        "application/vnd.ms-excel.revisionLog": "TODO",
        "application/vnd.openxmlformats-officedocument.spreadsheetml.revisionHeaders+xml": "TODO",
        "application/vnd.openxmlformats-officedocument.spreadsheetml.revisionLog+xml": "TODO",
        "application/vnd.openxmlformats-officedocument.spreadsheetml.userNames+xml": "TODO",
        /* Single Cell Table */
        "application/vnd.ms-excel.tableSingleCells": "TODO",
        "application/vnd.openxmlformats-officedocument.spreadsheetml.tableSingleCells+xml": "TODO",
        /* Slicer */
        "application/vnd.ms-excel.slicer": "TODO",
        "application/vnd.ms-excel.slicerCache": "TODO",
        "application/vnd.ms-excel.slicer+xml": "TODO",
        "application/vnd.ms-excel.slicerCache+xml": "TODO",
        /* Sort Map */
        "application/vnd.ms-excel.wsSortMap": "TODO",
        /* Table */
        "application/vnd.ms-excel.table": "TODO",
        "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml": "TODO",
        /* Themes */
        "application/vnd.openxmlformats-officedocument.theme+xml": "themes",
        /* Theme Override */
        "application/vnd.openxmlformats-officedocument.themeOverride+xml": "TODO",
        /* Timeline */
        "application/vnd.ms-excel.Timeline+xml": "TODO",
        /* verify */
        "application/vnd.ms-excel.TimelineCache+xml": "TODO",
        /* verify */
        /* VBA */
        "application/vnd.ms-office.vbaProject": "vba",
        "application/vnd.ms-office.vbaProjectSignature": "vba",
        /* Volatile Dependencies */
        "application/vnd.ms-office.volatileDependencies": "TODO",
        "application/vnd.openxmlformats-officedocument.spreadsheetml.volatileDependencies+xml": "TODO",
        /* Control Properties */
        "application/vnd.ms-excel.controlproperties+xml": "TODO",
        /* Data Model */
        "application/vnd.openxmlformats-officedocument.model+data": "TODO",
        /* Survey */
        "application/vnd.ms-excel.Survey+xml": "TODO",
        /* Drawing */
        "application/vnd.openxmlformats-officedocument.drawing+xml": "drawings",
        "application/vnd.openxmlformats-officedocument.drawingml.chart+xml": "TODO",
        "application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml": "TODO",
        "application/vnd.openxmlformats-officedocument.drawingml.diagramColors+xml": "TODO",
        "application/vnd.openxmlformats-officedocument.drawingml.diagramData+xml": "TODO",
        "application/vnd.openxmlformats-officedocument.drawingml.diagramLayout+xml": "TODO",
        "application/vnd.openxmlformats-officedocument.drawingml.diagramStyle+xml": "TODO",
        /* VML */
        "application/vnd.openxmlformats-officedocument.vmlDrawing": "TODO",
        "application/vnd.openxmlformats-package.relationships+xml": "rels",
        "application/vnd.openxmlformats-officedocument.oleObject": "TODO",
        /* Image */
        "image/png": "TODO",
        "sheet": "js"
    });
    var CT_LIST = (function () {
        var o = {
            workbooks: {
                xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml",
                xlsm: "application/vnd.ms-excel.sheet.macroEnabled.main+xml",
                xlsb: "application/vnd.ms-excel.sheet.binary.macroEnabled.main",
                xlam: "application/vnd.ms-excel.addin.macroEnabled.main+xml",
                xltx: "application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml"
            },
            strs: { /* Shared Strings */
                xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml",
                xlsb: "application/vnd.ms-excel.sharedStrings"
            },
            comments: { /* Comments */
                xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml",
                xlsb: "application/vnd.ms-excel.comments"
            },
            sheets: { /* Worksheet */
                xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml",
                xlsb: "application/vnd.ms-excel.worksheet"
            },
            charts: { /* Chartsheet */
                xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml",
                xlsb: "application/vnd.ms-excel.chartsheet"
            },
            dialogs: { /* Dialogsheet */
                xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml",
                xlsb: "application/vnd.ms-excel.dialogsheet"
            },
            macros: { /* Macrosheet (Excel 4.0 Macros) */
                xlsx: "application/vnd.ms-excel.macrosheet+xml",
                xlsb: "application/vnd.ms-excel.macrosheet"
            },
            styles: { /* Styles */
                xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml",
                xlsb: "application/vnd.ms-excel.styles"
            }
        };
        keys(o).forEach(function (k) {
            ["xlsm", "xlam"].forEach(function (v) {
                if (!o[k][v]) o[k][v] = o[k].xlsx;
            });
        });
        keys(o).forEach(function (k) {
            keys(o[k]).forEach(function (v) {
                ct2type[o[k][v]] = k;
            });
        });
        return o;
    })();
    var type2ct /*{[string]:Array}*/ = evert_arr(ct2type);
    XMLNS.CT = 'http://schemas.openxmlformats.org/package/2006/content-types';
    function new_ct() {
        return ({
            workbooks: [],
            sheets: [],
            charts: [],
            dialogs: [],
            macros: [],
            rels: [],
            strs: [],
            comments: [],
            links: [],
            coreprops: [],
            extprops: [],
            custprops: [],
            themes: [],
            styles: [],
            calcchains: [],
            vba: [],
            drawings: [],
            TODO: [],
            xmlns: ""
        });
    }
    function parse_ct(data) {
        var ct = new_ct();
        if (!data || !data.match) return ct;
        var ctext = {};
        (data.match(tagregex) || []).forEach(function (x) {
            var y = parsexmltag(x);
            switch (y[0].replace(nsregex, "<")) {
                case ' 0 ? ct.calcchains[0] : "";
        ct.sst = ct.strs.length > 0 ? ct.strs[0] : "";
        ct.style = ct.styles.length > 0 ? ct.styles[0] : "";
        ct.defaults = ctext;
        delete ct.calcchains;
        return ct;
    }
    var CTYPE_XML_ROOT = writextag('Types', null, {
        'xmlns': XMLNS.CT,
        'xmlns:xsd': XMLNS.xsd,
        'xmlns:xsi': XMLNS.xsi
    });
    var CTYPE_DEFAULTS = [
        ['xml', 'application/xml'],
        ['bin', 'application/vnd.ms-excel.sheet.binary.macroEnabled.main'],
        ['vml', 'application/vnd.openxmlformats-officedocument.vmlDrawing'],
        /* from test files */
        ['bmp', 'image/bmp'],
        ['png', 'image/png'],
        ['gif', 'image/gif'],
        ['emf', 'image/x-emf'],
        ['wmf', 'image/x-wmf'],
        ['jpg', 'image/jpeg'],
        ['jpeg', 'image/jpeg'],
        ['tif', 'image/tiff'],
        ['tiff', 'image/tiff'],
        ['pdf', 'application/pdf'],
        ['rels', type2ct.rels[0]]
    ].map(function (x) {
        return writextag('Default', null, {
            'Extension': x[0],
            'ContentType': x[1]
        });
    });
    function write_ct(ct, opts) {
        var o = [],
            v;
        o[o.length] = (XML_HEADER);
        o[o.length] = (CTYPE_XML_ROOT);
        o = o.concat(CTYPE_DEFAULTS);
        var f1 = function (w) {
            if (ct[w] && ct[w].length > 0) {
                v = ct[w][0];
                o[o.length] = (writextag('Override', null, {
                    'PartName': (v[0] == '/' ? "" : "/") + v,
                    'ContentType': CT_LIST[w][opts.bookType || 'xlsx']
                }));
            }
        };
        var f2 = function (w) {
            (ct[w] || []).forEach(function (v) {
                o[o.length] = (writextag('Override', null, {
                    'PartName': (v[0] == '/' ? "" : "/") + v,
                    'ContentType': CT_LIST[w][opts.bookType || 'xlsx']
                }));
            });
        };
        var f3 = function (t) {
            (ct[t] || []).forEach(function (v) {
                o[o.length] = (writextag('Override', null, {
                    'PartName': (v[0] == '/' ? "" : "/") + v,
                    'ContentType': type2ct[t][0]
                }));
            });
        };
        f1('workbooks');
        f2('sheets');
        f2('charts');
        f3('themes');
        ['strs', 'styles'].forEach(f1);
        ['coreprops', 'extprops', 'custprops'].forEach(f3);
        f3('vba');
        f3('comments');
        f3('drawings');
        if (o.length > 2) {
            o[o.length] = ('');
            o[1] = o[1].replace("/>", ">");
        }
        return o.join("");
    }
    /* 9.3 Relationships */
    var RELS = ({
        WB: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument",
        SHEET: "http://sheetjs.openxmlformats.org/officeDocument/2006/relationships/officeDocument",
        HLINK: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
        VML: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing",
        VBA: "http://schemas.microsoft.com/office/2006/relationships/vbaProject"
    });
    /* 9.3.3 Representing Relationships */
    function get_rels_path(file) {
        var n = file.lastIndexOf("/");
        return file.slice(0, n + 1) + '_rels/' + file.slice(n + 1) + ".rels";
    }
    function parse_rels(data, currentFilePath) {
        if (!data) return data;
        if (currentFilePath.charAt(0) !== '/') {
            currentFilePath = '/' + currentFilePath;
        }
        var rels = {};
        var hash = {};
        (data.match(tagregex) || []).forEach(function (x) {
            var y = parsexmltag(x);
            /* 9.3.2.2 OPC_Relationships */
            if (y[0] === ' 2) {
            o[o.length] = ('');
            o[1] = o[1].replace("/>", ">");
        }
        return o.join("");
    }
    function add_rels(rels, rId, f, type, relobj) {
        if (!relobj) relobj = {};
        if (!rels['!id']) rels['!id'] = {};
        if (rId < 0)
            for (rId = 1; rels['!id']['rId' + rId]; ++rId) { /* empty */ }
        relobj.Id = 'rId' + rId;
        relobj.Type = type;
        relobj.Target = f;
        if (relobj.Type == RELS.HLINK) relobj.TargetMode = "External";
        if (rels['!id'][relobj.Id]) throw new Error("Cannot rewrite rId " + rId);
        rels['!id'][relobj.Id] = relobj;
        rels[('/' + relobj.Target).replace("//", "/")] = relobj;
        return rId;
    }
    /* Open Document Format for Office Applications (OpenDocument) Version 1.2 */
    /* Part 3 Section 4 Manifest File */
    var CT_ODS = "application/vnd.oasis.opendocument.spreadsheet";
    function parse_manifest(d, opts) {
        var str = xlml_normalize(d);
        var Rn;
        var FEtag;
        while ((Rn = xlmlregex.exec(str))) switch (Rn[3]) {
            case 'manifest':
                break; // 4.2 
            case 'file-entry': // 4.3 
                FEtag = parsexmltag(Rn[0], false);
                if (FEtag.path == '/' && FEtag.type !== CT_ODS) throw new Error("This OpenDocument is not a spreadsheet");
                break;
            case 'encryption-data': // 4.4 
            case 'algorithm': // 4.5 
            case 'start-key-generation': // 4.6 
            case 'key-derivation': // 4.7 
                throw new Error("Unsupported ODS Encryption");
            default:
                if (opts && opts.WTF) throw Rn;
        }
    }
    function write_manifest(manifest) {
        var o = [XML_HEADER];
        o.push('\n');
        o.push('  \n');
        for (var i = 0; i < manifest.length; ++i) o.push('  \n');
        o.push('');
        return o.join("");
    }
    /* Part 3 Section 6 Metadata Manifest File */
    function write_rdf_type(file, res, tag) {
        return [
            '  \n',
            '    \n',
            '  \n'
        ].join("");
    }
    function write_rdf_has(base, file) {
        return [
            '  \n',
            '    \n',
            '  \n'
        ].join("");
    }
    function write_rdf(rdf) {
        var o = [XML_HEADER];
        o.push('\n');
        for (var i = 0; i != rdf.length; ++i) {
            o.push(write_rdf_type(rdf[i][0], rdf[i][1]));
            o.push(write_rdf_has("", rdf[i][0]));
        }
        o.push(write_rdf_type("", "Document", "pkg"));
        o.push('');
        return o.join("");
    }
    /* TODO: pull properties */
    var write_meta_ods = (function () {
        var payload = 'Sheet' + 'JS ' + XLSX.version + '';
        return function wmo() {
            return payload;
        };
    })();
    /* ECMA-376 Part II 11.1 Core Properties Part */
    /* [MS-OSHARED] 2.3.3.2.[1-2].1 (PIDSI/PIDDSI) */
    var CORE_PROPS = [
        ["cp:category", "Category"],
        ["cp:contentStatus", "ContentStatus"],
        ["cp:keywords", "Keywords"],
        ["cp:lastModifiedBy", "LastAuthor"],
        ["cp:lastPrinted", "LastPrinted"],
        ["cp:revision", "RevNumber"],
        ["cp:version", "Version"],
        ["dc:creator", "Author"],
        ["dc:description", "Comments"],
        ["dc:identifier", "Identifier"],
        ["dc:language", "Language"],
        ["dc:subject", "Subject"],
        ["dc:title", "Title"],
        ["dcterms:created", "CreatedDate", 'date'],
        ["dcterms:modified", "ModifiedDate", 'date']
    ];
    XMLNS.CORE_PROPS = "http://schemas.openxmlformats.org/package/2006/metadata/core-properties";
    RELS.CORE_PROPS = 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties';
    var CORE_PROPS_REGEX = (function () {
        var r = new Array(CORE_PROPS.length);
        for (var i = 0; i < CORE_PROPS.length; ++i) {
            var f = CORE_PROPS[i];
            var g = "(?:" + f[0].slice(0, f[0].indexOf(":")) + ":)" + f[0].slice(f[0].indexOf(":") + 1);
            r[i] = new RegExp("<" + g + "[^>]*>([\\s\\S]*?)<\/" + g + ">");
        }
        return r;
    })();
    function parse_core_props(data) {
        var p = {};
        data = utf8read(data);
        for (var i = 0; i < CORE_PROPS.length; ++i) {
            var f = CORE_PROPS[i],
                cur = data.match(CORE_PROPS_REGEX[i]);
            if (cur != null && cur.length > 0) p[f[1]] = cur[1];
            if (f[2] === 'date' && p[f[1]]) p[f[1]] = parseDate(p[f[1]]);
        }
        return p;
    }
    var CORE_PROPS_XML_ROOT = writextag('cp:coreProperties', null, {
        //'xmlns': XMLNS.CORE_PROPS,
        'xmlns:cp': XMLNS.CORE_PROPS,
        'xmlns:dc': XMLNS.dc,
        'xmlns:dcterms': XMLNS.dcterms,
        'xmlns:dcmitype': XMLNS.dcmitype,
        'xmlns:xsi': XMLNS.xsi
    });
    function cp_doit(f, g, h, o, p) {
        if (p[f] != null || g == null || g === "") return;
        p[f] = g;
        o[o.length] = (h ? writextag(f, g, h) : writetag(f, g));
    }
    function write_core_props(cp, _opts) {
        var opts = _opts || {};
        var o = [XML_HEADER, CORE_PROPS_XML_ROOT],
            p = {};
        if (!cp && !opts.Props) return o.join("");
        if (cp) {
            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);
            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);
        }
        for (var i = 0; i != CORE_PROPS.length; ++i) {
            var f = CORE_PROPS[i];
            var v = opts.Props && opts.Props[f[1]] != null ? opts.Props[f[1]] : cp ? cp[f[1]] : null;
            if (v === true) v = "1";
            else if (v === false) v = "0";
            else if (typeof v == "number") v = String(v);
            if (v != null) cp_doit(f[0], v, null, o, p);
        }
        if (o.length > 2) {
            o[o.length] = ('');
            o[1] = o[1].replace("/>", ">");
        }
        return o.join("");
    }
    /* 15.2.12.3 Extended File Properties Part */
    /* [MS-OSHARED] 2.3.3.2.[1-2].1 (PIDSI/PIDDSI) */
    var EXT_PROPS = [
        ["Application", "Application", "string"],
        ["AppVersion", "AppVersion", "string"],
        ["Company", "Company", "string"],
        ["DocSecurity", "DocSecurity", "string"],
        ["Manager", "Manager", "string"],
        ["HyperlinksChanged", "HyperlinksChanged", "bool"],
        ["SharedDoc", "SharedDoc", "bool"],
        ["LinksUpToDate", "LinksUpToDate", "bool"],
        ["ScaleCrop", "ScaleCrop", "bool"],
        ["HeadingPairs", "HeadingPairs", "raw"],
        ["TitlesOfParts", "TitlesOfParts", "raw"]
    ];
    XMLNS.EXT_PROPS = "http://schemas.openxmlformats.org/officeDocument/2006/extended-properties";
    RELS.EXT_PROPS = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties';
    var PseudoPropsPairs = [
        "Worksheets", "SheetNames",
        "NamedRanges", "DefinedNames",
        "Chartsheets", "ChartNames"
    ];
    function load_props_pairs(HP, TOP, props, opts) {
        var v = [];
        if (typeof HP == "string") v = parseVector(HP, opts);
        else
            for (var j = 0; j < HP.length; ++j) v = v.concat(HP[j].map(function (hp) {
                return {
                    v: hp
                };
            }));
        var parts = (typeof TOP == "string") ? parseVector(TOP, opts).map(function (x) {
            return x.v;
        }) : TOP;
        var idx = 0,
            len = 0;
        if (parts.length > 0)
            for (var i = 0; i !== v.length; i += 2) {
                len = +(v[i + 1].v);
                switch (v[i].v) {
                    case "Worksheets":
                    case "工作表":
                    case "Листы":
                    case "أوراق العمل":
                    case "ワークシート":
                    case "גליונות עבודה":
                    case "Arbeitsblätter":
                    case "Çalışma Sayfaları":
                    case "Feuilles de calcul":
                    case "Fogli di lavoro":
                    case "Folhas de cálculo":
                    case "Planilhas":
                    case "Regneark":
                    case "Werkbladen":
                        props.Worksheets = len;
                        props.SheetNames = parts.slice(idx, idx + len);
                        break;
                    case "Named Ranges":
                    case "名前付き一覧":
                    case "Benannte Bereiche":
                    case "Navngivne områder":
                        props.NamedRanges = len;
                        props.DefinedNames = parts.slice(idx, idx + len);
                        break;
                    case "Charts":
                    case "Diagramme":
                        props.Chartsheets = len;
                        props.ChartNames = parts.slice(idx, idx + len);
                        break;
                }
                idx += len;
            }
    }
    function parse_ext_props(data, p, opts) {
        var q = {};
        if (!p) p = {};
        data = utf8read(data);
        EXT_PROPS.forEach(function (f) {
            switch (f[2]) {
                case "string":
                    p[f[1]] = (data.match(matchtag(f[0])) || [])[1];
                    break;
                case "bool":
                    p[f[1]] = (data.match(matchtag(f[0])) || [])[1] === "true";
                    break;
                case "raw":
                    var cur = data.match(new RegExp("<" + f[0] + "[^>]*>([\\s\\S]*?)<\/" + f[0] + ">"));
                    if (cur && cur.length > 0) q[f[1]] = cur[1];
                    break;
            }
        });
        if (q.HeadingPairs && q.TitlesOfParts) load_props_pairs(q.HeadingPairs, q.TitlesOfParts, p, opts);
        return p;
    }
    var EXT_PROPS_XML_ROOT = writextag('Properties', null, {
        'xmlns': XMLNS.EXT_PROPS,
        'xmlns:vt': XMLNS.vt
    });
    function write_ext_props(cp) {
        var o = [],
            W = writextag;
        if (!cp) cp = {};
        cp.Application = "SheetJS";
        o[o.length] = (XML_HEADER);
        o[o.length] = (EXT_PROPS_XML_ROOT);
        EXT_PROPS.forEach(function (f) {
            if (cp[f[1]] === undefined) return;
            var v;
            switch (f[2]) {
                case 'string':
                    v = String(cp[f[1]]);
                    break;
                case 'bool':
                    v = cp[f[1]] ? 'true' : 'false';
                    break;
            }
            if (v !== undefined) o[o.length] = (W(f[0], v));
        });
        /* TODO: HeadingPairs, TitlesOfParts */
        o[o.length] = (W('HeadingPairs', W('vt:vector', W('vt:variant', 'Worksheets') + W('vt:variant', W('vt:i4', String(cp.Worksheets))), {
            size: 2,
            baseType: "variant"
        })));
        o[o.length] = (W('TitlesOfParts', W('vt:vector', cp.SheetNames.map(function (s) {
            return "" + escapexml(s) + "";
        }).join(""), {
            size: cp.Worksheets,
            baseType: "lpstr"
        })));
        if (o.length > 2) {
            o[o.length] = ('');
            o[1] = o[1].replace("/>", ">");
        }
        return o.join("");
    }
    /* 15.2.12.2 Custom File Properties Part */
    XMLNS.CUST_PROPS = "http://schemas.openxmlformats.org/officeDocument/2006/custom-properties";
    RELS.CUST_PROPS = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties';
    var custregex = /<[^>]+>[^<]*/g;
    function parse_cust_props(data, opts) {
        var p = {},
            name = "";
        var m = data.match(custregex);
        if (m)
            for (var i = 0; i != m.length; ++i) {
                var x = m[i],
                    y = parsexmltag(x);
                switch (y[0]) {
                    case '':
                        name = null;
                        break;
                    default:
                        if (x.indexOf('');
                            var type = toks[0].slice(4),
                                text = toks[1];
                            /* 22.4.2.32 (CT_Variant). Omit the binary types from 22.4 (Variant Types) */
                            switch (type) {
                                case 'lpstr':
                                case 'bstr':
                                case 'lpwstr':
                                    p[name] = unescapexml(text);
                                    break;
                                case 'bool':
                                    p[name] = parsexmlbool(text);
                                    break;
                                case 'i1':
                                case 'i2':
                                case 'i4':
                                case 'i8':
                                case 'int':
                                case 'uint':
                                    p[name] = parseInt(text, 10);
                                    break;
                                case 'r4':
                                case 'r8':
                                case 'decimal':
                                    p[name] = parseFloat(text);
                                    break;
                                case 'filetime':
                                case 'date':
                                    p[name] = parseDate(text);
                                    break;
                                case 'cy':
                                case 'error':
                                    p[name] = unescapexml(text);
                                    break;
                                default:
                                    if (type.slice(-1) == '/') break;
                                    if (opts.WTF && typeof console !== 'undefined') console.warn('Unexpected', x, type, toks);
                            }
                        } else if (x.slice(0, 2) === "") { /* empty */ } else if (opts.WTF) throw new Error(x);
                }
            }
        return p;
    }
    var CUST_PROPS_XML_ROOT = writextag('Properties', null, {
        'xmlns': XMLNS.CUST_PROPS,
        'xmlns:vt': XMLNS.vt
    });
    function write_cust_props(cp) {
        var o = [XML_HEADER, CUST_PROPS_XML_ROOT];
        if (!cp) return o.join("");
        var pid = 1;
        keys(cp).forEach(function custprop(k) {
            ++pid;
            o[o.length] = (writextag('property', write_vt(cp[k]), {
                'fmtid': '{D5CDD505-2E9C-101B-9397-08002B2CF9AE}',
                'pid': pid,
                'name': k
            }));
        });
        if (o.length > 2) {
            o[o.length] = '';
            o[1] = o[1].replace("/>", ">");
        }
        return o.join("");
    }
    /* Common Name -> XLML Name */
    var XLMLDocPropsMap = {
        Title: 'Title',
        Subject: 'Subject',
        Author: 'Author',
        Keywords: 'Keywords',
        Comments: 'Description',
        LastAuthor: 'LastAuthor',
        RevNumber: 'Revision',
        Application: 'AppName',
        /* TotalTime: 'TotalTime', */
        LastPrinted: 'LastPrinted',
        CreatedDate: 'Created',
        ModifiedDate: 'LastSaved',
        /* Pages */
        /* Words */
        /* Characters */
        Category: 'Category',
        /* PresentationFormat */
        Manager: 'Manager',
        Company: 'Company',
        /* Guid */
        /* HyperlinkBase */
        /* Bytes */
        /* Lines */
        /* Paragraphs */
        /* CharactersWithSpaces */
        AppVersion: 'Version',
        ContentStatus: 'ContentStatus',
        /* NOTE: missing from schema */
        Identifier: 'Identifier',
        /* NOTE: missing from schema */
        Language: 'Language' /* NOTE: missing from schema */
    };
    var evert_XLMLDPM = evert(XLMLDocPropsMap);
    function xlml_set_prop(Props, tag, val) {
        tag = evert_XLMLDPM[tag] || tag;
        Props[tag] = val;
    }
    function xlml_write_docprops(Props, opts) {
        var o = [];
        keys(XLMLDocPropsMap).map(function (m) {
            for (var i = 0; i < CORE_PROPS.length; ++i)
                if (CORE_PROPS[i][1] == m) return CORE_PROPS[i];
            for (i = 0; i < EXT_PROPS.length; ++i)
                if (EXT_PROPS[i][1] == m) return EXT_PROPS[i];
            throw m;
        }).forEach(function (p) {
            if (Props[p[1]] == null) return;
            var m = opts && opts.Props && opts.Props[p[1]] != null ? opts.Props[p[1]] : Props[p[1]];
            switch (p[2]) {
                case 'date':
                    m = new Date(m).toISOString().replace(/\.\d*Z/, "Z");
                    break;
            }
            if (typeof m == 'number') m = String(m);
            else if (m === true || m === false) {
                m = m ? "1" : "0";
            } else if (m instanceof Date) m = new Date(m).toISOString().replace(/\.\d*Z/, "");
            o.push(writetag(XLMLDocPropsMap[p[1]] || p[1], m));
        });
        return writextag('DocumentProperties', o.join(""), {
            xmlns: XLMLNS.o
        });
    }
    function xlml_write_custprops(Props, Custprops) {
        var BLACKLIST = ["Worksheets", "SheetNames"];
        var T = 'CustomDocumentProperties';
        var o = [];
        if (Props) keys(Props).forEach(function (k) {
            if (!Props.hasOwnProperty(k)) return;
            for (var i = 0; i < CORE_PROPS.length; ++i)
                if (k == CORE_PROPS[i][1]) return;
            for (i = 0; i < EXT_PROPS.length; ++i)
                if (k == EXT_PROPS[i][1]) return;
            for (i = 0; i < BLACKLIST.length; ++i)
                if (k == BLACKLIST[i]) return;
            var m = Props[k];
            var t = "string";
            if (typeof m == 'number') {
                t = "float";
                m = String(m);
            } else if (m === true || m === false) {
                t = "boolean";
                m = m ? "1" : "0";
            } else m = String(m);
            o.push(writextag(escapexmltag(k), m, {
                "dt:dt": t
            }));
        });
        if (Custprops) keys(Custprops).forEach(function (k) {
            if (!Custprops.hasOwnProperty(k)) return;
            if (Props && Props.hasOwnProperty(k)) return;
            var m = Custprops[k];
            var t = "string";
            if (typeof m == 'number') {
                t = "float";
                m = String(m);
            } else if (m === true || m === false) {
                t = "boolean";
                m = m ? "1" : "0";
            } else if (m instanceof Date) {
                t = "dateTime.tz";
                m = m.toISOString();
            } else m = String(m);
            o.push(writextag(escapexmltag(k), m, {
                "dt:dt": t
            }));
        });
        return '<' + T + ' xmlns="' + XLMLNS.o + '">' + o.join("") + '' + T + '>';
    }
    /* [MS-DTYP] 2.3.3 FILETIME */
    /* [MS-OLEDS] 2.1.3 FILETIME (Packet Version) */
    /* [MS-OLEPS] 2.8 FILETIME (Packet Version) */
    function parse_FILETIME(blob) {
        var dwLowDateTime = blob.read_shift(4),
            dwHighDateTime = blob.read_shift(4);
        return new Date(((dwHighDateTime / 1e7 * Math.pow(2, 32) + dwLowDateTime / 1e7) - 11644473600) * 1000).toISOString().replace(/\.000/, "");
    }
    function write_FILETIME(time) {
        var date = (typeof time == "string") ? new Date(Date.parse(time)) : time;
        var t = date.getTime() / 1000 + 11644473600;
        var l = t % Math.pow(2, 32),
            h = (t - l) / Math.pow(2, 32);
        l *= 1e7;
        h *= 1e7;
        var w = (l / Math.pow(2, 32)) | 0;
        if (w > 0) {
            l = l % Math.pow(2, 32);
            h += w;
        }
        var o = new_buf(8);
        o.write_shift(4, l);
        o.write_shift(4, h);
        return o;
    }
    /* [MS-OSHARED] 2.3.3.1.4 Lpstr */
    function parse_lpstr(blob, type, pad) {
        var start = blob.l;
        var str = blob.read_shift(0, 'lpstr-cp');
        if (pad)
            while ((blob.l - start) & 3) ++blob.l;
        return str;
    }
    /* [MS-OSHARED] 2.3.3.1.6 Lpwstr */
    function parse_lpwstr(blob, type, pad) {
        var str = blob.read_shift(0, 'lpwstr');
        if (pad) blob.l += (4 - ((str.length + 1) & 3)) & 3;
        return str;
    }
    /* [MS-OSHARED] 2.3.3.1.11 VtString */
    /* [MS-OSHARED] 2.3.3.1.12 VtUnalignedString */
    function parse_VtStringBase(blob, stringType, pad) {
        if (stringType === 0x1F /*VT_LPWSTR*/ ) return parse_lpwstr(blob);
        return parse_lpstr(blob, stringType, pad);
    }
    function parse_VtString(blob, t, pad) {
        return parse_VtStringBase(blob, t, pad === false ? 0 : 4);
    }
    function parse_VtUnalignedString(blob, t) {
        if (!t) throw new Error("VtUnalignedString must have positive length");
        return parse_VtStringBase(blob, t, 0);
    }
    /* [MS-OSHARED] 2.3.3.1.9 VtVecUnalignedLpstrValue */
    function parse_VtVecUnalignedLpstrValue(blob) {
        var length = blob.read_shift(4);
        var ret = [];
        for (var i = 0; i != length; ++i) ret[i] = blob.read_shift(0, 'lpstr-cp').replace(chr0, '');
        return ret;
    }
    /* [MS-OSHARED] 2.3.3.1.10 VtVecUnalignedLpstr */
    function parse_VtVecUnalignedLpstr(blob) {
        return parse_VtVecUnalignedLpstrValue(blob);
    }
    /* [MS-OSHARED] 2.3.3.1.13 VtHeadingPair */
    function parse_VtHeadingPair(blob) {
        var headingString = parse_TypedPropertyValue(blob, VT_USTR);
        var headerParts = parse_TypedPropertyValue(blob, VT_I4);
        return [headingString, headerParts];
    }
    /* [MS-OSHARED] 2.3.3.1.14 VtVecHeadingPairValue */
    function parse_VtVecHeadingPairValue(blob) {
        var cElements = blob.read_shift(4);
        var out = [];
        for (var i = 0; i != cElements / 2; ++i) out.push(parse_VtHeadingPair(blob));
        return out;
    }
    /* [MS-OSHARED] 2.3.3.1.15 VtVecHeadingPair */
    function parse_VtVecHeadingPair(blob) {
        // NOTE: When invoked, wType & padding were already consumed
        return parse_VtVecHeadingPairValue(blob);
    }
    /* [MS-OLEPS] 2.18.1 Dictionary (uses 2.17, 2.16) */
    function parse_dictionary(blob, CodePage) {
        var cnt = blob.read_shift(4);
        var dict = ({});
        for (var j = 0; j != cnt; ++j) {
            var pid = blob.read_shift(4);
            var len = blob.read_shift(4);
            dict[pid] = blob.read_shift(len, (CodePage === 0x4B0 ? 'utf16le' : 'utf8')).replace(chr0, '').replace(chr1, '!');
            if (CodePage === 0x4B0 && (len % 2)) blob.l += 2;
        }
        if (blob.l & 3) blob.l = (blob.l >> 2 + 1) << 2;
        return dict;
    }
    /* [MS-OLEPS] 2.9 BLOB */
    function parse_BLOB(blob) {
        var size = blob.read_shift(4);
        var bytes = blob.slice(blob.l, blob.l + size);
        blob.l += size;
        if ((size & 3) > 0) blob.l += (4 - (size & 3)) & 3;
        return bytes;
    }
    /* [MS-OLEPS] 2.11 ClipboardData */
    function parse_ClipboardData(blob) {
        // TODO
        var o = {};
        o.Size = blob.read_shift(4);
        //o.Format = blob.read_shift(4);
        blob.l += o.Size + 3 - (o.Size - 1) % 4;
        return o;
    }
    /* [MS-OLEPS] 2.15 TypedPropertyValue */
    function parse_TypedPropertyValue(blob, type, _opts) {
        var t = blob.read_shift(2),
            ret, opts = _opts || {};
        blob.l += 2;
        if (type !== VT_VARIANT)
            if (t !== type && VT_CUSTOM.indexOf(type) === -1) throw new Error('Expected type ' + type + ' saw ' + t);
        switch (type === VT_VARIANT ? t : type) {
            case 0x02 /*VT_I2*/ :
                ret = blob.read_shift(2, 'i');
                if (!opts.raw) blob.l += 2;
                return ret;
            case 0x03 /*VT_I4*/ :
                ret = blob.read_shift(4, 'i');
                return ret;
            case 0x0B /*VT_BOOL*/ :
                return blob.read_shift(4) !== 0x0;
            case 0x13 /*VT_UI4*/ :
                ret = blob.read_shift(4);
                return ret;
            case 0x1E /*VT_LPSTR*/ :
                return parse_lpstr(blob, t, 4).replace(chr0, '');
            case 0x1F /*VT_LPWSTR*/ :
                return parse_lpwstr(blob);
            case 0x40 /*VT_FILETIME*/ :
                return parse_FILETIME(blob);
            case 0x41 /*VT_BLOB*/ :
                return parse_BLOB(blob);
            case 0x47 /*VT_CF*/ :
                return parse_ClipboardData(blob);
            case 0x50 /*VT_STRING*/ :
                return parse_VtString(blob, t, !opts.raw).replace(chr0, '');
            case 0x51 /*VT_USTR*/ :
                return parse_VtUnalignedString(blob, t /*, 4*/ ).replace(chr0, '');
            case 0x100C /*VT_VECTOR|VT_VARIANT*/ :
                return parse_VtVecHeadingPair(blob);
            case 0x101E /*VT_LPSTR*/ :
                return parse_VtVecUnalignedLpstr(blob);
            default:
                throw new Error("TypedPropertyValue unrecognized type " + type + " " + t);
        }
    }
    function write_TypedPropertyValue(type, value) {
        var o = new_buf(4),
            p = new_buf(4);
        o.write_shift(4, type == 0x50 ? 0x1F : type);
        switch (type) {
            case 0x03 /*VT_I4*/ :
                p.write_shift(-4, value);
                break;
            case 0x05 /*VT_I4*/ :
                p = new_buf(8);
                p.write_shift(8, value, 'f');
                break;
            case 0x0B /*VT_BOOL*/ :
                p.write_shift(4, value ? 0x01 : 0x00);
                break;
            case 0x40 /*VT_FILETIME*/ :
                p = write_FILETIME(value);
                break;
            case 0x1F /*VT_LPWSTR*/ :
            case 0x50 /*VT_STRING*/ :
                p = new_buf(4 + 2 * (value.length + 1) + (value.length % 2 ? 0 : 2));
                p.write_shift(4, value.length + 1);
                p.write_shift(0, value, "dbcs");
                while (p.l != p.length) p.write_shift(1, 0);
                break;
            default:
                throw new Error("TypedPropertyValue unrecognized type " + type + " " + value);
        }
        return bconcat([o, p]);
    }
    /* [MS-OLEPS] 2.20 PropertySet */
    function parse_PropertySet(blob, PIDSI) {
        var start_addr = blob.l;
        var size = blob.read_shift(4);
        var NumProps = blob.read_shift(4);
        var Props = [],
            i = 0;
        var CodePage = 0;
        var Dictionary = -1,
            DictObj = ({});
        for (i = 0; i != NumProps; ++i) {
            var PropID = blob.read_shift(4);
            var Offset = blob.read_shift(4);
            Props[i] = [PropID, Offset + start_addr];
        }
        Props.sort(function (x, y) {
            return x[1] - y[1];
        });
        var PropH = {};
        for (i = 0; i != NumProps; ++i) {
            if (blob.l !== Props[i][1]) {
                var fail = true;
                if (i > 0 && PIDSI) switch (PIDSI[Props[i - 1][0]].t) {
                    case 0x02 /*VT_I2*/ :
                        if (blob.l + 2 === Props[i][1]) {
                            blob.l += 2;
                            fail = false;
                        }
                        break;
                    case 0x50 /*VT_STRING*/ :
                        if (blob.l <= Props[i][1]) {
                            blob.l = Props[i][1];
                            fail = false;
                        }
                        break;
                    case 0x100C /*VT_VECTOR|VT_VARIANT*/ :
                        if (blob.l <= Props[i][1]) {
                            blob.l = Props[i][1];
                            fail = false;
                        }
                        break;
                }
                if ((!PIDSI || i == 0) && blob.l <= Props[i][1]) {
                    fail = false;
                    blob.l = Props[i][1];
                }
                if (fail) throw new Error("Read Error: Expected address " + Props[i][1] + ' at ' + blob.l + ' :' + i);
            }
            if (PIDSI) {
                var piddsi = PIDSI[Props[i][0]];
                PropH[piddsi.n] = parse_TypedPropertyValue(blob, piddsi.t, {
                    raw: true
                });
                if (piddsi.p === 'version') PropH[piddsi.n] = String(PropH[piddsi.n] >> 16) + "." + ("0000" + String(PropH[piddsi.n] & 0xFFFF)).slice(-4);
                if (piddsi.n == "CodePage") switch (PropH[piddsi.n]) {
                    case 0:
                        PropH[piddsi.n] = 1252;
                        /* falls through */
                    case 874:
                    case 932:
                    case 936:
                    case 949:
                    case 950:
                    case 1250:
                    case 1251:
                    case 1253:
                    case 1254:
                    case 1255:
                    case 1256:
                    case 1257:
                    case 1258:
                    case 10000:
                    case 1200:
                    case 1201:
                    case 1252:
                    case 65000:
                    case -536:
                    case 65001:
                    case -535:
                        set_cp(CodePage = (PropH[piddsi.n] >>> 0) & 0xFFFF);
                        break;
                    default:
                        throw new Error("Unsupported CodePage: " + PropH[piddsi.n]);
                }
            } else {
                if (Props[i][0] === 0x1) {
                    CodePage = PropH.CodePage = (parse_TypedPropertyValue(blob, VT_I2));
                    set_cp(CodePage);
                    if (Dictionary !== -1) {
                        var oldpos = blob.l;
                        blob.l = Props[Dictionary][1];
                        DictObj = parse_dictionary(blob, CodePage);
                        blob.l = oldpos;
                    }
                } else if (Props[i][0] === 0) {
                    if (CodePage === 0) {
                        Dictionary = i;
                        blob.l = Props[i + 1][1];
                        continue;
                    }
                    DictObj = parse_dictionary(blob, CodePage);
                } else {
                    var name = DictObj[Props[i][0]];
                    var val;
                    /* [MS-OSHARED] 2.3.3.2.3.1.2 + PROPVARIANT */
                    switch (blob[blob.l]) {
                        case 0x41 /*VT_BLOB*/ :
                            blob.l += 4;
                            val = parse_BLOB(blob);
                            break;
                        case 0x1E /*VT_LPSTR*/ :
                            blob.l += 4;
                            val = parse_VtString(blob, blob[blob.l - 4]).replace(/\u0000+$/, "");
                            break;
                        case 0x1F /*VT_LPWSTR*/ :
                            blob.l += 4;
                            val = parse_VtString(blob, blob[blob.l - 4]).replace(/\u0000+$/, "");
                            break;
                        case 0x03 /*VT_I4*/ :
                            blob.l += 4;
                            val = blob.read_shift(4, 'i');
                            break;
                        case 0x13 /*VT_UI4*/ :
                            blob.l += 4;
                            val = blob.read_shift(4);
                            break;
                        case 0x05 /*VT_R8*/ :
                            blob.l += 4;
                            val = blob.read_shift(8, 'f');
                            break;
                        case 0x0B /*VT_BOOL*/ :
                            blob.l += 4;
                            val = parsebool(blob, 4);
                            break;
                        case 0x40 /*VT_FILETIME*/ :
                            blob.l += 4;
                            val = parseDate(parse_FILETIME(blob));
                            break;
                        default:
                            throw new Error("unparsed value: " + blob[blob.l]);
                    }
                    PropH[name] = val;
                }
            }
        }
        blob.l = start_addr + size; /* step ahead to skip padding */
        return PropH;
    }
    var XLSPSSkip = ["CodePage", "Thumbnail", "_PID_LINKBASE", "_PID_HLINKS", "SystemIdentifier", "FMTID"].concat(PseudoPropsPairs);
    function guess_property_type(val) {
        switch (typeof val) {
            case "boolean":
                return 0x0B;
            case "number":
                return ((val | 0) == val) ? 0x03 : 0x05;
            case "string":
                return 0x1F;
            case "object":
                if (val instanceof Date) return 0x40;
                break;
        }
        return -1;
    }
    function write_PropertySet(entries, RE, PIDSI) {
        var hdr = new_buf(8),
            piao = [],
            prop = [];
        var sz = 8,
            i = 0;
        var pr = new_buf(8),
            pio = new_buf(8);
        pr.write_shift(4, 0x0002);
        pr.write_shift(4, 0x04B0);
        pio.write_shift(4, 0x0001);
        prop.push(pr);
        piao.push(pio);
        sz += 8 + pr.length;
        if (!RE) {
            pio = new_buf(8);
            pio.write_shift(4, 0);
            piao.unshift(pio);
            var bufs = [new_buf(4)];
            bufs[0].write_shift(4, entries.length);
            for (i = 0; i < entries.length; ++i) {
                var value = entries[i][0];
                pr = new_buf(4 + 4 + 2 * (value.length + 1) + (value.length % 2 ? 0 : 2));
                pr.write_shift(4, i + 2);
                pr.write_shift(4, value.length + 1);
                pr.write_shift(0, value, "dbcs");
                while (pr.l != pr.length) pr.write_shift(1, 0);
                bufs.push(pr);
            }
            pr = bconcat(bufs);
            prop.unshift(pr);
            sz += 8 + pr.length;
        }
        for (i = 0; i < entries.length; ++i) {
            if (RE && !RE[entries[i][0]]) continue;
            if (XLSPSSkip.indexOf(entries[i][0]) > -1) continue;
            if (entries[i][1] == null) continue;
            var val = entries[i][1],
                idx = 0;
            if (RE) {
                idx = +RE[entries[i][0]];
                var pinfo = (PIDSI)[idx];
                if (pinfo.p == "version" && typeof val == "string") {
                    var arr = val.split(".");
                    val = ((+arr[0]) << 16) + ((+arr[1]) || 0);
                }
                pr = write_TypedPropertyValue(pinfo.t, val);
            } else {
                var T = guess_property_type(val);
                if (T == -1) {
                    T = 0x1F;
                    val = String(val);
                }
                pr = write_TypedPropertyValue(T, val);
            }
            prop.push(pr);
            pio = new_buf(8);
            pio.write_shift(4, !RE ? 2 + i : idx);
            piao.push(pio);
            sz += 8 + pr.length;
        }
        var w = 8 * (prop.length + 1);
        for (i = 0; i < prop.length; ++i) {
            piao[i].write_shift(4, w);
            w += prop[i].length;
        }
        hdr.write_shift(4, sz);
        hdr.write_shift(4, prop.length);
        return bconcat([hdr].concat(piao).concat(prop));
    }
    /* [MS-OLEPS] 2.21 PropertySetStream */
    function parse_PropertySetStream(file, PIDSI, clsid) {
        var blob = file.content;
        if (!blob) return ({});
        prep_blob(blob, 0);
        var NumSets, FMTID0, FMTID1, Offset0, Offset1 = 0;
        blob.chk('feff', 'Byte Order: ');
        /*var vers = */
        blob.read_shift(2); // TODO: check version
        var SystemIdentifier = blob.read_shift(4);
        var CLSID = blob.read_shift(16);
        if (CLSID !== CFB.utils.consts.HEADER_CLSID && CLSID !== clsid) throw new Error("Bad PropertySet CLSID " + CLSID);
        NumSets = blob.read_shift(4);
        if (NumSets !== 1 && NumSets !== 2) throw new Error("Unrecognized #Sets: " + NumSets);
        FMTID0 = blob.read_shift(16);
        Offset0 = blob.read_shift(4);
        if (NumSets === 1 && Offset0 !== blob.l) throw new Error("Length mismatch: " + Offset0 + " !== " + blob.l);
        else if (NumSets === 2) {
            FMTID1 = blob.read_shift(16);
            Offset1 = blob.read_shift(4);
        }
        var PSet0 = parse_PropertySet(blob, PIDSI);
        var rval = ({
            SystemIdentifier: SystemIdentifier
        });
        for (var y in PSet0) rval[y] = PSet0[y];
        //rval.blob = blob;
        rval.FMTID = FMTID0;
        //rval.PSet0 = PSet0;
        if (NumSets === 1) return rval;
        if (Offset1 - blob.l == 2) blob.l += 2;
        if (blob.l !== Offset1) throw new Error("Length mismatch 2: " + blob.l + " !== " + Offset1);
        var PSet1;
        try {
            PSet1 = parse_PropertySet(blob, null);
        } catch (e) { /* empty */ }
        for (y in PSet1) rval[y] = PSet1[y];
        rval.FMTID = [FMTID0, FMTID1]; // TODO: verify FMTID0/1
        return rval;
    }
    function write_PropertySetStream(entries, clsid, RE, PIDSI, entries2, clsid2) {
        var hdr = new_buf(entries2 ? 68 : 48);
        var bufs = [hdr];
        hdr.write_shift(2, 0xFFFE);
        hdr.write_shift(2, 0x0000); /* TODO: type 1 props */
        hdr.write_shift(4, 0x32363237);
        hdr.write_shift(16, CFB.utils.consts.HEADER_CLSID, "hex");
        hdr.write_shift(4, (entries2 ? 2 : 1));
        hdr.write_shift(16, clsid, "hex");
        hdr.write_shift(4, (entries2 ? 68 : 48));
        var ps0 = write_PropertySet(entries, RE, PIDSI);
        bufs.push(ps0);
        if (entries2) {
            var ps1 = write_PropertySet(entries2, null, null);
            hdr.write_shift(16, clsid2, "hex");
            hdr.write_shift(4, 68 + ps0.length);
            bufs.push(ps1);
        }
        return bconcat(bufs);
    }
    function parsenoop2(blob, length) {
        blob.read_shift(length);
        return null;
    }
    function writezeroes(n, o) {
        if (!o) o = new_buf(n);
        for (var j = 0; j < n; ++j) o.write_shift(1, 0);
        return o;
    }
    function parslurp(blob, length, cb) {
        var arr = [],
            target = blob.l + length;
        while (blob.l < target) arr.push(cb(blob, target - blob.l));
        if (target !== blob.l) throw new Error("Slurp error");
        return arr;
    }
    function parsebool(blob, length) {
        return blob.read_shift(length) === 0x1;
    }
    function writebool(v, o) {
        if (!o) o = new_buf(2);
        o.write_shift(2, +!!v);
        return o;
    }
    function parseuint16(blob) {
        return blob.read_shift(2, 'u');
    }
    function writeuint16(v, o) {
        if (!o) o = new_buf(2);
        o.write_shift(2, v);
        return o;
    }
    function parseuint16a(blob, length) {
        return parslurp(blob, length, parseuint16);
    }
    /* --- 2.5 Structures --- */
    /* [MS-XLS] 2.5.10 Bes (boolean or error) */
    function parse_Bes(blob) {
        var v = blob.read_shift(1),
            t = blob.read_shift(1);
        return t === 0x01 ? v : v === 0x01;
    }
    function write_Bes(v, t, o) {
        if (!o) o = new_buf(2);
        o.write_shift(1, +v);
        o.write_shift(1, ((t == 'e') ? 1 : 0));
        return o;
    }
    /* [MS-XLS] 2.5.240 ShortXLUnicodeString */
    function parse_ShortXLUnicodeString(blob, length, opts) {
        var cch = blob.read_shift(opts && opts.biff >= 12 ? 2 : 1);
        var encoding = 'sbcs-cont';
        var cp = current_codepage;
        if (opts && opts.biff >= 8) current_codepage = 1200;
        if (!opts || opts.biff == 8) {
            var fHighByte = blob.read_shift(1);
            if (fHighByte) {
                encoding = 'dbcs-cont';
            }
        } else if (opts.biff == 12) {
            encoding = 'wstr';
        }
        if (opts.biff >= 2 && opts.biff <= 5) encoding = 'cpstr';
        var o = cch ? blob.read_shift(cch, encoding) : "";
        current_codepage = cp;
        return o;
    }
    /* 2.5.293 XLUnicodeRichExtendedString */
    function parse_XLUnicodeRichExtendedString(blob) {
        var cp = current_codepage;
        current_codepage = 1200;
        var cch = blob.read_shift(2),
            flags = blob.read_shift(1);
        var /*fHighByte = flags & 0x1,*/ fExtSt = flags & 0x4,
            fRichSt = flags & 0x8;
        var width = 1 + (flags & 0x1); // 0x0 -> utf8, 0x1 -> dbcs
        var cRun = 0,
            cbExtRst;
        var z = {};
        if (fRichSt) cRun = blob.read_shift(2);
        if (fExtSt) cbExtRst = blob.read_shift(4);
        var encoding = width == 2 ? 'dbcs-cont' : 'sbcs-cont';
        var msg = cch === 0 ? "" : blob.read_shift(cch, encoding);
        if (fRichSt) blob.l += 4 * cRun; //TODO: parse this
        if (fExtSt) blob.l += cbExtRst; //TODO: parse this
        z.t = msg;
        if (!fRichSt) {
            z.raw = "" + z.t + "";
            z.r = z.t;
        }
        current_codepage = cp;
        return z;
    }
    /* 2.5.296 XLUnicodeStringNoCch */
    function parse_XLUnicodeStringNoCch(blob, cch, opts) {
        var retval;
        if (opts) {
            if (opts.biff >= 2 && opts.biff <= 5) return blob.read_shift(cch, 'cpstr');
            if (opts.biff >= 12) return blob.read_shift(cch, 'dbcs-cont');
        }
        var fHighByte = blob.read_shift(1);
        if (fHighByte === 0) {
            retval = blob.read_shift(cch, 'sbcs-cont');
        } else {
            retval = blob.read_shift(cch, 'dbcs-cont');
        }
        return retval;
    }
    /* 2.5.294 XLUnicodeString */
    function parse_XLUnicodeString(blob, length, opts) {
        var cch = blob.read_shift(opts && opts.biff == 2 ? 1 : 2);
        if (cch === 0) {
            blob.l++;
            return "";
        }
        return parse_XLUnicodeStringNoCch(blob, cch, opts);
    }
    /* BIFF5 override */
    function parse_XLUnicodeString2(blob, length, opts) {
        if (opts.biff > 5) return parse_XLUnicodeString(blob, length, opts);
        var cch = blob.read_shift(1);
        if (cch === 0) {
            blob.l++;
            return "";
        }
        return blob.read_shift(cch, (opts.biff <= 4 || !blob.lens) ? 'cpstr' : 'sbcs-cont');
    }
    /* TODO: BIFF5 and lower, codepage awareness */
    function write_XLUnicodeString(str, opts, o) {
        if (!o) o = new_buf(3 + 2 * str.length);
        o.write_shift(2, str.length);
        o.write_shift(1, 1);
        o.write_shift(31, str, 'utf16le');
        return o;
    }
    /* [MS-XLS] 2.5.61 ControlInfo */
    function parse_ControlInfo(blob) {
        var flags = blob.read_shift(1);
        blob.l++;
        var accel = blob.read_shift(2);
        blob.l += 2;
        return [flags, accel];
    }
    /* [MS-OSHARED] 2.3.7.6 URLMoniker TODO: flags */
    function parse_URLMoniker(blob) {
        var len = blob.read_shift(4),
            start = blob.l;
        var extra = false;
        if (len > 24) {
            /* look ahead */
            blob.l += len - 24;
            if (blob.read_shift(16) === "795881f43b1d7f48af2c825dc4852763") extra = true;
            blob.l = start;
        }
        var url = blob.read_shift((extra ? len - 24 : len) >> 1, 'utf16le').replace(chr0, "");
        if (extra) blob.l += 24;
        return url;
    }
    /* [MS-OSHARED] 2.3.7.8 FileMoniker TODO: all fields */
    function parse_FileMoniker(blob) {
        blob.l += 2; //var cAnti = blob.read_shift(2);
        var ansiPath = blob.read_shift(0, 'lpstr-ansi');
        blob.l += 2; //var endServer = blob.read_shift(2);
        if (blob.read_shift(2) != 0xDEAD) throw new Error("Bad FileMoniker");
        var sz = blob.read_shift(4);
        if (sz === 0) return ansiPath.replace(/\\/g, "/");
        var bytes = blob.read_shift(4);
        if (blob.read_shift(2) != 3) throw new Error("Bad FileMoniker");
        var unicodePath = blob.read_shift(bytes >> 1, 'utf16le').replace(chr0, "");
        return unicodePath;
    }
    /* [MS-OSHARED] 2.3.7.2 HyperlinkMoniker TODO: all the monikers */
    function parse_HyperlinkMoniker(blob, length) {
        var clsid = blob.read_shift(16);
        length -= 16;
        switch (clsid) {
            case "e0c9ea79f9bace118c8200aa004ba90b":
                return parse_URLMoniker(blob, length);
            case "0303000000000000c000000000000046":
                return parse_FileMoniker(blob, length);
            default:
                throw new Error("Unsupported Moniker " + clsid);
        }
    }
    /* [MS-OSHARED] 2.3.7.9 HyperlinkString */
    function parse_HyperlinkString(blob) {
        var len = blob.read_shift(4);
        var o = len > 0 ? blob.read_shift(len, 'utf16le').replace(chr0, "") : "";
        return o;
    }
    /* [MS-OSHARED] 2.3.7.1 Hyperlink Object */
    function parse_Hyperlink(blob, length) {
        var end = blob.l + length;
        var sVer = blob.read_shift(4);
        if (sVer !== 2) throw new Error("Unrecognized streamVersion: " + sVer);
        var flags = blob.read_shift(2);
        blob.l += 2;
        var displayName, targetFrameName, moniker, oleMoniker, Loc = "",
            guid, fileTime;
        if (flags & 0x0010) displayName = parse_HyperlinkString(blob, end - blob.l);
        if (flags & 0x0080) targetFrameName = parse_HyperlinkString(blob, end - blob.l);
        if ((flags & 0x0101) === 0x0101) moniker = parse_HyperlinkString(blob, end - blob.l);
        if ((flags & 0x0101) === 0x0001) oleMoniker = parse_HyperlinkMoniker(blob, end - blob.l);
        if (flags & 0x0008) Loc = parse_HyperlinkString(blob, end - blob.l);
        if (flags & 0x0020) guid = blob.read_shift(16);
        if (flags & 0x0040) fileTime = parse_FILETIME(blob /*, 8*/ );
        blob.l = end;
        var target = targetFrameName || moniker || oleMoniker || "";
        if (target && Loc) target += "#" + Loc;
        if (!target) target = "#" + Loc;
        var out = ({
            Target: target
        });
        if (guid) out.guid = guid;
        if (fileTime) out.time = fileTime;
        if (displayName) out.Tooltip = displayName;
        return out;
    }
    function write_Hyperlink(hl) {
        var out = new_buf(512),
            i = 0;
        var Target = hl.Target;
        var F = Target.indexOf("#") > -1 ? 0x1f : 0x17;
        switch (Target.charAt(0)) {
            case "#":
                F = 0x1c;
                break;
            case ".":
                F &= ~2;
                break;
        }
        out.write_shift(4, 2);
        out.write_shift(4, F);
        var data = [8, 6815827, 6619237, 4849780, 83];
        for (i = 0; i < data.length; ++i) out.write_shift(4, data[i]);
        if (F == 0x1C) {
            Target = Target.slice(1);
            out.write_shift(4, Target.length + 1);
            for (i = 0; i < Target.length; ++i) out.write_shift(2, Target.charCodeAt(i));
            out.write_shift(2, 0);
        } else if (F & 0x02) {
            data = "e0 c9 ea 79 f9 ba ce 11 8c 82 00 aa 00 4b a9 0b".split(" ");
            for (i = 0; i < data.length; ++i) out.write_shift(1, parseInt(data[i], 16));
            out.write_shift(4, 2 * (Target.length + 1));
            for (i = 0; i < Target.length; ++i) out.write_shift(2, Target.charCodeAt(i));
            out.write_shift(2, 0);
        } else {
            data = "03 03 00 00 00 00 00 00 c0 00 00 00 00 00 00 46".split(" ");
            for (i = 0; i < data.length; ++i) out.write_shift(1, parseInt(data[i], 16));
            var P = 0;
            while (Target.slice(P * 3, P * 3 + 3) == "../" || Target.slice(P * 3, P * 3 + 3) == "..\\") ++P;
            out.write_shift(2, P);
            out.write_shift(4, Target.length + 1);
            for (i = 0; i < Target.length; ++i) out.write_shift(1, Target.charCodeAt(i) & 0xFF);
            out.write_shift(1, 0);
            out.write_shift(2, 0xFFFF);
            out.write_shift(2, 0xDEAD);
            for (i = 0; i < 6; ++i) out.write_shift(4, 0);
        }
        return out.slice(0, out.l);
    }
    /* 2.5.178 LongRGBA */
    function parse_LongRGBA(blob) {
        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];
    }
    /* 2.5.177 LongRGB */
    function parse_LongRGB(blob, length) {
        var x = parse_LongRGBA(blob, length);
        x[3] = 0;
        return x;
    }
    /* [MS-XLS] 2.5.19 */
    function parse_XLSCell(blob) {
        var rw = blob.read_shift(2); // 0-indexed
        var col = blob.read_shift(2);
        var ixfe = blob.read_shift(2);
        return ({
            r: rw,
            c: col,
            ixfe: ixfe
        });
    }
    function write_XLSCell(R, C, ixfe, o) {
        if (!o) o = new_buf(6);
        o.write_shift(2, R);
        o.write_shift(2, C);
        o.write_shift(2, ixfe || 0);
        return o;
    }
    /* [MS-XLS] 2.5.134 */
    function parse_frtHeader(blob) {
        var rt = blob.read_shift(2);
        var flags = blob.read_shift(2); // TODO: parse these flags
        blob.l += 8;
        return {
            type: rt,
            flags: flags
        };
    }
    function parse_OptXLUnicodeString(blob, length, opts) {
        return length === 0 ? "" : parse_XLUnicodeString2(blob, length, opts);
    }
    /* [MS-XLS] 2.5.344 */
    function parse_XTI(blob, length, opts) {
        var w = opts.biff > 8 ? 4 : 2;
        var iSupBook = blob.read_shift(w),
            itabFirst = blob.read_shift(w, 'i'),
            itabLast = blob.read_shift(w, 'i');
        return [iSupBook, itabFirst, itabLast];
    }
    /* [MS-XLS] 2.5.218 */
    function parse_RkRec(blob) {
        var ixfe = blob.read_shift(2);
        var RK = parse_RkNumber(blob);
        return [ixfe, RK];
    }
    /* [MS-XLS] 2.5.1 */
    function parse_AddinUdf(blob, length, opts) {
        blob.l += 4;
        length -= 4;
        var l = blob.l + length;
        var udfName = parse_ShortXLUnicodeString(blob, length, opts);
        var cb = blob.read_shift(2);
        l -= blob.l;
        if (cb !== l) throw new Error("Malformed AddinUdf: padding = " + l + " != " + cb);
        blob.l += cb;
        return udfName;
    }
    /* [MS-XLS] 2.5.209 TODO: Check sizes */
    function parse_Ref8U(blob) {
        var rwFirst = blob.read_shift(2);
        var rwLast = blob.read_shift(2);
        var colFirst = blob.read_shift(2);
        var colLast = blob.read_shift(2);
        return {
            s: {
                c: colFirst,
                r: rwFirst
            },
            e: {
                c: colLast,
                r: rwLast
            }
        };
    }
    function write_Ref8U(r, o) {
        if (!o) o = new_buf(8);
        o.write_shift(2, r.s.r);
        o.write_shift(2, r.e.r);
        o.write_shift(2, r.s.c);
        o.write_shift(2, r.e.c);
        return o;
    }
    /* [MS-XLS] 2.5.211 */
    function parse_RefU(blob) {
        var rwFirst = blob.read_shift(2);
        var rwLast = blob.read_shift(2);
        var colFirst = blob.read_shift(1);
        var colLast = blob.read_shift(1);
        return {
            s: {
                c: colFirst,
                r: rwFirst
            },
            e: {
                c: colLast,
                r: rwLast
            }
        };
    }
    /* [MS-XLS] 2.5.207 */
    var parse_Ref = parse_RefU;
    /* [MS-XLS] 2.5.143 */
    function parse_FtCmo(blob) {
        blob.l += 4;
        var ot = blob.read_shift(2);
        var id = blob.read_shift(2);
        var flags = blob.read_shift(2);
        blob.l += 12;
        return [id, ot, flags];
    }
    /* [MS-XLS] 2.5.149 */
    function parse_FtNts(blob) {
        var out = {};
        blob.l += 4;
        blob.l += 16; // GUID TODO
        out.fSharedNote = blob.read_shift(2);
        blob.l += 4;
        return out;
    }
    /* [MS-XLS] 2.5.142 */
    function parse_FtCf(blob) {
        var out = {};
        blob.l += 4;
        blob.cf = blob.read_shift(2);
        return out;
    }
    /* [MS-XLS] 2.5.140 - 2.5.154 and friends */
    function parse_FtSkip(blob) {
        blob.l += 2;
        blob.l += blob.read_shift(2);
    }
    var FtTab = {
        0x00: parse_FtSkip,
        /* FtEnd */
        0x04: parse_FtSkip,
        /* FtMacro */
        0x05: parse_FtSkip,
        /* FtButton */
        0x06: parse_FtSkip,
        /* FtGmo */
        0x07: parse_FtCf,
        /* FtCf */
        0x08: parse_FtSkip,
        /* FtPioGrbit */
        0x09: parse_FtSkip,
        /* FtPictFmla */
        0x0A: parse_FtSkip,
        /* FtCbls */
        0x0B: parse_FtSkip,
        /* FtRbo */
        0x0C: parse_FtSkip,
        /* FtSbs */
        0x0D: parse_FtNts,
        /* FtNts */
        0x0E: parse_FtSkip,
        /* FtSbsFmla */
        0x0F: parse_FtSkip,
        /* FtGboData */
        0x10: parse_FtSkip,
        /* FtEdoData */
        0x11: parse_FtSkip,
        /* FtRboData */
        0x12: parse_FtSkip,
        /* FtCblsData */
        0x13: parse_FtSkip,
        /* FtLbsData */
        0x14: parse_FtSkip,
        /* FtCblsFmla */
        0x15: parse_FtCmo
    };
    function parse_FtArray(blob, length) {
        var tgt = blob.l + length;
        var fts = [];
        while (blob.l < tgt) {
            var ft = blob.read_shift(2);
            blob.l -= 2;
            try {
                fts.push(FtTab[ft](blob, tgt - blob.l));
            } catch (e) {
                blob.l = tgt;
                return fts;
            }
        }
        if (blob.l != tgt) blob.l = tgt; //throw new Error("bad Object Ft-sequence");
        return fts;
    }
    /* --- 2.4 Records --- */
    /* [MS-XLS] 2.4.21 */
    function parse_BOF(blob, length) {
        var o = {
            BIFFVer: 0,
            dt: 0
        };
        o.BIFFVer = blob.read_shift(2);
        length -= 2;
        if (length >= 2) {
            o.dt = blob.read_shift(2);
            blob.l -= 2;
        }
        switch (o.BIFFVer) {
            case 0x0600:
                /* BIFF8 */
            case 0x0500:
                /* BIFF5 */
            case 0x0400:
                /* BIFF4 */
            case 0x0300:
                /* BIFF3 */
            case 0x0200:
                /* BIFF2 */
            case 0x0002:
            case 0x0007:
                /* BIFF2 */
                break;
            default:
                if (length > 6) throw new Error("Unexpected BIFF Ver " + o.BIFFVer);
        }
        blob.read_shift(length);
        return o;
    }
    function write_BOF(wb, t, o) {
        var h = 0x0600,
            w = 16;
        switch (o.bookType) {
            case 'biff8':
                break;
            case 'biff5':
                h = 0x0500;
                w = 8;
                break;
            case 'biff4':
                h = 0x0004;
                w = 6;
                break;
            case 'biff3':
                h = 0x0003;
                w = 6;
                break;
            case 'biff2':
                h = 0x0002;
                w = 4;
                break;
            case 'xla':
                break;
            default:
                throw new Error("unsupported BIFF version");
        }
        var out = new_buf(w);
        out.write_shift(2, h);
        out.write_shift(2, t);
        if (w > 4) out.write_shift(2, 0x7262);
        if (w > 6) out.write_shift(2, 0x07CD);
        if (w > 8) {
            out.write_shift(2, 0xC009);
            out.write_shift(2, 0x0001);
            out.write_shift(2, 0x0706);
            out.write_shift(2, 0x0000);
        }
        return out;
    }
    /* [MS-XLS] 2.4.146 */
    function parse_InterfaceHdr(blob, length) {
        if (length === 0) return 0x04b0;
        if ((blob.read_shift(2)) !== 0x04b0) { /* empty */ }
        return 0x04b0;
    }
    /* [MS-XLS] 2.4.349 */
    function parse_WriteAccess(blob, length, opts) {
        if (opts.enc) {
            blob.l += length;
            return "";
        }
        var l = blob.l;
        // TODO: make sure XLUnicodeString doesnt overrun
        var UserName = parse_XLUnicodeString2(blob, 0, opts);
        blob.read_shift(length + l - blob.l);
        return UserName;
    }
    function write_WriteAccess(s, opts) {
        var b8 = !opts || opts.biff == 8;
        var o = new_buf(b8 ? 112 : 54);
        o.write_shift(opts.biff == 8 ? 2 : 1, 7);
        if (b8) o.write_shift(1, 0);
        o.write_shift(4, 0x33336853);
        o.write_shift(4, (0x00534A74 | (b8 ? 0 : 0x20000000)));
        while (o.l < o.length) o.write_shift(1, (b8 ? 0 : 32));
        return o;
    }
    /* [MS-XLS] 2.4.351 */
    function parse_WsBool(blob, length, opts) {
        var flags = opts && opts.biff == 8 || length == 2 ? blob.read_shift(2) : (blob.l += length, 0);
        return {
            fDialog: flags & 0x10
        };
    }
    /* [MS-XLS] 2.4.28 */
    function parse_BoundSheet8(blob, length, opts) {
        var pos = blob.read_shift(4);
        var hidden = blob.read_shift(1) & 0x03;
        var dt = blob.read_shift(1);
        switch (dt) {
            case 0:
                dt = 'Worksheet';
                break;
            case 1:
                dt = 'Macrosheet';
                break;
            case 2:
                dt = 'Chartsheet';
                break;
            case 6:
                dt = 'VBAModule';
                break;
        }
        var name = parse_ShortXLUnicodeString(blob, 0, opts);
        if (name.length === 0) name = "Sheet1";
        return {
            pos: pos,
            hs: hidden,
            dt: dt,
            name: name
        };
    }
    function write_BoundSheet8(data, opts) {
        var w = (!opts || opts.biff >= 8 ? 2 : 1);
        var o = new_buf(8 + w * data.name.length);
        o.write_shift(4, data.pos);
        o.write_shift(1, data.hs || 0);
        o.write_shift(1, data.dt);
        o.write_shift(1, data.name.length);
        if (opts.biff >= 8) o.write_shift(1, 1);
        o.write_shift(w * data.name.length, data.name, opts.biff < 8 ? 'sbcs' : 'utf16le');
        var out = o.slice(0, o.l);
        out.l = o.l;
        return out;
    }
    /* [MS-XLS] 2.4.265 TODO */
    function parse_SST(blob, length) {
        var end = blob.l + length;
        var cnt = blob.read_shift(4);
        var ucnt = blob.read_shift(4);
        var strs = ([]);
        for (var i = 0; i != ucnt && blob.l < end; ++i) {
            strs.push(parse_XLUnicodeRichExtendedString(blob));
        }
        strs.Count = cnt;
        strs.Unique = ucnt;
        return strs;
    }
    /* [MS-XLS] 2.4.107 */
    function parse_ExtSST(blob, length) {
        var extsst = {};
        extsst.dsst = blob.read_shift(2);
        blob.l += length - 2;
        return extsst;
    }
    /* [MS-XLS] 2.4.221 TODO: check BIFF2-4 */
    function parse_Row(blob) {
        var z = ({});
        z.r = blob.read_shift(2);
        z.c = blob.read_shift(2);
        z.cnt = blob.read_shift(2) - z.c;
        var miyRw = blob.read_shift(2);
        blob.l += 4; // reserved(2), unused(2)
        var flags = blob.read_shift(1); // various flags
        blob.l += 3; // reserved(8), ixfe(12), flags(4)
        if (flags & 0x07) z.level = flags & 0x07;
        // collapsed: flags & 0x10
        if (flags & 0x20) z.hidden = true;
        if (flags & 0x40) z.hpt = miyRw / 20;
        return z;
    }
    /* [MS-XLS] 2.4.125 */
    function parse_ForceFullCalculation(blob) {
        var header = parse_frtHeader(blob);
        if (header.type != 0x08A3) throw new Error("Invalid Future Record " + header.type);
        var fullcalc = blob.read_shift(4);
        return fullcalc !== 0x0;
    }
    /* [MS-XLS] 2.4.215 rt */
    function parse_RecalcId(blob) {
        blob.read_shift(2);
        return blob.read_shift(4);
    }
    /* [MS-XLS] 2.4.87 */
    function parse_DefaultRowHeight(blob, length, opts) {
        var f = 0;
        if (!(opts && opts.biff == 2)) {
            f = blob.read_shift(2);
        }
        var miyRw = blob.read_shift(2);
        if ((opts && opts.biff == 2)) {
            f = 1 - (miyRw >> 15);
            miyRw &= 0x7fff;
        }
        var fl = {
            Unsynced: f & 1,
            DyZero: (f & 2) >> 1,
            ExAsc: (f & 4) >> 2,
            ExDsc: (f & 8) >> 3
        };
        return [fl, miyRw];
    }
    /* [MS-XLS] 2.4.345 TODO */
    function parse_Window1(blob) {
        var xWn = blob.read_shift(2),
            yWn = blob.read_shift(2),
            dxWn = blob.read_shift(2),
            dyWn = blob.read_shift(2);
        var flags = blob.read_shift(2),
            iTabCur = blob.read_shift(2),
            iTabFirst = blob.read_shift(2);
        var ctabSel = blob.read_shift(2),
            wTabRatio = blob.read_shift(2);
        return {
            Pos: [xWn, yWn],
            Dim: [dxWn, dyWn],
            Flags: flags,
            CurTab: iTabCur,
            FirstTab: iTabFirst,
            Selected: ctabSel,
            TabRatio: wTabRatio
        };
    }
    function write_Window1() {
        var o = new_buf(18);
        o.write_shift(2, 0);
        o.write_shift(2, 0);
        o.write_shift(2, 0x7260);
        o.write_shift(2, 0x44c0);
        o.write_shift(2, 0x38);
        o.write_shift(2, 0);
        o.write_shift(2, 0);
        o.write_shift(2, 1);
        o.write_shift(2, 0x01f4);
        return o;
    }
    /* [MS-XLS] 2.4.346 TODO */
    function parse_Window2(blob, length, opts) {
        if (opts && opts.biff >= 2 && opts.biff < 8) return {};
        var f = blob.read_shift(2);
        return {
            RTL: f & 0x40
        };
    }
    function write_Window2(view) {
        var o = new_buf(18),
            f = 0x6b6;
        if (view && view.RTL) f |= 0x40;
        o.write_shift(2, f);
        o.write_shift(4, 0);
        o.write_shift(4, 64);
        o.write_shift(4, 0);
        o.write_shift(4, 0);
        return o;
    }
    /* [MS-XLS] 2.4.122 TODO */
    function parse_Font(blob, length, opts) {
        var o = {
            dyHeight: blob.read_shift(2),
            fl: blob.read_shift(2)
        };
        switch ((opts && opts.biff) || 8) {
            case 2:
                break;
            case 3:
            case 4:
                blob.l += 2;
                break;
            default:
                blob.l += 10;
                break;
        }
        o.name = parse_ShortXLUnicodeString(blob, 0, opts);
        return o;
    }
    function write_Font(data, opts) {
        var name = data.name || "Arial";
        var b5 = (opts && (opts.biff == 5)),
            w = (b5 ? (15 + name.length) : (16 + 2 * name.length));
        var o = new_buf(w);
        o.write_shift(2, (data.sz || 12) * 20);
        o.write_shift(4, 0);
        o.write_shift(2, 400);
        o.write_shift(4, 0);
        o.write_shift(2, 0);
        o.write_shift(1, name.length);
        if (!b5) o.write_shift(1, 1);
        o.write_shift((b5 ? 1 : 2) * name.length, name, (b5 ? "sbcs" : "utf16le"));
        return o;
    }
    /* [MS-XLS] 2.4.149 */
    function parse_LabelSst(blob) {
        var cell = parse_XLSCell(blob);
        cell.isst = blob.read_shift(4);
        return cell;
    }
    /* [MS-XLS] 2.4.148 */
    function parse_Label(blob, length, opts) {
        var target = blob.l + length;
        var cell = parse_XLSCell(blob, 6);
        if (opts.biff == 2) blob.l++;
        var str = parse_XLUnicodeString(blob, target - blob.l, opts);
        cell.val = str;
        return cell;
    }
    function write_Label(R, C, v, os, opts) {
        var b8 = !opts || opts.biff == 8;
        var o = new_buf(6 + 2 + (+b8) + (1 + b8) * v.length);
        write_XLSCell(R, C, os, o);
        o.write_shift(2, v.length);
        if (b8) o.write_shift(1, 1);
        o.write_shift((1 + b8) * v.length, v, b8 ? 'utf16le' : 'sbcs');
        return o;
    }
    /* [MS-XLS] 2.4.126 Number Formats */
    function parse_Format(blob, length, opts) {
        var numFmtId = blob.read_shift(2);
        var fmtstr = parse_XLUnicodeString2(blob, 0, opts);
        return [numFmtId, fmtstr];
    }
    function write_Format(i, f, opts, o) {
        var b5 = (opts && (opts.biff == 5));
        if (!o) o = new_buf(b5 ? (3 + f.length) : (5 + 2 * f.length));
        o.write_shift(2, i);
        o.write_shift((b5 ? 1 : 2), f.length);
        if (!b5) o.write_shift(1, 1);
        o.write_shift((b5 ? 1 : 2) * f.length, f, (b5 ? 'sbcs' : 'utf16le'));
        var out = (o.length > o.l) ? o.slice(0, o.l) : o;
        if (out.l == null) out.l = out.length;
        return out;
    }
    var parse_BIFF2Format = parse_XLUnicodeString2;
    /* [MS-XLS] 2.4.90 */
    function parse_Dimensions(blob, length, opts) {
        var end = blob.l + length;
        var w = opts.biff == 8 || !opts.biff ? 4 : 2;
        var r = blob.read_shift(w),
            R = blob.read_shift(w);
        var c = blob.read_shift(2),
            C = blob.read_shift(2);
        blob.l = end;
        return {
            s: {
                r: r,
                c: c
            },
            e: {
                r: R,
                c: C
            }
        };
    }
    function write_Dimensions(range, opts) {
        var w = opts.biff == 8 || !opts.biff ? 4 : 2;
        var o = new_buf(2 * w + 6);
        o.write_shift(w, range.s.r);
        o.write_shift(w, range.e.r + 1);
        o.write_shift(2, range.s.c);
        o.write_shift(2, range.e.c + 1);
        o.write_shift(2, 0);
        return o;
    }
    /* [MS-XLS] 2.4.220 */
    function parse_RK(blob) {
        var rw = blob.read_shift(2),
            col = blob.read_shift(2);
        var rkrec = parse_RkRec(blob);
        return {
            r: rw,
            c: col,
            ixfe: rkrec[0],
            rknum: rkrec[1]
        };
    }
    /* [MS-XLS] 2.4.175 */
    function parse_MulRk(blob, length) {
        var target = blob.l + length - 2;
        var rw = blob.read_shift(2),
            col = blob.read_shift(2);
        var rkrecs = [];
        while (blob.l < target) rkrecs.push(parse_RkRec(blob));
        if (blob.l !== target) throw new Error("MulRK read error");
        var lastcol = blob.read_shift(2);
        if (rkrecs.length != lastcol - col + 1) throw new Error("MulRK length mismatch");
        return {
            r: rw,
            c: col,
            C: lastcol,
            rkrec: rkrecs
        };
    }
    /* [MS-XLS] 2.4.174 */
    function parse_MulBlank(blob, length) {
        var target = blob.l + length - 2;
        var rw = blob.read_shift(2),
            col = blob.read_shift(2);
        var ixfes = [];
        while (blob.l < target) ixfes.push(blob.read_shift(2));
        if (blob.l !== target) throw new Error("MulBlank read error");
        var lastcol = blob.read_shift(2);
        if (ixfes.length != lastcol - col + 1) throw new Error("MulBlank length mismatch");
        return {
            r: rw,
            c: col,
            C: lastcol,
            ixfe: ixfes
        };
    }
    /* [MS-XLS] 2.5.20 2.5.249 TODO: interpret values here */
    function parse_CellStyleXF(blob, length, style, opts) {
        var o = {};
        var a = blob.read_shift(4),
            b = blob.read_shift(4);
        var c = blob.read_shift(4),
            d = blob.read_shift(2);
        o.patternType = XLSFillPattern[c >> 26];
        if (!opts.cellStyles) return o;
        o.alc = a & 0x07;
        o.fWrap = (a >> 3) & 0x01;
        o.alcV = (a >> 4) & 0x07;
        o.fJustLast = (a >> 7) & 0x01;
        o.trot = (a >> 8) & 0xFF;
        o.cIndent = (a >> 16) & 0x0F;
        o.fShrinkToFit = (a >> 20) & 0x01;
        o.iReadOrder = (a >> 22) & 0x02;
        o.fAtrNum = (a >> 26) & 0x01;
        o.fAtrFnt = (a >> 27) & 0x01;
        o.fAtrAlc = (a >> 28) & 0x01;
        o.fAtrBdr = (a >> 29) & 0x01;
        o.fAtrPat = (a >> 30) & 0x01;
        o.fAtrProt = (a >> 31) & 0x01;
        o.dgLeft = b & 0x0F;
        o.dgRight = (b >> 4) & 0x0F;
        o.dgTop = (b >> 8) & 0x0F;
        o.dgBottom = (b >> 12) & 0x0F;
        o.icvLeft = (b >> 16) & 0x7F;
        o.icvRight = (b >> 23) & 0x7F;
        o.grbitDiag = (b >> 30) & 0x03;
        o.icvTop = c & 0x7F;
        o.icvBottom = (c >> 7) & 0x7F;
        o.icvDiag = (c >> 14) & 0x7F;
        o.dgDiag = (c >> 21) & 0x0F;
        o.icvFore = d & 0x7F;
        o.icvBack = (d >> 7) & 0x7F;
        o.fsxButton = (d >> 14) & 0x01;
        return o;
    }
    //function parse_CellXF(blob, length, opts) {return parse_CellStyleXF(blob,length,0, opts);}
    //function parse_StyleXF(blob, length, opts) {return parse_CellStyleXF(blob,length,1, opts);}
    /* [MS-XLS] 2.4.353 TODO: actually do this right */
    function parse_XF(blob, length, opts) {
        var o = {};
        o.ifnt = blob.read_shift(2);
        o.numFmtId = blob.read_shift(2);
        o.flags = blob.read_shift(2);
        o.fStyle = (o.flags >> 2) & 0x01;
        length -= 6;
        o.data = parse_CellStyleXF(blob, length, o.fStyle, opts);
        return o;
    }
    function write_XF(data, ixfeP, opts, o) {
        var b5 = (opts && (opts.biff == 5));
        if (!o) o = new_buf(b5 ? 16 : 20);
        o.write_shift(2, 0);
        if (data.style) {
            o.write_shift(2, (data.numFmtId || 0));
            o.write_shift(2, 0xFFF4);
        } else {
            o.write_shift(2, (data.numFmtId || 0));
            o.write_shift(2, (ixfeP << 4));
        }
        o.write_shift(4, 0);
        o.write_shift(4, 0);
        if (!b5) o.write_shift(4, 0);
        o.write_shift(2, 0);
        return o;
    }
    /* [MS-XLS] 2.4.134 */
    function parse_Guts(blob) {
        blob.l += 4;
        var out = [blob.read_shift(2), blob.read_shift(2)];
        if (out[0] !== 0) out[0]--;
        if (out[1] !== 0) out[1]--;
        if (out[0] > 7 || out[1] > 7) throw new Error("Bad Gutters: " + out.join("|"));
        return out;
    }
    function write_Guts(guts) {
        var o = new_buf(8);
        o.write_shift(4, 0);
        o.write_shift(2, guts[0] ? guts[0] + 1 : 0);
        o.write_shift(2, guts[1] ? guts[1] + 1 : 0);
        return o;
    }
    /* [MS-XLS] 2.4.24 */
    function parse_BoolErr(blob, length, opts) {
        var cell = parse_XLSCell(blob, 6);
        if (opts.biff == 2) ++blob.l;
        var val = parse_Bes(blob, 2);
        cell.val = val;
        cell.t = (val === true || val === false) ? 'b' : 'e';
        return cell;
    }
    function write_BoolErr(R, C, v, os, opts, t) {
        var o = new_buf(8);
        write_XLSCell(R, C, os, o);
        write_Bes(v, t, o);
        return o;
    }
    /* [MS-XLS] 2.4.180 Number */
    function parse_Number(blob) {
        var cell = parse_XLSCell(blob, 6);
        var xnum = parse_Xnum(blob, 8);
        cell.val = xnum;
        return cell;
    }
    function write_Number(R, C, v, os) {
        var o = new_buf(14);
        write_XLSCell(R, C, os, o);
        write_Xnum(v, o);
        return o;
    }
    var parse_XLHeaderFooter = parse_OptXLUnicodeString; // TODO: parse 2.4.136
    /* [MS-XLS] 2.4.271 */
    function parse_SupBook(blob, length, opts) {
        var end = blob.l + length;
        var ctab = blob.read_shift(2);
        var cch = blob.read_shift(2);
        opts.sbcch = cch;
        if (cch == 0x0401 || cch == 0x3A01) return [cch, ctab];
        if (cch < 0x01 || cch > 0xff) throw new Error("Unexpected SupBook type: " + cch);
        var virtPath = parse_XLUnicodeStringNoCch(blob, cch);
        /* TODO: 2.5.277 Virtual Path */
        var rgst = [];
        while (end > blob.l) rgst.push(parse_XLUnicodeString(blob));
        return [cch, ctab, virtPath, rgst];
    }
    /* [MS-XLS] 2.4.105 TODO */
    function parse_ExternName(blob, length, opts) {
        var flags = blob.read_shift(2);
        var body;
        var o = ({
            fBuiltIn: flags & 0x01,
            fWantAdvise: (flags >>> 1) & 0x01,
            fWantPict: (flags >>> 2) & 0x01,
            fOle: (flags >>> 3) & 0x01,
            fOleLink: (flags >>> 4) & 0x01,
            cf: (flags >>> 5) & 0x3FF,
            fIcon: flags >>> 15 & 0x01
        });
        if (opts.sbcch === 0x3A01) body = parse_AddinUdf(blob, length - 2, opts);
        //else throw new Error("unsupported SupBook cch: " + opts.sbcch);
        o.body = body || blob.read_shift(length - 2);
        if (typeof body === "string") o.Name = body;
        return o;
    }
    /* [MS-XLS] 2.4.150 TODO */
    var XLSLblBuiltIn = [
        "_xlnm.Consolidate_Area",
        "_xlnm.Auto_Open",
        "_xlnm.Auto_Close",
        "_xlnm.Extract",
        "_xlnm.Database",
        "_xlnm.Criteria",
        "_xlnm.Print_Area",
        "_xlnm.Print_Titles",
        "_xlnm.Recorder",
        "_xlnm.Data_Form",
        "_xlnm.Auto_Activate",
        "_xlnm.Auto_Deactivate",
        "_xlnm.Sheet_Title",
        "_xlnm._FilterDatabase"
    ];
    function parse_Lbl(blob, length, opts) {
        var target = blob.l + length;
        var flags = blob.read_shift(2);
        var chKey = blob.read_shift(1);
        var cch = blob.read_shift(1);
        var cce = blob.read_shift(opts && opts.biff == 2 ? 1 : 2);
        var itab = 0;
        if (!opts || opts.biff >= 5) {
            if (opts.biff != 5) blob.l += 2;
            itab = blob.read_shift(2);
            if (opts.biff == 5) blob.l += 2;
            blob.l += 4;
        }
        var name = parse_XLUnicodeStringNoCch(blob, cch, opts);
        if (flags & 0x20) name = XLSLblBuiltIn[name.charCodeAt(0)];
        var npflen = target - blob.l;
        if (opts && opts.biff == 2) --npflen;
        var rgce = target == blob.l || cce === 0 ? [] : parse_NameParsedFormula(blob, npflen, opts, cce);
        return {
            chKey: chKey,
            Name: name,
            itab: itab,
            rgce: rgce
        };
    }
    /* [MS-XLS] 2.4.106 TODO: verify filename encoding */
    function parse_ExternSheet(blob, length, opts) {
        if (opts.biff < 8) return parse_BIFF5ExternSheet(blob, length, opts);
        var o = [],
            target = blob.l + length,
            len = blob.read_shift(opts.biff > 8 ? 4 : 2);
        while (len-- !== 0) o.push(parse_XTI(blob, opts.biff > 8 ? 12 : 6, opts));
        // [iSupBook, itabFirst, itabLast];
        if (blob.l != target) throw new Error("Bad ExternSheet: " + blob.l + " != " + target);
        return o;
    }
    function parse_BIFF5ExternSheet(blob, length, opts) {
        if (blob[blob.l + 1] == 0x03) blob[blob.l]++;
        var o = parse_ShortXLUnicodeString(blob, length, opts);
        return o.charCodeAt(0) == 0x03 ? o.slice(1) : o;
    }
    /* [MS-XLS] 2.4.176 TODO: check older biff */
    function parse_NameCmt(blob, length, opts) {
        if (opts.biff < 8) {
            blob.l += length;
            return;
        }
        var cchName = blob.read_shift(2);
        var cchComment = blob.read_shift(2);
        var name = parse_XLUnicodeStringNoCch(blob, cchName, opts);
        var comment = parse_XLUnicodeStringNoCch(blob, cchComment, opts);
        return [name, comment];
    }
    /* [MS-XLS] 2.4.260 */
    function parse_ShrFmla(blob, length, opts) {
        var ref = parse_RefU(blob, 6);
        blob.l++;
        var cUse = blob.read_shift(1);
        length -= 8;
        return [parse_SharedParsedFormula(blob, length, opts), cUse, ref];
    }
    /* [MS-XLS] 2.4.4 TODO */
    function parse_Array(blob, length, opts) {
        var ref = parse_Ref(blob, 6);
        /* TODO: fAlwaysCalc */
        switch (opts.biff) {
            case 2:
                blob.l++;
                length -= 7;
                break;
            case 3:
            case 4:
                blob.l += 2;
                length -= 8;
                break;
            default:
                blob.l += 6;
                length -= 12;
        }
        return [ref, parse_ArrayParsedFormula(blob, length, opts, ref)];
    }
    /* [MS-XLS] 2.4.173 */
    function parse_MTRSettings(blob) {
        var fMTREnabled = blob.read_shift(4) !== 0x00;
        var fUserSetThreadCount = blob.read_shift(4) !== 0x00;
        var cUserThreadCount = blob.read_shift(4);
        return [fMTREnabled, fUserSetThreadCount, cUserThreadCount];
    }
    /* [MS-XLS] 2.5.186 TODO: BIFF5 */
    function parse_NoteSh(blob, length, opts) {
        if (opts.biff < 8) return;
        var row = blob.read_shift(2),
            col = blob.read_shift(2);
        var flags = blob.read_shift(2),
            idObj = blob.read_shift(2);
        var stAuthor = parse_XLUnicodeString2(blob, 0, opts);
        if (opts.biff < 8) blob.read_shift(1);
        return [{
            r: row,
            c: col
        }, stAuthor, idObj, flags];
    }
    /* [MS-XLS] 2.4.179 */
    function parse_Note(blob, length, opts) {
        /* TODO: Support revisions */
        return parse_NoteSh(blob, length, opts);
    }
    /* [MS-XLS] 2.4.168 */
    function parse_MergeCells(blob, length) {
        var merges = [];
        var cmcs = blob.read_shift(2);
        while (cmcs--) merges.push(parse_Ref8U(blob, length));
        return merges;
    }
    function write_MergeCells(merges) {
        var o = new_buf(2 + merges.length * 8);
        o.write_shift(2, merges.length);
        for (var i = 0; i < merges.length; ++i) write_Ref8U(merges[i], o);
        return o;
    }
    /* [MS-XLS] 2.4.181 TODO: parse all the things! */
    function parse_Obj(blob, length, opts) {
        if (opts && opts.biff < 8) return parse_BIFF5Obj(blob, length, opts);
        var cmo = parse_FtCmo(blob, 22); // id, ot, flags
        var fts = parse_FtArray(blob, length - 22, cmo[1]);
        return {
            cmo: cmo,
            ft: fts
        };
    }
    /* from older spec */
    var parse_BIFF5OT = [];
    parse_BIFF5OT[0x08] = function (blob, length) {
        var tgt = blob.l + length;
        blob.l += 10; // todo
        var cf = blob.read_shift(2);
        blob.l += 4;
        blob.l += 2; //var cbPictFmla = blob.read_shift(2);
        blob.l += 2;
        blob.l += 2; //var grbit = blob.read_shift(2);
        blob.l += 4;
        var cchName = blob.read_shift(1);
        blob.l += cchName; // TODO: stName
        blob.l = tgt; // TODO: fmla
        return {
            fmt: cf
        };
    };
    function parse_BIFF5Obj(blob, length, opts) {
        blob.l += 4; //var cnt = blob.read_shift(4);
        var ot = blob.read_shift(2);
        var id = blob.read_shift(2);
        var grbit = blob.read_shift(2);
        blob.l += 2; //var colL = blob.read_shift(2);
        blob.l += 2; //var dxL = blob.read_shift(2);
        blob.l += 2; //var rwT = blob.read_shift(2);
        blob.l += 2; //var dyT = blob.read_shift(2);
        blob.l += 2; //var colR = blob.read_shift(2);
        blob.l += 2; //var dxR = blob.read_shift(2);
        blob.l += 2; //var rwB = blob.read_shift(2);
        blob.l += 2; //var dyB = blob.read_shift(2);
        blob.l += 2; //var cbMacro = blob.read_shift(2);
        blob.l += 6;
        length -= 36;
        var fts = [];
        fts.push((parse_BIFF5OT[ot] || parsenoop)(blob, length, opts));
        return {
            cmo: [id, ot, grbit],
            ft: fts
        };
    }
    /* [MS-XLS] 2.4.329 TODO: parse properly */
    function parse_TxO(blob, length, opts) {
        var s = blob.l;
        var texts = "";
        try {
            blob.l += 4;
            var ot = (opts.lastobj || {
                cmo: [0, 0]
            }).cmo[1];
            var controlInfo; // eslint-disable-line no-unused-vars
            if ([0, 5, 7, 11, 12, 14].indexOf(ot) == -1) blob.l += 6;
            else controlInfo = parse_ControlInfo(blob, 6, opts);
            var cchText = blob.read_shift(2);
            /*var cbRuns = */
            blob.read_shift(2);
            /*var ifntEmpty = */
            parseuint16(blob, 2);
            var len = blob.read_shift(2);
            blob.l += len;
            //var fmla = parse_ObjFmla(blob, s + length - blob.l);
            for (var i = 1; i < blob.lens.length - 1; ++i) {
                if (blob.l - s != blob.lens[i]) throw new Error("TxO: bad continue record");
                var hdr = blob[blob.l];
                var t = parse_XLUnicodeStringNoCch(blob, blob.lens[i + 1] - blob.lens[i] - 1);
                texts += t;
                if (texts.length >= (hdr ? cchText : 2 * cchText)) break;
            }
            if (texts.length !== cchText && texts.length !== cchText * 2) {
                throw new Error("cchText: " + cchText + " != " + texts.length);
            }
            blob.l = s + length;
            /* [MS-XLS] 2.5.272 TxORuns */
            //	var rgTxoRuns = [];
            //	for(var j = 0; j != cbRuns/8-1; ++j) blob.l += 8;
            //	var cchText2 = blob.read_shift(2);
            //	if(cchText2 !== cchText) throw new Error("TxOLastRun mismatch: " + cchText2 + " " + cchText);
            //	blob.l += 6;
            //	if(s + length != blob.l) throw new Error("TxO " + (s + length) + ", at " + blob.l);
            return {
                t: texts
            };
        } catch (e) {
            blob.l = s + length;
            return {
                t: texts
            };
        }
    }
    /* [MS-XLS] 2.4.140 */
    function parse_HLink(blob, length) {
        var ref = parse_Ref8U(blob, 8);
        blob.l += 16; /* CLSID */
        var hlink = parse_Hyperlink(blob, length - 24);
        return [ref, hlink];
    }
    function write_HLink(hl) {
        var O = new_buf(24);
        var ref = decode_cell(hl[0]);
        O.write_shift(2, ref.r);
        O.write_shift(2, ref.r);
        O.write_shift(2, ref.c);
        O.write_shift(2, ref.c);
        var clsid = "d0 c9 ea 79 f9 ba ce 11 8c 82 00 aa 00 4b a9 0b".split(" ");
        for (var i = 0; i < 16; ++i) O.write_shift(1, parseInt(clsid[i], 16));
        return bconcat([O, write_Hyperlink(hl[1])]);
    }
    /* [MS-XLS] 2.4.141 */
    function parse_HLinkTooltip(blob, length) {
        blob.read_shift(2);
        var ref = parse_Ref8U(blob, 8);
        var wzTooltip = blob.read_shift((length - 10) / 2, 'dbcs-cont');
        wzTooltip = wzTooltip.replace(chr0, "");
        return [ref, wzTooltip];
    }
    function write_HLinkTooltip(hl) {
        var TT = hl[1].Tooltip;
        var O = new_buf(10 + 2 * (TT.length + 1));
        O.write_shift(2, 0x0800);
        var ref = decode_cell(hl[0]);
        O.write_shift(2, ref.r);
        O.write_shift(2, ref.r);
        O.write_shift(2, ref.c);
        O.write_shift(2, ref.c);
        for (var i = 0; i < TT.length; ++i) O.write_shift(2, TT.charCodeAt(i));
        O.write_shift(2, 0);
        return O;
    }
    /* [MS-XLS] 2.4.63 */
    function parse_Country(blob) {
        var o = [0, 0],
            d;
        d = blob.read_shift(2);
        o[0] = CountryEnum[d] || d;
        d = blob.read_shift(2);
        o[1] = CountryEnum[d] || d;
        return o;
    }
    function write_Country(o) {
        if (!o) o = new_buf(4);
        o.write_shift(2, 0x01);
        o.write_shift(2, 0x01);
        return o;
    }
    /* [MS-XLS] 2.4.50 ClrtClient */
    function parse_ClrtClient(blob) {
        var ccv = blob.read_shift(2);
        var o = [];
        while (ccv-- > 0) o.push(parse_LongRGB(blob, 8));
        return o;
    }
    /* [MS-XLS] 2.4.188 */
    function parse_Palette(blob) {
        var ccv = blob.read_shift(2);
        var o = [];
        while (ccv-- > 0) o.push(parse_LongRGB(blob, 8));
        return o;
    }
    /* [MS-XLS] 2.4.354 */
    function parse_XFCRC(blob) {
        blob.l += 2;
        var o = {
            cxfs: 0,
            crc: 0
        };
        o.cxfs = blob.read_shift(2);
        o.crc = blob.read_shift(4);
        return o;
    }
    /* [MS-XLS] 2.4.53 TODO: parse flags */
    /* [MS-XLSB] 2.4.323 TODO: parse flags */
    function parse_ColInfo(blob, length, opts) {
        if (!opts.cellStyles) return parsenoop(blob, length);
        var w = opts && opts.biff >= 12 ? 4 : 2;
        var colFirst = blob.read_shift(w);
        var colLast = blob.read_shift(w);
        var coldx = blob.read_shift(w);
        var ixfe = blob.read_shift(w);
        var flags = blob.read_shift(2);
        if (w == 2) blob.l += 2;
        return {
            s: colFirst,
            e: colLast,
            w: coldx,
            ixfe: ixfe,
            flags: flags
        };
    }
    /* [MS-XLS] 2.4.257 */
    function parse_Setup(blob, length) {
        var o = {};
        if (length < 32) return o;
        blob.l += 16;
        o.header = parse_Xnum(blob, 8);
        o.footer = parse_Xnum(blob, 8);
        blob.l += 2;
        return o;
    }
    /* [MS-XLS] 2.4.261 */
    function parse_ShtProps(blob, length, opts) {
        var def = {
            area: false
        };
        if (opts.biff != 5) {
            blob.l += length;
            return def;
        }
        var d = blob.read_shift(1);
        blob.l += 3;
        if ((d & 0x10)) def.area = true;
        return def;
    }
    /* [MS-XLS] 2.4.241 */
    function write_RRTabId(n) {
        var out = new_buf(2 * n);
        for (var i = 0; i < n; ++i) out.write_shift(2, i + 1);
        return out;
    }
    var parse_Blank = parse_XLSCell; /* [MS-XLS] 2.4.20 Just the cell */
    var parse_Scl = parseuint16a; /* [MS-XLS] 2.4.247 num, den */
    var parse_String = parse_XLUnicodeString; /* [MS-XLS] 2.4.268 */
    /* --- Specific to versions before BIFF8 --- */
    function parse_ImData(blob) {
        var cf = blob.read_shift(2);
        var env = blob.read_shift(2);
        var lcb = blob.read_shift(4);
        var o = {
            fmt: cf,
            env: env,
            len: lcb,
            data: blob.slice(blob.l, blob.l + lcb)
        };
        blob.l += lcb;
        return o;
    }
    /* BIFF2_??? where ??? is the name from [XLS] */
    function parse_BIFF2STR(blob, length, opts) {
        var cell = parse_XLSCell(blob, 6);
        ++blob.l;
        var str = parse_XLUnicodeString2(blob, length - 7, opts);
        cell.t = 'str';
        cell.val = str;
        return cell;
    }
    function parse_BIFF2NUM(blob) {
        var cell = parse_XLSCell(blob, 6);
        ++blob.l;
        var num = parse_Xnum(blob, 8);
        cell.t = 'n';
        cell.val = num;
        return cell;
    }
    function write_BIFF2NUM(r, c, val) {
        var out = new_buf(15);
        write_BIFF2Cell(out, r, c);
        out.write_shift(8, val, 'f');
        return out;
    }
    function parse_BIFF2INT(blob) {
        var cell = parse_XLSCell(blob, 6);
        ++blob.l;
        var num = blob.read_shift(2);
        cell.t = 'n';
        cell.val = num;
        return cell;
    }
    function write_BIFF2INT(r, c, val) {
        var out = new_buf(9);
        write_BIFF2Cell(out, r, c);
        out.write_shift(2, val);
        return out;
    }
    function parse_BIFF2STRING(blob) {
        var cch = blob.read_shift(1);
        if (cch === 0) {
            blob.l++;
            return "";
        }
        return blob.read_shift(cch, 'sbcs-cont');
    }
    /* TODO: convert to BIFF8 font struct */
    function parse_BIFF2FONTXTRA(blob, length) {
        blob.l += 6; // unknown
        blob.l += 2; // font weight "bls"
        blob.l += 1; // charset
        blob.l += 3; // unknown
        blob.l += 1; // font family
        blob.l += length - 13;
    }
    /* TODO: parse rich text runs */
    function parse_RString(blob, length, opts) {
        var end = blob.l + length;
        var cell = parse_XLSCell(blob, 6);
        var cch = blob.read_shift(2);
        var str = parse_XLUnicodeStringNoCch(blob, cch, opts);
        blob.l = end;
        cell.t = 'str';
        cell.val = str;
        return cell;
    }
    /* from js-harb (C) 2014-present  SheetJS */
    var DBF = (function () {
        var dbf_codepage_map = {
            /* Code Pages Supported by Visual FoxPro */
            0x01: 437,
            0x02: 850,
            0x03: 1252,
            0x04: 10000,
            0x64: 852,
            0x65: 866,
            0x66: 865,
            0x67: 861,
            0x68: 895,
            0x69: 620,
            0x6A: 737,
            0x6B: 857,
            0x78: 950,
            0x79: 949,
            0x7A: 936,
            0x7B: 932,
            0x7C: 874,
            0x7D: 1255,
            0x7E: 1256,
            0x96: 10007,
            0x97: 10029,
            0x98: 10006,
            0xC8: 1250,
            0xC9: 1251,
            0xCA: 1254,
            0xCB: 1253,
            /* shapefile DBF extension */
            0x00: 20127,
            0x08: 865,
            0x09: 437,
            0x0A: 850,
            0x0B: 437,
            0x0D: 437,
            0x0E: 850,
            0x0F: 437,
            0x10: 850,
            0x11: 437,
            0x12: 850,
            0x13: 932,
            0x14: 850,
            0x15: 437,
            0x16: 850,
            0x17: 865,
            0x18: 437,
            0x19: 437,
            0x1A: 850,
            0x1B: 437,
            0x1C: 863,
            0x1D: 850,
            0x1F: 852,
            0x22: 852,
            0x23: 852,
            0x24: 860,
            0x25: 850,
            0x26: 866,
            0x37: 850,
            0x40: 852,
            0x4D: 936,
            0x4E: 949,
            0x4F: 950,
            0x50: 874,
            0x57: 1252,
            0x58: 1252,
            0x59: 1252,
            0xFF: 16969
        };
        /* TODO: find an actual specification */
        function dbf_to_aoa(buf, opts) {
            var out = [];
            /* TODO: browser based */
            var d = (new_raw_buf(1));
            switch (opts.type) {
                case 'base64':
                    d = s2a(Base64.decode(buf));
                    break;
                case 'binary':
                    d = s2a(buf);
                    break;
                case 'buffer':
                case 'array':
                    d = buf;
                    break;
            }
            prep_blob(d, 0);
            /* header */
            var ft = d.read_shift(1);
            var memo = false;
            var vfp = false,
                l7 = false;
            switch (ft) {
                case 0x02:
                case 0x03:
                    break;
                case 0x30:
                    vfp = true;
                    memo = true;
                    break;
                case 0x31:
                    vfp = true;
                    break;
                case 0x83:
                    memo = true;
                    break;
                case 0x8B:
                    memo = true;
                    break;
                case 0x8C:
                    memo = true;
                    l7 = true;
                    break;
                case 0xF5:
                    memo = true;
                    break;
                default:
                    throw new Error("DBF Unsupported Version: " + ft.toString(16));
            }
            var /*filedate = new Date(),*/ nrow = 0,
                fpos = 0;
            if (ft == 0x02) nrow = d.read_shift(2);
            /*filedate = new Date(d.read_shift(1) + 1900, d.read_shift(1) - 1, d.read_shift(1));*/
            d.l += 3;
            if (ft != 0x02) nrow = d.read_shift(4);
            if (ft != 0x02) fpos = d.read_shift(2);
            var rlen = d.read_shift(2);
            var /*flags = 0,*/ current_cp = 1252;
            if (ft != 0x02) {
                d.l += 16;
                /*flags = */
                d.read_shift(1);
                //if(memo && ((flags & 0x02) === 0)) throw new Error("DBF Flags " + flags.toString(16) + " ft " + ft.toString(16));
                /* codepage present in FoxPro */
                if (d[d.l] !== 0) current_cp = dbf_codepage_map[d[d.l]];
                d.l += 1;
                d.l += 2;
            }
            if (l7) d.l += 36;
            var fields = [],
                field = ({});
            var hend = fpos - 10 - (vfp ? 264 : 0),
                ww = l7 ? 32 : 11;
            while (ft == 0x02 ? d.l < d.length && d[d.l] != 0x0d : d.l < hend) {
                field = ({});
                field.name = cptable.utils.decode(current_cp, d.slice(d.l, d.l + ww)).replace(/[\u0000\r\n].*$/g, "");
                d.l += ww;
                field.type = String.fromCharCode(d.read_shift(1));
                if (ft != 0x02 && !l7) field.offset = d.read_shift(4);
                field.len = d.read_shift(1);
                if (ft == 0x02) field.offset = d.read_shift(2);
                field.dec = d.read_shift(1);
                if (field.name.length) fields.push(field);
                if (ft != 0x02) d.l += l7 ? 13 : 14;
                switch (field.type) {
                    case 'B': // VFP Double
                        if ((!vfp || field.len != 8) && opts.WTF) console.log('Skipping ' + field.name + ':' + field.type);
                        break;
                    case 'G': // General
                    case 'P': // Picture
                        if (opts.WTF) console.log('Skipping ' + field.name + ':' + field.type);
                        break;
                    case 'C': // character
                    case 'D': // date
                    case 'F': // floating point
                    case 'I': // long
                    case 'L': // boolean
                    case 'M': // memo
                    case 'N': // number
                    case 'O': // double
                    case 'T': // datetime
                    case 'Y': // currency
                    case '0': // VFP _NullFlags
                    case '@': // timestamp
                    case '+': // autoincrement
                        break;
                    default:
                        throw new Error('Unknown Field Type: ' + field.type);
                }
            }
            if (d[d.l] !== 0x0D) d.l = fpos - 1;
            else if (ft == 0x02) d.l = 0x209;
            if (ft != 0x02) {
                if (d.read_shift(1) !== 0x0D) throw new Error("DBF Terminator not found " + d.l + " " + d[d.l]);
                d.l = fpos;
            }
            /* data */
            var R = 0,
                C = 0;
            out[0] = [];
            for (C = 0; C != fields.length; ++C) out[0][C] = fields[C].name;
            while (nrow-- > 0) {
                if (d[d.l] === 0x2A) {
                    d.l += rlen;
                    continue;
                }
                ++d.l;
                out[++R] = [];
                C = 0;
                for (C = 0; C != fields.length; ++C) {
                    var dd = d.slice(d.l, d.l + fields[C].len);
                    d.l += fields[C].len;
                    prep_blob(dd, 0);
                    var s = cptable.utils.decode(current_cp, dd);
                    switch (fields[C].type) {
                        case 'C':
                            out[R][C] = cptable.utils.decode(current_cp, dd);
                            out[R][C] = out[R][C].trim();
                            break;
                        case 'D':
                            if (s.length === 8) out[R][C] = new Date(+s.slice(0, 4), +s.slice(4, 6) - 1, +s.slice(6, 8));
                            else out[R][C] = s;
                            break;
                        case 'F':
                            out[R][C] = parseFloat(s.trim());
                            break;
                        case '+':
                        case 'I':
                            out[R][C] = l7 ? dd.read_shift(-4, 'i') ^ 0x80000000 : dd.read_shift(4, 'i');
                            break;
                        case 'L':
                            switch (s.toUpperCase()) {
                                case 'Y':
                                case 'T':
                                    out[R][C] = true;
                                    break;
                                case 'N':
                                case 'F':
                                    out[R][C] = false;
                                    break;
                                case ' ':
                                case '?':
                                    out[R][C] = false;
                                    break; /* NOTE: technically uninitialized */
                                default:
                                    throw new Error("DBF Unrecognized L:|" + s + "|");
                            }
                            break;
                        case 'M':
                            /* TODO: handle memo files */
                            if (!memo) throw new Error("DBF Unexpected MEMO for type " + ft.toString(16));
                            out[R][C] = "##MEMO##" + (l7 ? parseInt(s.trim(), 10) : dd.read_shift(4));
                            break;
                        case 'N':
                            out[R][C] = +s.replace(/\u0000/g, "").trim();
                            break;
                        case '@':
                            out[R][C] = new Date(dd.read_shift(-8, 'f') - 0x388317533400);
                            break;
                        case 'T':
                            out[R][C] = new Date((dd.read_shift(4) - 0x253D8C) * 0x5265C00 + dd.read_shift(4));
                            break;
                        case 'Y':
                            out[R][C] = dd.read_shift(4, 'i') / 1e4;
                            break;
                        case 'O':
                            out[R][C] = -dd.read_shift(-8, 'f');
                            break;
                        case 'B':
                            if (vfp && fields[C].len == 8) {
                                out[R][C] = dd.read_shift(8, 'f');
                                break;
                            }
                            /* falls through */
                        case 'G':
                        case 'P':
                            dd.l += fields[C].len;
                            break;
                        case '0':
                            if (fields[C].name === '_NullFlags') break;
                            /* falls through */
                        default:
                            throw new Error("DBF Unsupported data type " + fields[C].type);
                    }
                }
            }
            if (ft != 0x02)
                if (d.l < d.length && d[d.l++] != 0x1A) throw new Error("DBF EOF Marker missing " + (d.l - 1) + " of " + d.length + " " + d[d.l - 1].toString(16));
            if (opts && opts.sheetRows) out = out.slice(0, opts.sheetRows);
            return out;
        }
        function dbf_to_sheet(buf, opts) {
            var o = opts || {};
            if (!o.dateNF) o.dateNF = "yyyymmdd";
            return aoa_to_sheet(dbf_to_aoa(buf, o), o);
        }
        function dbf_to_workbook(buf, opts) {
            try {
                return sheet_to_workbook(dbf_to_sheet(buf, opts), opts);
            } catch (e) {
                if (opts && opts.WTF) throw e;
            }
            return ({
                SheetNames: [],
                Sheets: {}
            });
        }
        var _RLEN = {
            'B': 8,
            'C': 250,
            'L': 1,
            'D': 8,
            '?': 0,
            '': 0
        };
        function sheet_to_dbf(ws, opts) {
            var o = opts || {};
            if (o.type == "string") throw new Error("Cannot write DBF to JS string");
            var ba = buf_array();
            var aoa = sheet_to_json(ws, {
                header: 1,
                cellDates: true
            });
            var headers = aoa[0],
                data = aoa.slice(1);
            var i = 0,
                j = 0,
                hcnt = 0,
                rlen = 1;
            for (i = 0; i < headers.length; ++i) {
                if (i == null) continue;
                ++hcnt;
                if (typeof headers[i] === 'number') headers[i] = headers[i].toString(10);
                if (typeof headers[i] !== 'string') throw new Error("DBF Invalid column name " + headers[i] + " |" + (typeof headers[i]) + "|");
                if (headers.indexOf(headers[i]) !== i)
                    for (j = 0; j < 1024; ++j)
                        if (headers.indexOf(headers[i] + "_" + j) == -1) {
                            headers[i] += "_" + j;
                            break;
                        }
            }
            var range = safe_decode_range(ws['!ref']);
            var coltypes = [];
            for (i = 0; i <= range.e.c - range.s.c; ++i) {
                var col = [];
                for (j = 0; j < data.length; ++j) {
                    if (data[j][i] != null) col.push(data[j][i]);
                }
                if (col.length == 0 || headers[i] == null) {
                    coltypes[i] = '?';
                    continue;
                }
                var guess = '',
                    _guess = '';
                for (j = 0; j < col.length; ++j) {
                    switch (typeof col[j]) {
                        /* TODO: check if L2 compat is desired */
                        case 'number':
                            _guess = 'B';
                            break;
                        case 'string':
                            _guess = 'C';
                            break;
                        case 'boolean':
                            _guess = 'L';
                            break;
                        case 'object':
                            _guess = col[j] instanceof Date ? 'D' : 'C';
                            break;
                        default:
                            _guess = 'C';
                    }
                    guess = guess && guess != _guess ? 'C' : _guess;
                    if (guess == 'C') break;
                }
                rlen += _RLEN[guess] || 0;
                coltypes[i] = guess;
            }
            var h = ba.next(32);
            h.write_shift(4, 0x13021130);
            h.write_shift(4, data.length);
            h.write_shift(2, 296 + 32 * hcnt);
            h.write_shift(2, rlen);
            for (i = 0; i < 4; ++i) h.write_shift(4, 0);
            h.write_shift(4, 0x00000300); // TODO: CP
            for (i = 0, j = 0; i < headers.length; ++i) {
                if (headers[i] == null) continue;
                var hf = ba.next(32);
                var _f = (headers[i].slice(-10) + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00").slice(0, 11);
                hf.write_shift(1, _f, "sbcs");
                hf.write_shift(1, coltypes[i] == '?' ? 'C' : coltypes[i], "sbcs");
                hf.write_shift(4, j);
                hf.write_shift(1, _RLEN[coltypes[i]] || 0);
                hf.write_shift(1, 0);
                hf.write_shift(1, 0x02);
                hf.write_shift(4, 0);
                hf.write_shift(1, 0);
                hf.write_shift(4, 0);
                hf.write_shift(4, 0);
                j += _RLEN[coltypes[i]] || 0;
            }
            var hb = ba.next(264);
            hb.write_shift(4, 0x0000000D);
            for (i = 0; i < 65; ++i) hb.write_shift(4, 0x00000000);
            for (i = 0; i < data.length; ++i) {
                var rout = ba.next(rlen);
                rout.write_shift(1, 0);
                for (j = 0; j < headers.length; ++j) {
                    if (headers[j] == null) continue;
                    switch (coltypes[j]) {
                        case 'L':
                            rout.write_shift(1, data[i][j] == null ? 0x3F : data[i][j] ? 0x54 : 0x46);
                            break;
                        case 'B':
                            rout.write_shift(8, data[i][j] || 0, 'f');
                            break;
                        case 'D':
                            if (!data[i][j]) rout.write_shift(8, "00000000", "sbcs");
                            else {
                                rout.write_shift(4, ("0000" + data[i][j].getFullYear()).slice(-4), "sbcs");
                                rout.write_shift(2, ("00" + (data[i][j].getMonth() + 1)).slice(-2), "sbcs");
                                rout.write_shift(2, ("00" + data[i][j].getDate()).slice(-2), "sbcs");
                            }
                            break;
                        case 'C':
                            var _s = String(data[i][j] || "");
                            rout.write_shift(1, _s, "sbcs");
                            for (hcnt = 0; hcnt < 250 - _s.length; ++hcnt) rout.write_shift(1, 0x20);
                            break;
                    }
                }
                // data
            }
            ba.next(1).write_shift(1, 0x1A);
            return ba.end();
        }
        return {
            to_workbook: dbf_to_workbook,
            to_sheet: dbf_to_sheet,
            from_sheet: sheet_to_dbf
        };
    })();
    var SYLK = (function () {
        /* TODO: find an actual specification */
        function sylk_to_aoa(d, opts) {
            switch (opts.type) {
                case 'base64':
                    return sylk_to_aoa_str(Base64.decode(d), opts);
                case 'binary':
                    return sylk_to_aoa_str(d, opts);
                case 'buffer':
                    return sylk_to_aoa_str(d.toString('binary'), opts);
                case 'array':
                    return sylk_to_aoa_str(cc2str(d), opts);
            }
            throw new Error("Unrecognized type " + opts.type);
        }
        function sylk_to_aoa_str(str, opts) {
            var records = str.split(/[\n\r]+/),
                R = -1,
                C = -1,
                ri = 0,
                rj = 0,
                arr = [];
            var formats = [];
            var next_cell_format = null;
            var sht = {},
                rowinfo = [],
                colinfo = [],
                cw = [];
            var Mval = 0,
                j;
            for (; ri !== records.length; ++ri) {
                Mval = 0;
                var rstr = records[ri].trim();
                var record = rstr.replace(/;;/g, "\u0001").split(";").map(function (x) {
                    return x.replace(/\u0001/g, ";");
                });
                var RT = record[0],
                    val;
                if (rstr.length > 0) switch (RT) {
                    case 'ID':
                        break; /* header */
                    case 'E':
                        break; /* EOF */
                    case 'B':
                        break; /* dimensions */
                    case 'O':
                        break; /* options? */
                    case 'P':
                        if (record[1].charAt(0) == 'P')
                            formats.push(rstr.slice(3).replace(/;;/g, ";"));
                        break;
                    case 'C':
                        var C_seen_K = false,
                            C_seen_X = false;
                        for (rj = 1; rj < record.length; ++rj) switch (record[rj].charAt(0)) {
                            case 'X':
                                C = parseInt(record[rj].slice(1)) - 1;
                                C_seen_X = true;
                                break;
                            case 'Y':
                                R = parseInt(record[rj].slice(1)) - 1;
                                if (!C_seen_X) C = 0;
                                for (j = arr.length; j <= R; ++j) arr[j] = [];
                                break;
                            case 'K':
                                val = record[rj].slice(1);
                                if (val.charAt(0) === '"') val = val.slice(1, val.length - 1);
                                else if (val === 'TRUE') val = true;
                                else if (val === 'FALSE') val = false;
                                else if (!isNaN(fuzzynum(val))) {
                                    val = fuzzynum(val);
                                    if (next_cell_format !== null && SSF.is_date(next_cell_format)) val = numdate(val);
                                } else if (!isNaN(fuzzydate(val).getDate())) {
                                    val = parseDate(val);
                                }
                                if (typeof cptable !== 'undefined' && typeof val == "string" && ((opts || {}).type != "string") && (opts || {}).codepage) val = cptable.utils.decode(opts.codepage, val);
                                C_seen_K = true;
                                break;
                            case 'E':
                                var formula = rc_to_a1(record[rj].slice(1), {
                                    r: R,
                                    c: C
                                });
                                arr[R][C] = [arr[R][C], formula];
                                break;
                            default:
                                if (opts && opts.WTF) throw new Error("SYLK bad record " + rstr);
                        }
                        if (C_seen_K) {
                            arr[R][C] = val;
                            next_cell_format = null;
                        }
                        break;
                    case 'F':
                        var F_seen = 0;
                        for (rj = 1; rj < record.length; ++rj) switch (record[rj].charAt(0)) {
                            case 'X':
                                C = parseInt(record[rj].slice(1)) - 1;
                                ++F_seen;
                                break;
                            case 'Y':
                                R = parseInt(record[rj].slice(1)) - 1; /*C = 0;*/
                                for (j = arr.length; j <= R; ++j) arr[j] = [];
                                break;
                            case 'M':
                                Mval = parseInt(record[rj].slice(1)) / 20;
                                break;
                            case 'F':
                                break; /* ??? */
                            case 'G':
                                break; /* hide grid */
                            case 'P':
                                next_cell_format = formats[parseInt(record[rj].slice(1))];
                                break;
                            case 'S':
                                break; /* cell style */
                            case 'D':
                                break; /* column */
                            case 'N':
                                break; /* font */
                            case 'W':
                                cw = record[rj].slice(1).split(" ");
                                for (j = parseInt(cw[0], 10); j <= parseInt(cw[1], 10); ++j) {
                                    Mval = parseInt(cw[2], 10);
                                    colinfo[j - 1] = Mval === 0 ? {
                                        hidden: true
                                    } : {
                                        wch: Mval
                                    };
                                    process_col(colinfo[j - 1]);
                                }
                                break;
                            case 'C':
                                /* default column format */
                                C = parseInt(record[rj].slice(1)) - 1;
                                if (!colinfo[C]) colinfo[C] = {};
                                break;
                            case 'R':
                                /* row properties */
                                R = parseInt(record[rj].slice(1)) - 1;
                                if (!rowinfo[R]) rowinfo[R] = {};
                                if (Mval > 0) {
                                    rowinfo[R].hpt = Mval;
                                    rowinfo[R].hpx = pt2px(Mval);
                                } else if (Mval === 0) rowinfo[R].hidden = true;
                                break;
                            default:
                                if (opts && opts.WTF) throw new Error("SYLK bad record " + rstr);
                        }
                        if (F_seen < 1) next_cell_format = null;
                        break;
                    default:
                        if (opts && opts.WTF) throw new Error("SYLK bad record " + rstr);
                }
            }
            if (rowinfo.length > 0) sht['!rows'] = rowinfo;
            if (colinfo.length > 0) sht['!cols'] = colinfo;
            if (opts && opts.sheetRows) arr = arr.slice(0, opts.sheetRows);
            return [arr, sht];
        }
        function sylk_to_sheet(d, opts) {
            var aoasht = sylk_to_aoa(d, opts);
            var aoa = aoasht[0],
                ws = aoasht[1];
            var o = aoa_to_sheet(aoa, opts);
            keys(ws).forEach(function (k) {
                o[k] = ws[k];
            });
            return o;
        }
        function sylk_to_workbook(d, opts) {
            return sheet_to_workbook(sylk_to_sheet(d, opts), opts);
        }
        function write_ws_cell_sylk(cell, ws, R, C) {
            var o = "C;Y" + (R + 1) + ";X" + (C + 1) + ";K";
            switch (cell.t) {
                case 'n':
                    o += (cell.v || 0);
                    if (cell.f && !cell.F) o += ";E" + a1_to_rc(cell.f, {
                        r: R,
                        c: C
                    });
                    break;
                case 'b':
                    o += cell.v ? "TRUE" : "FALSE";
                    break;
                case 'e':
                    o += cell.w || cell.v;
                    break;
                case 'd':
                    o += '"' + (cell.w || cell.v) + '"';
                    break;
                case 's':
                    o += '"' + cell.v.replace(/"/g, "") + '"';
                    break;
            }
            return o;
        }
        function write_ws_cols_sylk(out, cols) {
            cols.forEach(function (col, i) {
                var rec = "F;W" + (i + 1) + " " + (i + 1) + " ";
                if (col.hidden) rec += "0";
                else {
                    if (typeof col.width == 'number') col.wpx = width2px(col.width);
                    if (typeof col.wpx == 'number') col.wch = px2char(col.wpx);
                    if (typeof col.wch == 'number') rec += Math.round(col.wch);
                }
                if (rec.charAt(rec.length - 1) != " ") out.push(rec);
            });
        }
        function write_ws_rows_sylk(out, rows) {
            rows.forEach(function (row, i) {
                var rec = "F;";
                if (row.hidden) rec += "M0;";
                else if (row.hpt) rec += "M" + 20 * row.hpt + ";";
                else if (row.hpx) rec += "M" + 20 * px2pt(row.hpx) + ";";
                if (rec.length > 2) out.push(rec + "R" + (i + 1));
            });
        }
        function sheet_to_sylk(ws, opts) {
            var preamble = ["ID;PWXL;N;E"],
                o = [];
            var r = safe_decode_range(ws['!ref']),
                cell;
            var dense = Array.isArray(ws);
            var RS = "\r\n";
            preamble.push("P;PGeneral");
            preamble.push("F;P0;DG0G8;M255");
            if (ws['!cols']) write_ws_cols_sylk(preamble, ws['!cols']);
            if (ws['!rows']) write_ws_rows_sylk(preamble, ws['!rows']);
            preamble.push("B;Y" + (r.e.r - r.s.r + 1) + ";X" + (r.e.c - r.s.c + 1) + ";D" + [r.s.c, r.s.r, r.e.c, r.e.r].join(" "));
            for (var R = r.s.r; R <= r.e.r; ++R) {
                for (var C = r.s.c; C <= r.e.c; ++C) {
                    var coord = encode_cell({
                        r: R,
                        c: C
                    });
                    cell = dense ? (ws[R] || [])[C] : ws[coord];
                    if (!cell || (cell.v == null && (!cell.f || cell.F))) continue;
                    o.push(write_ws_cell_sylk(cell, ws, R, C, opts));
                }
            }
            return preamble.join(RS) + RS + o.join(RS) + RS + "E" + RS;
        }
        return {
            to_workbook: sylk_to_workbook,
            to_sheet: sylk_to_sheet,
            from_sheet: sheet_to_sylk
        };
    })();
    var DIF = (function () {
        function dif_to_aoa(d, opts) {
            switch (opts.type) {
                case 'base64':
                    return dif_to_aoa_str(Base64.decode(d), opts);
                case 'binary':
                    return dif_to_aoa_str(d, opts);
                case 'buffer':
                    return dif_to_aoa_str(d.toString('binary'), opts);
                case 'array':
                    return dif_to_aoa_str(cc2str(d), opts);
            }
            throw new Error("Unrecognized type " + opts.type);
        }
        function dif_to_aoa_str(str, opts) {
            var records = str.split('\n'),
                R = -1,
                C = -1,
                ri = 0,
                arr = [];
            for (; ri !== records.length; ++ri) {
                if (records[ri].trim() === 'BOT') {
                    arr[++R] = [];
                    C = 0;
                    continue;
                }
                if (R < 0) continue;
                var metadata = records[ri].trim().split(",");
                var type = metadata[0],
                    value = metadata[1];
                ++ri;
                var data = records[ri].trim();
                switch (+type) {
                    case -1:
                        if (data === 'BOT') {
                            arr[++R] = [];
                            C = 0;
                            continue;
                        } else if (data !== 'EOD') throw new Error("Unrecognized DIF special command " + data);
                        break;
                    case 0:
                        if (data === 'TRUE') arr[R][C] = true;
                        else if (data === 'FALSE') arr[R][C] = false;
                        else if (!isNaN(fuzzynum(value))) arr[R][C] = fuzzynum(value);
                        else if (!isNaN(fuzzydate(value).getDate())) arr[R][C] = parseDate(value);
                        else arr[R][C] = value;
                        ++C;
                        break;
                    case 1:
                        data = data.slice(1, data.length - 1);
                        arr[R][C++] = data !== '' ? data : null;
                        break;
                }
                if (data === 'EOD') break;
            }
            if (opts && opts.sheetRows) arr = arr.slice(0, opts.sheetRows);
            return arr;
        }
        function dif_to_sheet(str, opts) {
            return aoa_to_sheet(dif_to_aoa(str, opts), opts);
        }
        function dif_to_workbook(str, opts) {
            return sheet_to_workbook(dif_to_sheet(str, opts), opts);
        }
        var sheet_to_dif = (function () {
            var push_field = function pf(o, topic, v, n, s) {
                o.push(topic);
                o.push(v + "," + n);
                o.push('"' + s.replace(/"/g, '""') + '"');
            };
            var push_value = function po(o, type, v, s) {
                o.push(type + "," + v);
                o.push(type == 1 ? '"' + s.replace(/"/g, '""') + '"' : s);
            };
            return function sheet_to_dif(ws) {
                var o = [];
                var r = safe_decode_range(ws['!ref']),
                    cell;
                var dense = Array.isArray(ws);
                push_field(o, "TABLE", 0, 1, "sheetjs");
                push_field(o, "VECTORS", 0, r.e.r - r.s.r + 1, "");
                push_field(o, "TUPLES", 0, r.e.c - r.s.c + 1, "");
                push_field(o, "DATA", 0, 0, "");
                for (var R = r.s.r; R <= r.e.r; ++R) {
                    push_value(o, -1, 0, "BOT");
                    for (var C = r.s.c; C <= r.e.c; ++C) {
                        var coord = encode_cell({
                            r: R,
                            c: C
                        });
                        cell = dense ? (ws[R] || [])[C] : ws[coord];
                        if (!cell) {
                            push_value(o, 1, 0, "");
                            continue;
                        }
                        switch (cell.t) {
                            case 'n':
                                var val = DIF_XL ? cell.w : cell.v;
                                if (!val && cell.v != null) val = cell.v;
                                if (val == null) {
                                    if (DIF_XL && cell.f && !cell.F) push_value(o, 1, 0, "=" + cell.f);
                                    else push_value(o, 1, 0, "");
                                } else push_value(o, 0, val, "V");
                                break;
                            case 'b':
                                push_value(o, 0, cell.v ? 1 : 0, cell.v ? "TRUE" : "FALSE");
                                break;
                            case 's':
                                push_value(o, 1, 0, (!DIF_XL || isNaN(cell.v)) ? cell.v : '="' + cell.v + '"');
                                break;
                            case 'd':
                                if (!cell.w) cell.w = SSF.format(cell.z || SSF._table[14], datenum(parseDate(cell.v)));
                                if (DIF_XL) push_value(o, 0, cell.w, "V");
                                else push_value(o, 1, 0, cell.w);
                                break;
                            default:
                                push_value(o, 1, 0, "");
                        }
                    }
                }
                push_value(o, -1, 0, "EOD");
                var RS = "\r\n";
                var oo = o.join(RS);
                //while((oo.length & 0x7F) != 0) oo += "\0";
                return oo;
            };
        })();
        return {
            to_workbook: dif_to_workbook,
            to_sheet: dif_to_sheet,
            from_sheet: sheet_to_dif
        };
    })();
    var ETH = (function () {
        function decode(s) {
            return s.replace(/\\b/g, "\\").replace(/\\c/g, ":").replace(/\\n/g, "\n");
        }
        function encode(s) {
            return s.replace(/\\/g, "\\b").replace(/:/g, "\\c").replace(/\n/g, "\\n");
        }
        function eth_to_aoa(str, opts) {
            var records = str.split('\n'),
                R = -1,
                C = -1,
                ri = 0,
                arr = [];
            for (; ri !== records.length; ++ri) {
                var record = records[ri].trim().split(":");
                if (record[0] !== 'cell') continue;
                var addr = decode_cell(record[1]);
                if (arr.length <= addr.r)
                    for (R = arr.length; R <= addr.r; ++R)
                        if (!arr[R]) arr[R] = [];
                R = addr.r;
                C = addr.c;
                switch (record[2]) {
                    case 't':
                        arr[R][C] = decode(record[3]);
                        break;
                    case 'v':
                        arr[R][C] = +record[3];
                        break;
                    case 'vtf':
                        var _f = record[record.length - 1];
                        /* falls through */
                    case 'vtc':
                        switch (record[3]) {
                            case 'nl':
                                arr[R][C] = +record[4] ? true : false;
                                break;
                            default:
                                arr[R][C] = +record[4];
                                break;
                        }
                        if (record[2] == 'vtf') arr[R][C] = [arr[R][C], _f];
                }
            }
            if (opts && opts.sheetRows) arr = arr.slice(0, opts.sheetRows);
            return arr;
        }
        function eth_to_sheet(d, opts) {
            return aoa_to_sheet(eth_to_aoa(d, opts), opts);
        }
        function eth_to_workbook(d, opts) {
            return sheet_to_workbook(eth_to_sheet(d, opts), opts);
        }
        var header = [
            "socialcalc:version:1.5",
            "MIME-Version: 1.0",
            "Content-Type: multipart/mixed; boundary=SocialCalcSpreadsheetControlSave"
        ].join("\n");
        var sep = [
            "--SocialCalcSpreadsheetControlSave",
            "Content-type: text/plain; charset=UTF-8"
        ].join("\n") + "\n";
        /* TODO: the other parts */
        var meta = [
            "# SocialCalc Spreadsheet Control Save",
            "part:sheet"
        ].join("\n");
        var end = "--SocialCalcSpreadsheetControlSave--";
        function sheet_to_eth_data(ws) {
            if (!ws || !ws['!ref']) return "";
            var o = [],
                oo = [],
                cell, coord = "";
            var r = decode_range(ws['!ref']);
            var dense = Array.isArray(ws);
            for (var R = r.s.r; R <= r.e.r; ++R) {
                for (var C = r.s.c; C <= r.e.c; ++C) {
                    coord = encode_cell({
                        r: R,
                        c: C
                    });
                    cell = dense ? (ws[R] || [])[C] : ws[coord];
                    if (!cell || cell.v == null || cell.t === 'z') continue;
                    oo = ["cell", coord, 't'];
                    switch (cell.t) {
                        case 's':
                        case 'str':
                            oo.push(encode(cell.v));
                            break;
                        case 'n':
                            if (!cell.f) {
                                oo[2] = 'v';
                                oo[3] = cell.v;
                            } else {
                                oo[2] = 'vtf';
                                oo[3] = 'n';
                                oo[4] = cell.v;
                                oo[5] = encode(cell.f);
                            }
                            break;
                        case 'b':
                            oo[2] = 'vt' + (cell.f ? 'f' : 'c');
                            oo[3] = 'nl';
                            oo[4] = cell.v ? "1" : "0";
                            oo[5] = encode(cell.f || (cell.v ? 'TRUE' : 'FALSE'));
                            break;
                        case 'd':
                            var t = datenum(parseDate(cell.v));
                            oo[2] = 'vtc';
                            oo[3] = 'nd';
                            oo[4] = "" + t;
                            oo[5] = cell.w || SSF.format(cell.z || SSF._table[14], t);
                            break;
                        case 'e':
                            continue;
                    }
                    o.push(oo.join(":"));
                }
            }
            o.push("sheet:c:" + (r.e.c - r.s.c + 1) + ":r:" + (r.e.r - r.s.r + 1) + ":tvf:1");
            o.push("valueformat:1:text-wiki");
            //o.push("copiedfrom:" + ws['!ref']); // clipboard only
            return o.join("\n");
        }
        function sheet_to_eth(ws) {
            return [header, sep, meta, sep, sheet_to_eth_data(ws), end].join("\n");
            // return ["version:1.5", sheet_to_eth_data(ws)].join("\n"); // clipboard form
        }
        return {
            to_workbook: eth_to_workbook,
            to_sheet: eth_to_sheet,
            from_sheet: sheet_to_eth
        };
    })();
    var PRN = (function () {
        function set_text_arr(data, arr, R, C, o) {
            if (o.raw) arr[R][C] = data;
            else if (data === 'TRUE') arr[R][C] = true;
            else if (data === 'FALSE') arr[R][C] = false;
            else if (data === "") { /* empty */ } else if (!isNaN(fuzzynum(data))) arr[R][C] = fuzzynum(data);
            else if (!isNaN(fuzzydate(data).getDate())) arr[R][C] = parseDate(data);
            else arr[R][C] = data;
        }
        function prn_to_aoa_str(f, opts) {
            var o = opts || {};
            var arr = ([]);
            if (!f || f.length === 0) return arr;
            var lines = f.split(/[\r\n]/);
            var L = lines.length - 1;
            while (L >= 0 && lines[L].length === 0) --L;
            var start = 10,
                idx = 0;
            var R = 0;
            for (; R <= L; ++R) {
                idx = lines[R].indexOf(" ");
                if (idx == -1) idx = lines[R].length;
                else idx++;
                start = Math.max(start, idx);
            }
            for (R = 0; R <= L; ++R) {
                arr[R] = [];
                /* TODO: confirm that widths are always 10 */
                var C = 0;
                set_text_arr(lines[R].slice(0, start).trim(), arr, R, C, o);
                for (C = 1; C <= (lines[R].length - start) / 10 + 1; ++C)
                    set_text_arr(lines[R].slice(start + (C - 1) * 10, start + C * 10).trim(), arr, R, C, o);
            }
            if (o.sheetRows) arr = arr.slice(0, o.sheetRows);
            return arr;
        }
        // List of accepted CSV separators
        var guess_seps = {
            0x2C: ',',
            0x09: "\t",
            0x3B: ';'
        };
        // CSV separator weights to be used in case of equal numbers
        var guess_sep_weights = {
            0x2C: 3,
            0x09: 2,
            0x3B: 1
        };
        function guess_sep(str) {
            var cnt = {},
                instr = false,
                end = 0,
                cc = 0;
            for (; end < str.length; ++end) {
                if ((cc = str.charCodeAt(end)) == 0x22) instr = !instr;
                else if (!instr && cc in guess_seps) cnt[cc] = (cnt[cc] || 0) + 1;
            }
            cc = [];
            for (end in cnt)
                if (cnt.hasOwnProperty(end)) {
                    cc.push([cnt[end], end]);
                }
            if (!cc.length) {
                cnt = guess_sep_weights;
                for (end in cnt)
                    if (cnt.hasOwnProperty(end)) {
                        cc.push([cnt[end], end]);
                    }
            }
            cc.sort(function (a, b) {
                return a[0] - b[0] || guess_sep_weights[a[1]] - guess_sep_weights[b[1]];
            });
            return guess_seps[cc.pop()[1]];
        }
        function dsv_to_sheet_str(str, opts) {
            var o = opts || {};
            var sep = "";
            if (DENSE != null && o.dense == null) o.dense = DENSE;
            var ws = o.dense ? ([]) : ({});
            var range = ({
                s: {
                    c: 0,
                    r: 0
                },
                e: {
                    c: 0,
                    r: 0
                }
            });
            if (str.slice(0, 4) == "sep=" && str.charCodeAt(5) == 10) {
                sep = str.charAt(4);
                str = str.slice(6);
            } else sep = guess_sep(str.slice(0, 1024));
            var R = 0,
                C = 0,
                v = 0;
            var start = 0,
                end = 0,
                sepcc = sep.charCodeAt(0),
                instr = false,
                cc = 0;
            str = str.replace(/\r\n/mg, "\n");
            var _re = o.dateNF != null ? dateNF_regex(o.dateNF) : null;
            function finish_cell() {
                var s = str.slice(start, end);
                var cell = ({});
                if (s.charAt(0) == '"' && s.charAt(s.length - 1) == '"') s = s.slice(1, -1).replace(/""/g, '"');
                if (s.length === 0) cell.t = 'z';
                else if (o.raw) {
                    cell.t = 's';
                    cell.v = s;
                } else if (s.trim().length === 0) {
                    cell.t = 's';
                    cell.v = s;
                } else if (s.charCodeAt(0) == 0x3D) {
                    if (s.charCodeAt(1) == 0x22 && s.charCodeAt(s.length - 1) == 0x22) {
                        cell.t = 's';
                        cell.v = s.slice(2, -1).replace(/""/g, '"');
                    } else if (fuzzyfmla(s)) {
                        cell.t = 'n';
                        cell.f = s.slice(1);
                    } else {
                        cell.t = 's';
                        cell.v = s;
                    }
                } else if (s == "TRUE") {
                    cell.t = 'b';
                    cell.v = true;
                } else if (s == "FALSE") {
                    cell.t = 'b';
                    cell.v = false;
                } else if (!isNaN(v = fuzzynum(s))) {
                    cell.t = 'n';
                    if (o.cellText !== false) cell.w = s;
                    cell.v = v;
                } else if (!isNaN(fuzzydate(s).getDate()) || _re && s.match(_re)) {
                    cell.z = o.dateNF || SSF._table[14];
                    var k = 0;
                    if (_re && s.match(_re)) {
                        s = dateNF_fix(s, o.dateNF, (s.match(_re) || []));
                        k = 1;
                    }
                    if (o.cellDates) {
                        cell.t = 'd';
                        cell.v = parseDate(s, k);
                    } else {
                        cell.t = 'n';
                        cell.v = datenum(parseDate(s, k));
                    }
                    if (o.cellText !== false) cell.w = SSF.format(cell.z, cell.v instanceof Date ? datenum(cell.v) : cell.v);
                    if (!o.cellNF) delete cell.z;
                } else {
                    cell.t = 's';
                    cell.v = s;
                }
                if (cell.t == 'z') {} else if (o.dense) {
                    if (!ws[R]) ws[R] = [];
                    ws[R][C] = cell;
                } else ws[encode_cell({
                    c: C,
                    r: R
                })] = cell;
                start = end + 1;
                if (range.e.c < C) range.e.c = C;
                if (range.e.r < R) range.e.r = R;
                if (cc == sepcc) ++C;
                else {
                    C = 0;
                    ++R;
                    if (o.sheetRows && o.sheetRows <= R) return true;
                }
            }
            outer: for (; end < str.length; ++end) switch ((cc = str.charCodeAt(end))) {
                case 0x22:
                    instr = !instr;
                    break;
                case sepcc:
                case 0x0a:
                case 0x0d:
                    if (!instr && finish_cell()) break outer;
                    break;
                default:
                    break;
            }
            if (end - start > 0) finish_cell();
            ws['!ref'] = encode_range(range);
            return ws;
        }
        function prn_to_sheet_str(str, opts) {
            if (str.slice(0, 4) == "sep=") return dsv_to_sheet_str(str, opts);
            if (str.indexOf("\t") >= 0 || str.indexOf(",") >= 0 || str.indexOf(";") >= 0) return dsv_to_sheet_str(str, opts);
            return aoa_to_sheet(prn_to_aoa_str(str, opts), opts);
        }
        function prn_to_sheet(d, opts) {
            var str = "",
                bytes = opts.type == 'string' ? [0, 0, 0, 0] : firstbyte(d, opts);
            switch (opts.type) {
                case 'base64':
                    str = Base64.decode(d);
                    break;
                case 'binary':
                    str = d;
                    break;
                case 'buffer':
                    if (opts.codepage == 65001) str = d.toString('utf8');
                    else if (opts.codepage && typeof cptable !== 'undefined') str = cptable.utils.decode(opts.codepage, d);
                    else str = d.toString('binary');
                    break;
                case 'array':
                    str = cc2str(d);
                    break;
                case 'string':
                    str = d;
                    break;
                default:
                    throw new Error("Unrecognized type " + opts.type);
            }
            if (bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF) str = utf8read(str.slice(3));
            else if ((opts.type == 'binary') && typeof cptable !== 'undefined' && opts.codepage) str = cptable.utils.decode(opts.codepage, cptable.utils.encode(1252, str));
            if (str.slice(0, 19) == "socialcalc:version:") return ETH.to_sheet(opts.type == 'string' ? str : utf8read(str), opts);
            return prn_to_sheet_str(str, opts);
        }
        function prn_to_workbook(d, opts) {
            return sheet_to_workbook(prn_to_sheet(d, opts), opts);
        }
        function sheet_to_prn(ws) {
            var o = [];
            var r = safe_decode_range(ws['!ref']),
                cell;
            var dense = Array.isArray(ws);
            for (var R = r.s.r; R <= r.e.r; ++R) {
                var oo = [];
                for (var C = r.s.c; C <= r.e.c; ++C) {
                    var coord = encode_cell({
                        r: R,
                        c: C
                    });
                    cell = dense ? (ws[R] || [])[C] : ws[coord];
                    if (!cell || cell.v == null) {
                        oo.push("          ");
                        continue;
                    }
                    var w = (cell.w || (format_cell(cell), cell.w) || "").slice(0, 10);
                    while (w.length < 10) w += " ";
                    oo.push(w + (C === 0 ? " " : ""));
                }
                o.push(oo.join(""));
            }
            return o.join("\n");
        }
        return {
            to_workbook: prn_to_workbook,
            to_sheet: prn_to_sheet,
            from_sheet: sheet_to_prn
        };
    })();
    /* Excel defaults to SYLK but warns if data is not valid */
    function read_wb_ID(d, opts) {
        var o = opts || {},
            OLD_WTF = !!o.WTF;
        o.WTF = true;
        try {
            var out = SYLK.to_workbook(d, o);
            o.WTF = OLD_WTF;
            return out;
        } catch (e) {
            o.WTF = OLD_WTF;
            if (!e.message.match(/SYLK bad record ID/) && OLD_WTF) throw e;
            return PRN.to_workbook(d, opts);
        }
    }
    var WK_ = (function () {
        function lotushopper(data, cb, opts) {
            if (!data) return;
            prep_blob(data, data.l || 0);
            var Enum = opts.Enum || WK1Enum;
            while (data.l < data.length) {
                var RT = data.read_shift(2);
                var R = Enum[RT] || Enum[0xFF];
                var length = data.read_shift(2);
                var tgt = data.l + length;
                var d = (R.f || parsenoop)(data, length, opts);
                data.l = tgt;
                if (cb(d, R.n, RT)) return;
            }
        }
        function lotus_to_workbook(d, opts) {
            switch (opts.type) {
                case 'base64':
                    return lotus_to_workbook_buf(s2a(Base64.decode(d)), opts);
                case 'binary':
                    return lotus_to_workbook_buf(s2a(d), opts);
                case 'buffer':
                case 'array':
                    return lotus_to_workbook_buf(d, opts);
            }
            throw "Unsupported type " + opts.type;
        }
        function lotus_to_workbook_buf(d, opts) {
            if (!d) return d;
            var o = opts || {};
            if (DENSE != null && o.dense == null) o.dense = DENSE;
            var s = ((o.dense ? [] : {})),
                n = "Sheet1",
                sidx = 0;
            var sheets = {},
                snames = [n];
            var refguess = {
                s: {
                    r: 0,
                    c: 0
                },
                e: {
                    r: 0,
                    c: 0
                }
            };
            var sheetRows = o.sheetRows || 0;
            if (d[2] == 0x02) o.Enum = WK1Enum;
            else if (d[2] == 0x1a) o.Enum = WK3Enum;
            else if (d[2] == 0x0e) {
                o.Enum = WK3Enum;
                o.qpro = true;
                d.l = 0;
            } else throw new Error("Unrecognized LOTUS BOF " + d[2]);
            lotushopper(d, function (val, Rn, RT) {
                if (d[2] == 0x02) switch (RT) {
                    case 0x00:
                        o.vers = val;
                        if (val >= 0x1000) o.qpro = true;
                        break;
                    case 0x06:
                        refguess = val;
                        break; /* RANGE */
                    case 0x0F:
                        /* LABEL */
                        if (!o.qpro) val[1].v = val[1].v.slice(1);
                        /* falls through */
                    case 0x0D:
                        /* INTEGER */
                    case 0x0E:
                        /* NUMBER */
                    case 0x10:
                        /* FORMULA */
                    case 0x33:
                        /* STRING */
                        /* TODO: actual translation of the format code */
                        if (RT == 0x0E && (val[2] & 0x70) == 0x70 && (val[2] & 0x0F) > 1 && (val[2] & 0x0F) < 15) {
                            val[1].z = o.dateNF || SSF._table[14];
                            if (o.cellDates) {
                                val[1].t = 'd';
                                val[1].v = numdate(val[1].v);
                            }
                        }
                        if (o.dense) {
                            if (!s[val[0].r]) s[val[0].r] = [];
                            s[val[0].r][val[0].c] = val[1];
                        } else s[encode_cell(val[0])] = val[1];
                        break;
                } else switch (RT) {
                    case 0x16:
                        /* LABEL16 */
                        val[1].v = val[1].v.slice(1);
                        /* falls through */
                    case 0x17:
                        /* NUMBER17 */
                    case 0x18:
                        /* NUMBER18 */
                    case 0x19:
                        /* FORMULA19 */
                    case 0x25:
                        /* NUMBER25 */
                    case 0x27:
                        /* NUMBER27 */
                    case 0x28:
                        /* FORMULA28 */
                        if (val[3] > sidx) {
                            s["!ref"] = encode_range(refguess);
                            sheets[n] = s;
                            s = (o.dense ? [] : {});
                            refguess = {
                                s: {
                                    r: 0,
                                    c: 0
                                },
                                e: {
                                    r: 0,
                                    c: 0
                                }
                            };
                            sidx = val[3];
                            n = "Sheet" + (sidx + 1);
                            snames.push(n);
                        }
                        if (sheetRows > 0 && val[0].r >= sheetRows) break;
                        if (o.dense) {
                            if (!s[val[0].r]) s[val[0].r] = [];
                            s[val[0].r][val[0].c] = val[1];
                        } else s[encode_cell(val[0])] = val[1];
                        if (refguess.e.c < val[0].c) refguess.e.c = val[0].c;
                        if (refguess.e.r < val[0].r) refguess.e.r = val[0].r;
                        break;
                    default:
                        break;
                }
            }, o);
            s["!ref"] = encode_range(refguess);
            sheets[n] = s;
            return {
                SheetNames: snames,
                Sheets: sheets
            };
        }
        function parse_RANGE(blob) {
            var o = {
                s: {
                    c: 0,
                    r: 0
                },
                e: {
                    c: 0,
                    r: 0
                }
            };
            o.s.c = blob.read_shift(2);
            o.s.r = blob.read_shift(2);
            o.e.c = blob.read_shift(2);
            o.e.r = blob.read_shift(2);
            if (o.s.c == 0xFFFF) o.s.c = o.e.c = o.s.r = o.e.r = 0;
            return o;
        }
        function parse_cell(blob, length, opts) {
            var o = [{
                c: 0,
                r: 0
            }, {
                t: 'n',
                v: 0
            }, 0];
            if (opts.qpro && opts.vers != 0x5120) {
                o[0].c = blob.read_shift(1);
                blob.l++;
                o[0].r = blob.read_shift(2);
                blob.l += 2;
            } else {
                o[2] = blob.read_shift(1);
                o[0].c = blob.read_shift(2);
                o[0].r = blob.read_shift(2);
            }
            return o;
        }
        function parse_LABEL(blob, length, opts) {
            var tgt = blob.l + length;
            var o = parse_cell(blob, length, opts);
            o[1].t = 's';
            if (opts.vers == 0x5120) {
                blob.l++;
                var len = blob.read_shift(1);
                o[1].v = blob.read_shift(len, 'utf8');
                return o;
            }
            if (opts.qpro) blob.l++;
            o[1].v = blob.read_shift(tgt - blob.l, 'cstr');
            return o;
        }
        function parse_INTEGER(blob, length, opts) {
            var o = parse_cell(blob, length, opts);
            o[1].v = blob.read_shift(2, 'i');
            return o;
        }
        function parse_NUMBER(blob, length, opts) {
            var o = parse_cell(blob, length, opts);
            o[1].v = blob.read_shift(8, 'f');
            return o;
        }
        function parse_FORMULA(blob, length, opts) {
            var tgt = blob.l + length;
            var o = parse_cell(blob, length, opts);
            /* TODO: formula */
            o[1].v = blob.read_shift(8, 'f');
            if (opts.qpro) blob.l = tgt;
            else {
                var flen = blob.read_shift(2);
                blob.l += flen;
            }
            return o;
        }
        function parse_cell_3(blob) {
            var o = [{
                c: 0,
                r: 0
            }, {
                t: 'n',
                v: 0
            }, 0];
            o[0].r = blob.read_shift(2);
            o[3] = blob[blob.l++];
            o[0].c = blob[blob.l++];
            return o;
        }
        function parse_LABEL_16(blob, length) {
            var o = parse_cell_3(blob, length);
            o[1].t = 's';
            o[1].v = blob.read_shift(length - 4, 'cstr');
            return o;
        }
        function parse_NUMBER_18(blob, length) {
            var o = parse_cell_3(blob, length);
            o[1].v = blob.read_shift(2);
            var v = o[1].v >> 1;
            /* TODO: figure out all of the corner cases */
            if (o[1].v & 0x1) {
                switch (v & 0x07) {
                    case 1:
                        v = (v >> 3) * 500;
                        break;
                    case 2:
                        v = (v >> 3) / 20;
                        break;
                    case 4:
                        v = (v >> 3) / 2000;
                        break;
                    case 6:
                        v = (v >> 3) / 16;
                        break;
                    case 7:
                        v = (v >> 3) / 64;
                        break;
                    default:
                        throw "unknown NUMBER_18 encoding " + (v & 0x07);
                }
            }
            o[1].v = v;
            return o;
        }
        function parse_NUMBER_17(blob, length) {
            var o = parse_cell_3(blob, length);
            var v1 = blob.read_shift(4);
            var v2 = blob.read_shift(4);
            var e = blob.read_shift(2);
            if (e == 0xFFFF) {
                o[1].v = 0;
                return o;
            }
            var s = e & 0x8000;
            e = (e & 0x7FFF) - 16446;
            o[1].v = (s * 2 - 1) * ((e > 0 ? (v2 << e) : (v2 >>> -e)) + (e > -32 ? (v1 << (e + 32)) : (v1 >>> -(e + 32))));
            return o;
        }
        function parse_FORMULA_19(blob, length) {
            var o = parse_NUMBER_17(blob, 14);
            blob.l += length - 14; /* TODO: formula */
            return o;
        }
        function parse_NUMBER_25(blob, length) {
            var o = parse_cell_3(blob, length);
            var v1 = blob.read_shift(4);
            o[1].v = v1 >> 6;
            return o;
        }
        function parse_NUMBER_27(blob, length) {
            var o = parse_cell_3(blob, length);
            var v1 = blob.read_shift(8, 'f');
            o[1].v = v1;
            return o;
        }
        function parse_FORMULA_28(blob, length) {
            var o = parse_NUMBER_27(blob, 14);
            blob.l += length - 10; /* TODO: formula */
            return o;
        }
        var WK1Enum = {
            0x0000: {
                n: "BOF",
                f: parseuint16
            },
            0x0001: {
                n: "EOF"
            },
            0x0002: {
                n: "CALCMODE"
            },
            0x0003: {
                n: "CALCORDER"
            },
            0x0004: {
                n: "SPLIT"
            },
            0x0005: {
                n: "SYNC"
            },
            0x0006: {
                n: "RANGE",
                f: parse_RANGE
            },
            0x0007: {
                n: "WINDOW1"
            },
            0x0008: {
                n: "COLW1"
            },
            0x0009: {
                n: "WINTWO"
            },
            0x000A: {
                n: "COLW2"
            },
            0x000B: {
                n: "NAME"
            },
            0x000C: {
                n: "BLANK"
            },
            0x000D: {
                n: "INTEGER",
                f: parse_INTEGER
            },
            0x000E: {
                n: "NUMBER",
                f: parse_NUMBER
            },
            0x000F: {
                n: "LABEL",
                f: parse_LABEL
            },
            0x0010: {
                n: "FORMULA",
                f: parse_FORMULA
            },
            0x0018: {
                n: "TABLE"
            },
            0x0019: {
                n: "ORANGE"
            },
            0x001A: {
                n: "PRANGE"
            },
            0x001B: {
                n: "SRANGE"
            },
            0x001C: {
                n: "FRANGE"
            },
            0x001D: {
                n: "KRANGE1"
            },
            0x0020: {
                n: "HRANGE"
            },
            0x0023: {
                n: "KRANGE2"
            },
            0x0024: {
                n: "PROTEC"
            },
            0x0025: {
                n: "FOOTER"
            },
            0x0026: {
                n: "HEADER"
            },
            0x0027: {
                n: "SETUP"
            },
            0x0028: {
                n: "MARGINS"
            },
            0x0029: {
                n: "LABELFMT"
            },
            0x002A: {
                n: "TITLES"
            },
            0x002B: {
                n: "SHEETJS"
            },
            0x002D: {
                n: "GRAPH"
            },
            0x002E: {
                n: "NGRAPH"
            },
            0x002F: {
                n: "CALCCOUNT"
            },
            0x0030: {
                n: "UNFORMATTED"
            },
            0x0031: {
                n: "CURSORW12"
            },
            0x0032: {
                n: "WINDOW"
            },
            0x0033: {
                n: "STRING",
                f: parse_LABEL
            },
            0x0037: {
                n: "PASSWORD"
            },
            0x0038: {
                n: "LOCKED"
            },
            0x003C: {
                n: "QUERY"
            },
            0x003D: {
                n: "QUERYNAME"
            },
            0x003E: {
                n: "PRINT"
            },
            0x003F: {
                n: "PRINTNAME"
            },
            0x0040: {
                n: "GRAPH2"
            },
            0x0041: {
                n: "GRAPHNAME"
            },
            0x0042: {
                n: "ZOOM"
            },
            0x0043: {
                n: "SYMSPLIT"
            },
            0x0044: {
                n: "NSROWS"
            },
            0x0045: {
                n: "NSCOLS"
            },
            0x0046: {
                n: "RULER"
            },
            0x0047: {
                n: "NNAME"
            },
            0x0048: {
                n: "ACOMM"
            },
            0x0049: {
                n: "AMACRO"
            },
            0x004A: {
                n: "PARSE"
            },
            0x00FF: {
                n: "",
                f: parsenoop
            }
        };
        var WK3Enum = {
            0x0000: {
                n: "BOF"
            },
            0x0001: {
                n: "EOF"
            },
            0x0003: {
                n: "??"
            },
            0x0004: {
                n: "??"
            },
            0x0005: {
                n: "??"
            },
            0x0006: {
                n: "??"
            },
            0x0007: {
                n: "??"
            },
            0x0009: {
                n: "??"
            },
            0x000a: {
                n: "??"
            },
            0x000b: {
                n: "??"
            },
            0x000c: {
                n: "??"
            },
            0x000e: {
                n: "??"
            },
            0x000f: {
                n: "??"
            },
            0x0010: {
                n: "??"
            },
            0x0011: {
                n: "??"
            },
            0x0012: {
                n: "??"
            },
            0x0013: {
                n: "??"
            },
            0x0015: {
                n: "??"
            },
            0x0016: {
                n: "LABEL16",
                f: parse_LABEL_16
            },
            0x0017: {
                n: "NUMBER17",
                f: parse_NUMBER_17
            },
            0x0018: {
                n: "NUMBER18",
                f: parse_NUMBER_18
            },
            0x0019: {
                n: "FORMULA19",
                f: parse_FORMULA_19
            },
            0x001a: {
                n: "??"
            },
            0x001b: {
                n: "??"
            },
            0x001c: {
                n: "??"
            },
            0x001d: {
                n: "??"
            },
            0x001e: {
                n: "??"
            },
            0x001f: {
                n: "??"
            },
            0x0021: {
                n: "??"
            },
            0x0025: {
                n: "NUMBER25",
                f: parse_NUMBER_25
            },
            0x0027: {
                n: "NUMBER27",
                f: parse_NUMBER_27
            },
            0x0028: {
                n: "FORMULA28",
                f: parse_FORMULA_28
            },
            0x00FF: {
                n: "",
                f: parsenoop
            }
        };
        return {
            to_workbook: lotus_to_workbook
        };
    })();
    /* Parse a list of  tags */
    var parse_rs = (function parse_rs_factory() {
        var tregex = matchtag("t"),
            rpregex = matchtag("rPr"),
            rregex = /<(?:\w+:)?r>/g,
            rend = /<\/(?:\w+:)?r>/,
            nlregex = /\r\n/g;
        /* 18.4.7 rPr CT_RPrElt */
        var parse_rpr = function parse_rpr(rpr, intro, outro) {
            var font = {},
                cp = 65001,
                align = "";
            var pass = false;
            var m = rpr.match(tagregex),
                i = 0;
            if (m)
                for (; i != m.length; ++i) {
                    var y = parsexmltag(m[i]);
                    switch (y[0].replace(/\w*:/g, "")) {
                        /* 18.8.12 condense CT_BooleanProperty */
                        /* ** not required . */
                        case '':
                        case '':
                            font.shadow = 1;
                            break;
                        case '':
                            break;
                            /* 18.4.1 charset CT_IntProperty TODO */
                        case '':
                        case '':
                            font.outline = 1;
                            break;
                        case '':
                            break;
                            /* 18.4.5 rFont CT_FontName */
                        case '':
                        case '");
                outro.push("");
            }
            if (align == "superscript") align = "sup";
            else if (align == "subscript") align = "sub";
            if (align != "") {
                intro.push("<" + align + ">");
                outro.push("" + align + ">");
            }
            outro.push("");
            return cp;
        };
        /* 18.4.4 r CT_RElt */
        function parse_r(r) {
            var terms = [
                [], "", []
            ];
            /* 18.4.12 t ST_Xstring */
            var t = r.match(tregex) /*, cp = 65001*/ ;
            if (!t) return "";
            terms[1] = t[1];
            var rpr = r.match(rpregex);
            if (rpr) /*cp = */ parse_rpr(rpr[1], terms[0], terms[2]);
            return terms[0].join("") + terms[1].replace(nlregex, '
') + terms[2].join("");
        }
        return function parse_rs(rs) {
            return rs.replace(rregex, "").split(rend).map(parse_r).join("");
        };
    })();
    /* 18.4.8 si CT_Rst */
    var sitregex = /<(?:\w+:)?t[^>]*>([^<]*)<\/(?:\w+:)?t>/g,
        sirregex = /<(?:\w+:)?r>/;
    var sirphregex = /<(?:\w+:)?rPh.*?>([\s\S]*?)<\/(?:\w+:)?rPh>/g;
    function parse_si(x, opts) {
        var html = opts ? opts.cellHTML : true;
        var z = {};
        if (!x) return null;
        //var y;
        /* 18.4.12 t ST_Xstring (Plaintext String) */
        // TODO: is whitespace actually valid here?
        if (x.match(/^\s*<(?:\w+:)?t[^>]*>/)) {
            z.t = unescapexml(utf8read(x.slice(x.indexOf(">") + 1).split(/<\/(?:\w+:)?t>/)[0] || ""));
            z.r = utf8read(x);
            if (html) z.h = escapehtml(z.t);
        }
        /* 18.4.4 r CT_RElt (Rich Text Run) */
        else if (( /*y = */ x.match(sirregex))) {
            z.r = utf8read(x);
            z.t = unescapexml(utf8read((x.replace(sirphregex, '').match(sitregex) || []).join("").replace(tagregex, "")));
            if (html) z.h = parse_rs(z.r);
        }
        /* 18.4.3 phoneticPr CT_PhoneticPr (TODO: needed for Asian support) */
        /* 18.4.6 rPh CT_PhoneticRun (TODO: needed for Asian support) */
        return z;
    }
    /* 18.4 Shared String Table */
    var sstr0 = /<(?:\w+:)?sst([^>]*)>([\s\S]*)<\/(?:\w+:)?sst>/;
    var sstr1 = /<(?:\w+:)?(?:si|sstItem)>/g;
    var sstr2 = /<\/(?:\w+:)?(?:si|sstItem)>/;
    function parse_sst_xml(data, opts) {
        var s = ([]),
            ss = "";
        if (!data) return s;
        /* 18.4.9 sst CT_Sst */
        var sst = data.match(sstr0);
        if (sst) {
            ss = sst[2].replace(sstr1, "").split(sstr2);
            for (var i = 0; i != ss.length; ++i) {
                var o = parse_si(ss[i].trim(), opts);
                if (o != null) s[s.length] = o;
            }
            sst = parsexmltag(sst[1]);
            s.Count = sst.count;
            s.Unique = sst.uniqueCount;
        }
        return s;
    }
    RELS.SST = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings";
    var straywsregex = /^\s|\s$|[\t\n\r]/;
    function write_sst_xml(sst, opts) {
        if (!opts.bookSST) return "";
        var o = [XML_HEADER];
        o[o.length] = (writextag('sst', null, {
            xmlns: XMLNS.main[0],
            count: sst.Count,
            uniqueCount: sst.Unique
        }));
        for (var i = 0; i != sst.length; ++i) {
            if (sst[i] == null) continue;
            var s = sst[i];
            var sitag = "";
            if (s.r) sitag += s.r;
            else {
                sitag += "" + escapexml(s.t) + "";
            }
            sitag += "";
            o[o.length] = (sitag);
        }
        if (o.length > 2) {
            o[o.length] = ('');
            o[1] = o[1].replace("/>", ">");
        }
        return o.join("");
    }
    /* [MS-XLSB] 2.4.221 BrtBeginSst */
    function parse_BrtBeginSst(data) {
        return [data.read_shift(4), data.read_shift(4)];
    }
    /* [MS-XLSB] 2.1.7.45 Shared Strings */
    function parse_sst_bin(data, opts) {
        var s = ([]);
        var pass = false;
        recordhopper(data, function hopper_sst(val, R_n, RT) {
            switch (RT) {
                case 0x009F:
                    /* 'BrtBeginSst' */
                    s.Count = val[0];
                    s.Unique = val[1];
                    break;
                case 0x0013:
                    /* 'BrtSSTItem' */
                    s.push(val);
                    break;
                case 0x00A0:
                    /* 'BrtEndSst' */
                    return true;
                case 0x0023:
                    /* 'BrtFRTBegin' */
                    pass = true;
                    break;
                case 0x0024:
                    /* 'BrtFRTEnd' */
                    pass = false;
                    break;
                default:
                    if (R_n.indexOf("Begin") > 0) { /* empty */ } else if (R_n.indexOf("End") > 0) { /* empty */ }
                    if (!pass || opts.WTF) throw new Error("Unexpected record " + RT + " " + R_n);
            }
        });
        return s;
    }
    function write_BrtBeginSst(sst, o) {
        if (!o) o = new_buf(8);
        o.write_shift(4, sst.Count);
        o.write_shift(4, sst.Unique);
        return o;
    }
    var write_BrtSSTItem = write_RichStr;
    function write_sst_bin(sst) {
        var ba = buf_array();
        write_record(ba, "BrtBeginSst", write_BrtBeginSst(sst));
        for (var i = 0; i < sst.length; ++i) write_record(ba, "BrtSSTItem", write_BrtSSTItem(sst[i]));
        /* FRTSST */
        write_record(ba, "BrtEndSst");
        return ba.end();
    }
    function _JS2ANSI(str) {
        if (typeof cptable !== 'undefined') return cptable.utils.encode(current_ansi, str);
        var o = [],
            oo = str.split("");
        for (var i = 0; i < oo.length; ++i) o[i] = oo[i].charCodeAt(0);
        return o;
    }
    /* [MS-OFFCRYPTO] 2.1.4 Version */
    function parse_CRYPTOVersion(blob, length) {
        var o = {};
        o.Major = blob.read_shift(2);
        o.Minor = blob.read_shift(2);
        if (length >= 4) blob.l += length - 4;
        return o;
    }
    /* [MS-OFFCRYPTO] 2.1.5 DataSpaceVersionInfo */
    function parse_DataSpaceVersionInfo(blob) {
        var o = {};
        o.id = blob.read_shift(0, 'lpp4');
        o.R = parse_CRYPTOVersion(blob, 4);
        o.U = parse_CRYPTOVersion(blob, 4);
        o.W = parse_CRYPTOVersion(blob, 4);
        return o;
    }
    /* [MS-OFFCRYPTO] 2.1.6.1 DataSpaceMapEntry Structure */
    function parse_DataSpaceMapEntry(blob) {
        var len = blob.read_shift(4);
        var end = blob.l + len - 4;
        var o = {};
        var cnt = blob.read_shift(4);
        var comps = [];
        /* [MS-OFFCRYPTO] 2.1.6.2 DataSpaceReferenceComponent Structure */
        while (cnt-- > 0) comps.push({
            t: blob.read_shift(4),
            v: blob.read_shift(0, 'lpp4')
        });
        o.name = blob.read_shift(0, 'lpp4');
        o.comps = comps;
        if (blob.l != end) throw new Error("Bad DataSpaceMapEntry: " + blob.l + " != " + end);
        return o;
    }
    /* [MS-OFFCRYPTO] 2.1.6 DataSpaceMap */
    function parse_DataSpaceMap(blob) {
        var o = [];
        blob.l += 4; // must be 0x8
        var cnt = blob.read_shift(4);
        while (cnt-- > 0) o.push(parse_DataSpaceMapEntry(blob));
        return o;
    }
    /* [MS-OFFCRYPTO] 2.1.7 DataSpaceDefinition */
    function parse_DataSpaceDefinition(blob) {
        var o = [];
        blob.l += 4; // must be 0x8
        var cnt = blob.read_shift(4);
        while (cnt-- > 0) o.push(blob.read_shift(0, 'lpp4'));
        return o;
    }
    /* [MS-OFFCRYPTO] 2.1.8 DataSpaceDefinition */
    function parse_TransformInfoHeader(blob) {
        var o = {};
        /*var len = */
        blob.read_shift(4);
        blob.l += 4; // must be 0x1
        o.id = blob.read_shift(0, 'lpp4');
        o.name = blob.read_shift(0, 'lpp4');
        o.R = parse_CRYPTOVersion(blob, 4);
        o.U = parse_CRYPTOVersion(blob, 4);
        o.W = parse_CRYPTOVersion(blob, 4);
        return o;
    }
    function parse_Primary(blob) {
        /* [MS-OFFCRYPTO] 2.2.6 IRMDSTransformInfo */
        var hdr = parse_TransformInfoHeader(blob);
        /* [MS-OFFCRYPTO] 2.1.9 EncryptionTransformInfo */
        hdr.ename = blob.read_shift(0, '8lpp4');
        hdr.blksz = blob.read_shift(4);
        hdr.cmode = blob.read_shift(4);
        if (blob.read_shift(4) != 0x04) throw new Error("Bad !Primary record");
        return hdr;
    }
    /* [MS-OFFCRYPTO] 2.3.2 Encryption Header */
    function parse_EncryptionHeader(blob, length) {
        var tgt = blob.l + length;
        var o = {};
        o.Flags = (blob.read_shift(4) & 0x3F);
        blob.l += 4;
        o.AlgID = blob.read_shift(4);
        var valid = false;
        switch (o.AlgID) {
            case 0x660E:
            case 0x660F:
            case 0x6610:
                valid = (o.Flags == 0x24);
                break;
            case 0x6801:
                valid = (o.Flags == 0x04);
                break;
            case 0:
                valid = (o.Flags == 0x10 || o.Flags == 0x04 || o.Flags == 0x24);
                break;
            default:
                throw 'Unrecognized encryption algorithm: ' + o.AlgID;
        }
        if (!valid) throw new Error("Encryption Flags/AlgID mismatch");
        o.AlgIDHash = blob.read_shift(4);
        o.KeySize = blob.read_shift(4);
        o.ProviderType = blob.read_shift(4);
        blob.l += 8;
        o.CSPName = blob.read_shift((tgt - blob.l) >> 1, 'utf16le');
        blob.l = tgt;
        return o;
    }
    /* [MS-OFFCRYPTO] 2.3.3 Encryption Verifier */
    function parse_EncryptionVerifier(blob, length) {
        var o = {},
            tgt = blob.l + length;
        blob.l += 4; // SaltSize must be 0x10
        o.Salt = blob.slice(blob.l, blob.l + 16);
        blob.l += 16;
        o.Verifier = blob.slice(blob.l, blob.l + 16);
        blob.l += 16;
        /*var sz = */
        blob.read_shift(4);
        o.VerifierHash = blob.slice(blob.l, tgt);
        blob.l = tgt;
        return o;
    }
    /* [MS-OFFCRYPTO] 2.3.4.* EncryptionInfo Stream */
    function parse_EncryptionInfo(blob) {
        var vers = parse_CRYPTOVersion(blob);
        switch (vers.Minor) {
            case 0x02:
                return [vers.Minor, parse_EncInfoStd(blob, vers)];
            case 0x03:
                return [vers.Minor, parse_EncInfoExt(blob, vers)];
            case 0x04:
                return [vers.Minor, parse_EncInfoAgl(blob, vers)];
        }
        throw new Error("ECMA-376 Encrypted file unrecognized Version: " + vers.Minor);
    }
    /* [MS-OFFCRYPTO] 2.3.4.5  EncryptionInfo Stream (Standard Encryption) */
    function parse_EncInfoStd(blob) {
        var flags = blob.read_shift(4);
        if ((flags & 0x3F) != 0x24) throw new Error("EncryptionInfo mismatch");
        var sz = blob.read_shift(4);
        //var tgt = blob.l + sz;
        var hdr = parse_EncryptionHeader(blob, sz);
        var verifier = parse_EncryptionVerifier(blob, blob.length - blob.l);
        return {
            t: "Std",
            h: hdr,
            v: verifier
        };
    }
    /* [MS-OFFCRYPTO] 2.3.4.6  EncryptionInfo Stream (Extensible Encryption) */
    function parse_EncInfoExt() {
        throw new Error("File is password-protected: ECMA-376 Extensible");
    }
    /* [MS-OFFCRYPTO] 2.3.4.10 EncryptionInfo Stream (Agile Encryption) */
    function parse_EncInfoAgl(blob) {
        var KeyData = ["saltSize", "blockSize", "keyBits", "hashSize", "cipherAlgorithm", "cipherChaining", "hashAlgorithm", "saltValue"];
        blob.l += 4;
        var xml = blob.read_shift(blob.length - blob.l, 'utf8');
        var o = {};
        xml.replace(tagregex, function xml_agile(x) {
            var y = parsexmltag(x);
            switch (strip_ns(y[0])) {
                case '':
                    break;
                case '':
                case '':
                    break;
                case '':
                    break;
                case ' 4 || vers.Major < 2) throw new Error('unrecognized major version code: ' + vers.Major);
        o.Flags = blob.read_shift(4);
        length -= 4;
        var sz = blob.read_shift(4);
        length -= 4;
        o.EncryptionHeader = parse_EncryptionHeader(blob, sz);
        length -= sz;
        o.EncryptionVerifier = parse_EncryptionVerifier(blob, length);
        return o;
    }
    /* [MS-OFFCRYPTO] 2.3.6.1 RC4 Encryption Header */
    function parse_RC4Header(blob) {
        var o = {};
        var vers = o.EncryptionVersionInfo = parse_CRYPTOVersion(blob, 4);
        if (vers.Major != 1 || vers.Minor != 1) throw 'unrecognized version code ' + vers.Major + ' : ' + vers.Minor;
        o.Salt = blob.read_shift(16);
        o.EncryptedVerifier = blob.read_shift(16);
        o.EncryptedVerifierHash = blob.read_shift(16);
        return o;
    }
    /* [MS-OFFCRYPTO] 2.3.7.1 Binary Document Password Verifier Derivation */
    function crypto_CreatePasswordVerifier_Method1(Password) {
        var Verifier = 0x0000,
            PasswordArray;
        var PasswordDecoded = _JS2ANSI(Password);
        var len = PasswordDecoded.length + 1,
            i, PasswordByte;
        var Intermediate1, Intermediate2, Intermediate3;
        PasswordArray = new_raw_buf(len);
        PasswordArray[0] = PasswordDecoded.length;
        for (i = 1; i != len; ++i) PasswordArray[i] = PasswordDecoded[i - 1];
        for (i = len - 1; i >= 0; --i) {
            PasswordByte = PasswordArray[i];
            Intermediate1 = ((Verifier & 0x4000) === 0x0000) ? 0 : 1;
            Intermediate2 = (Verifier << 1) & 0x7FFF;
            Intermediate3 = Intermediate1 | Intermediate2;
            Verifier = Intermediate3 ^ PasswordByte;
        }
        return Verifier ^ 0xCE4B;
    }
    /* [MS-OFFCRYPTO] 2.3.7.2 Binary Document XOR Array Initialization */
    var crypto_CreateXorArray_Method1 = (function () {
        var PadArray = [0xBB, 0xFF, 0xFF, 0xBA, 0xFF, 0xFF, 0xB9, 0x80, 0x00, 0xBE, 0x0F, 0x00, 0xBF, 0x0F, 0x00];
        var InitialCode = [0xE1F0, 0x1D0F, 0xCC9C, 0x84C0, 0x110C, 0x0E10, 0xF1CE, 0x313E, 0x1872, 0xE139, 0xD40F, 0x84F9, 0x280C, 0xA96A, 0x4EC3];
        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];
        var Ror = function (Byte) {
            return ((Byte / 2) | (Byte * 128)) & 0xFF;
        };
        var XorRor = function (byte1, byte2) {
            return Ror(byte1 ^ byte2);
        };
        var CreateXorKey_Method1 = function (Password) {
            var XorKey = InitialCode[Password.length - 1];
            var CurrentElement = 0x68;
            for (var i = Password.length - 1; i >= 0; --i) {
                var Char = Password[i];
                for (var j = 0; j != 7; ++j) {
                    if (Char & 0x40) XorKey ^= XorMatrix[CurrentElement];
                    Char *= 2;
                    --CurrentElement;
                }
            }
            return XorKey;
        };
        return function (password) {
            var Password = _JS2ANSI(password);
            var XorKey = CreateXorKey_Method1(Password);
            var Index = Password.length;
            var ObfuscationArray = new_raw_buf(16);
            for (var i = 0; i != 16; ++i) ObfuscationArray[i] = 0x00;
            var Temp, PasswordLastChar, PadIndex;
            if ((Index & 1) === 1) {
                Temp = XorKey >> 8;
                ObfuscationArray[Index] = XorRor(PadArray[0], Temp);
                --Index;
                Temp = XorKey & 0xFF;
                PasswordLastChar = Password[Password.length - 1];
                ObfuscationArray[Index] = XorRor(PasswordLastChar, Temp);
            }
            while (Index > 0) {
                --Index;
                Temp = XorKey >> 8;
                ObfuscationArray[Index] = XorRor(Password[Index], Temp);
                --Index;
                Temp = XorKey & 0xFF;
                ObfuscationArray[Index] = XorRor(Password[Index], Temp);
            }
            Index = 15;
            PadIndex = 15 - Password.length;
            while (PadIndex > 0) {
                Temp = XorKey >> 8;
                ObfuscationArray[Index] = XorRor(PadArray[PadIndex], Temp);
                --Index;
                --PadIndex;
                Temp = XorKey & 0xFF;
                ObfuscationArray[Index] = XorRor(Password[Index], Temp);
                --Index;
                --PadIndex;
            }
            return ObfuscationArray;
        };
    })();
    /* [MS-OFFCRYPTO] 2.3.7.3 Binary Document XOR Data Transformation Method 1 */
    var crypto_DecryptData_Method1 = function (password, Data, XorArrayIndex, XorArray, O) {
        /* If XorArray is set, use it; if O is not set, make changes in-place */
        if (!O) O = Data;
        if (!XorArray) XorArray = crypto_CreateXorArray_Method1(password);
        var Index, Value;
        for (Index = 0; Index != Data.length; ++Index) {
            Value = Data[Index];
            Value ^= XorArray[XorArrayIndex];
            Value = ((Value >> 5) | (Value << 3)) & 0xFF;
            O[Index] = Value;
            ++XorArrayIndex;
        }
        return [O, XorArrayIndex, XorArray];
    };
    var crypto_MakeXorDecryptor = function (password) {
        var XorArrayIndex = 0,
            XorArray = crypto_CreateXorArray_Method1(password);
        return function (Data) {
            var O = crypto_DecryptData_Method1("", Data, XorArrayIndex, XorArray);
            XorArrayIndex = O[1];
            return O[0];
        };
    };
    /* 2.5.343 */
    function parse_XORObfuscation(blob, length, opts, out) {
        var o = ({
            key: parseuint16(blob),
            verificationBytes: parseuint16(blob)
        });
        if (opts.password) o.verifier = crypto_CreatePasswordVerifier_Method1(opts.password);
        out.valid = o.verificationBytes === o.verifier;
        if (out.valid) out.insitu = crypto_MakeXorDecryptor(opts.password);
        return o;
    }
    /* 2.4.117 */
    function parse_FilePassHeader(blob, length, oo) {
        var o = oo || {};
        o.Info = blob.read_shift(2);
        blob.l -= 2;
        if (o.Info === 1) o.Data = parse_RC4Header(blob, length);
        else o.Data = parse_RC4CryptoHeader(blob, length);
        return o;
    }
    function parse_FilePass(blob, length, opts) {
        var o = ({
            Type: opts.biff >= 8 ? blob.read_shift(2) : 0
        }); /* wEncryptionType */
        if (o.Type) parse_FilePassHeader(blob, length - 2, o);
        else parse_XORObfuscation(blob, opts.biff >= 8 ? length : length - 2, opts, o);
        return o;
    }
    var RTF = (function () {
        function rtf_to_sheet(d, opts) {
            switch (opts.type) {
                case 'base64':
                    return rtf_to_sheet_str(Base64.decode(d), opts);
                case 'binary':
                    return rtf_to_sheet_str(d, opts);
                case 'buffer':
                    return rtf_to_sheet_str(d.toString('binary'), opts);
                case 'array':
                    return rtf_to_sheet_str(cc2str(d), opts);
            }
            throw new Error("Unrecognized type " + opts.type);
        }
        function rtf_to_sheet_str(str, opts) {
            var o = opts || {};
            var ws = o.dense ? ([]) : ({});
            var range = ({
                s: {
                    c: 0,
                    r: 0
                },
                e: {
                    c: 0,
                    r: 0
                }
            });
            // TODO: parse
            if (!str.match(/\\trowd/)) throw new Error("RTF missing table");
            ws['!ref'] = encode_range(range);
            return ws;
        }
        function rtf_to_workbook(d, opts) {
            return sheet_to_workbook(rtf_to_sheet(d, opts), opts);
        }
        /* TODO: this is a stub */
        function sheet_to_rtf(ws) {
            var o = ["{\\rtf1\\ansi"];
            var r = safe_decode_range(ws['!ref']),
                cell;
            var dense = Array.isArray(ws);
            for (var R = r.s.r; R <= r.e.r; ++R) {
                o.push("\\trowd\\trautofit1");
                for (var C = r.s.c; C <= r.e.c; ++C) o.push("\\cellx" + (C + 1));
                o.push("\\pard\\intbl");
                for (C = r.s.c; C <= r.e.c; ++C) {
                    var coord = encode_cell({
                        r: R,
                        c: C
                    });
                    cell = dense ? (ws[R] || [])[C] : ws[coord];
                    if (!cell || cell.v == null && (!cell.f || cell.F)) continue;
                    o.push(" " + (cell.w || (format_cell(cell), cell.w)));
                    o.push("\\cell");
                }
                o.push("\\pard\\intbl\\row");
            }
            return o.join("") + "}";
        }
        return {
            to_workbook: rtf_to_workbook,
            to_sheet: rtf_to_sheet,
            from_sheet: sheet_to_rtf
        };
    })();
    function hex2RGB(h) {
        var o = h.slice(h[0] === "#" ? 1 : 0).slice(0, 6);
        return [parseInt(o.slice(0, 2), 16), parseInt(o.slice(2, 4), 16), parseInt(o.slice(4, 6), 16)];
    }
    function rgb2Hex(rgb) {
        for (var i = 0, o = 1; i != 3; ++i) o = o * 256 + (rgb[i] > 255 ? 255 : rgb[i] < 0 ? 0 : rgb[i]);
        return o.toString(16).toUpperCase().slice(1);
    }
    function rgb2HSL(rgb) {
        var R = rgb[0] / 255,
            G = rgb[1] / 255,
            B = rgb[2] / 255;
        var M = Math.max(R, G, B),
            m = Math.min(R, G, B),
            C = M - m;
        if (C === 0) return [0, 0, R];
        var H6 = 0,
            S = 0,
            L2 = (M + m);
        S = C / (L2 > 1 ? 2 - L2 : L2);
        switch (M) {
            case R:
                H6 = ((G - B) / C + 6) % 6;
                break;
            case G:
                H6 = ((B - R) / C + 2);
                break;
            case B:
                H6 = ((R - G) / C + 4);
                break;
        }
        return [H6 / 6, S, L2 / 2];
    }
    function hsl2RGB(hsl) {
        var H = hsl[0],
            S = hsl[1],
            L = hsl[2];
        var C = S * 2 * (L < 0.5 ? L : 1 - L),
            m = L - C / 2;
        var rgb = [m, m, m],
            h6 = 6 * H;
        var X;
        if (S !== 0) switch (h6 | 0) {
            case 0:
            case 6:
                X = C * h6;
                rgb[0] += C;
                rgb[1] += X;
                break;
            case 1:
                X = C * (2 - h6);
                rgb[0] += X;
                rgb[1] += C;
                break;
            case 2:
                X = C * (h6 - 2);
                rgb[1] += C;
                rgb[2] += X;
                break;
            case 3:
                X = C * (4 - h6);
                rgb[1] += X;
                rgb[2] += C;
                break;
            case 4:
                X = C * (h6 - 4);
                rgb[2] += C;
                rgb[0] += X;
                break;
            case 5:
                X = C * (6 - h6);
                rgb[2] += X;
                rgb[0] += C;
                break;
        }
        for (var i = 0; i != 3; ++i) rgb[i] = Math.round(rgb[i] * 255);
        return rgb;
    }
    /* 18.8.3 bgColor tint algorithm */
    function rgb_tint(hex, tint) {
        if (tint === 0) return hex;
        var hsl = rgb2HSL(hex2RGB(hex));
        if (tint < 0) hsl[2] = hsl[2] * (1 + tint);
        else hsl[2] = 1 - (1 - hsl[2]) * (1 - tint);
        return rgb2Hex(hsl2RGB(hsl));
    }
    /* 18.3.1.13 width calculations */
    /* [MS-OI29500] 2.1.595 Column Width & Formatting */
    var DEF_MDW = 6,
        MAX_MDW = 15,
        MIN_MDW = 1,
        MDW = DEF_MDW;
    function width2px(width) {
        return Math.floor((width + (Math.round(128 / MDW)) / 256) * MDW);
    }
    function px2char(px) {
        return (Math.floor((px - 5) / MDW * 100 + 0.5)) / 100;
    }
    function char2width(chr) {
        return (Math.round((chr * MDW + 5) / MDW * 256)) / 256;
    }
    //function px2char_(px) { return (((px - 5)/MDW * 100 + 0.5))/100; }
    //function char2width_(chr) { return (((chr * MDW + 5)/MDW*256))/256; }
    function cycle_width(collw) {
        return char2width(px2char(width2px(collw)));
    }
    /* XLSX/XLSB/XLS specify width in units of MDW */
    function find_mdw_colw(collw) {
        var delta = Math.abs(collw - cycle_width(collw)),
            _MDW = MDW;
        if (delta > 0.005)
            for (MDW = MIN_MDW; MDW < MAX_MDW; ++MDW)
                if (Math.abs(collw - cycle_width(collw)) <= delta) {
                    delta = Math.abs(collw - cycle_width(collw));
                    _MDW = MDW;
                }
        MDW = _MDW;
    }
    /* XLML specifies width in terms of pixels */
    /*function find_mdw_wpx(wpx) {
    	var delta = Infinity, guess = 0, _MDW = MIN_MDW;
    	for(MDW=MIN_MDW; MDW 0.5) guess--;
    		if(Math.abs(guess) < delta) { delta = Math.abs(guess); _MDW = MDW; }
    	}
    	MDW = _MDW;
    }*/
    function process_col(coll) {
        if (coll.width) {
            coll.wpx = width2px(coll.width);
            coll.wch = px2char(coll.wpx);
            coll.MDW = MDW;
        } else if (coll.wpx) {
            coll.wch = px2char(coll.wpx);
            coll.width = char2width(coll.wch);
            coll.MDW = MDW;
        } else if (typeof coll.wch == 'number') {
            coll.width = char2width(coll.wch);
            coll.wpx = width2px(coll.width);
            coll.MDW = MDW;
        }
        if (coll.customWidth) delete coll.customWidth;
    }
    var DEF_PPI = 96,
        PPI = DEF_PPI;
    function px2pt(px) {
        return px * 96 / PPI;
    }
    function pt2px(pt) {
        return pt * PPI / 96;
    }
    /* [MS-EXSPXML3] 2.4.54 ST_enmPattern */
    var XLMLPatternTypeMap = {
        "None": "none",
        "Solid": "solid",
        "Gray50": "mediumGray",
        "Gray75": "darkGray",
        "Gray25": "lightGray",
        "HorzStripe": "darkHorizontal",
        "VertStripe": "darkVertical",
        "ReverseDiagStripe": "darkDown",
        "DiagStripe": "darkUp",
        "DiagCross": "darkGrid",
        "ThickDiagCross": "darkTrellis",
        "ThinHorzStripe": "lightHorizontal",
        "ThinVertStripe": "lightVertical",
        "ThinReverseDiagStripe": "lightDown",
        "ThinHorzCross": "lightGrid"
    };
    /* 18.8.5 borders CT_Borders */
    function parse_borders(t, styles, themes, opts) {
        styles.Borders = [];
        var border = {} /*, sub_border = {}*/ ;
        var pass = false;
        t[0].match(tagregex).forEach(function (x) {
            var y = parsexmltag(x);
            switch (strip_ns(y[0])) {
                case '':
                case '':
                    break;
                    /* 18.8.4 border CT_Border */
                case '':
                case '':
                    border = {};
                    if (y.diagonalUp) {
                        border.diagonalUp = y.diagonalUp;
                    }
                    if (y.diagonalDown) {
                        border.diagonalDown = y.diagonalDown;
                    }
                    styles.Borders.push(border);
                    break;
                case '':
                    break;
                    /* note: not in spec, appears to be CT_BorderPr */
                case '':
                    break;
                case '':
                    break;
                case '':
                    break;
                    /* note: not in spec, appears to be CT_BorderPr */
                case '':
                    break;
                case '':
                    break;
                case '':
                    break;
                    /* 18.8.43 top CT_BorderPr */
                case '':
                    break;
                case '':
                    break;
                case '':
                    break;
                    /* 18.8.6 bottom CT_BorderPr */
                case '':
                    break;
                case '':
                    break;
                case '':
                    break;
                    /* 18.8.13 diagonal CT_BorderPr */
                case '':
                case '':
                    break;
                case '':
                    break;
                    /* 18.8.25 horizontal CT_BorderPr */
                case '':
                case '':
                    break;
                case '':
                    break;
                    /* 18.8.44 vertical CT_BorderPr */
                case '':
                case '':
                    break;
                case '':
                    break;
                    /* 18.8.37 start CT_BorderPr */
                case '':
                case '':
                    break;
                case '':
                    break;
                    /* 18.8.16 end CT_BorderPr */
                case '':
                case '':
                    break;
                case '':
                    break;
                    /* 18.8.? color CT_Color */
                case '':
                    break;
                case '':
                case '':
                    break;
                    /* 18.2.10 extLst CT_ExtensionList ? */
                case '':
                case '':
                    break;
                case '':
                    pass = false;
                    break;
                default:
                    if (opts && opts.WTF) {
                        if (!pass) throw new Error('unrecognized ' + y[0] + ' in borders');
                    }
            }
        });
    }
    /* 18.8.21 fills CT_Fills */
    function parse_fills(t, styles, themes, opts) {
        styles.Fills = [];
        var fill = {};
        var pass = false;
        t[0].match(tagregex).forEach(function (x) {
            var y = parsexmltag(x);
            switch (strip_ns(y[0])) {
                case '':
                case '':
                    break;
                    /* 18.8.20 fill CT_Fill */
                case '':
                case '':
                    fill = {};
                    styles.Fills.push(fill);
                    break;
                case '':
                    break;
                    /* 18.8.24 gradientFill CT_GradientFill */
                case '':
                    break;
                case '':
                    styles.Fills.push(fill);
                    fill = {};
                    break;
                    /* 18.8.32 patternFill CT_PatternFill */
                case '':
                    if (y.patternType) fill.patternType = y.patternType;
                    break;
                case '':
                case '':
                    break;
                    /* 18.8.3 bgColor CT_Color */
                case '':
                case '':
                    break;
                    /* 18.8.19 fgColor CT_Color */
                case '':
                case '':
                    break;
                    /* 18.8.38 stop CT_GradientStop */
                case '':
                    break;
                case '':
                    break;
                    /* 18.8.? color CT_Color */
                case '':
                    break;
                case '':
                    break;
                    /* 18.2.10 extLst CT_ExtensionList ? */
                case '':
                case '':
                    break;
                case '':
                    pass = false;
                    break;
                default:
                    if (opts && opts.WTF) {
                        if (!pass) throw new Error('unrecognized ' + y[0] + ' in fills');
                    }
            }
        });
    }
    /* 18.8.23 fonts CT_Fonts */
    function parse_fonts(t, styles, themes, opts) {
        styles.Fonts = [];
        var font = {};
        var pass = false;
        t[0].match(tagregex).forEach(function (x) {
            var y = parsexmltag(x);
            switch (strip_ns(y[0])) {
                case '':
                case '':
                    break;
                    /* 18.8.22 font CT_Font */
                case '':
                    break;
                case '':
                case '':
                    styles.Fonts.push(font);
                    font = {};
                    break;
                    /* 18.8.29 name CT_FontName */
                case '':
                case '':
                    break;
                    /* 18.8.2  b CT_BooleanProperty */
                case '':
                    font.bold = 1;
                    break;
                    /* 18.8.26 i CT_BooleanProperty */
                case '':
                    font.italic = 1;
                    break;
                    /* 18.4.13 u CT_UnderlineProperty */
                case '':
                    font.underline = 1;
                    break;
                    /* 18.4.10 strike CT_BooleanProperty */
                case '';
    }
    /* TODO */
    function write_ws_xlml_table(ws, opts, idx, wb) {
        if (!ws['!ref']) return "";
        var range = safe_decode_range(ws['!ref']);
        var marr = ws['!merges'] || [],
            mi = 0;
        var o = [];
        if (ws['!cols']) ws['!cols'].forEach(function (n, i) {
            process_col(n);
            var w = !!n.width;
            var p = col_obj_w(i, n);
            var k = {
                "ss:Index": i + 1
            };
            if (w) k['ss:Width'] = width2px(p.width);
            if (n.hidden) k['ss:Hidden'] = "1";
            o.push(writextag("Column", null, k));
        });
        var dense = Array.isArray(ws);
        for (var R = range.s.r; R <= range.e.r; ++R) {
            var row = [write_ws_xlml_row(R, (ws['!rows'] || [])[R])];
            for (var C = range.s.c; C <= range.e.c; ++C) {
                var skip = false;
                for (mi = 0; mi != marr.length; ++mi) {
                    if (marr[mi].s.c > C) continue;
                    if (marr[mi].s.r > R) continue;
                    if (marr[mi].e.c < C) continue;
                    if (marr[mi].e.r < R) continue;
                    if (marr[mi].s.c != C || marr[mi].s.r != R) skip = true;
                    break;
                }
                if (skip) continue;
                var addr = {
                    r: R,
                    c: C
                };
                var ref = encode_cell(addr),
                    cell = dense ? (ws[R] || [])[C] : ws[ref];
                row.push(write_ws_xlml_cell(cell, ref, ws, opts, idx, wb, addr));
            }
            row.push("
");
            if (row.length > 2) o.push(row.join(""));
        }
        return o.join("");
    }
    function write_ws_xlml(idx, opts, wb) {
        var o = [];
        var s = wb.SheetNames[idx];
        var ws = wb.Sheets[s];
        var t = ws ? write_ws_xlml_names(ws, opts, idx, wb) : "";
        if (t.length > 0) o.push("" + t + "");
        /* Table */
        t = ws ? write_ws_xlml_table(ws, opts, idx, wb) : "";
        if (t.length > 0) o.push("");
        /* WorksheetOptions */
        o.push(write_ws_xlml_wsopts(ws, opts, idx, wb));
        return o.join("");
    }
    function write_xlml(wb, opts) {
        if (!opts) opts = {};
        if (!wb.SSF) wb.SSF = SSF.get_table();
        if (wb.SSF) {
            make_ssf(SSF);
            SSF.load_table(wb.SSF);
            // $FlowIgnore
            opts.revssf = evert_num(wb.SSF);
            opts.revssf[wb.SSF[65535]] = 0;
            opts.ssf = wb.SSF;
            opts.cellXfs = [];
            get_cell_style(opts.cellXfs, {}, {
                revssf: {
                    "General": 0
                }
            });
        }
        var d = [];
        d.push(write_props_xlml(wb, opts));
        d.push(write_wb_xlml(wb, opts));
        d.push("");
        d.push("");
        for (var i = 0; i < wb.SheetNames.length; ++i)
            d.push(writextag("Worksheet", write_ws_xlml(i, opts, wb), {
                "ss:Name": escapexml(wb.SheetNames[i])
            }));
        d[2] = write_sty_xlml(wb, opts);
        d[3] = write_names_xlml(wb, opts);
        return XML_HEADER + writextag("Workbook", d.join(""), {
            'xmlns': XLMLNS.ss,
            'xmlns:o': XLMLNS.o,
            'xmlns:x': XLMLNS.x,
            'xmlns:ss': XLMLNS.ss,
            'xmlns:dt': XLMLNS.dt,
            'xmlns:html': XLMLNS.html
        });
    }
    /* [MS-OLEDS] 2.3.8 CompObjStream */
    function parse_compobj(obj) {
        var v = {};
        var o = obj.content;
        /* [MS-OLEDS] 2.3.7 CompObjHeader -- All fields MUST be ignored */
        o.l = 28;
        v.AnsiUserType = o.read_shift(0, "lpstr-ansi");
        v.AnsiClipboardFormat = parse_ClipboardFormatOrAnsiString(o);
        if (o.length - o.l <= 4) return v;
        var m = o.read_shift(4);
        if (m == 0 || m > 40) return v;
        o.l -= 4;
        v.Reserved1 = o.read_shift(0, "lpstr-ansi");
        if (o.length - o.l <= 4) return v;
        m = o.read_shift(4);
        if (m !== 0x71b239f4) return v;
        v.UnicodeClipboardFormat = parse_ClipboardFormatOrUnicodeString(o);
        m = o.read_shift(4);
        if (m == 0 || m > 40) return v;
        o.l -= 4;
        v.Reserved2 = o.read_shift(0, "lpwstr");
    }
    /*
    	Continue logic for:
    	- 2.4.58 Continue
    	- 2.4.59 ContinueBigName
    	- 2.4.60 ContinueFrt
    	- 2.4.61 ContinueFrt11
    	- 2.4.62 ContinueFrt12
    */
    function slurp(R, blob, length, opts) {
        var l = length;
        var bufs = [];
        var d = blob.slice(blob.l, blob.l + l);
        if (opts && opts.enc && opts.enc.insitu) switch (R.n) {
            case 'BOF':
            case 'FilePass':
            case 'FileLock':
            case 'InterfaceHdr':
            case 'RRDInfo':
            case 'RRDHead':
            case 'UsrExcl':
                break;
            default:
                if (d.length === 0) break;
                opts.enc.insitu(d);
        }
        bufs.push(d);
        blob.l += l;
        var next = (XLSRecordEnum[__readUInt16LE(blob, blob.l)]);
        var start = 0;
        while (next != null && next.n.slice(0, 8) === 'Continue') {
            l = __readUInt16LE(blob, blob.l + 2);
            start = blob.l + 4;
            if (next.n == 'ContinueFrt') start += 4;
            else if (next.n.slice(0, 11) == 'ContinueFrt') start += 12;
            bufs.push(blob.slice(start, blob.l + 4 + l));
            blob.l += 4 + l;
            next = (XLSRecordEnum[__readUInt16LE(blob, blob.l)]);
        }
        var b = (bconcat(bufs));
        prep_blob(b, 0);
        var ll = 0;
        b.lens = [];
        for (var j = 0; j < bufs.length; ++j) {
            b.lens.push(ll);
            ll += bufs[j].length;
        }
        return R.f(b, b.length, opts);
    }
    function safe_format_xf(p, opts, date1904) {
        if (p.t === 'z') return;
        if (!p.XF) return;
        var fmtid = 0;
        try {
            fmtid = p.z || p.XF.numFmtId || 0;
            if (opts.cellNF) p.z = SSF._table[fmtid];
        } catch (e) {
            if (opts.WTF) throw e;
        }
        if (!opts || opts.cellText !== false) try {
            if (p.t === 'e') {
                p.w = p.w || BErr[p.v];
            } else if (fmtid === 0 || fmtid == "General") {
                if (p.t === 'n') {
                    if ((p.v | 0) === p.v) p.w = SSF._general_int(p.v);
                    else p.w = SSF._general_num(p.v);
                } else p.w = SSF._general(p.v);
            } else p.w = SSF.format(fmtid, p.v, {
                date1904: !!date1904
            });
        } catch (e) {
            if (opts.WTF) throw e;
        }
        if (opts.cellDates && fmtid && p.t == 'n' && SSF.is_date(SSF._table[fmtid] || String(fmtid))) {
            var _d = SSF.parse_date_code(p.v);
            if (_d) {
                p.t = 'd';
                p.v = new Date(_d.y, _d.m - 1, _d.d, _d.H, _d.M, _d.S, _d.u);
            }
        }
    }
    function make_cell(val, ixfe, t) {
        return ({
            v: val,
            ixfe: ixfe,
            t: t
        });
    }
    // 2.3.2
    function parse_workbook(blob, options) {
        var wb = ({
            opts: {}
        });
        var Sheets = {};
        if (DENSE != null && options.dense == null) options.dense = DENSE;
        var out = ((options.dense ? [] : {}));
        var Directory = {};
        var range = ({});
        var last_formula = null;
        var sst = ([]);
        var cur_sheet = "";
        var Preamble = {};
        var lastcell, last_cell = "",
            cc, cmnt, rngC, rngR;
        var sharedf = {};
        var arrayf = [];
        var temp_val;
        var country;
        var cell_valid = true;
        var XFs = []; /* XF records */
        var palette = [];
        var Workbook = ({
                Sheets: [],
                WBProps: {
                    date1904: false
                },
                Views: [{}]
            }),
            wsprops = {};
        var get_rgb = function getrgb(icv) {
            if (icv < 8) return XLSIcv[icv];
            if (icv < 64) return palette[icv - 8] || XLSIcv[icv];
            return XLSIcv[icv];
        };
        var process_cell_style = function pcs(cell, line, options) {
            var xfd = line.XF.data;
            if (!xfd || !xfd.patternType || !options || !options.cellStyles) return;
            line.s = ({});
            line.s.patternType = xfd.patternType;
            var t;
            if ((t = rgb2Hex(get_rgb(xfd.icvFore)))) {
                line.s.fgColor = {
                    rgb: t
                };
            }
            if ((t = rgb2Hex(get_rgb(xfd.icvBack)))) {
                line.s.bgColor = {
                    rgb: t
                };
            }
        };
        var addcell = function addcell(cell, line, options) {
            if (file_depth > 1) return;
            if (options.sheetRows && cell.r >= options.sheetRows) cell_valid = false;
            if (!cell_valid) return;
            if (options.cellStyles && line.XF && line.XF.data) process_cell_style(cell, line, options);
            delete line.ixfe;
            delete line.XF;
            lastcell = cell;
            last_cell = encode_cell(cell);
            if (!range || !range.s || !range.e) range = {
                s: {
                    r: 0,
                    c: 0
                },
                e: {
                    r: 0,
                    c: 0
                }
            };
            if (cell.r < range.s.r) range.s.r = cell.r;
            if (cell.c < range.s.c) range.s.c = cell.c;
            if (cell.r + 1 > range.e.r) range.e.r = cell.r + 1;
            if (cell.c + 1 > range.e.c) range.e.c = cell.c + 1;
            if (options.cellFormula && line.f) {
                for (var afi = 0; afi < arrayf.length; ++afi) {
                    if (arrayf[afi][0].s.c > cell.c || arrayf[afi][0].s.r > cell.r) continue;
                    if (arrayf[afi][0].e.c < cell.c || arrayf[afi][0].e.r < cell.r) continue;
                    line.F = encode_range(arrayf[afi][0]);
                    if (arrayf[afi][0].s.c != cell.c || arrayf[afi][0].s.r != cell.r) delete line.f;
                    if (line.f) line.f = "" + stringify_formula(arrayf[afi][1], range, cell, supbooks, opts);
                    break;
                }
            } {
                if (options.dense) {
                    if (!out[cell.r]) out[cell.r] = [];
                    out[cell.r][cell.c] = line;
                } else out[last_cell] = line;
            }
        };
        var opts = ({
            enc: false, // encrypted
            sbcch: 0, // cch in the preceding SupBook
            snames: [], // sheetnames
            sharedf: sharedf, // shared formulae by address
            arrayf: arrayf, // array formulae array
            rrtabid: [], // RRTabId
            lastuser: "", // Last User from WriteAccess
            biff: 8, // BIFF version
            codepage: 0, // CP from CodePage record
            winlocked: 0, // fLockWn from WinProtect
            cellStyles: !!options && !!options.cellStyles,
            WTF: !!options && !!options.wtf
        });
        if (options.password) opts.password = options.password;
        var themes;
        var merges = [];
        var objects = [];
        var colinfo = [],
            rowinfo = [];
        // eslint-disable-next-line no-unused-vars
        var defwidth = 0,
            defheight = 0; // twips / MDW respectively
        var seencol = false;
        var supbooks = ([]); // 1-indexed, will hold extern names
        supbooks.SheetNames = opts.snames;
        supbooks.sharedf = opts.sharedf;
        supbooks.arrayf = opts.arrayf;
        supbooks.names = [];
        supbooks.XTI = [];
        var last_Rn = '';
        var file_depth = 0; /* TODO: make a real stack */
        var BIFF2Fmt = 0,
            BIFF2FmtTable = [];
        var FilterDatabases = []; /* TODO: sort out supbooks and process elsewhere */
        var last_lbl;
        /* explicit override for some broken writers */
        opts.codepage = 1200;
        set_cp(1200);
        var seen_codepage = false;
        while (blob.l < blob.length - 1) {
            var s = blob.l;
            var RecordType = blob.read_shift(2);
            if (RecordType === 0 && last_Rn === 'EOF') break;
            var length = (blob.l === blob.length ? 0 : blob.read_shift(2));
            var R = XLSRecordEnum[RecordType];
            //console.log(RecordType.toString(16), RecordType, R, blob.l, length, blob.length);
            //if(!R) console.log(blob.slice(blob.l, blob.l + length));
            if (R && R.f) {
                if (options.bookSheets) {
                    if (last_Rn === 'BoundSheet8' && R.n !== 'BoundSheet8') break;
                }
                last_Rn = R.n;
                if (R.r === 2 || R.r == 12) {
                    var rt = blob.read_shift(2);
                    length -= 2;
                    if (!opts.enc && rt !== RecordType && (((rt & 0xFF) << 8) | (rt >> 8)) !== RecordType) throw new Error("rt mismatch: " + rt + "!=" + RecordType);
                    if (R.r == 12) {
                        blob.l += 10;
                        length -= 10;
                    } // skip FRT
                }
                //console.error(R,blob.l,length,blob.length);
                var val;
                if (R.n === 'EOF') val = R.f(blob, length, opts);
                else val = slurp(R, blob, length, opts);
                var Rn = R.n;
                if (file_depth == 0 && Rn != 'BOF') continue;
                /* nested switch statements to workaround V8 128 limit */
                switch (Rn) {
                    /* Workbook Options */
                    case 'Date1904':
                        wb.opts.Date1904 = Workbook.WBProps.date1904 = val;
                        break;
                    case 'WriteProtect':
                        wb.opts.WriteProtect = true;
                        break;
                    case 'FilePass':
                        if (!opts.enc) blob.l = 0;
                        opts.enc = val;
                        if (!options.password) throw new Error("File is password-protected");
                        if (val.valid == null) throw new Error("Encryption scheme unsupported");
                        if (!val.valid) throw new Error("Password is incorrect");
                        break;
                    case 'WriteAccess':
                        opts.lastuser = val;
                        break;
                    case 'FileSharing':
                        break; //TODO
                    case 'CodePage':
                        /* overrides based on test cases */
                        switch (val) {
                            case 0x5212:
                                val = 1200;
                                break;
                            case 0x8000:
                                val = 10000;
                                break;
                            case 0x8001:
                                val = 1252;
                                break;
                        }
                        set_cp(opts.codepage = val);
                        seen_codepage = true;
                        break;
                    case 'RRTabId':
                        opts.rrtabid = val;
                        break;
                    case 'WinProtect':
                        opts.winlocked = val;
                        break;
                    case 'Template':
                        break; // TODO
                    case 'BookBool':
                        break; // TODO
                    case 'UsesELFs':
                        break;
                    case 'MTRSettings':
                        break;
                    case 'RefreshAll':
                    case 'CalcCount':
                    case 'CalcDelta':
                    case 'CalcIter':
                    case 'CalcMode':
                    case 'CalcPrecision':
                    case 'CalcSaveRecalc':
                        wb.opts[Rn] = val;
                        break;
                    case 'CalcRefMode':
                        opts.CalcRefMode = val;
                        break; // TODO: implement R1C1
                    case 'Uncalced':
                        break;
                    case 'ForceFullCalculation':
                        wb.opts.FullCalc = val;
                        break;
                    case 'WsBool':
                        if (val.fDialog) out["!type"] = "dialog";
                        break; // TODO
                    case 'XF':
                        XFs.push(val);
                        break;
                    case 'ExtSST':
                        break; // TODO
                    case 'BookExt':
                        break; // TODO
                    case 'RichTextStream':
                        break;
                    case 'BkHim':
                        break;
                    case 'SupBook':
                        supbooks.push([val]);
                        supbooks[supbooks.length - 1].XTI = [];
                        break;
                    case 'ExternName':
                        supbooks[supbooks.length - 1].push(val);
                        break;
                    case 'Index':
                        break; // TODO
                    case 'Lbl':
                        last_lbl = ({
                            Name: val.Name,
                            Ref: stringify_formula(val.rgce, range, null, supbooks, opts)
                        });
                        if (val.itab > 0) last_lbl.Sheet = val.itab - 1;
                        supbooks.names.push(last_lbl);
                        if (!supbooks[0]) {
                            supbooks[0] = [];
                            supbooks[0].XTI = [];
                        }
                        supbooks[supbooks.length - 1].push(val);
                        if (val.Name == "_xlnm._FilterDatabase" && val.itab > 0)
                            if (val.rgce && val.rgce[0] && val.rgce[0][0] && val.rgce[0][0][0] == 'PtgArea3d')
                                FilterDatabases[val.itab - 1] = {
                                    ref: encode_range(val.rgce[0][0][1][2])
                                };
                        break;
                    case 'ExternCount':
                        opts.ExternCount = val;
                        break;
                    case 'ExternSheet':
                        if (supbooks.length == 0) {
                            supbooks[0] = [];
                            supbooks[0].XTI = [];
                        }
                        supbooks[supbooks.length - 1].XTI = supbooks[supbooks.length - 1].XTI.concat(val);
                        supbooks.XTI = supbooks.XTI.concat(val);
                        break;
                    case 'NameCmt':
                        /* TODO: search for correct name */
                        if (opts.biff < 8) break;
                        if (last_lbl != null) last_lbl.Comment = val[1];
                        break;
                    case 'Protect':
                        out["!protect"] = val;
                        break; /* for sheet or book */
                    case 'Password':
                        if (val !== 0 && opts.WTF) console.error("Password verifier: " + val);
                        break;
                    case 'Prot4Rev':
                    case 'Prot4RevPass':
                        break; /*TODO: Revision Control*/
                    case 'BoundSheet8':
                        {
                            Directory[val.pos] = val;
                            opts.snames.push(val.name);
                        }
                        break;
                    case 'EOF':
                        {
                            if (--file_depth) break;
                            if (range.e) {
                                if (range.e.r > 0 && range.e.c > 0) {
                                    range.e.r--;
                                    range.e.c--;
                                    out["!ref"] = encode_range(range);
                                    if (options.sheetRows && options.sheetRows <= range.e.r) {
                                        var tmpri = range.e.r;
                                        range.e.r = options.sheetRows - 1;
                                        out["!fullref"] = out["!ref"];
                                        out["!ref"] = encode_range(range);
                                        range.e.r = tmpri;
                                    }
                                    range.e.r++;
                                    range.e.c++;
                                }
                                if (merges.length > 0) out["!merges"] = merges;
                                if (objects.length > 0) out["!objects"] = objects;
                                if (colinfo.length > 0) out["!cols"] = colinfo;
                                if (rowinfo.length > 0) out["!rows"] = rowinfo;
                                Workbook.Sheets.push(wsprops);
                            }
                            if (cur_sheet === "") Preamble = out;
                            else Sheets[cur_sheet] = out;
                            out = ((options.dense ? [] : {}));
                        }
                        break;
                    case 'BOF':
                        {
                            if (opts.biff === 8) opts.biff = {
                                0x0009: 2,
                                0x0209: 3,
                                0x0409: 4
                            } [RecordType] || {
                                0x0200: 2,
                                0x0300: 3,
                                0x0400: 4,
                                0x0500: 5,
                                0x0600: 8,
                                0x0002: 2,
                                0x0007: 2
                            } [val.BIFFVer] || 8;
                            if (opts.biff == 8 && val.BIFFVer == 0 && val.dt == 16) opts.biff = 2;
                            if (file_depth++) break;
                            cell_valid = true;
                            out = ((options.dense ? [] : {}));
                            if (opts.biff < 8 && !seen_codepage) {
                                seen_codepage = true;
                                set_cp(opts.codepage = options.codepage || 1252);
                            }
                            if (opts.biff < 5) {
                                if (cur_sheet === "") cur_sheet = "Sheet1";
                                range = {
                                    s: {
                                        r: 0,
                                        c: 0
                                    },
                                    e: {
                                        r: 0,
                                        c: 0
                                    }
                                };
                                /* fake BoundSheet8 */
                                var fakebs8 = {
                                    pos: blob.l - length,
                                    name: cur_sheet
                                };
                                Directory[fakebs8.pos] = fakebs8;
                                opts.snames.push(cur_sheet);
                            } else cur_sheet = (Directory[s] || {
                                name: ""
                            }).name;
                            if (val.dt == 0x20) out["!type"] = "chart";
                            if (val.dt == 0x40) out["!type"] = "macro";
                            merges = [];
                            objects = [];
                            opts.arrayf = arrayf = [];
                            colinfo = [];rowinfo = [];
                            defwidth = defheight = 0;
                            seencol = false;
                            wsprops = {
                                Hidden: (Directory[s] || {
                                    hs: 0
                                }).hs,
                                name: cur_sheet
                            };
                        }
                        break;
                    case 'Number':
                    case 'BIFF2NUM':
                    case 'BIFF2INT':
                        {
                            if (out["!type"] == "chart")
                                if (options.dense ? (out[val.r] || [])[val.c] : out[encode_cell({
                                        c: val.c,
                                        r: val.r
                                    })]) ++val.c;
                            temp_val = ({
                                ixfe: val.ixfe,
                                XF: XFs[val.ixfe] || {},
                                v: val.val,
                                t: 'n'
                            });
                            if (BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe >> 8) & 0x1F];
                            safe_format_xf(temp_val, options, wb.opts.Date1904);
                            addcell({
                                c: val.c,
                                r: val.r
                            }, temp_val, options);
                        }
                        break;
                    case 'BoolErr':
                        {
                            temp_val = ({
                                ixfe: val.ixfe,
                                XF: XFs[val.ixfe],
                                v: val.val,
                                t: val.t
                            });
                            if (BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe >> 8) & 0x1F];
                            safe_format_xf(temp_val, options, wb.opts.Date1904);
                            addcell({
                                c: val.c,
                                r: val.r
                            }, temp_val, options);
                        }
                        break;
                    case 'RK':
                        {
                            temp_val = ({
                                ixfe: val.ixfe,
                                XF: XFs[val.ixfe],
                                v: val.rknum,
                                t: 'n'
                            });
                            if (BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe >> 8) & 0x1F];
                            safe_format_xf(temp_val, options, wb.opts.Date1904);
                            addcell({
                                c: val.c,
                                r: val.r
                            }, temp_val, options);
                        }
                        break;
                    case 'MulRk':
                        {
                            for (var j = val.c; j <= val.C; ++j) {
                                var ixfe = val.rkrec[j - val.c][0];
                                temp_val = ({
                                    ixfe: ixfe,
                                    XF: XFs[ixfe],
                                    v: val.rkrec[j - val.c][1],
                                    t: 'n'
                                });
                                if (BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe >> 8) & 0x1F];
                                safe_format_xf(temp_val, options, wb.opts.Date1904);
                                addcell({
                                    c: j,
                                    r: val.r
                                }, temp_val, options);
                            }
                        }
                        break;
                    case 'Formula':
                        {
                            if (val.val == 'String') {
                                last_formula = val;
                                break;
                            }
                            temp_val = make_cell(val.val, val.cell.ixfe, val.tt);
                            temp_val.XF = XFs[temp_val.ixfe];
                            if (options.cellFormula) {
                                var _f = val.formula;
                                if (_f && _f[0] && _f[0][0] && _f[0][0][0] == 'PtgExp') {
                                    var _fr = _f[0][0][1][0],
                                        _fc = _f[0][0][1][1];
                                    var _fe = encode_cell({
                                        r: _fr,
                                        c: _fc
                                    });
                                    if (sharedf[_fe]) temp_val.f = "" + stringify_formula(val.formula, range, val.cell, supbooks, opts);
                                    else temp_val.F = ((options.dense ? (out[_fr] || [])[_fc] : out[_fe]) || {}).F;
                                } else temp_val.f = "" + stringify_formula(val.formula, range, val.cell, supbooks, opts);
                            }
                            if (BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe >> 8) & 0x1F];
                            safe_format_xf(temp_val, options, wb.opts.Date1904);
                            addcell(val.cell, temp_val, options);
                            last_formula = val;
                        }
                        break;
                    case 'String':
                        {
                            if (last_formula) { /* technically always true */
                                last_formula.val = val;
                                temp_val = make_cell(val, last_formula.cell.ixfe, 's');
                                temp_val.XF = XFs[temp_val.ixfe];
                                if (options.cellFormula) {
                                    temp_val.f = "" + stringify_formula(last_formula.formula, range, last_formula.cell, supbooks, opts);
                                }
                                if (BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe >> 8) & 0x1F];
                                safe_format_xf(temp_val, options, wb.opts.Date1904);
                                addcell(last_formula.cell, temp_val, options);
                                last_formula = null;
                            } else throw new Error("String record expects Formula");
                        }
                        break;
                    case 'Array':
                        {
                            arrayf.push(val);
                            var _arraystart = encode_cell(val[0].s);
                            cc = options.dense ? (out[val[0].s.r] || [])[val[0].s.c] : out[_arraystart];
                            if (options.cellFormula && cc) {
                                if (!last_formula) break; /* technically unreachable */
                                if (!_arraystart || !cc) break;
                                cc.f = "" + stringify_formula(val[1], range, val[0], supbooks, opts);
                                cc.F = encode_range(val[0]);
                            }
                        }
                        break;
                    case 'ShrFmla':
                        {
                            if (!cell_valid) break;
                            if (!options.cellFormula) break;
                            if (last_cell) {
                                /* TODO: capture range */
                                if (!last_formula) break; /* technically unreachable */
                                sharedf[encode_cell(last_formula.cell)] = val[0];
                                cc = options.dense ? (out[last_formula.cell.r] || [])[last_formula.cell.c] : out[encode_cell(last_formula.cell)];
                                (cc || {}).f = "" + stringify_formula(val[0], range, lastcell, supbooks, opts);
                            }
                        }
                        break;
                    case 'LabelSst':
                        temp_val = make_cell(sst[val.isst].t, val.ixfe, 's');
                        temp_val.XF = XFs[temp_val.ixfe];
                        if (BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe >> 8) & 0x1F];
                        safe_format_xf(temp_val, options, wb.opts.Date1904);
                        addcell({
                            c: val.c,
                            r: val.r
                        }, temp_val, options);
                        break;
                    case 'Blank':
                        if (options.sheetStubs) {
                            temp_val = ({
                                ixfe: val.ixfe,
                                XF: XFs[val.ixfe],
                                t: 'z'
                            });
                            if (BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe >> 8) & 0x1F];
                            safe_format_xf(temp_val, options, wb.opts.Date1904);
                            addcell({
                                c: val.c,
                                r: val.r
                            }, temp_val, options);
                        }
                        break;
                    case 'MulBlank':
                        if (options.sheetStubs) {
                            for (var _j = val.c; _j <= val.C; ++_j) {
                                var _ixfe = val.ixfe[_j - val.c];
                                temp_val = ({
                                    ixfe: _ixfe,
                                    XF: XFs[_ixfe],
                                    t: 'z'
                                });
                                if (BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe >> 8) & 0x1F];
                                safe_format_xf(temp_val, options, wb.opts.Date1904);
                                addcell({
                                    c: _j,
                                    r: val.r
                                }, temp_val, options);
                            }
                        }
                        break;
                    case 'RString':
                    case 'Label':
                    case 'BIFF2STR':
                        temp_val = make_cell(val.val, val.ixfe, 's');
                        temp_val.XF = XFs[temp_val.ixfe];
                        if (BIFF2Fmt > 0) temp_val.z = BIFF2FmtTable[(temp_val.ixfe >> 8) & 0x1F];
                        safe_format_xf(temp_val, options, wb.opts.Date1904);
                        addcell({
                            c: val.c,
                            r: val.r
                        }, temp_val, options);
                        break;
                    case 'Dimensions':
                        {
                            if (file_depth === 1) range = val; /* TODO: stack */
                        }
                        break;
                    case 'SST':
                        {
                            sst = val;
                        }
                        break;
                    case 'Format':
                        { /* val = [id, fmt] */
                            if (opts.biff == 4) {
                                BIFF2FmtTable[BIFF2Fmt++] = val[1];
                                for (var b4idx = 0; b4idx < BIFF2Fmt + 163; ++b4idx)
                                    if (SSF._table[b4idx] == val[1]) break;
                                if (b4idx >= 163) SSF.load(val[1], BIFF2Fmt + 163);
                            } else SSF.load(val[1], val[0]);
                        }
                        break;
                    case 'BIFF2FORMAT':
                        {
                            BIFF2FmtTable[BIFF2Fmt++] = val;
                            for (var b2idx = 0; b2idx < BIFF2Fmt + 163; ++b2idx)
                                if (SSF._table[b2idx] == val) break;
                            if (b2idx >= 163) SSF.load(val, BIFF2Fmt + 163);
                        }
                        break;
                    case 'MergeCells':
                        merges = merges.concat(val);
                        break;
                    case 'Obj':
                        objects[val.cmo[0]] = opts.lastobj = val;
                        break;
                    case 'TxO':
                        opts.lastobj.TxO = val;
                        break;
                    case 'ImData':
                        opts.lastobj.ImData = val;
                        break;
                    case 'HLink':
                        {
                            for (rngR = val[0].s.r; rngR <= val[0].e.r; ++rngR)
                                for (rngC = val[0].s.c; rngC <= val[0].e.c; ++rngC) {
                                    cc = options.dense ? (out[rngR] || [])[rngC] : out[encode_cell({
                                        c: rngC,
                                        r: rngR
                                    })];
                                    if (cc) cc.l = val[1];
                                }
                        }
                        break;
                    case 'HLinkTooltip':
                        {
                            for (rngR = val[0].s.r; rngR <= val[0].e.r; ++rngR)
                                for (rngC = val[0].s.c; rngC <= val[0].e.c; ++rngC) {
                                    cc = options.dense ? (out[rngR] || [])[rngC] : out[encode_cell({
                                        c: rngC,
                                        r: rngR
                                    })];
                                    if (cc && cc.l) cc.l.Tooltip = val[1];
                                }
                        }
                        break;
                        /* Comments */
                    case 'Note':
                        {
                            if (opts.biff <= 5 && opts.biff >= 2) break; /* TODO: BIFF5 */
                            cc = options.dense ? (out[val[0].r] || [])[val[0].c] : out[encode_cell(val[0])];
                            var noteobj = objects[val[2]];
                            if (!cc) break;
                            if (!cc.c) cc.c = [];
                            cmnt = {
                                a: val[1],
                                t: noteobj.TxO.t
                            };
                            cc.c.push(cmnt);
                        }
                        break;
                    default:
                        switch (R.n) { /* nested */
                            case 'ClrtClient':
                                break;
                            case 'XFExt':
                                update_xfext(XFs[val.ixfe], val.ext);
                                break;
                            case 'DefColWidth':
                                defwidth = val;
                                break;
                            case 'DefaultRowHeight':
                                defheight = val[1];
                                break; // TODO: flags
                            case 'ColInfo':
                                {
                                    if (!opts.cellStyles) break;
                                    while (val.e >= val.s) {
                                        colinfo[val.e--] = {
                                            width: val.w / 256
                                        };
                                        if (!seencol) {
                                            seencol = true;
                                            find_mdw_colw(val.w / 256);
                                        }
                                        process_col(colinfo[val.e + 1]);
                                    }
                                }
                                break;
                            case 'Row':
                                {
                                    var rowobj = {};
                                    if (val.level != null) {
                                        rowinfo[val.r] = rowobj;
                                        rowobj.level = val.level;
                                    }
                                    if (val.hidden) {
                                        rowinfo[val.r] = rowobj;
                                        rowobj.hidden = true;
                                    }
                                    if (val.hpt) {
                                        rowinfo[val.r] = rowobj;
                                        rowobj.hpt = val.hpt;
                                        rowobj.hpx = pt2px(val.hpt);
                                    }
                                }
                                break;
                            case 'LeftMargin':
                            case 'RightMargin':
                            case 'TopMargin':
                            case 'BottomMargin':
                                if (!out['!margins']) default_margins(out['!margins'] = {});
                                out['!margins'][Rn.slice(0, -6).toLowerCase()] = val;
                                break;
                            case 'Setup': // TODO
                                if (!out['!margins']) default_margins(out['!margins'] = {});
                                out['!margins'].header = val.header;
                                out['!margins'].footer = val.footer;
                                break;
                            case 'Window2': // TODO
                                // $FlowIgnore
                                if (val.RTL) Workbook.Views[0].RTL = true;
                                break;
                            case 'Header':
                                break; // TODO
                            case 'Footer':
                                break; // TODO
                            case 'HCenter':
                                break; // TODO
                            case 'VCenter':
                                break; // TODO
                            case 'Pls':
                                break; // TODO
                            case 'GCW':
                                break;
                            case 'LHRecord':
                                break;
                            case 'DBCell':
                                break; // TODO
                            case 'EntExU2':
                                break; // TODO
                            case 'SxView':
                                break; // TODO
                            case 'Sxvd':
                                break; // TODO
                            case 'SXVI':
                                break; // TODO
                            case 'SXVDEx':
                                break; // TODO
                            case 'SxIvd':
                                break; // TODO
                            case 'SXString':
                                break; // TODO
                            case 'Sync':
                                break;
                            case 'Addin':
                                break;
                            case 'SXDI':
                                break; // TODO
                            case 'SXLI':
                                break; // TODO
                            case 'SXEx':
                                break; // TODO
                            case 'QsiSXTag':
                                break; // TODO
                            case 'Selection':
                                break;
                            case 'Feat':
                                break;
                            case 'FeatHdr':
                            case 'FeatHdr11':
                                break;
                            case 'Feature11':
                            case 'Feature12':
                            case 'List12':
                                break;
                            case 'Country':
                                country = val;
                                break;
                            case 'RecalcId':
                                break;
                            case 'DxGCol':
                                break; // TODO: htmlify
                            case 'Fbi':
                            case 'Fbi2':
                            case 'GelFrame':
                                break;
                            case 'Font':
                                break; // TODO
                            case 'XFCRC':
                                break; // TODO
                            case 'Style':
                                break; // TODO
                            case 'StyleExt':
                                break; // TODO
                            case 'Palette':
                                palette = val;
                                break;
                            case 'Theme':
                                themes = val;
                                break;
                                /* Protection */
                            case 'ScenarioProtect':
                                break;
                            case 'ObjProtect':
                                break;
                                /* Conditional Formatting */
                            case 'CondFmt12':
                                break;
                                /* Table */
                            case 'Table':
                                break; // TODO
                            case 'TableStyles':
                                break; // TODO
                            case 'TableStyle':
                                break; // TODO
                            case 'TableStyleElement':
                                break; // TODO
                                /* PivotTable */
                            case 'SXStreamID':
                                break; // TODO
                            case 'SXVS':
                                break; // TODO
                            case 'DConRef':
                                break; // TODO
                            case 'SXAddl':
                                break; // TODO
                            case 'DConBin':
                                break; // TODO
                            case 'DConName':
                                break; // TODO
                            case 'SXPI':
                                break; // TODO
                            case 'SxFormat':
                                break; // TODO
                            case 'SxSelect':
                                break; // TODO
                            case 'SxRule':
                                break; // TODO
                            case 'SxFilt':
                                break; // TODO
                            case 'SxItm':
                                break; // TODO
                            case 'SxDXF':
                                break; // TODO
                                /* Scenario Manager */
                            case 'ScenMan':
                                break;
                                /* Data Consolidation */
                            case 'DCon':
                                break;
                                /* Watched Cell */
                            case 'CellWatch':
                                break;
                                /* Print Settings */
                            case 'PrintRowCol':
                                break;
                            case 'PrintGrid':
                                break;
                            case 'PrintSize':
                                break;
                            case 'XCT':
                                break;
                            case 'CRN':
                                break;
                            case 'Scl':
                                {
                                    //console.log("Zoom Level:", val[0]/val[1],val);
                                }
                                break;
                            case 'SheetExt':
                                {
                                    /* empty */
                                }
                                break;
                            case 'SheetExtOptional':
                                {
                                    /* empty */
                                }
                                break;
                                /* VBA */
                            case 'ObNoMacros':
                                {
                                    /* empty */
                                }
                                break;
                            case 'ObProj':
                                {
                                    /* empty */
                                }
                                break;
                            case 'CodeName':
                                {
                                    if (!cur_sheet) Workbook.WBProps.CodeName = val || "ThisWorkbook";
                                    else wsprops.CodeName = val || wsprops.name;
                                }
                                break;
                            case 'GUIDTypeLib':
                                {
                                    /* empty */
                                }
                                break;
                            case 'WOpt':
                                break; // TODO: WTF?
                            case 'PhoneticInfo':
                                break;
                            case 'OleObjectSize':
                                break;
                                /* Differential Formatting */
                            case 'DXF':
                            case 'DXFN':
                            case 'DXFN12':
                            case 'DXFN12List':
                            case 'DXFN12NoCB':
                                break;
                                /* Data Validation */
                            case 'Dv':
                            case 'DVal':
                                break;
                                /* Data Series */
                            case 'BRAI':
                            case 'Series':
                            case 'SeriesText':
                                break;
                                /* Data Connection */
                            case 'DConn':
                                break;
                            case 'DbOrParamQry':
                                break;
                            case 'DBQueryExt':
                                break;
                            case 'OleDbConn':
                                break;
                            case 'ExtString':
                                break;
                                /* Formatting */
                            case 'IFmtRecord':
                                break;
                            case 'CondFmt':
                            case 'CF':
                            case 'CF12':
                            case 'CFEx':
                                break;
                                /* Explicitly Ignored */
                            case 'Excel9File':
                                break;
                            case 'Units':
                                break;
                            case 'InterfaceHdr':
                            case 'Mms':
                            case 'InterfaceEnd':
                            case 'DSF':
                                break;
                            case 'BuiltInFnGroupCount':
                                /* 2.4.30 0x0E or 0x10 but excel 2011 generates 0x11? */ break;
                                /* View Stuff */
                            case 'Window1':
                            case 'HideObj':
                            case 'GridSet':
                            case 'Guts':
                            case 'UserBView':
                            case 'UserSViewBegin':
                            case 'UserSViewEnd':
                            case 'Pane':
                                break;
                            default:
                                switch (R.n) { /* nested */
                                    /* Chart */
                                    case 'Dat':
                                    case 'Begin':
                                    case 'End':
                                    case 'StartBlock':
                                    case 'EndBlock':
                                    case 'Frame':
                                    case 'Area':
                                    case 'Axis':
                                    case 'AxisLine':
                                    case 'Tick':
                                        break;
                                    case 'AxesUsed':
                                    case 'CrtLayout12':
                                    case 'CrtLayout12A':
                                    case 'CrtLink':
                                    case 'CrtLine':
                                    case 'CrtMlFrt':
                                    case 'CrtMlFrtContinue':
                                        break;
                                    case 'LineFormat':
                                    case 'AreaFormat':
                                    case 'Chart':
                                    case 'Chart3d':
                                    case 'Chart3DBarShape':
                                    case 'ChartFormat':
                                    case 'ChartFrtInfo':
                                        break;
                                    case 'PlotArea':
                                    case 'PlotGrowth':
                                        break;
                                    case 'SeriesList':
                                    case 'SerParent':
                                    case 'SerAuxTrend':
                                        break;
                                    case 'DataFormat':
                                    case 'SerToCrt':
                                    case 'FontX':
                                        break;
                                    case 'CatSerRange':
                                    case 'AxcExt':
                                    case 'SerFmt':
                                        break;
                                    case 'ShtProps':
                                        break;
                                    case 'DefaultText':
                                    case 'Text':
                                    case 'CatLab':
                                        break;
                                    case 'DataLabExtContents':
                                        break;
                                    case 'Legend':
                                    case 'LegendException':
                                        break;
                                    case 'Pie':
                                    case 'Scatter':
                                        break;
                                    case 'PieFormat':
                                    case 'MarkerFormat':
                                        break;
                                    case 'StartObject':
                                    case 'EndObject':
                                        break;
                                    case 'AlRuns':
                                    case 'ObjectLink':
                                        break;
                                    case 'SIIndex':
                                        break;
                                    case 'AttachedLabel':
                                    case 'YMult':
                                        break;
                                        /* Chart Group */
                                    case 'Line':
                                    case 'Bar':
                                        break;
                                    case 'Surf':
                                        break;
                                        /* Axis Group */
                                    case 'AxisParent':
                                        break;
                                    case 'Pos':
                                        break;
                                    case 'ValueRange':
                                        break;
                                        /* Pivot Chart */
                                    case 'SXViewEx9':
                                        break; // TODO
                                    case 'SXViewLink':
                                        break;
                                    case 'PivotChartBits':
                                        break;
                                    case 'SBaseRef':
                                        break;
                                    case 'TextPropsStream':
                                        break;
                                        /* Chart Misc */
                                    case 'LnExt':
                                        break;
                                    case 'MkrExt':
                                        break;
                                    case 'CrtCoopt':
                                        break;
                                        /* Query Table */
                                    case 'Qsi':
                                    case 'Qsif':
                                    case 'Qsir':
                                    case 'QsiSXTag':
                                        break;
                                    case 'TxtQry':
                                        break;
                                        /* Filter */
                                    case 'FilterMode':
                                        break;
                                    case 'AutoFilter':
                                    case 'AutoFilterInfo':
                                        break;
                                    case 'AutoFilter12':
                                        break;
                                    case 'DropDownObjIds':
                                        break;
                                    case 'Sort':
                                        break;
                                    case 'SortData':
                                        break;
                                        /* Drawing */
                                    case 'ShapePropsStream':
                                        break;
                                    case 'MsoDrawing':
                                    case 'MsoDrawingGroup':
                                    case 'MsoDrawingSelection':
                                        break;
                                        /* Pub Stuff */
                                    case 'WebPub':
                                    case 'AutoWebPub':
                                        break;
                                        /* Print Stuff */
                                    case 'HeaderFooter':
                                    case 'HFPicture':
                                    case 'PLV':
                                    case 'HorizontalPageBreaks':
                                    case 'VerticalPageBreaks':
                                        break;
                                        /* Behavioral */
                                    case 'Backup':
                                    case 'CompressPictures':
                                    case 'Compat12':
                                        break;
                                        /* Should not Happen */
                                    case 'Continue':
                                    case 'ContinueFrt12':
                                        break;
                                        /* Future Records */
                                    case 'FrtFontList':
                                    case 'FrtWrapper':
                                        break;
                                    default:
                                        switch (R.n) { /* nested */
                                            /* BIFF5 records */
                                            case 'TabIdConf':
                                            case 'Radar':
                                            case 'RadarArea':
                                            case 'DropBar':
                                            case 'Intl':
                                            case 'CoordList':
                                            case 'SerAuxErrBar':
                                                break;
                                                /* BIFF2-4 records */
                                            case 'BIFF2FONTCLR':
                                            case 'BIFF2FMTCNT':
                                            case 'BIFF2FONTXTRA':
                                                break;
                                            case 'BIFF2XF':
                                            case 'BIFF3XF':
                                            case 'BIFF4XF':
                                                break;
                                            case 'BIFF4FMTCNT':
                                            case 'BIFF2ROW':
                                            case 'BIFF2WINDOW2':
                                                break;
                                                /* Miscellaneous */
                                            case 'SCENARIO':
                                            case 'DConBin':
                                            case 'PicF':
                                            case 'DataLabExt':
                                            case 'Lel':
                                            case 'BopPop':
                                            case 'BopPopCustom':
                                            case 'RealTimeData':
                                            case 'Name':
                                                break;
                                            case 'LHNGraph':
                                            case 'FnGroupName':
                                            case 'AddMenu':
                                            case 'LPr':
                                                break;
                                            case 'ListObj':
                                            case 'ListField':
                                                break;
                                            case 'RRSort':
                                                break;
                                            case 'BigName':
                                                break;
                                            case 'ToolbarHdr':
                                            case 'ToolbarEnd':
                                                break;
                                            case 'DDEObjName':
                                                break;
                                            case 'FRTArchId$':
                                                break;
                                            default:
                                                if (options.WTF) throw 'Unrecognized Record ' + R.n;
                                        }
                                }
                        }
                }
            } else blob.l += length;
        }
        wb.SheetNames = keys(Directory).sort(function (a, b) {
            return Number(a) - Number(b);
        }).map(function (x) {
            return Directory[x].name;
        });
        if (!options.bookSheets) wb.Sheets = Sheets;
        if (wb.Sheets) FilterDatabases.forEach(function (r, i) {
            wb.Sheets[wb.SheetNames[i]]['!autofilter'] = r;
        });
        wb.Preamble = Preamble;
        wb.Strings = sst;
        wb.SSF = SSF.get_table();
        if (opts.enc) wb.Encryption = opts.enc;
        if (themes) wb.Themes = themes;
        wb.Metadata = {};
        if (country !== undefined) wb.Metadata.Country = country;
        if (supbooks.names.length > 0) Workbook.Names = supbooks.names;
        wb.Workbook = Workbook;
        return wb;
    }
    /* TODO: split props*/
    var PSCLSID = {
        SI: "e0859ff2f94f6810ab9108002b27b3d9",
        DSI: "02d5cdd59c2e1b10939708002b2cf9ae",
        UDI: "05d5cdd59c2e1b10939708002b2cf9ae"
    };
    function parse_xls_props(cfb, props, o) {
        /* [MS-OSHARED] 2.3.3.2.2 Document Summary Information Property Set */
        var DSI = CFB.find(cfb, '!DocumentSummaryInformation');
        if (DSI && DSI.size > 0) try {
            var DocSummary = parse_PropertySetStream(DSI, DocSummaryPIDDSI, PSCLSID.DSI);
            for (var d in DocSummary) props[d] = DocSummary[d];
        } catch (e) {
            if (o.WTF) throw e; /* empty */
        }
        /* [MS-OSHARED] 2.3.3.2.1 Summary Information Property Set*/
        var SI = CFB.find(cfb, '!SummaryInformation');
        if (SI && SI.size > 0) try {
            var Summary = parse_PropertySetStream(SI, SummaryPIDSI, PSCLSID.SI);
            for (var s in Summary)
                if (props[s] == null) props[s] = Summary[s];
        } catch (e) {
            if (o.WTF) throw e; /* empty */
        }
        if (props.HeadingPairs && props.TitlesOfParts) {
            load_props_pairs(props.HeadingPairs, props.TitlesOfParts, props, o);
            delete props.HeadingPairs;
            delete props.TitlesOfParts;
        }
    }
    function write_xls_props(wb, cfb) {
        var DSEntries = [],
            SEntries = [],
            CEntries = [];
        var i = 0,
            Keys;
        if (wb.Props) {
            Keys = keys(wb.Props);
            // $FlowIgnore
            for (i = 0; i < Keys.length; ++i)(DocSummaryRE.hasOwnProperty(Keys[i]) ? DSEntries : SummaryRE.hasOwnProperty(Keys[i]) ? SEntries : CEntries).push([Keys[i], wb.Props[Keys[i]]]);
        }
        if (wb.Custprops) {
            Keys = keys(wb.Custprops);
            // $FlowIgnore
            for (i = 0; i < Keys.length; ++i)
                if (!(wb.Props || {}).hasOwnProperty(Keys[i]))(DocSummaryRE.hasOwnProperty(Keys[i]) ? DSEntries : SummaryRE.hasOwnProperty(Keys[i]) ? SEntries : CEntries).push([Keys[i], wb.Custprops[Keys[i]]]);
        }
        var CEntries2 = [];
        for (i = 0; i < CEntries.length; ++i) {
            if (XLSPSSkip.indexOf(CEntries[i][0]) > -1) continue;
            if (CEntries[i][1] == null) continue;
            CEntries2.push(CEntries[i]);
        }
        if (SEntries.length) CFB.utils.cfb_add(cfb, "/\u0005SummaryInformation", write_PropertySetStream(SEntries, PSCLSID.SI, SummaryRE, SummaryPIDSI));
        if (DSEntries.length || CEntries2.length) CFB.utils.cfb_add(cfb, "/\u0005DocumentSummaryInformation", write_PropertySetStream(DSEntries, PSCLSID.DSI, DocSummaryRE, DocSummaryPIDDSI, CEntries2.length ? CEntries2 : null, PSCLSID.UDI));
    }
    function parse_xlscfb(cfb, options) {
        if (!options) options = {};
        fix_read_opts(options);
        reset_cp();
        if (options.codepage) set_ansi(options.codepage);
        var CompObj, WB;
        if (cfb.FullPaths) {
            if (CFB.find(cfb, '/encryption')) throw new Error("File is password-protected");
            CompObj = CFB.find(cfb, '!CompObj');
            WB = CFB.find(cfb, '/Workbook') || CFB.find(cfb, '/Book');
        } else {
            switch (options.type) {
                case 'base64':
                    cfb = s2a(Base64.decode(cfb));
                    break;
                case 'binary':
                    cfb = s2a(cfb);
                    break;
                case 'buffer':
                    break;
                case 'array':
                    if (!Array.isArray(cfb)) cfb = Array.prototype.slice.call(cfb);
                    break;
            }
            prep_blob(cfb, 0);
            WB = ({
                content: cfb
            });
        }
        var WorkbookP;
        var _data;
        if (CompObj) parse_compobj(CompObj);
        if (options.bookProps && !options.bookSheets) WorkbookP = ({});
        else {
            var T = has_buf ? 'buffer' : 'array';
            if (WB && WB.content) WorkbookP = parse_workbook(WB.content, options);
            /* Quattro Pro 7-8 */
            else if ((_data = CFB.find(cfb, 'PerfectOffice_MAIN')) && _data.content) WorkbookP = WK_.to_workbook(_data.content, (options.type = T, options));
            /* Quattro Pro 9 */
            else if ((_data = CFB.find(cfb, 'NativeContent_MAIN')) && _data.content) WorkbookP = WK_.to_workbook(_data.content, (options.type = T, options));
            else throw new Error("Cannot find Workbook stream");
            if (options.bookVBA && cfb.FullPaths && CFB.find(cfb, '/_VBA_PROJECT_CUR/VBA/dir')) WorkbookP.vbaraw = make_vba_xls(cfb);
        }
        var props = {};
        if (cfb.FullPaths) parse_xls_props(cfb, props, options);
        WorkbookP.Props = WorkbookP.Custprops = props; /* TODO: split up properties */
        if (options.bookFiles) WorkbookP.cfb = cfb;
        /*WorkbookP.CompObjP = CompObjP; // TODO: storage? */
        return WorkbookP;
    }
    function write_xlscfb(wb, opts) {
        var o = opts || {};
        var cfb = CFB.utils.cfb_new({
            root: "R"
        });
        var wbpath = "/Workbook";
        switch (o.bookType || "xls") {
            case "xls":
                o.bookType = "biff8";
                /* falls through */
            case "xla":
                if (!o.bookType) o.bookType = "xla";
                /* falls through */
            case "biff8":
                wbpath = "/Workbook";
                o.biff = 8;
                break;
            case "biff5":
                wbpath = "/Book";
                o.biff = 5;
                break;
            default:
                throw new Error("invalid type " + o.bookType + " for XLS CFB");
        }
        CFB.utils.cfb_add(cfb, wbpath, write_biff_buf(wb, o));
        if (o.biff == 8 && (wb.Props || wb.Custprops)) write_xls_props(wb, cfb);
        // TODO: SI, DSI, CO
        if (o.biff == 8 && wb.vbaraw) fill_vba_xls(cfb, CFB.read(wb.vbaraw, {
            type: typeof wb.vbaraw == "string" ? "binary" : "buffer"
        }));
        return cfb;
    }
    /* [MS-XLSB] 2.3 Record Enumeration */
    var XLSBRecordEnum = {
        0x0000: {
            n: "BrtRowHdr",
            f: parse_BrtRowHdr
        },
        0x0001: {
            n: "BrtCellBlank",
            f: parse_BrtCellBlank
        },
        0x0002: {
            n: "BrtCellRk",
            f: parse_BrtCellRk
        },
        0x0003: {
            n: "BrtCellError",
            f: parse_BrtCellError
        },
        0x0004: {
            n: "BrtCellBool",
            f: parse_BrtCellBool
        },
        0x0005: {
            n: "BrtCellReal",
            f: parse_BrtCellReal
        },
        0x0006: {
            n: "BrtCellSt",
            f: parse_BrtCellSt
        },
        0x0007: {
            n: "BrtCellIsst",
            f: parse_BrtCellIsst
        },
        0x0008: {
            n: "BrtFmlaString",
            f: parse_BrtFmlaString
        },
        0x0009: {
            n: "BrtFmlaNum",
            f: parse_BrtFmlaNum
        },
        0x000A: {
            n: "BrtFmlaBool",
            f: parse_BrtFmlaBool
        },
        0x000B: {
            n: "BrtFmlaError",
            f: parse_BrtFmlaError
        },
        0x0010: {
            n: "BrtFRTArchID$",
            f: parse_BrtFRTArchID$
        },
        0x0013: {
            n: "BrtSSTItem",
            f: parse_RichStr
        },
        0x0014: {
            n: "BrtPCDIMissing"
        },
        0x0015: {
            n: "BrtPCDINumber"
        },
        0x0016: {
            n: "BrtPCDIBoolean"
        },
        0x0017: {
            n: "BrtPCDIError"
        },
        0x0018: {
            n: "BrtPCDIString"
        },
        0x0019: {
            n: "BrtPCDIDatetime"
        },
        0x001A: {
            n: "BrtPCDIIndex"
        },
        0x001B: {
            n: "BrtPCDIAMissing"
        },
        0x001C: {
            n: "BrtPCDIANumber"
        },
        0x001D: {
            n: "BrtPCDIABoolean"
        },
        0x001E: {
            n: "BrtPCDIAError"
        },
        0x001F: {
            n: "BrtPCDIAString"
        },
        0x0020: {
            n: "BrtPCDIADatetime"
        },
        0x0021: {
            n: "BrtPCRRecord"
        },
        0x0022: {
            n: "BrtPCRRecordDt"
        },
        0x0023: {
            n: "BrtFRTBegin"
        },
        0x0024: {
            n: "BrtFRTEnd"
        },
        0x0025: {
            n: "BrtACBegin"
        },
        0x0026: {
            n: "BrtACEnd"
        },
        0x0027: {
            n: "BrtName",
            f: parse_BrtName
        },
        0x0028: {
            n: "BrtIndexRowBlock"
        },
        0x002A: {
            n: "BrtIndexBlock"
        },
        0x002B: {
            n: "BrtFont",
            f: parse_BrtFont
        },
        0x002C: {
            n: "BrtFmt",
            f: parse_BrtFmt
        },
        0x002D: {
            n: "BrtFill",
            f: parse_BrtFill
        },
        0x002E: {
            n: "BrtBorder",
            f: parse_BrtBorder
        },
        0x002F: {
            n: "BrtXF",
            f: parse_BrtXF
        },
        0x0030: {
            n: "BrtStyle"
        },
        0x0031: {
            n: "BrtCellMeta"
        },
        0x0032: {
            n: "BrtValueMeta"
        },
        0x0033: {
            n: "BrtMdb"
        },
        0x0034: {
            n: "BrtBeginFmd"
        },
        0x0035: {
            n: "BrtEndFmd"
        },
        0x0036: {
            n: "BrtBeginMdx"
        },
        0x0037: {
            n: "BrtEndMdx"
        },
        0x0038: {
            n: "BrtBeginMdxTuple"
        },
        0x0039: {
            n: "BrtEndMdxTuple"
        },
        0x003A: {
            n: "BrtMdxMbrIstr"
        },
        0x003B: {
            n: "BrtStr"
        },
        0x003C: {
            n: "BrtColInfo",
            f: parse_ColInfo
        },
        0x003E: {
            n: "BrtCellRString"
        },
        0x003F: {
            n: "BrtCalcChainItem$",
            f: parse_BrtCalcChainItem$
        },
        0x0040: {
            n: "BrtDVal"
        },
        0x0041: {
            n: "BrtSxvcellNum"
        },
        0x0042: {
            n: "BrtSxvcellStr"
        },
        0x0043: {
            n: "BrtSxvcellBool"
        },
        0x0044: {
            n: "BrtSxvcellErr"
        },
        0x0045: {
            n: "BrtSxvcellDate"
        },
        0x0046: {
            n: "BrtSxvcellNil"
        },
        0x0080: {
            n: "BrtFileVersion"
        },
        0x0081: {
            n: "BrtBeginSheet"
        },
        0x0082: {
            n: "BrtEndSheet"
        },
        0x0083: {
            n: "BrtBeginBook",
            f: parsenoop,
            p: 0
        },
        0x0084: {
            n: "BrtEndBook"
        },
        0x0085: {
            n: "BrtBeginWsViews"
        },
        0x0086: {
            n: "BrtEndWsViews"
        },
        0x0087: {
            n: "BrtBeginBookViews"
        },
        0x0088: {
            n: "BrtEndBookViews"
        },
        0x0089: {
            n: "BrtBeginWsView",
            f: parse_BrtBeginWsView
        },
        0x008A: {
            n: "BrtEndWsView"
        },
        0x008B: {
            n: "BrtBeginCsViews"
        },
        0x008C: {
            n: "BrtEndCsViews"
        },
        0x008D: {
            n: "BrtBeginCsView"
        },
        0x008E: {
            n: "BrtEndCsView"
        },
        0x008F: {
            n: "BrtBeginBundleShs"
        },
        0x0090: {
            n: "BrtEndBundleShs"
        },
        0x0091: {
            n: "BrtBeginSheetData"
        },
        0x0092: {
            n: "BrtEndSheetData"
        },
        0x0093: {
            n: "BrtWsProp",
            f: parse_BrtWsProp
        },
        0x0094: {
            n: "BrtWsDim",
            f: parse_BrtWsDim,
            p: 16
        },
        0x0097: {
            n: "BrtPane"
        },
        0x0098: {
            n: "BrtSel"
        },
        0x0099: {
            n: "BrtWbProp",
            f: parse_BrtWbProp
        },
        0x009A: {
            n: "BrtWbFactoid"
        },
        0x009B: {
            n: "BrtFileRecover"
        },
        0x009C: {
            n: "BrtBundleSh",
            f: parse_BrtBundleSh
        },
        0x009D: {
            n: "BrtCalcProp"
        },
        0x009E: {
            n: "BrtBookView"
        },
        0x009F: {
            n: "BrtBeginSst",
            f: parse_BrtBeginSst
        },
        0x00A0: {
            n: "BrtEndSst"
        },
        0x00A1: {
            n: "BrtBeginAFilter",
            f: parse_UncheckedRfX
        },
        0x00A2: {
            n: "BrtEndAFilter"
        },
        0x00A3: {
            n: "BrtBeginFilterColumn"
        },
        0x00A4: {
            n: "BrtEndFilterColumn"
        },
        0x00A5: {
            n: "BrtBeginFilters"
        },
        0x00A6: {
            n: "BrtEndFilters"
        },
        0x00A7: {
            n: "BrtFilter"
        },
        0x00A8: {
            n: "BrtColorFilter"
        },
        0x00A9: {
            n: "BrtIconFilter"
        },
        0x00AA: {
            n: "BrtTop10Filter"
        },
        0x00AB: {
            n: "BrtDynamicFilter"
        },
        0x00AC: {
            n: "BrtBeginCustomFilters"
        },
        0x00AD: {
            n: "BrtEndCustomFilters"
        },
        0x00AE: {
            n: "BrtCustomFilter"
        },
        0x00AF: {
            n: "BrtAFilterDateGroupItem"
        },
        0x00B0: {
            n: "BrtMergeCell",
            f: parse_BrtMergeCell
        },
        0x00B1: {
            n: "BrtBeginMergeCells"
        },
        0x00B2: {
            n: "BrtEndMergeCells"
        },
        0x00B3: {
            n: "BrtBeginPivotCacheDef"
        },
        0x00B4: {
            n: "BrtEndPivotCacheDef"
        },
        0x00B5: {
            n: "BrtBeginPCDFields"
        },
        0x00B6: {
            n: "BrtEndPCDFields"
        },
        0x00B7: {
            n: "BrtBeginPCDField"
        },
        0x00B8: {
            n: "BrtEndPCDField"
        },
        0x00B9: {
            n: "BrtBeginPCDSource"
        },
        0x00BA: {
            n: "BrtEndPCDSource"
        },
        0x00BB: {
            n: "BrtBeginPCDSRange"
        },
        0x00BC: {
            n: "BrtEndPCDSRange"
        },
        0x00BD: {
            n: "BrtBeginPCDFAtbl"
        },
        0x00BE: {
            n: "BrtEndPCDFAtbl"
        },
        0x00BF: {
            n: "BrtBeginPCDIRun"
        },
        0x00C0: {
            n: "BrtEndPCDIRun"
        },
        0x00C1: {
            n: "BrtBeginPivotCacheRecords"
        },
        0x00C2: {
            n: "BrtEndPivotCacheRecords"
        },
        0x00C3: {
            n: "BrtBeginPCDHierarchies"
        },
        0x00C4: {
            n: "BrtEndPCDHierarchies"
        },
        0x00C5: {
            n: "BrtBeginPCDHierarchy"
        },
        0x00C6: {
            n: "BrtEndPCDHierarchy"
        },
        0x00C7: {
            n: "BrtBeginPCDHFieldsUsage"
        },
        0x00C8: {
            n: "BrtEndPCDHFieldsUsage"
        },
        0x00C9: {
            n: "BrtBeginExtConnection"
        },
        0x00CA: {
            n: "BrtEndExtConnection"
        },
        0x00CB: {
            n: "BrtBeginECDbProps"
        },
        0x00CC: {
            n: "BrtEndECDbProps"
        },
        0x00CD: {
            n: "BrtBeginECOlapProps"
        },
        0x00CE: {
            n: "BrtEndECOlapProps"
        },
        0x00CF: {
            n: "BrtBeginPCDSConsol"
        },
        0x00D0: {
            n: "BrtEndPCDSConsol"
        },
        0x00D1: {
            n: "BrtBeginPCDSCPages"
        },
        0x00D2: {
            n: "BrtEndPCDSCPages"
        },
        0x00D3: {
            n: "BrtBeginPCDSCPage"
        },
        0x00D4: {
            n: "BrtEndPCDSCPage"
        },
        0x00D5: {
            n: "BrtBeginPCDSCPItem"
        },
        0x00D6: {
            n: "BrtEndPCDSCPItem"
        },
        0x00D7: {
            n: "BrtBeginPCDSCSets"
        },
        0x00D8: {
            n: "BrtEndPCDSCSets"
        },
        0x00D9: {
            n: "BrtBeginPCDSCSet"
        },
        0x00DA: {
            n: "BrtEndPCDSCSet"
        },
        0x00DB: {
            n: "BrtBeginPCDFGroup"
        },
        0x00DC: {
            n: "BrtEndPCDFGroup"
        },
        0x00DD: {
            n: "BrtBeginPCDFGItems"
        },
        0x00DE: {
            n: "BrtEndPCDFGItems"
        },
        0x00DF: {
            n: "BrtBeginPCDFGRange"
        },
        0x00E0: {
            n: "BrtEndPCDFGRange"
        },
        0x00E1: {
            n: "BrtBeginPCDFGDiscrete"
        },
        0x00E2: {
            n: "BrtEndPCDFGDiscrete"
        },
        0x00E3: {
            n: "BrtBeginPCDSDTupleCache"
        },
        0x00E4: {
            n: "BrtEndPCDSDTupleCache"
        },
        0x00E5: {
            n: "BrtBeginPCDSDTCEntries"
        },
        0x00E6: {
            n: "BrtEndPCDSDTCEntries"
        },
        0x00E7: {
            n: "BrtBeginPCDSDTCEMembers"
        },
        0x00E8: {
            n: "BrtEndPCDSDTCEMembers"
        },
        0x00E9: {
            n: "BrtBeginPCDSDTCEMember"
        },
        0x00EA: {
            n: "BrtEndPCDSDTCEMember"
        },
        0x00EB: {
            n: "BrtBeginPCDSDTCQueries"
        },
        0x00EC: {
            n: "BrtEndPCDSDTCQueries"
        },
        0x00ED: {
            n: "BrtBeginPCDSDTCQuery"
        },
        0x00EE: {
            n: "BrtEndPCDSDTCQuery"
        },
        0x00EF: {
            n: "BrtBeginPCDSDTCSets"
        },
        0x00F0: {
            n: "BrtEndPCDSDTCSets"
        },
        0x00F1: {
            n: "BrtBeginPCDSDTCSet"
        },
        0x00F2: {
            n: "BrtEndPCDSDTCSet"
        },
        0x00F3: {
            n: "BrtBeginPCDCalcItems"
        },
        0x00F4: {
            n: "BrtEndPCDCalcItems"
        },
        0x00F5: {
            n: "BrtBeginPCDCalcItem"
        },
        0x00F6: {
            n: "BrtEndPCDCalcItem"
        },
        0x00F7: {
            n: "BrtBeginPRule"
        },
        0x00F8: {
            n: "BrtEndPRule"
        },
        0x00F9: {
            n: "BrtBeginPRFilters"
        },
        0x00FA: {
            n: "BrtEndPRFilters"
        },
        0x00FB: {
            n: "BrtBeginPRFilter"
        },
        0x00FC: {
            n: "BrtEndPRFilter"
        },
        0x00FD: {
            n: "BrtBeginPNames"
        },
        0x00FE: {
            n: "BrtEndPNames"
        },
        0x00FF: {
            n: "BrtBeginPName"
        },
        0x0100: {
            n: "BrtEndPName"
        },
        0x0101: {
            n: "BrtBeginPNPairs"
        },
        0x0102: {
            n: "BrtEndPNPairs"
        },
        0x0103: {
            n: "BrtBeginPNPair"
        },
        0x0104: {
            n: "BrtEndPNPair"
        },
        0x0105: {
            n: "BrtBeginECWebProps"
        },
        0x0106: {
            n: "BrtEndECWebProps"
        },
        0x0107: {
            n: "BrtBeginEcWpTables"
        },
        0x0108: {
            n: "BrtEndECWPTables"
        },
        0x0109: {
            n: "BrtBeginECParams"
        },
        0x010A: {
            n: "BrtEndECParams"
        },
        0x010B: {
            n: "BrtBeginECParam"
        },
        0x010C: {
            n: "BrtEndECParam"
        },
        0x010D: {
            n: "BrtBeginPCDKPIs"
        },
        0x010E: {
            n: "BrtEndPCDKPIs"
        },
        0x010F: {
            n: "BrtBeginPCDKPI"
        },
        0x0110: {
            n: "BrtEndPCDKPI"
        },
        0x0111: {
            n: "BrtBeginDims"
        },
        0x0112: {
            n: "BrtEndDims"
        },
        0x0113: {
            n: "BrtBeginDim"
        },
        0x0114: {
            n: "BrtEndDim"
        },
        0x0115: {
            n: "BrtIndexPartEnd"
        },
        0x0116: {
            n: "BrtBeginStyleSheet"
        },
        0x0117: {
            n: "BrtEndStyleSheet"
        },
        0x0118: {
            n: "BrtBeginSXView"
        },
        0x0119: {
            n: "BrtEndSXVI"
        },
        0x011A: {
            n: "BrtBeginSXVI"
        },
        0x011B: {
            n: "BrtBeginSXVIs"
        },
        0x011C: {
            n: "BrtEndSXVIs"
        },
        0x011D: {
            n: "BrtBeginSXVD"
        },
        0x011E: {
            n: "BrtEndSXVD"
        },
        0x011F: {
            n: "BrtBeginSXVDs"
        },
        0x0120: {
            n: "BrtEndSXVDs"
        },
        0x0121: {
            n: "BrtBeginSXPI"
        },
        0x0122: {
            n: "BrtEndSXPI"
        },
        0x0123: {
            n: "BrtBeginSXPIs"
        },
        0x0124: {
            n: "BrtEndSXPIs"
        },
        0x0125: {
            n: "BrtBeginSXDI"
        },
        0x0126: {
            n: "BrtEndSXDI"
        },
        0x0127: {
            n: "BrtBeginSXDIs"
        },
        0x0128: {
            n: "BrtEndSXDIs"
        },
        0x0129: {
            n: "BrtBeginSXLI"
        },
        0x012A: {
            n: "BrtEndSXLI"
        },
        0x012B: {
            n: "BrtBeginSXLIRws"
        },
        0x012C: {
            n: "BrtEndSXLIRws"
        },
        0x012D: {
            n: "BrtBeginSXLICols"
        },
        0x012E: {
            n: "BrtEndSXLICols"
        },
        0x012F: {
            n: "BrtBeginSXFormat"
        },
        0x0130: {
            n: "BrtEndSXFormat"
        },
        0x0131: {
            n: "BrtBeginSXFormats"
        },
        0x0132: {
            n: "BrtEndSxFormats"
        },
        0x0133: {
            n: "BrtBeginSxSelect"
        },
        0x0134: {
            n: "BrtEndSxSelect"
        },
        0x0135: {
            n: "BrtBeginISXVDRws"
        },
        0x0136: {
            n: "BrtEndISXVDRws"
        },
        0x0137: {
            n: "BrtBeginISXVDCols"
        },
        0x0138: {
            n: "BrtEndISXVDCols"
        },
        0x0139: {
            n: "BrtEndSXLocation"
        },
        0x013A: {
            n: "BrtBeginSXLocation"
        },
        0x013B: {
            n: "BrtEndSXView"
        },
        0x013C: {
            n: "BrtBeginSXTHs"
        },
        0x013D: {
            n: "BrtEndSXTHs"
        },
        0x013E: {
            n: "BrtBeginSXTH"
        },
        0x013F: {
            n: "BrtEndSXTH"
        },
        0x0140: {
            n: "BrtBeginISXTHRws"
        },
        0x0141: {
            n: "BrtEndISXTHRws"
        },
        0x0142: {
            n: "BrtBeginISXTHCols"
        },
        0x0143: {
            n: "BrtEndISXTHCols"
        },
        0x0144: {
            n: "BrtBeginSXTDMPS"
        },
        0x0145: {
            n: "BrtEndSXTDMPs"
        },
        0x0146: {
            n: "BrtBeginSXTDMP"
        },
        0x0147: {
            n: "BrtEndSXTDMP"
        },
        0x0148: {
            n: "BrtBeginSXTHItems"
        },
        0x0149: {
            n: "BrtEndSXTHItems"
        },
        0x014A: {
            n: "BrtBeginSXTHItem"
        },
        0x014B: {
            n: "BrtEndSXTHItem"
        },
        0x014C: {
            n: "BrtBeginMetadata"
        },
        0x014D: {
            n: "BrtEndMetadata"
        },
        0x014E: {
            n: "BrtBeginEsmdtinfo"
        },
        0x014F: {
            n: "BrtMdtinfo"
        },
        0x0150: {
            n: "BrtEndEsmdtinfo"
        },
        0x0151: {
            n: "BrtBeginEsmdb"
        },
        0x0152: {
            n: "BrtEndEsmdb"
        },
        0x0153: {
            n: "BrtBeginEsfmd"
        },
        0x0154: {
            n: "BrtEndEsfmd"
        },
        0x0155: {
            n: "BrtBeginSingleCells"
        },
        0x0156: {
            n: "BrtEndSingleCells"
        },
        0x0157: {
            n: "BrtBeginList"
        },
        0x0158: {
            n: "BrtEndList"
        },
        0x0159: {
            n: "BrtBeginListCols"
        },
        0x015A: {
            n: "BrtEndListCols"
        },
        0x015B: {
            n: "BrtBeginListCol"
        },
        0x015C: {
            n: "BrtEndListCol"
        },
        0x015D: {
            n: "BrtBeginListXmlCPr"
        },
        0x015E: {
            n: "BrtEndListXmlCPr"
        },
        0x015F: {
            n: "BrtListCCFmla"
        },
        0x0160: {
            n: "BrtListTrFmla"
        },
        0x0161: {
            n: "BrtBeginExternals"
        },
        0x0162: {
            n: "BrtEndExternals"
        },
        0x0163: {
            n: "BrtSupBookSrc",
            f: parse_RelID
        },
        0x0165: {
            n: "BrtSupSelf"
        },
        0x0166: {
            n: "BrtSupSame"
        },
        0x0167: {
            n: "BrtSupTabs"
        },
        0x0168: {
            n: "BrtBeginSupBook"
        },
        0x0169: {
            n: "BrtPlaceholderName"
        },
        0x016A: {
            n: "BrtExternSheet",
            f: parse_ExternSheet
        },
        0x016B: {
            n: "BrtExternTableStart"
        },
        0x016C: {
            n: "BrtExternTableEnd"
        },
        0x016E: {
            n: "BrtExternRowHdr"
        },
        0x016F: {
            n: "BrtExternCellBlank"
        },
        0x0170: {
            n: "BrtExternCellReal"
        },
        0x0171: {
            n: "BrtExternCellBool"
        },
        0x0172: {
            n: "BrtExternCellError"
        },
        0x0173: {
            n: "BrtExternCellString"
        },
        0x0174: {
            n: "BrtBeginEsmdx"
        },
        0x0175: {
            n: "BrtEndEsmdx"
        },
        0x0176: {
            n: "BrtBeginMdxSet"
        },
        0x0177: {
            n: "BrtEndMdxSet"
        },
        0x0178: {
            n: "BrtBeginMdxMbrProp"
        },
        0x0179: {
            n: "BrtEndMdxMbrProp"
        },
        0x017A: {
            n: "BrtBeginMdxKPI"
        },
        0x017B: {
            n: "BrtEndMdxKPI"
        },
        0x017C: {
            n: "BrtBeginEsstr"
        },
        0x017D: {
            n: "BrtEndEsstr"
        },
        0x017E: {
            n: "BrtBeginPRFItem"
        },
        0x017F: {
            n: "BrtEndPRFItem"
        },
        0x0180: {
            n: "BrtBeginPivotCacheIDs"
        },
        0x0181: {
            n: "BrtEndPivotCacheIDs"
        },
        0x0182: {
            n: "BrtBeginPivotCacheID"
        },
        0x0183: {
            n: "BrtEndPivotCacheID"
        },
        0x0184: {
            n: "BrtBeginISXVIs"
        },
        0x0185: {
            n: "BrtEndISXVIs"
        },
        0x0186: {
            n: "BrtBeginColInfos"
        },
        0x0187: {
            n: "BrtEndColInfos"
        },
        0x0188: {
            n: "BrtBeginRwBrk"
        },
        0x0189: {
            n: "BrtEndRwBrk"
        },
        0x018A: {
            n: "BrtBeginColBrk"
        },
        0x018B: {
            n: "BrtEndColBrk"
        },
        0x018C: {
            n: "BrtBrk"
        },
        0x018D: {
            n: "BrtUserBookView"
        },
        0x018E: {
            n: "BrtInfo"
        },
        0x018F: {
            n: "BrtCUsr"
        },
        0x0190: {
            n: "BrtUsr"
        },
        0x0191: {
            n: "BrtBeginUsers"
        },
        0x0193: {
            n: "BrtEOF"
        },
        0x0194: {
            n: "BrtUCR"
        },
        0x0195: {
            n: "BrtRRInsDel"
        },
        0x0196: {
            n: "BrtRREndInsDel"
        },
        0x0197: {
            n: "BrtRRMove"
        },
        0x0198: {
            n: "BrtRREndMove"
        },
        0x0199: {
            n: "BrtRRChgCell"
        },
        0x019A: {
            n: "BrtRREndChgCell"
        },
        0x019B: {
            n: "BrtRRHeader"
        },
        0x019C: {
            n: "BrtRRUserView"
        },
        0x019D: {
            n: "BrtRRRenSheet"
        },
        0x019E: {
            n: "BrtRRInsertSh"
        },
        0x019F: {
            n: "BrtRRDefName"
        },
        0x01A0: {
            n: "BrtRRNote"
        },
        0x01A1: {
            n: "BrtRRConflict"
        },
        0x01A2: {
            n: "BrtRRTQSIF"
        },
        0x01A3: {
            n: "BrtRRFormat"
        },
        0x01A4: {
            n: "BrtRREndFormat"
        },
        0x01A5: {
            n: "BrtRRAutoFmt"
        },
        0x01A6: {
            n: "BrtBeginUserShViews"
        },
        0x01A7: {
            n: "BrtBeginUserShView"
        },
        0x01A8: {
            n: "BrtEndUserShView"
        },
        0x01A9: {
            n: "BrtEndUserShViews"
        },
        0x01AA: {
            n: "BrtArrFmla",
            f: parse_BrtArrFmla
        },
        0x01AB: {
            n: "BrtShrFmla",
            f: parse_BrtShrFmla
        },
        0x01AC: {
            n: "BrtTable"
        },
        0x01AD: {
            n: "BrtBeginExtConnections"
        },
        0x01AE: {
            n: "BrtEndExtConnections"
        },
        0x01AF: {
            n: "BrtBeginPCDCalcMems"
        },
        0x01B0: {
            n: "BrtEndPCDCalcMems"
        },
        0x01B1: {
            n: "BrtBeginPCDCalcMem"
        },
        0x01B2: {
            n: "BrtEndPCDCalcMem"
        },
        0x01B3: {
            n: "BrtBeginPCDHGLevels"
        },
        0x01B4: {
            n: "BrtEndPCDHGLevels"
        },
        0x01B5: {
            n: "BrtBeginPCDHGLevel"
        },
        0x01B6: {
            n: "BrtEndPCDHGLevel"
        },
        0x01B7: {
            n: "BrtBeginPCDHGLGroups"
        },
        0x01B8: {
            n: "BrtEndPCDHGLGroups"
        },
        0x01B9: {
            n: "BrtBeginPCDHGLGroup"
        },
        0x01BA: {
            n: "BrtEndPCDHGLGroup"
        },
        0x01BB: {
            n: "BrtBeginPCDHGLGMembers"
        },
        0x01BC: {
            n: "BrtEndPCDHGLGMembers"
        },
        0x01BD: {
            n: "BrtBeginPCDHGLGMember"
        },
        0x01BE: {
            n: "BrtEndPCDHGLGMember"
        },
        0x01BF: {
            n: "BrtBeginQSI"
        },
        0x01C0: {
            n: "BrtEndQSI"
        },
        0x01C1: {
            n: "BrtBeginQSIR"
        },
        0x01C2: {
            n: "BrtEndQSIR"
        },
        0x01C3: {
            n: "BrtBeginDeletedNames"
        },
        0x01C4: {
            n: "BrtEndDeletedNames"
        },
        0x01C5: {
            n: "BrtBeginDeletedName"
        },
        0x01C6: {
            n: "BrtEndDeletedName"
        },
        0x01C7: {
            n: "BrtBeginQSIFs"
        },
        0x01C8: {
            n: "BrtEndQSIFs"
        },
        0x01C9: {
            n: "BrtBeginQSIF"
        },
        0x01CA: {
            n: "BrtEndQSIF"
        },
        0x01CB: {
            n: "BrtBeginAutoSortScope"
        },
        0x01CC: {
            n: "BrtEndAutoSortScope"
        },
        0x01CD: {
            n: "BrtBeginConditionalFormatting"
        },
        0x01CE: {
            n: "BrtEndConditionalFormatting"
        },
        0x01CF: {
            n: "BrtBeginCFRule"
        },
        0x01D0: {
            n: "BrtEndCFRule"
        },
        0x01D1: {
            n: "BrtBeginIconSet"
        },
        0x01D2: {
            n: "BrtEndIconSet"
        },
        0x01D3: {
            n: "BrtBeginDatabar"
        },
        0x01D4: {
            n: "BrtEndDatabar"
        },
        0x01D5: {
            n: "BrtBeginColorScale"
        },
        0x01D6: {
            n: "BrtEndColorScale"
        },
        0x01D7: {
            n: "BrtCFVO"
        },
        0x01D8: {
            n: "BrtExternValueMeta"
        },
        0x01D9: {
            n: "BrtBeginColorPalette"
        },
        0x01DA: {
            n: "BrtEndColorPalette"
        },
        0x01DB: {
            n: "BrtIndexedColor"
        },
        0x01DC: {
            n: "BrtMargins",
            f: parse_BrtMargins
        },
        0x01DD: {
            n: "BrtPrintOptions"
        },
        0x01DE: {
            n: "BrtPageSetup"
        },
        0x01DF: {
            n: "BrtBeginHeaderFooter"
        },
        0x01E0: {
            n: "BrtEndHeaderFooter"
        },
        0x01E1: {
            n: "BrtBeginSXCrtFormat"
        },
        0x01E2: {
            n: "BrtEndSXCrtFormat"
        },
        0x01E3: {
            n: "BrtBeginSXCrtFormats"
        },
        0x01E4: {
            n: "BrtEndSXCrtFormats"
        },
        0x01E5: {
            n: "BrtWsFmtInfo",
            f: parse_BrtWsFmtInfo
        },
        0x01E6: {
            n: "BrtBeginMgs"
        },
        0x01E7: {
            n: "BrtEndMGs"
        },
        0x01E8: {
            n: "BrtBeginMGMaps"
        },
        0x01E9: {
            n: "BrtEndMGMaps"
        },
        0x01EA: {
            n: "BrtBeginMG"
        },
        0x01EB: {
            n: "BrtEndMG"
        },
        0x01EC: {
            n: "BrtBeginMap"
        },
        0x01ED: {
            n: "BrtEndMap"
        },
        0x01EE: {
            n: "BrtHLink",
            f: parse_BrtHLink
        },
        0x01EF: {
            n: "BrtBeginDCon"
        },
        0x01F0: {
            n: "BrtEndDCon"
        },
        0x01F1: {
            n: "BrtBeginDRefs"
        },
        0x01F2: {
            n: "BrtEndDRefs"
        },
        0x01F3: {
            n: "BrtDRef"
        },
        0x01F4: {
            n: "BrtBeginScenMan"
        },
        0x01F5: {
            n: "BrtEndScenMan"
        },
        0x01F6: {
            n: "BrtBeginSct"
        },
        0x01F7: {
            n: "BrtEndSct"
        },
        0x01F8: {
            n: "BrtSlc"
        },
        0x01F9: {
            n: "BrtBeginDXFs"
        },
        0x01FA: {
            n: "BrtEndDXFs"
        },
        0x01FB: {
            n: "BrtDXF"
        },
        0x01FC: {
            n: "BrtBeginTableStyles"
        },
        0x01FD: {
            n: "BrtEndTableStyles"
        },
        0x01FE: {
            n: "BrtBeginTableStyle"
        },
        0x01FF: {
            n: "BrtEndTableStyle"
        },
        0x0200: {
            n: "BrtTableStyleElement"
        },
        0x0201: {
            n: "BrtTableStyleClient"
        },
        0x0202: {
            n: "BrtBeginVolDeps"
        },
        0x0203: {
            n: "BrtEndVolDeps"
        },
        0x0204: {
            n: "BrtBeginVolType"
        },
        0x0205: {
            n: "BrtEndVolType"
        },
        0x0206: {
            n: "BrtBeginVolMain"
        },
        0x0207: {
            n: "BrtEndVolMain"
        },
        0x0208: {
            n: "BrtBeginVolTopic"
        },
        0x0209: {
            n: "BrtEndVolTopic"
        },
        0x020A: {
            n: "BrtVolSubtopic"
        },
        0x020B: {
            n: "BrtVolRef"
        },
        0x020C: {
            n: "BrtVolNum"
        },
        0x020D: {
            n: "BrtVolErr"
        },
        0x020E: {
            n: "BrtVolStr"
        },
        0x020F: {
            n: "BrtVolBool"
        },
        0x0210: {
            n: "BrtBeginCalcChain$"
        },
        0x0211: {
            n: "BrtEndCalcChain$"
        },
        0x0212: {
            n: "BrtBeginSortState"
        },
        0x0213: {
            n: "BrtEndSortState"
        },
        0x0214: {
            n: "BrtBeginSortCond"
        },
        0x0215: {
            n: "BrtEndSortCond"
        },
        0x0216: {
            n: "BrtBookProtection"
        },
        0x0217: {
            n: "BrtSheetProtection"
        },
        0x0218: {
            n: "BrtRangeProtection"
        },
        0x0219: {
            n: "BrtPhoneticInfo"
        },
        0x021A: {
            n: "BrtBeginECTxtWiz"
        },
        0x021B: {
            n: "BrtEndECTxtWiz"
        },
        0x021C: {
            n: "BrtBeginECTWFldInfoLst"
        },
        0x021D: {
            n: "BrtEndECTWFldInfoLst"
        },
        0x021E: {
            n: "BrtBeginECTwFldInfo"
        },
        0x0224: {
            n: "BrtFileSharing"
        },
        0x0225: {
            n: "BrtOleSize"
        },
        0x0226: {
            n: "BrtDrawing",
            f: parse_RelID
        },
        0x0227: {
            n: "BrtLegacyDrawing"
        },
        0x0228: {
            n: "BrtLegacyDrawingHF"
        },
        0x0229: {
            n: "BrtWebOpt"
        },
        0x022A: {
            n: "BrtBeginWebPubItems"
        },
        0x022B: {
            n: "BrtEndWebPubItems"
        },
        0x022C: {
            n: "BrtBeginWebPubItem"
        },
        0x022D: {
            n: "BrtEndWebPubItem"
        },
        0x022E: {
            n: "BrtBeginSXCondFmt"
        },
        0x022F: {
            n: "BrtEndSXCondFmt"
        },
        0x0230: {
            n: "BrtBeginSXCondFmts"
        },
        0x0231: {
            n: "BrtEndSXCondFmts"
        },
        0x0232: {
            n: "BrtBkHim"
        },
        0x0234: {
            n: "BrtColor"
        },
        0x0235: {
            n: "BrtBeginIndexedColors"
        },
        0x0236: {
            n: "BrtEndIndexedColors"
        },
        0x0239: {
            n: "BrtBeginMRUColors"
        },
        0x023A: {
            n: "BrtEndMRUColors"
        },
        0x023C: {
            n: "BrtMRUColor"
        },
        0x023D: {
            n: "BrtBeginDVals"
        },
        0x023E: {
            n: "BrtEndDVals"
        },
        0x0241: {
            n: "BrtSupNameStart"
        },
        0x0242: {
            n: "BrtSupNameValueStart"
        },
        0x0243: {
            n: "BrtSupNameValueEnd"
        },
        0x0244: {
            n: "BrtSupNameNum"
        },
        0x0245: {
            n: "BrtSupNameErr"
        },
        0x0246: {
            n: "BrtSupNameSt"
        },
        0x0247: {
            n: "BrtSupNameNil"
        },
        0x0248: {
            n: "BrtSupNameBool"
        },
        0x0249: {
            n: "BrtSupNameFmla"
        },
        0x024A: {
            n: "BrtSupNameBits"
        },
        0x024B: {
            n: "BrtSupNameEnd"
        },
        0x024C: {
            n: "BrtEndSupBook"
        },
        0x024D: {
            n: "BrtCellSmartTagProperty"
        },
        0x024E: {
            n: "BrtBeginCellSmartTag"
        },
        0x024F: {
            n: "BrtEndCellSmartTag"
        },
        0x0250: {
            n: "BrtBeginCellSmartTags"
        },
        0x0251: {
            n: "BrtEndCellSmartTags"
        },
        0x0252: {
            n: "BrtBeginSmartTags"
        },
        0x0253: {
            n: "BrtEndSmartTags"
        },
        0x0254: {
            n: "BrtSmartTagType"
        },
        0x0255: {
            n: "BrtBeginSmartTagTypes"
        },
        0x0256: {
            n: "BrtEndSmartTagTypes"
        },
        0x0257: {
            n: "BrtBeginSXFilters"
        },
        0x0258: {
            n: "BrtEndSXFilters"
        },
        0x0259: {
            n: "BrtBeginSXFILTER"
        },
        0x025A: {
            n: "BrtEndSXFilter"
        },
        0x025B: {
            n: "BrtBeginFills"
        },
        0x025C: {
            n: "BrtEndFills"
        },
        0x025D: {
            n: "BrtBeginCellWatches"
        },
        0x025E: {
            n: "BrtEndCellWatches"
        },
        0x025F: {
            n: "BrtCellWatch"
        },
        0x0260: {
            n: "BrtBeginCRErrs"
        },
        0x0261: {
            n: "BrtEndCRErrs"
        },
        0x0262: {
            n: "BrtCrashRecErr"
        },
        0x0263: {
            n: "BrtBeginFonts"
        },
        0x0264: {
            n: "BrtEndFonts"
        },
        0x0265: {
            n: "BrtBeginBorders"
        },
        0x0266: {
            n: "BrtEndBorders"
        },
        0x0267: {
            n: "BrtBeginFmts"
        },
        0x0268: {
            n: "BrtEndFmts"
        },
        0x0269: {
            n: "BrtBeginCellXFs"
        },
        0x026A: {
            n: "BrtEndCellXFs"
        },
        0x026B: {
            n: "BrtBeginStyles"
        },
        0x026C: {
            n: "BrtEndStyles"
        },
        0x0271: {
            n: "BrtBigName"
        },
        0x0272: {
            n: "BrtBeginCellStyleXFs"
        },
        0x0273: {
            n: "BrtEndCellStyleXFs"
        },
        0x0274: {
            n: "BrtBeginComments"
        },
        0x0275: {
            n: "BrtEndComments"
        },
        0x0276: {
            n: "BrtBeginCommentAuthors"
        },
        0x0277: {
            n: "BrtEndCommentAuthors"
        },
        0x0278: {
            n: "BrtCommentAuthor",
            f: parse_BrtCommentAuthor
        },
        0x0279: {
            n: "BrtBeginCommentList"
        },
        0x027A: {
            n: "BrtEndCommentList"
        },
        0x027B: {
            n: "BrtBeginComment",
            f: parse_BrtBeginComment
        },
        0x027C: {
            n: "BrtEndComment"
        },
        0x027D: {
            n: "BrtCommentText",
            f: parse_BrtCommentText
        },
        0x027E: {
            n: "BrtBeginOleObjects"
        },
        0x027F: {
            n: "BrtOleObject"
        },
        0x0280: {
            n: "BrtEndOleObjects"
        },
        0x0281: {
            n: "BrtBeginSxrules"
        },
        0x0282: {
            n: "BrtEndSxRules"
        },
        0x0283: {
            n: "BrtBeginActiveXControls"
        },
        0x0284: {
            n: "BrtActiveX"
        },
        0x0285: {
            n: "BrtEndActiveXControls"
        },
        0x0286: {
            n: "BrtBeginPCDSDTCEMembersSortBy"
        },
        0x0288: {
            n: "BrtBeginCellIgnoreECs"
        },
        0x0289: {
            n: "BrtCellIgnoreEC"
        },
        0x028A: {
            n: "BrtEndCellIgnoreECs"
        },
        0x028B: {
            n: "BrtCsProp",
            f: parse_BrtCsProp
        },
        0x028C: {
            n: "BrtCsPageSetup"
        },
        0x028D: {
            n: "BrtBeginUserCsViews"
        },
        0x028E: {
            n: "BrtEndUserCsViews"
        },
        0x028F: {
            n: "BrtBeginUserCsView"
        },
        0x0290: {
            n: "BrtEndUserCsView"
        },
        0x0291: {
            n: "BrtBeginPcdSFCIEntries"
        },
        0x0292: {
            n: "BrtEndPCDSFCIEntries"
        },
        0x0293: {
            n: "BrtPCDSFCIEntry"
        },
        0x0294: {
            n: "BrtBeginListParts"
        },
        0x0295: {
            n: "BrtListPart"
        },
        0x0296: {
            n: "BrtEndListParts"
        },
        0x0297: {
            n: "BrtSheetCalcProp"
        },
        0x0298: {
            n: "BrtBeginFnGroup"
        },
        0x0299: {
            n: "BrtFnGroup"
        },
        0x029A: {
            n: "BrtEndFnGroup"
        },
        0x029B: {
            n: "BrtSupAddin"
        },
        0x029C: {
            n: "BrtSXTDMPOrder"
        },
        0x029D: {
            n: "BrtCsProtection"
        },
        0x029F: {
            n: "BrtBeginWsSortMap"
        },
        0x02A0: {
            n: "BrtEndWsSortMap"
        },
        0x02A1: {
            n: "BrtBeginRRSort"
        },
        0x02A2: {
            n: "BrtEndRRSort"
        },
        0x02A3: {
            n: "BrtRRSortItem"
        },
        0x02A4: {
            n: "BrtFileSharingIso"
        },
        0x02A5: {
            n: "BrtBookProtectionIso"
        },
        0x02A6: {
            n: "BrtSheetProtectionIso"
        },
        0x02A7: {
            n: "BrtCsProtectionIso"
        },
        0x02A8: {
            n: "BrtRangeProtectionIso"
        },
        0x0400: {
            n: "BrtRwDescent"
        },
        0x0401: {
            n: "BrtKnownFonts"
        },
        0x0402: {
            n: "BrtBeginSXTupleSet"
        },
        0x0403: {
            n: "BrtEndSXTupleSet"
        },
        0x0404: {
            n: "BrtBeginSXTupleSetHeader"
        },
        0x0405: {
            n: "BrtEndSXTupleSetHeader"
        },
        0x0406: {
            n: "BrtSXTupleSetHeaderItem"
        },
        0x0407: {
            n: "BrtBeginSXTupleSetData"
        },
        0x0408: {
            n: "BrtEndSXTupleSetData"
        },
        0x0409: {
            n: "BrtBeginSXTupleSetRow"
        },
        0x040A: {
            n: "BrtEndSXTupleSetRow"
        },
        0x040B: {
            n: "BrtSXTupleSetRowItem"
        },
        0x040C: {
            n: "BrtNameExt"
        },
        0x040D: {
            n: "BrtPCDH14"
        },
        0x040E: {
            n: "BrtBeginPCDCalcMem14"
        },
        0x040F: {
            n: "BrtEndPCDCalcMem14"
        },
        0x0410: {
            n: "BrtSXTH14"
        },
        0x0411: {
            n: "BrtBeginSparklineGroup"
        },
        0x0412: {
            n: "BrtEndSparklineGroup"
        },
        0x0413: {
            n: "BrtSparkline"
        },
        0x0414: {
            n: "BrtSXDI14"
        },
        0x0415: {
            n: "BrtWsFmtInfoEx14"
        },
        0x0416: {
            n: "BrtBeginConditionalFormatting14"
        },
        0x0417: {
            n: "BrtEndConditionalFormatting14"
        },
        0x0418: {
            n: "BrtBeginCFRule14"
        },
        0x0419: {
            n: "BrtEndCFRule14"
        },
        0x041A: {
            n: "BrtCFVO14"
        },
        0x041B: {
            n: "BrtBeginDatabar14"
        },
        0x041C: {
            n: "BrtBeginIconSet14"
        },
        0x041D: {
            n: "BrtDVal14"
        },
        0x041E: {
            n: "BrtBeginDVals14"
        },
        0x041F: {
            n: "BrtColor14"
        },
        0x0420: {
            n: "BrtBeginSparklines"
        },
        0x0421: {
            n: "BrtEndSparklines"
        },
        0x0422: {
            n: "BrtBeginSparklineGroups"
        },
        0x0423: {
            n: "BrtEndSparklineGroups"
        },
        0x0425: {
            n: "BrtSXVD14"
        },
        0x0426: {
            n: "BrtBeginSXView14"
        },
        0x0427: {
            n: "BrtEndSXView14"
        },
        0x0428: {
            n: "BrtBeginSXView16"
        },
        0x0429: {
            n: "BrtEndSXView16"
        },
        0x042A: {
            n: "BrtBeginPCD14"
        },
        0x042B: {
            n: "BrtEndPCD14"
        },
        0x042C: {
            n: "BrtBeginExtConn14"
        },
        0x042D: {
            n: "BrtEndExtConn14"
        },
        0x042E: {
            n: "BrtBeginSlicerCacheIDs"
        },
        0x042F: {
            n: "BrtEndSlicerCacheIDs"
        },
        0x0430: {
            n: "BrtBeginSlicerCacheID"
        },
        0x0431: {
            n: "BrtEndSlicerCacheID"
        },
        0x0433: {
            n: "BrtBeginSlicerCache"
        },
        0x0434: {
            n: "BrtEndSlicerCache"
        },
        0x0435: {
            n: "BrtBeginSlicerCacheDef"
        },
        0x0436: {
            n: "BrtEndSlicerCacheDef"
        },
        0x0437: {
            n: "BrtBeginSlicersEx"
        },
        0x0438: {
            n: "BrtEndSlicersEx"
        },
        0x0439: {
            n: "BrtBeginSlicerEx"
        },
        0x043A: {
            n: "BrtEndSlicerEx"
        },
        0x043B: {
            n: "BrtBeginSlicer"
        },
        0x043C: {
            n: "BrtEndSlicer"
        },
        0x043D: {
            n: "BrtSlicerCachePivotTables"
        },
        0x043E: {
            n: "BrtBeginSlicerCacheOlapImpl"
        },
        0x043F: {
            n: "BrtEndSlicerCacheOlapImpl"
        },
        0x0440: {
            n: "BrtBeginSlicerCacheLevelsData"
        },
        0x0441: {
            n: "BrtEndSlicerCacheLevelsData"
        },
        0x0442: {
            n: "BrtBeginSlicerCacheLevelData"
        },
        0x0443: {
            n: "BrtEndSlicerCacheLevelData"
        },
        0x0444: {
            n: "BrtBeginSlicerCacheSiRanges"
        },
        0x0445: {
            n: "BrtEndSlicerCacheSiRanges"
        },
        0x0446: {
            n: "BrtBeginSlicerCacheSiRange"
        },
        0x0447: {
            n: "BrtEndSlicerCacheSiRange"
        },
        0x0448: {
            n: "BrtSlicerCacheOlapItem"
        },
        0x0449: {
            n: "BrtBeginSlicerCacheSelections"
        },
        0x044A: {
            n: "BrtSlicerCacheSelection"
        },
        0x044B: {
            n: "BrtEndSlicerCacheSelections"
        },
        0x044C: {
            n: "BrtBeginSlicerCacheNative"
        },
        0x044D: {
            n: "BrtEndSlicerCacheNative"
        },
        0x044E: {
            n: "BrtSlicerCacheNativeItem"
        },
        0x044F: {
            n: "BrtRangeProtection14"
        },
        0x0450: {
            n: "BrtRangeProtectionIso14"
        },
        0x0451: {
            n: "BrtCellIgnoreEC14"
        },
        0x0457: {
            n: "BrtList14"
        },
        0x0458: {
            n: "BrtCFIcon"
        },
        0x0459: {
            n: "BrtBeginSlicerCachesPivotCacheIDs"
        },
        0x045A: {
            n: "BrtEndSlicerCachesPivotCacheIDs"
        },
        0x045B: {
            n: "BrtBeginSlicers"
        },
        0x045C: {
            n: "BrtEndSlicers"
        },
        0x045D: {
            n: "BrtWbProp14"
        },
        0x045E: {
            n: "BrtBeginSXEdit"
        },
        0x045F: {
            n: "BrtEndSXEdit"
        },
        0x0460: {
            n: "BrtBeginSXEdits"
        },
        0x0461: {
            n: "BrtEndSXEdits"
        },
        0x0462: {
            n: "BrtBeginSXChange"
        },
        0x0463: {
            n: "BrtEndSXChange"
        },
        0x0464: {
            n: "BrtBeginSXChanges"
        },
        0x0465: {
            n: "BrtEndSXChanges"
        },
        0x0466: {
            n: "BrtSXTupleItems"
        },
        0x0468: {
            n: "BrtBeginSlicerStyle"
        },
        0x0469: {
            n: "BrtEndSlicerStyle"
        },
        0x046A: {
            n: "BrtSlicerStyleElement"
        },
        0x046B: {
            n: "BrtBeginStyleSheetExt14"
        },
        0x046C: {
            n: "BrtEndStyleSheetExt14"
        },
        0x046D: {
            n: "BrtBeginSlicerCachesPivotCacheID"
        },
        0x046E: {
            n: "BrtEndSlicerCachesPivotCacheID"
        },
        0x046F: {
            n: "BrtBeginConditionalFormattings"
        },
        0x0470: {
            n: "BrtEndConditionalFormattings"
        },
        0x0471: {
            n: "BrtBeginPCDCalcMemExt"
        },
        0x0472: {
            n: "BrtEndPCDCalcMemExt"
        },
        0x0473: {
            n: "BrtBeginPCDCalcMemsExt"
        },
        0x0474: {
            n: "BrtEndPCDCalcMemsExt"
        },
        0x0475: {
            n: "BrtPCDField14"
        },
        0x0476: {
            n: "BrtBeginSlicerStyles"
        },
        0x0477: {
            n: "BrtEndSlicerStyles"
        },
        0x0478: {
            n: "BrtBeginSlicerStyleElements"
        },
        0x0479: {
            n: "BrtEndSlicerStyleElements"
        },
        0x047A: {
            n: "BrtCFRuleExt"
        },
        0x047B: {
            n: "BrtBeginSXCondFmt14"
        },
        0x047C: {
            n: "BrtEndSXCondFmt14"
        },
        0x047D: {
            n: "BrtBeginSXCondFmts14"
        },
        0x047E: {
            n: "BrtEndSXCondFmts14"
        },
        0x0480: {
            n: "BrtBeginSortCond14"
        },
        0x0481: {
            n: "BrtEndSortCond14"
        },
        0x0482: {
            n: "BrtEndDVals14"
        },
        0x0483: {
            n: "BrtEndIconSet14"
        },
        0x0484: {
            n: "BrtEndDatabar14"
        },
        0x0485: {
            n: "BrtBeginColorScale14"
        },
        0x0486: {
            n: "BrtEndColorScale14"
        },
        0x0487: {
            n: "BrtBeginSxrules14"
        },
        0x0488: {
            n: "BrtEndSxrules14"
        },
        0x0489: {
            n: "BrtBeginPRule14"
        },
        0x048A: {
            n: "BrtEndPRule14"
        },
        0x048B: {
            n: "BrtBeginPRFilters14"
        },
        0x048C: {
            n: "BrtEndPRFilters14"
        },
        0x048D: {
            n: "BrtBeginPRFilter14"
        },
        0x048E: {
            n: "BrtEndPRFilter14"
        },
        0x048F: {
            n: "BrtBeginPRFItem14"
        },
        0x0490: {
            n: "BrtEndPRFItem14"
        },
        0x0491: {
            n: "BrtBeginCellIgnoreECs14"
        },
        0x0492: {
            n: "BrtEndCellIgnoreECs14"
        },
        0x0493: {
            n: "BrtDxf14"
        },
        0x0494: {
            n: "BrtBeginDxF14s"
        },
        0x0495: {
            n: "BrtEndDxf14s"
        },
        0x0499: {
            n: "BrtFilter14"
        },
        0x049A: {
            n: "BrtBeginCustomFilters14"
        },
        0x049C: {
            n: "BrtCustomFilter14"
        },
        0x049D: {
            n: "BrtIconFilter14"
        },
        0x049E: {
            n: "BrtPivotCacheConnectionName"
        },
        0x0800: {
            n: "BrtBeginDecoupledPivotCacheIDs"
        },
        0x0801: {
            n: "BrtEndDecoupledPivotCacheIDs"
        },
        0x0802: {
            n: "BrtDecoupledPivotCacheID"
        },
        0x0803: {
            n: "BrtBeginPivotTableRefs"
        },
        0x0804: {
            n: "BrtEndPivotTableRefs"
        },
        0x0805: {
            n: "BrtPivotTableRef"
        },
        0x0806: {
            n: "BrtSlicerCacheBookPivotTables"
        },
        0x0807: {
            n: "BrtBeginSxvcells"
        },
        0x0808: {
            n: "BrtEndSxvcells"
        },
        0x0809: {
            n: "BrtBeginSxRow"
        },
        0x080A: {
            n: "BrtEndSxRow"
        },
        0x080C: {
            n: "BrtPcdCalcMem15"
        },
        0x0813: {
            n: "BrtQsi15"
        },
        0x0814: {
            n: "BrtBeginWebExtensions"
        },
        0x0815: {
            n: "BrtEndWebExtensions"
        },
        0x0816: {
            n: "BrtWebExtension"
        },
        0x0817: {
            n: "BrtAbsPath15"
        },
        0x0818: {
            n: "BrtBeginPivotTableUISettings"
        },
        0x0819: {
            n: "BrtEndPivotTableUISettings"
        },
        0x081B: {
            n: "BrtTableSlicerCacheIDs"
        },
        0x081C: {
            n: "BrtTableSlicerCacheID"
        },
        0x081D: {
            n: "BrtBeginTableSlicerCache"
        },
        0x081E: {
            n: "BrtEndTableSlicerCache"
        },
        0x081F: {
            n: "BrtSxFilter15"
        },
        0x0820: {
            n: "BrtBeginTimelineCachePivotCacheIDs"
        },
        0x0821: {
            n: "BrtEndTimelineCachePivotCacheIDs"
        },
        0x0822: {
            n: "BrtTimelineCachePivotCacheID"
        },
        0x0823: {
            n: "BrtBeginTimelineCacheIDs"
        },
        0x0824: {
            n: "BrtEndTimelineCacheIDs"
        },
        0x0825: {
            n: "BrtBeginTimelineCacheID"
        },
        0x0826: {
            n: "BrtEndTimelineCacheID"
        },
        0x0827: {
            n: "BrtBeginTimelinesEx"
        },
        0x0828: {
            n: "BrtEndTimelinesEx"
        },
        0x0829: {
            n: "BrtBeginTimelineEx"
        },
        0x082A: {
            n: "BrtEndTimelineEx"
        },
        0x082B: {
            n: "BrtWorkBookPr15"
        },
        0x082C: {
            n: "BrtPCDH15"
        },
        0x082D: {
            n: "BrtBeginTimelineStyle"
        },
        0x082E: {
            n: "BrtEndTimelineStyle"
        },
        0x082F: {
            n: "BrtTimelineStyleElement"
        },
        0x0830: {
            n: "BrtBeginTimelineStylesheetExt15"
        },
        0x0831: {
            n: "BrtEndTimelineStylesheetExt15"
        },
        0x0832: {
            n: "BrtBeginTimelineStyles"
        },
        0x0833: {
            n: "BrtEndTimelineStyles"
        },
        0x0834: {
            n: "BrtBeginTimelineStyleElements"
        },
        0x0835: {
            n: "BrtEndTimelineStyleElements"
        },
        0x0836: {
            n: "BrtDxf15"
        },
        0x0837: {
            n: "BrtBeginDxfs15"
        },
        0x0838: {
            n: "brtEndDxfs15"
        },
        0x0839: {
            n: "BrtSlicerCacheHideItemsWithNoData"
        },
        0x083A: {
            n: "BrtBeginItemUniqueNames"
        },
        0x083B: {
            n: "BrtEndItemUniqueNames"
        },
        0x083C: {
            n: "BrtItemUniqueName"
        },
        0x083D: {
            n: "BrtBeginExtConn15"
        },
        0x083E: {
            n: "BrtEndExtConn15"
        },
        0x083F: {
            n: "BrtBeginOledbPr15"
        },
        0x0840: {
            n: "BrtEndOledbPr15"
        },
        0x0841: {
            n: "BrtBeginDataFeedPr15"
        },
        0x0842: {
            n: "BrtEndDataFeedPr15"
        },
        0x0843: {
            n: "BrtTextPr15"
        },
        0x0844: {
            n: "BrtRangePr15"
        },
        0x0845: {
            n: "BrtDbCommand15"
        },
        0x0846: {
            n: "BrtBeginDbTables15"
        },
        0x0847: {
            n: "BrtEndDbTables15"
        },
        0x0848: {
            n: "BrtDbTable15"
        },
        0x0849: {
            n: "BrtBeginDataModel"
        },
        0x084A: {
            n: "BrtEndDataModel"
        },
        0x084B: {
            n: "BrtBeginModelTables"
        },
        0x084C: {
            n: "BrtEndModelTables"
        },
        0x084D: {
            n: "BrtModelTable"
        },
        0x084E: {
            n: "BrtBeginModelRelationships"
        },
        0x084F: {
            n: "BrtEndModelRelationships"
        },
        0x0850: {
            n: "BrtModelRelationship"
        },
        0x0851: {
            n: "BrtBeginECTxtWiz15"
        },
        0x0852: {
            n: "BrtEndECTxtWiz15"
        },
        0x0853: {
            n: "BrtBeginECTWFldInfoLst15"
        },
        0x0854: {
            n: "BrtEndECTWFldInfoLst15"
        },
        0x0855: {
            n: "BrtBeginECTWFldInfo15"
        },
        0x0856: {
            n: "BrtFieldListActiveItem"
        },
        0x0857: {
            n: "BrtPivotCacheIdVersion"
        },
        0x0858: {
            n: "BrtSXDI15"
        },
        0x0859: {
            n: "BrtBeginModelTimeGroupings"
        },
        0x085A: {
            n: "BrtEndModelTimeGroupings"
        },
        0x085B: {
            n: "BrtBeginModelTimeGrouping"
        },
        0x085C: {
            n: "BrtEndModelTimeGrouping"
        },
        0x085D: {
            n: "BrtModelTimeGroupingCalcCol"
        },
        0x0C00: {
            n: "BrtUid"
        },
        0x0C01: {
            n: "BrtRevisionPtr"
        },
        0xFFFF: {
            n: ""
        }
    };
    var XLSBRE = evert_key(XLSBRecordEnum, 'n');
    /* [MS-XLS] 2.3 Record Enumeration */
    var XLSRecordEnum = {
        0x0003: {
            n: "BIFF2NUM",
            f: parse_BIFF2NUM
        },
        0x0004: {
            n: "BIFF2STR",
            f: parse_BIFF2STR
        },
        0x0006: {
            n: "Formula",
            f: parse_Formula
        },
        0x0009: {
            n: 'BOF',
            f: parse_BOF
        },
        0x000a: {
            n: 'EOF',
            f: parsenoop2
        },
        0x000c: {
            n: "CalcCount",
            f: parseuint16
        },
        0x000d: {
            n: "CalcMode",
            f: parseuint16
        },
        0x000e: {
            n: "CalcPrecision",
            f: parsebool
        },
        0x000f: {
            n: "CalcRefMode",
            f: parsebool
        },
        0x0010: {
            n: "CalcDelta",
            f: parse_Xnum
        },
        0x0011: {
            n: "CalcIter",
            f: parsebool
        },
        0x0012: {
            n: "Protect",
            f: parsebool
        },
        0x0013: {
            n: "Password",
            f: parseuint16
        },
        0x0014: {
            n: "Header",
            f: parse_XLHeaderFooter
        },
        0x0015: {
            n: "Footer",
            f: parse_XLHeaderFooter
        },
        0x0017: {
            n: "ExternSheet",
            f: parse_ExternSheet
        },
        0x0018: {
            n: "Lbl",
            f: parse_Lbl
        },
        0x0019: {
            n: "WinProtect",
            f: parsebool
        },
        0x001a: {
            n: "VerticalPageBreaks"
        },
        0x001b: {
            n: "HorizontalPageBreaks"
        },
        0x001c: {
            n: "Note",
            f: parse_Note
        },
        0x001d: {
            n: "Selection"
        },
        0x0022: {
            n: "Date1904",
            f: parsebool
        },
        0x0023: {
            n: "ExternName",
            f: parse_ExternName
        },
        0x0026: {
            n: "LeftMargin",
            f: parse_Xnum
        },
        0x0027: {
            n: "RightMargin",
            f: parse_Xnum
        },
        0x0028: {
            n: "TopMargin",
            f: parse_Xnum
        },
        0x0029: {
            n: "BottomMargin",
            f: parse_Xnum
        },
        0x002a: {
            n: "PrintRowCol",
            f: parsebool
        },
        0x002b: {
            n: "PrintGrid",
            f: parsebool
        },
        0x002f: {
            n: "FilePass",
            f: parse_FilePass
        },
        0x0031: {
            n: "Font",
            f: parse_Font
        },
        0x0033: {
            n: "PrintSize",
            f: parseuint16
        },
        0x003c: {
            n: "Continue"
        },
        0x003d: {
            n: "Window1",
            f: parse_Window1
        },
        0x0040: {
            n: "Backup",
            f: parsebool
        },
        0x0041: {
            n: "Pane"
        },
        0x0042: {
            n: 'CodePage',
            f: parseuint16
        },
        0x004d: {
            n: "Pls"
        },
        0x0050: {
            n: "DCon"
        },
        0x0051: {
            n: "DConRef"
        },
        0x0052: {
            n: "DConName"
        },
        0x0055: {
            n: "DefColWidth",
            f: parseuint16
        },
        0x0059: {
            n: "XCT"
        },
        0x005a: {
            n: "CRN"
        },
        0x005b: {
            n: "FileSharing"
        },
        0x005c: {
            n: 'WriteAccess',
            f: parse_WriteAccess
        },
        0x005d: {
            n: "Obj",
            f: parse_Obj
        },
        0x005e: {
            n: "Uncalced"
        },
        0x005f: {
            n: "CalcSaveRecalc",
            f: parsebool
        },
        0x0060: {
            n: "Template"
        },
        0x0061: {
            n: "Intl"
        },
        0x0063: {
            n: "ObjProtect",
            f: parsebool
        },
        0x007d: {
            n: "ColInfo",
            f: parse_ColInfo
        },
        0x0080: {
            n: "Guts",
            f: parse_Guts
        },
        0x0081: {
            n: "WsBool",
            f: parse_WsBool
        },
        0x0082: {
            n: "GridSet",
            f: parseuint16
        },
        0x0083: {
            n: "HCenter",
            f: parsebool
        },
        0x0084: {
            n: "VCenter",
            f: parsebool
        },
        0x0085: {
            n: 'BoundSheet8',
            f: parse_BoundSheet8
        },
        0x0086: {
            n: "WriteProtect"
        },
        0x008c: {
            n: "Country",
            f: parse_Country
        },
        0x008d: {
            n: "HideObj",
            f: parseuint16
        },
        0x0090: {
            n: "Sort"
        },
        0x0092: {
            n: "Palette",
            f: parse_Palette
        },
        0x0097: {
            n: "Sync"
        },
        0x0098: {
            n: "LPr"
        },
        0x0099: {
            n: "DxGCol"
        },
        0x009a: {
            n: "FnGroupName"
        },
        0x009b: {
            n: "FilterMode"
        },
        0x009c: {
            n: "BuiltInFnGroupCount",
            f: parseuint16
        },
        0x009d: {
            n: "AutoFilterInfo"
        },
        0x009e: {
            n: "AutoFilter"
        },
        0x00a0: {
            n: "Scl",
            f: parse_Scl
        },
        0x00a1: {
            n: "Setup",
            f: parse_Setup
        },
        0x00ae: {
            n: "ScenMan"
        },
        0x00af: {
            n: "SCENARIO"
        },
        0x00b0: {
            n: "SxView"
        },
        0x00b1: {
            n: "Sxvd"
        },
        0x00b2: {
            n: "SXVI"
        },
        0x00b4: {
            n: "SxIvd"
        },
        0x00b5: {
            n: "SXLI"
        },
        0x00b6: {
            n: "SXPI"
        },
        0x00b8: {
            n: "DocRoute"
        },
        0x00b9: {
            n: "RecipName"
        },
        0x00bd: {
            n: "MulRk",
            f: parse_MulRk
        },
        0x00be: {
            n: "MulBlank",
            f: parse_MulBlank
        },
        0x00c1: {
            n: 'Mms',
            f: parsenoop2
        },
        0x00c5: {
            n: "SXDI"
        },
        0x00c6: {
            n: "SXDB"
        },
        0x00c7: {
            n: "SXFDB"
        },
        0x00c8: {
            n: "SXDBB"
        },
        0x00c9: {
            n: "SXNum"
        },
        0x00ca: {
            n: "SxBool",
            f: parsebool
        },
        0x00cb: {
            n: "SxErr"
        },
        0x00cc: {
            n: "SXInt"
        },
        0x00cd: {
            n: "SXString"
        },
        0x00ce: {
            n: "SXDtr"
        },
        0x00cf: {
            n: "SxNil"
        },
        0x00d0: {
            n: "SXTbl"
        },
        0x00d1: {
            n: "SXTBRGIITM"
        },
        0x00d2: {
            n: "SxTbpg"
        },
        0x00d3: {
            n: "ObProj"
        },
        0x00d5: {
            n: "SXStreamID"
        },
        0x00d7: {
            n: "DBCell"
        },
        0x00d8: {
            n: "SXRng"
        },
        0x00d9: {
            n: "SxIsxoper"
        },
        0x00da: {
            n: "BookBool",
            f: parseuint16
        },
        0x00dc: {
            n: "DbOrParamQry"
        },
        0x00dd: {
            n: "ScenarioProtect",
            f: parsebool
        },
        0x00de: {
            n: "OleObjectSize"
        },
        0x00e0: {
            n: "XF",
            f: parse_XF
        },
        0x00e1: {
            n: 'InterfaceHdr',
            f: parse_InterfaceHdr
        },
        0x00e2: {
            n: 'InterfaceEnd',
            f: parsenoop2
        },
        0x00e3: {
            n: "SXVS"
        },
        0x00e5: {
            n: "MergeCells",
            f: parse_MergeCells
        },
        0x00e9: {
            n: "BkHim"
        },
        0x00eb: {
            n: "MsoDrawingGroup"
        },
        0x00ec: {
            n: "MsoDrawing"
        },
        0x00ed: {
            n: "MsoDrawingSelection"
        },
        0x00ef: {
            n: "PhoneticInfo"
        },
        0x00f0: {
            n: "SxRule"
        },
        0x00f1: {
            n: "SXEx"
        },
        0x00f2: {
            n: "SxFilt"
        },
        0x00f4: {
            n: "SxDXF"
        },
        0x00f5: {
            n: "SxItm"
        },
        0x00f6: {
            n: "SxName"
        },
        0x00f7: {
            n: "SxSelect"
        },
        0x00f8: {
            n: "SXPair"
        },
        0x00f9: {
            n: "SxFmla"
        },
        0x00fb: {
            n: "SxFormat"
        },
        0x00fc: {
            n: "SST",
            f: parse_SST
        },
        0x00fd: {
            n: "LabelSst",
            f: parse_LabelSst
        },
        0x00ff: {
            n: "ExtSST",
            f: parse_ExtSST
        },
        0x0100: {
            n: "SXVDEx"
        },
        0x0103: {
            n: "SXFormula"
        },
        0x0122: {
            n: "SXDBEx"
        },
        0x0137: {
            n: "RRDInsDel"
        },
        0x0138: {
            n: "RRDHead"
        },
        0x013b: {
            n: "RRDChgCell"
        },
        0x013d: {
            n: "RRTabId",
            f: parseuint16a
        },
        0x013e: {
            n: "RRDRenSheet"
        },
        0x013f: {
            n: "RRSort"
        },
        0x0140: {
            n: "RRDMove"
        },
        0x014a: {
            n: "RRFormat"
        },
        0x014b: {
            n: "RRAutoFmt"
        },
        0x014d: {
            n: "RRInsertSh"
        },
        0x014e: {
            n: "RRDMoveBegin"
        },
        0x014f: {
            n: "RRDMoveEnd"
        },
        0x0150: {
            n: "RRDInsDelBegin"
        },
        0x0151: {
            n: "RRDInsDelEnd"
        },
        0x0152: {
            n: "RRDConflict"
        },
        0x0153: {
            n: "RRDDefName"
        },
        0x0154: {
            n: "RRDRstEtxp"
        },
        0x015f: {
            n: "LRng"
        },
        0x0160: {
            n: "UsesELFs",
            f: parsebool
        },
        0x0161: {
            n: "DSF",
            f: parsenoop2
        },
        0x0191: {
            n: "CUsr"
        },
        0x0192: {
            n: "CbUsr"
        },
        0x0193: {
            n: "UsrInfo"
        },
        0x0194: {
            n: "UsrExcl"
        },
        0x0195: {
            n: "FileLock"
        },
        0x0196: {
            n: "RRDInfo"
        },
        0x0197: {
            n: "BCUsrs"
        },
        0x0198: {
            n: "UsrChk"
        },
        0x01a9: {
            n: "UserBView"
        },
        0x01aa: {
            n: "UserSViewBegin"
        },
        0x01ab: {
            n: "UserSViewEnd"
        },
        0x01ac: {
            n: "RRDUserView"
        },
        0x01ad: {
            n: "Qsi"
        },
        0x01ae: {
            n: "SupBook",
            f: parse_SupBook
        },
        0x01af: {
            n: "Prot4Rev",
            f: parsebool
        },
        0x01b0: {
            n: "CondFmt"
        },
        0x01b1: {
            n: "CF"
        },
        0x01b2: {
            n: "DVal"
        },
        0x01b5: {
            n: "DConBin"
        },
        0x01b6: {
            n: "TxO",
            f: parse_TxO
        },
        0x01b7: {
            n: "RefreshAll",
            f: parsebool
        },
        0x01b8: {
            n: "HLink",
            f: parse_HLink
        },
        0x01b9: {
            n: "Lel"
        },
        0x01ba: {
            n: "CodeName",
            f: parse_XLUnicodeString
        },
        0x01bb: {
            n: "SXFDBType"
        },
        0x01bc: {
            n: "Prot4RevPass",
            f: parseuint16
        },
        0x01bd: {
            n: "ObNoMacros"
        },
        0x01be: {
            n: "Dv"
        },
        0x01c0: {
            n: "Excel9File",
            f: parsenoop2
        },
        0x01c1: {
            n: "RecalcId",
            f: parse_RecalcId,
            r: 2
        },
        0x01c2: {
            n: "EntExU2",
            f: parsenoop2
        },
        0x0200: {
            n: "Dimensions",
            f: parse_Dimensions
        },
        0x0201: {
            n: "Blank",
            f: parse_Blank
        },
        0x0203: {
            n: "Number",
            f: parse_Number
        },
        0x0204: {
            n: "Label",
            f: parse_Label
        },
        0x0205: {
            n: "BoolErr",
            f: parse_BoolErr
        },
        0x0206: {
            n: "Formula",
            f: parse_Formula
        },
        0x0207: {
            n: "String",
            f: parse_String
        },
        0x0208: {
            n: 'Row',
            f: parse_Row
        },
        0x020b: {
            n: "Index"
        },
        0x0221: {
            n: "Array",
            f: parse_Array
        },
        0x0225: {
            n: "DefaultRowHeight",
            f: parse_DefaultRowHeight
        },
        0x0236: {
            n: "Table"
        },
        0x023e: {
            n: "Window2",
            f: parse_Window2
        },
        0x027e: {
            n: "RK",
            f: parse_RK
        },
        0x0293: {
            n: "Style"
        },
        0x0406: {
            n: "Formula",
            f: parse_Formula
        },
        0x0418: {
            n: "BigName"
        },
        0x041e: {
            n: "Format",
            f: parse_Format
        },
        0x043c: {
            n: "ContinueBigName"
        },
        0x04bc: {
            n: "ShrFmla",
            f: parse_ShrFmla
        },
        0x0800: {
            n: "HLinkTooltip",
            f: parse_HLinkTooltip
        },
        0x0801: {
            n: "WebPub"
        },
        0x0802: {
            n: "QsiSXTag"
        },
        0x0803: {
            n: "DBQueryExt"
        },
        0x0804: {
            n: "ExtString"
        },
        0x0805: {
            n: "TxtQry"
        },
        0x0806: {
            n: "Qsir"
        },
        0x0807: {
            n: "Qsif"
        },
        0x0808: {
            n: "RRDTQSIF"
        },
        0x0809: {
            n: 'BOF',
            f: parse_BOF
        },
        0x080a: {
            n: "OleDbConn"
        },
        0x080b: {
            n: "WOpt"
        },
        0x080c: {
            n: "SXViewEx"
        },
        0x080d: {
            n: "SXTH"
        },
        0x080e: {
            n: "SXPIEx"
        },
        0x080f: {
            n: "SXVDTEx"
        },
        0x0810: {
            n: "SXViewEx9"
        },
        0x0812: {
            n: "ContinueFrt"
        },
        0x0813: {
            n: "RealTimeData"
        },
        0x0850: {
            n: "ChartFrtInfo"
        },
        0x0851: {
            n: "FrtWrapper"
        },
        0x0852: {
            n: "StartBlock"
        },
        0x0853: {
            n: "EndBlock"
        },
        0x0854: {
            n: "StartObject"
        },
        0x0855: {
            n: "EndObject"
        },
        0x0856: {
            n: "CatLab"
        },
        0x0857: {
            n: "YMult"
        },
        0x0858: {
            n: "SXViewLink"
        },
        0x0859: {
            n: "PivotChartBits"
        },
        0x085a: {
            n: "FrtFontList"
        },
        0x0862: {
            n: "SheetExt"
        },
        0x0863: {
            n: "BookExt",
            r: 12
        },
        0x0864: {
            n: "SXAddl"
        },
        0x0865: {
            n: "CrErr"
        },
        0x0866: {
            n: "HFPicture"
        },
        0x0867: {
            n: 'FeatHdr',
            f: parsenoop2
        },
        0x0868: {
            n: "Feat"
        },
        0x086a: {
            n: "DataLabExt"
        },
        0x086b: {
            n: "DataLabExtContents"
        },
        0x086c: {
            n: "CellWatch"
        },
        0x0871: {
            n: "FeatHdr11"
        },
        0x0872: {
            n: "Feature11"
        },
        0x0874: {
            n: "DropDownObjIds"
        },
        0x0875: {
            n: "ContinueFrt11"
        },
        0x0876: {
            n: "DConn"
        },
        0x0877: {
            n: "List12"
        },
        0x0878: {
            n: "Feature12"
        },
        0x0879: {
            n: "CondFmt12"
        },
        0x087a: {
            n: "CF12"
        },
        0x087b: {
            n: "CFEx"
        },
        0x087c: {
            n: "XFCRC",
            f: parse_XFCRC,
            r: 12
        },
        0x087d: {
            n: "XFExt",
            f: parse_XFExt,
            r: 12
        },
        0x087e: {
            n: "AutoFilter12"
        },
        0x087f: {
            n: "ContinueFrt12"
        },
        0x0884: {
            n: "MDTInfo"
        },
        0x0885: {
            n: "MDXStr"
        },
        0x0886: {
            n: "MDXTuple"
        },
        0x0887: {
            n: "MDXSet"
        },
        0x0888: {
            n: "MDXProp"
        },
        0x0889: {
            n: "MDXKPI"
        },
        0x088a: {
            n: "MDB"
        },
        0x088b: {
            n: "PLV"
        },
        0x088c: {
            n: "Compat12",
            f: parsebool,
            r: 12
        },
        0x088d: {
            n: "DXF"
        },
        0x088e: {
            n: "TableStyles",
            r: 12
        },
        0x088f: {
            n: "TableStyle"
        },
        0x0890: {
            n: "TableStyleElement"
        },
        0x0892: {
            n: "StyleExt"
        },
        0x0893: {
            n: "NamePublish"
        },
        0x0894: {
            n: "NameCmt",
            f: parse_NameCmt,
            r: 12
        },
        0x0895: {
            n: "SortData"
        },
        0x0896: {
            n: "Theme",
            f: parse_Theme,
            r: 12
        },
        0x0897: {
            n: "GUIDTypeLib"
        },
        0x0898: {
            n: "FnGrp12"
        },
        0x0899: {
            n: "NameFnGrp12"
        },
        0x089a: {
            n: "MTRSettings",
            f: parse_MTRSettings,
            r: 12
        },
        0x089b: {
            n: "CompressPictures",
            f: parsenoop2
        },
        0x089c: {
            n: "HeaderFooter"
        },
        0x089d: {
            n: "CrtLayout12"
        },
        0x089e: {
            n: "CrtMlFrt"
        },
        0x089f: {
            n: "CrtMlFrtContinue"
        },
        0x08a3: {
            n: "ForceFullCalculation",
            f: parse_ForceFullCalculation
        },
        0x08a4: {
            n: "ShapePropsStream"
        },
        0x08a5: {
            n: "TextPropsStream"
        },
        0x08a6: {
            n: "RichTextStream"
        },
        0x08a7: {
            n: "CrtLayout12A"
        },
        0x1001: {
            n: "Units"
        },
        0x1002: {
            n: "Chart"
        },
        0x1003: {
            n: "Series"
        },
        0x1006: {
            n: "DataFormat"
        },
        0x1007: {
            n: "LineFormat"
        },
        0x1009: {
            n: "MarkerFormat"
        },
        0x100a: {
            n: "AreaFormat"
        },
        0x100b: {
            n: "PieFormat"
        },
        0x100c: {
            n: "AttachedLabel"
        },
        0x100d: {
            n: "SeriesText"
        },
        0x1014: {
            n: "ChartFormat"
        },
        0x1015: {
            n: "Legend"
        },
        0x1016: {
            n: "SeriesList"
        },
        0x1017: {
            n: "Bar"
        },
        0x1018: {
            n: "Line"
        },
        0x1019: {
            n: "Pie"
        },
        0x101a: {
            n: "Area"
        },
        0x101b: {
            n: "Scatter"
        },
        0x101c: {
            n: "CrtLine"
        },
        0x101d: {
            n: "Axis"
        },
        0x101e: {
            n: "Tick"
        },
        0x101f: {
            n: "ValueRange"
        },
        0x1020: {
            n: "CatSerRange"
        },
        0x1021: {
            n: "AxisLine"
        },
        0x1022: {
            n: "CrtLink"
        },
        0x1024: {
            n: "DefaultText"
        },
        0x1025: {
            n: "Text"
        },
        0x1026: {
            n: "FontX",
            f: parseuint16
        },
        0x1027: {
            n: "ObjectLink"
        },
        0x1032: {
            n: "Frame"
        },
        0x1033: {
            n: "Begin"
        },
        0x1034: {
            n: "End"
        },
        0x1035: {
            n: "PlotArea"
        },
        0x103a: {
            n: "Chart3d"
        },
        0x103c: {
            n: "PicF"
        },
        0x103d: {
            n: "DropBar"
        },
        0x103e: {
            n: "Radar"
        },
        0x103f: {
            n: "Surf"
        },
        0x1040: {
            n: "RadarArea"
        },
        0x1041: {
            n: "AxisParent"
        },
        0x1043: {
            n: "LegendException"
        },
        0x1044: {
            n: "ShtProps",
            f: parse_ShtProps
        },
        0x1045: {
            n: "SerToCrt"
        },
        0x1046: {
            n: "AxesUsed"
        },
        0x1048: {
            n: "SBaseRef"
        },
        0x104a: {
            n: "SerParent"
        },
        0x104b: {
            n: "SerAuxTrend"
        },
        0x104e: {
            n: "IFmtRecord"
        },
        0x104f: {
            n: "Pos"
        },
        0x1050: {
            n: "AlRuns"
        },
        0x1051: {
            n: "BRAI"
        },
        0x105b: {
            n: "SerAuxErrBar"
        },
        0x105c: {
            n: "ClrtClient",
            f: parse_ClrtClient
        },
        0x105d: {
            n: "SerFmt"
        },
        0x105f: {
            n: "Chart3DBarShape"
        },
        0x1060: {
            n: "Fbi"
        },
        0x1061: {
            n: "BopPop"
        },
        0x1062: {
            n: "AxcExt"
        },
        0x1063: {
            n: "Dat"
        },
        0x1064: {
            n: "PlotGrowth"
        },
        0x1065: {
            n: "SIIndex"
        },
        0x1066: {
            n: "GelFrame"
        },
        0x1067: {
            n: "BopPopCustom"
        },
        0x1068: {
            n: "Fbi2"
        },
        0x0000: {
            n: "Dimensions",
            f: parse_Dimensions
        },
        0x0002: {
            n: "BIFF2INT",
            f: parse_BIFF2INT
        },
        0x0005: {
            n: "BoolErr",
            f: parse_BoolErr
        },
        0x0007: {
            n: "String",
            f: parse_BIFF2STRING
        },
        0x0008: {
            n: "BIFF2ROW"
        },
        0x000b: {
            n: "Index"
        },
        0x0016: {
            n: "ExternCount",
            f: parseuint16
        },
        0x001e: {
            n: "BIFF2FORMAT",
            f: parse_BIFF2Format
        },
        0x001f: {
            n: "BIFF2FMTCNT"
        },
        /* 16-bit cnt of BIFF2FORMAT records */
        0x0020: {
            n: "BIFF2COLINFO"
        },
        0x0021: {
            n: "Array",
            f: parse_Array
        },
        0x0025: {
            n: "DefaultRowHeight",
            f: parse_DefaultRowHeight
        },
        0x0032: {
            n: "BIFF2FONTXTRA",
            f: parse_BIFF2FONTXTRA
        },
        0x0034: {
            n: "DDEObjName"
        },
        0x003e: {
            n: "BIFF2WINDOW2"
        },
        0x0043: {
            n: "BIFF2XF"
        },
        0x0045: {
            n: "BIFF2FONTCLR"
        },
        0x0056: {
            n: "BIFF4FMTCNT"
        },
        /* 16-bit cnt, similar to BIFF2 */
        0x007e: {
            n: "RK"
        },
        /* Not necessarily same as 0x027e */
        0x007f: {
            n: "ImData",
            f: parse_ImData
        },
        0x0087: {
            n: "Addin"
        },
        0x0088: {
            n: "Edg"
        },
        0x0089: {
            n: "Pub"
        },
        0x0091: {
            n: "Sub"
        },
        0x0094: {
            n: "LHRecord"
        },
        0x0095: {
            n: "LHNGraph"
        },
        0x0096: {
            n: "Sound"
        },
        0x00a9: {
            n: "CoordList"
        },
        0x00ab: {
            n: "GCW"
        },
        0x00bc: {
            n: "ShrFmla"
        },
        /* Not necessarily same as 0x04bc */
        0x00bf: {
            n: "ToolbarHdr"
        },
        0x00c0: {
            n: "ToolbarEnd"
        },
        0x00c2: {
            n: "AddMenu"
        },
        0x00c3: {
            n: "DelMenu"
        },
        0x00d6: {
            n: "RString",
            f: parse_RString
        },
        0x00df: {
            n: "UDDesc"
        },
        0x00ea: {
            n: "TabIdConf"
        },
        0x0162: {
            n: "XL5Modify"
        },
        0x01a5: {
            n: "FileSharing2"
        },
        0x0209: {
            n: 'BOF',
            f: parse_BOF
        },
        0x0218: {
            n: "Lbl",
            f: parse_Lbl
        },
        0x0223: {
            n: "ExternName",
            f: parse_ExternName
        },
        0x0231: {
            n: "Font"
        },
        0x0243: {
            n: "BIFF3XF"
        },
        0x0409: {
            n: 'BOF',
            f: parse_BOF
        },
        0x0443: {
            n: "BIFF4XF"
        },
        0x086d: {
            n: "FeatInfo"
        },
        0x0873: {
            n: "FeatInfo11"
        },
        0x0881: {
            n: "SXAddl12"
        },
        0x08c0: {
            n: "AutoWebPub"
        },
        0x08c1: {
            n: "ListObj"
        },
        0x08c2: {
            n: "ListField"
        },
        0x08c3: {
            n: "ListDV"
        },
        0x08c4: {
            n: "ListCondFmt"
        },
        0x08c5: {
            n: "ListCF"
        },
        0x08c6: {
            n: "FMQry"
        },
        0x08c7: {
            n: "FMSQry"
        },
        0x08c8: {
            n: "PLV"
        },
        0x08c9: {
            n: "LnExt"
        },
        0x08ca: {
            n: "MkrExt"
        },
        0x08cb: {
            n: "CrtCoopt"
        },
        0x08d6: {
            n: "FRTArchId$",
            r: 12
        },
        0x7262: {}
    };
    var XLSRE = evert_key(XLSRecordEnum, 'n');
    function write_biff_rec(ba, type, payload, length) {
        var t = +type || +XLSRE[type];
        if (isNaN(t)) return;
        var len = length || (payload || []).length || 0;
        var o = ba.next(4);
        o.write_shift(2, t);
        o.write_shift(2, len);
        if (len > 0 && is_buf(payload)) ba.push(payload);
    }
    function write_BIFF2Cell(out, r, c) {
        if (!out) out = new_buf(7);
        out.write_shift(2, r);
        out.write_shift(2, c);
        out.write_shift(2, 0);
        out.write_shift(1, 0);
        return out;
    }
    function write_BIFF2BERR(r, c, val, t) {
        var out = new_buf(9);
        write_BIFF2Cell(out, r, c);
        if (t == 'e') {
            out.write_shift(1, val);
            out.write_shift(1, 1);
        } else {
            out.write_shift(1, val ? 1 : 0);
            out.write_shift(1, 0);
        }
        return out;
    }
    /* TODO: codepage, large strings */
    function write_BIFF2LABEL(r, c, val) {
        var out = new_buf(8 + 2 * val.length);
        write_BIFF2Cell(out, r, c);
        out.write_shift(1, val.length);
        out.write_shift(val.length, val, 'sbcs');
        return out.l < out.length ? out.slice(0, out.l) : out;
    }
    function write_ws_biff2_cell(ba, cell, R, C) {
        if (cell.v != null) switch (cell.t) {
            case 'd':
            case 'n':
                var v = cell.t == 'd' ? datenum(parseDate(cell.v)) : cell.v;
                if ((v == (v | 0)) && (v >= 0) && (v < 65536))
                    write_biff_rec(ba, 0x0002, write_BIFF2INT(R, C, v));
                else
                    write_biff_rec(ba, 0x0003, write_BIFF2NUM(R, C, v));
                return;
            case 'b':
            case 'e':
                write_biff_rec(ba, 0x0005, write_BIFF2BERR(R, C, cell.v, cell.t));
                return;
                /* TODO: codepage, sst */
            case 's':
            case 'str':
                write_biff_rec(ba, 0x0004, write_BIFF2LABEL(R, C, cell.v));
                return;
        }
        write_biff_rec(ba, 0x0001, write_BIFF2Cell(null, R, C));
    }
    function write_ws_biff2(ba, ws, idx, opts) {
        var dense = Array.isArray(ws);
        var range = safe_decode_range(ws['!ref'] || "A1"),
            ref, rr = "",
            cols = [];
        if (range.e.c > 0xFF || range.e.r > 0x3FFF) {
            if (opts.WTF) throw new Error("Range " + (ws['!ref'] || "A1") + " exceeds format limit A1:IV16384");
            range.e.c = Math.min(range.e.c, 0xFF);
            range.e.r = Math.min(range.e.c, 0x3FFF);
            ref = encode_range(range);
        }
        for (var R = range.s.r; R <= range.e.r; ++R) {
            rr = encode_row(R);
            for (var C = range.s.c; C <= range.e.c; ++C) {
                if (R === range.s.r) cols[C] = encode_col(C);
                ref = cols[C] + rr;
                var cell = dense ? (ws[R] || [])[C] : ws[ref];
                if (!cell) continue;
                /* write cell */
                write_ws_biff2_cell(ba, cell, R, C, opts);
            }
        }
    }
    /* Based on test files */
    function write_biff2_buf(wb, opts) {
        var o = opts || {};
        if (DENSE != null && o.dense == null) o.dense = DENSE;
        var ba = buf_array();
        var idx = 0;
        for (var i = 0; i < wb.SheetNames.length; ++i)
            if (wb.SheetNames[i] == o.sheet) idx = i;
        if (idx == 0 && !!o.sheet && wb.SheetNames[0] != o.sheet) throw new Error("Sheet not found: " + o.sheet);
        write_biff_rec(ba, 0x0009, write_BOF(wb, 0x10, o));
        /* ... */
        write_ws_biff2(ba, wb.Sheets[wb.SheetNames[idx]], idx, o, wb);
        /* ... */
        write_biff_rec(ba, 0x000A);
        return ba.end();
    }
    function write_FONTS_biff8(ba, data, opts) {
        write_biff_rec(ba, "Font", write_Font({
            sz: 12,
            color: {
                theme: 1
            },
            name: "Arial",
            family: 2,
            scheme: "minor"
        }, opts));
    }
    function write_FMTS_biff8(ba, NF, opts) {
        if (!NF) return;
        [
            [5, 8],
            [23, 26],
            [41, 44],
            [ /*63*/ 50, /*66],[164,*/ 392]
        ].forEach(function (r) {
            for (var i = r[0]; i <= r[1]; ++i)
                if (NF[i] != null) write_biff_rec(ba, "Format", write_Format(i, NF[i], opts));
        });
    }
    function write_FEAT(ba, ws) {
        /* [MS-XLS] 2.4.112 */
        var o = new_buf(19);
        o.write_shift(4, 0x867);
        o.write_shift(4, 0);
        o.write_shift(4, 0);
        o.write_shift(2, 3);
        o.write_shift(1, 1);
        o.write_shift(4, 0);
        write_biff_rec(ba, "FeatHdr", o);
        /* [MS-XLS] 2.4.111 */
        o = new_buf(39);
        o.write_shift(4, 0x868);
        o.write_shift(4, 0);
        o.write_shift(4, 0);
        o.write_shift(2, 3);
        o.write_shift(1, 0);
        o.write_shift(4, 0);
        o.write_shift(2, 1);
        o.write_shift(4, 4);
        o.write_shift(2, 0);
        write_Ref8U(safe_decode_range(ws['!ref'] || "A1"), o);
        o.write_shift(4, 4);
        write_biff_rec(ba, "Feat", o);
    }
    function write_CELLXFS_biff8(ba, opts) {
        for (var i = 0; i < 16; ++i) write_biff_rec(ba, "XF", write_XF({
            numFmtId: 0,
            style: true
        }, 0, opts));
        opts.cellXfs.forEach(function (c) {
            write_biff_rec(ba, "XF", write_XF(c, 0, opts));
        });
    }
    function write_ws_biff8_hlinks(ba, ws) {
        for (var R = 0; R < ws['!links'].length; ++R) {
            var HL = ws['!links'][R];
            write_biff_rec(ba, "HLink", write_HLink(HL));
            if (HL[1].Tooltip) write_biff_rec(ba, "HLinkTooltip", write_HLinkTooltip(HL));
        }
        delete ws['!links'];
    }
    function write_ws_biff8_cell(ba, cell, R, C, opts) {
        var os = 16 + get_cell_style(opts.cellXfs, cell, opts);
        if (cell.v != null) switch (cell.t) {
            case 'd':
            case 'n':
                var v = cell.t == 'd' ? datenum(parseDate(cell.v)) : cell.v;
                /* TODO: emit RK as appropriate */
                write_biff_rec(ba, "Number", write_Number(R, C, v, os, opts));
                return;
            case 'b':
            case 'e':
                write_biff_rec(ba, 0x0205, write_BoolErr(R, C, cell.v, os, opts, cell.t));
                return;
                /* TODO: codepage, sst */
            case 's':
            case 'str':
                write_biff_rec(ba, "Label", write_Label(R, C, cell.v, os, opts));
                return;
        }
        write_biff_rec(ba, "Blank", write_XLSCell(R, C, os));
    }
    /* [MS-XLS] 2.1.7.20.5 */
    function write_ws_biff8(idx, opts, wb) {
        var ba = buf_array();
        var s = wb.SheetNames[idx],
            ws = wb.Sheets[s] || {};
        var _WB = ((wb || {}).Workbook || {});
        var _sheet = ((_WB.Sheets || [])[idx] || {});
        var dense = Array.isArray(ws);
        var b8 = opts.biff == 8;
        var ref, rr = "",
            cols = [];
        var range = safe_decode_range(ws['!ref'] || "A1");
        var MAX_ROWS = b8 ? 65536 : 16384;
        if (range.e.c > 0xFF || range.e.r >= MAX_ROWS) {
            if (opts.WTF) throw new Error("Range " + (ws['!ref'] || "A1") + " exceeds format limit A1:IV16384");
            range.e.c = Math.min(range.e.c, 0xFF);
            range.e.r = Math.min(range.e.c, MAX_ROWS - 1);
        }
        write_biff_rec(ba, 0x0809, write_BOF(wb, 0x10, opts));
        /* ... */
        write_biff_rec(ba, "CalcMode", writeuint16(1));
        write_biff_rec(ba, "CalcCount", writeuint16(100));
        write_biff_rec(ba, "CalcRefMode", writebool(true));
        write_biff_rec(ba, "CalcIter", writebool(false));
        write_biff_rec(ba, "CalcDelta", write_Xnum(0.001));
        write_biff_rec(ba, "CalcSaveRecalc", writebool(true));
        write_biff_rec(ba, "PrintRowCol", writebool(false));
        write_biff_rec(ba, "PrintGrid", writebool(false));
        write_biff_rec(ba, "GridSet", writeuint16(1));
        write_biff_rec(ba, "Guts", write_Guts([0, 0]));
        /* ... */
        write_biff_rec(ba, "HCenter", writebool(false));
        write_biff_rec(ba, "VCenter", writebool(false));
        /* ... */
        write_biff_rec(ba, 0x200, write_Dimensions(range, opts));
        /* ... */
        if (b8) ws['!links'] = [];
        for (var R = range.s.r; R <= range.e.r; ++R) {
            rr = encode_row(R);
            for (var C = range.s.c; C <= range.e.c; ++C) {
                if (R === range.s.r) cols[C] = encode_col(C);
                ref = cols[C] + rr;
                var cell = dense ? (ws[R] || [])[C] : ws[ref];
                if (!cell) continue;
                /* write cell */
                write_ws_biff8_cell(ba, cell, R, C, opts);
                if (b8 && cell.l) ws['!links'].push([ref, cell.l]);
            }
        }
        var cname = _sheet.CodeName || _sheet.name || s;
        /* ... */
        if (b8 && _WB.Views) write_biff_rec(ba, "Window2", write_Window2(_WB.Views[0]));
        /* ... */
        if (b8 && (ws['!merges'] || []).length) write_biff_rec(ba, "MergeCells", write_MergeCells(ws['!merges']));
        /* ... */
        if (b8) write_ws_biff8_hlinks(ba, ws);
        /* ... */
        write_biff_rec(ba, "CodeName", write_XLUnicodeString(cname, opts));
        /* ... */
        if (b8) write_FEAT(ba, ws);
        /* ... */
        write_biff_rec(ba, "EOF");
        return ba.end();
    }
    /* [MS-XLS] 2.1.7.20.3 */
    function write_biff8_global(wb, bufs, opts) {
        var A = buf_array();
        var _WB = ((wb || {}).Workbook || {});
        var _sheets = (_WB.Sheets || []);
        var _wb = _WB.WBProps || {};
        var b8 = opts.biff == 8,
            b5 = opts.biff == 5;
        write_biff_rec(A, 0x0809, write_BOF(wb, 0x05, opts));
        if (opts.bookType == "xla") write_biff_rec(A, "Addin");
        write_biff_rec(A, "InterfaceHdr", b8 ? writeuint16(0x04b0) : null);
        write_biff_rec(A, "Mms", writezeroes(2));
        if (b5) write_biff_rec(A, "ToolbarHdr");
        if (b5) write_biff_rec(A, "ToolbarEnd");
        write_biff_rec(A, "InterfaceEnd");
        write_biff_rec(A, "WriteAccess", write_WriteAccess("SheetJS", opts));
        write_biff_rec(A, "CodePage", writeuint16(b8 ? 0x04b0 : 0x04E4));
        if (b8) write_biff_rec(A, "DSF", writeuint16(0));
        if (b8) write_biff_rec(A, "Excel9File");
        write_biff_rec(A, "RRTabId", write_RRTabId(wb.SheetNames.length));
        if (b8 && wb.vbaraw) {
            write_biff_rec(A, "ObProj");
            var cname = _wb.CodeName || "ThisWorkbook";
            write_biff_rec(A, "CodeName", write_XLUnicodeString(cname, opts));
        }
        write_biff_rec(A, "BuiltInFnGroupCount", writeuint16(0x11));
        write_biff_rec(A, "WinProtect", writebool(false));
        write_biff_rec(A, "Protect", writebool(false));
        write_biff_rec(A, "Password", writeuint16(0));
        if (b8) write_biff_rec(A, "Prot4Rev", writebool(false));
        if (b8) write_biff_rec(A, "Prot4RevPass", writeuint16(0));
        write_biff_rec(A, "Window1", write_Window1(opts));
        write_biff_rec(A, "Backup", writebool(false));
        write_biff_rec(A, "HideObj", writeuint16(0));
        write_biff_rec(A, "Date1904", writebool(safe1904(wb) == "true"));
        write_biff_rec(A, "CalcPrecision", writebool(true));
        if (b8) write_biff_rec(A, "RefreshAll", writebool(false));
        write_biff_rec(A, "BookBool", writeuint16(0));
        /* ... */
        write_FONTS_biff8(A, wb, opts);
        write_FMTS_biff8(A, wb.SSF, opts);
        write_CELLXFS_biff8(A, opts);
        /* ... */
        if (b8) write_biff_rec(A, "UsesELFs", writebool(false));
        var a = A.end();
        var C = buf_array();
        if (b8) write_biff_rec(C, "Country", write_Country());
        /* BIFF8: [SST *Continue] ExtSST */
        write_biff_rec(C, "EOF");
        var c = C.end();
        var B = buf_array();
        var blen = 0,
            j = 0;
        for (j = 0; j < wb.SheetNames.length; ++j) blen += (b8 ? 12 : 11) + (b8 ? 2 : 1) * wb.SheetNames[j].length;
        var start = a.length + blen + c.length;
        for (j = 0; j < wb.SheetNames.length; ++j) {
            var _sheet = _sheets[j] || ({});
            write_biff_rec(B, "BoundSheet8", write_BoundSheet8({
                pos: start,
                hs: _sheet.Hidden || 0,
                dt: 0,
                name: wb.SheetNames[j]
            }, opts));
            start += bufs[j].length;
        }
        /* 1*BoundSheet8 */
        var b = B.end();
        if (blen != b.length) throw new Error("BS8 " + blen + " != " + b.length);
        var out = [];
        if (a.length) out.push(a);
        if (b.length) out.push(b);
        if (c.length) out.push(c);
        return __toBuffer([out]);
    }
    /* [MS-XLS] 2.1.7.20 Workbook Stream */
    function write_biff8_buf(wb, opts) {
        var o = opts || {};
        var bufs = [];
        if (wb && !wb.SSF) {
            wb.SSF = SSF.get_table();
        }
        if (wb && wb.SSF) {
            make_ssf(SSF);
            SSF.load_table(wb.SSF);
            // $FlowIgnore
            o.revssf = evert_num(wb.SSF);
            o.revssf[wb.SSF[65535]] = 0;
            o.ssf = wb.SSF;
        }
        o.cellXfs = [];
        o.Strings = [];
        o.Strings.Count = 0;
        o.Strings.Unique = 0;
        get_cell_style(o.cellXfs, {}, {
            revssf: {
                "General": 0
            }
        });
        for (var i = 0; i < wb.SheetNames.length; ++i) bufs[bufs.length] = write_ws_biff8(i, o, wb);
        bufs.unshift(write_biff8_global(wb, bufs, o));
        return __toBuffer([bufs]);
    }
    function write_biff_buf(wb, opts) {
        var o = opts || {};
        switch (o.biff || 2) {
            case 8:
            case 5:
                return write_biff8_buf(wb, opts);
            case 4:
            case 3:
            case 2:
                return write_biff2_buf(wb, opts);
        }
        throw new Error("invalid type " + o.bookType + " for BIFF");
    }
    /* note: browser DOM element cannot see mso- style attrs, must parse */
    var HTML_ = (function () {
        function html_to_sheet(str, _opts) {
            var opts = _opts || {};
            if (DENSE != null && opts.dense == null) opts.dense = DENSE;
            var ws = opts.dense ? ([]) : ({});
            var mtch = str.match(/");
            var mtch2 = str.match(/<\/table/i);
            var i = mtch.index,
                j = mtch2 && mtch2.index || str.length;
            var rows = split_regex(str.slice(i, j), /(:?]*>)/i, "
");
            var R = -1,
                C = 0,
                RS = 0,
                CS = 0;
            var range = {
                s: {
                    r: 10000000,
                    c: 10000000
                },
                e: {
                    r: 0,
                    c: 0
                }
            };
            var merges = [];
            for (i = 0; i < rows.length; ++i) {
                var row = rows[i].trim();
                var hd = row.slice(0, 3).toLowerCase();
                if (hd == "
/i);
                for (j = 0; j < cells.length; ++j) {
                    var cell = cells[j].trim();
                    if (!cell.match(/")) > -1) m = m.slice(cc + 1);
                    var tag = parsexmltag(cell.slice(0, cell.indexOf(">")));
                    CS = tag.colspan ? +tag.colspan : 1;
                    if ((RS = +tag.rowspan) > 1 || CS > 1) merges.push({
                        s: {
                            r: R,
                            c: C
                        },
                        e: {
                            r: R + (RS || 1) - 1,
                            c: C + CS - 1
                        }
                    });
                    var _t = tag.t || "";
                    /* TODO: generate stub cells */
                    if (!m.length) {
                        C += CS;
                        continue;
                    }
                    m = htmldecode(m);
                    if (range.s.r > R) range.s.r = R;
                    if (range.e.r < R) range.e.r = R;
                    if (range.s.c > C) range.s.c = C;
                    if (range.e.c < C) range.e.c = C;
                    if (!m.length) continue;
                    var o = {
                        t: 's',
                        v: m
                    };
                    if (opts.raw || !m.trim().length || _t == 's') {} else if (m === 'TRUE') o = {
                        t: 'b',
                        v: true
                    };
                    else if (m === 'FALSE') o = {
                        t: 'b',
                        v: false
                    };
                    else if (!isNaN(fuzzynum(m))) o = {
                        t: 'n',
                        v: fuzzynum(m)
                    };
                    else if (!isNaN(fuzzydate(m).getDate())) {
                        o = ({
                            t: 'd',
                            v: parseDate(m)
                        });
                        if (!opts.cellDates) o = ({
                            t: 'n',
                            v: datenum(o.v)
                        });
                        o.z = opts.dateNF || SSF._table[14];
                    }
                    if (opts.dense) {
                        if (!ws[R]) ws[R] = [];
                        ws[R][C] = o;
                    } else ws[encode_cell({
                        r: R,
                        c: C
                    })] = o;
                    C += CS;
                }
            }
            ws['!ref'] = encode_range(range);
            return ws;
        }
        function html_to_book(str, opts) {
            return sheet_to_workbook(html_to_sheet(str, opts), opts);
        }
        function make_html_row(ws, r, R, o) {
            var M = (ws['!merges'] || []);
            var oo = [];
            for (var C = r.s.c; C <= r.e.c; ++C) {
                var RS = 0,
                    CS = 0;
                for (var j = 0; j < M.length; ++j) {
                    if (M[j].s.r > R || M[j].s.c > C) continue;
                    if (M[j].e.r < R || M[j].e.c < C) continue;
                    if (M[j].s.r < R || M[j].s.c < C) {
                        RS = -1;
                        break;
                    }
                    RS = M[j].e.r - M[j].s.r + 1;
                    CS = M[j].e.c - M[j].s.c + 1;
                    break;
                }
                if (RS < 0) continue;
                var coord = encode_cell({
                    r: R,
                    c: C
                });
                var cell = o.dense ? (ws[R] || [])[C] : ws[coord];
                var sp = {};
                if (RS > 1) sp.rowspan = RS;
                if (CS > 1) sp.colspan = CS;
                /* TODO: html entities */
                var w = (cell && cell.v != null) && (cell.h || escapehtml(cell.w || (format_cell(cell), cell.w) || "")) || "";
                sp.t = cell && cell.t || 'z';
                if (o.editable) w = '' + w + '';
                sp.id = "sjs-" + coord;
                oo.push(writextag('td', w, sp));
            }
            var preamble = "";
            return preamble + oo.join("") + "
";
        }
        function make_html_preamble(ws, R, o) {
            var out = [];
            return out.join("") + '';
        }
        var _BEGIN = 'SheetJS Table Export';
        var _END = '';
        function sheet_to_html(ws, opts /*, wb:?Workbook*/ ) {
            var o = opts || {};
            var header = o.header != null ? o.header : _BEGIN;
            var footer = o.footer != null ? o.footer : _END;
            var out = [header];
            var r = decode_range(ws['!ref']);
            o.dense = Array.isArray(ws);
            out.push(make_html_preamble(ws, r, o));
            for (var R = r.s.r; R <= r.e.r; ++R) out.push(make_html_row(ws, r, R, o));
            out.push("
" + footer);
            return out.join("");
        }
        return {
            to_workbook: html_to_book,
            to_sheet: html_to_sheet,
            _row: make_html_row,
            BEGIN: _BEGIN,
            END: _END,
            _preamble: make_html_preamble,
            from_sheet: sheet_to_html
        };
    })();
    function parse_dom_table(table, _opts) {
        var opts = _opts || {};
        if (DENSE != null) opts.dense = DENSE;
        var ws = opts.dense ? ([]) : ({});
        var rows = table.getElementsByTagName('tr');
        var sheetRows = opts.sheetRows || 10000000;
        var range = {
            s: {
                r: 0,
                c: 0
            },
            e: {
                r: 0,
                c: 0
            }
        };
        var merges = [],
            midx = 0;
        var rowinfo = [];
        var _R = 0,
            R = 0,
            _C, C, RS, CS;
        for (; _R < rows.length && R < sheetRows; ++_R) {
            var row = rows[_R];
            if (is_dom_element_hidden(row)) {
                if (opts.display) continue;
                rowinfo[R] = {
                    hidden: true
                };
            }
            var elts = (row.children);
            for (_C = C = 0; _C < elts.length; ++_C) {
                var elt = elts[_C];
                if (opts.display && is_dom_element_hidden(elt)) continue;
                var v = htmldecode(elt.innerHTML);
                for (midx = 0; midx < merges.length; ++midx) {
                    var m = merges[midx];
                    if (m.s.c == C && m.s.r <= R && R <= m.e.r) {
                        C = m.e.c + 1;
                        midx = -1;
                    }
                }
                /* TODO: figure out how to extract nonstandard mso- style */
                CS = +elt.getAttribute("colspan") || 1;
                if ((RS = +elt.getAttribute("rowspan")) > 0 || CS > 1) merges.push({
                    s: {
                        r: R,
                        c: C
                    },
                    e: {
                        r: R + (RS || 1) - 1,
                        c: C + CS - 1
                    }
                });
                var o = {
                    t: 's',
                    v: v
                };
                var _t = elt.getAttribute("t") || "";
                if (v != null) {
                    if (v.length == 0) o.t = _t || 'z';
                    else if (opts.raw || v.trim().length == 0 || _t == "s") {} else if (v === 'TRUE') o = {
                        t: 'b',
                        v: true
                    };
                    else if (v === 'FALSE') o = {
                        t: 'b',
                        v: false
                    };
                    else if (!isNaN(fuzzynum(v))) o = {
                        t: 'n',
                        v: fuzzynum(v)
                    };
                    else if (!isNaN(fuzzydate(v).getDate())) {
                        o = ({
                            t: 'd',
                            v: parseDate(v)
                        });
                        if (!opts.cellDates) o = ({
                            t: 'n',
                            v: datenum(o.v)
                        });
                        o.z = opts.dateNF || SSF._table[14];
                    }
                }
                if (opts.dense) {
                    if (!ws[R]) ws[R] = [];
                    ws[R][C] = o;
                } else ws[encode_cell({
                    c: C,
                    r: R
                })] = o;
                if (range.e.c < C) range.e.c = C;
                C += CS;
            }
            ++R;
        }
        if (merges.length) ws['!merges'] = merges;
        if (rowinfo.length) ws['!rows'] = rowinfo;
        range.e.r = R - 1;
        ws['!ref'] = encode_range(range);
        if (R >= sheetRows) ws['!fullref'] = encode_range((range.e.r = rows.length - _R + R - 1, range)); // We can count the real number of rows to parse but we don't to improve the performance
        return ws;
    }
    function table_to_book(table, opts) {
        return sheet_to_workbook(parse_dom_table(table, opts), opts);
    }
    function is_dom_element_hidden(element) {
        var display = '';
        var get_computed_style = get_get_computed_style_function(element);
        if (get_computed_style) display = get_computed_style(element).getPropertyValue('display');
        if (!display) display = element.style.display; // Fallback for cases when getComputedStyle is not available (e.g. an old browser or some Node.js environments) or doesn't work (e.g. if the element is not inserted to a document)
        return display === 'none';
    }
    /* global getComputedStyle */
    function get_get_computed_style_function(element) {
        // The proper getComputedStyle implementation is the one defined in the element window
        if (element.ownerDocument.defaultView && typeof element.ownerDocument.defaultView.getComputedStyle === 'function') return element.ownerDocument.defaultView.getComputedStyle;
        // If it is not available, try to get one from the global namespace
        if (typeof getComputedStyle === 'function') return getComputedStyle;
        return null;
    }
    /* OpenDocument */
    var parse_content_xml = (function () {
        /* 6.1.2 White Space Characters */
        var parse_text_p = function (text) {
            return unescapexml(text
                .replace(/[\t\r\n]/g, " ").trim().replace(/ +/g, " ")
                .replace(//g, " ")
                .replace(//g, function ($$, $1) {
                    return Array(parseInt($1, 10) + 1).join(" ");
                })
                .replace(/]*\/>/g, "\t")
                .replace(//g, "\n")
                .replace(/<[^>]*>/g, "")
            );
        };
        var number_formats = {
            /* ods name: [short ssf fmt, long ssf fmt] */
            day: ["d", "dd"],
            month: ["m", "mm"],
            year: ["y", "yy"],
            hours: ["h", "hh"],
            minutes: ["m", "mm"],
            seconds: ["s", "ss"],
            "am-pm": ["A/P", "AM/PM"],
            "day-of-week": ["ddd", "dddd"],
            era: ["e", "ee"],
            /* there is no native representation of LO "Q" format */
            quarter: ["\\Qm", "m\\\"th quarter\""]
        };
        return function pcx(d, _opts) {
            var opts = _opts || {};
            if (DENSE != null && opts.dense == null) opts.dense = DENSE;
            var str = xlml_normalize(d);
            var state = [],
                tmp;
            var tag;
            var NFtag = {
                    name: ""
                },
                NF = "",
                pidx = 0;
            var sheetag;
            var rowtag;
            var Sheets = {},
                SheetNames = [];
            var ws = opts.dense ? ([]) : ({});
            var Rn, q;
            var ctag = ({
                value: ""
            });
            var textp = "",
                textpidx = 0,
                textptag;
            var R = -1,
                C = -1,
                range = {
                    s: {
                        r: 1000000,
                        c: 10000000
                    },
                    e: {
                        r: 0,
                        c: 0
                    }
                };
            var row_ol = 0;
            var number_format_map = {};
            var merges = [],
                mrange = {},
                mR = 0,
                mC = 0;
            var rowinfo = [],
                rowpeat = 1,
                colpeat = 1;
            var arrayf = [];
            var WB = {
                Names: []
            };
            var atag = ({});
            var _Ref = ["", ""];
            var comments = [],
                comment = ({});
            var creator = "",
                creatoridx = 0;
            var isstub = false,
                intable = false;
            var i = 0;
            xlmlregex.lastIndex = 0;
            str = str.replace(//mg, "").replace(//gm, "");
            while ((Rn = xlmlregex.exec(str))) switch ((Rn[3] = Rn[3].replace(/_.*$/, ""))) {
                case 'table':
                case '工作表': // 9.1.2 
                    if (Rn[1] === '/') {
                        if (range.e.c >= range.s.c && range.e.r >= range.s.r) ws['!ref'] = encode_range(range);
                        if (opts.sheetRows > 0 && opts.sheetRows <= range.e.r) {
                            ws['!fullref'] = ws['!ref'];
                            range.e.r = opts.sheetRows - 1;
                            ws['!ref'] = encode_range(range);
                        }
                        if (merges.length) ws['!merges'] = merges;
                        if (rowinfo.length) ws["!rows"] = rowinfo;
                        sheetag.name = sheetag['名称'] || sheetag.name;
                        if (typeof JSON !== 'undefined') JSON.stringify(sheetag);
                        SheetNames.push(sheetag.name);
                        Sheets[sheetag.name] = ws;
                        intable = false;
                    } else if (Rn[0].charAt(Rn[0].length - 2) !== '/') {
                        sheetag = parsexmltag(Rn[0], false);
                        R = C = -1;
                        range.s.r = range.s.c = 10000000;
                        range.e.r = range.e.c = 0;
                        ws = opts.dense ? ([]) : ({});
                        merges = [];
                        rowinfo = [];
                        intable = true;
                    }
                    break;
                case 'table-row-group': // 9.1.9 
                    if (Rn[1] === "/") --row_ol;
                    else ++row_ol;
                    break;
                case 'table-row':
                case '行': // 9.1.3 
                    if (Rn[1] === '/') {
                        R += rowpeat;
                        rowpeat = 1;
                        break;
                    }
                    rowtag = parsexmltag(Rn[0], false);
                    if (rowtag['行号']) R = rowtag['行号'] - 1;
                    else if (R == -1) R = 0;
                    rowpeat = +rowtag['number-rows-repeated'] || 1;
                    /* TODO: remove magic */
                    if (rowpeat < 10)
                        for (i = 0; i < rowpeat; ++i)
                            if (row_ol > 0) rowinfo[R + i] = {
                                level: row_ol
                            };
                    C = -1;
                    break;
                case 'covered-table-cell': // 9.1.5 
                    ++C;
                    if (opts.sheetStubs) {
                        if (opts.dense) {
                            if (!ws[R]) ws[R] = [];
                            ws[R][C] = {
                                t: 'z'
                            };
                        } else ws[encode_cell({
                            r: R,
                            c: C
                        })] = {
                            t: 'z'
                        };
                    }
                    break; /* stub */
                case 'table-cell':
                case '数据':
                    if (Rn[0].charAt(Rn[0].length - 2) === '/') {
                        ++C;
                        ctag = parsexmltag(Rn[0], false);
                        colpeat = parseInt(ctag['number-columns-repeated'] || "1", 10);
                        q = ({
                            t: 'z',
                            v: null
                        });
                        if (ctag.formula && opts.cellFormula != false) q.f = ods_to_csf_formula(unescapexml(ctag.formula));
                        if ((ctag['数据类型'] || ctag['value-type']) == "string") {
                            q.t = "s";
                            q.v = unescapexml(ctag['string-value'] || "");
                            if (opts.dense) {
                                if (!ws[R]) ws[R] = [];
                                ws[R][C] = q;
                            } else {
                                ws[encode_cell({
                                    r: R,
                                    c: C
                                })] = q;
                            }
                        }
                        C += colpeat - 1;
                    } else if (Rn[1] !== '/') {
                        ++C;
                        colpeat = 1;
                        var rptR = rowpeat ? R + rowpeat - 1 : R;
                        if (C > range.e.c) range.e.c = C;
                        if (C < range.s.c) range.s.c = C;
                        if (R < range.s.r) range.s.r = R;
                        if (rptR > range.e.r) range.e.r = rptR;
                        ctag = parsexmltag(Rn[0], false);
                        comments = [];
                        comment = ({});
                        q = ({
                            t: ctag['数据类型'] || ctag['value-type'],
                            v: null
                        });
                        if (opts.cellFormula) {
                            if (ctag.formula) ctag.formula = unescapexml(ctag.formula);
                            if (ctag['number-matrix-columns-spanned'] && ctag['number-matrix-rows-spanned']) {
                                mR = parseInt(ctag['number-matrix-rows-spanned'], 10) || 0;
                                mC = parseInt(ctag['number-matrix-columns-spanned'], 10) || 0;
                                mrange = {
                                    s: {
                                        r: R,
                                        c: C
                                    },
                                    e: {
                                        r: R + mR - 1,
                                        c: C + mC - 1
                                    }
                                };
                                q.F = encode_range(mrange);
                                arrayf.push([mrange, q.F]);
                            }
                            if (ctag.formula) q.f = ods_to_csf_formula(ctag.formula);
                            else
                                for (i = 0; i < arrayf.length; ++i)
                                    if (R >= arrayf[i][0].s.r && R <= arrayf[i][0].e.r)
                                        if (C >= arrayf[i][0].s.c && C <= arrayf[i][0].e.c)
                                            q.F = arrayf[i][1];
                        }
                        if (ctag['number-columns-spanned'] || ctag['number-rows-spanned']) {
                            mR = parseInt(ctag['number-rows-spanned'], 10) || 0;
                            mC = parseInt(ctag['number-columns-spanned'], 10) || 0;
                            mrange = {
                                s: {
                                    r: R,
                                    c: C
                                },
                                e: {
                                    r: R + mR - 1,
                                    c: C + mC - 1
                                }
                            };
                            merges.push(mrange);
                        }
                        /* 19.675.2 table:number-columns-repeated */
                        if (ctag['number-columns-repeated']) colpeat = parseInt(ctag['number-columns-repeated'], 10);
                        /* 19.385 office:value-type */
                        switch (q.t) {
                            case 'boolean':
                                q.t = 'b';
                                q.v = parsexmlbool(ctag['boolean-value']);
                                break;
                            case 'float':
                                q.t = 'n';
                                q.v = parseFloat(ctag.value);
                                break;
                            case 'percentage':
                                q.t = 'n';
                                q.v = parseFloat(ctag.value);
                                break;
                            case 'currency':
                                q.t = 'n';
                                q.v = parseFloat(ctag.value);
                                break;
                            case 'date':
                                q.t = 'd';
                                q.v = parseDate(ctag['date-value']);
                                if (!opts.cellDates) {
                                    q.t = 'n';
                                    q.v = datenum(q.v);
                                }
                                q.z = 'm/d/yy';
                                break;
                            case 'time':
                                q.t = 'n';
                                q.v = parse_isodur(ctag['time-value']) / 86400;
                                break;
                            case 'number':
                                q.t = 'n';
                                q.v = parseFloat(ctag['数据数值']);
                                break;
                            default:
                                if (q.t === 'string' || q.t === 'text' || !q.t) {
                                    q.t = 's';
                                    if (ctag['string-value'] != null) textp = unescapexml(ctag['string-value']);
                                } else throw new Error('Unsupported value type ' + q.t);
                        }
                    } else {
                        isstub = false;
                        if (q.t === 's') {
                            q.v = textp || '';
                            isstub = textpidx == 0;
                        }
                        if (atag.Target) q.l = atag;
                        if (comments.length > 0) {
                            q.c = comments;
                            comments = [];
                        }
                        if (textp && opts.cellText !== false) q.w = textp;
                        if (!isstub || opts.sheetStubs) {
                            if (!(opts.sheetRows && opts.sheetRows <= R)) {
                                for (var rpt = 0; rpt < rowpeat; ++rpt) {
                                    colpeat = parseInt(ctag['number-columns-repeated'] || "1", 10);
                                    if (opts.dense) {
                                        if (!ws[R + rpt]) ws[R + rpt] = [];
                                        ws[R + rpt][C] = rpt == 0 ? q : dup(q);
                                        while (--colpeat > 0) ws[R + rpt][C + colpeat] = dup(q);
                                    } else {
                                        ws[encode_cell({
                                            r: R + rpt,
                                            c: C
                                        })] = q;
                                        while (--colpeat > 0) ws[encode_cell({
                                            r: R + rpt,
                                            c: C + colpeat
                                        })] = dup(q);
                                    }
                                    if (range.e.c <= C) range.e.c = C;
                                }
                            }
                        }
                        colpeat = parseInt(ctag['number-columns-repeated'] || "1", 10);
                        C += colpeat - 1;
                        colpeat = 0;
                        q = {};
                        textp = "";
                    }
                    atag = ({});
                    break; // 9.1.4 
                    /* pure state */
                case 'document': // TODO:  is the root for FODS
                case 'document-content':
                case '电子表格文档': // 3.1.3.2 
                case 'spreadsheet':
                case '主体': // 3.7 
                case 'scripts': // 3.12 
                case 'styles': // TODO 
                case 'font-face-decls': // 3.14 
                    if (Rn[1] === '/') {
                        if ((tmp = state.pop())[0] !== Rn[3]) throw "Bad state: " + tmp;
                    } else if (Rn[0].charAt(Rn[0].length - 2) !== '/') state.push([Rn[3], true]);
                    break;
                case 'annotation': // 14.1 
                    if (Rn[1] === '/') {
                        if ((tmp = state.pop())[0] !== Rn[3]) throw "Bad state: " + tmp;
                        comment.t = textp;
                        comment.a = creator;
                        comments.push(comment);
                    } else if (Rn[0].charAt(Rn[0].length - 2) !== '/') {
                        state.push([Rn[3], false]);
                    }
                    creator = "";
                    creatoridx = 0;
                    textp = "";
                    textpidx = 0;
                    break;
                case 'creator': // 4.3.2.7 
                    if (Rn[1] === '/') {
                        creator = str.slice(creatoridx, Rn.index);
                    } else creatoridx = Rn.index + Rn[0].length;
                    break;
                    /* ignore state */
                case 'meta':
                case '元数据': // TODO:   FODS/UOF
                case 'settings': // TODO: 
                case 'config-item-set': // TODO: 
                case 'config-item-map-indexed': // TODO: 
                case 'config-item-map-entry': // TODO: 
                case 'config-item-map-named': // TODO: 
                case 'shapes': // 9.2.8 
                case 'frame': // 10.4.2 
                case 'text-box': // 10.4.3 
                case 'image': // 10.4.4 
                case 'data-pilot-tables': // 9.6.2 
                case 'list-style': // 16.30 
                case 'form': // 13.13 
                case 'dde-links': // 9.8 
                case 'event-listeners': // TODO
                case 'chart': // TODO
                    if (Rn[1] === '/') {
                        if ((tmp = state.pop())[0] !== Rn[3]) throw "Bad state: " + tmp;
                    } else if (Rn[0].charAt(Rn[0].length - 2) !== '/') state.push([Rn[3], false]);
                    textp = "";
                    textpidx = 0;
                    break;
                case 'scientific-number': // TODO: 
                    break;
                case 'currency-symbol': // TODO: 
                    break;
                case 'currency-style': // TODO: 
                    break;
                case 'number-style': // 16.27.2 
                case 'percentage-style': // 16.27.9 
                case 'date-style': // 16.27.10 
                case 'time-style': // 16.27.18 
                    if (Rn[1] === '/') {
                        number_format_map[NFtag.name] = NF;
                        if ((tmp = state.pop())[0] !== Rn[3]) throw "Bad state: " + tmp;
                    } else if (Rn[0].charAt(Rn[0].length - 2) !== '/') {
                        NF = "";
                        NFtag = parsexmltag(Rn[0], false);
                        state.push([Rn[3], true]);
                    }
                    break;
                case 'script':
                    break; // 3.13 
                case 'libraries':
                    break; // TODO: 
                case 'automatic-styles':
                    break; // 3.15.3 
                case 'master-styles':
                    break; // TODO: 
                case 'default-style': // TODO: 
                case 'page-layout':
                    break; // TODO: 
                case 'style':
                    break; // 16.2 
                case 'map':
                    break; // 16.3 
                case 'font-face':
                    break; // 16.21 
                case 'paragraph-properties':
                    break; // 17.6 
                case 'table-properties':
                    break; // 17.15 
                case 'table-column-properties':
                    break; // 17.16 
                case 'table-row-properties':
                    break; // 17.17 
                case 'table-cell-properties':
                    break; // 17.18 
                case 'number': // 16.27.3 
                    switch (state[state.length - 1][0]) {
                        case 'time-style':
                        case 'date-style':
                            tag = parsexmltag(Rn[0], false);
                            NF += number_formats[Rn[3]][tag.style === 'long' ? 1 : 0];
                            break;
                    }
                    break;
                case 'fraction':
                    break; // TODO 16.27.6 
                case 'day': // 16.27.11 
                case 'month': // 16.27.12 
                case 'year': // 16.27.13 
                case 'era': // 16.27.14 
                case 'day-of-week': // 16.27.15 
                case 'week-of-year': // 16.27.16 
                case 'quarter': // 16.27.17 
                case 'hours': // 16.27.19 
                case 'minutes': // 16.27.20 
                case 'seconds': // 16.27.21 
                case 'am-pm': // 16.27.22 
                    switch (state[state.length - 1][0]) {
                        case 'time-style':
                        case 'date-style':
                            tag = parsexmltag(Rn[0], false);
                            NF += number_formats[Rn[3]][tag.style === 'long' ? 1 : 0];
                            break;
                    }
                    break;
                case 'boolean-style':
                    break; // 16.27.23 
                case 'boolean':
                    break; // 16.27.24 
                case 'text-style':
                    break; // 16.27.25 
                case 'text': // 16.27.26 
                    if (Rn[0].slice(-2) === "/>") break;
                    else if (Rn[1] === "/") switch (state[state.length - 1][0]) {
                        case 'number-style':
                        case 'date-style':
                        case 'time-style':
                            NF += str.slice(pidx, Rn.index);
                            break;
                    }
                    else pidx = Rn.index + Rn[0].length;
                    break;
                case 'named-range': // 9.4.12 
                    tag = parsexmltag(Rn[0], false);
                    _Ref = ods_to_csf_3D(tag['cell-range-address']);
                    var nrange = ({
                        Name: tag.name,
                        Ref: _Ref[0] + '!' + _Ref[1]
                    });
                    if (intable) nrange.Sheet = SheetNames.length;
                    WB.Names.push(nrange);
                    break;
                case 'text-content':
                    break; // 16.27.27 
                case 'text-properties':
                    break; // 16.27.27 
                case 'embedded-text':
                    break; // 16.27.4 
                case 'body':
                case '电子表格':
                    break; // 3.3 16.9.6 19.726.3
                case 'forms':
                    break; // 12.25.2 13.2
                case 'table-column':
                    break; // 9.1.6 
                case 'table-header-rows':
                    break; // 9.1.7 
                case 'table-rows':
                    break; // 9.1.12 
                    /* TODO: outline levels */
                case 'table-column-group':
                    break; // 9.1.10 
                case 'table-header-columns':
                    break; // 9.1.11 
                case 'table-columns':
                    break; // 9.1.12 
                case 'null-date':
                    break; // 9.4.2  TODO: date1904
                case 'graphic-properties':
                    break; // 17.21 
                case 'calculation-settings':
                    break; // 9.4.1 
                case 'named-expressions':
                    break; // 9.4.11 
                case 'label-range':
                    break; // 9.4.9 
                case 'label-ranges':
                    break; // 9.4.10 
                case 'named-expression':
                    break; // 9.4.13 
                case 'sort':
                    break; // 9.4.19 
                case 'sort-by':
                    break; // 9.4.20 
                case 'sort-groups':
                    break; // 9.4.22 
                case 'tab':
                    break; // 6.1.4 
                case 'line-break':
                    break; // 6.1.5 
                case 'span':
                    break; // 6.1.7 
                case 'p':
                case '文本串': // 5.1.3 
                    if (Rn[1] === '/' && (!ctag || !ctag['string-value'])) textp = (textp.length > 0 ? textp + "\n" : "") + parse_text_p(str.slice(textpidx, Rn.index), textptag);
                    else {
                        textptag = parsexmltag(Rn[0], false);
                        textpidx = Rn.index + Rn[0].length;
                    }
                    break; // 
                case 's':
                    break; // 
                case 'database-range': // 9.4.15 
                    if (Rn[1] === '/') break;
                    try {
                        _Ref = ods_to_csf_3D(parsexmltag(Rn[0])['target-range-address']);
                        Sheets[_Ref[0]]['!autofilter'] = {
                            ref: _Ref[1]
                        };
                    } catch (e) { /* empty */ }
                    break;
                case 'date':
                    break; // <*:date>
                case 'object':
                    break; // 10.4.6.2 
                case 'title':
                case '标题':
                    break; // <*:title> OR 
                case 'desc':
                    break; // <*:desc>
                case 'binary-data':
                    break; // 10.4.5 TODO: b64 blob
                    /* 9.2 Advanced Tables */
                case 'table-source':
                    break; // 9.2.6
                case 'scenario':
                    break; // 9.2.6
                case 'iteration':
                    break; // 9.4.3 
                case 'content-validations':
                    break; // 9.4.4 
                case 'filter':
                    break; // 9.5.2 
                case 'filter-and':
                    break; // 9.5.3 
                case 'filter-or':
                    break; // 9.5.4 
                case 'filter-condition':
                    break; // 9.5.5 
                case 'list-level-style-bullet':
                    break; // 16.31 
                case 'page-count':
                    break; // TODO 
                case 'time':
                    break; // TODO 
                    /* 9.3 Advanced Table Cells */
                case 'cell-range-source':
                    break; // 9.3.1 
                case 'property':
                    break; // 13.8 
                case 'a': // 6.1.8 hyperlink
                    if (Rn[1] !== '/') {
                        atag = parsexmltag(Rn[0], false);
                        if (!atag.href) break;
                        atag.Target = atag.href;
                        delete atag.href;
                        if (atag.Target.charAt(0) == "#" && atag.Target.indexOf(".") > -1) {
                            _Ref = ods_to_csf_3D(atag.Target.slice(1));
                            atag.Target = "#" + _Ref[0] + "!" + _Ref[1];
                        }
                    }
                    break;
                    /* non-standard */
                case 'table-protection':
                    break;
                case 'data-pilot-grand-total':
                    break; // ';
        return function wso() {
            return XML_HEADER + payload;
        };
    })();
    var write_content_ods = (function () {
        /* 6.1.2 White Space Characters */
        var write_text_p = function (text) {
            return escapexml(text)
                .replace(/  +/g, function ($$) {
                    return '';
                })
                .replace(/\t/g, "")
                .replace(/\n/g, "")
                .replace(/^ /, "").replace(/ $/, "");
        };
        var null_cell_xml = '          \n';
        var covered_cell_xml = '          \n';
        var write_ws = function (ws, wb, i) {
            /* Section 9 Tables */
            var o = [];
            o.push('      \n');
            var R = 0,
                C = 0,
                range = decode_range(ws['!ref']);
            var marr = ws['!merges'] || [],
                mi = 0;
            var dense = Array.isArray(ws);
            for (R = 0; R < range.s.r; ++R) o.push('        \n');
            for (; R <= range.e.r; ++R) {
                o.push('        \n');
                for (C = 0; C < range.s.c; ++C) o.push(null_cell_xml);
                for (; C <= range.e.c; ++C) {
                    var skip = false,
                        ct = {},
                        textp = "";
                    for (mi = 0; mi != marr.length; ++mi) {
                        if (marr[mi].s.c > C) continue;
                        if (marr[mi].s.r > R) continue;
                        if (marr[mi].e.c < C) continue;
                        if (marr[mi].e.r < R) continue;
                        if (marr[mi].s.c != C || marr[mi].s.r != R) skip = true;
                        ct['table:number-columns-spanned'] = (marr[mi].e.c - marr[mi].s.c + 1);
                        ct['table:number-rows-spanned'] = (marr[mi].e.r - marr[mi].s.r + 1);
                        break;
                    }
                    if (skip) {
                        o.push(covered_cell_xml);
                        continue;
                    }
                    var ref = encode_cell({
                            r: R,
                            c: C
                        }),
                        cell = dense ? (ws[R] || [])[C] : ws[ref];
                    if (cell && cell.f) {
                        ct['table:formula'] = escapexml(csf_to_ods_formula(cell.f));
                        if (cell.F) {
                            if (cell.F.slice(0, ref.length) == ref) {
                                var _Fref = decode_range(cell.F);
                                ct['table:number-matrix-columns-spanned'] = (_Fref.e.c - _Fref.s.c + 1);
                                ct['table:number-matrix-rows-spanned'] = (_Fref.e.r - _Fref.s.r + 1);
                            }
                        }
                    }
                    if (!cell) {
                        o.push(null_cell_xml);
                        continue;
                    }
                    switch (cell.t) {
                        case 'b':
                            textp = (cell.v ? 'TRUE' : 'FALSE');
                            ct['office:value-type'] = "boolean";
                            ct['office:boolean-value'] = (cell.v ? 'true' : 'false');
                            break;
                        case 'n':
                            textp = (cell.w || String(cell.v || 0));
                            ct['office:value-type'] = "float";
                            ct['office:value'] = (cell.v || 0);
                            break;
                        case 's':
                        case 'str':
                            textp = cell.v;
                            ct['office:value-type'] = "string";
                            break;
                        case 'd':
                            textp = (cell.w || (parseDate(cell.v).toISOString()));
                            ct['office:value-type'] = "date";
                            ct['office:date-value'] = (parseDate(cell.v).toISOString());
                            ct['table:style-name'] = "ce1";
                            break;
                            //case 'e':
                        default:
                            o.push(null_cell_xml);
                            continue;
                    }
                    var text_p = write_text_p(textp);
                    if (cell.l && cell.l.Target) {
                        var _tgt = cell.l.Target;
                        _tgt = _tgt.charAt(0) == "#" ? "#" + csf_to_ods_3D(_tgt.slice(1)) : _tgt;
                        text_p = writextag('text:a', text_p, {
                            'xlink:href': _tgt
                        });
                    }
                    o.push('          ' + writextag('table:table-cell', writextag('text:p', text_p, {}), ct) + '\n');
                }
                o.push('        \n');
            }
            o.push('      \n');
            return o.join("");
        };
        var write_automatic_styles_ods = function (o) {
            o.push(' \n');
            o.push('  \n');
            o.push('   \n');
            o.push('   /\n');
            o.push('   \n');
            o.push('   /\n');
            o.push('   \n');
            o.push('  \n');
            o.push('  \n');
            o.push(' \n');
        };
        return function wcx(wb, opts) {
            var o = [XML_HEADER];
            /* 3.1.3.2 */
            var attr = wxt_helper({
                'xmlns:office': "urn:oasis:names:tc:opendocument:xmlns:office:1.0",
                'xmlns:table': "urn:oasis:names:tc:opendocument:xmlns:table:1.0",
                'xmlns:style': "urn:oasis:names:tc:opendocument:xmlns:style:1.0",
                'xmlns:text': "urn:oasis:names:tc:opendocument:xmlns:text:1.0",
                'xmlns:draw': "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0",
                'xmlns:fo': "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0",
                'xmlns:xlink': "http://www.w3.org/1999/xlink",
                'xmlns:dc': "http://purl.org/dc/elements/1.1/",
                'xmlns:meta': "urn:oasis:names:tc:opendocument:xmlns:meta:1.0",
                'xmlns:number': "urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0",
                'xmlns:presentation': "urn:oasis:names:tc:opendocument:xmlns:presentation:1.0",
                'xmlns:svg': "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0",
                'xmlns:chart': "urn:oasis:names:tc:opendocument:xmlns:chart:1.0",
                'xmlns:dr3d': "urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0",
                'xmlns:math': "http://www.w3.org/1998/Math/MathML",
                'xmlns:form': "urn:oasis:names:tc:opendocument:xmlns:form:1.0",
                'xmlns:script': "urn:oasis:names:tc:opendocument:xmlns:script:1.0",
                'xmlns:ooo': "http://openoffice.org/2004/office",
                'xmlns:ooow': "http://openoffice.org/2004/writer",
                'xmlns:oooc': "http://openoffice.org/2004/calc",
                'xmlns:dom': "http://www.w3.org/2001/xml-events",
                'xmlns:xforms': "http://www.w3.org/2002/xforms",
                'xmlns:xsd': "http://www.w3.org/2001/XMLSchema",
                'xmlns:xsi': "http://www.w3.org/2001/XMLSchema-instance",
                'xmlns:sheet': "urn:oasis:names:tc:opendocument:sh33tjs:1.0",
                'xmlns:rpt': "http://openoffice.org/2005/report",
                'xmlns:of': "urn:oasis:names:tc:opendocument:xmlns:of:1.2",
                'xmlns:xhtml': "http://www.w3.org/1999/xhtml",
                'xmlns:grddl': "http://www.w3.org/2003/g/data-view#",
                'xmlns:tableooo': "http://openoffice.org/2009/table",
                'xmlns:drawooo': "http://openoffice.org/2010/draw",
                'xmlns:calcext': "urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0",
                'xmlns:loext': "urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0",
                'xmlns:field': "urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0",
                'xmlns:formx': "urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0",
                'xmlns:css3t': "http://www.w3.org/TR/css3-text/",
                'office:version': "1.2"
            });
            var fods = wxt_helper({
                'xmlns:config': "urn:oasis:names:tc:opendocument:xmlns:config:1.0",
                'office:mimetype': "application/vnd.oasis.opendocument.spreadsheet"
            });
            if (opts.bookType == "fods") o.push('\n');
            else o.push('\n');
            write_automatic_styles_ods(o);
            o.push('  \n');
            o.push('    \n');
            for (var i = 0; i != wb.SheetNames.length; ++i) o.push(write_ws(wb.Sheets[wb.SheetNames[i]], wb, i, opts));
            o.push('    \n');
            o.push('  \n');
            if (opts.bookType == "fods") o.push('');
            else o.push('');
            return o.join("");
        };
    })();
    function write_ods(wb, opts) {
        if (opts.bookType == "fods") return write_content_ods(wb, opts);
        var zip = new jszip();
        var f = "";
        var manifest = [];
        var rdf = [];
        /* Part 3 Section 3.3 MIME Media Type */
        f = "mimetype";
        zip.file(f, "application/vnd.oasis.opendocument.spreadsheet");
        /* Part 1 Section 2.2 Documents */
        f = "content.xml";
        zip.file(f, write_content_ods(wb, opts));
        manifest.push([f, "text/xml"]);
        rdf.push([f, "ContentFile"]);
        /* TODO: these are hard-coded styles to satiate excel */
        f = "styles.xml";
        zip.file(f, write_styles_ods(wb, opts));
        manifest.push([f, "text/xml"]);
        rdf.push([f, "StylesFile"]);
        /* TODO: this is hard-coded to satiate excel */
        f = "meta.xml";
        zip.file(f, write_meta_ods());
        manifest.push([f, "text/xml"]);
        rdf.push([f, "MetadataFile"]);
        /* Part 3 Section 6 Metadata Manifest File */
        f = "manifest.rdf";
        zip.file(f, write_rdf(rdf /*, opts*/ ));
        manifest.push([f, "application/rdf+xml"]);
        /* Part 3 Section 4 Manifest File */
        f = "META-INF/manifest.xml";
        zip.file(f, write_manifest(manifest /*, opts*/ ));
        return zip;
    }
    function write_sheet_index(wb, sheet) {
        if (!sheet) return 0;
        var idx = wb.SheetNames.indexOf(sheet);
        if (idx == -1) throw new Error("Sheet not found: " + sheet);
        return idx;
    }
    function write_obj_str(factory) {
        return function write_str(wb, o) {
            var idx = write_sheet_index(wb, o.sheet);
            return factory.from_sheet(wb.Sheets[wb.SheetNames[idx]], o, wb);
        };
    }
    var write_htm_str = write_obj_str(HTML_);
    var write_csv_str = write_obj_str({
        from_sheet: sheet_to_csv
    });
    var write_slk_str = write_obj_str(SYLK);
    var write_dif_str = write_obj_str(DIF);
    var write_prn_str = write_obj_str(PRN);
    var write_rtf_str = write_obj_str(RTF);
    var write_txt_str = write_obj_str({
        from_sheet: sheet_to_txt
    });
    var write_dbf_buf = write_obj_str(DBF);
    var write_eth_str = write_obj_str(ETH);
    function fix_opts_func(defaults) {
        return function fix_opts(opts) {
            for (var i = 0; i != defaults.length; ++i) {
                var d = defaults[i];
                if (opts[d[0]] === undefined) opts[d[0]] = d[1];
                if (d[2] === 'n') opts[d[0]] = Number(opts[d[0]]);
            }
        };
    }
    var fix_read_opts = fix_opts_func([
        ['cellNF', false], /* emit cell number format string as .z */
        ['cellHTML', true], /* emit html string as .h */
        ['cellFormula', true], /* emit formulae as .f */
        ['cellStyles', false], /* emits style/theme as .s */
        ['cellText', true], /* emit formatted text as .w */
        ['cellDates', false], /* emit date cells with type `d` */
        ['sheetStubs', false], /* emit empty cells */
        ['sheetRows', 0, 'n'], /* read n rows (0 = read all rows) */
        ['bookDeps', false], /* parse calculation chains */
        ['bookSheets', false], /* only try to get sheet names (no Sheets) */
        ['bookProps', false], /* only try to get properties (no Sheets) */
        ['bookFiles', false], /* include raw file structure (keys, files, cfb) */
        ['bookVBA', false], /* include vba raw data (vbaraw) */
        ['password', ''], /* password */
        ['WTF', false] /* WTF mode (throws errors) */
    ]);
    var fix_write_opts = fix_opts_func([
        ['cellDates', false], /* write date cells with type `d` */
        ['bookSST', false], /* Generate Shared String Table */
        ['bookType', 'xlsx'], /* Type of workbook (xlsx/m/b) */
        ['compression', false], /* Use file compression */
        ['WTF', false] /* WTF mode (throws errors) */
    ]);
    function get_sheet_type(n) {
        if (RELS.WS.indexOf(n) > -1) return "sheet";
        if (RELS.CS && n == RELS.CS) return "chart";
        if (RELS.DS && n == RELS.DS) return "dialog";
        if (RELS.MS && n == RELS.MS) return "macro";
        return (n && n.length) ? n : "sheet";
    }
    function safe_parse_wbrels(wbrels, sheets) {
        if (!wbrels) return 0;
        try {
            wbrels = sheets.map(function pwbr(w) {
                if (!w.id) w.id = w.strRelID;
                return [w.name, wbrels['!id'][w.id].Target, get_sheet_type(wbrels['!id'][w.id].Type)];
            });
        } catch (e) {
            return null;
        }
        return !wbrels || wbrels.length === 0 ? null : wbrels;
    }
    function safe_parse_sheet(zip, path, relsPath, sheet, idx, sheetRels, sheets, stype, opts, wb, themes, styles) {
        try {
            sheetRels[sheet] = parse_rels(getzipstr(zip, relsPath, true), path);
            var data = getzipdata(zip, path);
            var _ws;
            switch (stype) {
                case 'sheet':
                    _ws = parse_ws(data, path, idx, opts, sheetRels[sheet], wb, themes, styles);
                    break;
                case 'chart':
                    _ws = parse_cs(data, path, idx, opts, sheetRels[sheet], wb, themes, styles);
                    if (!_ws || !_ws['!chart']) break;
                    var dfile = resolve_path(_ws['!chart'].Target, path);
                    var drelsp = get_rels_path(dfile);
                    var draw = parse_drawing(getzipstr(zip, dfile, true), parse_rels(getzipstr(zip, drelsp, true), dfile));
                    var chartp = resolve_path(draw, dfile);
                    var crelsp = get_rels_path(chartp);
                    _ws = parse_chart(getzipstr(zip, chartp, true), chartp, opts, parse_rels(getzipstr(zip, crelsp, true), chartp), wb, _ws);
                    break;
                case 'macro':
                    _ws = parse_ms(data, path, idx, opts, sheetRels[sheet], wb, themes, styles);
                    break;
                case 'dialog':
                    _ws = parse_ds(data, path, idx, opts, sheetRels[sheet], wb, themes, styles);
                    break;
            }
            sheets[sheet] = _ws;
        } catch (e) {
            if (opts.WTF) throw e;
        }
    }
    function strip_front_slash(x) {
        return x.charAt(0) == '/' ? x.slice(1) : x;
    }
    function parse_zip(zip, opts) {
        make_ssf(SSF);
        opts = opts || {};
        fix_read_opts(opts);
        /* OpenDocument Part 3 Section 2.2.1 OpenDocument Package */
        if (safegetzipfile(zip, 'META-INF/manifest.xml')) return parse_ods(zip, opts);
        /* UOC */
        if (safegetzipfile(zip, 'objectdata.xml')) return parse_ods(zip, opts);
        /* Numbers */
        if (safegetzipfile(zip, 'Index/Document.iwa')) throw new Error('Unsupported NUMBERS file');
        var entries = zipentries(zip);
        var dir = parse_ct((getzipstr(zip, '[Content_Types].xml')));
        var xlsb = false;
        var sheets, binname;
        if (dir.workbooks.length === 0) {
            binname = "xl/workbook.xml";
            if (getzipdata(zip, binname, true)) dir.workbooks.push(binname);
        }
        if (dir.workbooks.length === 0) {
            binname = "xl/workbook.bin";
            if (!getzipdata(zip, binname, true)) throw new Error("Could not find workbook");
            dir.workbooks.push(binname);
            xlsb = true;
        }
        if (dir.workbooks[0].slice(-3) == "bin") xlsb = true;
        var themes = ({});
        var styles = ({});
        if (!opts.bookSheets && !opts.bookProps) {
            strs = [];
            if (dir.sst) try {
                strs = parse_sst(getzipdata(zip, strip_front_slash(dir.sst)), dir.sst, opts);
            } catch (e) {
                if (opts.WTF) throw e;
            }
            if (opts.cellStyles && dir.themes.length) themes = parse_theme(getzipstr(zip, dir.themes[0].replace(/^\//, ''), true) || "", dir.themes[0], opts);
            if (dir.style) styles = parse_sty(getzipdata(zip, strip_front_slash(dir.style)), dir.style, themes, opts);
        }
        /*var externbooks = */
        dir.links.map(function (link) {
            return parse_xlink(getzipdata(zip, strip_front_slash(link)), link, opts);
        });
        var wb = parse_wb(getzipdata(zip, strip_front_slash(dir.workbooks[0])), dir.workbooks[0], opts);
        var props = {},
            propdata = "";
        if (dir.coreprops.length) {
            propdata = getzipdata(zip, strip_front_slash(dir.coreprops[0]), true);
            if (propdata) props = parse_core_props(propdata);
            if (dir.extprops.length !== 0) {
                propdata = getzipdata(zip, strip_front_slash(dir.extprops[0]), true);
                if (propdata) parse_ext_props(propdata, props, opts);
            }
        }
        var custprops = {};
        if (!opts.bookSheets || opts.bookProps) {
            if (dir.custprops.length !== 0) {
                propdata = getzipstr(zip, strip_front_slash(dir.custprops[0]), true);
                if (propdata) custprops = parse_cust_props(propdata, opts);
            }
        }
        var out = ({});
        if (opts.bookSheets || opts.bookProps) {
            if (wb.Sheets) sheets = wb.Sheets.map(function pluck(x) {
                return x.name;
            });
            else if (props.Worksheets && props.SheetNames.length > 0) sheets = props.SheetNames;
            if (opts.bookProps) {
                out.Props = props;
                out.Custprops = custprops;
            }
            if (opts.bookSheets && typeof sheets !== 'undefined') out.SheetNames = sheets;
            if (opts.bookSheets ? out.SheetNames : opts.bookProps) return out;
        }
        sheets = {};
        var deps = {};
        if (opts.bookDeps && dir.calcchain) deps = parse_cc(getzipdata(zip, strip_front_slash(dir.calcchain)), dir.calcchain, opts);
        var i = 0;
        var sheetRels = ({});
        var path, relsPath;
        {
            var wbsheets = wb.Sheets;
            props.Worksheets = wbsheets.length;
            props.SheetNames = [];
            for (var j = 0; j != wbsheets.length; ++j) {
                props.SheetNames[j] = wbsheets[j].name;
            }
        }
        var wbext = xlsb ? "bin" : "xml";
        var wbrelsi = dir.workbooks[0].lastIndexOf("/");
        var wbrelsfile = (dir.workbooks[0].slice(0, wbrelsi + 1) + "_rels/" + dir.workbooks[0].slice(wbrelsi + 1) + ".rels").replace(/^\//, "");
        if (!safegetzipfile(zip, wbrelsfile)) wbrelsfile = 'xl/_rels/workbook.' + wbext + '.rels';
        var wbrels = parse_rels(getzipstr(zip, wbrelsfile, true), wbrelsfile);
        if (wbrels) wbrels = safe_parse_wbrels(wbrels, wb.Sheets);
        /* Numbers iOS hack */
        var nmode = (getzipdata(zip, "xl/worksheets/sheet.xml", true)) ? 1 : 0;
        for (i = 0; i != props.Worksheets; ++i) {
            var stype = "sheet";
            if (wbrels && wbrels[i]) {
                path = 'xl/' + (wbrels[i][1]).replace(/[\/]?xl\//, "");
                if (!safegetzipfile(zip, path)) path = wbrels[i][1];
                if (!safegetzipfile(zip, path)) path = wbrelsfile.replace(/_rels\/.*$/, "") + wbrels[i][1];
                stype = wbrels[i][2];
            } else {
                path = 'xl/worksheets/sheet' + (i + 1 - nmode) + "." + wbext;
                path = path.replace(/sheet0\./, "sheet.");
            }
            relsPath = path.replace(/^(.*)(\/)([^\/]*)$/, "$1/_rels/$3.rels");
            safe_parse_sheet(zip, path, relsPath, props.SheetNames[i], i, sheetRels, sheets, stype, opts, wb, themes, styles);
        }
        if (dir.comments) parse_comments(zip, dir.comments, sheets, sheetRels, opts);
        out = ({
            Directory: dir,
            Workbook: wb,
            Props: props,
            Custprops: custprops,
            Deps: deps,
            Sheets: sheets,
            SheetNames: props.SheetNames,
            Strings: strs,
            Styles: styles,
            Themes: themes,
            SSF: SSF.get_table()
        });
        if (opts.bookFiles) {
            out.keys = entries;
            out.files = zip.files;
        }
        if (opts.bookVBA) {
            if (dir.vba.length > 0) out.vbaraw = getzipdata(zip, strip_front_slash(dir.vba[0]), true);
            else if (dir.defaults && dir.defaults.bin === CT_VBA) out.vbaraw = getzipdata(zip, 'xl/vbaProject.bin', true);
        }
        return out;
    }
    /* [MS-OFFCRYPTO] 2.1.1 */
    function parse_xlsxcfb(cfb, _opts) {
        var opts = _opts || {};
        var f = 'Workbook',
            data = CFB.find(cfb, f);
        try {
            f = '/!DataSpaces/Version';
            data = CFB.find(cfb, f);
            if (!data || !data.content) throw new Error("ECMA-376 Encrypted file missing " + f);
            /*var version = */
            parse_DataSpaceVersionInfo(data.content);
            /* 2.3.4.1 */
            f = '/!DataSpaces/DataSpaceMap';
            data = CFB.find(cfb, f);
            if (!data || !data.content) throw new Error("ECMA-376 Encrypted file missing " + f);
            var dsm = parse_DataSpaceMap(data.content);
            if (dsm.length !== 1 || dsm[0].comps.length !== 1 || dsm[0].comps[0].t !== 0 || dsm[0].name !== "StrongEncryptionDataSpace" || dsm[0].comps[0].v !== "EncryptedPackage")
                throw new Error("ECMA-376 Encrypted file bad " + f);
            /* 2.3.4.2 */
            f = '/!DataSpaces/DataSpaceInfo/StrongEncryptionDataSpace';
            data = CFB.find(cfb, f);
            if (!data || !data.content) throw new Error("ECMA-376 Encrypted file missing " + f);
            var seds = parse_DataSpaceDefinition(data.content);
            if (seds.length != 1 || seds[0] != "StrongEncryptionTransform")
                throw new Error("ECMA-376 Encrypted file bad " + f);
            /* 2.3.4.3 */
            f = '/!DataSpaces/TransformInfo/StrongEncryptionTransform/!Primary';
            data = CFB.find(cfb, f);
            if (!data || !data.content) throw new Error("ECMA-376 Encrypted file missing " + f);
            /*var hdr = */
            parse_Primary(data.content);
        } catch (e) {}
        f = '/EncryptionInfo';
        data = CFB.find(cfb, f);
        if (!data || !data.content) throw new Error("ECMA-376 Encrypted file missing " + f);
        var einfo = parse_EncryptionInfo(data.content);
        /* 2.3.4.4 */
        f = '/EncryptedPackage';
        data = CFB.find(cfb, f);
        if (!data || !data.content) throw new Error("ECMA-376 Encrypted file missing " + f);
        /*global decrypt_agile */
        if (einfo[0] == 0x04 && typeof decrypt_agile !== 'undefined') return decrypt_agile(einfo[1], data.content, opts.password || "", opts);
        /*global decrypt_std76 */
        if (einfo[0] == 0x02 && typeof decrypt_std76 !== 'undefined') return decrypt_std76(einfo[1], data.content, opts.password || "", opts);
        throw new Error("File is password-protected");
    }
    function write_zip(wb, opts) {
        _shapeid = 1024;
        if (opts.bookType == "ods") return write_ods(wb, opts);
        if (wb && !wb.SSF) {
            wb.SSF = SSF.get_table();
        }
        if (wb && wb.SSF) {
            make_ssf(SSF);
            SSF.load_table(wb.SSF);
            // $FlowIgnore
            opts.revssf = evert_num(wb.SSF);
            opts.revssf[wb.SSF[65535]] = 0;
            opts.ssf = wb.SSF;
        }
        opts.rels = {};
        opts.wbrels = {};
        opts.Strings = [];
        opts.Strings.Count = 0;
        opts.Strings.Unique = 0;
        if (browser_has_Map) opts.revStrings = new Map();
        else {
            opts.revStrings = {};
            opts.revStrings.foo = [];
            delete opts.revStrings.foo;
        }
        var wbext = opts.bookType == "xlsb" ? "bin" : "xml";
        var vbafmt = VBAFMTS.indexOf(opts.bookType) > -1;
        var ct = new_ct();
        fix_write_opts(opts = opts || {});
        var zip = new jszip();
        var f = "",
            rId = 0;
        opts.cellXfs = [];
        get_cell_style(opts.cellXfs, {}, {
            revssf: {
                "General": 0
            }
        });
        if (!wb.Props) wb.Props = {};
        f = "docProps/core.xml";
        zip.file(f, write_core_props(wb.Props, opts));
        ct.coreprops.push(f);
        add_rels(opts.rels, 2, f, RELS.CORE_PROPS);
        f = "docProps/app.xml";
        if (wb.Props && wb.Props.SheetNames) { /* empty */ } else if (!wb.Workbook || !wb.Workbook.Sheets) wb.Props.SheetNames = wb.SheetNames;
        else {
            var _sn = [];
            for (var _i = 0; _i < wb.SheetNames.length; ++_i)
                if ((wb.Workbook.Sheets[_i] || {}).Hidden != 2) _sn.push(wb.SheetNames[_i]);
            wb.Props.SheetNames = _sn;
        }
        wb.Props.Worksheets = wb.Props.SheetNames.length;
        zip.file(f, write_ext_props(wb.Props, opts));
        ct.extprops.push(f);
        add_rels(opts.rels, 3, f, RELS.EXT_PROPS);
        if (wb.Custprops !== wb.Props && keys(wb.Custprops || {}).length > 0) {
            f = "docProps/custom.xml";
            zip.file(f, write_cust_props(wb.Custprops, opts));
            ct.custprops.push(f);
            add_rels(opts.rels, 4, f, RELS.CUST_PROPS);
        }
        for (rId = 1; rId <= wb.SheetNames.length; ++rId) {
            var wsrels = {
                '!id': {}
            };
            var ws = wb.Sheets[wb.SheetNames[rId - 1]];
            var _type = (ws || {})["!type"] || "sheet";
            switch (_type) {
                case "chart":
                    /*
			f = "xl/chartsheets/sheet" + rId + "." + wbext;
			zip.file(f, write_cs(rId-1, f, opts, wb, wsrels));
			ct.charts.push(f);
			add_rels(wsrels, -1, "chartsheets/sheet" + rId + "." + wbext, RELS.CS);
			break; */
                    /* falls through */
                default:
                    f = "xl/worksheets/sheet" + rId + "." + wbext;
                    zip.file(f, write_ws(rId - 1, f, opts, wb, wsrels));
                    ct.sheets.push(f);
                    add_rels(opts.wbrels, -1, "worksheets/sheet" + rId + "." + wbext, RELS.WS[0]);
            }
            if (ws) {
                var comments = ws['!comments'];
                if (comments && comments.length > 0) {
                    var cf = "xl/comments" + rId + "." + wbext;
                    zip.file(cf, write_cmnt(comments, cf, opts));
                    ct.comments.push(cf);
                    add_rels(wsrels, -1, "../comments" + rId + "." + wbext, RELS.CMNT);
                }
                if (ws['!legacy']) {
                    zip.file("xl/drawings/vmlDrawing" + (rId) + ".vml", write_comments_vml(rId, ws['!comments']));
                }
                delete ws['!comments'];
                delete ws['!legacy'];
            }
            if (wsrels['!id'].rId1) zip.file(get_rels_path(f), write_rels(wsrels));
        }
        if (opts.Strings != null && opts.Strings.length > 0) {
            f = "xl/sharedStrings." + wbext;
            zip.file(f, write_sst(opts.Strings, f, opts));
            ct.strs.push(f);
            add_rels(opts.wbrels, -1, "sharedStrings." + wbext, RELS.SST);
        }
        f = "xl/workbook." + wbext;
        zip.file(f, write_wb(wb, f, opts));
        ct.workbooks.push(f);
        add_rels(opts.rels, 1, f, RELS.WB);
        /* TODO: something more intelligent with themes */
        f = "xl/theme/theme1.xml";
        zip.file(f, write_theme(wb.Themes, opts));
        ct.themes.push(f);
        add_rels(opts.wbrels, -1, "theme/theme1.xml", RELS.THEME);
        /* TODO: something more intelligent with styles */
        f = "xl/styles." + wbext;
        zip.file(f, write_sty(wb, f, opts));
        ct.styles.push(f);
        add_rels(opts.wbrels, -1, "styles." + wbext, RELS.STY);
        if (wb.vbaraw && vbafmt) {
            f = "xl/vbaProject.bin";
            zip.file(f, wb.vbaraw);
            ct.vba.push(f);
            add_rels(opts.wbrels, -1, "vbaProject.bin", RELS.VBA);
        }
        zip.file("[Content_Types].xml", write_ct(ct, opts));
        zip.file('_rels/.rels', write_rels(opts.rels));
        zip.file('xl/_rels/workbook.' + wbext + '.rels', write_rels(opts.wbrels));
        delete opts.revssf;
        delete opts.ssf;
        return zip;
    }
    function firstbyte(f, o) {
        var x = "";
        switch ((o || {}).type || "base64") {
            case 'buffer':
                return [f[0], f[1], f[2], f[3]];
            case 'base64':
                x = Base64.decode(f.slice(0, 24));
                break;
            case 'binary':
                x = f;
                break;
            case 'array':
                return [f[0], f[1], f[2], f[3]];
            default:
                throw new Error("Unrecognized type " + (o && o.type || "undefined"));
        }
        return [x.charCodeAt(0), x.charCodeAt(1), x.charCodeAt(2), x.charCodeAt(3)];
    }
    function read_cfb(cfb, opts) {
        if (CFB.find(cfb, "EncryptedPackage")) return parse_xlsxcfb(cfb, opts);
        return parse_xlscfb(cfb, opts);
    }
    function read_zip(data, opts) {
        var zip, d = data;
        var o = opts || {};
        if (!o.type) o.type = (has_buf && Buffer.isBuffer(data)) ? "buffer" : "base64";
        switch (o.type) {
            case "base64":
                zip = new jszip(d, {
                    base64: true
                });
                break;
            case "binary":
            case "array":
                zip = new jszip(d, {
                    base64: false
                });
                break;
            case "buffer":
                zip = new jszip(d);
                break;
            default:
                throw new Error("Unrecognized type " + o.type);
        }
        return parse_zip(zip, o);
    }
    function read_plaintext(data, o) {
        var i = 0;
        main: while (i < data.length) switch (data.charCodeAt(i)) {
            case 0x0A:
            case 0x0D:
            case 0x20:
                ++i;
                break;
            case 0x3C:
                return parse_xlml(data.slice(i), o);
            default:
                break main;
        }
        return PRN.to_workbook(data, o);
    }
    function read_plaintext_raw(data, o) {
        var str = "",
            bytes = firstbyte(data, o);
        switch (o.type) {
            case 'base64':
                str = Base64.decode(data);
                break;
            case 'binary':
                str = data;
                break;
            case 'buffer':
                str = data.toString('binary');
                break;
            case 'array':
                str = cc2str(data);
                break;
            default:
                throw new Error("Unrecognized type " + o.type);
        }
        if (bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF) str = utf8read(str);
        return read_plaintext(str, o);
    }
    function read_utf16(data, o) {
        var d = data;
        if (o.type == 'base64') d = Base64.decode(d);
        d = cptable.utils.decode(1200, d.slice(2), 'str');
        o.type = "binary";
        return read_plaintext(d, o);
    }
    function bstrify(data) {
        return !data.match(/[^\x00-\x7F]/) ? data : utf8write(data);
    }
    function read_prn(data, d, o, str) {
        if (str) {
            o.type = "string";
            return PRN.to_workbook(data, o);
        }
        return PRN.to_workbook(d, o);
    }
    function readSync(data, opts) {
        reset_cp();
        if (typeof ArrayBuffer !== 'undefined' && data instanceof ArrayBuffer) return readSync(new Uint8Array(data), opts);
        var d = data,
            n = [0, 0, 0, 0],
            str = false;
        var o = opts || {};
        _ssfopts = {};
        if (o.dateNF) _ssfopts.dateNF = o.dateNF;
        if (!o.type) o.type = (has_buf && Buffer.isBuffer(data)) ? "buffer" : "base64";
        if (o.type == "file") {
            o.type = has_buf ? "buffer" : "binary";
            d = read_binary(data);
        }
        if (o.type == "string") {
            str = true;
            o.type = "binary";
            o.codepage = 65001;
            d = bstrify(data);
        }
        if (o.type == 'array' && typeof Uint8Array !== 'undefined' && data instanceof Uint8Array && typeof ArrayBuffer !== 'undefined') {
            // $FlowIgnore
            var ab = new ArrayBuffer(3),
                vu = new Uint8Array(ab);
            vu.foo = "bar";
            // $FlowIgnore
            if (!vu.foo) {
                o = dup(o);
                o.type = 'array';
                return readSync(ab2a(d), o);
            }
        }
        switch ((n = firstbyte(d, o))[0]) {
            case 0xD0:
                return read_cfb(CFB.read(d, o), o);
            case 0x09:
                return parse_xlscfb(d, o);
            case 0x3C:
                return parse_xlml(d, o);
            case 0x49:
                if (n[1] === 0x44) return read_wb_ID(d, o);
                break;
            case 0x54:
                if (n[1] === 0x41 && n[2] === 0x42 && n[3] === 0x4C) return DIF.to_workbook(d, o);
                break;
            case 0x50:
                return (n[1] === 0x4B && n[2] < 0x09 && n[3] < 0x09) ? read_zip(d, o) : read_prn(data, d, o, str);
            case 0xEF:
                return n[3] === 0x3C ? parse_xlml(d, o) : read_prn(data, d, o, str);
            case 0xFF:
                if (n[1] === 0xFE) {
                    return read_utf16(d, o);
                }
                break;
            case 0x00:
                if (n[1] === 0x00 && n[2] >= 0x02 && n[3] === 0x00) return WK_.to_workbook(d, o);
                break;
            case 0x03:
            case 0x83:
            case 0x8B:
            case 0x8C:
                return DBF.to_workbook(d, o);
            case 0x7B:
                if (n[1] === 0x5C && n[2] === 0x72 && n[3] === 0x74) return RTF.to_workbook(d, o);
                break;
            case 0x0A:
            case 0x0D:
            case 0x20:
                return read_plaintext_raw(d, o);
        }
        if (n[2] <= 12 && n[3] <= 31) return DBF.to_workbook(d, o);
        return read_prn(data, d, o, str);
    }
    function readFileSync(filename, opts) {
        var o = opts || {};
        o.type = 'file';
        return readSync(filename, o);
    }
    function write_cfb_ctr(cfb, o) {
        switch (o.type) {
            case "base64":
            case "binary":
                break;
            case "buffer":
            case "array":
                o.type = "";
                break;
            case "file":
                return write_dl(o.file, CFB.write(cfb, {
                    type: has_buf ? 'buffer' : ""
                }));
            case "string":
                throw new Error("'string' output type invalid for '" + o.bookType + "' files");
            default:
                throw new Error("Unrecognized type " + o.type);
        }
        return CFB.write(cfb, o);
    }
    /*global encrypt_agile */
    function write_zip_type(wb, opts) {
        var o = opts || {};
        var z = write_zip(wb, o);
        var oopts = {};
        if (o.compression) oopts.compression = 'DEFLATE';
        if (o.password) oopts.type = has_buf ? "nodebuffer" : "string";
        else switch (o.type) {
            case "base64":
                oopts.type = "base64";
                break;
            case "binary":
                oopts.type = "string";
                break;
            case "string":
                throw new Error("'string' output type invalid for '" + o.bookType + "' files");
            case "buffer":
            case "file":
                oopts.type = has_buf ? "nodebuffer" : "string";
                break;
            default:
                throw new Error("Unrecognized type " + o.type);
        }
        var out = z.generate(oopts);
        if (o.password && typeof encrypt_agile !== 'undefined') return write_cfb_ctr(encrypt_agile(out, o.password), o);
        if (o.type === "file") return write_dl(o.file, out);
        return o.type == "string" ? utf8read(out) : out;
    }
    function write_cfb_type(wb, opts) {
        var o = opts || {};
        var cfb = write_xlscfb(wb, o);
        return write_cfb_ctr(cfb, o);
    }
    function write_string_type(out, opts, bom) {
        if (!bom) bom = "";
        var o = bom + out;
        switch (opts.type) {
            case "base64":
                return Base64.encode(utf8write(o));
            case "binary":
                return utf8write(o);
            case "string":
                return out;
            case "file":
                return write_dl(opts.file, o, 'utf8');
            case "buffer":
                {
                    // $FlowIgnore
                    if (has_buf) return Buffer_from(o, 'utf8');
                    else return write_string_type(o, {
                        type: 'binary'
                    }).split("").map(function (c) {
                        return c.charCodeAt(0);
                    });
                }
        }
        throw new Error("Unrecognized type " + opts.type);
    }
    function write_stxt_type(out, opts) {
        switch (opts.type) {
            case "base64":
                return Base64.encode(out);
            case "binary":
                return out;
            case "string":
                return out; /* override in sheet_to_txt */
            case "file":
                return write_dl(opts.file, out, 'binary');
            case "buffer":
                {
                    // $FlowIgnore
                    if (has_buf) return Buffer_from(out, 'binary');
                    else return out.split("").map(function (c) {
                        return c.charCodeAt(0);
                    });
                }
        }
        throw new Error("Unrecognized type " + opts.type);
    }
    /* TODO: test consistency */
    function write_binary_type(out, opts) {
        switch (opts.type) {
            case "string":
            case "base64":
            case "binary":
                var bstr = "";
                // $FlowIgnore
                for (var i = 0; i < out.length; ++i) bstr += String.fromCharCode(out[i]);
                return opts.type == 'base64' ? Base64.encode(bstr) : opts.type == 'string' ? utf8read(bstr) : bstr;
            case "file":
                return write_dl(opts.file, out);
            case "buffer":
                return out;
            default:
                throw new Error("Unrecognized type " + opts.type);
        }
    }
    function writeSync(wb, opts) {
        check_wb(wb);
        var o = opts || {};
        if (o.type == "array") {
            o.type = "binary";
            var out = (writeSync(wb, o));
            o.type = "array";
            return s2ab(out);
        }
        switch (o.bookType || 'xlsb') {
            case 'xml':
            case 'xlml':
                return write_string_type(write_xlml(wb, o), o);
            case 'slk':
            case 'sylk':
                return write_string_type(write_slk_str(wb, o), o);
            case 'htm':
            case 'html':
                return write_string_type(write_htm_str(wb, o), o);
            case 'txt':
                return write_stxt_type(write_txt_str(wb, o), o);
            case 'csv':
                return write_string_type(write_csv_str(wb, o), o, "\ufeff");
            case 'dif':
                return write_string_type(write_dif_str(wb, o), o);
            case 'dbf':
                return write_binary_type(write_dbf_buf(wb, o), o);
            case 'prn':
                return write_string_type(write_prn_str(wb, o), o);
            case 'rtf':
                return write_string_type(write_rtf_str(wb, o), o);
            case 'eth':
                return write_string_type(write_eth_str(wb, o), o);
            case 'fods':
                return write_string_type(write_ods(wb, o), o);
            case 'biff2':
                if (!o.biff) o.biff = 2; /* falls through */
            case 'biff3':
                if (!o.biff) o.biff = 3; /* falls through */
            case 'biff4':
                if (!o.biff) o.biff = 4;
                return write_binary_type(write_biff_buf(wb, o), o);
            case 'biff5':
                if (!o.biff) o.biff = 5; /* falls through */
            case 'biff8':
            case 'xla':
            case 'xls':
                if (!o.biff) o.biff = 8;
                return write_cfb_type(wb, o);
            case 'xlsx':
            case 'xlsm':
            case 'xlam':
            case 'xlsb':
            case 'ods':
                return write_zip_type(wb, o);
            default:
                throw new Error("Unrecognized bookType |" + o.bookType + "|");
        }
    }
    function resolve_book_type(o) {
        if (o.bookType) return;
        var _BT = {
            "xls": "biff8",
            "htm": "html",
            "slk": "sylk",
            "socialcalc": "eth",
            "Sh33tJS": "WTF"
        };
        var ext = o.file.slice(o.file.lastIndexOf(".")).toLowerCase();
        if (ext.match(/^\.[a-z]+$/)) o.bookType = ext.slice(1);
        o.bookType = _BT[o.bookType] || o.bookType;
    }
    function writeFileSync(wb, filename, opts) {
        var o = opts || {};
        o.type = 'file';
        o.file = filename;
        resolve_book_type(o);
        return writeSync(wb, o);
    }
    function writeFileAsync(filename, wb, opts, cb) {
        var o = opts || {};
        o.type = 'file';
        o.file = filename;
        resolve_book_type(o);
        o.type = 'buffer';
        var _cb = cb;
        if (!(_cb instanceof Function)) _cb = (opts);
        return _fs.writeFile(filename, writeSync(wb, o), _cb);
    }
    function make_json_row(sheet, r, R, cols, header, hdr, dense, o) {
        var rr = encode_row(R);
        var defval = o.defval,
            raw = o.raw || !o.hasOwnProperty("raw");
        var isempty = true;
        var row = (header === 1) ? [] : {};
        if (header !== 1) {
            if (Object.defineProperty) try {
                Object.defineProperty(row, '__rowNum__', {
                    value: R,
                    enumerable: false
                });
            } catch (e) {
                row.__rowNum__ = R;
            }
            else row.__rowNum__ = R;
        }
        if (!dense || sheet[R])
            for (var C = r.s.c; C <= r.e.c; ++C) {
                var val = dense ? sheet[R][C] : sheet[cols[C] + rr];
                if (val === undefined || val.t === undefined) {
                    if (defval === undefined) continue;
                    if (hdr[C] != null) {
                        row[hdr[C]] = defval;
                    }
                    continue;
                }
                var v = val.v;
                switch (val.t) {
                    case 'z':
                        if (v == null) break;
                        continue;
                    case 'e':
                        v = void 0;
                        break;
                    case 's':
                    case 'd':
                    case 'b':
                    case 'n':
                        break;
                    default:
                        throw new Error('unrecognized type ' + val.t);
                }
                if (hdr[C] != null) {
                    if (v == null) {
                        if (defval !== undefined) row[hdr[C]] = defval;
                        else if (raw && v === null) row[hdr[C]] = null;
                        else continue;
                    } else {
                        row[hdr[C]] = raw ? v : format_cell(val, v, o);
                    }
                    if (v != null) isempty = false;
                }
            }
        return {
            row: row,
            isempty: isempty
        };
    }
    function sheet_to_json(sheet, opts) {
        if (sheet == null || sheet["!ref"] == null) return [];
        var val = {
                t: 'n',
                v: 0
            },
            header = 0,
            offset = 1,
            hdr = [],
            v = 0,
            vv = "";
        var r = {
            s: {
                r: 0,
                c: 0
            },
            e: {
                r: 0,
                c: 0
            }
        };
        var o = opts || {};
        var range = o.range != null ? o.range : sheet["!ref"];
        if (o.header === 1) header = 1;
        else if (o.header === "A") header = 2;
        else if (Array.isArray(o.header)) header = 3;
        switch (typeof range) {
            case 'string':
                r = safe_decode_range(range);
                break;
            case 'number':
                r = safe_decode_range(sheet["!ref"]);
                r.s.r = range;
                break;
            default:
                r = range;
        }
        if (header > 0) offset = 0;
        var rr = encode_row(r.s.r);
        var cols = [];
        var out = [];
        var outi = 0,
            counter = 0;
        var dense = Array.isArray(sheet);
        var R = r.s.r,
            C = 0,
            CC = 0;
        if (dense && !sheet[R]) sheet[R] = [];
        for (C = r.s.c; C <= r.e.c; ++C) {
            cols[C] = encode_col(C);
            val = dense ? sheet[R][C] : sheet[cols[C] + rr];
            switch (header) {
                case 1:
                    hdr[C] = C - r.s.c;
                    break;
                case 2:
                    hdr[C] = cols[C];
                    break;
                case 3:
                    hdr[C] = o.header[C - r.s.c];
                    break;
                default:
                    if (val == null) val = {
                        w: "__EMPTY",
                        t: "s"
                    };
                    vv = v = format_cell(val, null, o);
                    counter = 0;
                    for (CC = 0; CC < hdr.length; ++CC)
                        if (hdr[CC] == vv) vv = v + "_" + (++counter);
                    hdr[C] = vv;
            }
        }
        for (R = r.s.r + offset; R <= r.e.r; ++R) {
            var row = make_json_row(sheet, r, R, cols, header, hdr, dense, o);
            if ((row.isempty === false) || (header === 1 ? o.blankrows !== false : !!o.blankrows)) out[outi++] = row.row;
        }
        out.length = outi;
        return out;
    }
    var qreg = /"/g;
    function make_csv_row(sheet, r, R, cols, fs, rs, FS, o) {
        var isempty = true;
        var row = [],
            txt = "",
            rr = encode_row(R);
        for (var C = r.s.c; C <= r.e.c; ++C) {
            if (!cols[C]) continue;
            var val = o.dense ? (sheet[R] || [])[C] : sheet[cols[C] + rr];
            if (val == null) txt = "";
            else if (val.v != null) {
                isempty = false;
                txt = '' + format_cell(val, null, o);
                for (var i = 0, cc = 0; i !== txt.length; ++i)
                    if ((cc = txt.charCodeAt(i)) === fs || cc === rs || cc === 34) {
                        txt = "\"" + txt.replace(qreg, '""') + "\"";
                        break;
                    }
                if (txt == "ID") txt = '"ID"';
            } else if (val.f != null && !val.F) {
                isempty = false;
                txt = '=' + val.f;
                if (txt.indexOf(",") >= 0) txt = '"' + txt.replace(qreg, '""') + '"';
            } else txt = "";
            /* NOTE: Excel CSV does not support array formulae */
            row.push(txt);
        }
        if (o.blankrows === false && isempty) return null;
        return row.join(FS);
    }
    function sheet_to_csv(sheet, opts) {
        var out = [];
        var o = opts == null ? {} : opts;
        if (sheet == null || sheet["!ref"] == null) return "";
        var r = safe_decode_range(sheet["!ref"]);
        var FS = o.FS !== undefined ? o.FS : ",",
            fs = FS.charCodeAt(0);
        var RS = o.RS !== undefined ? o.RS : "\n",
            rs = RS.charCodeAt(0);
        var endregex = new RegExp((FS == "|" ? "\\|" : FS) + "+$");
        var row = "",
            cols = [];
        o.dense = Array.isArray(sheet);
        var colinfo = o.skipHidden && sheet["!cols"] || [];
        var rowinfo = o.skipHidden && sheet["!rows"] || [];
        for (var C = r.s.c; C <= r.e.c; ++C)
            if (!((colinfo[C] || {}).hidden)) cols[C] = encode_col(C);
        for (var R = r.s.r; R <= r.e.r; ++R) {
            if ((rowinfo[R] || {}).hidden) continue;
            row = make_csv_row(sheet, r, R, cols, fs, rs, FS, o);
            if (row == null) {
                continue;
            }
            if (o.strip) row = row.replace(endregex, "");
            out.push(row + RS);
        }
        delete o.dense;
        return out.join("");
    }
    function sheet_to_txt(sheet, opts) {
        if (!opts) opts = {};
        opts.FS = "\t";
        opts.RS = "\n";
        var s = sheet_to_csv(sheet, opts);
        if (typeof cptable == 'undefined' || opts.type == 'string') return s;
        var o = cptable.utils.encode(1200, s, 'str');
        return String.fromCharCode(255) + String.fromCharCode(254) + o;
    }
    function sheet_to_formulae(sheet) {
        var y = "",
            x, val = "";
        if (sheet == null || sheet["!ref"] == null) return [];
        var r = safe_decode_range(sheet['!ref']),
            rr = "",
            cols = [],
            C;
        var cmds = [];
        var dense = Array.isArray(sheet);
        for (C = r.s.c; C <= r.e.c; ++C) cols[C] = encode_col(C);
        for (var R = r.s.r; R <= r.e.r; ++R) {
            rr = encode_row(R);
            for (C = r.s.c; C <= r.e.c; ++C) {
                y = cols[C] + rr;
                x = dense ? (sheet[R] || [])[C] : sheet[y];
                val = "";
                if (x === undefined) continue;
                else if (x.F != null) {
                    y = x.F;
                    if (!x.f) continue;
                    val = x.f;
                    if (y.indexOf(":") == -1) y = y + ":" + y;
                }
                if (x.f != null) val = x.f;
                else if (x.t == 'z') continue;
                else if (x.t == 'n' && x.v != null) val = "" + x.v;
                else if (x.t == 'b') val = x.v ? "TRUE" : "FALSE";
                else if (x.w !== undefined) val = "'" + x.w;
                else if (x.v === undefined) continue;
                else if (x.t == 's') val = "'" + x.v;
                else val = "" + x.v;
                cmds[cmds.length] = y + "=" + val;
            }
        }
        return cmds;
    }
    function sheet_add_json(_ws, js, opts) {
        var o = opts || {};
        var offset = +!o.skipHeader;
        var ws = _ws || ({});
        var _R = 0,
            _C = 0;
        if (ws && o.origin != null) {
            if (typeof o.origin == 'number') _R = o.origin;
            else {
                var _origin = typeof o.origin == "string" ? decode_cell(o.origin) : o.origin;
                _R = _origin.r;
                _C = _origin.c;
            }
        }
        var cell;
        var range = ({
            s: {
                c: 0,
                r: 0
            },
            e: {
                c: _C,
                r: _R + js.length - 1 + offset
            }
        });
        if (ws['!ref']) {
            var _range = safe_decode_range(ws['!ref']);
            range.e.c = Math.max(range.e.c, _range.e.c);
            range.e.r = Math.max(range.e.r, _range.e.r);
            if (_R == -1) {
                _R = range.e.r + 1;
                range.e.r = _R + js.length - 1 + offset;
            }
        }
        var hdr = o.header || [],
            C = 0;
        js.forEach(function (JS, R) {
            keys(JS).forEach(function (k) {
                if ((C = hdr.indexOf(k)) == -1) hdr[C = hdr.length] = k;
                var v = JS[k];
                var t = 'z';
                var z = "";
                if (v && typeof v === 'object' && !(v instanceof Date)) {
                    ws[encode_cell({
                        c: _C + C,
                        r: _R + R + offset
                    })] = v;
                } else {
                    if (typeof v == 'number') t = 'n';
                    else if (typeof v == 'boolean') t = 'b';
                    else if (typeof v == 'string') t = 's';
                    else if (v instanceof Date) {
                        t = 'd';
                        if (!o.cellDates) {
                            t = 'n';
                            v = datenum(v);
                        }
                        z = o.dateNF || SSF._table[14];
                    }
                    ws[encode_cell({
                        c: _C + C,
                        r: _R + R + offset
                    })] = cell = ({
                        t: t,
                        v: v
                    });
                    if (z) cell.z = z;
                }
            });
        });
        range.e.c = Math.max(range.e.c, _C + hdr.length - 1);
        var __R = encode_row(_R);
        if (offset)
            for (C = 0; C < hdr.length; ++C) ws[encode_col(C + _C) + __R] = {
                t: 's',
                v: hdr[C]
            };
        ws['!ref'] = encode_range(range);
        return ws;
    }
    function json_to_sheet(js, opts) {
        return sheet_add_json(null, js, opts);
    }
    var utils = {
        encode_col: encode_col,
        encode_row: encode_row,
        encode_cell: encode_cell,
        encode_range: encode_range,
        decode_col: decode_col,
        decode_row: decode_row,
        split_cell: split_cell,
        decode_cell: decode_cell,
        decode_range: decode_range,
        format_cell: format_cell,
        get_formulae: sheet_to_formulae,
        make_csv: sheet_to_csv,
        make_json: sheet_to_json,
        make_formulae: sheet_to_formulae,
        sheet_add_aoa: sheet_add_aoa,
        sheet_add_json: sheet_add_json,
        aoa_to_sheet: aoa_to_sheet,
        json_to_sheet: json_to_sheet,
        table_to_sheet: parse_dom_table,
        table_to_book: table_to_book,
        sheet_to_csv: sheet_to_csv,
        sheet_to_txt: sheet_to_txt,
        sheet_to_json: sheet_to_json,
        sheet_to_html: HTML_.from_sheet,
        sheet_to_dif: DIF.from_sheet,
        sheet_to_slk: SYLK.from_sheet,
        sheet_to_eth: ETH.from_sheet,
        sheet_to_formulae: sheet_to_formulae,
        sheet_to_row_object_array: sheet_to_json
    };
    (function (utils) {
        utils.consts = utils.consts || {};
        function add_consts(R /*Array*/ ) {
            R.forEach(function (a) {
                utils.consts[a[0]] = a[1];
            });
        }
        function get_default(x, y, z) {
            return x[y] != null ? x[y] : (x[y] = z);
        }
        /* get cell, creating a stub if necessary */
        function ws_get_cell_stub(ws, R, C) {
            /* A1 cell address */
            if (typeof R == "string") return ws[R] || (ws[R] = {
                t: 'z'
            });
            /* cell address object */
            if (typeof R != "number") return ws_get_cell_stub(ws, encode_cell(R));
            /* R and C are 0-based indices */
            return ws_get_cell_stub(ws, encode_cell({
                r: R,
                c: C || 0
            }));
        }
        /* find sheet index for given name / validate index */
        function wb_sheet_idx(wb, sh) {
            if (typeof sh == "number") {
                if (sh >= 0 && wb.SheetNames.length > sh) return sh;
                throw new Error("Cannot find sheet # " + sh);
            } else if (typeof sh == "string") {
                var idx = wb.SheetNames.indexOf(sh);
                if (idx > -1) return idx;
                throw new Error("Cannot find sheet name |" + sh + "|");
            } else throw new Error("Cannot find sheet |" + sh + "|");
        }
        /* simple blank workbook object */
        utils.book_new = function () {
            return {
                SheetNames: [],
                Sheets: {}
            };
        };
        /* add a worksheet to the end of a given workbook */
        utils.book_append_sheet = function (wb, ws, name) {
            if (!name)
                for (var i = 1; i <= 0xFFFF; ++i)
                    if (wb.SheetNames.indexOf(name = "Sheet" + i) == -1) break;
            if (!name) throw new Error("Too many worksheets");
            check_ws_name(name);
            if (wb.SheetNames.indexOf(name) >= 0) throw new Error("Worksheet with name |" + name + "| already exists!");
            wb.SheetNames.push(name);
            wb.Sheets[name] = ws;
        };
        /* set sheet visibility (visible/hidden/very hidden) */
        utils.book_set_sheet_visibility = function (wb, sh, vis) {
            get_default(wb, "Workbook", {});
            get_default(wb.Workbook, "Sheets", []);
            var idx = wb_sheet_idx(wb, sh);
            // $FlowIgnore
            get_default(wb.Workbook.Sheets, idx, {});
            switch (vis) {
                case 0:
                case 1:
                case 2:
                    break;
                default:
                    throw new Error("Bad sheet visibility setting " + vis);
            }
            // $FlowIgnore
            wb.Workbook.Sheets[idx].Hidden = vis;
        };
        add_consts([
            ["SHEET_VISIBLE", 0],
            ["SHEET_HIDDEN", 1],
            ["SHEET_VERY_HIDDEN", 2]
        ]);
        /* set number format */
        utils.cell_set_number_format = function (cell, fmt) {
            cell.z = fmt;
            return cell;
        };
        /* set cell hyperlink */
        utils.cell_set_hyperlink = function (cell, target, tooltip) {
            if (!target) {
                delete cell.l;
            } else {
                cell.l = ({
                    Target: target
                });
                if (tooltip) cell.l.Tooltip = tooltip;
            }
            return cell;
        };
        utils.cell_set_internal_link = function (cell, range, tooltip) {
            return utils.cell_set_hyperlink(cell, "#" + range, tooltip);
        };
        /* add to cell comments */
        utils.cell_add_comment = function (cell, text, author) {
            if (!cell.c) cell.c = [];
            cell.c.push({
                t: text,
                a: author || "SheetJS"
            });
        };
        /* set array formula and flush related cells */
        utils.sheet_set_array_formula = function (ws, range, formula) {
            var rng = typeof range != "string" ? range : safe_decode_range(range);
            var rngstr = typeof range == "string" ? range : encode_range(range);
            for (var R = rng.s.r; R <= rng.e.r; ++R)
                for (var C = rng.s.c; C <= rng.e.c; ++C) {
                    var cell = ws_get_cell_stub(ws, R, C);
                    cell.t = 'n';
                    cell.F = rngstr;
                    delete cell.v;
                    if (R == rng.s.r && C == rng.s.c) cell.f = formula;
                }
            return ws;
        };
        return utils;
    })(utils);
    if (has_buf && typeof require != 'undefined')(function () {
        var Readable = require('stream').Readable;
        var write_csv_stream = function (sheet, opts) {
            var stream = Readable();
            var o = opts == null ? {} : opts;
            if (sheet == null || sheet["!ref"] == null) {
                stream.push(null);
                return stream;
            }
            var r = safe_decode_range(sheet["!ref"]);
            var FS = o.FS !== undefined ? o.FS : ",",
                fs = FS.charCodeAt(0);
            var RS = o.RS !== undefined ? o.RS : "\n",
                rs = RS.charCodeAt(0);
            var endregex = new RegExp((FS == "|" ? "\\|" : FS) + "+$");
            var row = "",
                cols = [];
            o.dense = Array.isArray(sheet);
            var colinfo = o.skipHidden && sheet["!cols"] || [];
            var rowinfo = o.skipHidden && sheet["!rows"] || [];
            for (var C = r.s.c; C <= r.e.c; ++C)
                if (!((colinfo[C] || {}).hidden)) cols[C] = encode_col(C);
            var R = r.s.r;
            var BOM = false;
            stream._read = function () {
                if (!BOM) {
                    BOM = true;
                    return stream.push("\uFEFF");
                }
                if (R > r.e.r) return stream.push(null);
                while (R <= r.e.r) {
                    ++R;
                    if ((rowinfo[R - 1] || {}).hidden) continue;
                    row = make_csv_row(sheet, r, R - 1, cols, fs, rs, FS, o);
                    if (row != null) {
                        if (o.strip) row = row.replace(endregex, "");
                        stream.push(row + RS);
                        break;
                    }
                }
            };
            return stream;
        };
        var write_html_stream = function (ws, opts) {
            var stream = Readable();
            var o = opts || {};
            var header = o.header != null ? o.header : HTML_.BEGIN;
            var footer = o.footer != null ? o.footer : HTML_.END;
            stream.push(header);
            var r = decode_range(ws['!ref']);
            o.dense = Array.isArray(ws);
            stream.push(HTML_._preamble(ws, r, o));
            var R = r.s.r;
            var end = false;
            stream._read = function () {
                if (R > r.e.r) {
                    if (!end) {
                        end = true;
                        stream.push("
" + footer);
                    }
                    return stream.push(null);
                }
                while (R <= r.e.r) {
                    stream.push(HTML_._row(ws, r, R, o));
                    ++R;
                    break;
                }
            };
            return stream;
        };
        var write_json_stream = function (sheet, opts) {
            var stream = Readable({
                objectMode: true
            });
            if (sheet == null || sheet["!ref"] == null) {
                stream.push(null);
                return stream;
            }
            var val = {
                    t: 'n',
                    v: 0
                },
                header = 0,
                offset = 1,
                hdr = [],
                v = 0,
                vv = "";
            var r = {
                s: {
                    r: 0,
                    c: 0
                },
                e: {
                    r: 0,
                    c: 0
                }
            };
            var o = opts || {};
            var range = o.range != null ? o.range : sheet["!ref"];
            if (o.header === 1) header = 1;
            else if (o.header === "A") header = 2;
            else if (Array.isArray(o.header)) header = 3;
            switch (typeof range) {
                case 'string':
                    r = safe_decode_range(range);
                    break;
                case 'number':
                    r = safe_decode_range(sheet["!ref"]);
                    r.s.r = range;
                    break;
                default:
                    r = range;
            }
            if (header > 0) offset = 0;
            var rr = encode_row(r.s.r);
            var cols = [];
            var counter = 0;
            var dense = Array.isArray(sheet);
            var R = r.s.r,
                C = 0,
                CC = 0;
            if (dense && !sheet[R]) sheet[R] = [];
            for (C = r.s.c; C <= r.e.c; ++C) {
                cols[C] = encode_col(C);
                val = dense ? sheet[R][C] : sheet[cols[C] + rr];
                switch (header) {
                    case 1:
                        hdr[C] = C - r.s.c;
                        break;
                    case 2:
                        hdr[C] = cols[C];
                        break;
                    case 3:
                        hdr[C] = o.header[C - r.s.c];
                        break;
                    default:
                        if (val == null) val = {
                            w: "__EMPTY",
                            t: "s"
                        };
                        vv = v = format_cell(val, null, o);
                        counter = 0;
                        for (CC = 0; CC < hdr.length; ++CC)
                            if (hdr[CC] == vv) vv = v + "_" + (++counter);
                        hdr[C] = vv;
                }
            }
            R = r.s.r + offset;
            stream._read = function () {
                if (R > r.e.r) return stream.push(null);
                while (R <= r.e.r) {
                    //if ((rowinfo[R-1]||{}).hidden) continue;
                    var row = make_json_row(sheet, r, R, cols, header, hdr, dense, o);
                    ++R;
                    if ((row.isempty === false) || (header === 1 ? o.blankrows !== false : !!o.blankrows)) {
                        stream.push(row.row);
                        break;
                    }
                }
            };
            return stream;
        };
        XLSX.stream = {
            to_json: write_json_stream,
            to_html: write_html_stream,
            to_csv: write_csv_stream
        };
    })();
    XLSX.parse_xlscfb = parse_xlscfb;
    XLSX.parse_ods = parse_ods;
    XLSX.parse_fods = parse_fods;
    XLSX.write_ods = write_ods;
    XLSX.parse_zip = parse_zip;
    XLSX.read = readSync; //xlsread
    XLSX.readFile = readFileSync; //readFile
    XLSX.readFileSync = readFileSync;
    XLSX.write = writeSync;
    XLSX.writeFile = writeFileSync;
    XLSX.writeFileSync = writeFileSync;
    XLSX.writeFileAsync = writeFileAsync;
    XLSX.utils = utils;
    XLSX.SSF = SSF;
    XLSX.CFB = CFB;
}
/*global define */
if (typeof exports !== 'undefined') make_xlsx_lib(exports);
else if (typeof module !== 'undefined' && module.exports) make_xlsx_lib(module.exports);
else if (typeof define === 'function' && define.amd) define('xlsx', function () {
    if (!XLSX.version) make_xlsx_lib(XLSX);
    return XLSX;
});
else make_xlsx_lib(XLSX);
/*exported XLS, ODS */
var XLS = XLSX,
    ODS = XLSX;