version bump 0.11.19: browser writeFile

- IE6-9 ActiveX + VBScript shim
- `writeFile` supported in browser
- `oldie` demo for IE write strategies
This commit is contained in:
SheetJS 2018-02-03 15:46:32 -05:00
parent edf7150ca8
commit 75845a0ca7
88 changed files with 1098 additions and 416 deletions

@ -36,7 +36,6 @@ CDNjs
CommonJS
Ethercalc
ExtendScript
FileSaver
IndexedDB
JavaScriptCore
LocalStorage
@ -58,6 +57,7 @@ webpack
weex
# Other terms
ActiveX
APIs
ArrayBuffer
Base64
@ -66,6 +66,7 @@ JS
NoSQL
README
UTF-16
VBScript
XHR
XMLHttpRequest
bundler

@ -4,6 +4,10 @@ This log is intended to keep track of backwards-incompatible changes, including
but not limited to API changes and file location changes. Minor behavioral
changes may not be included if they are not expected to break existing code.
## 0.11.19
* Error on empty workbook
## 0.11.16 (2017-12-30)
* XLS ANSI/CP separation

@ -215,6 +215,7 @@ The [`demos` directory](demos/) includes sample projects for:
- [`Headless Browsers`](demos/headless/)
- [`canvas-datagrid`](demos/datagrid/)
- [`Swift JSC and other engines`](demos/altjs/)
- [`internet explorer`](demos/oldie/)
### Optional Modules
@ -339,7 +340,7 @@ var worksheet = XLSX.read(htmlstr, {type:'string'});
<summary><b>Browser download file (ajax)</b> (click to show)</summary>
Note: for a more complete example that works in older browsers, check the demo
at <http://oss.sheetjs.com/js-xlsx/ajax.html>). The <demos/xhr/> directory also
at <http://oss.sheetjs.com/js-xlsx/ajax.html>). The [`xhr` demo](demos/xhr/)
includes more examples with `XMLHttpRequest` and `fetch`.
```js
@ -414,6 +415,8 @@ input_dom_element.addEventListener('change', handleFile, false);
</details>
More specialized cases, including mobile app file processing, are covered in the
[included demos](demos/)
### Parsing Examples
@ -590,8 +593,7 @@ Assuming `workbook` is a workbook object:
<details>
<summary><b>nodejs write a file</b> (click to show)</summary>
`writeFile` is only available in server environments. Browsers have no API for
writing arbitrary files given a path, so another strategy must be used.
`XLSX.writeFile` uses `fs.writeFileSync` in server environments:
```js
if(typeof require !== 'undefined') XLSX = require('xlsx');
@ -603,7 +605,7 @@ XLSX.writeFile(workbook, 'out.xlsb');
</details>
<details>
<summary><b>Browser add to web page</b> (click to show)</summary>
<summary><b>Browser add TABLE element to page</b> (click to show)</summary>
The `sheet_to_html` utility function generates HTML code that can be added to
any DOM element.
@ -614,29 +616,10 @@ var container = document.getElementById('tableau');
container.innerHTML = XLSX.utils.sheet_to_html(worksheet);
```
</details>
<details>
<summary><b>Browser save file</b> (click to show)</summary>
Note: browser generates binary blob and forces a "download" to client. This
example uses [FileSaver](https://github.com/eligrey/FileSaver.js/):
```js
/* bookType can be any supported output type */
var wopts = { bookType:'xlsx', bookSST:false, type:'array' };
var wbout = XLSX.write(workbook,wopts);
/* the saveAs call downloads a file on the local machine */
saveAs(new Blob([wbout],{type:"application/octet-stream"}), "test.xlsx");
```
</details>
<details>
<summary><b>Browser upload to server</b> (click to show)</summary>
<summary><b>Browser upload file (ajax)</b> (click to show)</summary>
A complete example using XHR is [included in the XHR demo](demos/xhr/), along
with examples for fetch and wrapper libraries. This example assumes the server
@ -658,6 +641,65 @@ req.send(formdata);
</details>
<details>
<summary><b>Browser save file</b> (click to show)</summary>
`XLSX.writeFile` wraps a few techniques for triggering a file save:
- `URL` browser API creates an object URL for the file, which the library uses
by creating a link and forcing a click. It is supported in modern browsers.
- `msSaveBlob` is an IE10+ API for triggering a file save.
- `IE_FileSave` uses VBScript and ActiveX to write a file in IE6+ for Windows
XP and Windows 7. The shim must be included in the containing HTML page.
There is no standard way to determine if the actual file has been downloaded.
```js
/* output format determined by filename */
XLSX.writeFile(workbook, 'out.xlsb');
/* at this point, out.xlsb will have been downloaded */
```
</details>
<details>
<summary><b>Browser save file (compatibility)</b> (click to show)</summary>
`XLSX.writeFile` techniques work for most modern browsers as well as older IE.
For much older browsers, there are workarounds implemented by wrapper libraries.
[`FileSaver.js`](https://github.com/eligrey/FileSaver.js/) implements `saveAs`.
Note: `XLSX.writeFile` will automatically call `saveAs` if available.
```js
/* bookType can be any supported output type */
var wopts = { bookType:'xlsx', bookSST:false, type:'array' };
var wbout = XLSX.write(workbook,wopts);
/* the saveAs call downloads a file on the local machine */
saveAs(new Blob([wbout],{type:"application/octet-stream"}), "test.xlsx");
```
[`Downloadify`](https://github.com/dcneiner/downloadify) uses a Flash SWF button
to generate local files, suitable for environments where ActiveX is unavailable:
```js
Downloadify.create(id,{
/* other options are required! read the downloadify docs for more info */
filename: "test.xlsx",
data: function() { return XLSX.write(wb, {bookType:"xlsx", type:'base64'}); },
append: false,
dataType: 'base64'
});
```
The [`oldie` demo](demos/oldie/) shows an IE-compatible fallback scenario.
</details>
The [included demos](demos/) cover mobile apps and other special deployments.
### Writing Examples
- <http://sheetjs.com/demos/table.html> exporting an HTML table
@ -705,7 +747,8 @@ Parse options are described in the [Parsing Options](#parsing-options) section.
`XLSX.write(wb, write_opts)` attempts to write the workbook `wb`
`XLSX.writeFile(wb, filename, write_opts)` attempts to write `wb` to `filename`
`XLSX.writeFile(wb, filename, write_opts)` attempts to write `wb` to `filename`.
In browser-based environments, it will attempt to force a client-side download.
`XLSX.writeFileAsync(filename, wb, o, cb)` attempts to write `wb` to `filename`.
If `o` is omitted, the writer will use the third argument as the callback.
@ -2015,6 +2058,7 @@ produces HTML output. The function takes an options argument:
| Option Name | Default | Description |
| :---------- | :------: | :-------------------------------------------------- |
|`id` | | Specify the `id` attribute for the `TABLE` element |
|`editable` | false | If true, set `contenteditable="true"` for every TD |
|`header` | | Override header (default `html body`) |
|`footer` | | Override footer (default `/body /html`) |

@ -1 +1 @@
XLSX.version = '0.11.18';
XLSX.version = '0.11.19';

@ -19,11 +19,16 @@ function s2ab(s/*:string*/) {
return buf;
}
function arr2str(data/*:any*/)/*:string*/ {
function a2s(data/*:any*/)/*:string*/ {
if(Array.isArray(data)) return data.map(_chr).join("");
var o/*:Array<string>*/ = []; for(var i = 0; i < data.length; ++i) o[i] = _chr(data[i]); return o.join("");
}
function a2u(data/*:Array<number>*/)/*:Uint8Array*/ {
if(typeof Uint8Array === 'undefined') throw new Error("Unsupported");
return new Uint8Array(data);
}
function ab2a(data/*:ArrayBuffer|Uint8Array*/)/*:Array<number>*/ {
if(typeof ArrayBuffer == 'undefined') throw new Error("Unsupported");
if(data instanceof ArrayBuffer) return ab2a(new Uint8Array(data));

37
bits/19_fsutils.js Normal file

@ -0,0 +1,37 @@
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/*:string*/, payload/*:any*/, enc/*:?string*/) {
/*global IE_SaveFile, Blob, navigator, saveAs, URL, document */
if(typeof _fs !== 'undefined' && _fs.writeFileSync) return enc ? _fs.writeFileSync(fname, payload, enc) : _fs.writeFileSync(fname, payload);
var data = (enc == "utf8") ? utf8write(payload) : payload;
/*:: declare var IE_SaveFile: any; */
if(typeof IE_SaveFile !== 'undefined') return IE_SaveFile(data, fname);
if(typeof Blob !== 'undefined') {
var blob = new Blob([blobify(data)], {type:"application/octet-stream"});
/*:: declare var navigator: any; */
if(typeof navigator !== 'undefined' && navigator.msSaveBlob) return navigator.msSaveBlob(blob, fname);
/*:: declare var saveAs: any; */
if(typeof saveAs !== 'undefined') return saveAs(blob, fname);
if(typeof URL !== 'undefined' && typeof document !== 'undefined' && document.createElement && URL.createObjectURL) {
var a = document.createElement("a");
if(a.download != null) {
var url = URL.createObjectURL(blob);
/*:: if(document.body == null) throw new Error("unreachable"); */
a.download = fname; a.href = url; document.body.appendChild(a); a.click();
/*:: if(document.body == null) throw new Error("unreachable"); */ document.body.removeChild(a);
if(URL.revokeObjectURL && typeof setTimeout !== 'undefined') setTimeout(function() { URL.revokeObjectURL(url); }, 60000);
return url;
}
}
}
throw new Error("cannot initiate download");
}

@ -51,14 +51,13 @@ function getzipstr(zip, file/*:string*/, safe/*:?boolean*/)/*:?string*/ {
try { return getzipstr(zip, file); } catch(e) { return null; }
}
var _fs, jszip;
var jszip;
/*:: declare var JSZip:any; */
/*global JSZip:true */
if(typeof JSZip !== 'undefined') jszip = JSZip;
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
if(typeof exports !== 'undefined') {
if(typeof module !== 'undefined' && module.exports) {
if(typeof jszip === 'undefined') jszip = require('./jszip.js');
try { _fs = require('fs'); } catch(e) { }
}
}

@ -132,6 +132,7 @@ function check_wb_names(N) {
}
function check_wb(wb) {
if(!wb || !wb.SheetNames || !wb.Sheets) throw new Error("Invalid Workbook");
if(!wb.SheetNames.length) throw new Error("Workbook is empty");
check_wb_names(wb.SheetNames);
/* TODO: validate workbook */
}

@ -833,7 +833,7 @@ function parse_xlml(data/*:RawBytes|string*/, opts)/*:Workbook*/ {
switch(opts.type||"base64") {
case "base64": return parse_xlml_xml(Base64.decode(data), opts);
case "binary": case "buffer": case "file": return parse_xlml_xml(data, opts);
case "array": return parse_xlml_xml(arr2str(data), opts);
case "array": return parse_xlml_xml(a2s(data), opts);
}
/*:: throw new Error("unsupported type " + opts.type); */
}

@ -84,9 +84,9 @@ var HTML_ = (function() {
var preamble = "<tr>";
return preamble + oo.join("") + "</tr>";
}
function make_html_preamble(/*::ws:Worksheet, R:Range, o:Sheet2HTMLOpts*/)/*:string*/ {
function make_html_preamble(ws/*:Worksheet*/, R/*:Range*/, o/*:Sheet2HTMLOpts*/)/*:string*/ {
var out/*:Array<string>*/ = [];
return out.join("") + '<table>';
return out.join("") + '<table' + (o && o.id ? ' id="' + o.id + '"' : "") + '>';
}
var _BEGIN = '<html><head><meta charset="utf-8"/><title>SheetJS Table Export</title></head><body>';
var _END = '</body></html>';

@ -100,7 +100,6 @@ function readSync(data/*:RawData*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
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);
if(0x20>n[0]||n[0]>0x7F) throw new Error("Unsupported file " + n.join("|"));
return read_prn(data, d, o, str);
}

@ -6,12 +6,12 @@ function write_zip_type(wb/*:Workbook*/, opts/*:?WriteOpts*/)/*:any*/ {
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 "string": throw new Error("'string' output type invalid for '" + o.bookType + "' files");
case "buffer":
case "file": oopts.type = "nodebuffer"; break;
case "file": oopts.type = has_buf ? "nodebuffer" : "string"; break;
default: throw new Error("Unrecognized type " + o.type);
}
if(o.type === "file") return _fs.writeFileSync(o.file, z.generate(oopts));
if(o.type === "file") return write_dl(o.file, z.generate(oopts));
var out = z.generate(oopts);
// $FlowIgnore
return o.type == "string" ? utf8read(out) : out;
@ -23,8 +23,8 @@ function write_cfb_type(wb/*:Workbook*/, opts/*:?WriteOpts*/)/*:any*/ {
switch(o.type) {
case "base64": case "binary": break;
case "buffer": case "array": o.type = ""; break;
case "file": return _fs.writeFileSync(o.file, CFB.write(cfb, {type:'buffer'}));
case "string": throw new Error("'string' output type invalid for '" + o.bookType + ' files');
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);
@ -37,7 +37,7 @@ function write_string_type(out/*:string*/, opts/*:WriteOpts*/, bom/*:?string*/)/
case "base64": return Base64.encode(utf8write(o));
case "binary": return utf8write(o);
case "string": return out;
case "file": return _fs.writeFileSync(opts.file, o, 'utf8');
case "file": return write_dl(opts.file, o, 'utf8');
case "buffer": {
if(has_buf) return new Buffer(o, 'utf8');
else return write_string_type(o, {type:'binary'}).split("").map(function(c) { return c.charCodeAt(0); });
@ -51,7 +51,7 @@ function write_stxt_type(out/*:string*/, opts/*:WriteOpts*/)/*:any*/ {
case "base64": return Base64.encode(out);
case "binary": return out;
case "string": return out; /* override in sheet_to_txt */
case "file": return _fs.writeFileSync(opts.file, out, 'binary');
case "file": return write_dl(opts.file, out, 'binary');
case "buffer": {
if(has_buf) return new Buffer(out, 'binary');
else return out.split("").map(function(c) { return c.charCodeAt(0); });
@ -70,7 +70,7 @@ function write_binary_type(out, opts/*:WriteOpts*/)/*:any*/ {
// $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 _fs.writeFileSync(opts.file, out);
case "file": return write_dl(opts.file, out);
case "buffer": return out;
default: throw new Error("Unrecognized type " + opts.type);
}

@ -131,7 +131,7 @@ function sheet_to_txt(sheet/*:Worksheet*/, opts/*:?Sheet2CSVOpts*/) {
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 "\xff\xfe" + o;
return String.fromCharCode(255) + String.fromCharCode(254) + o;
}
function sheet_to_formulae(sheet/*:Worksheet*/)/*:Array<string>*/ {

@ -44,5 +44,6 @@ can be installed with Bash on Windows or with `cygwin`.
- [`Headless Browsers`](headless/)
- [`canvas-datagrid`](datagrid/)
- [`Swift JSC and other engines`](altjs/)
- [`internet explorer`](oldie/)
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-xlsx?pixel)](https://github.com/SheetJS/js-xlsx)

@ -81,18 +81,15 @@ var ws = XLSX.utils.json_to_sheet(data);
var wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "Presidents");
/* write workbook (use type 'array' for ArrayBuffer) */
var wbout = XLSX.write(wb, {bookType:'xlsx', type:'array'});
/* generate a download */
saveAs(new Blob([wbout],{type:"application/octet-stream"}), "sheetjs.xlsx");
/* write workbook and force a download */
XLSX.writeFile(wb, "sheetjs.xlsx");
```
`SheetJSExportService` exposes export functions for `XLSB` and `XLSX`. Other
formats are easily supported by changing the `bookType` variable. It grabs
values from the grid, builds an array of arrays, generates a workbook and uses
FileSaver to generate a download. By setting the `filename` and `sheetname`
options in the ui-grid options, the output can be controlled.
values from the grid, builds an array of arrays, generates a workbook and forces
a download. By setting the `filename` and `sheetname` options in the ui-grid
options, the output can be controlled.
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-xlsx?pixel)](https://github.com/SheetJS/js-xlsx)

@ -12,8 +12,7 @@ function SheetJSExportService(uiGridExporterService) {
var wb = XLSX.utils.book_new(), ws = uigrid_to_sheet(data, columns);
XLSX.utils.book_append_sheet(wb, ws, sheetName);
var wbout = XLSX.write(wb, wopts);
saveAs(new Blob([wbout], { type: 'application/octet-stream' }), fileName);
XLSX.writeFile(wb, fileName);
}
var service = {};

@ -13,10 +13,8 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-grid/4.0.0/ui-grid.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-grid/4.0.0/ui-grid.css"/>
<!-- FileSaver shim for exporting files -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.3/FileSaver.min.js"></script>
<!-- SheetJS js-xlsx library -->
<script src="shim.js"></script>
<script src="xlsx.full.min.js"></script>
<!-- SheetJS Service -->

1
demos/angular/shim.js vendored Symbolic link

@ -0,0 +1 @@
../../shim.js

@ -2,24 +2,27 @@
angular:
# Test Angular2 build
cp package.json-angular2 package.json
rm -rf node_modules
npm install
if [ ! -e node_modules ]; then mkdir node_modules; fi
if [ ! -e node_modules/xlsx ]; then cd node_modules; ln -s ../../../ xlsx; cd -; fi
ng build
npm run build
# Test Angular4 build
cp package.json-angular4 package.json
rm -rf node_modules
npm install
if [ ! -e node_modules ]; then mkdir node_modules; fi
if [ ! -e node_modules/xlsx ]; then cd node_modules; ln -s ../../../ xlsx; cd -; fi
ng build
npm run build
# Test Angular5 build
cp package.json-angular5 package.json
rm -rf node_modules
npm install
if [ ! -e node_modules ]; then mkdir node_modules; fi
if [ ! -e node_modules/xlsx ]; then cd node_modules; ln -s ../../../ xlsx; cd -; fi
ng build
npm run build
.PHONY: ionic
ionic:

@ -38,8 +38,7 @@ const wb: XLSX.WorkBook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'Sheet1');
/* save to file */
const wbout: string = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
saveAs(new Blob([wbout]), 'SheetJS.xlsx');
XLSX.writeFile(wb, 'SheetJS.xlsx');
```
`sheet_to_json` with the option `header:1` makes importing simple:

@ -5,7 +5,7 @@ if [ ! -e SheetJSIonic ]; then
ionic cordova platform add browser </dev/null
ionic cordova platform add ios </dev/null
ionic cordova plugin add cordova-plugin-file </dev/null
npm install --save @ionic-native/file file-saver
npm install --save @ionic-native/file
npm install --save xlsx
cp src/app/app.module.ts{,.bak}

@ -5,7 +5,6 @@ import { Component } from '@angular/core';
import * as XLSX from 'xlsx';
import { File } from '@ionic-native/file';
import { saveAs } from 'file-saver';
type AOA = any[][];
@ -48,7 +47,7 @@ export class HomePage {
this.data = <AOA>(XLSX.utils.sheet_to_json(ws, {header: 1}));
};
write(): ArrayBuffer {
write(): XLSX.WorkBook {
/* generate worksheet */
const ws: XLSX.WorkSheet = XLSX.utils.aoa_to_sheet(this.data);
@ -56,9 +55,7 @@ export class HomePage {
const wb: XLSX.WorkBook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'SheetJS');
/* save to ArrayBuffer */
const wbout: ArrayBuffer = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
return wbout;
return wb;
};
/* File Input element for browser */
@ -91,17 +88,26 @@ export class HomePage {
/* Export button */
async export() {
const wbout: ArrayBuffer = this.write();
const wb: XLSX.WorkBook = this.write();
const filename: string = "SheetJSIonic.xlsx";
const blob: Blob = new Blob([wbout], {type: 'application/octet-stream'});
try {
/* generate Blob */
const wbout: ArrayBuffer = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
const blob: Blob = new Blob([wbout], {type: 'application/octet-stream'});
/* find appropriate path for mobile */
const target: string = this.file.documentsDirectory || this.file.externalDataDirectory || this.file.dataDirectory || '';
const dentry = await this.file.resolveDirectoryUrl(target);
const url: string = dentry.nativeURL || '';
/* attempt to save blob to file */
await this.file.writeFile(url, filename, blob, {replace: true});
alert(`Wrote to SheetJSIonic.xlsx in ${url}`);
} catch(e) {
if(e.message.match(/It was determined/)) saveAs(blob, filename);
if(e.message.match(/It was determined/)) {
/* in the browser, use writeFile */
XLSX.writeFile(wb, filename);
}
else alert(`Error: ${e.message}`);
}
};

@ -24,11 +24,10 @@
"rxjs": "^5.5.2",
"zone.js": "^0.8.14",
"file-saver": "^1.3.3"
"zone.js": "^0.8.14"
},
"devDependencies": {
"@angular/cli": "1.5.0",
"@angular/cli": "^1.5.3",
"@angular/compiler-cli": "^5.0.0",
"@angular/language-service": "^5.0.0",
"@types/node": "~6.0.60",

@ -24,8 +24,7 @@
"reflect-metadata": "^0.1.8",
"rxjs": "^5.0.2",
"systemjs": "0.19.40",
"zone.js": "^0.7.4",
"file-saver": "^1.3.3"
"zone.js": "^0.7.4"
},
"devDependencies": {
"@angular/cli": "1.1.2",

@ -24,8 +24,7 @@
"rxjs": "^5.1.0",
"zone.js": "^0.8.4",
"file-saver": "^1.3.3"
"zone.js": "^0.8.4"
},
"devDependencies": {
"@angular/cli": "1.1.2",

@ -24,11 +24,10 @@
"rxjs": "^5.5.2",
"zone.js": "^0.8.14",
"file-saver": "^1.3.3"
"zone.js": "^0.8.14"
},
"devDependencies": {
"@angular/cli": "1.5.0",
"@angular/cli": "^1.5.3",
"@angular/compiler-cli": "^5.0.0",
"@angular/language-service": "^5.0.0",
"@types/node": "~6.0.60",

@ -4,8 +4,6 @@ import { Component } from '@angular/core';
import * as XLSX from 'xlsx';
import { saveAs } from 'file-saver';
type AOA = any[][];
@Component({
@ -57,7 +55,6 @@ export class SheetJSComponent {
XLSX.utils.book_append_sheet(wb, ws, 'Sheet1');
/* save to file */
const wbout: ArrayBuffer = XLSX.write(wb, this.wopts);
saveAs(new Blob([wbout], { type: 'application/octet-stream' }), this.fileName);
XLSX.writeFile(wb, this.fileName);
}
}

@ -9,42 +9,42 @@ var client = redis.createClient();
/* Sample data */
var init = [
["FLUSHALL", []],
["SADD", ["birdpowers", "flight", "pecking"]],
["SET", ["foo", "bar"]],
["SET", ["baz", 0]],
["RPUSH", ["friends", "sam", "alice", "bob"]],
["ZADD", ["hackers", 1906, 'Grace Hopper', 1912, 'Alan Turing', 1916, 'Claude Shannon', 1940, 'Alan Kay', 1953, 'Richard Stallman', 1957, 'Sophie Wilson', 1965, 'Yukihiro Matsumoto', 1969, 'Linus Torvalds']],
["SADD", ["superpowers", "flight", 'x-ray vision']],
["HMSET", ["user:1000", "name", 'John Smith', "email", 'john.smith@example.com', "password", "s3cret", "visits", 1]],
["HMSET", ["user:1001", "name", 'Mary Jones', "email", 'mjones@example.com', "password", "hidden"]]
["FLUSHALL", []],
["SADD", ["birdpowers", "flight", "pecking"]],
["SET", ["foo", "bar"]],
["SET", ["baz", 0]],
["RPUSH", ["friends", "sam", "alice", "bob"]],
["ZADD", ["hackers", 1906, 'Grace Hopper', 1912, 'Alan Turing', 1916, 'Claude Shannon', 1940, 'Alan Kay', 1953, 'Richard Stallman', 1957, 'Sophie Wilson', 1965, 'Yukihiro Matsumoto', 1969, 'Linus Torvalds']],
["SADD", ["superpowers", "flight", 'x-ray vision']],
["HMSET", ["user:1000", "name", 'John Smith', "email", 'john.smith@example.com', "password", "s3cret", "visits", 1]],
["HMSET", ["user:1001", "name", 'Mary Jones', "email", 'mjones@example.com', "password", "hidden"]]
];
const R = (()=>{
const Rcache = {};
const R_ = (n) => Rcache[n] || (Rcache[n] = util.promisify(client[n]).bind(client));
return (n) => R_(n.toLowerCase());
const Rcache = {};
const R_ = (n) => Rcache[n] || (Rcache[n] = util.promisify(client[n]).bind(client));
return (n) => R_(n.toLowerCase());
})();
(async () => {
for(var i = 0; i < init.length; ++i) await R(init[i][0])(init[i][1]);
for(var i = 0; i < init.length; ++i) await R(init[i][0])(init[i][1]);
/* Export database to XLSX */
var wb = await SheetJSRedis.redis_to_wb(R);
var wb = await SheetJSRedis.redis_to_wb(R);
XLSX.writeFile(wb, "redis.xlsx");
/* Import XLSX to database */
await R("flushall")();
await R("flushall")();
var wb2 = XLSX.readFile("redis.xlsx");
await SheetJSRedis.wb_to_redis(wb2, R);
await SheetJSRedis.wb_to_redis(wb2, R);
/* Verify */
assert.equal(await R("get")("foo"), "bar");
assert.equal(await R("lindex")("friends", 1), "alice");
assert.equal(await R("zscore")("hackers", "Claude Shannon"), 1916);
assert.equal(await R("hget")("user:1000", "name"), "John Smith");
assert.equal(await R("sismember")("superpowers", "flight"), "1");
assert.equal(await R("sismember")("birdpowers", "pecking"), "1");
/* Verify */
assert.equal(await R("get")("foo"), "bar");
assert.equal(await R("lindex")("friends", 1), "alice");
assert.equal(await R("zscore")("hackers", "Claude Shannon"), 1916);
assert.equal(await R("hget")("user:1000", "name"), "John Smith");
assert.equal(await R("sismember")("superpowers", "flight"), "1");
assert.equal(await R("sismember")("birdpowers", "pecking"), "1");
client.quit();
client.quit();
})();

@ -6,68 +6,68 @@ const pair = (arr) => arr.map((x,i)=>!(i%2)&&[x,+arr[i+1]]).filter(x=>x);
const keyify = (obj) => Object.keys(obj).map(x => [x, obj[x]]);
async function redis_to_wb(R) {
var wb = XLSX.utils.book_new();
var manifest = [], strs = [];
var wb = XLSX.utils.book_new();
var manifest = [], strs = [];
/* store strings in strs and keep note of other objects in manifest */
var keys = await R("keys")("*"), type = "";
for(var i = 0; i < keys.length; ++i) {
type = await R("type")(keys[i]);
switch(type) {
case "string": strs.push({key:keys[i], value: await R("get")(keys[i])}); break;
case "list": case "zset": case "set": case "hash": manifest.push({key:keys[i], type:type}); break;
default: throw new Error("bad type " + type);
}
}
/* store strings in strs and keep note of other objects in manifest */
var keys = await R("keys")("*"), type = "";
for(var i = 0; i < keys.length; ++i) {
type = await R("type")(keys[i]);
switch(type) {
case "string": strs.push({key:keys[i], value: await R("get")(keys[i])}); break;
case "list": case "zset": case "set": case "hash": manifest.push({key:keys[i], type:type}); break;
default: throw new Error("bad type " + type);
}
}
/* add worksheets if relevant */
if(strs.length > 0) {
var wss = XLSX.utils.json_to_sheet(strs, {header: ["key", "value"], skipHeader:1});
XLSX.utils.book_append_sheet(wb, wss, "_strs");
}
if(manifest.length > 0) {
var wsm = XLSX.utils.json_to_sheet(manifest, {header: ["key", "type"]});
XLSX.utils.book_append_sheet(wb, wsm, "_manifest");
}
for(i = 0; i < manifest.length; ++i) {
var sn = "obj" + i;
var aoa, key = manifest[i].key;
switch((type=manifest[i].type)) {
case "list":
aoa = (await R("lrange")(key, 0, -1)).map(x => [x]); break;
case "set":
aoa = (await R("smembers")(key)).map(x => [x]); break;
case "zset":
aoa = pair(await R("zrange")(key, 0, -1, "withscores")); break;
case "hash":
aoa = keyify(await R("hgetall")(key)); break;
default: throw new Error("bad type " + type);
}
XLSX.utils.book_append_sheet(wb, XLSX.utils.aoa_to_sheet(aoa), sn);
}
return wb;
/* add worksheets if relevant */
if(strs.length > 0) {
var wss = XLSX.utils.json_to_sheet(strs, {header: ["key", "value"], skipHeader:1});
XLSX.utils.book_append_sheet(wb, wss, "_strs");
}
if(manifest.length > 0) {
var wsm = XLSX.utils.json_to_sheet(manifest, {header: ["key", "type"]});
XLSX.utils.book_append_sheet(wb, wsm, "_manifest");
}
for(i = 0; i < manifest.length; ++i) {
var sn = "obj" + i;
var aoa, key = manifest[i].key;
switch((type=manifest[i].type)) {
case "list":
aoa = (await R("lrange")(key, 0, -1)).map(x => [x]); break;
case "set":
aoa = (await R("smembers")(key)).map(x => [x]); break;
case "zset":
aoa = pair(await R("zrange")(key, 0, -1, "withscores")); break;
case "hash":
aoa = keyify(await R("hgetall")(key)); break;
default: throw new Error("bad type " + type);
}
XLSX.utils.book_append_sheet(wb, XLSX.utils.aoa_to_sheet(aoa), sn);
}
return wb;
}
/* convert worksheet aoa to specific redis type */
const aoa_to_redis = {
list: async (aoa, R, key) => await R("RPUSH")([key].concat(aoa.map(x=>x[0]))),
zset: async (aoa, R, key) => await R("ZADD" )([key].concat(aoa.reduce((acc,x)=>acc.concat([+x[1], x[0]]), []))),
hash: async (aoa, R, key) => await R("HMSET")([key].concat(aoa.reduce((acc,x)=>acc.concat(x), []))),
set: async (aoa, R, key) => await R("SADD" )([key].concat(aoa.map(x=>x[0])))
list: async (aoa, R, key) => await R("RPUSH")([key].concat(aoa.map(x=>x[0]))),
zset: async (aoa, R, key) => await R("ZADD" )([key].concat(aoa.reduce((acc,x)=>acc.concat([+x[1], x[0]]), []))),
hash: async (aoa, R, key) => await R("HMSET")([key].concat(aoa.reduce((acc,x)=>acc.concat(x), []))),
set: async (aoa, R, key) => await R("SADD" )([key].concat(aoa.map(x=>x[0])))
};
async function wb_to_redis(wb, R) {
if(wb.Sheets._strs) {
var strs = XLSX.utils.sheet_to_json(wb.Sheets._strs, {header:1});
for(var i = 0; i < strs.length; ++i) await R("SET")(strs[i]);
}
if(!wb.Sheets._manifest) return;
var M = XLSX.utils.sheet_to_json(wb.Sheets._manifest);
for(i = 0; i < M.length; ++i) {
var aoa = XLSX.utils.sheet_to_json(wb.Sheets["obj" + i], {header:1});
await aoa_to_redis[M[i].type](aoa, R, M[i].key);
}
if(wb.Sheets._strs) {
var strs = XLSX.utils.sheet_to_json(wb.Sheets._strs, {header:1});
for(var i = 0; i < strs.length; ++i) await R("SET")(strs[i]);
}
if(!wb.Sheets._manifest) return;
var M = XLSX.utils.sheet_to_json(wb.Sheets._manifest);
for(i = 0; i < M.length; ++i) {
var aoa = XLSX.utils.sheet_to_json(wb.Sheets["obj" + i], {header:1});
await aoa_to_redis[M[i].type](aoa, R, M[i].key);
}
}
module.exports = {
redis_to_wb,
wb_to_redis
redis_to_wb,
wb_to_redis
};

@ -49,9 +49,9 @@ function sheet_to_sql(ws, sname, mode) {
var out = [];
var BT = mode == "PGSQL" ? "" : "`";
var Q = mode == "PGSQL" ? "'" : '"';
var R = mode == "PGSQL" ? /'/g : /"/g;
var BT = mode == "PGSQL" ? "" : "`";
var Q = mode == "PGSQL" ? "'" : '"';
var R = mode == "PGSQL" ? /'/g : /"/g;
out.push("DROP TABLE IF EXISTS " + BT + sname + BT );
out.push("CREATE TABLE " + BT + sname + BT + " (" + names.map(function(n, i) { return BT + n + BT + " " + (types[i]||"TEXT"); }).join(", ") + ");" );

@ -82,7 +82,8 @@ var ws = XLSX.utils.aoa_to_sheet(prep(grid.data));
var wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'SheetJS');
/* .. generate download (see documentation for examples) .. */
/* generate download */
XLSX.writeFile(wb, "SheetJS.xlsx");
```
## Additional Features

@ -40,8 +40,7 @@ Use readAsBinaryString: (when available) <input type="checkbox" name="userabs" c
<div id="htmlout"></div>
<br />
<script src="https://unpkg.com/canvas-datagrid/dist/canvas-datagrid.js"></script>
<script type="text/javascript" src="https://rawgit.com/eligrey/Blob.js/master/Blob.js"></script>
<script type="text/javascript" src="https://rawgit.com/eligrey/FileSaver.js/master/FileSaver.js"></script>
<script src="shim.js"></script>
<script src="xlsx.full.min.js"></script>
<script>
/*jshint browser:true */
@ -151,11 +150,7 @@ var export_xlsx = (function() {
XLSX.utils.book_append_sheet(new_wb, new_ws, 'SheetJS');
/* write file and trigger a download */
var wbout = XLSX.write(new_wb, {bookType:'xlsx', bookSST:true, type:'array'});
var fname = 'sheetjs.xlsx';
try {
saveAs(new Blob([wbout],{type:"application/octet-stream"}), fname);
} catch(e) { if(typeof console != 'undefined') console.log(e, wbout); }
XLSX.writeFile(new_wb, 'sheetjs.xlsx', {bookSST:true});
};
})();
</script>

1
demos/datagrid/shim.js Symbolic link

@ -0,0 +1 @@
../../shim.js

@ -6,9 +6,9 @@ start:
init:
if [ ! -e .meteor ]; then meteor create .; fi;
@npm install babel-runtime meteor-node-stubs
@meteor add pfafman:filesaver check
@meteor add check
@mkdir -p node_modules; cd node_modules; ln -s ../../../ xlsx; cd -
.PHONY: lint
lint:
@meteor npm run lint
@meteor npm run lint

@ -61,13 +61,9 @@ const html = document.getElementById('out').innerHTML;
// SERVER SIDE
const wb = XLSX.read(html, { type: 'binary' });
// CLIENT SIDE
const o = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
saveAs(new Blob([o], {type:'application/octet-stream'}), 'sheetjs.xlsx');
XLSX.writeFile(wb, 'sheetjs.xlsx');
```
This demo uses the FileSaver library for writing files, installed through the
[`pfafman:filesaver` wrapper](https://atmospherejs.com/pfafman/filesaver).
## Setup
@ -76,7 +72,6 @@ This tree does not include the `.meteor` structure. Rebuild the project with:
```bash
meteor create .
npm install babel-runtime meteor-node-stubs xlsx
meteor add pfafman:filesaver
meteor
```

@ -1,7 +1,5 @@
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
import XLSX from 'xlsx';
/* note: saveAs is made available via the smart package */
/* global saveAs */
import { Meteor } from 'meteor/meteor';
import { Template } from 'meteor/templating';
@ -13,11 +11,12 @@ Template.sheetjs.events({
/* "Browser file upload form element" from SheetJS README */
const file = event.currentTarget.files[0];
const reader = new FileReader();
const rABS = !!reader.readAsBinaryString;
reader.onload = function(e) {
const data = e.target.result;
const name = file.name;
/* Meteor magic */
Meteor.call('upload', data, name, function(err, wb) {
Meteor.call(rABS ? 'uploadS' : 'uploadU', rABS ? data : new Uint8Array(data), name, function(err, wb) {
if (err) throw err;
/* load the first worksheet */
const ws = wb.Sheets[wb.SheetNames[0]];
@ -27,15 +26,14 @@ Template.sheetjs.events({
document.getElementById('dnload').disabled = false;
});
};
reader.readAsBinaryString(file);
if(rABS) reader.readAsBinaryString(file); else reader.readAsArrayBuffer(file);
},
'click button' () {
const html = document.getElementById('out').innerHTML;
Meteor.call('download', html, function(err, wb) {
if (err) throw err;
/* "Browser download file" from SheetJS README */
const wbout = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
saveAs(new Blob([wbout], { type: 'application/octet-stream' }), 'sheetjs.xlsx');
XLSX.writeFile(wb, 'sheetjs.xlsx');
});
},
});

@ -4,12 +4,17 @@ import { check } from 'meteor/check';
import XLSX from 'xlsx';
Meteor.methods({
upload: (bstr, name) => {
/* read the data and return the workbook object to the frontend */
/* read the data and return the workbook object to the frontend */
uploadS: (bstr, name) => {
check(bstr, String);
check(name, String);
return XLSX.read(bstr, { type: 'binary' });
},
uploadU: (ab, name) => {
check(ab, Uint8Array);
check(name, String);
return XLSX.read(ab, { type: 'array' });
},
download: (html) => {
check(html, String);
let wb;

70
demos/oldie/README.md Normal file

@ -0,0 +1,70 @@
# Internet Explorer
Despite the efforts to deprecate the pertinent operating systems, IE is still
very popular, required for various government and corporate websites throughout
the world. The modern download strategies are not available in older versions
of IE, but there are alternative approaches.
## Strategies
#### IE10 and IE11 File API
As part of the File API implementation, IE10 and IE11 provide the `msSaveBlob`
and `msSaveOrOpenBlob` functions to save blobs to the client computer. This
approach is embedded in `XLSX.writeFile` and no additional shims are necessary.
#### Flash-based Download
It is possible to write to the file system using a SWF. `Downloadify` library
implements one solution. Since a genuine click is required, there is no way to
force a download. The demo generates a button for each desired output format.
#### ActiveX-based Download
Through the `Scripting.FileSystemObject` object model, a script in the VBScript
scripting language can write to an arbitrary path on the filesystem. The shim
includes a special `IE_SaveFile` function to write binary strings to file. It
attempts to write to the Downloads folder or Documents folder or Desktop.
This approach can be triggered, but it requires the user to enable ActiveX. It
is embedded as a strategy in `writeFile` and used only if the shim script is
included in the page and the relevant features are enabled on the target system.
## Demo
The included demo starts from an array of arrays, generating an editable HTML
table with `aoa_to_sheet` and adding it to the page:
```js
var ws = XLSX.utils.aoa_to_sheet(aoa);
var html_string = XLSX.utils.sheet_to_html(ws, { id: "table", editable: true });
document.getElementById("container").innerHTML = html_string;
```
The included download buttons use `table_to_book` to construct a new workbook
based on the table and `writeFile` to force a download:
```js
var elt = document.getElementById('table');
var wb = XLSX.utils.table_to_book(elt, { sheet: "Sheet JS" });
XLSX.writeFile(wb, filename);
```
The shim is included in the HTML page, unlocking the ActiveX pathway if enabled
in browser settings.
The corresponding SWF buttons are displayed in environments where Flash is
available and `Downloadify` is supported. The easiest solution involves writing
to a Base64 string and passing to the library:
```js
Downloadify.create(element_id, {
/* the demo includes the other options required by Downloadify */
filename: "test.xlsx",
data: function() { return XLSX.write(wb, {bookType:"xlsx", type:'base64'}); },
dataType: 'base64'
});
```
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-xlsx?pixel)](https://github.com/SheetJS/js-xlsx)

1
demos/oldie/base64.min.js vendored Normal file

@ -0,0 +1 @@
!function(){function t(t){this.message=t}var r="undefined"!=typeof exports?exports:self,e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";t.prototype=new Error,t.prototype.name="InvalidCharacterError",r.btoa||(r.btoa=function(r){for(var o,n,a=String(r),i=0,c=e,d="";a.charAt(0|i)||(c="=",i%1);d+=c.charAt(63&o>>8-i%1*8)){if(n=a.charCodeAt(i+=.75),n>255)throw new t("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.");o=o<<8|n}return d}),r.atob||(r.atob=function(r){var o=String(r).replace(/=+$/,"");if(o.length%4==1)throw new t("'atob' failed: The string to be decoded is not correctly encoded.");for(var n,a,i=0,c=0,d="";a=o.charAt(c++);~a&&(n=i%4?64*n+a:a,i++%4)?d+=String.fromCharCode(255&n>>(-2*i&6)):0)a=e.indexOf(a);return d})}();

BIN
demos/oldie/download.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

3
demos/oldie/downloadify.min.js vendored Executable file

@ -0,0 +1,3 @@
/* Downloadify 0.2 (c) 2009 by Douglas Neiner. Licensed under the MIT license */
/* See http://github.com/dcneiner/Downloadify for license and more info */
(function(){Downloadify=window.Downloadify={queue:{},uid:new Date().getTime(),getTextForSave:function(a){var b=Downloadify.queue[a];if(b)return b.getData();return""},getFileNameForSave:function(a){var b=Downloadify.queue[a];if(b)return b.getFilename();return""},getDataTypeForSave:function(a){var b=Downloadify.queue[a];if(b)return b.getDataType();return""},saveComplete:function(a){var b=Downloadify.queue[a];if(b)b.complete();return true},saveCancel:function(a){var b=Downloadify.queue[a];if(b)b.cancel();return true},saveError:function(a){var b=Downloadify.queue[a];if(b)b.error();return true},addToQueue:function(a){Downloadify.queue[a.queue_name]=a},getUID:function(a){if(a.id=="")a.id='downloadify_'+Downloadify.uid++;return a.id}};Downloadify.create=function(a,b){var c=(typeof(a)=="string"?document.getElementById(a):a);return new Downloadify.Container(c,b)};Downloadify.Container=function(d,e){var f=this;f.el=d;f.enabled=true;f.dataCallback=null;f.filenameCallback=null;f.data=null;f.filename=null;var g=function(){f.options=e;if(!f.options.append)f.el.innerHTML="";f.flashContainer=document.createElement('span');f.el.appendChild(f.flashContainer);f.queue_name=Downloadify.getUID(f.flashContainer);if(typeof(f.options.filename)==="function")f.filenameCallback=f.options.filename;else if(f.options.filename)f.filename=f.options.filename;if(typeof(f.options.data)==="function")f.dataCallback=f.options.data;else if(f.options.data)f.data=f.options.data;var a={queue_name:f.queue_name,width:f.options.width,height:f.options.height};var b={allowScriptAccess:'always'};var c={id:f.flashContainer.id,name:f.flashContainer.id};if(f.options.enabled===false)f.enabled=false;if(f.options.transparent===true)b.wmode="transparent";if(f.options.downloadImage)a.downloadImage=f.options.downloadImage;swfobject.embedSWF(f.options.swf,f.flashContainer.id,f.options.width,f.options.height,"10",null,a,b,c);Downloadify.addToQueue(f)};f.enable=function(){var a=document.getElementById(f.flashContainer.id);a.setEnabled(true);f.enabled=true};f.disable=function(){var a=document.getElementById(f.flashContainer.id);a.setEnabled(false);f.enabled=false};f.getData=function(){if(!f.enabled)return"";if(f.dataCallback)return f.dataCallback();else if(f.data)return f.data;else return""};f.getFilename=function(){if(f.filenameCallback)return f.filenameCallback();else if(f.filename)return f.filename;else return""};f.getDataType=function(){if(f.options.dataType)return f.options.dataType;return"string"};f.complete=function(){if(typeof(f.options.onComplete)==="function")f.options.onComplete()};f.cancel=function(){if(typeof(f.options.onCancel)==="function")f.options.onCancel()};f.error=function(){if(typeof(f.options.onError)==="function")f.options.onError()};g()};Downloadify.defaultOptions={swf:'media/downloadify.swf',downloadImage:'images/download.png',width:100,height:30,transparent:true,append:false,dataType:"string"}})();if(typeof(jQuery)!="undefined"){(function($){$.fn.downloadify=function(b){return this.each(function(){b=$.extend({},Downloadify.defaultOptions,b);var a=Downloadify.create(this,b);$(this).data('Downloadify',a)})}})(jQuery)};if(typeof(MooTools)!='undefined'){Element.implement({downloadify:function(a){a=$merge(Downloadify.defaultOptions,a);return this.store('Downloadify',Downloadify.create(this,a))}})};

BIN
demos/oldie/downloadify.swf Executable file

Binary file not shown.

122
demos/oldie/index.html Normal file

@ -0,0 +1,122 @@
<!DOCTYPE html>
<!-- (C) 2013-present SheetJS http://sheetjs.com -->
<!-- vim: set ts=2: -->
<html>
<head>
<title>SheetJS JS-XLSX In-Browser HTML Table Export Demo</title>
<meta charset="utf-8" />
<style>
.xport, .btn {
display: inline;
text-align:center;
}
a { text-decoration: none }
#data-table, #data-table th, #data-table td { border: 1px solid black }
</style>
</head>
<body>
<!-- SheetJS js-xlsx library -->
<script type="text/javascript" src="shim.min.js"></script>
<script type="text/javascript" src="xlsx.full.min.js"></script>
<!-- Downloadify Flash fallback for IE 9 and below if ActiveX is unavailable -->
<!--[if lte IE 9]>
<script type="text/javascript" src="swfobject.js"></script>
<script type="text/javascript" src="downloadify.min.js"></script>
<script type="text/javascript" src="base64.min.js"></script>
<![endif]-->
<script>
function doit(type, fn, dl) {
var elt = document.getElementById('data-table');
var wb = XLSX.utils.table_to_book(elt, {sheet:"Sheet JS"});
return dl ?
XLSX.write(wb, {bookType:type, bookSST:true, type: 'base64'}) :
XLSX.writeFile(wb, fn || ('test.' + (type || 'xlsx')));
}
</script>
<pre>
<h3><a href="//sheetjs.com/">SheetJS</a> JS-XLSX In-Browser HTML Table Export Demo</h3>
<b>Compatibility notes:</b>
- Editable table leverages the HTML5 contenteditable feature, supported in most browsers.
- IE6-9 requires ActiveX or Flash to download files.
- iOS Safari file download may not work. <a href="http://git.io/ios_save">This is a known issue</a>.
<b>Editable Data Table:</b> (click a cell to edit it)
</pre>
<div id="container"></div>
<script type="text/javascript">
/* initial table */
var aoa = [
["This", "is", "a", "Test"],
["வணக்கம்", "สวัสดี", "你好", "가지마"],
[1, 2, 3, 4],
["Click", "to", "edit", "cells"]
];
var ws = XLSX.utils.aoa_to_sheet(aoa);
var html_string = XLSX.utils.sheet_to_html(ws, { id: "data-table", editable: true });
document.getElementById("container").innerHTML = html_string;
</script>
<br />
<pre><b>Export it!</b></pre>
<table id="xport">
<tr><td><pre>XLSX Excel 2007+ XML</pre></td><td>
<p id="xportxlsx" class="xport"><input type="submit" value="Export to XLSX!" onclick="doit('xlsx');"></p>
<p id="xlsxbtn" class="btn">Flash required for actually downloading the generated file.</p>
</td></tr>
<tr><td><pre>XLSB Excel 2007+ Binary</pre></td><td>
<p id="xportxlsb" class="xport"><input type="submit" value="Export to XLSB!" onclick="doit('xlsb');"></p>
<p id="xlsbbtn" class="btn">Flash required for actually downloading the generated file.</p>
</td></tr>
<tr><td><pre>XLS Excel 97-2004 Binary</pre></td><td>
<p id="xportbiff8" class="xport"><input type="submit" value="Export to XLS!" onclick="doit('biff8', 'test.xls');"></p>
<p id="biff8btn" class="btn">Flash required for actually downloading the generated file.</p>
</td></tr>
<tr><td><pre>ODS</pre></td><td>
<p id="xportods" class="xport"><input type="submit" value="Export to ODS!" onclick="doit('ods');"></p>
<p id="odsbtn" class="btn">Flash required for actually downloading the generated file.</p>
</td></tr>
<tr><td><pre>Flat ODS</pre></td><td>
<p id="xportfods" class="xport"><input type="submit" value="Export to FODS!" onclick="doit('fods', 'test.fods');"></p>
<p id="fodsbtn" class="btn">Flash required for actually downloading the generated file.</p>
</td></tr>
</table>
<pre><b>Powered by the <a href="//sheetjs.com/opensource">community version of js-xlsx</a></b></pre>
<script type="text/javascript">
function tableau(pid, iid, fmt, ofile) {
if(typeof Downloadify !== 'undefined') Downloadify.create(pid,{
swf: 'downloadify.swf',
downloadImage: 'download.png',
width: 100,
height: 30,
filename: ofile, data: function() { return doit(fmt, ofile, true); },
transparent: false,
append: false,
dataType: 'base64',
onComplete: function(){ alert('Your File Has Been Saved!'); },
onCancel: function(){ alert('You have cancelled the saving of this file.'); },
onError: function(){ alert('You must put something in the File Contents or there will be nothing to save!'); }
}); else document.getElementById(pid).innerHTML = "";
}
tableau('biff8btn', 'xportbiff8', 'biff8', 'test.xls');
tableau('odsbtn', 'xportods', 'ods', 'test.ods');
tableau('fodsbtn', 'xportfods', 'fods', 'test.fods');
tableau('xlsbbtn', 'xportxlsb', 'xlsb', 'test.xlsb');
tableau('xlsxbtn', 'xportxlsx', 'xlsx', 'test.xlsx');
</script>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-36810333-1']);
_gaq.push(['_setDomainName', 'sheetjs.com']);
_gaq.push(['_setAllowLinker', true]);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</body>
</html>

1
demos/oldie/shim.min.js vendored Symbolic link

@ -0,0 +1 @@
../../dist/shim.min.js

4
demos/oldie/swfobject.js Executable file

File diff suppressed because one or more lines are too long

1
demos/oldie/xlsx.full.min.js vendored Symbolic link

@ -0,0 +1 @@
../../dist/xlsx.full.min.js

@ -1,2 +1,3 @@
SheetJS
.next
static/shim.js

@ -4,8 +4,9 @@ react: ## Simple server for react and clones
.PHONY: next
next: init ## next.js demo
mkdir -p pages
mkdir -p pages static
cat nexthdr.js sheetjs.jsx > pages/sheetjs.js
cp ../../shim.js static/shim.js
next
.PHONY: native
@ -24,4 +25,3 @@ android: native ## react-native android sim
init: ## set up node_modules and symlink
mkdir -p node_modules
cd node_modules; if [ ! -e xlsx ]; then ln -s ../../../ xlsx; fi; cd -
if [ ! -e node_modules/file-saver ]; then npm install file-saver; fi

@ -9,8 +9,8 @@
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react-dom.min.js"></script>
<script src="node_modules/xlsx/dist/shim.min.js"></script>
<script src="node_modules/xlsx/dist/xlsx.full.min.js"></script>
<script src="https://unpkg.com/file-saver/FileSaver.js"></script>
<style>body, #app { height: 100%; };</style>
</head>
<body>

@ -1,3 +1,2 @@
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
import XLSX from 'xlsx';
import { saveAs } from 'file-saver';

@ -6,6 +6,7 @@ export default () => (
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>SheetJS React Demo</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" />
<script src="/static/shim.js"></script>
<style jsx>{`
body, #app { height: 100%; };
`}</style>

@ -4,20 +4,20 @@
<html lang="en" style="height: 100%">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>SheetJS React Demo</title>
<title>SheetJS Preact Demo</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" />
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script src="//unpkg.com/preact"></script>
<script src="//unpkg.com/proptypes"></script>
<script src="//unpkg.com/preact-compat"></script>
<script>var React = preactCompat, ReactDOM = preactCompat;</script>
<script src="https://unpkg.com/xlsx/dist/xlsx.full.min.js"></script>
<script src="https://unpkg.com/file-saver/FileSaver.js"></script>
<script src="node_modules/xlsx/dist/shim.min.js"></script>
<script src="node_modules/xlsx/dist/xlsx.full.min.js"></script>
<style>body, #app { height: 100%; };</style>
</head>
<body>
<div class="container-fluid">
<h1><a href="http://sheetjs.com">SheetJS React Demo</a></h1>
<h1><a href="http://sheetjs.com">SheetJS Preact Demo</a></h1>
<br />
<a href="https://github.com/SheetJS/js-xlsx">Source Code Repo</a><br />
<a href="https://github.com/SheetJS/js-xlsx/issues">Issues? Something look weird? Click here and report an issue</a><br /><br />

@ -18,10 +18,11 @@ class SheetJSApp extends React.Component {
handleFile(file/*:File*/) {
/* Boilerplate to set up FileReader */
const reader = new FileReader();
const rABS = !!reader.readAsBinaryString;
reader.onload = (e) => {
/* Parse data */
const bstr = e.target.result;
const wb = XLSX.read(bstr, {type:'binary'});
const wb = XLSX.read(bstr, {type:rABS ? 'binary' : 'array'});
/* Get first worksheet */
const wsname = wb.SheetNames[0];
const ws = wb.Sheets[wsname];
@ -30,17 +31,15 @@ class SheetJSApp extends React.Component {
/* Update state */
this.setState({ data: data, cols: make_cols(ws['!ref']) });
};
reader.readAsBinaryString(file);
if(rABS) reader.readAsBinaryString(file); else reader.readAsArrayBuffer(file);
};
exportFile() {
/* convert state to workbook */
const ws = XLSX.utils.aoa_to_sheet(this.state.data);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "SheetJS");
/* generate XLSX file */
const wbout = XLSX.write(wb, {type:"array", bookType:"xlsx"});
/* send to client */
saveAs(new Blob([wbout],{type:"application/octet-stream"}), "sheetjs.xlsx");
/* generate XLSX file and send to client */
XLSX.writeFile(wb, "sheetjs.xlsx")
};
render() { return (
<DragDropFile handleFile={this.handleFile}>
@ -137,4 +136,8 @@ const SheetJSFT = [
].map(function(x) { return "." + x; }).join(",");
/* generate an array of column objects */
const make_cols = refstr => Array(XLSX.utils.decode_range(refstr).e.c + 1).fill(0).map((x,i) => ({name:XLSX.utils.encode_col(i), key:i}));
const make_cols = refstr => {
let o = [], C = XLSX.utils.decode_range(refstr).e.c + 1;
for(var i = 0; i < C; ++i) o[i] = {name:XLSX.utils.encode_col(i), key:i}
return o;
};

@ -33,6 +33,23 @@ var buf = fs.readFileSync("sheetjs.xlsx");
var wb = XLSX.read(buf, {type:'buffer'});
```
### Responding to Form Uploads
Using `formidable`, files uploaded to forms are stored to temporary files that
can be read with `readFile`:
```js
/* within the server callback function(request, response) { */
var form = new formidable.IncomingForm();
form.parse(req, function(err, fields, files) {
var f = files[Object.keys(files)[0]];
var workbook = XLSX.readFile(f.path);
/* DO SOMETHING WITH workbook HERE */
});
```
The `node.js` demo shows a plain HTTP server that accepts file uploads and
converts data to requested output format.
### Example servers

84
demos/server/node.js Normal file

@ -0,0 +1,84 @@
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
var http = require('http');
var XLSX = require('xlsx');
var formidable = require('formidable');
var html = "";
var PORT = 3000;
var extmap = {};
var server = http.createServer(function(req, res) {
if(req.method !== 'POST') return res.end(html);
var form = new formidable.IncomingForm();
form.parse(req, function(err, fields, files) {
var f = files[Object.keys(files)[0]];
var wb = XLSX.readFile(f.path);
var ext = (fields.bookType || "xlsx").toLowerCase();
res.setHeader('Content-Disposition', 'attachment; filename="download.' + (extmap[ext] || ext) + '";');
res.end(XLSX.write(wb, {type:"buffer", bookType:ext}));
});
}).listen(PORT);
html = [
'<pre>',
'<h3><a href="http://sheetjs.com/">SheetJS File Converter</a></h3>',
'Upload a file to convert the contents to another format.',
'',
'<b>Form Fields</b>:',
'- bookType: output format type (defaults to "XLSX")',
'- basename: basename for output file (defaults to "download")',
'',
'<form method="POST" enctype="multipart/form-data" action="/">',
'<input type="file" id="file" name="file"/>',
'<select name="bookType">',
[
["xlsb", "XLSB"],
["xlsx", "XLSX"],
["xlsm", "XLSM"],
["biff8", "BIFF8 XLS"],
["biff5", "BIFF5 XLS"],
["biff2", "BIFF2 XLS"],
["xlml", "SSML 2003"],
["ods", "ODS"],
["fods", "Flat ODS"],
["csv", "CSV"],
["txt", "Unicode Text"],
["sylk", "Symbolic Link"],
["html", "HTML"],
["dif", "DIF"],
["dbf", "DBF"],
["rtf", "RTF"],
["prn", "Lotus PRN"],
["eth", "Ethercalc"],
].map(function(x) { return ' <option value="' + x[0] + '">' + x[1] + '</option>'; }).join("\n"),
'</select>',
'<input type="submit" value="Submit Form">',
'</form>',
'',
'<b>Form code:</b>',
'&lt;form method="POST" enctype="multipart/form-data" action="/"&gt;',
'&lt;input type="file" id="file" name="file"/&gt;',
'&lt;select name="bookType"&gt',
'&lt;!-- options here --&gt;',
'&lt;/select&gt',
'&lt;input type="submit" value="Submit Form"&gt;',
'&lt;/form&gt;',
'',
'<b>fetch Code:</b>',
'var blob = new Blob("1,2,3\\n4,5,6".split("")); // original file',
'var fd = new FormData();',
'fd.set("data", blob, "foo.bar");',
'fd.set("bookType", "xlsb");',
'var res = await fetch("/", {method:"POST", body:fd});',
'var data = await res.arrayBuffer();',
'</pre>'
].join("\n");
extmap = {
"biff2" : "xls",
"biff5" : "xls",
"biff8" : "xls",
"xlml" : "xls"
};
console.log('listening on port ' + PORT);

@ -1,3 +1,9 @@
.PHONY: all
all:
npm run build
.PHONY: init
init:
mkdir -p node_modules
npm install typescript
cd node_modules; ln -s ../../../ xlsx; cd -

@ -101,8 +101,8 @@ The scripts should be treated as external resources in `nuxt.config.js`:
module.exports = {
head: {
script: [
{ src: "https://unpkg.com/xlsx/dist/xlsx.full.min.js" }, // library
{ src: "https://unpkg.com/file-saver/FileSaver.js" } // saveAs shim
{ src: "https://unpkg.com/xlsx/dist/shim.min.js" },
{ src: "https://unpkg.com/xlsx/dist/xlsx.full.min.js" }
]
}
};

@ -55,11 +55,8 @@ Vue.component('html-preview', {
onexport: function(evt) {
/* generate workbook object from table */
var wb = XLSX.utils.table_to_book(document.getElementById('out-table'));
/* get binary string as output */
var wbout = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
/* force a download */
saveAs(new Blob([wbout], { type: 'application/octet-stream' }), "sheetjs.xlsx");
/* generate file and force a download*/
XLSX.writeFile(wb, "sheetjs.xlsx");
}
}
});

@ -7,8 +7,8 @@
<!-- Vue 2 -->
<script src="https://unpkg.com/vue@2.x"></script>
<!-- FileSaver shim for exporting files -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.3/FileSaver.min.js"></script>
<!-- Various shims -->
<script src="shim.js"></script>
<!-- SheetJS js-xlsx library -->
<script src="xlsx.full.min.js"></script>

@ -1,9 +1,10 @@
module.exports = {
head: {
script: [
// { src: "https://unpkg.com/xlsx/dist/xlsx.full.min.js" }, // CDN
{ src: "xlsx.full.min.js" }, // development
{ src: "https://unpkg.com/file-saver/FileSaver.js" }
// { src: "https://unpkg.com/xlsx/dist/shim.min.js" }, // CDN
// { src: "https://unpkg.com/xlsx/dist/xlsx.full.min.js" } // CDN
{ src: "shim.js" }, // development
{ src: "xlsx.full.min.js" } // development
]
}
};

@ -66,10 +66,8 @@ export default {
const ws = XLSX.utils.aoa_to_sheet(this.data);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "SheetJS");
/* generate X file */
const wbout = XLSX.write(wb, {type:"array", bookType:"xlsx"});
/* send to client */
saveAs(new Blob([wbout],{type:"application/octet-stream"}), "sheetjs.xlsx");
/* generate file and send to client */
XLSX.writeFile(wb, "sheetjs.xlsx");
},
_file(file) {
/* Boilerplate to set up FileReader */

1
demos/vue/shim.js Symbolic link

@ -0,0 +1 @@
../../shim.js

1
demos/vue/static/shim.js Symbolic link

@ -0,0 +1 @@
../shim.js

@ -87,7 +87,7 @@ The upload portion only differs in the actual request command:
superagent.post("/upload").send(fd);
```
### superagent Wrapper Library
### axios Wrapper Library
The `axios` library presents a Promise interface. The axios demo uses a single
promise, but for production deployments it may make sense to separate parsing:

2
dist/shim.min.js generated vendored

File diff suppressed because one or more lines are too long

26
dist/xlsx.core.min.js generated vendored

File diff suppressed because one or more lines are too long

2
dist/xlsx.core.min.map generated vendored

File diff suppressed because one or more lines are too long

30
dist/xlsx.full.min.js generated vendored

File diff suppressed because one or more lines are too long

2
dist/xlsx.full.min.map generated vendored

File diff suppressed because one or more lines are too long

75
dist/xlsx.js generated vendored

@ -4,7 +4,7 @@
/*global global, exports, module, require:false, process:false, Buffer:false, ArrayBuffer:false */
var XLSX = {};
(function make_xlsx(XLSX){
XLSX.version = '0.11.18';
XLSX.version = '0.11.19';
var current_codepage = 1200, current_ansi = 1252;
/*global cptable:true */
if(typeof module !== "undefined" && typeof require !== 'undefined') {
@ -140,11 +140,16 @@ function s2ab(s) {
return buf;
}
function arr2str(data) {
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));
@ -1782,6 +1787,39 @@ 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 */
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 a = document.createElement("a");
if(a.download != null) {
var url = URL.createObjectURL(blob);
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;
}
}
}
throw new Error("cannot initiate download");
}
function keys(o) { return Object.keys(o); }
function evert_key(obj, key) {
@ -1973,13 +2011,12 @@ function getzipstr(zip, file, safe) {
try { return getzipstr(zip, file); } catch(e) { return null; }
}
var _fs, jszip;
var jszip;
/*global JSZip:true */
if(typeof JSZip !== 'undefined') jszip = JSZip;
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
if(typeof exports !== 'undefined') {
if(typeof module !== 'undefined' && module.exports) {
if(typeof jszip === 'undefined') jszip = undefined;
try { _fs = require('fs'); } catch(e) { }
}
}
@ -13286,6 +13323,7 @@ function check_wb_names(N) {
}
function check_wb(wb) {
if(!wb || !wb.SheetNames || !wb.Sheets) throw new Error("Invalid Workbook");
if(!wb.SheetNames.length) throw new Error("Workbook is empty");
check_wb_names(wb.SheetNames);
/* TODO: validate workbook */
}
@ -14723,7 +14761,7 @@ function parse_xlml(data, opts) {
switch(opts.type||"base64") {
case "base64": return parse_xlml_xml(Base64.decode(data), opts);
case "binary": case "buffer": case "file": return parse_xlml_xml(data, opts);
case "array": return parse_xlml_xml(arr2str(data), opts);
case "array": return parse_xlml_xml(a2s(data), opts);
}
}
@ -17536,9 +17574,9 @@ var HTML_ = (function() {
var preamble = "<tr>";
return preamble + oo.join("") + "</tr>";
}
function make_html_preamble() {
function make_html_preamble(ws, R, o) {
var out = [];
return out.join("") + '<table>';
return out.join("") + '<table' + (o && o.id ? ' id="' + o.id + '"' : "") + '>';
}
var _BEGIN = '<html><head><meta charset="utf-8"/><title>SheetJS Table Export</title></head><body>';
var _END = '</body></html>';
@ -18899,7 +18937,6 @@ function readSync(data, opts) {
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);
if(0x20>n[0]||n[0]>0x7F) throw new Error("Unsupported file " + n.join("|"));
return read_prn(data, d, o, str);
}
@ -18915,12 +18952,12 @@ function write_zip_type(wb, opts) {
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 "string": throw new Error("'string' output type invalid for '" + o.bookType + "' files");
case "buffer":
case "file": oopts.type = "nodebuffer"; break;
case "file": oopts.type = has_buf ? "nodebuffer" : "string"; break;
default: throw new Error("Unrecognized type " + o.type);
}
if(o.type === "file") return _fs.writeFileSync(o.file, z.generate(oopts));
if(o.type === "file") return write_dl(o.file, z.generate(oopts));
var out = z.generate(oopts);
// $FlowIgnore
return o.type == "string" ? utf8read(out) : out;
@ -18932,8 +18969,8 @@ function write_cfb_type(wb, opts) {
switch(o.type) {
case "base64": case "binary": break;
case "buffer": case "array": o.type = ""; break;
case "file": return _fs.writeFileSync(o.file, CFB.write(cfb, {type:'buffer'}));
case "string": throw new Error("'string' output type invalid for '" + o.bookType + ' files');
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);
@ -18946,7 +18983,7 @@ function write_string_type(out, opts, bom) {
case "base64": return Base64.encode(utf8write(o));
case "binary": return utf8write(o);
case "string": return out;
case "file": return _fs.writeFileSync(opts.file, o, 'utf8');
case "file": return write_dl(opts.file, o, 'utf8');
case "buffer": {
if(has_buf) return new Buffer(o, 'utf8');
else return write_string_type(o, {type:'binary'}).split("").map(function(c) { return c.charCodeAt(0); });
@ -18960,7 +18997,7 @@ function write_stxt_type(out, opts) {
case "base64": return Base64.encode(out);
case "binary": return out;
case "string": return out; /* override in sheet_to_txt */
case "file": return _fs.writeFileSync(opts.file, out, 'binary');
case "file": return write_dl(opts.file, out, 'binary');
case "buffer": {
if(has_buf) return new Buffer(out, 'binary');
else return out.split("").map(function(c) { return c.charCodeAt(0); });
@ -18979,7 +19016,7 @@ function write_binary_type(out, opts) {
// $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 _fs.writeFileSync(opts.file, out);
case "file": return write_dl(opts.file, out);
case "buffer": return out;
default: throw new Error("Unrecognized type " + opts.type);
}
@ -19182,7 +19219,7 @@ function sheet_to_txt(sheet, opts) {
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 "\xff\xfe" + o;
return String.fromCharCode(255) + String.fromCharCode(254) + o;
}
function sheet_to_formulae(sheet) {

24
dist/xlsx.min.js generated vendored

File diff suppressed because one or more lines are too long

2
dist/xlsx.min.map generated vendored

File diff suppressed because one or more lines are too long

@ -29,4 +29,5 @@ The [`demos` directory](demos/) includes sample projects for:
- [`Headless Browsers`](demos/headless/)
- [`canvas-datagrid`](demos/datagrid/)
- [`Swift JSC and other engines`](demos/altjs/)
- [`internet explorer`](demos/oldie/)

@ -41,7 +41,7 @@ var worksheet = XLSX.read(htmlstr, {type:'string'});
<summary><b>Browser download file (ajax)</b> (click to show)</summary>
Note: for a more complete example that works in older browsers, check the demo
at <http://oss.sheetjs.com/js-xlsx/ajax.html>). The <demos/xhr/> directory also
at <http://oss.sheetjs.com/js-xlsx/ajax.html>). The [`xhr` demo](demos/xhr/)
includes more examples with `XMLHttpRequest` and `fetch`.
```js
@ -116,6 +116,8 @@ input_dom_element.addEventListener('change', handleFile, false);
</details>
More specialized cases, including mobile app file processing, are covered in the
[included demos](demos/)
### Parsing Examples

@ -8,8 +8,7 @@ Assuming `workbook` is a workbook object:
<details>
<summary><b>nodejs write a file</b> (click to show)</summary>
`writeFile` is only available in server environments. Browsers have no API for
writing arbitrary files given a path, so another strategy must be used.
`XLSX.writeFile` uses `fs.writeFileSync` in server environments:
```js
if(typeof require !== 'undefined') XLSX = require('xlsx');
@ -21,7 +20,7 @@ XLSX.writeFile(workbook, 'out.xlsb');
</details>
<details>
<summary><b>Browser add to web page</b> (click to show)</summary>
<summary><b>Browser add TABLE element to page</b> (click to show)</summary>
The `sheet_to_html` utility function generates HTML code that can be added to
any DOM element.
@ -32,29 +31,10 @@ var container = document.getElementById('tableau');
container.innerHTML = XLSX.utils.sheet_to_html(worksheet);
```
</details>
<details>
<summary><b>Browser save file</b> (click to show)</summary>
Note: browser generates binary blob and forces a "download" to client. This
example uses [FileSaver](https://github.com/eligrey/FileSaver.js/):
```js
/* bookType can be any supported output type */
var wopts = { bookType:'xlsx', bookSST:false, type:'array' };
var wbout = XLSX.write(workbook,wopts);
/* the saveAs call downloads a file on the local machine */
saveAs(new Blob([wbout],{type:"application/octet-stream"}), "test.xlsx");
```
</details>
<details>
<summary><b>Browser upload to server</b> (click to show)</summary>
<summary><b>Browser upload file (ajax)</b> (click to show)</summary>
A complete example using XHR is [included in the XHR demo](demos/xhr/), along
with examples for fetch and wrapper libraries. This example assumes the server
@ -76,6 +56,65 @@ req.send(formdata);
</details>
<details>
<summary><b>Browser save file</b> (click to show)</summary>
`XLSX.writeFile` wraps a few techniques for triggering a file save:
- `URL` browser API creates an object URL for the file, which the library uses
by creating a link and forcing a click. It is supported in modern browsers.
- `msSaveBlob` is an IE10+ API for triggering a file save.
- `IE_FileSave` uses VBScript and ActiveX to write a file in IE6+ for Windows
XP and Windows 7. The shim must be included in the containing HTML page.
There is no standard way to determine if the actual file has been downloaded.
```js
/* output format determined by filename */
XLSX.writeFile(workbook, 'out.xlsb');
/* at this point, out.xlsb will have been downloaded */
```
</details>
<details>
<summary><b>Browser save file (compatibility)</b> (click to show)</summary>
`XLSX.writeFile` techniques work for most modern browsers as well as older IE.
For much older browsers, there are workarounds implemented by wrapper libraries.
[`FileSaver.js`](https://github.com/eligrey/FileSaver.js/) implements `saveAs`.
Note: `XLSX.writeFile` will automatically call `saveAs` if available.
```js
/* bookType can be any supported output type */
var wopts = { bookType:'xlsx', bookSST:false, type:'array' };
var wbout = XLSX.write(workbook,wopts);
/* the saveAs call downloads a file on the local machine */
saveAs(new Blob([wbout],{type:"application/octet-stream"}), "test.xlsx");
```
[`Downloadify`](https://github.com/dcneiner/downloadify) uses a Flash SWF button
to generate local files, suitable for environments where ActiveX is unavailable:
```js
Downloadify.create(id,{
/* other options are required! read the downloadify docs for more info */
filename: "test.xlsx",
data: function() { return XLSX.write(wb, {bookType:"xlsx", type:'base64'}); },
append: false,
dataType: 'base64'
});
```
The [`oldie` demo](demos/oldie/) shows an IE-compatible fallback scenario.
</details>
The [included demos](demos/) cover mobile apps and other special deployments.
### Writing Examples
- <http://sheetjs.com/demos/table.html> exporting an HTML table

@ -18,7 +18,8 @@ Parse options are described in the [Parsing Options](#parsing-options) section.
`XLSX.write(wb, write_opts)` attempts to write the workbook `wb`
`XLSX.writeFile(wb, filename, write_opts)` attempts to write `wb` to `filename`
`XLSX.writeFile(wb, filename, write_opts)` attempts to write `wb` to `filename`.
In browser-based environments, it will attempt to force a client-side download.
`XLSX.writeFileAsync(filename, wb, o, cb)` attempts to write `wb` to `filename`.
If `o` is omitted, the writer will use the third argument as the callback.

@ -310,6 +310,7 @@ produces HTML output. The function takes an options argument:
| Option Name | Default | Description |
| :---------- | :------: | :-------------------------------------------------- |
|`id` | | Specify the `id` attribute for the `TABLE` element |
|`editable` | false | If true, set `contenteditable="true"` for every TD |
|`header` | | Override header (default `html body`) |
|`footer` | | Override footer (default `/body /html`) |

@ -206,6 +206,7 @@ The [`demos` directory](demos/) includes sample projects for:
- [`Headless Browsers`](demos/headless/)
- [`canvas-datagrid`](demos/datagrid/)
- [`Swift JSC and other engines`](demos/altjs/)
- [`internet explorer`](demos/oldie/)
### Optional Modules
@ -316,7 +317,7 @@ var worksheet = XLSX.read(htmlstr, {type:'string'});
Note: for a more complete example that works in older browsers, check the demo
at <http://oss.sheetjs.com/js-xlsx/ajax.html>). The <demos/xhr/> directory also
at <http://oss.sheetjs.com/js-xlsx/ajax.html>). The [`xhr` demo](demos/xhr/)
includes more examples with `XMLHttpRequest` and `fetch`.
```js
@ -384,6 +385,8 @@ input_dom_element.addEventListener('change', handleFile, false);
```
More specialized cases, including mobile app file processing, are covered in the
[included demos](demos/)
### Parsing Examples
@ -540,8 +543,7 @@ dissemination. The second step is to actual share the data with the end point.
Assuming `workbook` is a workbook object:
`writeFile` is only available in server environments. Browsers have no API for
writing arbitrary files given a path, so another strategy must be used.
`XLSX.writeFile` uses `fs.writeFileSync` in server environments:
```js
if(typeof require !== 'undefined') XLSX = require('xlsx');
@ -563,22 +565,6 @@ container.innerHTML = XLSX.utils.sheet_to_html(worksheet);
Note: browser generates binary blob and forces a "download" to client. This
example uses [FileSaver](https://github.com/eligrey/FileSaver.js/):
```js
/* bookType can be any supported output type */
var wopts = { bookType:'xlsx', bookSST:false, type:'array' };
var wbout = XLSX.write(workbook,wopts);
/* the saveAs call downloads a file on the local machine */
saveAs(new Blob([wbout],{type:"application/octet-stream"}), "test.xlsx");
```
A complete example using XHR is [included in the XHR demo](demos/xhr/), along
with examples for fetch and wrapper libraries. This example assumes the server
can handle Base64-encoded files (see the demo for a basic nodejs server):
@ -598,6 +584,59 @@ req.send(formdata);
```
`XLSX.writeFile` wraps a few techniques for triggering a file save:
- `URL` browser API creates an object URL for the file, which the library uses
by creating a link and forcing a click. It is supported in modern browsers.
- `msSaveBlob` is an IE10+ API for triggering a file save.
- `IE_FileSave` uses VBScript and ActiveX to write a file in IE6+ for Windows
XP and Windows 7. The shim must be included in the containing HTML page.
There is no standard way to determine if the actual file has been downloaded.
```js
/* output format determined by filename */
XLSX.writeFile(workbook, 'out.xlsb');
/* at this point, out.xlsb will have been downloaded */
```
`XLSX.writeFile` techniques work for most modern browsers as well as older IE.
For much older browsers, there are workarounds implemented by wrapper libraries.
[`FileSaver.js`](https://github.com/eligrey/FileSaver.js/) implements `saveAs`.
Note: `XLSX.writeFile` will automatically call `saveAs` if available.
```js
/* bookType can be any supported output type */
var wopts = { bookType:'xlsx', bookSST:false, type:'array' };
var wbout = XLSX.write(workbook,wopts);
/* the saveAs call downloads a file on the local machine */
saveAs(new Blob([wbout],{type:"application/octet-stream"}), "test.xlsx");
```
[`Downloadify`](https://github.com/dcneiner/downloadify) uses a Flash SWF button
to generate local files, suitable for environments where ActiveX is unavailable:
```js
Downloadify.create(id,{
/* other options are required! read the downloadify docs for more info */
filename: "test.xlsx",
data: function() { return XLSX.write(wb, {bookType:"xlsx", type:'base64'}); },
append: false,
dataType: 'base64'
});
```
The [`oldie` demo](demos/oldie/) shows an IE-compatible fallback scenario.
The [included demos](demos/) cover mobile apps and other special deployments.
### Writing Examples
- <http://sheetjs.com/demos/table.html> exporting an HTML table
@ -642,7 +681,8 @@ Parse options are described in the [Parsing Options](#parsing-options) section.
`XLSX.write(wb, write_opts)` attempts to write the workbook `wb`
`XLSX.writeFile(wb, filename, write_opts)` attempts to write `wb` to `filename`
`XLSX.writeFile(wb, filename, write_opts)` attempts to write `wb` to `filename`.
In browser-based environments, it will attempt to force a client-side download.
`XLSX.writeFileAsync(filename, wb, o, cb)` attempts to write `wb` to `filename`.
If `o` is omitted, the writer will use the third argument as the callback.
@ -1859,6 +1899,7 @@ produces HTML output. The function takes an options argument:
| Option Name | Default | Description |
| :---------- | :------: | :-------------------------------------------------- |
|`id` | | Specify the `id` attribute for the `TABLE` element |
|`editable` | false | If true, set `contenteditable="true"` for every TD |
|`header` | | Override header (default `html body`) |
|`footer` | | Override footer (default `/body /html`) |

@ -1,6 +1,6 @@
{
"name": "xlsx",
"version": "0.11.18",
"version": "0.11.19",
"author": "sheetjs",
"description": "SheetJS Spreadsheet data parser and writer",
"keywords": [

27
shim.js

@ -341,3 +341,30 @@ if(typeof Uint8Array !== 'undefined' && !Uint8Array.prototype.slice) Uint8Array.
while(start <= --end) out[end - start] = this[end];
return out;
};
// VBScript + ActiveX fallback for IE5+
var IE_SaveFile = (function() { try {
if(typeof IE_SaveFile_Impl == "undefined") document.write([
'<script type="text/vbscript" language="vbscript">',
'IE_GetProfileAndPath_Key = "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders\\"',
'Function IE_GetProfileAndPath(key): Set wshell = CreateObject("WScript.Shell"): IE_GetProfileAndPath = wshell.RegRead(IE_GetProfileAndPath_Key & key): IE_GetProfileAndPath = wshell.ExpandEnvironmentStrings("%USERPROFILE%") & "!" & IE_GetProfileAndPath: End Function',
'Function IE_SaveFile_Impl(FileName, payload): Dim data, plen, i, bit: data = CStr(payload): plen = Len(data): Set fso = CreateObject("Scripting.FileSystemObject"): fso.CreateTextFile FileName, True: Set f = fso.GetFile(FileName): Set stream = f.OpenAsTextStream(2, 0): For i = 1 To plen Step 3: bit = Mid(data, i, 2): stream.write Chr(CLng("&h" & bit)): Next: stream.Close: IE_SaveFile_Impl = True: End Function',
'|/script>'.replace("|","<")
].join("\r\n"));
if(typeof IE_SaveFile_Impl == "undefined") return void 0;
var IE_GetPath = (function() {
var DDP1 = "";
try { DDP1 = IE_GetProfileAndPath("{374DE290-123F-4565-9164-39C4925E467B}"); } catch(e) { try { DDP1 = IE_GetProfileAndPath("Personal"); } catch(e) { try { DDP1 = IE_GetProfileAndPath("Desktop"); } catch(e) { throw e; }}}
var o = DDP1.split("!");
DDP = o[1].replace("%USERPROFILE%", o[0]);
return function(path) { return DDP + "\\" + path; };
})();
function fix_data(data) {
var out = [];
var T = typeof data == "string";
for(var i = 0; i < data.length; ++i) out.push(("00"+(T ? data.charCodeAt(i) : data[i]).toString(16)).slice(-2));
var o = out.join("|");
return o;
}
return function(data, filename) { return IE_SaveFile_Impl(IE_GetPath(filename), fix_data(data)); };
} catch(e) { return void 0; }})();

40
test.js

@ -1,7 +1,9 @@
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
/* vim: set ts=2: */
/*jshint mocha:true */
/* eslint-env mocha */
/*global process, document, require */
/*global ArrayBuffer, Uint8Array */
/*::
declare type EmptyFunc = (() => void) | null;
declare type DescribeIt = { (desc:string, test:EmptyFunc):void; skip(desc:string, test:EmptyFunc):void; };
@ -190,7 +192,6 @@ var MCPaths = pathit("mc", ["xlsx", "xlsb", "xls", "xml", "ods"]);
var CSSPaths = pathit("css", ["xlsx", "xlsb", "xls", "xml"]);
var NFPaths = pathit("nf", ["xlsx", "xlsb", "xls", "xml"]);
var DTPaths = pathit("dt", ["xlsx", "xlsb", "xls", "xml"]);
var NFPaths = pathit("nf", ["xlsx", "xlsb", "xls", "xml"]);
var HLPaths = pathit("hl", ["xlsx", "xlsb", "xls", "xml"]);
var ILPaths = pathit("il", ["xlsx", "xlsb", "xls", "xml", "ods", "xls5"]);
var OLPaths = pathit("ol", ["xlsx", "xlsb", "xls", "ods", "xls5"]);
@ -318,12 +319,11 @@ var wbtable = {};
wbtable[dir + x] = wb;
parsetest(x, wb, true);
});
fullex.forEach(function(ext, idx) {
fullex.forEach(function(ext) {
it(x + ' [' + ext + ']', function(){
var wb = wbtable[dir + x];
if(!wb) wb = X.readFile(dir + x, opts);
wb = X.read(X.write(wb, {type:"buffer", bookType:ext.replace(/\./,"")}), {WTF:opts.WTF, cellNF: true});
parsetest(x, wb, ext.replace(/\./,"") !== "xlsb", ext);
});
});
@ -471,7 +471,7 @@ describe('parse options', function() {
[paths.cssxlsx /*, paths.cssxlsb, paths.cssxls, paths.cssxml*/].forEach(function(p) {
var wb = X.read(fs.readFileSync(p), {type:TYPE, cellStyles:true});
var found = false;
each_sheet(wb, function(ws, i) { each_cell(ws, function(cell) {
each_sheet(wb, function(ws/*::, i*/) { /*:: void i; */each_cell(ws, function(cell) {
if(typeof cell.s !== 'undefined') return (found = true);
}); });
assert(found);
@ -480,7 +480,7 @@ describe('parse options', function() {
it('should not generate cell dates by default', function() {
DTPaths.forEach(function(p) {
var wb = X.read(fs.readFileSync(p), {type:TYPE});
each_sheet(wb, function(ws, i) { each_cell(ws, function(cell) {
each_sheet(wb, function(ws/*::, i*/) { /*:: void i; */each_cell(ws, function(cell) {
assert(cell.t !== 'd');
}); });
});
@ -489,7 +489,7 @@ describe('parse options', function() {
DTPaths.forEach(function(p) {
var wb = X.read(fs.readFileSync(p), {type:TYPE, cellDates: true, WTF:1});
var found = false;
each_sheet(wb, function(ws, i) { each_cell(ws, function(cell) {
each_sheet(wb, function(ws/*::, i*/) { /*:: void i; */each_cell(ws, function(cell) {
if(cell.t === 'd') return (found = true);
}); });
assert(found);
@ -911,7 +911,7 @@ describe('parse features', function() {
var wbs = [], wbs_no_slk = [];
var bef = (function() {
X = require(modp);
wbs = CWPaths.map(function(n) { return X.read(fs.readFileSync(CWPaths[0]), {type:TYPE, cellStyles:true}); });
wbs = CWPaths.map(function(n) { return X.read(fs.readFileSync(n), {type:TYPE, cellStyles:true}); });
wbs_no_slk = wbs.slice(0, 5);
});
if(typeof before != 'undefined') before(bef);
@ -1172,6 +1172,7 @@ describe('parse features', function() {
'A3:A10', 'B3:B10', 'E1:E10', 'F6:F8', /* cols */
'H1:J4', 'H10' /* blocks */
];
/*eslint-disable */
var exp/*:Array<any>*/ = [
{ patternType: 'darkHorizontal',
fgColor: { theme: 9, raw_rgb: 'F79646' },
@ -1198,6 +1199,7 @@ describe('parse features', function() {
fgColor: { theme: 3, raw_rgb: 'EEECE1' },
bgColor: { theme: 7, raw_rgb: '8064A2' } }
];
/*eslint-enable */
ranges.forEach(function(rng) {
it('XLS | ' + rng,function(){cmparr(rn2(rng).map(function(x){ return get_cell(wsxls,x).s; }));});
it('XLSX | ' + rng,function(){cmparr(rn2(rng).map(function(x){ return get_cell(wsxlsx,x).s; }));});
@ -1699,7 +1701,7 @@ describe('json output', function() {
});
it('should preserve values when column header is missing', function() {
/*jshint elision:true */
var _data = [[,"a","b",,"c"], [1,2,3,,5],[,3,4,5,6]];
var _data = [[,"a","b",,"c"], [1,2,3,,5],[,3,4,5,6]]; // eslint-disable-line no-sparse-arrays
/*jshint elision:false */
var _ws = X.utils.aoa_to_sheet(_data);
var json1 = X.utils.sheet_to_json(_ws, { raw: true });
@ -1721,7 +1723,7 @@ var plaintext_val = [
["B3", 's', " ", " "],
["A3"]
];
function plaintext_test(wb, raw, t) {
function plaintext_test(wb, raw) {
var sheet = wb.Sheets[wb.SheetNames[0]];
plaintext_val.forEach(function(x) {
var cell = get_cell(sheet, x[0]);
@ -1791,8 +1793,8 @@ describe('CSV', function() {
assert.equal(cell.v.getMonth(), 2);
assert.equal(cell.w, "2/3/14");
});
it('should interpret values by default', function() { plaintext_test(X.read(csv_bstr, {type:"binary"}), false, false); });
it('should generate strings if raw option is passed', function() { plaintext_test(X.read(csv_str, {type:"string", raw:true}), true, false); });
it('should interpret values by default', function() { plaintext_test(X.read(csv_bstr, {type:"binary"}), false); });
it('should generate strings if raw option is passed', function() { plaintext_test(X.read(csv_str, {type:"string", raw:true}), true); });
it('should handle formulae', function() {
var bb = '=,=1+1,="100"';
var sheet = X.read(bb, {type:"binary"}).Sheets.Sheet1;
@ -1921,9 +1923,9 @@ function get_dom_element(html) {
describe('HTML', function() {
describe('input string', function(){
it('should interpret values by default', function() { plaintext_test(X.read(html_bstr, {type:"binary"}), false, false); });
it('should generate strings if raw option is passed', function() { plaintext_test(X.read(html_bstr, {type:"binary", raw:true}), true, false); });
it('should handle "string" type', function() { plaintext_test(X.read(html_str, {type:"string"}), false, false); });
it('should interpret values by default', function() { plaintext_test(X.read(html_bstr, {type:"binary"}), false); });
it('should generate strings if raw option is passed', function() { plaintext_test(X.read(html_bstr, {type:"binary", raw:true}), true); });
it('should handle "string" type', function() { plaintext_test(X.read(html_str, {type:"string"}), false); });
it('should handle newlines correctly', function() {
var table = "<table><tr><td>foo<br/>bar</td><td>baz</td></tr></table>";
var wb = X.read(table, {type:"string"});
@ -1931,8 +1933,8 @@ describe('HTML', function() {
});
});
(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, true); });
it('should generate strings if raw option is passed', function() { plaintext_test(X.utils.table_to_book(get_dom_element(html_str), {raw:true}), true, true); });
it('should interpret values by default', function() { plaintext_test(X.utils.table_to_book(get_dom_element(html_str)), false); });
it('should generate strings if raw option is passed', function() { plaintext_test(X.utils.table_to_book(get_dom_element(html_str), {raw:true}), true); });
it('should handle newlines correctly', function() {
var table = get_dom_element("<table><tr><td>foo<br/>bar</td><td>baz</td></tr></table>");
var ws = X.utils.table_to_sheet(table);
@ -2030,14 +2032,14 @@ describe('corner cases', function() {
});
it('SSF', function() {
X.SSF.format("General", "dafuq");
assert.throws(function(x) { return X.SSF.format("General", {sheet:"js"});});
assert.throws(function() { return X.SSF.format("General", {sheet:"js"});});
X.SSF.format("b e ddd hh AM/PM", 41722.4097222222);
X.SSF.format("b ddd hh m", 41722.4097222222);
["hhh","hhh A/P","hhmmm","sss","[hhh]","G eneral"].forEach(function(f) {
assert.throws(function(x) { return X.SSF.format(f, 12345.6789);});
assert.throws(function() { return X.SSF.format(f, 12345.6789);});
});
["[m]","[s]"].forEach(function(f) {
assert.doesNotThrow(function(x) { return X.SSF.format(f, 12345.6789);});
assert.doesNotThrow(function() { return X.SSF.format(f, 12345.6789);});
});
});
if(typeof JSON !== 'undefined') it('SSF oddities', function() {

@ -439,7 +439,7 @@ smart_tags_2007.xlsx
spout-xlsx_attack_billion_laughs.xlsx
spout-xlsx_attack_quadratic_blowup.xlsx
# spout-xlsx_file_corrupted.xlsx
spout-xlsx_file_with_no_sheets_in_workbook_xml.xlsx
# spout-xlsx_file_with_no_sheets_in_workbook_xml.xlsx # explicit error on RT
# spout-xlsx_file_with_sheet_xml_not_matching_content_types.xlsx
spout-xlsx_one_sheet_with_inline_strings.xlsx
spout-xlsx_one_sheet_with_invalid_xml_characters.xlsx

41
tests/core.js generated

@ -1,7 +1,9 @@
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
/* vim: set ts=2: */
/*jshint mocha:true */
/* eslint-env mocha */
/*global process, document, require */
/*global ArrayBuffer, Uint8Array */
/*::
declare type EmptyFunc = (() => void) | null;
declare type DescribeIt = { (desc:string, test:EmptyFunc):void; skip(desc:string, test:EmptyFunc):void; };
@ -17,6 +19,7 @@ describe('source',function(){it('should load',function(){X=require(modp);});});
var DIF_XL = true;
var browser = typeof document !== 'undefined';
// $FlowIgnore
if(!browser) try { require('./shim'); } catch(e) { }
var opts = ({cellNF: true}/*:any*/);
@ -189,7 +192,6 @@ var MCPaths = pathit("mc", ["xlsx", "xlsb", "xls", "xml", "ods"]);
var CSSPaths = pathit("css", ["xlsx", "xlsb", "xls", "xml"]);
var NFPaths = pathit("nf", ["xlsx", "xlsb", "xls", "xml"]);
var DTPaths = pathit("dt", ["xlsx", "xlsb", "xls", "xml"]);
var NFPaths = pathit("nf", ["xlsx", "xlsb", "xls", "xml"]);
var HLPaths = pathit("hl", ["xlsx", "xlsb", "xls", "xml"]);
var ILPaths = pathit("il", ["xlsx", "xlsb", "xls", "xml", "ods", "xls5"]);
var OLPaths = pathit("ol", ["xlsx", "xlsb", "xls", "ods", "xls5"]);
@ -317,12 +319,11 @@ var wbtable = {};
wbtable[dir + x] = wb;
parsetest(x, wb, true);
});
fullex.forEach(function(ext, idx) {
fullex.forEach(function(ext) {
it(x + ' [' + ext + ']', function(){
var wb = wbtable[dir + x];
if(!wb) wb = X.readFile(dir + x, opts);
wb = X.read(X.write(wb, {type:"buffer", bookType:ext.replace(/\./,"")}), {WTF:opts.WTF, cellNF: true});
parsetest(x, wb, ext.replace(/\./,"") !== "xlsb", ext);
});
});
@ -470,7 +471,7 @@ describe('parse options', function() {
[paths.cssxlsx /*, paths.cssxlsb, paths.cssxls, paths.cssxml*/].forEach(function(p) {
var wb = X.read(fs.readFileSync(p), {type:TYPE, cellStyles:true});
var found = false;
each_sheet(wb, function(ws, i) { each_cell(ws, function(cell) {
each_sheet(wb, function(ws/*::, i*/) { /*:: void i; */each_cell(ws, function(cell) {
if(typeof cell.s !== 'undefined') return (found = true);
}); });
assert(found);
@ -479,7 +480,7 @@ describe('parse options', function() {
it('should not generate cell dates by default', function() {
DTPaths.forEach(function(p) {
var wb = X.read(fs.readFileSync(p), {type:TYPE});
each_sheet(wb, function(ws, i) { each_cell(ws, function(cell) {
each_sheet(wb, function(ws/*::, i*/) { /*:: void i; */each_cell(ws, function(cell) {
assert(cell.t !== 'd');
}); });
});
@ -488,7 +489,7 @@ describe('parse options', function() {
DTPaths.forEach(function(p) {
var wb = X.read(fs.readFileSync(p), {type:TYPE, cellDates: true, WTF:1});
var found = false;
each_sheet(wb, function(ws, i) { each_cell(ws, function(cell) {
each_sheet(wb, function(ws/*::, i*/) { /*:: void i; */each_cell(ws, function(cell) {
if(cell.t === 'd') return (found = true);
}); });
assert(found);
@ -910,7 +911,7 @@ describe('parse features', function() {
var wbs = [], wbs_no_slk = [];
var bef = (function() {
X = require(modp);
wbs = CWPaths.map(function(n) { return X.read(fs.readFileSync(CWPaths[0]), {type:TYPE, cellStyles:true}); });
wbs = CWPaths.map(function(n) { return X.read(fs.readFileSync(n), {type:TYPE, cellStyles:true}); });
wbs_no_slk = wbs.slice(0, 5);
});
if(typeof before != 'undefined') before(bef);
@ -1171,6 +1172,7 @@ describe('parse features', function() {
'A3:A10', 'B3:B10', 'E1:E10', 'F6:F8', /* cols */
'H1:J4', 'H10' /* blocks */
];
/*eslint-disable */
var exp/*:Array<any>*/ = [
{ patternType: 'darkHorizontal',
fgColor: { theme: 9, raw_rgb: 'F79646' },
@ -1197,6 +1199,7 @@ describe('parse features', function() {
fgColor: { theme: 3, raw_rgb: 'EEECE1' },
bgColor: { theme: 7, raw_rgb: '8064A2' } }
];
/*eslint-enable */
ranges.forEach(function(rng) {
it('XLS | ' + rng,function(){cmparr(rn2(rng).map(function(x){ return get_cell(wsxls,x).s; }));});
it('XLSX | ' + rng,function(){cmparr(rn2(rng).map(function(x){ return get_cell(wsxlsx,x).s; }));});
@ -1698,7 +1701,7 @@ describe('json output', function() {
});
it('should preserve values when column header is missing', function() {
/*jshint elision:true */
var _data = [[,"a","b",,"c"], [1,2,3,,5],[,3,4,5,6]];
var _data = [[,"a","b",,"c"], [1,2,3,,5],[,3,4,5,6]]; // eslint-disable-line no-sparse-arrays
/*jshint elision:false */
var _ws = X.utils.aoa_to_sheet(_data);
var json1 = X.utils.sheet_to_json(_ws, { raw: true });
@ -1720,7 +1723,7 @@ var plaintext_val = [
["B3", 's', " ", " "],
["A3"]
];
function plaintext_test(wb, raw, t) {
function plaintext_test(wb, raw) {
var sheet = wb.Sheets[wb.SheetNames[0]];
plaintext_val.forEach(function(x) {
var cell = get_cell(sheet, x[0]);
@ -1790,8 +1793,8 @@ describe('CSV', function() {
assert.equal(cell.v.getMonth(), 2);
assert.equal(cell.w, "2/3/14");
});
it('should interpret values by default', function() { plaintext_test(X.read(csv_bstr, {type:"binary"}), false, false); });
it('should generate strings if raw option is passed', function() { plaintext_test(X.read(csv_str, {type:"string", raw:true}), true, false); });
it('should interpret values by default', function() { plaintext_test(X.read(csv_bstr, {type:"binary"}), false); });
it('should generate strings if raw option is passed', function() { plaintext_test(X.read(csv_str, {type:"string", raw:true}), true); });
it('should handle formulae', function() {
var bb = '=,=1+1,="100"';
var sheet = X.read(bb, {type:"binary"}).Sheets.Sheet1;
@ -1920,9 +1923,9 @@ function get_dom_element(html) {
describe('HTML', function() {
describe('input string', function(){
it('should interpret values by default', function() { plaintext_test(X.read(html_bstr, {type:"binary"}), false, false); });
it('should generate strings if raw option is passed', function() { plaintext_test(X.read(html_bstr, {type:"binary", raw:true}), true, false); });
it('should handle "string" type', function() { plaintext_test(X.read(html_str, {type:"string"}), false, false); });
it('should interpret values by default', function() { plaintext_test(X.read(html_bstr, {type:"binary"}), false); });
it('should generate strings if raw option is passed', function() { plaintext_test(X.read(html_bstr, {type:"binary", raw:true}), true); });
it('should handle "string" type', function() { plaintext_test(X.read(html_str, {type:"string"}), false); });
it('should handle newlines correctly', function() {
var table = "<table><tr><td>foo<br/>bar</td><td>baz</td></tr></table>";
var wb = X.read(table, {type:"string"});
@ -1930,8 +1933,8 @@ describe('HTML', function() {
});
});
(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, true); });
it('should generate strings if raw option is passed', function() { plaintext_test(X.utils.table_to_book(get_dom_element(html_str), {raw:true}), true, true); });
it('should interpret values by default', function() { plaintext_test(X.utils.table_to_book(get_dom_element(html_str)), false); });
it('should generate strings if raw option is passed', function() { plaintext_test(X.utils.table_to_book(get_dom_element(html_str), {raw:true}), true); });
it('should handle newlines correctly', function() {
var table = get_dom_element("<table><tr><td>foo<br/>bar</td><td>baz</td></tr></table>");
var ws = X.utils.table_to_sheet(table);
@ -2029,14 +2032,14 @@ describe('corner cases', function() {
});
it('SSF', function() {
X.SSF.format("General", "dafuq");
assert.throws(function(x) { return X.SSF.format("General", {sheet:"js"});});
assert.throws(function() { return X.SSF.format("General", {sheet:"js"});});
X.SSF.format("b e ddd hh AM/PM", 41722.4097222222);
X.SSF.format("b ddd hh m", 41722.4097222222);
["hhh","hhh A/P","hhmmm","sss","[hhh]","G eneral"].forEach(function(f) {
assert.throws(function(x) { return X.SSF.format(f, 12345.6789);});
assert.throws(function() { return X.SSF.format(f, 12345.6789);});
});
["[m]","[s]"].forEach(function(f) {
assert.doesNotThrow(function(x) { return X.SSF.format(f, 12345.6789);});
assert.doesNotThrow(function() { return X.SSF.format(f, 12345.6789);});
});
});
if(typeof JSON !== 'undefined') it('SSF oddities', function() {

40
tests/write.html Normal file

@ -0,0 +1,40 @@
<!DOCTYPE html>
<!-- (C) 2013-present SheetJS http://sheetjs.com -->
<!-- vim: set ts=2: -->
<html>
<head>
<title>SheetJS JS-XLSX In-Browser Export Demo</title>
<meta charset="utf-8" />
<style>
a { text-decoration: none }
</style>
</head>
<body>
<pre>
<h3><a href="//sheetjs.com/">SheetJS</a> JS-XLSX In-Browser Export Demo</h3>
<b>Example Code</b>
/* Generate Workbook */
var wb = XLSX.utils.book_new();
var ws = XLSX.utils.aoa_to_sheet([["a","b"],[1,2,3]]);
XLSX.utils.book_append_sheet(wb, ws, "SheetJS");
/* Trigger Download with `writeFile` */
XLSX.writeFile(wb, "SheetJS.xlsb", {compression:true});
<b>Download Generation Methods:</b>
- IE6-9 require ActiveX and Windows Script support.
The IE_SaveFile function from the included shim uses VBScript.
- IE10-11 use msSaveBlob API.
- When supported, `saveAs` will be used.
- When available, modern browsers use `URL.createObjectURL`.
</pre>
<script src="shim.js"></script>
<script src="xlsx.full.min.js"></script>
<script src="write.js"></script>
</body>

@ -1,7 +1,9 @@
/* writing feature test -- look for TEST: in comments */
/* vim: set ts=2 ft=javascript: */
var ext = !!process.argv[2];
if(typeof console === 'undefined') console = {log: function(){}};
var ext = typeof process !== 'undefined' && !!process.argv[2];
/* original data */
var data = [
@ -191,9 +193,21 @@ var filenames = [
['sheetjs.prn']
];
var OUT = ["base64", "binary", "string", "array"];
if(typeof Buffer !== 'undefined') OUT.push("buffer");
filenames.forEach(function(r) {
/* write file */
XLSX.writeFile(wb, r[0], r[1]);
/* test by reading back files */
XLSX.readFile(r[0]);
/* write file */
XLSX.writeFile(wb, r[0], r[1]);
/* test by reading back files */
if(typeof process !== 'undefined') XLSX.readFile(r[0]);
var ext = r[1] && r[1].bookType || r[0].split(".")[1];
ext = {"htm":"html"}[ext] || ext;
OUT.forEach(function(type) {
if(type == "string" && ["xlsx", "xlsm", "xlsb", "xlam", "biff8", "biff5", "xla", "ods", "dbf"].indexOf(ext) > -1) return;
if(type == "array" && ["xlsx", "xlsm", "xlsb", "xlam", "ods"].indexOf(ext) > -1 && typeof Uint8Array === 'undefined') return;
var datout = XLSX.write(wb, {type: type, bookType: ext, sheet:r[1] && r[1].sheet || null});
XLSX.read(datout, {type:type});
if(type == "array") console.log(ext, datout);
});
});

7
types/index.d.ts vendored

@ -10,11 +10,11 @@ export const SSF: any;
/** CFB Library */
export const CFB: any;
/** Attempts to read filename and parse */
/** NODE ONLY! Attempts to read filename and parse */
export function readFile(filename: string, opts?: ParsingOptions): WorkBook;
/** Attempts to parse data */
export function read(data: any, opts?: ParsingOptions): WorkBook;
/** NODE ONLY! Attempts to write workbook data to filename */
/** Attempts to write or download workbook data to file */
export function writeFile(data: WorkBook, filename: string, opts?: WritingOptions): any;
/** Attempts to write the workbook data */
export function write(data: WorkBook, opts?: WritingOptions): any;
@ -602,6 +602,9 @@ export interface OriginOption {
}
export interface Sheet2HTMLOpts {
/** TABLE element id attribute */
id?: string;
/** Add contenteditable to every cell */
editable?: boolean;

@ -4,7 +4,7 @@
/*global global, exports, module, require:false, process:false, Buffer:false, ArrayBuffer:false */
var XLSX = {};
(function make_xlsx(XLSX){
XLSX.version = '0.11.18';
XLSX.version = '0.11.19';
var current_codepage = 1200, current_ansi = 1252;
/*:: declare var cptable:any; */
/*global cptable:true */
@ -141,11 +141,16 @@ function s2ab(s/*:string*/) {
return buf;
}
function arr2str(data/*:any*/)/*:string*/ {
function a2s(data/*:any*/)/*:string*/ {
if(Array.isArray(data)) return data.map(_chr).join("");
var o/*:Array<string>*/ = []; for(var i = 0; i < data.length; ++i) o[i] = _chr(data[i]); return o.join("");
}
function a2u(data/*:Array<number>*/)/*:Uint8Array*/ {
if(typeof Uint8Array === 'undefined') throw new Error("Unsupported");
return new Uint8Array(data);
}
function ab2a(data/*:ArrayBuffer|Uint8Array*/)/*:Array<number>*/ {
if(typeof ArrayBuffer == 'undefined') throw new Error("Unsupported");
if(data instanceof ArrayBuffer) return ab2a(new Uint8Array(data));
@ -1853,6 +1858,43 @@ 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/*:string*/, payload/*:any*/, enc/*:?string*/) {
/*global IE_SaveFile, Blob, navigator, saveAs, URL, document */
if(typeof _fs !== 'undefined' && _fs.writeFileSync) return enc ? _fs.writeFileSync(fname, payload, enc) : _fs.writeFileSync(fname, payload);
var data = (enc == "utf8") ? utf8write(payload) : payload;
/*:: declare var IE_SaveFile: any; */
if(typeof IE_SaveFile !== 'undefined') return IE_SaveFile(data, fname);
if(typeof Blob !== 'undefined') {
var blob = new Blob([blobify(data)], {type:"application/octet-stream"});
/*:: declare var navigator: any; */
if(typeof navigator !== 'undefined' && navigator.msSaveBlob) return navigator.msSaveBlob(blob, fname);
/*:: declare var saveAs: any; */
if(typeof saveAs !== 'undefined') return saveAs(blob, fname);
if(typeof URL !== 'undefined' && typeof document !== 'undefined' && document.createElement && URL.createObjectURL) {
var a = document.createElement("a");
if(a.download != null) {
var url = URL.createObjectURL(blob);
/*:: if(document.body == null) throw new Error("unreachable"); */
a.download = fname; a.href = url; document.body.appendChild(a); a.click();
/*:: if(document.body == null) throw new Error("unreachable"); */ document.body.removeChild(a);
if(URL.revokeObjectURL && typeof setTimeout !== 'undefined') setTimeout(function() { URL.revokeObjectURL(url); }, 60000);
return url;
}
}
}
throw new Error("cannot initiate download");
}
function keys(o/*:any*/)/*:Array<any>*/ { return Object.keys(o); }
function evert_key(obj/*:any*/, key/*:string*/)/*:EvertType*/ {
@ -2045,14 +2087,13 @@ function getzipstr(zip, file/*:string*/, safe/*:?boolean*/)/*:?string*/ {
try { return getzipstr(zip, file); } catch(e) { return null; }
}
var _fs, jszip;
var jszip;
/*:: declare var JSZip:any; */
/*global JSZip:true */
if(typeof JSZip !== 'undefined') jszip = JSZip;
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
if(typeof exports !== 'undefined') {
if(typeof module !== 'undefined' && module.exports) {
if(typeof jszip === 'undefined') jszip = require('./jszip.js');
try { _fs = require('fs'); } catch(e) { }
}
}
@ -13378,6 +13419,7 @@ function check_wb_names(N) {
}
function check_wb(wb) {
if(!wb || !wb.SheetNames || !wb.Sheets) throw new Error("Invalid Workbook");
if(!wb.SheetNames.length) throw new Error("Workbook is empty");
check_wb_names(wb.SheetNames);
/* TODO: validate workbook */
}
@ -14821,7 +14863,7 @@ function parse_xlml(data/*:RawBytes|string*/, opts)/*:Workbook*/ {
switch(opts.type||"base64") {
case "base64": return parse_xlml_xml(Base64.decode(data), opts);
case "binary": case "buffer": case "file": return parse_xlml_xml(data, opts);
case "array": return parse_xlml_xml(arr2str(data), opts);
case "array": return parse_xlml_xml(a2s(data), opts);
}
/*:: throw new Error("unsupported type " + opts.type); */
}
@ -17641,9 +17683,9 @@ var HTML_ = (function() {
var preamble = "<tr>";
return preamble + oo.join("") + "</tr>";
}
function make_html_preamble(/*::ws:Worksheet, R:Range, o:Sheet2HTMLOpts*/)/*:string*/ {
function make_html_preamble(ws/*:Worksheet*/, R/*:Range*/, o/*:Sheet2HTMLOpts*/)/*:string*/ {
var out/*:Array<string>*/ = [];
return out.join("") + '<table>';
return out.join("") + '<table' + (o && o.id ? ' id="' + o.id + '"' : "") + '>';
}
var _BEGIN = '<html><head><meta charset="utf-8"/><title>SheetJS Table Export</title></head><body>';
var _END = '</body></html>';
@ -19010,7 +19052,6 @@ function readSync(data/*:RawData*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
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);
if(0x20>n[0]||n[0]>0x7F) throw new Error("Unsupported file " + n.join("|"));
return read_prn(data, d, o, str);
}
@ -19026,12 +19067,12 @@ function write_zip_type(wb/*:Workbook*/, opts/*:?WriteOpts*/)/*:any*/ {
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 "string": throw new Error("'string' output type invalid for '" + o.bookType + "' files");
case "buffer":
case "file": oopts.type = "nodebuffer"; break;
case "file": oopts.type = has_buf ? "nodebuffer" : "string"; break;
default: throw new Error("Unrecognized type " + o.type);
}
if(o.type === "file") return _fs.writeFileSync(o.file, z.generate(oopts));
if(o.type === "file") return write_dl(o.file, z.generate(oopts));
var out = z.generate(oopts);
// $FlowIgnore
return o.type == "string" ? utf8read(out) : out;
@ -19043,8 +19084,8 @@ function write_cfb_type(wb/*:Workbook*/, opts/*:?WriteOpts*/)/*:any*/ {
switch(o.type) {
case "base64": case "binary": break;
case "buffer": case "array": o.type = ""; break;
case "file": return _fs.writeFileSync(o.file, CFB.write(cfb, {type:'buffer'}));
case "string": throw new Error("'string' output type invalid for '" + o.bookType + ' files');
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);
@ -19057,7 +19098,7 @@ function write_string_type(out/*:string*/, opts/*:WriteOpts*/, bom/*:?string*/)/
case "base64": return Base64.encode(utf8write(o));
case "binary": return utf8write(o);
case "string": return out;
case "file": return _fs.writeFileSync(opts.file, o, 'utf8');
case "file": return write_dl(opts.file, o, 'utf8');
case "buffer": {
if(has_buf) return new Buffer(o, 'utf8');
else return write_string_type(o, {type:'binary'}).split("").map(function(c) { return c.charCodeAt(0); });
@ -19071,7 +19112,7 @@ function write_stxt_type(out/*:string*/, opts/*:WriteOpts*/)/*:any*/ {
case "base64": return Base64.encode(out);
case "binary": return out;
case "string": return out; /* override in sheet_to_txt */
case "file": return _fs.writeFileSync(opts.file, out, 'binary');
case "file": return write_dl(opts.file, out, 'binary');
case "buffer": {
if(has_buf) return new Buffer(out, 'binary');
else return out.split("").map(function(c) { return c.charCodeAt(0); });
@ -19090,7 +19131,7 @@ function write_binary_type(out, opts/*:WriteOpts*/)/*:any*/ {
// $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 _fs.writeFileSync(opts.file, out);
case "file": return write_dl(opts.file, out);
case "buffer": return out;
default: throw new Error("Unrecognized type " + opts.type);
}
@ -19293,7 +19334,7 @@ function sheet_to_txt(sheet/*:Worksheet*/, opts/*:?Sheet2CSVOpts*/) {
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 "\xff\xfe" + o;
return String.fromCharCode(255) + String.fromCharCode(254) + o;
}
function sheet_to_formulae(sheet/*:Worksheet*/)/*:Array<string>*/ {

75
xlsx.js generated

@ -4,7 +4,7 @@
/*global global, exports, module, require:false, process:false, Buffer:false, ArrayBuffer:false */
var XLSX = {};
(function make_xlsx(XLSX){
XLSX.version = '0.11.18';
XLSX.version = '0.11.19';
var current_codepage = 1200, current_ansi = 1252;
/*global cptable:true */
if(typeof module !== "undefined" && typeof require !== 'undefined') {
@ -140,11 +140,16 @@ function s2ab(s) {
return buf;
}
function arr2str(data) {
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));
@ -1782,6 +1787,39 @@ 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 */
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 a = document.createElement("a");
if(a.download != null) {
var url = URL.createObjectURL(blob);
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;
}
}
}
throw new Error("cannot initiate download");
}
function keys(o) { return Object.keys(o); }
function evert_key(obj, key) {
@ -1973,13 +2011,12 @@ function getzipstr(zip, file, safe) {
try { return getzipstr(zip, file); } catch(e) { return null; }
}
var _fs, jszip;
var jszip;
/*global JSZip:true */
if(typeof JSZip !== 'undefined') jszip = JSZip;
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
if(typeof exports !== 'undefined') {
if(typeof module !== 'undefined' && module.exports) {
if(typeof jszip === 'undefined') jszip = require('./jszip.js');
try { _fs = require('fs'); } catch(e) { }
}
}
@ -13286,6 +13323,7 @@ function check_wb_names(N) {
}
function check_wb(wb) {
if(!wb || !wb.SheetNames || !wb.Sheets) throw new Error("Invalid Workbook");
if(!wb.SheetNames.length) throw new Error("Workbook is empty");
check_wb_names(wb.SheetNames);
/* TODO: validate workbook */
}
@ -14723,7 +14761,7 @@ function parse_xlml(data, opts) {
switch(opts.type||"base64") {
case "base64": return parse_xlml_xml(Base64.decode(data), opts);
case "binary": case "buffer": case "file": return parse_xlml_xml(data, opts);
case "array": return parse_xlml_xml(arr2str(data), opts);
case "array": return parse_xlml_xml(a2s(data), opts);
}
}
@ -17536,9 +17574,9 @@ var HTML_ = (function() {
var preamble = "<tr>";
return preamble + oo.join("") + "</tr>";
}
function make_html_preamble() {
function make_html_preamble(ws, R, o) {
var out = [];
return out.join("") + '<table>';
return out.join("") + '<table' + (o && o.id ? ' id="' + o.id + '"' : "") + '>';
}
var _BEGIN = '<html><head><meta charset="utf-8"/><title>SheetJS Table Export</title></head><body>';
var _END = '</body></html>';
@ -18899,7 +18937,6 @@ function readSync(data, opts) {
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);
if(0x20>n[0]||n[0]>0x7F) throw new Error("Unsupported file " + n.join("|"));
return read_prn(data, d, o, str);
}
@ -18915,12 +18952,12 @@ function write_zip_type(wb, opts) {
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 "string": throw new Error("'string' output type invalid for '" + o.bookType + "' files");
case "buffer":
case "file": oopts.type = "nodebuffer"; break;
case "file": oopts.type = has_buf ? "nodebuffer" : "string"; break;
default: throw new Error("Unrecognized type " + o.type);
}
if(o.type === "file") return _fs.writeFileSync(o.file, z.generate(oopts));
if(o.type === "file") return write_dl(o.file, z.generate(oopts));
var out = z.generate(oopts);
// $FlowIgnore
return o.type == "string" ? utf8read(out) : out;
@ -18932,8 +18969,8 @@ function write_cfb_type(wb, opts) {
switch(o.type) {
case "base64": case "binary": break;
case "buffer": case "array": o.type = ""; break;
case "file": return _fs.writeFileSync(o.file, CFB.write(cfb, {type:'buffer'}));
case "string": throw new Error("'string' output type invalid for '" + o.bookType + ' files');
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);
@ -18946,7 +18983,7 @@ function write_string_type(out, opts, bom) {
case "base64": return Base64.encode(utf8write(o));
case "binary": return utf8write(o);
case "string": return out;
case "file": return _fs.writeFileSync(opts.file, o, 'utf8');
case "file": return write_dl(opts.file, o, 'utf8');
case "buffer": {
if(has_buf) return new Buffer(o, 'utf8');
else return write_string_type(o, {type:'binary'}).split("").map(function(c) { return c.charCodeAt(0); });
@ -18960,7 +18997,7 @@ function write_stxt_type(out, opts) {
case "base64": return Base64.encode(out);
case "binary": return out;
case "string": return out; /* override in sheet_to_txt */
case "file": return _fs.writeFileSync(opts.file, out, 'binary');
case "file": return write_dl(opts.file, out, 'binary');
case "buffer": {
if(has_buf) return new Buffer(out, 'binary');
else return out.split("").map(function(c) { return c.charCodeAt(0); });
@ -18979,7 +19016,7 @@ function write_binary_type(out, opts) {
// $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 _fs.writeFileSync(opts.file, out);
case "file": return write_dl(opts.file, out);
case "buffer": return out;
default: throw new Error("Unrecognized type " + opts.type);
}
@ -19182,7 +19219,7 @@ function sheet_to_txt(sheet, opts) {
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 "\xff\xfe" + o;
return String.fromCharCode(255) + String.fromCharCode(254) + o;
}
function sheet_to_formulae(sheet) {