version bump 0.11.2: proper plaintext parsing

- dollar currencies and percentages properly detected as numbers
- parse potential CDATA segments in XLSX (fixes #775 h/t @awb99)
- IE8 issues with regex
- altjs demos

Issues:
- fixes #673 h/t @huhm
- fixes #748 h/t @sangpuion
- fixes #749 h/t @GreggOD
- fixes #772 h/t @sangpuion @jyyan
This commit is contained in:
SheetJS 2017-08-10 19:46:34 -04:00
parent 9ba09bab5e
commit 5b67ac0806
26 changed files with 366 additions and 141 deletions

@ -167,19 +167,25 @@ CDNjs automatically pulls the latest version and makes all versions available at
The `demos` directory includes sample projects for:
**Frameworks**
- [`angular 1.x`](demos/angular/)
- [`angular 2.x / 4.x`](demos/angular2/)
- [`browserify`](demos/browserify/)
- [`canvas-datagrid`](demos/datagrid/)
- [`Adobe ExtendScript`](demos/extendscript/)
- [`meteor`](demos/meteor/)
- [`phantomjs`](demos/phantomjs/)
- [`vue 2`](demos/vue/)
**JS Bundlers and Tooling**
- [`browserify`](demos/browserify/)
- [`requirejs`](demos/requirejs/)
- [`rollup`](demos/rollup/)
- [`systemjs`](demos/systemjs/)
- [`vue 2`](demos/vue/)
- [`webpack`](demos/webpack/)
**JS Platforms and Integrations**
- [`Adobe ExtendScript`](demos/extendscript/)
- [`phantomjs`](demos/phantomjs/)
- [`canvas-datagrid`](demos/datagrid/)
- [`Other JS engines`](demos/altjs/)
### Optional Modules
<details>

@ -1 +1 @@
XLSX.version = '0.11.1';
XLSX.version = '0.11.2';

@ -72,7 +72,7 @@ function parse_isodur(s) {
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 aa date as a local date */
/* parses a date as a local date */
function parseDate(str/*:string|Date*/, fixdate/*:?number*/)/*:Date*/ {
var d = new Date(str);
if(good_pd) {
@ -88,7 +88,9 @@ function parseDate(str/*:string|Date*/, fixdate/*:?number*/)/*:Date*/ {
d.setFullYear(d.getFullYear() + 100); return d;
}
var n = str.match(/\d+/g)||["2017","2","19","0","0","0"];
return new Date(+n[0], +n[1] - 1, +n[2], (+n[3]||0), (+n[4]||0), (+n[5]||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/*:Array<number>*/)/*:string*/ {
@ -117,8 +119,11 @@ function fill(c/*:string*/,l/*:number*/)/*:string*/ { var o = ""; while(o.length
function fuzzynum(s/*:string*/)/*:number*/ {
var v/*:number*/ = Number(s);
if(!isNaN(v)) return v;
var ss = s.replace(/([\d]),([\d])/g,"$1$2").replace(/[$]/g,"");
if(!isNaN(v = Number(ss))) 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/*:string*/)/*:Date*/ {
@ -128,7 +133,14 @@ function fuzzydate(s/*:string*/)/*:Date*/ {
if(y < 0 || y > 8099) return n;
if((m > 0 || d > 1) && y != 101) return o;
if(s.toLowerCase().match(/jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec/)) return o;
if(!s.match(/[a-zA-Z]/)) return o;
if(s.match(/[^-0-9:,\/\\]/)) return o;
return n;
}
var safe_split_regex = "abacaba".split(/(:?b)/i).length == 5;
function split_regex(str/*:string*/, re, def/*:string*/)/*:Array<string>*/ {
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;
}

@ -44,8 +44,10 @@ var unescapexml/*:StringConv*/ = (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/*:string*/)/*:string*/ {
var s = text + '';
return s.replace(encregex, function($$, $1) { return encodings[$$]||String.fromCharCode(parseInt($1,$$.indexOf("x")>-1?16:10))||$$; }).replace(coderegex,function(m,c) {return String.fromCharCode(parseInt(c,16));});
var s = text + '', i = s.indexOf("<![CDATA[");
if(i == -1) return s.replace(encregex, function($$, $1) { return encodings[$$]||String.fromCharCode(parseInt($1,$$.indexOf("x")>-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));
};
})();

@ -8,7 +8,7 @@ var HTML_ = (function() {
if(!mtch) throw new Error("Invalid HTML: could not find <table>");
var mtch2/*:any*/ = str.match(/<\/table/i);
var i/*:number*/ = mtch.index, j/*:number*/ = mtch2 && mtch2.index || str.length;
var rows = str.slice(i, j).split(/(:?<tr[^>]*>)/i);
var rows = split_regex(str.slice(i, j), /(:?<tr[^>]*>)/i, "<tr>");
var R = -1, C = 0, RS = 0, CS = 0;
var range = {s:{r:10000000, c:10000000},e:{r:0,c:0}};
var merges = [], midx = 0;

2
demos/altjs/.gitignore vendored Normal file

@ -0,0 +1,2 @@
jvm-npm.js
sheetjs.*

15
demos/altjs/Makefile Normal file

@ -0,0 +1,15 @@
.PHONY: all
all: duktape nashorn
.PHONY: base
base:
if [ ! -e sheetjs.xlsx ]; then node ../../tests/write.js; fi
.PHONY: duktape
duktape: base ## duktape / skookum demo
sjs skookum.js
.PHONY: nashorn
nashorn: base ## nashorn demo
if [ ! -e jvm-npm.js ]; then curl -O https://rawgit.com/nodyn/jvm-npm/master/src/main/javascript/jvm-npm.js; fi
jjs nashorn.js

34
demos/altjs/README.md Normal file

@ -0,0 +1,34 @@
# Other JS Engines and Deployments
There are many JS engines and deployments outside of web browsers. NodeJS is the
most popular deployment, but there are many others for special use cases. Some
optimize for low overhead and others optimize for ease of embedding within other
applications. Since it was designed for ES3 engines, the library can be used in
those settings! This demo tries to demonstrate a few alternative deployments.
## Nashorn
Nashorn ships with Java 8. It includes a command-line tool `jjs` for running JS
scripts. It is somewhat limited but does offer access to the full Java runtime.
`jjs` does not provide a CommonJS `require` implementation. This demo uses a
[`shim`](https://rawgit.com/nodyn/jvm-npm/master/src/main/javascript/jvm-npm.js)
and manually requires the library.
The Java `nio` API provides the `Files.readAllBytes` method to read a file into
a byte array. To use in `XLSX.read`, the demo copies the bytes into a plain JS
array and calls `XLSX.read` with type `"array"`.
## duktape and skookum
[Duktape](http://duktape.org/) is an embeddable JS engine written in C. The
amalgamation makes integration extremely simple! Duktape understands the source
code and can process binary strings out the box, but does not provide I/O or
other standard library features.
To demonstrate compatibility with duktape, this demo uses the JS runtime from
[Skookum JS](https://github.com/saghul/sjs). Built upon the duktape engine, it
adds a simple I/O interface to enable reading from files.

28
demos/altjs/nashorn.js Normal file

@ -0,0 +1,28 @@
#!/usr/bin/env jjs
/* read file */
var path = java.nio.file.Paths.get('sheetjs.xlsx');
var fileArray = java.nio.file.Files.readAllBytes(path);
/* convert to plain JS array */
function b2a(b) {
var out = new Array(b.length);
for(var i = 0; i < out.length; i++) out[i] = b[i];
return out;
}
var u8a = b2a(fileArray);
/* load module */
load('./jvm-npm.js');
JSZip = require('../../jszip.js');
cptable = require('../../dist/cpexcel.js');
XLSX = require('../../xlsx.js');
/* read file */
var wb = XLSX.read(u8a, {type:"array"});
/* get first worksheet */
var ws = wb.Sheets[wb.SheetNames[0]];
var js = XLSX.utils.sheet_to_json(ws, {header:1});
/* print out every line */
js.forEach(function(l) { java.lang.System.out.println(JSON.stringify(l)); });

12
demos/altjs/skookum.js Normal file

@ -0,0 +1,12 @@
#!/usr/bin/env sjs
var XLSX = require('../../xlsx.js');
var io = require('io');
var file = io.open("sheetjs.xlsx", "rb");
var strs = [], str = "";
while((str = file.read()).length > 0) strs.push(str);
var data = (Buffer.concat(strs.map(function(x) { return new Buffer(x); })));
var wb = XLSX.read(data, {type:"buffer"});
console.log(wb.Sheets[wb.SheetNames[0]]);

@ -105,6 +105,8 @@ function process_wb(wb) {
var data = XLSX.utils.sheet_to_json(ws, {header:1});
if(!cDg) cDg = canvasDatagrid({ parentNode:document.getElementById('htmlout'), data:data });
else cDg.data = data;
var range = XLSX.utils.decode_range(ws['!ref']);
for(var i = range.s.c; i <= range.e.c; ++i) cDg.schema[i - range.s.c].title = XLSX.utils.encode_col(i);
document.getElementById('xport').disabled = false;
if(typeof console !== 'undefined') console.log("output", new Date());
}

24
dist/xlsx.core.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

24
dist/xlsx.full.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

111
dist/xlsx.js vendored

@ -6,7 +6,7 @@
/*global global, exports, module, require:false, process:false, Buffer:false */
var XLSX = {};
(function make_xlsx(XLSX){
XLSX.version = '0.11.1';
XLSX.version = '0.11.2';
var current_codepage = 1200;
/*global cptable:true */
if(typeof module !== "undefined" && typeof require !== 'undefined') {
@ -1026,7 +1026,7 @@ var DO_NOT_EXPORT_CFB = true;
/* [MS-CFB] v20130118 */
var CFB = (function _CFB(){
var exports = {};
exports.version = '0.12.0';
exports.version = '0.12.1';
function parse(file, options) {
var mver = 3; // major version
var ssz = 512; // sector size
@ -1197,7 +1197,7 @@ function build_full_paths(FI, FPD, FP, Paths) {
if(FI[i].type === 0 /* unknown */) continue;
j = dad[i];
if(j === 0) FP[i] = FP[0] + "/" + FP[i];
else while(j !== 0) {
else while(j !== 0 && j !== dad[j]) {
FP[i] = FP[j] + "/" + FP[i];
j = dad[j];
}
@ -1298,7 +1298,6 @@ function read_directory(dir_start, sector_list, sectors, Paths, nmfs, files, Fil
var blob = sector.slice(i, i+128);
prep_blob(blob, 64);
namelen = blob.read_shift(2);
if(namelen === 0) continue;
name = __utf16le(blob,0,namelen-pl);
Paths.push(name);
var o = ({
@ -1319,6 +1318,7 @@ function read_directory(dir_start, sector_list, sectors, Paths, nmfs, files, Fil
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";
@ -1331,7 +1331,7 @@ function read_directory(dir_start, sector_list, sectors, Paths, nmfs, files, Fil
prep_blob(o.content, 0);
} else {
o.storage = 'minifat';
if(minifat_store !== ENDOFCHAIN && o.start !== ENDOFCHAIN) {
if(minifat_store !== ENDOFCHAIN && o.start !== ENDOFCHAIN && sector_list[minifat_store]) {
o.content = (sector_list[minifat_store].data.slice(o.start*MSSZ,o.start*MSSZ+o.size));
prep_blob(o.content, 0);
}
@ -1360,6 +1360,9 @@ function readSync(blob, options) {
return parse(blob, options);
}
function find(cfb, path) {
return cfb.find(path);
}
/** CFB Constants */
var MSSZ = 64; /* Mini Sector Size = 1<<6 */
//var MSCSZ = 4096; /* Mini Stream Cutoff Size */
@ -1385,6 +1388,7 @@ var consts = {
EntryTypes: ['unknown','storage','stream','lockbytes','property','root']
};
exports.find = find;
exports.read = readSync;
exports.parse = parse;
exports.utils = {
@ -1473,7 +1477,7 @@ function parse_isodur(s) {
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 aa date as a local date */
/* parses a date as a local date */
function parseDate(str, fixdate) {
var d = new Date(str);
if(good_pd) {
@ -1488,7 +1492,9 @@ if(fixdate > 0) d.setTime(d.getTime() + d.getTimezoneOffset() * 60 * 1000);
d.setFullYear(d.getFullYear() + 100); return d;
}
var n = str.match(/\d+/g)||["2017","2","19","0","0","0"];
return new Date(+n[0], +n[1] - 1, +n[2], (+n[3]||0), (+n[4]||0), (+n[5]||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) {
@ -1514,6 +1520,16 @@ function dup(o) {
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();
@ -1521,10 +1537,17 @@ function fuzzydate(s) {
if(y < 0 || y > 8099) return n;
if((m > 0 || d > 1) && y != 101) return o;
if(s.toLowerCase().match(/jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec/)) return o;
if(!s.match(/[a-zA-Z]/)) return o;
if(s.match(/[^-0-9:,\/\\]/)) return o;
return n;
}
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);
@ -1600,7 +1623,7 @@ function resolve_path(path, base) {
return result.join('/');
}
var attregexg=/([^"\s?>\/]+)=((?:")([^"]*)(?:")|(?:')([^']*)(?:')|([^'">\s]+))/g;
var tagregex=/<[^>]*>/g;
var tagregex=/<[\/\?]?[a-zA-Z0-9:]+(?:\s+[^"\s?>\/]+=(?:"[^"]*"|'[^']*'|[^'">\s]+))*\s?[\/\?]?>/g;
var nsregex=/<\w*:/, nsregex2 = /<(\/?)\w+:/;
function parsexmltag(tag, skip_root) {
var z = ({});
@ -1645,8 +1668,10 @@ 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 + '';
return s.replace(encregex, function($$, $1) { return encodings[$$]||String.fromCharCode(parseInt($1,$$.indexOf("x")>-1?16:10))||$$; }).replace(coderegex,function(m,c) {return String.fromCharCode(parseInt(c,16));});
var s = text + '', i = s.indexOf("<![CDATA[");
if(i == -1) return s.replace(encregex, function($$, $1) { return encodings[$$]||String.fromCharCode(parseInt($1,$$.indexOf("x")>-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));
};
})();
@ -5409,8 +5434,8 @@ var SYLK = (function() {
if(val.charAt(0) === '"') val = val.substr(1,val.length - 2);
else if(val === 'TRUE') val = true;
else if(val === 'FALSE') val = false;
else if(+val === +val) {
val = +val;
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);
@ -5571,7 +5596,7 @@ var DIF = (function() {
case 0:
if(data === 'TRUE') arr[R][C] = true;
else if(data === 'FALSE') arr[R][C] = false;
else if(+value == +value) arr[R][C] = +value;
else if(!isNaN(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;
@ -5657,7 +5682,7 @@ var PRN = (function() {
else if(data === 'TRUE') arr[R][C] = true;
else if(data === 'FALSE') arr[R][C] = false;
else if(data === ""){/* empty */}
else if(+data == +data) arr[R][C] = +data;
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;
}
@ -5708,7 +5733,7 @@ var PRN = (function() {
else if(s.charCodeAt(0) == 0x3D) { cell.t = 'n'; cell.f = s.substr(1); }
else if(s == "TRUE") { cell.t = 'b'; cell.v = true; }
else if(s == "FALSE") { cell.t = 'b'; cell.v = false; }
else if(!isNaN(v = +s)) { cell.t = 'n'; cell.w = s; cell.v = v; }
else if(!isNaN(v = fuzzynum(s))) { cell.t = 'n'; 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;
@ -14854,11 +14879,11 @@ wb.opts.Date1904 = Workbook.WBProps.date1904 = val; break;
/* TODO: WTF */
function parse_props(cfb) {
/* [MS-OSHARED] 2.3.3.2.2 Document Summary Information Property Set */
var DSI = cfb.find('!DocumentSummaryInformation');
var DSI = CFB.find(cfb, '!DocumentSummaryInformation');
if(DSI) try { cfb.DocSummary = parse_PropertySetStream(DSI, DocSummaryPIDDSI); } catch(e) {/* empty */}
/* [MS-OSHARED] 2.3.3.2.1 Summary Information Property Set*/
var SI = cfb.find('!SummaryInformation');
var SI = CFB.find(cfb, '!SummaryInformation');
if(SI) try { cfb.Summary = parse_PropertySetStream(SI, SummaryPIDSI); } catch(e) {/* empty */}
}
@ -14866,27 +14891,28 @@ function parse_xlscfb(cfb, options) {
if(!options) options = {};
fix_read_opts(options);
reset_cp();
var CompObj, Summary, Workbook;
var CompObj, Summary, WB;
if(cfb.FullPaths) {
CompObj = cfb.find('!CompObj');
Summary = cfb.find('!SummaryInformation');
Workbook = cfb.find('/Workbook');
CompObj = CFB.find(cfb, '!CompObj');
Summary = CFB.find(cfb, '!SummaryInformation');
WB = CFB.find(cfb, '/Workbook');
} else {
prep_blob(cfb, 0);
Workbook = ({content: cfb});
WB = ({content: cfb});
}
if(!Workbook) Workbook = cfb.find('/Book');
if(!WB) WB = CFB.find(cfb, '/Book');
var CompObjP, SummaryP, WorkbookP;
var _data;
if(CompObj) CompObjP = parse_compobj(CompObj);
if(options.bookProps && !options.bookSheets) WorkbookP = ({});
else {
if(Workbook) WorkbookP = parse_workbook(Workbook.content, options);
if(WB && WB.content) WorkbookP = parse_workbook(WB.content, options);
/* Quattro Pro 7-8 */
else if(cfb.find('PerfectOffice_MAIN')) WorkbookP = WK_.to_workbook(cfb.find('PerfectOffice_MAIN').content, options);
else if((_data=CFB.find(cfb, 'PerfectOffice_MAIN')) && _data.content) WorkbookP = WK_.to_workbook(_data.content, options);
/* Quattro Pro 9 */
else if(cfb.find('NativeContent_MAIN')) WorkbookP = WK_.to_workbook(cfb.find('NativeContent_MAIN').content, options);
else if((_data=CFB.find(cfb, 'NativeContent_MAIN')) && _data.content) WorkbookP = WK_.to_workbook(_data.content, options);
else throw new Error("Cannot find Workbook stream");
}
@ -16271,7 +16297,7 @@ var HTML_ = (function() {
if(!mtch) throw new Error("Invalid HTML: could not find <table>");
var mtch2 = str.match(/<\/table/i);
var i = mtch.index, j = mtch2 && mtch2.index || str.length;
var rows = str.slice(i, j).split(/(:?<tr[^>]*>)/i);
var rows = split_regex(str.slice(i, j), /(:?<tr[^>]*>)/i, "<tr>");
var R = -1, C = 0, RS = 0, CS = 0;
var range = {s:{r:10000000, c:10000000},e:{r:0,c:0}};
var merges = [], midx = 0;
@ -16300,12 +16326,14 @@ var HTML_ = (function() {
if(range.e.c < C) range.e.c = C;
if(opts.dense) {
if(!ws[R]) ws[R] = [];
if(Number(m) == Number(m)) ws[R][C] = {t:'n', v:+m};
if(opts.raw) ws[R][C] = {t:'s', v:m};
else if(!isNaN(fuzzynum(m))) ws[R][C] = {t:'n', v:fuzzynum(m)};
else ws[R][C] = {t:'s', v:m};
} else {
var coord = encode_cell({r:R, c:C});
/* TODO: value parsing */
if(Number(m) == Number(m)) ws[coord] = {t:'n', v:+m};
if(opts.raw) ws[coord] = {t:'s', v:m};
else if(!isNaN(fuzzynum(m))) ws[coord] = {t:'n', v:fuzzynum(m)};
else ws[coord] = {t:'s', v:m};
}
C += CS;
@ -16397,7 +16425,8 @@ function parse_dom_table(table, _opts) {
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};
if(v != null && v.length) {
if(!isNaN(Number(v))) o = {t:'n', v:Number(v)};
if(opts.raw) o = {t:'s', v:v};
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)});
@ -17320,20 +17349,20 @@ function parse_zip(zip, opts) {
/* references to [MS-OFFCRYPTO] */
function parse_xlsxcfb(cfb, opts) {
var f = 'Version';
var data = cfb.find(f);
var 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 = 'DataSpaceMap';
data = cfb.find(f);
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);
f = 'StrongEncryptionDataSpace';
data = cfb.find(f);
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")
@ -17341,12 +17370,12 @@ function parse_xlsxcfb(cfb, opts) {
/* 2.3.4.3 */
f = '!Primary';
data = cfb.find(f);
data = CFB.find(cfb, f);
if(!data || !data.content) throw new Error("ECMA-376 Encrypted file missing " + f);
var hdr = parse_Primary(data.content);
f = 'EncryptionInfo';
data = cfb.find(f);
data = CFB.find(cfb, f);
if(!data || !data.content) throw new Error("ECMA-376 Encrypted file missing " + f);
var einfo = parse_EncryptionInfo(data.content);
@ -17491,7 +17520,7 @@ function firstbyte(f,o) {
}
function read_cfb(cfb, opts) {
if(cfb.find("EncryptedPackage")) return parse_xlsxcfb(cfb, opts);
if(CFB.find(cfb, "EncryptedPackage")) return parse_xlsxcfb(cfb, opts);
return parse_xlscfb(cfb, opts);
}
@ -17688,11 +17717,11 @@ function writeFileAsync(filename, wb, opts, cb) {
var _cb = cb; if(!(_cb instanceof Function)) _cb = (opts);
return _fs.writeFile(filename, writeSync(wb, o), _cb);
}
function sheet_to_json(sheet, opts){
function sheet_to_json(sheet, opts) {
if(sheet == null || sheet["!ref"] == null) return [];
var val = {t:'n',v:0}, header = 0, offset = 1, hdr = [], isempty = true, v=0, vv="";
var r = {s:{r:0,c:0},e:{r:0,c:0}};
var o = opts != null ? opts : {};
var o = opts || {};
var raw = o.raw;
var defval = o.defval;
var range = o.range != null ? o.range : sheet["!ref"];
@ -17706,8 +17735,8 @@ function sheet_to_json(sheet, opts){
}
if(header > 0) offset = 0;
var rr = encode_row(r.s.r);
var cols = new Array(r.e.c-r.s.c+1);
var out = new Array(r.e.r-r.s.r-offset+1);
var cols = [];
var out = [];
var outi = 0, counter = 0;
var dense = Array.isArray(sheet);
var R = r.s.r, C = 0, CC = 0;

22
dist/xlsx.min.js vendored

File diff suppressed because one or more lines are too long

2
dist/xlsx.min.map vendored

File diff suppressed because one or more lines are too long

@ -25,19 +25,25 @@ CDNjs automatically pulls the latest version and makes all versions available at
The `demos` directory includes sample projects for:
**Frameworks**
- [`angular 1.x`](demos/angular/)
- [`angular 2.x / 4.x`](demos/angular2/)
- [`browserify`](demos/browserify/)
- [`canvas-datagrid`](demos/datagrid/)
- [`Adobe ExtendScript`](demos/extendscript/)
- [`meteor`](demos/meteor/)
- [`phantomjs`](demos/phantomjs/)
- [`vue 2`](demos/vue/)
**JS Bundlers and Tooling**
- [`browserify`](demos/browserify/)
- [`requirejs`](demos/requirejs/)
- [`rollup`](demos/rollup/)
- [`systemjs`](demos/systemjs/)
- [`vue 2`](demos/vue/)
- [`webpack`](demos/webpack/)
**JS Platforms and Integrations**
- [`Adobe ExtendScript`](demos/extendscript/)
- [`phantomjs`](demos/phantomjs/)
- [`canvas-datagrid`](demos/datagrid/)
- [`Other JS engines`](demos/altjs/)
### Optional Modules
<details>

@ -161,19 +161,25 @@ CDNjs automatically pulls the latest version and makes all versions available at
The `demos` directory includes sample projects for:
**Frameworks**
- [`angular 1.x`](demos/angular/)
- [`angular 2.x / 4.x`](demos/angular2/)
- [`browserify`](demos/browserify/)
- [`canvas-datagrid`](demos/datagrid/)
- [`Adobe ExtendScript`](demos/extendscript/)
- [`meteor`](demos/meteor/)
- [`phantomjs`](demos/phantomjs/)
- [`vue 2`](demos/vue/)
**JS Bundlers and Tooling**
- [`browserify`](demos/browserify/)
- [`requirejs`](demos/requirejs/)
- [`rollup`](demos/rollup/)
- [`systemjs`](demos/systemjs/)
- [`vue 2`](demos/vue/)
- [`webpack`](demos/webpack/)
**JS Platforms and Integrations**
- [`Adobe ExtendScript`](demos/extendscript/)
- [`phantomjs`](demos/phantomjs/)
- [`canvas-datagrid`](demos/datagrid/)
- [`Other JS engines`](demos/altjs/)
### Optional Modules

@ -1,6 +1,6 @@
{
"name": "xlsx",
"version": "0.11.1",
"version": "0.11.2",
"author": "sheetjs",
"description": "Excel (XLSB/XLSX/XLS/XML) ODS and other spreadsheet format (CSV/DIF/DBF/SYLK) parser and writer",
"keywords": [ "excel", "xls", "xlsx", "xlsb", "xlsm", "ods", "csv", "dbf", "dif", "sylk", "office", "spreadsheet" ],

32
test.js

@ -568,14 +568,14 @@ describe('input formats', function() {
X.read(fs.readFileSync(paths.cstxlsb, 'base64'), {type: 'base64'});
});
var k = browser ? 'array' : 'buffer';
(typeof UInt8Array !== 'undefined' ? it : it.skip)('should read ' + k + 's', function() {
X.read(fs.readFileSync(paths.cstxls, 'buffer'), {type: k});
X.read(fs.readFileSync(paths.cstxml, 'buffer'), {type: k});
X.read(fs.readFileSync(paths.cstods, 'buffer'), {type: k});
X.read(fs.readFileSync(paths.cstxlsx, 'buffer'), {type: k});
X.read(fs.readFileSync(paths.cstxlsb, 'buffer'), {type: k});
(typeof Uint8Array !== 'undefined' ? it : it.skip)('should read ' + k + 's', function() {
X.read(fs.readFileSync(paths.cstxls, browser ? 'buffer' : null), {type: k});
X.read(fs.readFileSync(paths.cstxml, browser ? 'buffer' : null), {type: k});
X.read(fs.readFileSync(paths.cstods, browser ? 'buffer' : null), {type: k});
X.read(fs.readFileSync(paths.cstxlsx, browser ? 'buffer' : null), {type: k});
X.read(fs.readFileSync(paths.cstxlsb, browser ? 'buffer' : null), {type: k});
});
(typeof UInt8Array !== 'undefined' ? it : it.skip)('should read array', function() {
(typeof Uint8Array !== 'undefined' ? it : it.skip)('should read array', function() {
X.read(fs.readFileSync(paths.cstxls, 'binary').split("").map(function(x) { return x.charCodeAt(0); }), {type:'array'});
X.read(fs.readFileSync(paths.cstxml, 'binary').split("").map(function(x) { return x.charCodeAt(0); }), {type:'array'});
X.read(fs.readFileSync(paths.cstxlsx, 'binary').split("").map(function(x) { return x.charCodeAt(0); }), {type:'array'});
@ -1847,26 +1847,34 @@ describe('csv', function() {
describe('HTML', function() {
describe('input', function(){
var b = "<table><tr><td>1</td><td>4,001</td></tr><tr><td>$41.08</td></tr></table>";
var b = "<table><tr><td>-0.08</td><td>4,001</td><td>\u00e3\u0081\u0082 1</td></tr><tr><td>$41.08</td><td>11%</td></tr></table>";
it('should generate numbers by default', function() {
var sheet = X.read(b, {type:"binary"}).Sheets.Sheet1
var sheet = X.read(b, {type:"binary"}).Sheets.Sheet1;
var cell = get_cell(sheet, "A1");
assert.equal(cell.v, 1);
assert.equal(cell.v, -0.08);
assert.equal(cell.t, 'n');
cell = get_cell(sheet, "B1");
assert.equal(cell.v, 4001);
cell = get_cell(sheet, "C1");
assert.equal(cell.v, "あ 1");
cell = get_cell(sheet, "A2");
assert.equal(cell.v, 41.08);
cell = get_cell(sheet, "B2");
assert.equal(cell.v, .11);
});
it('should generate strings if raw option is passed', function() {
var sheet = X.read(b, {type:"binary", raw:true}).Sheets.Sheet1
var sheet = X.read(b, {type:"binary", raw:true}).Sheets.Sheet1;
var cell = get_cell(sheet, "A1");
assert.equal(cell.v, "1");
assert.equal(cell.v, "-0.08");
assert.equal(cell.t, 's');
cell = get_cell(sheet, "B1");
assert.equal(cell.v, "4,001");
cell = get_cell(sheet, "C1");
assert.equal(cell.v, "あ 1");
cell = get_cell(sheet, "A2");
assert.equal(cell.v, "$41.08");
cell = get_cell(sheet, "B2");
assert.equal(cell.v, "11%");
});
});
});

@ -568,14 +568,14 @@ describe('input formats', function() {
X.read(fs.readFileSync(paths.cstxlsb, 'base64'), {type: 'base64'});
});
var k = browser ? 'array' : 'buffer';
(typeof UInt8Array !== 'undefined' ? it : it.skip)('should read ' + k + 's', function() {
X.read(fs.readFileSync(paths.cstxls, 'buffer'), {type: k});
X.read(fs.readFileSync(paths.cstxml, 'buffer'), {type: k});
X.read(fs.readFileSync(paths.cstods, 'buffer'), {type: k});
X.read(fs.readFileSync(paths.cstxlsx, 'buffer'), {type: k});
X.read(fs.readFileSync(paths.cstxlsb, 'buffer'), {type: k});
(typeof Uint8Array !== 'undefined' ? it : it.skip)('should read ' + k + 's', function() {
X.read(fs.readFileSync(paths.cstxls, browser ? 'buffer' : null), {type: k});
X.read(fs.readFileSync(paths.cstxml, browser ? 'buffer' : null), {type: k});
X.read(fs.readFileSync(paths.cstods, browser ? 'buffer' : null), {type: k});
X.read(fs.readFileSync(paths.cstxlsx, browser ? 'buffer' : null), {type: k});
X.read(fs.readFileSync(paths.cstxlsb, browser ? 'buffer' : null), {type: k});
});
(typeof UInt8Array !== 'undefined' ? it : it.skip)('should read array', function() {
(typeof Uint8Array !== 'undefined' ? it : it.skip)('should read array', function() {
X.read(fs.readFileSync(paths.cstxls, 'binary').split("").map(function(x) { return x.charCodeAt(0); }), {type:'array'});
X.read(fs.readFileSync(paths.cstxml, 'binary').split("").map(function(x) { return x.charCodeAt(0); }), {type:'array'});
X.read(fs.readFileSync(paths.cstxlsx, 'binary').split("").map(function(x) { return x.charCodeAt(0); }), {type:'array'});
@ -1845,6 +1845,40 @@ describe('csv', function() {
});
});
describe('HTML', function() {
describe('input', function(){
var b = "<table><tr><td>-0.08</td><td>4,001</td><td>\u00e3\u0081\u0082 1</td></tr><tr><td>$41.08</td><td>11%</td></tr></table>";
it('should generate numbers by default', function() {
var sheet = X.read(b, {type:"binary"}).Sheets.Sheet1;
var cell = get_cell(sheet, "A1");
assert.equal(cell.v, -0.08);
assert.equal(cell.t, 'n');
cell = get_cell(sheet, "B1");
assert.equal(cell.v, 4001);
cell = get_cell(sheet, "C1");
assert.equal(cell.v, "あ 1");
cell = get_cell(sheet, "A2");
assert.equal(cell.v, 41.08);
cell = get_cell(sheet, "B2");
assert.equal(cell.v, .11);
});
it('should generate strings if raw option is passed', function() {
var sheet = X.read(b, {type:"binary", raw:true}).Sheets.Sheet1;
var cell = get_cell(sheet, "A1");
assert.equal(cell.v, "-0.08");
assert.equal(cell.t, 's');
cell = get_cell(sheet, "B1");
assert.equal(cell.v, "4,001");
cell = get_cell(sheet, "C1");
assert.equal(cell.v, "あ 1");
cell = get_cell(sheet, "A2");
assert.equal(cell.v, "$41.08");
cell = get_cell(sheet, "B2");
assert.equal(cell.v, "11%");
});
});
});
describe('js -> file -> js', function() {
var data, ws, wb, BIN="binary";
var bef = (function() {

@ -1,4 +1,5 @@
Makefile
*xlsx*.ts
doc.ts
write.ts
tslint.json

@ -6,7 +6,7 @@
/*global global, exports, module, require:false, process:false, Buffer:false */
var XLSX = {};
(function make_xlsx(XLSX){
XLSX.version = '0.11.1';
XLSX.version = '0.11.2';
var current_codepage = 1200;
/*:: declare var cptable:any; */
/*global cptable:true */
@ -1539,7 +1539,7 @@ function parse_isodur(s) {
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 aa date as a local date */
/* parses a date as a local date */
function parseDate(str/*:string|Date*/, fixdate/*:?number*/)/*:Date*/ {
var d = new Date(str);
if(good_pd) {
@ -1555,7 +1555,9 @@ function parseDate(str/*:string|Date*/, fixdate/*:?number*/)/*:Date*/ {
d.setFullYear(d.getFullYear() + 100); return d;
}
var n = str.match(/\d+/g)||["2017","2","19","0","0","0"];
return new Date(+n[0], +n[1] - 1, +n[2], (+n[3]||0), (+n[4]||0), (+n[5]||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/*:Array<number>*/)/*:string*/ {
@ -1584,8 +1586,11 @@ function fill(c/*:string*/,l/*:number*/)/*:string*/ { var o = ""; while(o.length
function fuzzynum(s/*:string*/)/*:number*/ {
var v/*:number*/ = Number(s);
if(!isNaN(v)) return v;
var ss = s.replace(/([\d]),([\d])/g,"$1$2").replace(/[$]/g,"");
if(!isNaN(v = Number(ss))) 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/*:string*/)/*:Date*/ {
@ -1595,10 +1600,17 @@ function fuzzydate(s/*:string*/)/*:Date*/ {
if(y < 0 || y > 8099) return n;
if((m > 0 || d > 1) && y != 101) return o;
if(s.toLowerCase().match(/jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec/)) return o;
if(!s.match(/[a-zA-Z]/)) return o;
if(s.match(/[^-0-9:,\/\\]/)) return o;
return n;
}
var safe_split_regex = "abacaba".split(/(:?b)/i).length == 5;
function split_regex(str/*:string*/, re, def/*:string*/)/*:Array<string>*/ {
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)/*:?string*/ {
if(!data) return null;
if(data.data) return debom(data.data);
@ -1720,8 +1732,10 @@ var unescapexml/*:StringConv*/ = (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/*:string*/)/*:string*/ {
var s = text + '';
return s.replace(encregex, function($$, $1) { return encodings[$$]||String.fromCharCode(parseInt($1,$$.indexOf("x")>-1?16:10))||$$; }).replace(coderegex,function(m,c) {return String.fromCharCode(parseInt(c,16));});
var s = text + '', i = s.indexOf("<![CDATA[");
if(i == -1) return s.replace(encregex, function($$, $1) { return encodings[$$]||String.fromCharCode(parseInt($1,$$.indexOf("x")>-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));
};
})();
@ -16369,7 +16383,7 @@ var HTML_ = (function() {
if(!mtch) throw new Error("Invalid HTML: could not find <table>");
var mtch2/*:any*/ = str.match(/<\/table/i);
var i/*:number*/ = mtch.index, j/*:number*/ = mtch2 && mtch2.index || str.length;
var rows = str.slice(i, j).split(/(:?<tr[^>]*>)/i);
var rows = split_regex(str.slice(i, j), /(:?<tr[^>]*>)/i, "<tr>");
var R = -1, C = 0, RS = 0, CS = 0;
var range = {s:{r:10000000, c:10000000},e:{r:0,c:0}};
var merges = [], midx = 0;

32
xlsx.js

@ -6,7 +6,7 @@
/*global global, exports, module, require:false, process:false, Buffer:false */
var XLSX = {};
(function make_xlsx(XLSX){
XLSX.version = '0.11.1';
XLSX.version = '0.11.2';
var current_codepage = 1200;
/*global cptable:true */
if(typeof module !== "undefined" && typeof require !== 'undefined') {
@ -1477,7 +1477,7 @@ function parse_isodur(s) {
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 aa date as a local date */
/* parses a date as a local date */
function parseDate(str, fixdate) {
var d = new Date(str);
if(good_pd) {
@ -1492,7 +1492,9 @@ if(fixdate > 0) d.setTime(d.getTime() + d.getTimezoneOffset() * 60 * 1000);
d.setFullYear(d.getFullYear() + 100); return d;
}
var n = str.match(/\d+/g)||["2017","2","19","0","0","0"];
return new Date(+n[0], +n[1] - 1, +n[2], (+n[3]||0), (+n[4]||0), (+n[5]||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) {
@ -1521,8 +1523,11 @@ function fill(c,l) { var o = ""; while(o.length < l) o+=c; return o; }
function fuzzynum(s) {
var v = Number(s);
if(!isNaN(v)) return v;
var ss = s.replace(/([\d]),([\d])/g,"$1$2").replace(/[$]/g,"");
if(!isNaN(v = Number(ss))) 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) {
@ -1532,10 +1537,17 @@ function fuzzydate(s) {
if(y < 0 || y > 8099) return n;
if((m > 0 || d > 1) && y != 101) return o;
if(s.toLowerCase().match(/jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec/)) return o;
if(!s.match(/[a-zA-Z]/)) return o;
if(s.match(/[^-0-9:,\/\\]/)) return o;
return n;
}
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);
@ -1656,8 +1668,10 @@ 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 + '';
return s.replace(encregex, function($$, $1) { return encodings[$$]||String.fromCharCode(parseInt($1,$$.indexOf("x")>-1?16:10))||$$; }).replace(coderegex,function(m,c) {return String.fromCharCode(parseInt(c,16));});
var s = text + '', i = s.indexOf("<![CDATA[");
if(i == -1) return s.replace(encregex, function($$, $1) { return encodings[$$]||String.fromCharCode(parseInt($1,$$.indexOf("x")>-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));
};
})();
@ -16283,7 +16297,7 @@ var HTML_ = (function() {
if(!mtch) throw new Error("Invalid HTML: could not find <table>");
var mtch2 = str.match(/<\/table/i);
var i = mtch.index, j = mtch2 && mtch2.index || str.length;
var rows = str.slice(i, j).split(/(:?<tr[^>]*>)/i);
var rows = split_regex(str.slice(i, j), /(:?<tr[^>]*>)/i, "<tr>");
var R = -1, C = 0, RS = 0, CS = 0;
var range = {s:{r:10000000, c:10000000},e:{r:0,c:0}};
var merges = [], midx = 0;