updating to 0.18.6

This commit is contained in:
SheetJS 2022-04-15 19:17:23 -04:00
parent 99b99379e9
commit cfd92222d2
12 changed files with 6735 additions and 5136 deletions

@ -6,6 +6,8 @@ update:
git show master:xlsx.js > xlsx.js
git show master:shim.js > shim.js
git show master:shim.js > tests/shim.js
git show master:tests/base64.js > tests/base64.js
git show master:dist/xlsx.zahl.js > tests/xlsx.zahl.js
git show master:tests/core.js > tests/core.js
git show master:tests/fixtures.js > tests/fixtures.js
git show master:tests/fs_.js > tests/fs_.js
@ -13,5 +15,7 @@ update:
git show master:tests/mocha.js > tests/mocha.js
git show master:tests/write.js > tests/write.js
git show master:tests/write.html > tests/write.html
git show master:tests/index.html > tests/index.html
git show master:demos/datagrid/index.html > datagrid.html
git show master:demos/xspreadsheet/index.html > x-spreadsheet.html
git show master:demos/xspreadsheet/xlsxspread.js > xlsxspread.js

@ -34,7 +34,6 @@ a { text-decoration: none }
<input type="file" name="xlfile" id="xlf" /> ... or click here to select a file
<textarea id="b64data">... or paste a base64-encoding here</textarea>
<b>Advanced Demo Options:</b>
Use readAsBinaryString: (when available) <input type="checkbox" name="userabs" checked>
</pre>
<p><input type="submit" value="Export to XLSX!" id="xport" onclick="export_xlsx();" disabled="true"></p>
<div id="htmlout"></div>
@ -48,7 +47,6 @@ Use readAsBinaryString: (when available) <input type="checkbox" name="userabs" c
/* eslint no-use-before-define:0 */
/*global Uint8Array, Uint16Array, ArrayBuffer */
/*global XLSX */
var X = XLSX;
var cDg;
@ -80,22 +78,16 @@ var process_wb = (function() {
})();
var do_file = (function() {
var rABS = typeof FileReader !== "undefined" && (FileReader.prototype||{}).readAsBinaryString;
var domrabs = document.getElementsByName("userabs")[0];
if(!rABS) domrabs.disabled = !(domrabs.checked = false);
return function do_file(files) {
rABS = domrabs.checked;
var f = files[0];
var reader = new FileReader();
reader.onload = function(e) {
if(typeof console !== 'undefined') console.log("onload", new Date(), rABS);
if(typeof console !== 'undefined') console.log("onload", new Date());
var data = e.target.result;
if(!rABS) data = new Uint8Array(data);
process_wb(X.read(data, {type: rABS ? 'binary' : 'array'}));
data = new Uint8Array(data);
process_wb(XLSX.read(data, {type: 'array'}));
};
if(rABS) reader.readAsBinaryString(f);
else reader.readAsArrayBuffer(f);
reader.readAsArrayBuffer(f);
};
})();

67
tests/base64.js Normal file

@ -0,0 +1,67 @@
// https://github.com/davidchambers/Base64.js
// (C) 2015 David Chambers and contributors
// Base64.js may be freely distributed under the Apache 2.0 License.
;(function () {
var object =
typeof exports != 'undefined' ? exports :
typeof self != 'undefined' ? self : // #8: web workers
$.global; // #31: ExtendScript
var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
function InvalidCharacterError(message) {
this.message = message;
}
InvalidCharacterError.prototype = new Error;
InvalidCharacterError.prototype.name = 'InvalidCharacterError';
// encoder
// [https://gist.github.com/999166] by [https://github.com/nignag]
object.btoa || (
object.btoa = function (input) {
var str = String(input);
for (
// initialize result and counter
var block, charCode, idx = 0, map = chars, output = '';
// if the next str index does not exist:
// change the mapping table to "="
// check if d has no fractional digits
str.charAt(idx | 0) || (map = '=', idx % 1);
// "8 - idx % 1 * 8" generates the sequence 2, 4, 6, 8
output += map.charAt(63 & block >> 8 - idx % 1 * 8)
) {
charCode = str.charCodeAt(idx += 3/4);
if (charCode > 0xFF) {
throw new InvalidCharacterError("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.");
}
block = block << 8 | charCode;
}
return output;
});
// decoder
// [https://gist.github.com/1020396] by [https://github.com/atk]
object.atob || (
object.atob = function (input) {
var str = String(input).replace(/[=]+$/, ''); // #31: ExtendScript bad parse of /=
if (str.length % 4 == 1) {
throw new InvalidCharacterError("'atob' failed: The string to be decoded is not correctly encoded.");
}
for (
// initialize result and counters
var bc = 0, bs, buffer, idx = 0, output = '';
// get next character
buffer = str.charAt(idx++);
// character found in table? initialize bit storage and add its ascii value;
~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer,
// and if not first of each 4 characters,
// convert the first 8 bits to one ascii character
bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0
) {
// try to find character in table (0-63, not found => -1)
buffer = chars.indexOf(buffer);
}
return output;
});
}());

@ -13,7 +13,7 @@ declare var before:(test:EmptyFunc)=>void;
declare var afterEach:(test:EmptyFunc)=>void;
declare var cptable: any;
*/
var X;
var X, XLSX_ZAHL = XLSX_ZAHL_PAYLOAD;
var modp = './';
var fs = require('fs'), assert = require('assert');
describe('source',function(){it('should load',function(){X=require(modp);});});
@ -144,6 +144,8 @@ var paths = {
dtxlsx: dir + 'xlsx-stream-d-date-cell.xlsx',
dtxlsb: dir + 'xlsx-stream-d-date-cell.xlsb',
dtfxlsx: dir + 'DataTypesFormats.xlsx',
fstxls: dir + 'formula_stress_test.xls',
fstxml: dir + 'formula_stress_test.xls.xml',
fstxlsx: dir + 'formula_stress_test.xlsx',
@ -165,6 +167,9 @@ var paths = {
lonxls: dir + 'LONumbers.xls',
lonxlsx: dir + 'LONumbers.xlsx',
m19xlsx: dir + 'metadata_2019.xlsx',
m19xlsb: dir + 'metadata_2019.xlsb',
mcxls: dir + 'merge_cells.xls',
mcxml: dir + 'merge_cells.xls.xml',
mcxlsx: dir + 'merge_cells.xlsx',
@ -248,7 +253,7 @@ function parsetest(x/*:string*/, wb/*:Workbook*/, full/*:boolean*/, ext/*:?strin
describe(x + ext + ' should generate CSV', function() {
wb.SheetNames.forEach(function(ws, i) {
it('#' + i + ' (' + ws + ')', function() {
X.utils.make_csv(wb.Sheets[ws]);
X.utils.sheet_to_csv(wb.Sheets[ws]);
});
});
});
@ -262,7 +267,7 @@ function parsetest(x/*:string*/, wb/*:Workbook*/, full/*:boolean*/, ext/*:?strin
describe(x + ext + ' should generate formulae', function() {
wb.SheetNames.forEach(function(ws, i) {
it('#' + i + ' (' + ws + ')', function() {
X.utils.get_formulae(wb.Sheets[ws]);
X.utils.sheet_to_formulae(wb.Sheets[ws]);
});
});
});
@ -289,7 +294,7 @@ function parsetest(x/*:string*/, wb/*:Workbook*/, full/*:boolean*/, ext/*:?strin
var name = getfile(dir, x, i, ".csv");
if(fs.existsSync(name)) it('#' + i + ' (' + ws + ')', function() {
var file = fs.readFileSync(name, 'utf-8');
var csv = X.utils.make_csv(wb.Sheets[ws]);
var csv = X.utils.sheet_to_csv(wb.Sheets[ws]);
assert.equal(fixcsv(csv), fixcsv(file), "CSV badness");
});
});
@ -299,14 +304,14 @@ function parsetest(x/*:string*/, wb/*:Workbook*/, full/*:boolean*/, ext/*:?strin
var rawjson = getfile(dir, x, i, ".rawjson");
if(fs.existsSync(rawjson)) it('#' + i + ' (' + ws + ')', function() {
var file = fs.readFileSync(rawjson, 'utf-8');
var json = X.utils.make_json(wb.Sheets[ws],{raw:true});
var json = X.utils.sheet_to_json(wb.Sheets[ws],{raw:true});
assert.equal(JSON.stringify(json), fixjson(file), "JSON badness");
});
var jsonf = getfile(dir, x, i, ".json");
if(fs.existsSync(jsonf)) it('#' + i + ' (' + ws + ')', function() {
var file = fs.readFileSync(jsonf, 'utf-8');
var json = X.utils.make_json(wb.Sheets[ws], {raw:false});
var json = X.utils.sheet_to_json(wb.Sheets[ws], {raw:false});
assert.equal(JSON.stringify(json), fixjson(file), "JSON badness");
});
});
@ -524,9 +529,11 @@ describe('parse options', function() {
} } };
var str = X.write(wb, {bookType: "xlsx", type: "binary"});
var wb2 = X.read(str, {type: "binary"});
/*jshint -W069 */
assert.equal(wb2.Sheets.Sheet1["A1"].f, "IFS(2>3,1,3>2,2)");
var wb3 = X.read(str, {type: "binary", xlfn: true});
assert.equal(wb3.Sheets.Sheet1["A1"].f, "_xlfn.IFS(2>3,1,3>2,2)");
/*jshint +W069 */
});
});
describe('sheet', function() {
@ -569,21 +576,22 @@ describe('parse options', function() {
checkcells(X.read(fs.readFileSync(p), {type:TYPE, sheetRows:10}), false, false, false, true);
}); });
it('sheetRows n=1', function() { ofmt.forEach(function(fmt) {
[TYPE, "base64", "binary", "array"].forEach(function(ot) {
var data = [[1,2],[3,4],[5,6]];
var ws = X.utils.aoa_to_sheet(data);
assert(ws['!ref'] === "A1:B3");
var wb = X.utils.book_new();
X.utils.book_append_sheet(wb, ws, "Sheet1");
var bs = X.write(wb, { bookType: fmt, type: "binary" });
var bs = X.write(wb, { bookType: fmt, type: ot, WTF: 1 });
var wb0 = X.read(bs, { type: "binary" });
var wb0 = X.read(bs, { type: ot, WTF: 1 });
var ws0 = wb0.Sheets.Sheet1;
assert.equal(ws0['!ref'], "A1:B3");
assert.equal(get_cell(ws0, "A1").v, 1);
assert.equal(get_cell(ws0, "B2").v, 4);
assert.equal(get_cell(ws0, "A3").v, 5);
var wb1 = X.read(bs, { type: "binary", sheetRows: 1 });
var wb1 = X.read(bs, { type: ot, sheetRows: 1 });
var ws1 = wb1.Sheets.Sheet1;
assert.equal(ws1['!ref'], "A1:B1");
assert.equal(get_cell(ws1, "A1").v, 1);
@ -591,7 +599,7 @@ describe('parse options', function() {
assert(!get_cell(ws1, "A3"));
if(ws1['!fullref']) assert.equal(ws1['!fullref'], "A1:B3");
var wb2 = X.read(bs, { type: "binary", sheetRows: 2 });
var wb2 = X.read(bs, { type: ot, sheetRows: 2 });
var ws2 = wb2.Sheets.Sheet1;
assert.equal(ws2['!ref'], "A1:B2");
assert.equal(get_cell(ws2, "A1").v, 1);
@ -599,14 +607,14 @@ describe('parse options', function() {
assert(!get_cell(ws2, "A3"));
if(ws2['!fullref']) assert.equal(ws2['!fullref'], "A1:B3");
var wb3 = X.read(bs, { type: "binary", sheetRows: 3 });
var wb3 = X.read(bs, { type: ot, sheetRows: 3 });
var ws3 = wb3.Sheets.Sheet1;
assert.equal(ws3['!ref'], "A1:B3");
assert.equal(get_cell(ws3, "A1").v, 1);
assert.equal(get_cell(ws3, "B2").v, 4);
assert.equal(get_cell(ws3, "A3").v, 5);
if(ws3['!fullref']) assert.equal(ws3['!fullref'], "A1:B3");
}); });
}); }); });
});
describe('book', function() {
it('bookSheets should not generate sheets', function() {
@ -1224,6 +1232,23 @@ describe('parse features', function() {
});
}); }); });
describe('workbook codename unicode', function() {
var ws, wb;
var bef = (function() {
wb = X.utils.book_new();
ws = X.utils.aoa_to_sheet([[1]]);
X.utils.book_append_sheet(wb, ws, "Sheet1");
wb.Workbook = { WBProps: { CodeName: "本工作簿" } };
});
if(typeof before != 'undefined') before(bef);
else it('before', bef);
['xlsx', 'xlsb'].forEach(function(m) { it(m, function() {
var bstr = X.write(wb, {type: "binary", bookType: m});
var nwb = X.read(bstr, {type: "binary"});
assert.equal(nwb.Workbook.WBProps.CodeName, wb.Workbook.WBProps.CodeName);
}); });
});
describe('auto filter', function() {[
['xlsx', paths.afxlsx],
['xlsb', paths.afxlsb],
@ -1255,7 +1280,8 @@ describe('parse features', function() {
assert.equal(get_cell(wb2.Sheets.Sheet1, "A2").h, "&amp;");
assert.equal(get_cell(wb2.Sheets.Sheet1, "B2").h, "&lt;");
assert.equal(get_cell(wb2.Sheets.Sheet1, "C2").h, "&gt;");
assert.equal(get_cell(wb2.Sheets.Sheet1, "D2").h, "<br/>");
var h = get_cell(wb2.Sheets.Sheet1, "D2").h;
assert(h == "&#x000a;" || h == "<br/>");
}); });
});
@ -1357,6 +1383,20 @@ describe('parse features', function() {
});
});
});
describe('data types formats', function() {[
['xlsx', paths.dtfxlsx],
].forEach(function(m) { it(m[0], function() {
var wb = X.read(fs.readFileSync(m[1]), {type: TYPE, cellDates: true});
var ws = wb.Sheets[wb.SheetNames[0]];
var data = X.utils.sheet_to_json(ws, { header: 1, raw: true, rawNumbers: false });
assert(data[0][1] instanceof Date);
assert(data[1][1] instanceof Date);
assert.equal(data[2][1], '$123.00');
assert.equal(data[3][1], '98.76%');
assert.equal(data[4][1], '456.00');
assert.equal(data[5][1], '7,890');
}); }); });
});
describe('write features', function() {
@ -1432,8 +1472,8 @@ describe('write features', function() {
var wb = X.utils.book_new();
X.utils.book_append_sheet(wb, X.utils.aoa_to_sheet([[1,2],[3,4]]), "Sheet1");
X.utils.book_append_sheet(wb, X.utils.aoa_to_sheet([[5,6],[7,8]]), "Sheet2");
assert.equal(X.write(wb, {type:"string", bookType:"csv", sheet:"Sheet1"}), "1,2\n3,4\n");
assert.equal(X.write(wb, {type:"string", bookType:"csv", sheet:"Sheet2"}), "5,6\n7,8\n");
assert.equal(X.write(wb, {type:"string", bookType:"csv", sheet:"Sheet1"}), "1,2\n3,4");
assert.equal(X.write(wb, {type:"string", bookType:"csv", sheet:"Sheet2"}), "5,6\n7,8");
assert.throws(function() { X.write(wb, {type:"string", bookType:"csv", sheet:"Sheet3"}); });
});
});
@ -1504,9 +1544,9 @@ describe('roundtrip features', function() {
}); });
describe('should preserve merge cells', function() {
["xlsx", "xlsb", "xlml", "ods", "biff8"].forEach(function(f) { it(f, function() {
["xlsx", "xlsb", "xlml", "ods", "biff8", "numbers"].forEach(function(f) { it(f, function() {
var wb1 = X.read(fs.readFileSync(paths.mcxlsx), {type:TYPE});
var wb2 = X.read(X.write(wb1,{bookType:f,type:'binary'}),{type:'binary'});
var wb2 = X.read(X.write(wb1,{bookType:f,type:'binary',numbers:XLSX_ZAHL}),{type:'binary'});
var m1 = wb1.Sheets.Merge['!merges'].map(X.utils.encode_range);
var m2 = wb2.Sheets.Merge['!merges'].map(X.utils.encode_range);
assert.equal(m1.length, m2.length);
@ -1545,8 +1585,8 @@ describe('roundtrip features', function() {
['xlsx', paths.fstxlsx],
['ods', paths.fstods]
].forEach(function(w) { it(w[0], function() {
var wb1 = X.read(fs.readFileSync(w[1]), {type:TYPE, cellFormula:true});
var wb2 = X.read(X.write(wb1, {bookType:w[0], type:TYPE}), {cellFormula:true, type:TYPE});
var wb1 = X.read(fs.readFileSync(w[1]), {type:TYPE, cellFormula:true, WTF:1});
var wb2 = X.read(X.write(wb1, {bookType:w[0], type:TYPE}), {cellFormula:true, type:TYPE, WTF:1});
wb1.SheetNames.forEach(function(n) {
assert.equal(
X.utils.sheet_to_formulae(wb1.Sheets[n]).sort().join("\n"),
@ -1555,6 +1595,22 @@ describe('roundtrip features', function() {
});
}); }); });
describe('should preserve dynamic array formulae', function() { [
['xlsx', paths.m19xlsx]
].forEach(function(w) { it(w[0], function() {
var wb1 = X.read(fs.readFileSync(w[1]), {xlfn: true, type:TYPE, cellFormula:true, WTF:1});
var wb2 = X.read(X.write(wb1, {bookType:w[0], type:TYPE}), {cellFormula:true, xlfn: true, type:TYPE, WTF:1});
assert.equal(!!get_cell(wb2.Sheets.Sheet1, "B3").D, true);
assert.equal(!!get_cell(wb2.Sheets.Sheet1, "B13").D, true);
assert.equal(!!get_cell(wb2.Sheets.Sheet1, "C13").D, true);
get_cell(wb2.Sheets.Sheet1, "B3").D = false;
var wb3 = X.read(X.write(wb2, {bookType:w[0], type:TYPE}), {cellFormula:true, xlfn: true, type:TYPE, WTF:1});
assert.equal(!!get_cell(wb3.Sheets.Sheet1, "B3").D, false);
assert.equal(!!get_cell(wb3.Sheets.Sheet1, "B13").D, true);
assert.equal(!!get_cell(wb3.Sheets.Sheet1, "C13").D, true);
}); }); });
describe('should preserve hyperlink', function() { [
['xlml', paths.hlxml, true],
['xls', paths.hlxls, true],
@ -1611,7 +1667,7 @@ describe('roundtrip features', function() {
});
describe('should preserve column properties', function() { [
'xlml', /*'biff2', 'biff8', */ 'xlsx', 'xlsb', 'slk'
/*'xlml',*/ /*'biff2', 'biff8', */ 'xlsx', 'xlsb', 'slk'
].forEach(function(w) { it(w, function() {
var ws1 = X.utils.aoa_to_sheet([["hpx12", "hpt24", "hpx48", "hidden"]]);
ws1['!cols'] = [{wch:9},{wpx:100},{width:80},{hidden:true}];
@ -1685,6 +1741,8 @@ var password_files = [
];
describe('invalid files', function() {
describe('parse', function() { [
['KEY files', 'numbers/Untitled.key'],
['PAGES files', 'numbers/Untitled.pages'],
['password', 'apachepoi_password.xls'],
['passwords', 'apachepoi_xor-encryption-abc.xls'],
['DOC files', 'word_doc.doc']
@ -1739,7 +1797,7 @@ describe('json output', function() {
if(typeof before != 'undefined') before(bef);
else it('before', bef);
it('should use first-row headers and full sheet by default', function() {
var json = X.utils.sheet_to_json(ws, {raw: null});
var json = X.utils.sheet_to_json(ws, {raw: false});
assert.equal(json.length, data.length - 1);
assert.equal(json[0][1], "TRUE");
assert.equal(json[1][2], "bar");
@ -1748,7 +1806,7 @@ describe('json output', function() {
assert.throws(function() { seeker(json, [1,2,3], "baz"); });
});
it('should create array of arrays if header == 1', function() {
var json = X.utils.sheet_to_json(ws, {header:1, raw:""});
var json = X.utils.sheet_to_json(ws, {header:1, raw:false});
assert.equal(json.length, data.length);
assert.equal(json[1][0], "TRUE");
assert.equal(json[2][1], "bar");
@ -1802,6 +1860,30 @@ describe('json output', function() {
X.utils.sheet_to_json(ws, {raw:true});
X.utils.sheet_to_json(ws, {raw:true, defval: 'jimjin'});
});
it('should handle skipHidden for rows if requested', function() {
var ws2 = X.utils.aoa_to_sheet(data), json = X.utils.sheet_to_json(ws2);
assert.equal(json[0]["1"], true);
assert.equal(json[2]["3"], "qux");
ws2["!rows"] = [null,{hidden:true},null,null]; json = X.utils.sheet_to_json(ws2, {skipHidden: 1});
assert.equal(json[0]["1"], "foo");
assert.equal(json[1]["3"], "qux");
});
it('should handle skipHidden for columns if requested', function() {
var ws2 = X.utils.aoa_to_sheet(data), json = X.utils.sheet_to_json(ws2);
assert.equal(json[1]["2"], "bar");
assert.equal(json[2]["3"], "qux");
ws2["!cols"] = [null,{hidden:true},null,null]; json = X.utils.sheet_to_json(ws2, {skipHidden: 1});
assert.equal(json[1]["2"], void 0);
assert.equal(json[2]["3"], "qux");
});
it('should handle skipHidden when first row is hidden', function() {
var ws2 = X.utils.aoa_to_sheet(data), json = X.utils.sheet_to_json(ws2);
assert.equal(json[0]["1"], true);
assert.equal(json[2]["3"], "qux");
ws2["!rows"] = [{hidden:true},null,null,null]; json = X.utils.sheet_to_json(ws2, {skipHidden: 1});
assert.equal(json[1]["1"], "foo");
assert.equal(json[2]["3"], "qux");
});
it('should disambiguate headers', function() {
var _data = [["S","h","e","e","t","J","S"],[1,2,3,4,5,6,7],[2,3,4,5,6,7,8]];
var _ws = X.utils.aoa_to_sheet(_data);
@ -1816,6 +1898,14 @@ describe('json output', function() {
assert.equal(json[i].S_1, 7 + i);
}
});
it('should handle collisions in disambiguation', function() {
var _data = [["a_1","a","a"],[1,2,3]];
var _ws = X.utils.aoa_to_sheet(_data);
var json = X.utils.sheet_to_json(_ws);
assert.equal(json[0].a, 2);
assert.equal(json[0].a_1, 1);
assert.equal(json[0].a_2, 3);
});
it('should handle raw data if requested', function() {
var _ws = X.utils.aoa_to_sheet(data, {cellDates:true});
var json = X.utils.sheet_to_json(_ws, {header:1, raw:true});
@ -2009,7 +2099,7 @@ describe('CSV', function() {
if(typeof before != 'undefined') before(bef);
else it('before', bef);
it('should generate csv', function() {
var baseline = "1,2,3,\nTRUE,FALSE,,sheetjs\nfoo,bar,2/19/14,0.3\n,,,\nbaz,,qux,\n";
var baseline = "1,2,3,\nTRUE,FALSE,,sheetjs\nfoo,bar,2/19/14,0.3\n,,,\nbaz,,qux,";
assert.equal(baseline, X.utils.sheet_to_csv(ws));
});
it('should handle FS', function() {
@ -2021,18 +2111,18 @@ describe('CSV', function() {
assert.equal(X.utils.sheet_to_csv(ws, {RS:";"}).replace(/[;]/g,"\n"), X.utils.sheet_to_csv(ws));
});
it('should handle dateNF', function() {
var baseline = "1,2,3,\nTRUE,FALSE,,sheetjs\nfoo,bar,20140219,0.3\n,,,\nbaz,,qux,\n";
var baseline = "1,2,3,\nTRUE,FALSE,,sheetjs\nfoo,bar,20140219,0.3\n,,,\nbaz,,qux,";
var _ws = X.utils.aoa_to_sheet(data, {cellDates:true});
delete get_cell(_ws,"C3").w;
delete get_cell(_ws,"C3").z;
assert.equal(baseline, X.utils.sheet_to_csv(_ws, {dateNF:"YYYYMMDD"}));
});
it('should handle strip', function() {
var baseline = "1,2,3\nTRUE,FALSE,,sheetjs\nfoo,bar,2/19/14,0.3\n\nbaz,,qux\n";
var baseline = "1,2,3\nTRUE,FALSE,,sheetjs\nfoo,bar,2/19/14,0.3\n\nbaz,,qux";
assert.equal(baseline, X.utils.sheet_to_csv(ws, {strip:true}));
});
it('should handle blankrows', function() {
var baseline = "1,2,3,\nTRUE,FALSE,,sheetjs\nfoo,bar,2/19/14,0.3\nbaz,,qux,\n";
var baseline = "1,2,3,\nTRUE,FALSE,,sheetjs\nfoo,bar,2/19/14,0.3\nbaz,,qux,";
assert.equal(baseline, X.utils.sheet_to_csv(ws, {blankrows:false}));
});
it('should handle various line endings', function() {
@ -2045,27 +2135,34 @@ describe('CSV', function() {
});
});
it('should handle skipHidden for rows if requested', function() {
var baseline = "1,2,3,\nTRUE,FALSE,,sheetjs\nfoo,bar,2/19/14,0.3\n,,,\nbaz,,qux,\n";
var baseline = "1,2,3,\nTRUE,FALSE,,sheetjs\nfoo,bar,2/19/14,0.3\n,,,\nbaz,,qux,";
delete ws["!rows"];
assert.equal(X.utils.sheet_to_csv(ws), baseline);
assert.equal(X.utils.sheet_to_csv(ws, {skipHidden:true}), baseline);
ws["!rows"] = [null,{hidden:true},null,null];
assert.equal(X.utils.sheet_to_csv(ws), baseline);
assert.equal(X.utils.sheet_to_csv(ws, {skipHidden:true}), "1,2,3,\nfoo,bar,2/19/14,0.3\n,,,\nbaz,,qux,\n");
assert.equal(X.utils.sheet_to_csv(ws, {skipHidden:true}), "1,2,3,\nfoo,bar,2/19/14,0.3\n,,,\nbaz,,qux,");
delete ws["!rows"];
});
it('should handle skipHidden for columns if requested', function() {
var baseline = "1,2,3,\nTRUE,FALSE,,sheetjs\nfoo,bar,2/19/14,0.3\n,,,\nbaz,,qux,\n";
var baseline = "1,2,3,\nTRUE,FALSE,,sheetjs\nfoo,bar,2/19/14,0.3\n,,,\nbaz,,qux,";
delete ws["!cols"];
assert.equal(X.utils.sheet_to_csv(ws), baseline);
assert.equal(X.utils.sheet_to_csv(ws, {skipHidden:true}), baseline);
ws["!cols"] = [null,{hidden:true},null,null];
assert.equal(X.utils.sheet_to_csv(ws), baseline);
assert.equal(X.utils.sheet_to_csv(ws, {skipHidden:true}), "1,3,\nTRUE,,sheetjs\nfoo,2/19/14,0.3\n,,\nbaz,qux,\n");
assert.equal(X.utils.sheet_to_csv(ws, {skipHidden:true}), "1,3,\nTRUE,,sheetjs\nfoo,2/19/14,0.3\n,,\nbaz,qux,");
ws["!cols"] = [{hidden:true},null,null,null];
assert.equal(X.utils.sheet_to_csv(ws, {skipHidden:true}), "2,3,\nFALSE,,sheetjs\nbar,2/19/14,0.3\n,,\n,qux,\n");
assert.equal(X.utils.sheet_to_csv(ws, {skipHidden:true}), "2,3,\nFALSE,,sheetjs\nbar,2/19/14,0.3\n,,\n,qux,");
delete ws["!cols"];
});
it('should properly handle blankrows and strip options', function() {
var _ws = X.utils.aoa_to_sheet([[""],[],["", ""]]);
assert.equal(X.utils.sheet_to_csv(_ws, {}), ",\n,\n,");
assert.equal(X.utils.sheet_to_csv(_ws, {strip: true}), "\n\n");
assert.equal(X.utils.sheet_to_csv(_ws, {blankrows: false}), ",\n,");
assert.equal(X.utils.sheet_to_csv(_ws, {blankrows: false, strip: true}), "");
});
});
});
@ -2088,6 +2185,28 @@ describe('sylk', function() {
});
});
(typeof Uint8Array !== "undefined" ? describe : describe.skip)('numbers', function() {
it('should parse files from Numbers 6.x', function() {
var wb = X.read(fs.readFileSync(dir + 'numbers/types_61.numbers'), {type:TYPE, WTF:1});
var ws = wb.Sheets["Sheet 1"];
assert.equal(get_cell(ws, "A1").v, "Sheet");
assert.equal(get_cell(ws, "B1").v, "JS");
assert.equal(get_cell(ws, "B2").v, 123);
assert.equal(get_cell(ws, "B11").v, true);
assert.equal(get_cell(ws, "B13").v, 50);
});
it('should cap cols at 1000 (ALL)', function() {
var aoa = [[1], [], []]; aoa[1][999] = 2; aoa[2][1000] = 3;
var ws1 = X.utils.aoa_to_sheet(aoa);
var wb1 = X.utils.book_new(); X.utils.book_append_sheet(wb1, ws1, "Sheet1");
var wb2 = X.read(X.write(wb1,{bookType:"numbers",type:'binary',numbers:XLSX_ZAHL}),{type:'binary'});
var ws2 = wb2.Sheets.Sheet1;
assert.equal(ws2["!ref"], "A1:ALL3");
assert.equal(get_cell(ws2, "A1").v, 1);
assert.equal(get_cell(ws2, "ALL2").v, 2);
});
});
if(fs.existsSync(dir + 'dbf/d11.dbf')) describe('dbf', function() {
var wbs/*:Array<any>*/ = ([
['d11', dir + 'dbf/d11.dbf'],
@ -2142,6 +2261,18 @@ describe('HTML', function() {
var wb = X.read(table, {type:"string"});
assert.equal(get_cell(wb.Sheets.Sheet1, "A1").v, "foo\nbar");
});
it('should generate multi-sheet workbooks', function() {
var table = "";
for(var i = 0; i < 4; ++i) table += "<table><tr><td>" + X.utils.encode_col(i) + "</td><td>" + i + "</td></tr></table>";
table += table; table += table;
var wb = X.read(table, {type: "string"});
assert.equal(wb.SheetNames.length, 16);
assert.equal(wb.SheetNames[1], "Sheet2");
for(var j = 0; j < 4; ++j) {
assert.equal(get_cell(wb.Sheets["Sheet" + (j+1)], "A1").v, X.utils.encode_col(j));
assert.equal(get_cell(wb.Sheets["Sheet" + (j+1)], "B1").v, j);
}
});
});
(domtest ? describe : describe.skip)('input DOM', function() {
it('should interpret values by default', function() { plaintext_test(X.utils.table_to_book(get_dom_element(html_str)), false); });
@ -2184,7 +2315,12 @@ describe('HTML', function() {
var expected_rows = [];
expected_rows[0] = expected_rows[2] = {hidden: true};
assert.equal(ws['!ref'], "A1:A3");
assert.deepEqual(ws['!rows'], expected_rows);
try {
assert.deepEqual(ws['!rows'], expected_rows);
} catch(e) {
expected_rows[1] = {};
assert.deepEqual(ws['!rows'], expected_rows);
}
assert.equal(get_cell(ws, "A1").v, "Foo");
assert.equal(get_cell(ws, "A2").v, "Bar");
assert.equal(get_cell(ws, "A3").v, "Baz");
@ -2214,11 +2350,25 @@ describe('HTML', function() {
var html = "<table><thead><tr><th>A</th><th>B</th></tr></thead><tbody><tr><td>1</td><td>2</td></tr><tr><td>3</td><td>4</td></tr></tbody><tfoot><tr><th>4</th><th>6</th></tr></tfoot></table>";
it('HTML string', function() {
var ws = X.read(html, {type:'string'}).Sheets.Sheet1;
assert.equal(X.utils.sheet_to_csv(ws), "A,B\n1,2\n3,4\n4,6\n");
assert.equal(X.utils.sheet_to_csv(ws), "A,B\n1,2\n3,4\n4,6");
});
if(domtest) it('DOM', function() {
var ws = X.utils.table_to_sheet(get_dom_element(html));
assert.equal(X.utils.sheet_to_csv(ws), "A,B\n1,2\n3,4\n4,6\n");
assert.equal(X.utils.sheet_to_csv(ws), "A,B\n1,2\n3,4\n4,6");
});
});
describe('empty cell containing html element should increment cell index', function() {
var html = "<table><tr><td>abc</td><td><b> </b></td><td>def</td></tr></table>";
var expectedCellCount = 3;
it('HTML string', function() {
var ws = X.read(html, {type:'string'}).Sheets.Sheet1;
var range = X.utils.decode_range(ws['!ref']);
assert.equal(range.e.c,expectedCellCount - 1);
});
if(domtest) it('DOM', function() {
var ws = X.utils.table_to_sheet(get_dom_element(html));
var range = X.utils.decode_range(ws['!ref']);
assert.equal(range.e.c, expectedCellCount - 1);
});
});
});
@ -2250,10 +2400,15 @@ describe('js -> file -> js', function() {
['B2', 'B3'].forEach(cb); /* bool */
['C2', 'C3'].forEach(cb); /* string */
if(!DIF_XL) cb('D4'); /* date */
if(DIF_XL && f == "dif") assert.equal(get_cell(newwb.Sheets.Sheet1, 'C5').v, '=""0.3""');// dif forces string formula
else if(f != 'csv' && f != 'txt') eqcell(wb, newwb, 'Sheet1', 'C5');
if(f != 'csv' && f != 'txt') eqcell(wb, newwb, 'Sheet1', 'C5');
});
});
it('should roundtrip DIF strings', function() {
var wb1 = X.read(X.write(wb, {type:BIN, bookType: 'dif'}), {type:BIN});
var wb2 = X.read(X.write(wb1, {type:BIN, bookType: 'dif'}), {type:BIN});
eqcell(wb, wb1, 'Sheet1', 'C5');
eqcell(wb, wb2, 'Sheet1', 'C5');
});
});
describe('corner cases', function() {
@ -2267,9 +2422,9 @@ describe('corner cases', function() {
get_cell(ws,"A1").f = ""; get_cell(ws,"A1").w = "";
delete get_cell(ws,"C3").w; delete get_cell(ws,"C3").z; get_cell(ws,"C3").XF = {ifmt:14};
get_cell(ws,"A4").t = "e";
X.utils.get_formulae(ws);
X.utils.make_csv(ws);
X.utils.make_json(ws);
X.utils.sheet_to_formulae(ws);
X.utils.sheet_to_csv(ws);
X.utils.sheet_to_json(ws);
ws['!cols'] = [ {wch:6}, {wch:7}, {wch:10}, {wch:20} ];
var wb = {SheetNames:['sheetjs'], Sheets:{sheetjs:ws}};
@ -2281,7 +2436,7 @@ describe('corner cases', function() {
X.write(wb, {type: "binary", bookType: 'biff5'});
X.write(wb, {type: "binary", bookType: 'biff8'});
get_cell(ws,"A2").t = "f";
assert.throws(function() { X.utils.make_json(ws); });
assert.throws(function() { X.utils.sheet_to_json(ws); });
});
it('SSF', function() {
X.SSF.format("General", "dafuq");
@ -2344,6 +2499,29 @@ describe('corner cases', function() {
assert.equal(wb.Sheets.Sheet1.A10.f, "'a!b'!A1");
assert.equal(wb.Sheets.Sheet1.A11.f, "'a b'!A1");
});
it.skip('should parse CSV date values with preceding space', function() {
function check_ws(ws, dNF) {
//var d = X.SSF.parse_date_code(ws.B1.v);
assert.equal(ws.B1.w, dNF ? '2018-03-24' : "3/23/18");
//assert.equal(d.d, 24);
//assert.equal(d.m, 3);
//assert.equal(d.y, 2018);
}
[true, false].forEach(function(cD) {
[null, 'yyyy-mm-dd'].forEach(function(dNF) {
var ws1 = X.read(
'7,2018-03-24',
{cellDates: cD, dateNF: dNF, type:'string'}
).Sheets.Sheet1;
check_ws(ws1, dNF);
var ws2 = X.read(
'7, 2018-03-24',
{cellDates: cD, dateNF: dNF, type:'string'}
).Sheets.Sheet1;
check_ws(ws2, dNF);
});
});
});
});
describe('encryption', function() {

File diff suppressed because one or more lines are too long

@ -36,8 +36,10 @@
})();
</script>
<script src="shim.js"></script>
<script src="base64.js"></script>
<script src="fs_.js"></script>
<script src="fixtures.js"></script>
<script src="xlsx.zahl.js"></script>
<script src="xlsx.full.min.js"></script>
<div id="mocha"></div>
<script src="mocha.js"></script>

4
tests/xlsx.zahl.js Normal file

File diff suppressed because one or more lines are too long

@ -35,15 +35,14 @@ a { text-decoration: none }
<div id="drop">Drop a spreadsheet file here to see sheet data</div>
<input type="file" name="xlfile" id="xlf" /> ... or click here to select a file
<textarea id="b64data">... or paste a base64-encoding here</textarea>
<b>Advanced Demo Options:</b>
Use readAsBinaryString: (when available) <input type="checkbox" name="userabs" checked>
</pre>
<p><input type="submit" value="Export to XLSX!" id="xport" onclick="export_xlsx();" disabled="true"></p>
<p><input type="submit" value="Export to XLSX!" id="xport" onclick="export_xlsx();"></p>
<div id="htmlout"></div>
<br />
<script src="https://unpkg.com/x-data-spreadsheet/dist/xspreadsheet.js"></script>
<script src="shim.js"></script>
<script src="xlsx.full.min.js"></script>
<script src="xlsxspread.js"></script>
<script>
/*jshint browser:true */
/* eslint-env browser */
@ -56,43 +55,6 @@ var xspr = x_spreadsheet(HTMLOUT);
HTMLOUT.style.height = (window.innerHeight - 400) + "px";
HTMLOUT.style.width = (window.innerWidth - 50) + "px";
function stox(wb) {
var out = [];
wb.SheetNames.forEach(function(name) {
var o = {name:name, rows:{}};
var ws = wb.Sheets[name];
var aoa = XLSX.utils.sheet_to_json(ws, {raw: false, header:1});
aoa.forEach(function(r, i) {
var cells = {};
r.forEach(function(c, j) { cells[j] = ({ text: c }); });
o.rows[i] = { cells: cells };
})
out.push(o);
});
return out;
}
function xtos(sdata) {
var out = XLSX.utils.book_new();
sdata.forEach(function(xws) {
var aoa = [[]];
var rowobj = xws.rows;
for(var ri = 0; ri < rowobj.len; ++ri) {
var row = rowobj[ri];
if(!row) continue;
aoa[ri] = [];
Object.keys(row.cells).forEach(function(k) {
var idx = +k;
if(isNaN(idx)) return;
aoa[ri][idx] = row.cells[k].text;
});
}
var ws = XLSX.utils.aoa_to_sheet(aoa);
XLSX.utils.book_append_sheet(out, ws, xws.name);
});
return out;
}
var process_wb = (function() {
var XPORT = document.getElementById('xport');
@ -109,22 +71,16 @@ var process_wb = (function() {
})();
var do_file = (function() {
var rABS = typeof FileReader !== "undefined" && (FileReader.prototype||{}).readAsBinaryString;
var domrabs = document.getElementsByName("userabs")[0];
if(!rABS) domrabs.disabled = !(domrabs.checked = false);
return function do_file(files) {
rABS = domrabs.checked;
var f = files[0];
var reader = new FileReader();
reader.onload = function(e) {
if(typeof console !== 'undefined') console.log("onload", new Date(), rABS);
if(typeof console !== 'undefined') console.log("onload", new Date());
var data = e.target.result;
if(!rABS) data = new Uint8Array(data);
process_wb(XLSX.read(data, {type: rABS ? 'binary' : 'array'}));
data = new Uint8Array(data);
process_wb(XLSX.read(data, {type: 'array'}));
};
if(rABS) reader.readAsBinaryString(f);
else reader.readAsArrayBuffer(f);
reader.readAsArrayBuffer(f);
};
})();

31
xlsx.core.min.js vendored

File diff suppressed because one or more lines are too long

44
xlsx.full.min.js vendored

File diff suppressed because one or more lines are too long

11213
xlsx.js

File diff suppressed because it is too large Load Diff

130
xlsxspread.js Normal file

@ -0,0 +1,130 @@
/*! xlsxspread.js (C) SheetJS LLC -- https://sheetjs.com/ */
/* eslint-env browser */
/*global XLSX */
/*exported stox, xtos */
/**
* Converts data from SheetJS to x-spreadsheet
*
* @param {Object} wb SheetJS workbook object
*
* @returns {Object[]} An x-spreadsheet data
*/
function stox(wb) {
var out = [];
wb.SheetNames.forEach(function (name) {
var o = { name: name, rows: {} };
var ws = wb.Sheets[name];
var range = XLSX.utils.decode_range(ws['!ref']);
// sheet_to_json will lost empty row and col at begin as default
range.s = { r: 0, c: 0 };
var aoa = XLSX.utils.sheet_to_json(ws, {
raw: false,
header: 1,
range: range
});
aoa.forEach(function (r, i) {
var cells = {};
r.forEach(function (c, j) {
cells[j] = { text: c };
var cellRef = XLSX.utils.encode_cell({ r: i, c: j });
if ( ws[cellRef] != null && ws[cellRef].f != null) {
cells[j].text = "=" + ws[cellRef].f;
}
});
o.rows[i] = { cells: cells };
});
o.merges = [];
(ws["!merges"]||[]).forEach(function (merge, i) {
//Needed to support merged cells with empty content
if (o.rows[merge.s.r] == null) {
o.rows[merge.s.r] = { cells: {} };
}
if (o.rows[merge.s.r].cells[merge.s.c] == null) {
o.rows[merge.s.r].cells[merge.s.c] = {};
}
o.rows[merge.s.r].cells[merge.s.c].merge = [
merge.e.r - merge.s.r,
merge.e.c - merge.s.c
];
o.merges[i] = XLSX.utils.encode_range(merge);
});
out.push(o);
});
return out;
}
/**
* Converts data from x-spreadsheet to SheetJS
*
* @param {Object[]} sdata An x-spreadsheet data object
*
* @returns {Object} A SheetJS workbook object
*/
function xtos(sdata) {
var out = XLSX.utils.book_new();
sdata.forEach(function (xws) {
var ws = {};
var rowobj = xws.rows;
var minCoord = { r: 0, c: 0 }, maxCoord = { r: 0, c: 0 };
for (var ri = 0; ri < rowobj.len; ++ri) {
var row = rowobj[ri];
if (!row) continue;
Object.keys(row.cells).forEach(function (k) {
var idx = +k;
if (isNaN(idx)) return;
var lastRef = XLSX.utils.encode_cell({ r: ri, c: idx });
if (ri > maxCoord.r) maxCoord.r = ri;
if (idx > maxCoord.c) maxCoord.c = idx;
var cellText = row.cells[k].text, type = "s";
if (!cellText) {
cellText = "";
type = "z";
} else if (!isNaN(parseFloat(cellText))) {
cellText = parseFloat(cellText);
type = "n";
} else if (cellText.toLowerCase() === "true" || cellText.toLowerCase() === "false") {
cellText = Boolean(cellText);
type = "b";
}
ws[lastRef] = { v: cellText, t: type };
if (type == "s" && cellText[0] == "=") {
ws[lastRef].f = cellText.slice(1);
}
if (row.cells[k].merge != null) {
if (ws["!merges"] == null) ws["!merges"] = [];
ws["!merges"].push({
s: { r: ri, c: idx },
e: {
r: ri + row.cells[k].merge[0],
c: idx + row.cells[k].merge[1]
}
});
}
});
}
ws["!ref"] = minCoord ? XLSX.utils.encode_range({
s: minCoord,
e: maxCoord
}) : "A1";
XLSX.utils.book_append_sheet(out, ws, xws.name);
});
return out;
}