forked from sheetjs/sheetjs
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:
parent
ad47cb433c
commit
f03e32fc9a
12
README.md
12
README.md
@ -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/)
|
||||
|
22
bin/xlsx.njs
22
bin/xlsx.njs
@ -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);
|
||||
} });
|
||||
|
||||
|
4
demos/altjs/.gitignore
vendored
4
demos/altjs/.gitignore
vendored
@ -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
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
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
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
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
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
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
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
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
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(); });
|
10
demos/electron/package.json
Normal file
10
demos/electron/package.json
Normal file
@ -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
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
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
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
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
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
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
2
demos/react/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
SheetJS
|
||||
.next
|
22
demos/react/Makefile
Normal file
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
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
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
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
1
demos/react/pages/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
sheetjs.js
|
26
demos/react/pages/index.js
Normal file
26
demos/react/pages/index.js
Normal file
@ -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
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
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
BIN
demos/react/screen.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 68 KiB |
139
demos/react/sheetjs.jsx
Normal file
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
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
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
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
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
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
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
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
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
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
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
2
demos/vue/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
SheetJS
|
||||
.nuxt
|
17
demos/vue/Makefile
Normal file
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
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
1
demos/vue/package.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
102
demos/vue/pages/index.vue
Normal file
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
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
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/)
|
||||
|
Loading…
Reference in New Issue
Block a user