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

88 files changed

Lines changed: 1098 additions & 416 deletions

Some content is hidden

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

.spelling

Lines changed: 2 additions & 1 deletion
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

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
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

README.md

Lines changed: 69 additions & 25 deletions
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`) |

bits/01_version.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
XLSX.version = '0.11.18';
1+
XLSX.version = '0.11.19';

bits/05_buf.js

Lines changed: 6 additions & 1 deletion
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));

bits/19_fsutils.js

Lines changed: 37 additions & 0 deletions
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+

bits/21_ziputils.js

Lines changed: 3 additions & 4 deletions
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

bits/71_wbcommon.js

Lines changed: 1 addition & 0 deletions
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
}

bits/75_xlml.js

Lines changed: 1 addition & 1 deletion
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
}

bits/79_html.js

Lines changed: 2 additions & 2 deletions
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>';

0 commit comments

Comments
 (0)