Skip to content

Commit 75845a0

Browse files
committed
version bump 0.11.19: browser writeFile
- IE6-9 ActiveX + VBScript shim - `writeFile` supported in browser - `oldie` demo for IE write strategies
1 parent edf7150 commit 75845a0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

88 files changed

+1098
-416
lines changed

Diff for: .spelling

+2-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ CDNjs
3636
CommonJS
3737
Ethercalc
3838
ExtendScript
39-
FileSaver
4039
IndexedDB
4140
JavaScriptCore
4241
LocalStorage
@@ -58,6 +57,7 @@ webpack
5857
weex
5958

6059
# Other terms
60+
ActiveX
6161
APIs
6262
ArrayBuffer
6363
Base64
@@ -66,6 +66,7 @@ JS
6666
NoSQL
6767
README
6868
UTF-16
69+
VBScript
6970
XHR
7071
XMLHttpRequest
7172
bundler

Diff for: CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ This log is intended to keep track of backwards-incompatible changes, including
44
but not limited to API changes and file location changes. Minor behavioral
55
changes may not be included if they are not expected to break existing code.
66

7+
## 0.11.19
8+
9+
* Error on empty workbook
10+
711
## 0.11.16 (2017-12-30)
812

913
* XLS ANSI/CP separation

Diff for: README.md

+69-25
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ The [`demos` directory](demos/) includes sample projects for:
215215
- [`Headless Browsers`](demos/headless/)
216216
- [`canvas-datagrid`](demos/datagrid/)
217217
- [`Swift JSC and other engines`](demos/altjs/)
218+
- [`internet explorer`](demos/oldie/)
218219

219220
### Optional Modules
220221

@@ -339,7 +340,7 @@ var worksheet = XLSX.read(htmlstr, {type:'string'});
339340
<summary><b>Browser download file (ajax)</b> (click to show)</summary>
340341

341342
Note: for a more complete example that works in older browsers, check the demo
342-
at <http://oss.sheetjs.com/js-xlsx/ajax.html>). The <demos/xhr/> directory also
343+
at <http://oss.sheetjs.com/js-xlsx/ajax.html>). The [`xhr` demo](demos/xhr/)
343344
includes more examples with `XMLHttpRequest` and `fetch`.
344345

345346
```js
@@ -414,6 +415,8 @@ input_dom_element.addEventListener('change', handleFile, false);
414415

415416
</details>
416417

418+
More specialized cases, including mobile app file processing, are covered in the
419+
[included demos](demos/)
417420

418421
### Parsing Examples
419422

@@ -590,8 +593,7 @@ Assuming `workbook` is a workbook object:
590593
<details>
591594
<summary><b>nodejs write a file</b> (click to show)</summary>
592595

593-
`writeFile` is only available in server environments. Browsers have no API for
594-
writing arbitrary files given a path, so another strategy must be used.
596+
`XLSX.writeFile` uses `fs.writeFileSync` in server environments:
595597

596598
```js
597599
if(typeof require !== 'undefined') XLSX = require('xlsx');
@@ -603,7 +605,7 @@ XLSX.writeFile(workbook, 'out.xlsb');
603605
</details>
604606

605607
<details>
606-
<summary><b>Browser add to web page</b> (click to show)</summary>
608+
<summary><b>Browser add TABLE element to page</b> (click to show)</summary>
607609

608610
The `sheet_to_html` utility function generates HTML code that can be added to
609611
any DOM element.
@@ -614,29 +616,10 @@ var container = document.getElementById('tableau');
614616
container.innerHTML = XLSX.utils.sheet_to_html(worksheet);
615617
```
616618

617-
618-
</details>
619-
620-
<details>
621-
<summary><b>Browser save file</b> (click to show)</summary>
622-
623-
Note: browser generates binary blob and forces a "download" to client. This
624-
example uses [FileSaver](https://github.com/eligrey/FileSaver.js/):
625-
626-
```js
627-
/* bookType can be any supported output type */
628-
var wopts = { bookType:'xlsx', bookSST:false, type:'array' };
629-
630-
var wbout = XLSX.write(workbook,wopts);
631-
632-
/* the saveAs call downloads a file on the local machine */
633-
saveAs(new Blob([wbout],{type:"application/octet-stream"}), "test.xlsx");
634-
```
635-
636619
</details>
637620

638621
<details>
639-
<summary><b>Browser upload to server</b> (click to show)</summary>
622+
<summary><b>Browser upload file (ajax)</b> (click to show)</summary>
640623

641624
A complete example using XHR is [included in the XHR demo](demos/xhr/), along
642625
with examples for fetch and wrapper libraries. This example assumes the server
@@ -658,6 +641,65 @@ req.send(formdata);
658641

659642
</details>
660643

644+
<details>
645+
<summary><b>Browser save file</b> (click to show)</summary>
646+
647+
`XLSX.writeFile` wraps a few techniques for triggering a file save:
648+
649+
- `URL` browser API creates an object URL for the file, which the library uses
650+
by creating a link and forcing a click. It is supported in modern browsers.
651+
- `msSaveBlob` is an IE10+ API for triggering a file save.
652+
- `IE_FileSave` uses VBScript and ActiveX to write a file in IE6+ for Windows
653+
XP and Windows 7. The shim must be included in the containing HTML page.
654+
655+
There is no standard way to determine if the actual file has been downloaded.
656+
657+
```js
658+
/* output format determined by filename */
659+
XLSX.writeFile(workbook, 'out.xlsb');
660+
/* at this point, out.xlsb will have been downloaded */
661+
```
662+
663+
</details>
664+
665+
<details>
666+
<summary><b>Browser save file (compatibility)</b> (click to show)</summary>
667+
668+
`XLSX.writeFile` techniques work for most modern browsers as well as older IE.
669+
For much older browsers, there are workarounds implemented by wrapper libraries.
670+
671+
[`FileSaver.js`](https://github.com/eligrey/FileSaver.js/) implements `saveAs`.
672+
Note: `XLSX.writeFile` will automatically call `saveAs` if available.
673+
674+
```js
675+
/* bookType can be any supported output type */
676+
var wopts = { bookType:'xlsx', bookSST:false, type:'array' };
677+
678+
var wbout = XLSX.write(workbook,wopts);
679+
680+
/* the saveAs call downloads a file on the local machine */
681+
saveAs(new Blob([wbout],{type:"application/octet-stream"}), "test.xlsx");
682+
```
683+
684+
[`Downloadify`](https://github.com/dcneiner/downloadify) uses a Flash SWF button
685+
to generate local files, suitable for environments where ActiveX is unavailable:
686+
687+
```js
688+
Downloadify.create(id,{
689+
/* other options are required! read the downloadify docs for more info */
690+
filename: "test.xlsx",
691+
data: function() { return XLSX.write(wb, {bookType:"xlsx", type:'base64'}); },
692+
append: false,
693+
dataType: 'base64'
694+
});
695+
```
696+
697+
The [`oldie` demo](demos/oldie/) shows an IE-compatible fallback scenario.
698+
699+
</details>
700+
701+
The [included demos](demos/) cover mobile apps and other special deployments.
702+
661703
### Writing Examples
662704

663705
- <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.
705747

706748
`XLSX.write(wb, write_opts)` attempts to write the workbook `wb`
707749

708-
`XLSX.writeFile(wb, filename, write_opts)` attempts to write `wb` to `filename`
750+
`XLSX.writeFile(wb, filename, write_opts)` attempts to write `wb` to `filename`.
751+
In browser-based environments, it will attempt to force a client-side download.
709752

710753
`XLSX.writeFileAsync(filename, wb, o, cb)` attempts to write `wb` to `filename`.
711754
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:
20152058

20162059
| Option Name | Default | Description |
20172060
| :---------- | :------: | :-------------------------------------------------- |
2061+
|`id` | | Specify the `id` attribute for the `TABLE` element |
20182062
|`editable` | false | If true, set `contenteditable="true"` for every TD |
20192063
|`header` | | Override header (default `html body`) |
20202064
|`footer` | | Override footer (default `/body /html`) |

Diff for: bits/01_version.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
XLSX.version = '0.11.18';
1+
XLSX.version = '0.11.19';

Diff for: bits/05_buf.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,16 @@ function s2ab(s/*:string*/) {
1919
return buf;
2020
}
2121

22-
function arr2str(data/*:any*/)/*:string*/ {
22+
function a2s(data/*:any*/)/*:string*/ {
2323
if(Array.isArray(data)) return data.map(_chr).join("");
2424
var o/*:Array<string>*/ = []; for(var i = 0; i < data.length; ++i) o[i] = _chr(data[i]); return o.join("");
2525
}
2626

27+
function a2u(data/*:Array<number>*/)/*:Uint8Array*/ {
28+
if(typeof Uint8Array === 'undefined') throw new Error("Unsupported");
29+
return new Uint8Array(data);
30+
}
31+
2732
function ab2a(data/*:ArrayBuffer|Uint8Array*/)/*:Array<number>*/ {
2833
if(typeof ArrayBuffer == 'undefined') throw new Error("Unsupported");
2934
if(data instanceof ArrayBuffer) return ab2a(new Uint8Array(data));

Diff for: bits/19_fsutils.js

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
var _fs;
2+
if(typeof require !== 'undefined') try { _fs = require('fs'); } catch(e) {}
3+
4+
/* normalize data for blob ctor */
5+
function blobify(data) {
6+
if(typeof data === "string") return s2ab(data);
7+
if(Array.isArray(data)) return a2u(data);
8+
return data;
9+
}
10+
/* write or download file */
11+
function write_dl(fname/*:string*/, payload/*:any*/, enc/*:?string*/) {
12+
/*global IE_SaveFile, Blob, navigator, saveAs, URL, document */
13+
if(typeof _fs !== 'undefined' && _fs.writeFileSync) return enc ? _fs.writeFileSync(fname, payload, enc) : _fs.writeFileSync(fname, payload);
14+
var data = (enc == "utf8") ? utf8write(payload) : payload;
15+
/*:: declare var IE_SaveFile: any; */
16+
if(typeof IE_SaveFile !== 'undefined') return IE_SaveFile(data, fname);
17+
if(typeof Blob !== 'undefined') {
18+
var blob = new Blob([blobify(data)], {type:"application/octet-stream"});
19+
/*:: declare var navigator: any; */
20+
if(typeof navigator !== 'undefined' && navigator.msSaveBlob) return navigator.msSaveBlob(blob, fname);
21+
/*:: declare var saveAs: any; */
22+
if(typeof saveAs !== 'undefined') return saveAs(blob, fname);
23+
if(typeof URL !== 'undefined' && typeof document !== 'undefined' && document.createElement && URL.createObjectURL) {
24+
var a = document.createElement("a");
25+
if(a.download != null) {
26+
var url = URL.createObjectURL(blob);
27+
/*:: if(document.body == null) throw new Error("unreachable"); */
28+
a.download = fname; a.href = url; document.body.appendChild(a); a.click();
29+
/*:: if(document.body == null) throw new Error("unreachable"); */ document.body.removeChild(a);
30+
if(URL.revokeObjectURL && typeof setTimeout !== 'undefined') setTimeout(function() { URL.revokeObjectURL(url); }, 60000);
31+
return url;
32+
}
33+
}
34+
}
35+
throw new Error("cannot initiate download");
36+
}
37+

Diff for: bits/21_ziputils.js

+3-4
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,13 @@ function getzipstr(zip, file/*:string*/, safe/*:?boolean*/)/*:?string*/ {
5151
try { return getzipstr(zip, file); } catch(e) { return null; }
5252
}
5353

54-
var _fs, jszip;
54+
var jszip;
5555
/*:: declare var JSZip:any; */
5656
/*global JSZip:true */
5757
if(typeof JSZip !== 'undefined') jszip = JSZip;
58-
if (typeof exports !== 'undefined') {
59-
if (typeof module !== 'undefined' && module.exports) {
58+
if(typeof exports !== 'undefined') {
59+
if(typeof module !== 'undefined' && module.exports) {
6060
if(typeof jszip === 'undefined') jszip = require('./jszip.js');
61-
try { _fs = require('fs'); } catch(e) { }
6261
}
6362
}
6463

Diff for: bits/71_wbcommon.js

+1
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ function check_wb_names(N) {
132132
}
133133
function check_wb(wb) {
134134
if(!wb || !wb.SheetNames || !wb.Sheets) throw new Error("Invalid Workbook");
135+
if(!wb.SheetNames.length) throw new Error("Workbook is empty");
135136
check_wb_names(wb.SheetNames);
136137
/* TODO: validate workbook */
137138
}

Diff for: bits/75_xlml.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -833,7 +833,7 @@ function parse_xlml(data/*:RawBytes|string*/, opts)/*:Workbook*/ {
833833
switch(opts.type||"base64") {
834834
case "base64": return parse_xlml_xml(Base64.decode(data), opts);
835835
case "binary": case "buffer": case "file": return parse_xlml_xml(data, opts);
836-
case "array": return parse_xlml_xml(arr2str(data), opts);
836+
case "array": return parse_xlml_xml(a2s(data), opts);
837837
}
838838
/*:: throw new Error("unsupported type " + opts.type); */
839839
}

Diff for: bits/79_html.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,9 @@ var HTML_ = (function() {
8484
var preamble = "<tr>";
8585
return preamble + oo.join("") + "</tr>";
8686
}
87-
function make_html_preamble(/*::ws:Worksheet, R:Range, o:Sheet2HTMLOpts*/)/*:string*/ {
87+
function make_html_preamble(ws/*:Worksheet*/, R/*:Range*/, o/*:Sheet2HTMLOpts*/)/*:string*/ {
8888
var out/*:Array<string>*/ = [];
89-
return out.join("") + '<table>';
89+
return out.join("") + '<table' + (o && o.id ? ' id="' + o.id + '"' : "") + '>';
9090
}
9191
var _BEGIN = '<html><head><meta charset="utf-8"/><title>SheetJS Table Export</title></head><body>';
9292
var _END = '</body></html>';

Diff for: bits/87_read.js

-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,6 @@ function readSync(data/*:RawData*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
100100
case 0x0A: case 0x0D: case 0x20: return read_plaintext_raw(d, o);
101101
}
102102
if(n[2] <= 12 && n[3] <= 31) return DBF.to_workbook(d, o);
103-
if(0x20>n[0]||n[0]>0x7F) throw new Error("Unsupported file " + n.join("|"));
104103
return read_prn(data, d, o, str);
105104
}
106105

Diff for: bits/88_write.js

+8-8
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ function write_zip_type(wb/*:Workbook*/, opts/*:?WriteOpts*/)/*:any*/ {
66
switch(o.type) {
77
case "base64": oopts.type = "base64"; break;
88
case "binary": oopts.type = "string"; break;
9-
case "string": throw new Error("'string' output type invalid for '" + o.bookType + ' files');
9+
case "string": throw new Error("'string' output type invalid for '" + o.bookType + "' files");
1010
case "buffer":
11-
case "file": oopts.type = "nodebuffer"; break;
11+
case "file": oopts.type = has_buf ? "nodebuffer" : "string"; break;
1212
default: throw new Error("Unrecognized type " + o.type);
1313
}
14-
if(o.type === "file") return _fs.writeFileSync(o.file, z.generate(oopts));
14+
if(o.type === "file") return write_dl(o.file, z.generate(oopts));
1515
var out = z.generate(oopts);
1616
// $FlowIgnore
1717
return o.type == "string" ? utf8read(out) : out;
@@ -23,8 +23,8 @@ function write_cfb_type(wb/*:Workbook*/, opts/*:?WriteOpts*/)/*:any*/ {
2323
switch(o.type) {
2424
case "base64": case "binary": break;
2525
case "buffer": case "array": o.type = ""; break;
26-
case "file": return _fs.writeFileSync(o.file, CFB.write(cfb, {type:'buffer'}));
27-
case "string": throw new Error("'string' output type invalid for '" + o.bookType + ' files');
26+
case "file": return write_dl(o.file, CFB.write(cfb, {type:has_buf ? 'buffer' : ""}));
27+
case "string": throw new Error("'string' output type invalid for '" + o.bookType + "' files");
2828
default: throw new Error("Unrecognized type " + o.type);
2929
}
3030
return CFB.write(cfb, o);
@@ -37,7 +37,7 @@ function write_string_type(out/*:string*/, opts/*:WriteOpts*/, bom/*:?string*/)/
3737
case "base64": return Base64.encode(utf8write(o));
3838
case "binary": return utf8write(o);
3939
case "string": return out;
40-
case "file": return _fs.writeFileSync(opts.file, o, 'utf8');
40+
case "file": return write_dl(opts.file, o, 'utf8');
4141
case "buffer": {
4242
if(has_buf) return new Buffer(o, 'utf8');
4343
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*/ {
5151
case "base64": return Base64.encode(out);
5252
case "binary": return out;
5353
case "string": return out; /* override in sheet_to_txt */
54-
case "file": return _fs.writeFileSync(opts.file, out, 'binary');
54+
case "file": return write_dl(opts.file, out, 'binary');
5555
case "buffer": {
5656
if(has_buf) return new Buffer(out, 'binary');
5757
else return out.split("").map(function(c) { return c.charCodeAt(0); });
@@ -70,7 +70,7 @@ function write_binary_type(out, opts/*:WriteOpts*/)/*:any*/ {
7070
// $FlowIgnore
7171
for(var i = 0; i < out.length; ++i) bstr += String.fromCharCode(out[i]);
7272
return opts.type == 'base64' ? Base64.encode(bstr) : opts.type == 'string' ? utf8read(bstr) : bstr;
73-
case "file": return _fs.writeFileSync(opts.file, out);
73+
case "file": return write_dl(opts.file, out);
7474
case "buffer": return out;
7575
default: throw new Error("Unrecognized type " + opts.type);
7676
}

Diff for: bits/90_utils.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ function sheet_to_txt(sheet/*:Worksheet*/, opts/*:?Sheet2CSVOpts*/) {
131131
var s = sheet_to_csv(sheet, opts);
132132
if(typeof cptable == 'undefined' || opts.type == 'string') return s;
133133
var o = cptable.utils.encode(1200, s, 'str');
134-
return "\xff\xfe" + o;
134+
return String.fromCharCode(255) + String.fromCharCode(254) + o;
135135
}
136136

137137
function sheet_to_formulae(sheet/*:Worksheet*/)/*:Array<string>*/ {

Diff for: demos/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -44,5 +44,6 @@ can be installed with Bash on Windows or with `cygwin`.
4444
- [`Headless Browsers`](headless/)
4545
- [`canvas-datagrid`](datagrid/)
4646
- [`Swift JSC and other engines`](altjs/)
47+
- [`internet explorer`](oldie/)
4748

4849
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-xlsx?pixel)](https://github.com/SheetJS/js-xlsx)

0 commit comments

Comments
 (0)