updated demos [ci skip]

- frameworks: react, react-native, preact, next.js, weex, nuxt.js
- deployments: nodejs server, duktape, chakra, electron, nw.js
This commit is contained in:
SheetJS 2017-09-12 16:02:06 -04:00
parent ad47cb433c
commit f03e32fc9a
58 changed files with 1730 additions and 71 deletions

@ -167,21 +167,25 @@ CDNjs automatically pulls the latest version and makes all versions available at
The `demos` directory includes sample projects for:
**JS Frameworks and APIs**
**Frameworks and APIs**
- [`angular 1.x`](demos/angular/)
- [`angular 2.x / 4.x`](demos/angular2/)
- [`meteor`](demos/meteor/)
- [`vue 2.x`](demos/vue/)
- [`react and react-native`](demos/react/)
- [`vue 2.x and weex`](demos/vue/)
- [`XMLHttpRequest and fetch`](demos/xhr/)
- [`nodejs server`](demos/server/)
**JS Bundlers and Tooling**
**Bundlers and Tooling**
- [`browserify`](demos/browserify/)
- [`requirejs`](demos/requirejs/)
- [`rollup`](demos/rollup/)
- [`systemjs`](demos/systemjs/)
- [`webpack 2.x`](demos/webpack/)
**JS Platforms and Integrations**
**Platforms and Integrations**
- [`electron application`](demos/electron/)
- [`nw.js application`](demos/nwjs/)
- [`Adobe ExtendScript`](demos/extendscript/)
- [`Headless Browsers`](demos/headless/)
- [`canvas-datagrid`](demos/datagrid/)

@ -89,8 +89,14 @@ function wb_fmt() {
opts.cellNF = true;
if(program.output) sheetname = program.output;
}
workbook_formats.forEach(function(m) { if(program[m]) { wb_fmt(); } });
wb_formats_2.forEach(function(m) { if(program[m[0]]) { wb_fmt(); } });
function isfmt(m) {
if(!program.output) return false;
var t = m.charAt(0) == "." ? m : "." + m;
console.log(m);
return program.output.slice(-m.length) == m;
}
workbook_formats.forEach(function(m) { if(program[m] || isfmt(m)) { wb_fmt(); } });
wb_formats_2.forEach(function(m) { if(program[m[0]] || isfmt(m[0])) { wb_fmt(); } });
if(seen) {
} else if(program.formulae) opts.cellFormula = true;
else opts.cellFormula = false;
@ -129,14 +135,14 @@ var wopts = ({WTF:opts.WTF, bookSST:program.sst}/*:any*/);
if(program.compress) wopts.compression = true;
/* full workbook formats */
workbook_formats.forEach(function(m) { if(program[m]) {
X.writeFile(wb, sheetname || ((filename || "") + "." + m), wopts);
workbook_formats.forEach(function(m) { if(program[m] || isfmt(m)) {
X.writeFile(wb, program.output || sheetname || ((filename || "") + "." + m), wopts);
process.exit(0);
} });
wb_formats_2.forEach(function(m) { if(program[m[0]]) {
wb_formats_2.forEach(function(m) { if(program[m[0]] || isfmt(m[0])) {
wopts.bookType = m[1];
X.writeFile(wb, sheetname || ((filename || "") + "." + m[2]), wopts);
X.writeFile(wb, program.output || sheetname || ((filename || "") + "." + m[2]), wopts);
process.exit(0);
} });
@ -168,9 +174,9 @@ if(program.readOnly) process.exit(0);
['prn', '.prn'],
['txt', '.txt'],
['dif', '.dif']
].forEach(function(m) { if(program[m[0]]) {
].forEach(function(m) { if(program[m[0]] || isfmt(m[1])) {
wopts.bookType = m[0];
X.writeFile(wb, sheetname || ((filename || "") + m[1]), wopts);
X.writeFile(wb, program.output || sheetname || ((filename || "") + m[1]), wopts);
process.exit(0);
} });

@ -1,6 +1,8 @@
jvm-npm.js
sheetjs.*
duk*
*.class
*.jar
rhino
xlsx.swift.js
xlsx.*.js
payload.js

@ -6,8 +6,11 @@ base:
if [ ! -e sheetjs.xlsx ]; then node ../../tests/write.js; fi
.PHONY: duktape
duktape: base ## duktape / skookum demo
sjs skookum.js
duktape: base ## duktape demo
bash ./duktape.sh
gcc -std=c99 -Wall -osheetjs.duk sheetjs.duk.c duktape.c -lm
if [ ! -e xlsx.duktape.js ]; then cp ../../dist/xlsx.full.min.js xlsx.duktape.js; fi
./sheetjs.duk
.PHONY: nashorn
nashorn: base ## nashorn demo
@ -19,6 +22,12 @@ swift: base ## swift demo
if [ ! -e xlsx.swift.js ]; then cp ../../dist/xlsx.full.min.js xlsx.swift.js; fi
./SheetJSCore.swift
.PHONY: chakra
chakra: base ## Chakra demo
node -pe "fs.writeFileSync('payload.js', 'var payload = \"' + fs.readFileSync('sheetjs.xlsx').toString('base64') + '\";')"
cat global.js ../../dist/xlsx.full.min.js payload.js chakra.js > xlsx.chakra.js
chakra ./xlsx.chakra.js
.PHONY: rhinojs ## rhino demo
rhinojs: base SheetJSRhino.class
java -cp .:SheetJS.jar:rhino.jar SheetJSRhino sheetjs.xlsx

@ -50,14 +50,30 @@ context.setOptimizationLevel(-1);
```
## duktape and skookum
## ChakraCore
ChakraCore is an embeddable JS engine written in C++. The library and binary
distributions include a command-line tool `chakra` for running JS scripts.
The simplest way to interop with the engine is to pass Base64 strings. The make
target builds a very simple payload with the data.
## Duktape
[Duktape](http://duktape.org/) is an embeddable JS engine written in C. The
amalgamation makes integration extremely simple! Duktape understands the source
code and can process binary strings out the box, but does not provide I/O or
other standard library features.
amalgamation makes integration extremely simple! It supports `Buffer` natively:
To demonstrate compatibility with duktape, this demo uses the JS runtime from
[Skookum JS](https://github.com/saghul/sjs). Built upon the duktape engine, it
adds a simple I/O interface to enable reading from files.
```C
/* parse a C char array as a workbook object */
duk_push_external_buffer(ctx);
duk_config_buffer(ctx, -1, buf, len);
duk_put_global_string(ctx, "buf");
duk_eval_string_noresult("workbook = XLSX.read(buf, {type:'buffer'});");
/* write a workbook object to a C char array */
duk_eval_string(ctx, "XLSX.write(workbook, {type:'buffer', bookType:'xlsx'})");
duk_size_t sz;
char *buf = (char *)duk_get_buffer_data(ctx, -1, sz);
duk_pop(ctx);
```

3
demos/altjs/chakra.js Normal file

@ -0,0 +1,3 @@
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
var wb = XLSX.read(payload, {type:'base64'});
console.log(XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]));

17
demos/altjs/duktape.sh Executable file

@ -0,0 +1,17 @@
#!/bin/bash
DUKTAPE_VER=2.1.1
if [ ! -e duktape-$DUKTAPE_VER ]; then
if [ ! -e duktape-$DUKTAPE_VER.tar ]; then
if [ ! -e duktape-$DUKTAPE_VER.tar.xz ]; then
curl -O http://duktape.org/duktape-$DUKTAPE_VER.tar.xz
fi
xz -d duktape-$DUKTAPE_VER.tar.xz
fi
tar -xf duktape-$DUKTAPE_VER.tar
fi
for f in duktape.{c,h} duk_config.h; do
cp duktape-$DUKTAPE_VER/src/$f .
done

3
demos/altjs/global.js Normal file

@ -0,0 +1,3 @@
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
var global = (function(){ return this; }).call(null);

101
demos/altjs/sheetjs.duk.c Normal file

@ -0,0 +1,101 @@
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "duktape.h"
#define FAIL_LOAD { \
duk_push_undefined(ctx); \
perror("Error in load_file"); \
return 1; \
}
static char *read_file(const char *filename, size_t *sz) {
FILE *f = fopen(filename, "rb");
if(!f) return NULL;
long fsize; { fseek(f, 0, SEEK_END); fsize = ftell(f); fseek(f, 0, SEEK_SET); }
char *buf = (char *)malloc(fsize * sizeof(char));
*sz = fread((void *) buf, 1, fsize, f);
fclose(f);
return buf;
}
static duk_int_t eval_file(duk_context *ctx, const char *filename) {
size_t len; char *buf = read_file(filename, &len);
if(!buf) FAIL_LOAD
duk_push_lstring(ctx, (const char *)buf, (duk_size_t)len);
duk_int_t retval = duk_peval(ctx);
duk_pop(ctx);
return retval;
}
static duk_int_t load_file(duk_context *ctx, const char *filename, const char *var) {
size_t len; char *buf = read_file(filename, &len);
if(!buf) FAIL_LOAD
duk_push_external_buffer(ctx);
duk_config_buffer(ctx, -1, buf, len);
duk_put_global_string(ctx, var);
return 0;
}
static duk_int_t save_file(duk_context *ctx, const char *filename, const char *var) {
duk_get_global_string(ctx, var);
duk_size_t sz;
char *buf = (char *)duk_get_buffer_data(ctx, -1, &sz);
if(!buf) return 1;
FILE *f = fopen(filename, "wb"); fwrite(buf, 1, sz, f); fclose(f);
return 0;
}
#define FAIL(cmd) { \
printf("error in %s: %s\n", cmd, duk_safe_to_string(ctx, -1)); \
duk_destroy_heap(ctx); \
return res; \
}
#define DOIT(cmd) duk_eval_string_noresult(ctx, cmd);
int main(int argc, char *argv[]) {
duk_int_t res = 0;
/* initialize */
duk_context *ctx = duk_create_heap_default();
/* duktape does not expose a standard "global" by default */
DOIT("var global = (function(){ return this; }).call(null);");
/* load library */
res = eval_file(ctx, "xlsx.duktape.js");
if(res != 0) FAIL("library load")
/* get version string */
duk_eval_string(ctx, "XLSX.version");
printf("SheetJS library version %s\n", duk_get_string(ctx, -1));
duk_pop(ctx);
/* read file */
res = load_file(ctx, "sheetjs.xlsx", "buf");
if(res != 0) FAIL("load sheetjs.xlsx")
/* parse workbook */
DOIT("wb = XLSX.read(buf, {type:'buffer'});");
DOIT("ws = wb.Sheets[wb.SheetNames[0]]");
/* print CSV */
duk_eval_string(ctx, "XLSX.utils.sheet_to_csv(ws)");
printf("%s\n", duk_get_string(ctx, -1));
duk_pop(ctx);
/* change cell A1 to 3 */
DOIT("ws['A1'].v = 3; delete ws['A1'].w;");
/* write file */
DOIT("newbuf = XLSX.write(wb, {type:'buffer', bookType:'xlsx'})");
res = save_file(ctx, "sheetjsw.xlsx", "newbuf");
if(res != 0) FAIL("save sheetjsw.xlsx")
/* cleanup */
duk_destroy_heap(ctx);
return res;
}

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

@ -6,9 +6,9 @@ The library can be imported directly from TS code with:
import * as XLSX from 'xlsx';
```
This demo uses an array of arrays as the core data structure. The component
template includes a file input element, a table that updates based on the data,
and a button to export the data.
This demo uses an array of arrays (type `Array<Array<any>>`) as the core state.
The component template includes a file input element, a table that updates with
the data, and a button to export the data.
## Switching between Angular versions

@ -24,8 +24,8 @@ function s2ab(s: string): ArrayBuffer {
<table class="sjs-table">
<tr *ngFor="let row of data">
<td *ngFor="let val of row">
{{val}}
</td>
{{val}}
</td>
</tr>
</table>
<button (click)="export()">Export!</button>

8
demos/electron/Makefile Normal file

@ -0,0 +1,8 @@
.PHONY: init
init:
mkdir -p node_modules
cd node_modules; if [ ! -e xlsx ]; then ln -s ../../../ xlsx ; fi; cd -
.PHONY: run
run:
electron .

19
demos/electron/README.md Normal file

@ -0,0 +1,19 @@
# Electron
This library is compatible with Electron and should just work out of the box.
The demonstration uses Electron v1.7.5. The library is added via `require` from
the render process. It can also be required from the main process, as shown in
this demo to render a version string in the About dialog on OSX.
The standard HTML5 `FileReader` techniques from the browser apply to Electron.
This demo includes a drag-and-drop box as well as a file input box, mirroring
the [SheetJS Data Preview Live Demo](http://oss.sheetjs.com/js-xlsx/)
Since electron provides an `fs` implementation, `readFile` and `writeFile` can
be used in conjunction with the standard dialogs. For example:
```js
var dialog = require('electron').remote.dialog;
var o = (dialog.showOpenDialog({ properties: ['openFile'] })||[''])[0];
var workbook = X.readFile(o);
```

37
demos/electron/index.html Normal file

@ -0,0 +1,37 @@
<!DOCTYPE html>
<!-- xlsx.js (C) 2013-present SheetJS http://sheetjs.com -->
<!-- vim: set ts=2: -->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>SheetJS Electron Demo</title>
<style>
#drop{
border:2px dashed #bbb;
-moz-border-radius:5px;
-webkit-border-radius:5px;
border-radius:5px;
padding:25px;
text-align:center;
font:20pt bold,"Vollkorn";color:#bbb
}
a { text-decoration: none }
</style>
</head>
<body>
<pre>
<b><a href="http://sheetjs.com">SheetJS Electron Demo</a></b>
<a href="https://github.com/SheetJS/js-xlsx">Source Code Repo</a>
<a href="https://github.com/SheetJS/js-xlsx/issues">Issues? Something look weird? Click here and report an issue</a>
<br />
<button id="readf">Click here to select a file from your computer</button><br />
<div id="drop">Drop a spreadsheet file here to see sheet data</div>
<input type="file" name="xlfile" id="xlf" /> ... or click here to select a file
</pre>
<div id="htmlout"></div>
<br />
<script src="index.js"></script>
</body>
</html>

79
demos/electron/index.js Normal file

@ -0,0 +1,79 @@
var X = require('xlsx');
var electron = require('electron').remote;
var process_wb = (function() {
var HTMLOUT = document.getElementById('htmlout');
return function process_wb(wb) {
HTMLOUT.innerHTML = "";
wb.SheetNames.forEach(function(sheetName) {
var htmlstr = X.write(wb, {sheet:sheetName, type:'binary', bookType:'html'});
HTMLOUT.innerHTML += htmlstr;
});
};
})();
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-36810333-1']);
_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);
})();
var do_file = (function() {
return function do_file(files) {
var f = files[0];
var reader = new FileReader();
reader.onload = function(e) {
var data = e.target.result;
data = new Uint8Array(data);
process_wb(X.read(data, {type: 'array'}));
};
reader.readAsArrayBuffer(f);
};
})();
(function() {
var drop = document.getElementById('drop');
function handleDrop(e) {
e.stopPropagation();
e.preventDefault();
do_file(e.dataTransfer.files);
}
function handleDragover(e) {
e.stopPropagation();
e.preventDefault();
e.dataTransfer.dropEffect = 'copy';
}
drop.addEventListener('dragenter', handleDragover, false);
drop.addEventListener('dragover', handleDragover, false);
drop.addEventListener('drop', handleDrop, false);
})();
(function() {
var readf = document.getElementById('readf');
function handleF(e) {
var o = electron.dialog.showOpenDialog({
title: 'Select a file',
filters: [{
name: "Spreadsheets",
extensions: "xls|xlsx|xlsm|xlsb|xml|xlw|xlc|csv|txt|dif|sylk|slk|prn|ods|fods|uos|dbf|wks|123|wq1|qpw|htm|html".split("|")
}],
properties: ['openFile']
});
if(o.length > 0) process_wb(X.readFile(o[0]));
}
readf.addEventListener('click', handleF, false);
})();
(function() {
var xlf = document.getElementById('xlf');
function handleFile(e) { do_file(e.target.files); }
xlf.addEventListener('change', handleFile, false);
})();

19
demos/electron/main.js Normal file

@ -0,0 +1,19 @@
/* from the electron quick-start */
var electron = require('electron');
var XLSX = require('xlsx');
var app = electron.app;
var win = null;
function createWindow() {
if(win) return;
win = new electron.BrowserWindow({width:800, height:600});
win.loadURL("file://" + __dirname + "/index.html");
win.webContents.openDevTools();
win.on('closed', function() { win = null; });
}
if(app.setAboutPanelOptions) app.setAboutPanelOptions({ applicationName: 'sheetjs-electron', applicationVersion: "XLSX " + XLSX.version, copyright: "(C) 2017-present SheetJS LLC" });
app.on('open-file', function() { console.log(arguments); });
app.on('ready', createWindow);
app.on('activate', createWindow);
app.on('window-all-closed', function() { if(process.platform !== 'darwin') app.quit(); });

@ -0,0 +1,10 @@
{
"name": "sheetjs-electron",
"author": "sheetjs",
"version": "0.0.0",
"main": "main.js",
"dependencies": {
"electron": "~1.7.x",
"xlsx": "*"
}
}

7
demos/nwjs/Makefile Normal file

@ -0,0 +1,7 @@
.PHONY: init
init:
if [ ! -e xlsx.full.min.js ]; then ln -s ../../dist/xlsx.full.min.js . ; fi
.PHONY: run
run:
nw .

10
demos/nwjs/README.md Normal file

@ -0,0 +1,10 @@
# NW.js
This library is compatible with NW.js and should just work out of the box.
The demonstration uses NW.js 0.24 with the dist script.
The standard HTML5 `FileReader` techniques from the browser apply to NW.js.
This demo includes a drag-and-drop box as well as a file input box, mirroring
the [SheetJS Data Preview Live Demo](http://oss.sheetjs.com/js-xlsx/)

37
demos/nwjs/index.html Normal file

@ -0,0 +1,37 @@
<!DOCTYPE html>
<!-- xlsx.js (C) 2013-present SheetJS http://sheetjs.com -->
<!-- vim: set ts=2: -->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>SheetJS NW.js Demo</title>
<style>
#drop{
border:2px dashed #bbb;
-moz-border-radius:5px;
-webkit-border-radius:5px;
border-radius:5px;
padding:25px;
text-align:center;
font:20pt bold,"Vollkorn";color:#bbb
}
a { text-decoration: none }
</style>
</head>
<body>
<pre>
<b><a href="http://sheetjs.com">SheetJS NW.js Demo</a></b>
<a href="https://github.com/SheetJS/js-xlsx">Source Code Repo</a>
<a href="https://github.com/SheetJS/js-xlsx/issues">Issues? Something look weird? Click here and report an issue</a>
<br />
<div id="drop">Drop a spreadsheet file here to see sheet data</div>
<input type="file" name="xlfile" id="xlf" /> ... or click here to select a file
</pre>
<div id="htmlout"></div>
<br />
<script src="xlsx.full.min.js"></script>
<script src="index.js"></script>
</body>
</html>

62
demos/nwjs/index.js Normal file

@ -0,0 +1,62 @@
var X = XLSX;
var process_wb = (function() {
var HTMLOUT = document.getElementById('htmlout');
return function process_wb(wb) {
HTMLOUT.innerHTML = "";
wb.SheetNames.forEach(function(sheetName) {
var htmlstr = X.write(wb, {sheet:sheetName, type:'binary', bookType:'html'});
HTMLOUT.innerHTML += htmlstr;
});
};
})();
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-36810333-1']);
_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);
})();
var do_file = (function() {
return function do_file(files) {
var f = files[0];
var reader = new FileReader();
reader.onload = function(e) {
var data = e.target.result;
data = new Uint8Array(data);
process_wb(X.read(data, {type: 'array'}));
};
reader.readAsArrayBuffer(f);
};
})();
(function() {
var drop = document.getElementById('drop');
function handleDrop(e) {
e.stopPropagation();
e.preventDefault();
do_file(e.dataTransfer.files);
}
function handleDragover(e) {
e.stopPropagation();
e.preventDefault();
e.dataTransfer.dropEffect = 'copy';
}
drop.addEventListener('dragenter', handleDragover, false);
drop.addEventListener('dragover', handleDragover, false);
drop.addEventListener('drop', handleDrop, false);
})();
(function() {
var xlf = document.getElementById('xlf');
function handleFile(e) { do_file(e.target.files); }
xlf.addEventListener('change', handleFile, false);
})();

10
demos/nwjs/package.json Normal file

@ -0,0 +1,10 @@
{
"name": "sheetjs-nwjs",
"author": "sheetjs",
"version": "0.0.0",
"main": "index.html",
"dependencies": {
"nw": "~0.24.4",
"xlsx": "*"
}
}

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

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

2
demos/react/.gitignore vendored Normal file

@ -0,0 +1,2 @@
SheetJS
.next

22
demos/react/Makefile Normal file

@ -0,0 +1,22 @@
.PHONY: react
react: ## Simple server for react and clones
python -mSimpleHTTPServer
.PHONY: next
next: ## next.js demo
# next doesn't support jsx extension
mkdir -p pages
cp sheetjs.jsx pages/sheetjs.js
next
.PHONY: native
native: ## Build react-native project
bash ./native.sh
.PHONY: ios
ios: native ## react-native ios sim
cd SheetJS; react-native run-ios; cd -
.PHONY: android
android: native ## react-native android sim
cd SheetJS; react-native run-android; cd -

68
demos/react/README.md Normal file

@ -0,0 +1,68 @@
# React
The `xlsx.core.min.js` and `xlsx.full.min.js` scripts are designed to be dropped
into web pages with script tags e.g.
```html
<script src="xlsx.full.min.js"></script>
```
The library can also be imported directly from JSX code with:
```js
import * as XLSX from 'xlsx';
```
This demo shows a simple JSX component transpiled in the browser using the babel
standalone library. Since there is no standard React table model, this demo
settles on the array of arrays approach.
Other scripts in this demo show:
- server-rendered React component (with `next.js`)
- `preact` using the react compatibility library
- `react-native` deployment for iOS and android
## Internal State
The simplest state representation is an array of arrays. To avoid having the
table component depend on the library, the column labels are precomputed. The
state in this demo is shaped like the following object:
```js
{
cols: [
{ name: "A", key: 0 },
{ name: "B", key: 1 },
{ name: "C", key: 2 },
],
data: [
[ "id", "name", "value" ],
[ 1, "sheetjs", 7262 ]
[ 2, "js-xlsx", 6969 ]
]
}
```
The appropriate state model is application-specific.
## React Native
<img src="screen.png" width="400px"/>
Reproducing the full project is straightforward:
```bash
react-native init SheetJS
cd SheetJS
npm i -S xlsx react react-native react-native-table-component react-native-fs
cp ../react-native.js index.ios.js
cp ../react-native.js index.android.js
react-native link
```
This uses `react-native-fs` to read and write files on devices. The app will
prompt before reading and after writing data. The printed location will be:
- android: path in the device filesystem
- iOS simulator: local path to file
- iOS device: a path accessible from iTunes App Documents view

40
demos/react/index.html Normal file

@ -0,0 +1,40 @@
<!DOCTYPE html>
<!-- xlsx.js (C) 2013-present SheetJS http://sheetjs.com -->
<!-- vim: set ts=2: -->
<html lang="en" style="height: 100%">
<head>
<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="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="https://unpkg.com/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>
<div class="container-fluid">
<h1><a href="http://sheetjs.com">SheetJS React 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 />
</div>
<div id="app" class="container-fluid"></div>
<script type="text/babel" src="sheetjs.jsx"></script>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-36810333-1']);
_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>
<script type="text/babel">
ReactDOM.render( <SheetJSApp />, document.getElementById('app') );
</script>
</body>
</html>

17
demos/react/native.sh Executable file

@ -0,0 +1,17 @@
#!/bin/bash
if [ ! -e SheetJS ]; then
react-native init SheetJS
cd SheetJS
npm i -S xlsx react react-native react-native-table-component react-native-fs
cd -
fi
if [ ! -e SheetJS/logo.png ]; then
curl -O http://oss.sheetjs.com/assets/img/logo.png
mv logo.png SheetJS/logo.png
fi
cp react-native.js SheetJS/index.ios.js
cp react-native.js SheetJS/index.android.js
cd SheetJS;
react-native link
cd -;

1
demos/react/pages/.gitignore vendored Normal file

@ -0,0 +1 @@
sheetjs.js

@ -0,0 +1,26 @@
import Head from 'next/head'
import SheetJSApp from './sheetjs.js'
export default () => (
<div>
<Head>
<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="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="https://unpkg.com/xlsx/dist/xlsx.full.min.js"></script>
<script src="https://unpkg.com/file-saver/FileSaver.js"></script>
<style jsx>{`
body, #app { height: 100%; };
`}</style>
</Head>
<div class="container-fluid">
<h1><a href="http://sheetjs.com">SheetJS React 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 />
</div>
<SheetJSApp />
</div>
)

42
demos/react/preact.html Normal file

@ -0,0 +1,42 @@
<!DOCTYPE html>
<!-- xlsx.js (C) 2013-present SheetJS http://sheetjs.com -->
<!-- vim: set ts=2: -->
<html lang="en" style="height: 100%">
<head>
<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="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>
<style>body, #app { height: 100%; };</style>
</head>
<body>
<div class="container-fluid">
<h1><a href="http://sheetjs.com">SheetJS React 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 />
</div>
<div id="app" class="container-fluid"></div>
<script type="text/babel" src="sheetjs.jsx"></script>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-36810333-1']);
_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>
<script type="text/babel">
ReactDOM.render( <SheetJSApp />, document.getElementById('app') );
</script>
</body>
</html>

76
demos/react/react-native.js vendored Normal file

@ -0,0 +1,76 @@
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
import * as XLSX from 'xlsx';
import React, { Component } from 'react';
import { AppRegistry, StyleSheet, Text, View, Button, Alert, Image } from 'react-native';
import { Table, Row, Rows } from 'react-native-table-component';
import { writeFile, readFile, DocumentDirectoryPath } from 'react-native-fs'
const DDP = DocumentDirectoryPath + "/";
const make_cols = refstr => Array.from({length: XLSX.utils.decode_range(refstr).e.c + 1}, (x,i) => XLSX.utils.encode_col(i));
export default class SheetJS extends Component {
constructor(props) {
super(props);
this.state = {
data: [[1,2,3],[4,5,6]],
cols: make_cols("A1:C2")
};
this.importFile = this.importFile.bind(this);
this.exportFile = this.exportFile.bind(this);
};
importFile() {
Alert.alert("Rename file to sheetjs.xlsx", "Copy to " + DDP, [
{text: 'Cancel', onPress: () => {}, style: 'cancel' },
{text: 'Import', onPress: () => {
readFile(DDP + "sheetjs.xlsx", 'ascii').then((res) => {
const wb = XLSX.read(res, {type:'binary'});
const wsname = wb.SheetNames[0];
const ws = wb.Sheets[wsname];
const data = XLSX.utils.sheet_to_json(ws, {header:1});
this.setState({ data: data, cols: make_cols(ws['!ref']) });
}).catch((err) => { Alert.alert("importFile Error", "Error " + err.message); });
}}
]);
}
exportFile() {
const ws = XLSX.utils.aoa_to_sheet(this.state.data);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "SheetJS");
const wbout = XLSX.write(wb, {type:"binary", bookType:"xlsx"});
const file = DDP + "sheetjsw.xlsx";
writeFile(file, wbout, 'ascii').then((res) =>{
Alert.alert("exportFile success", "Exported to " + file);
}).catch((err) => { Alert.alert("exportFile Error", "Error " + err.message); });
};
render() { return (
<View style={styles.container}>
<Image style={{width: 128, height: 128}} source={require('./logo.png')} />
<Text style={styles.welcome}>SheetJS React Native Demo</Text>
<Text style={styles.instructions}>Import Data</Text>
<Button onPress={this.importFile} title="Import data from a spreadsheet" color="#841584" />
<Text style={styles.instructions}>Export Data</Text>
<Button disabled={!this.state.data.length} onPress={this.exportFile} title="Export data to XLSX" color="#841584" />
<Text style={styles.instructions}>Current Data</Text>
<Table style={styles.table}>
<Row data={this.state.cols} style={styles.thead} textStyle={styles.text}/>
<Rows data={this.state.data} style={styles.tr} textStyle={styles.text}/>
</Table>
</View>
); };
};
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#F5FCFF' },
welcome: { fontSize: 20, textAlign: 'center', margin: 10 },
instructions: { textAlign: 'center', color: '#333333', marginBottom: 5 },
thead: { height: 40, backgroundColor: '#f1f8ff' },
tr: { height: 30 },
text: { marginLeft: 5 },
table: { width: "100%" }
});
AppRegistry.registerComponent('SheetJS', () => SheetJS);

BIN
demos/react/screen.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

139
demos/react/sheetjs.jsx Normal file

@ -0,0 +1,139 @@
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
const SheetJSFT = [
"xlsx", "xlsb", "xlsm", "xls", "xml", "csv", "txt", "ods", "fods", "uos", "sylk", "dif", "dbf", "prn", "qpw", "123", "wb*", "wq*", "html", "htm"
].map(function(x) { return "." + x; }).join(",");
/*
Simple HTML5 file drag-and-drop wrapper
usage: <DragDropFile handleFile={handleFile}>...</DragDropFile>
handleFile(file:File):void;
*/
class DragDropFile extends React.Component {
constructor(props) {
super(props);
this.onDrop = this.onDrop.bind(this);
};
suppress(evt) { evt.stopPropagation(); evt.preventDefault(); };
onDrop(evt) { evt.stopPropagation(); evt.preventDefault();
const files = evt.dataTransfer.files;
if(files && files[0]) this.props.handleFile(files[0]);
};
render() { return (
<div onDrop={this.onDrop} onDragEnter={this.suppress} onDragOver={this.suppress}>
{this.props.children}
</div>
); };
};
/*
Simple HTML5 file input wrapper
usage: <DataInput handleFile={callback} />
handleFile(file:File):void;
*/
class DataInput extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
};
handleChange(e) {
const files = e.target.files;
if(files && files[0]) this.props.handleFile(files[0]);
};
render() { return (
<form className="form-inline">
<div className="form-group">
<label htmlFor="file">Spreadsheet</label>
<input type="file" className="form-control" id="file" accept={SheetJSFT} onChange={this.handleChange} />
</div>
</form>
); };
}
/* 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}));
/*
Simple HTML Table
usage: <OutTable data={data} cols={cols} />
data:Array<Array<any> >;
cols:Array<{name:string, key:number|string}>;
*/
class OutTable extends React.Component {
constructor(props) { super(props); };
render() { return (
<div className="table-responsive">
<table className="table table-striped">
<thead>
<tr>{this.props.cols.map((c) => <th>{c.name}</th>)}</tr>
</thead>
<tbody>
{this.props.data.map(r => <tr>
{this.props.cols.map(c => <td key={c.key}>{ r[c.key] }</td>)}
</tr>)}
</tbody>
</table>
</div>
); };
};
/* see Browser download file example in docs */
function s2ab(s) {
const buf = new ArrayBuffer(s.length);
const view = new Uint8Array(buf);
for (let i=0; i!=s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
return buf;
}
class SheetJSApp extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [], /* Array of Arrays e.g. [["a","b"],[1,2]] */
cols: [] /* Array of column objects e.g. { name: "C", key: 2 } */
};
this.handleFile = this.handleFile.bind(this);
this.exportFile = this.exportFile.bind(this);
};
handleFile(file/*:File*/) {
/* Boilerplate to set up FileReader */
const reader = new FileReader();
reader.onload = (e) => {
/* Parse data */
const bstr = e.target.result;
const wb = XLSX.read(bstr, {type:'binary'});
/* Get first worksheet */
const wsname = wb.SheetNames[0];
const ws = wb.Sheets[wsname];
/* Convert array of arrays */
const data = XLSX.utils.sheet_to_json(ws, {header:1});
/* Update state */
this.setState({ data: data, cols: make_cols(ws['!ref']) });
};
reader.readAsBinaryString(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:"binary", bookType:"xlsx"});
/* send to client */
saveAs(new Blob([s2ab(wbout)],{type:"application/octet-stream"}), "sheetjs.xlsx");
};
render() { return (
<DragDropFile handleFile={this.handleFile}>
<div className="row"><div className="col-xs-12">
<DataInput handleFile={this.handleFile} />
</div></div>
<div className="row"><div className="col-xs-12">
<button disabled={!this.state.data.length} className="btn btn-success" onClick={this.exportFile}>Export</button>
</div></div>
<div className="row"><div className="col-xs-12">
<OutTable data={this.state.data} cols={this.state.cols} />
</div></div>
</DragDropFile>
); };
};
if(typeof module !== 'undefined') module.exports = SheetJSApp

21
demos/server/Makefile Normal file

@ -0,0 +1,21 @@
.PHONY: init
init:
if [ ! -e sheetjs.xlsx ]; then ln -s ../../sheetjs.xlsx; fi
mkdir -p node_modules
cd node_modules; if [ ! -e xlsx ]; then ln -s ../../../ xlsx; fi; cd -
.PHONY: request
request: init ## request demo
node _request.js
.PHONY: express
express: init ## express demo
node express.js
.PHONY: micro
micro: init ## micro demo
micro -p 7262 micro.js
.PHONY: koa
koa: init ## koa demo
node koa.js

96
demos/server/README.md Normal file

@ -0,0 +1,96 @@
# NodeJS Server Deployments
This library is 100% pure JS. This is great for compatibility but tends to lock
up long-running processes. In the web browser, Web Workers are used to offload
work from the main browser thread. In NodeJS, there are other strategies. This
demo shows a few different strategies applied to different server frameworks.
NOTE: these examples merely demonstrate the core concepts and do not include
appropriate error checking or other production-level features.
### Node Buffer
The `read` and `write` functions can handle `Buffer` data with `type:"buffer"`.
For example, the `request` library returns data in a buffer:
```js
var XLSX = require('xlsx'), request = require('request');
request(url, {encoding: null}, function(err, res, data) {
if(err || res.statusCode !== 200) return;
/* data is a node Buffer that can be passed to XLSX.read */
var workbook = XLSX.read(data, {type:'buffer'});
/* DO SOMETHING WITH workbook HERE */
});
```
### Example servers
Each example server is expected to hold an array-of-arrays in memory. They are
expected to handle:
- `POST / ` accepts an encoded `file` and updates the internal storage
- `GET /?t=<type>` returns the internal storage in the specified type
- `POST /?f=<name>` reads the local file and updates the internal storage
- `GET /?f=<name>` writes the file to the specified name
Testing with cURL is straightforward:
```bash
# upload test.xls and update data
curl -X POST -F "data=@test.xls" http://localhost:7262/
# download data in SYLK format
curl -X GET http://localhost:7262/?t=slk
# read sheetjs.xlsx from the server directory
curl -X POST http://localhost:7262/?f=sheetjs.xlsx
# write sheetjs.xlsb in the XLSB format
curl -X GET http://localhost:7262/?f=sheetjs.xlsb
```
## Main-process logic with express
The most straightforward approach is to handle the data directly in HTTP event
handlers. The `buffer` type for `XLSX.read` and `XLSX.write` work with `http`
module and with express directly. The following snippet generates a workbook
based on an array of arrays and sends it to the client:
```js
function send_aoa_to_client(req, res, data, bookType) {
/* generate workbook */
var ws = XLSX.utils.aoa_to_sheet(data);
var wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "SheetJS");
/* generate buffer */
var buf = XLSX.write(wb, {type:'buffer', bookType:bookType || "xlsx"});
/* send to client */
res.status(200).send(buf);
}
```
## fork with koa
`child_process.fork` provides a light-weight and customizable way to offload
work from the main server process. This demo passes commands to a custom child
process and the child passes back buffers of data.
The main server script is `koa.js` and the worker script is `koasub.js`. State
is maintained in the worker script.
## xlsx script with micro
The node module ships with the `xlsx` bin script. For global installs, symlinks
are configured to enable running `xlsx` from anywhere. For local installs, the
appropriate symlink is set up in `node_modules/.bin/`.
The `--arrays` option directs `xlsx` to generate an array of arrays that can be
parsed by the server. To generate files, the `json2csv` module exports the JS
array of arrays to a CSV, the server writes the file, and the `xlsx` command is
used to generate files of different formats.

4
demos/server/_cors.js Normal file

@ -0,0 +1,4 @@
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
var cors = function(req, res) { res.header('Access-Control-Allow-Origin', '*'); };
cors.mw = function(req, res, next) { cors(req, res); next(); };
module.exports = cors;

7
demos/server/_logit.js Normal file

@ -0,0 +1,7 @@
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
var sprintf = require('printj').sprintf;
var logit = function(req, res) {
console.log(sprintf("%s %s %d", req.method, req.url, res.statusCode));
};
logit.mw = function(req, res, next) { logit(req, res); next(); }
module.exports = logit;

9
demos/server/_request.js Normal file

@ -0,0 +1,9 @@
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
var XLSX = require('xlsx'), request = require('request');
var url = 'http://www.freddiemac.com/pmms/2017/historicalweeklydata.xls'
request(url, {encoding: null}, function(err, res, data) {
if(err || res.statusCode !== 200) return;
var wb = XLSX.read(data, {type:'buffer'});
var ws = wb.Sheets[wb.SheetNames[0]];
console.log(XLSX.utils.sheet_to_csv(ws, {blankrows:false}));
});

65
demos/server/express.js Normal file

@ -0,0 +1,65 @@
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
var fs = require('fs'), path = require('path'), URL = require('url');
var express = require('express'), app = express();
var sprintf = require('printj').sprintf;
var logit = require('./_logit');
var cors = require('./_cors');
var data = "a,b,c\n1,2,3".split("\n").map(function(x) { return x.split(","); });
var XLSX = require('xlsx');
/* helper to generate the workbook object */
function make_book() {
var ws = XLSX.utils.aoa_to_sheet(data);
var wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "SheetJS");
return wb;
}
function get_data(req, res, type) {
var wb = make_book();
/* send buffer back */
res.status(200).send(XLSX.write(wb, {type:'buffer', bookType:type}));
}
function get_file(req, res, file) {
var wb = make_book();
/* write using XLSX.writeFile */
XLSX.writeFile(wb, file);
res.status(200).send("wrote to " + file + "\n");
}
function load_data(file) {
var wb = XLSX.readFile(file);
/* generate array of arrays */
data = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]], {header:1});
console.log(data);
}
function post_data(req, res) {
var keys = Object.keys(req.files), k = keys[0];
load_data(req.files[k].path);
res.status(200).send("ok\n");
}
function post_file(req, res, file) {
load_data(file);
res.status(200).send("ok\n");
}
app.use(logit.mw);
app.use(cors.mw);
app.use(require('express-formidable')());
app.get('/', function(req, res, next) {
var url = URL.parse(req.url, true);
if(url.query.t) return get_data(req, res, url.query.t);
else if(url.query.f) return get_file(req, res, url.query.f);
res.status(403).end("Forbidden");
});
app.post('/', function(req, res, next) {
var url = URL.parse(req.url, true);
if(url.query.f) return post_file(req, res, url.query.f);
return post_data(req, res);
});
var port = +process.argv[2] || +process.env.PORT || 7262;
app.listen(port, function() { console.log('Serving HTTP on port ' + port); });

41
demos/server/hapi.js Normal file

@ -0,0 +1,41 @@
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
var Hapi = require('hapi'), server = new Hapi.Server();
var logit = require('./_logit');
var Worker = require('webworker-threads').Worker;
var data = "a,b,c\n1,2,3".split("\n").map(x => x.split(","));
function get_data(req, res, type) {
var work = new Worker(function(){
var XLSX = require('xlsx');
this.onmessage = function(e) {
console.log("get data " + e.data);
var ws = XLSX.utils.aoa_to_sheet(e.data[1]);
var wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "SheetJS");
console.log("prepared wb");
postMessage(XLSX.write(wb, {type:'binary', bookType:type}));
console.log("sent data");
};
});
work.onmessage = function(e) { console.log(e); res(e); };
work.postMessage([type, data]);
}
var port = 7262;
server.connection({ host:'localhost', port: port});
server.route({ method: 'GET', path: '/', handler: function(req, res) {
logit(req.raw.req, req.raw.res);
if(req.query.t) return get_data(req, res, req.query.t);
else if(req.query.f) return get_file(req, res, req.query.f);
return res('Forbidden').code(403);
}});
server.route({ method: 'POST', path: '/', handler: function(req, res) {
logit(req.raw.req, req.raw.res);
if(req.query.f) return post_file(req, res, req.query.f);
return post_data(req, res);
}});
server.start(function(err) {
if(err) throw err;
console.log('Serving HTTP on port ' + port);
});

79
demos/server/koa.js Normal file

@ -0,0 +1,79 @@
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
const Koa = require('koa'), app = new Koa();
const { sprintf } = require('printj');
const { IncomingForm } = require('formidable');
const { fork } = require('child_process');
const logit = require('./_logit');
const subprocess = fork('koasub.js');
const get_data = async (ctx, type) => {
await new Promise((resolve, reject) => {
const cb = (data) => {
ctx.response.body = Buffer(data);
subprocess.removeListener('message', cb);
resolve();
};
subprocess.on('message', cb);
subprocess.send(['get data', type]);
});
};
const get_file = async (ctx, file) => {
await new Promise((resolve, reject) => {
const cb = (data) => {
ctx.response.body = Buffer(data);
subprocess.removeListener('message', cb);
resolve();
};
subprocess.on('message', cb);
subprocess.send(['get file', file]);
});
};
const load_data = async (ctx, file) => {
await new Promise((resolve, reject) => {
const cb = (data) => {
ctx.response.body = "ok\n";
subprocess.removeListener('message', cb);
resolve();
};
subprocess.on('message', cb);
subprocess.send(['load data', file]);
});
};
const post_data = async (ctx) => {
const keys = Object.keys(ctx.request._files), k = keys[0];
await load_data(ctx, ctx.request._files[k].path);
};
app.use(async (ctx, next) => { logit(ctx.req, ctx.res); await next(); });
app.use(async (ctx, next) => {
const form = new IncomingForm();
await new Promise((resolve, reject) => {
form.parse(ctx.req, (err, fields, files) => {
if(err) return reject(err);
ctx.request._fields = fields;
ctx.request._files = files;
resolve();
});
});
await next();
});
app.use(async (ctx, next) => {
if(ctx.request.method !== 'GET') await next();
else if(ctx.request.path !== '/') await next();
else if(ctx.request.query.t) await get_data(ctx, ctx.request.query.t);
else if(ctx.request.query.f) await get_file(ctx, ctx.request.query.f);
else ctx.throw(403, "Forbidden");
});
app.use(async (ctx, next) => {
if(ctx.request.method !== 'POST') await next();
else if(ctx.request.path !== '/') await next();
else if(ctx.request.query.f) await load_data(ctx, ctx.request.query.f);
else await post_data(ctx);
});
const port = +process.argv[2] || +process.env.PORT || 7262;
app.listen(port, () => { console.log('Serving HTTP on port ' + port); });

39
demos/server/koasub.js Normal file

@ -0,0 +1,39 @@
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
const XLSX = require('xlsx');
let data = "a,b,c\n1,2,3".split("\n").map(x => x.split(","));
process.on('message', ([m, data] = _) => {
switch(m) {
case 'load data': load_data(data); break;
case 'get data': get_data(data); break;
case 'get file': get_file(data); break;
}
});
function load_data(file) {
var wb = XLSX.readFile(file);
/* generate array of arrays */
data = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]], {header:1});
console.log(data);
process.send("done");
}
/* helper to generate the workbook object */
function make_book() {
var ws = XLSX.utils.aoa_to_sheet(data);
var wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "SheetJS");
return wb;
}
function get_data(type) {
var wb = make_book();
/* send buffer back */
process.send(XLSX.write(wb, {type:'buffer', bookType:type}));
}
function get_file(file) {
var wb = make_book();
/* write using XLSX.writeFile */
XLSX.writeFile(wb, file);
process.send("wrote to " + file + "\n");
}

85
demos/server/micro.js Normal file

@ -0,0 +1,85 @@
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
var fs = require('fs');
var URL = require('url');
var child_process = require('child_process');
var micro = require('micro'), formidable = require('formidable');
var logit = require('./_logit'), cors = require('./_cors');
var json2csv = require('json2csv');
var data = "a,b,c\n1,2,3".split("\n").map(function(x) { return x.split(","); });
var xlsx = '../../bin/xlsx.njs';
function get_data(req, res, type) {
var file = "_tmp." + type;
/* prepare CSV */
var csv = json2csv({data:data, hasCSVColumnTitle:false});
/* write it to a temp file */
fs.writeFile('tmp.csv', csv, function(err1) {
/* call xlsx to read the csv and write to another temp file */
child_process.exec(xlsx+' tmp.csv -o '+ file, function(err, stdout, stderr){
cors(req, res);
/* read the new file and send it to the client */
micro.send(res, 200, fs.readFileSync(file));
});
});
}
function get_file(req, res, file) {
var csv = json2csv({data:data, hasCSVColumnTitle:false});
fs.writeFile('tmp.csv', csv, function(err1) {
/* write to specified file */
child_process.exec(xlsx+' tmp.csv -o '+file, function(err, stdout, stderr) {
cors(req, res);
micro.send(res, 200, "wrote to " + file + "\n");
});
});
}
function post_data(req, res) {
var form = new formidable.IncomingForm();
form.on('file', function(field, file) {
/* file.path is the location of the file in the system */
child_process.exec(xlsx+' --arrays ' + file.path, post_cb(req, res));
});
form.parse(req);
}
function post_file(req, res, file) {
child_process.exec(xlsx+' --arrays ' + file, post_cb(req, res));
}
function post_cb(req, res) {
return function(err, stdout, stderr) {
cors(req, res);
/* xlsx --arrays writes JSON to stdout, so parse and assign to data var */
data = JSON.parse(stdout);
console.log(data);
return micro.send(res, 200, "ok\n");
};
}
function get(req, res) {
var url = URL.parse(req.url, true);
if(url.pathname.length > 1) micro.send(res, 404, "File not found");
else if(url.query.t) get_data(req, res, url.query.t);
else if(url.query.f) get_file(req, res, url.query.f);
else micro.send(res, 403, "Forbidden\n");
}
function post(req, res) {
var url = URL.parse(req.url, true);
if(url.pathname.length > 1) micro.send(res, 404, "File not found");
else if(url.query.f) post_file(req, res, url.query.f);
else post_data(req, res);
}
module.exports = function(req, res) {
logit(req, res);
switch(req.method) {
case 'GET': return get(req, res);
case 'POST': return post(req, res);
}
return micro.send(res, 501, "Unsupported method " + req.method + "\n");
};

2
demos/vue/.gitignore vendored Normal file

@ -0,0 +1,2 @@
SheetJS
.nuxt

17
demos/vue/Makefile Normal file

@ -0,0 +1,17 @@
.PHONY: vue
vue: ## Simple server for vue
python -mSimpleHTTPServer
.PHONY: nuxt
nuxt: ## nuxt.js demo
mkdir -p node_modules
cd node_modules; if [ ! -e xlsx ]; then ln -s ../../../ xlsx; fi; cd ..
nuxt
.PHONY: weex
weex: ## Build weex project
bash ./weex.sh
.PHONY: ios
ios: weex ## weex ios sim
cd SheetJS; weexpack run ios; cd -

@ -13,8 +13,59 @@ as you would with any other browser-friendly library.
This demo directly generates HTML using `sheet_to_html` and adds an element to
a pregenerated template. It also has a button for exporting as XLSX.
Other scripts in this demo show:
- server-rendered VueJS component (with `nuxt.js`)
- `weex` deployment for iOS
## Single File Components
For Single File Components, a simple `import XLSX from 'xlsx'` should suffice.
The webpack demo includes a sample `webpack.config.js`.
## WeeX
WeeX is a framework for building real mobile apps, akin to React Native. The
ecosystem is not quite as mature as React Native, missing basic features like
document access. As a result, this demo uses the `stream.fetch` API to upload
Base64-encoded documents to <https://hastebin.com> and download a precomputed
[Base64-encoded workbook](http://sheetjs.com/sheetjs.xlsx.b64).
Using NodeJS it is straightforward to convert to/from base64:
```js
/* convert sheetjs.xlsx -> sheetjs.xlsx.b64 */
var buf = fs.readFileSync("sheetjs.xlsx");
fs.writeFileSync("sheetjs.xlsx.b64", buf.toString("base64"));
/* convert sheetjs.xls.b64 -> sheetjs.xls */
var str = fs.readFileSync("sheetjs.xls.b64").toString();
fs.writeFileSync("sheetjs.xls", new Buffer(str, "base64"));
```
## Nuxt and State
The `nuxt.js` demo uses the same state approach as the React next.js demo:
```js
{
cols: [
{ name: "A", key: 0 },
{ name: "B", key: 1 },
{ name: "C", key: 2 },
],
data: [
[ "id", "name", "value" ],
[ 1, "sheetjs", 7262 ]
[ 2, "js-xlsx", 6969 ]
]
}
```
Due to webpack configuration issues on client/server bundles, the library should
be explicitly included in the layout HTML (as script tag) and in the component:
```js
const _XLSX = require('xlsx');
const X = typeof XLSX !== 'undefined' ? XLSX : _XLSX;
/* use the variable X rather than XLSX in the component */
```

@ -1,7 +1,7 @@
<!DOCTYPE html>
<!-- xlsx.js (C) 2013-present SheetJS http://sheetjs.com -->
<!-- vim: set ts=2: -->
<html ng-app="app">
<html>
<head>
<title>SheetJS + VueJS2</title>
<!-- Vue 2 -->
@ -38,6 +38,17 @@ This demo shows a sample Vue component "html-preview" that:
<a href="https://obamawhitehouse.archives.gov/sites/default/files/omb/budget/fy2014/assets/receipts.xls">Sample Spreadsheet</a>
</pre>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-36810333-1']);
_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>
<div id="app">
<html-preview></html-preview>

66
demos/vue/native.vue Normal file

@ -0,0 +1,66 @@
<!-- xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com -->
<template>
<div class="container">
<image :src="logoUrl" class="logo"></image>
<text class="welcome">SheetJS WeeX Demo {{version}}</text>
<text class="instructions">Import Data</text>
<text :style="{ color: '#841584' }" @click="importFile">Download spreadsheet</text>
<text class="instructions">Export Data</text>
<text :style="{ color: data.length ? '#841584' : '#CDCDCD', disabled: !data.length }" @click="exportFile">Upload XLSX</text>
<text style="instructions">Current Data</text>
<scroller class="scroller">
<div class="row" v-for="(row, ridx) in data">
<text>ROW {{ridx + 1}}</text>
<text v-for="(cell, cidx) in row">CELL {{get_label(ridx, cidx)}}:{{cell}}</text>
</div>
</scroller>
</div>
</template>
<style>
.container { height: 100%; flex: 1; justify-content: center; align-items: center; background-color: '#F5FCFF'; }
.logo { width: 256px; height: 256px; }
.welcome { font-size: 40; text-align: 'center'; margin: 10; }
.instructions { padding-top: 20px; color:#888; font-size: 24px;}
.scroller { height: 500px; border-width: 3px; width: 700px; }
.loading { justify-content: center; }
</style>
<script>
import XLSX from 'xlsx';
const modal = weex.requireModule('modal');
const stream = weex.requireModule('stream');
export default {
data: {
data: [[1,2,3],[4,5,6]],
logoUrl: 'http://oss.sheetjs.com/assets/img/logo.png',
version: XLSX.version,
fileUrl: 'http://sheetjs.com/sheetjs.xlsx.b64',
binUrl: 'https://hastebin.com/documents'
},
methods: {
importFile: function (e) {
modal.toast({ message: 'getting ' + this.fileUrl, duration: 1 });
var self = this;
stream.fetch({method:'GET', type:'text', url:this.fileUrl}, function(res){
const wb = XLSX.read(res.data, {type:'base64'});
const ws = wb.Sheets[wb.SheetNames[0]];
self.data = XLSX.utils.sheet_to_json(ws, {header:1});
});
},
exportFile: function (e) {
var self = this;
const ws = XLSX.utils.aoa_to_sheet(this.data);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "SheetJS");
const wbout = XLSX.write(wb, {type:"base64", bookType:"xlsx"});
const body = wbout;
stream.fetch({method:'POST', type:'json', url:this.binUrl, body:body}, function(res) {
modal.toast({ message: 'KEY: ' + res.data.key, duration: 10 });
self.version = res.data.key;
});
},
get_label: function(r, c) { return XLSX.utils.encode_cell({r:r, c:c})}
}
}
</script>

1
demos/vue/package.json Normal file

@ -0,0 +1 @@
{}

102
demos/vue/pages/index.vue Normal file

@ -0,0 +1,102 @@
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
<template>
<div @drop="_drop" @dragenter="_suppress" @dragover="_suppress">
<div class="row"><div class="col-xs-12">
<form class="form-inline">
<div class="form-group">
<label for="file">Spreadsheet</label>
<input type="file" class="form-control" id="file" :accept="SheetJSFT" @change="_change" />
</div>
</form>
</div></div>
<div class="row"><div class="col-xs-12">
<button :disabled="data.length ? false : true" class="btn btn-success" @click="_export">Export</button>
</div></div>
<div class="row"><div class="col-xs-12">
<div class="table-responsive">
<table class="table table-striped">
<thead><tr>
<th v-for="c in cols">{{c.name}}</th>
</tr></thead>
<tbody><tr v-for="r in data">
<td v-for="c in cols"> {{ r[c.key] }}</td>
</tr></tbody>
</table>
</div>
</div></div>
</div>
</template>
<script>
const _XLSX = require('xlsx');
const X = typeof XLSX !== 'undefined' ? XLSX : _XLSX;
const make_cols = refstr => Array(X.utils.decode_range(refstr).e.c + 1).fill(0).map((x,i) => ({name:X.utils.encode_col(i), key:i}));
/* see Browser download file example in docs */
function s2ab(s) {
const buf = new ArrayBuffer(s.length);
const view = new Uint8Array(buf);
for (let i=0; i!=s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
return buf;
}
const _SheetJSFT = [
"xlsx", "xlsb", "xlsm", "xls", "xml", "csv", "txt", "ods", "fods", "uos", "sylk", "dif", "dbf", "prn", "qpw", "123", "wb*", "wq*", "html", "htm"
].map(function(x) { return "." + x; }).join(",");
export default {
data() {
return {
data: ["SheetJS".split(""), "1234567".split("")],
cols: [
{name:"A", key:0},
{name:"B", key:1},
{name:"C", key:2},
{name:"D", key:3},
{name:"E", key:4},
{name:"F", key:5},
{name:"G", key:6},
],
SheetJSFT: _SheetJSFT
}; },
methods: {
_suppress(evt) { evt.stopPropagation(); evt.preventDefault(); },
_drop(evt) {
evt.stopPropagation(); evt.preventDefault();
const files = evt.dataTransfer.files;
if(files && files[0]) this._file(files[0]);
},
_change(evt) {
const files = evt.target.files;
if(files && files[0]) this._file(files[0]);
},
_export(evt) {
/* convert state to workbook */
const ws = X.utils.aoa_to_sheet(this.data);
const wb = X.utils.book_new();
X.utils.book_append_sheet(wb, ws, "SheetJS");
/* generate X file */
const wbout = X.write(wb, {type:"binary", bookType:"xlsx"});
/* send to client */
saveAs(new Blob([s2ab(wbout)],{type:"application/octet-stream"}), "sheetjs.xlsx");
},
_file(file) {
/* Boilerplate to set up FileReader */
const reader = new FileReader();
reader.onload = (e) => {
/* Parse data */
const bstr = e.target.result;
const wb = X.read(bstr, {type:'binary'});
/* Get first worksheet */
const wsname = wb.SheetNames[0];
const ws = wb.Sheets[wsname];
/* Convert array of arrays */
const data = X.utils.sheet_to_json(ws, {header:1});
/* Update state */
this.data = data;
this.cols = make_cols(ws['!ref']);
};
reader.readAsBinaryString(file);
}
}
};
</script>

1
demos/vue/static/xlsx.full.min.js vendored Symbolic link

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

16
demos/vue/weex.sh Normal file

@ -0,0 +1,16 @@
#!/bin/bash
if [ ! -e SheetJS ]; then
weexpack create SheetJS
cd SheetJS
npm install
weexpack platform add ios
sed -i 's/ATSDK-Weex/ATSDK/g' platforms/ios/Podfile
cd -
# weexpack run ios
fi
cp native.vue SheetJS/src/index.vue
if [ ! -e SheetJS/web/bootstrap.min.css ]; then
curl -O https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css
mv bootstrap.min.css SheetJS/web/
fi

@ -1,4 +1,4 @@
# XMLHttpRequest and Friends
# XMLHttpRequest and fetch
`XMLHttpRequest` and `fetch` browser APIs enable binary data transfer between
web browser clients and web servers. Since this library works in web browsers,
@ -14,21 +14,88 @@ name specified in `file`.
To start the demo, run `npm start` and navigate to <http://localhost:7262/>
## XMLHttpRequest (xhr.html)
## XMLHttpRequest
For downloading data, the `arraybuffer` response type generates an `ArrayBuffer`
that can be viewed as an `Uint8Array` and fed to `XLSX.read` using `array` type.
that can be viewed as an `Uint8Array` and fed to `XLSX.read` using `array` type:
```js
/* set up an async GET request */
var req = new XMLHttpRequest();
req.open("GET", url, true);
req.responseType = "arraybuffer";
req.onload = function(e) {
/* parse the data when it is received */
var data = new Uint8Array(oReq.response);
var workbook = XLSX.read(data, {type:"array"});
/* DO SOMETHING WITH workbook HERE */
};
req.send();
```
For uploading data, this demo populates a `FormData` object with string data
generated with the `base64` output type.
generated with the `base64` output type:
## axios (axios.html) and superagent (superagent.html)
```js
/* generate XLSX as base64 string */
var b64 = XLSX.write(workbook, {bookType:'xlsx', type:'base64'});
The codes are structurally similar to the XMLHttpRequest example. `axios` uses
a Promise-based API while `superagent` opts for a more traditional chain.
/* build FormData with the generated file */
var fd = new FormData();
fd.append('data', b64);
## fetch (fetch.html)
/* send data */
var req = new XMLHttpRequest();
req.open("POST", "/upload", true);
req.send(formdata);
```
axios and superagent patterns are similar to the XMLHttpRequest pattern but
involve much less boilerplate:
```js
/* set up an async GET request with axios */
axios(url, {responseType:'arraybuffer'}).then(function(res) {
/* parse the data when it is received */
var data = new Uint8Array(res.data);
var workbook = XLSX.read(data, {type:"array"});
/* DO SOMETHING WITH workbook HERE */
});
/* set up an async GET request with superagent */
superagent.get(url).responseType('arraybuffer').end(function(err, res) {
/* parse the data when it is received */
var data = new Uint8Array(res.body);
var workbook = XLSX.read(data, {type:"array"});
/* DO SOMETHING WITH workbook HERE */
});
```
## fetch
For downloading data, `response.blob()` resolves to a `Blob` object that can be
converted to `ArrayBuffer` using a `FileReader`.
converted to `ArrayBuffer` using a `FileReader`:
```js
fetch(url).then(function(res) {
/* get the data as a Blob */
if(!res.ok) throw new Error("fetch failed");
return res.blob();
}).then(function(blob) {
/* configure a FileReader to process the blob */
var reader = new FileReader();
reader.addEventListener("loadend", function() {
/* parse the data when it is received */
var data = new Uint8Array(this.result);
var workbook = XLSX.read(data, {type:"array"});
/* DO SOMETHING WITH workbook HERE */
});
reader.readAsArrayBuffer(blob);
});
```

@ -4,23 +4,14 @@
var fs = require('fs'), path = require('path');
var express = require('express'), app = express();
var sprintf = require('printj').sprintf;
var logit = require('../server/_logit');
var cors = require('../server/_cors');
var port = +process.argv[2] || +process.env.PORT || 7262;
var basepath = process.cwd();
function doit(cb) {
return function(req, res, next) {
cb(req, res);
next();
};
}
app.use(doit(function(req, res) {
console.log(sprintf("%s %s %d", req.method, req.url, res.statusCode));
}));
app.use(doit(function(req, res) {
res.header('Access-Control-Allow-Origin', '*');
}));
app.use(logit.mw);
app.use(cors.mw);
app.use(require('express-formidable')());
app.post('/upload', function(req, res) {
fs.writeFile(req.fields.file, req.fields.data, 'base64', function(err, r) {

@ -25,21 +25,25 @@ CDNjs automatically pulls the latest version and makes all versions available at
The `demos` directory includes sample projects for:
**JS Frameworks and APIs**
**Frameworks and APIs**
- [`angular 1.x`](demos/angular/)
- [`angular 2.x / 4.x`](demos/angular2/)
- [`meteor`](demos/meteor/)
- [`vue 2.x`](demos/vue/)
- [`react and react-native`](demos/react/)
- [`vue 2.x and weex`](demos/vue/)
- [`XMLHttpRequest and fetch`](demos/xhr/)
- [`nodejs server`](demos/server/)
**JS Bundlers and Tooling**
**Bundlers and Tooling**
- [`browserify`](demos/browserify/)
- [`requirejs`](demos/requirejs/)
- [`rollup`](demos/rollup/)
- [`systemjs`](demos/systemjs/)
- [`webpack 2.x`](demos/webpack/)
**JS Platforms and Integrations**
**Platforms and Integrations**
- [`electron application`](demos/electron/)
- [`nw.js application`](demos/nwjs/)
- [`Adobe ExtendScript`](demos/extendscript/)
- [`Headless Browsers`](demos/headless/)
- [`canvas-datagrid`](demos/datagrid/)

@ -161,21 +161,25 @@ CDNjs automatically pulls the latest version and makes all versions available at
The `demos` directory includes sample projects for:
**JS Frameworks and APIs**
**Frameworks and APIs**
- [`angular 1.x`](demos/angular/)
- [`angular 2.x / 4.x`](demos/angular2/)
- [`meteor`](demos/meteor/)
- [`vue 2.x`](demos/vue/)
- [`react and react-native`](demos/react/)
- [`vue 2.x and weex`](demos/vue/)
- [`XMLHttpRequest and fetch`](demos/xhr/)
- [`nodejs server`](demos/server/)
**JS Bundlers and Tooling**
**Bundlers and Tooling**
- [`browserify`](demos/browserify/)
- [`requirejs`](demos/requirejs/)
- [`rollup`](demos/rollup/)
- [`systemjs`](demos/systemjs/)
- [`webpack 2.x`](demos/webpack/)
**JS Platforms and Integrations**
**Platforms and Integrations**
- [`electron application`](demos/electron/)
- [`nw.js application`](demos/nwjs/)
- [`Adobe ExtendScript`](demos/extendscript/)
- [`Headless Browsers`](demos/headless/)
- [`canvas-datagrid`](demos/datagrid/)