From f03e32fc9abd3f984543b36fca25d0910c199c3c Mon Sep 17 00:00:00 2001 From: SheetJS Date: Tue, 12 Sep 2017 16:02:06 -0400 Subject: [PATCH] updated demos [ci skip] - frameworks: react, react-native, preact, next.js, weex, nuxt.js - deployments: nodejs server, duktape, chakra, electron, nw.js --- README.md | 12 +- bin/xlsx.njs | 22 ++-- demos/altjs/.gitignore | 4 +- demos/altjs/Makefile | 13 +- demos/altjs/README.md | 30 ++++- demos/altjs/chakra.js | 3 + demos/altjs/duktape.sh | 17 +++ demos/altjs/global.js | 3 + demos/altjs/sheetjs.duk.c | 101 ++++++++++++++ demos/altjs/skookum.js | 13 -- demos/angular2/README.md | 6 +- demos/angular2/src/app/sheetjs.component.ts | 4 +- demos/electron/Makefile | 8 ++ demos/electron/README.md | 19 +++ demos/electron/index.html | 37 ++++++ demos/electron/index.js | 79 +++++++++++ demos/electron/main.js | 19 +++ demos/electron/package.json | 10 ++ demos/nwjs/Makefile | 7 + demos/nwjs/README.md | 10 ++ demos/nwjs/index.html | 37 ++++++ demos/nwjs/index.js | 62 +++++++++ demos/nwjs/package.json | 10 ++ demos/nwjs/xlsx.full.min.js | 1 + demos/react/.gitignore | 2 + demos/react/Makefile | 22 ++++ demos/react/README.md | 68 ++++++++++ demos/react/index.html | 40 ++++++ demos/react/native.sh | 17 +++ demos/react/pages/.gitignore | 1 + demos/react/pages/index.js | 26 ++++ demos/react/preact.html | 42 ++++++ demos/react/react-native.js | 76 +++++++++++ demos/react/screen.png | Bin 0 -> 70135 bytes demos/react/sheetjs.jsx | 139 ++++++++++++++++++++ demos/server/Makefile | 21 +++ demos/server/README.md | 96 ++++++++++++++ demos/server/_cors.js | 4 + demos/server/_logit.js | 7 + demos/server/_request.js | 9 ++ demos/server/express.js | 65 +++++++++ demos/server/hapi.js | 41 ++++++ demos/server/koa.js | 79 +++++++++++ demos/server/koasub.js | 39 ++++++ demos/server/micro.js | 85 ++++++++++++ demos/vue/.gitignore | 2 + demos/vue/Makefile | 17 +++ demos/vue/README.md | 51 +++++++ demos/vue/index.html | 13 +- demos/vue/native.vue | 66 ++++++++++ demos/vue/package.json | 1 + demos/vue/pages/index.vue | 102 ++++++++++++++ demos/vue/static/xlsx.full.min.js | 1 + demos/vue/weex.sh | 16 +++ demos/xhr/README.md | 85 ++++++++++-- demos/xhr/server.js | 17 +-- docbits/10_install.md | 12 +- misc/docs/README.md | 12 +- 58 files changed, 1730 insertions(+), 71 deletions(-) create mode 100644 demos/altjs/chakra.js create mode 100755 demos/altjs/duktape.sh create mode 100644 demos/altjs/global.js create mode 100644 demos/altjs/sheetjs.duk.c delete mode 100644 demos/altjs/skookum.js create mode 100644 demos/electron/Makefile create mode 100644 demos/electron/README.md create mode 100644 demos/electron/index.html create mode 100644 demos/electron/index.js create mode 100644 demos/electron/main.js create mode 100644 demos/electron/package.json create mode 100644 demos/nwjs/Makefile create mode 100644 demos/nwjs/README.md create mode 100644 demos/nwjs/index.html create mode 100644 demos/nwjs/index.js create mode 100644 demos/nwjs/package.json create mode 120000 demos/nwjs/xlsx.full.min.js create mode 100644 demos/react/.gitignore create mode 100644 demos/react/Makefile create mode 100644 demos/react/README.md create mode 100644 demos/react/index.html create mode 100755 demos/react/native.sh create mode 100644 demos/react/pages/.gitignore create mode 100644 demos/react/pages/index.js create mode 100644 demos/react/preact.html create mode 100644 demos/react/react-native.js create mode 100644 demos/react/screen.png create mode 100644 demos/react/sheetjs.jsx create mode 100644 demos/server/Makefile create mode 100644 demos/server/README.md create mode 100644 demos/server/_cors.js create mode 100644 demos/server/_logit.js create mode 100644 demos/server/_request.js create mode 100644 demos/server/express.js create mode 100644 demos/server/hapi.js create mode 100644 demos/server/koa.js create mode 100644 demos/server/koasub.js create mode 100644 demos/server/micro.js create mode 100644 demos/vue/.gitignore create mode 100644 demos/vue/Makefile create mode 100644 demos/vue/native.vue create mode 100644 demos/vue/package.json create mode 100644 demos/vue/pages/index.vue create mode 120000 demos/vue/static/xlsx.full.min.js create mode 100644 demos/vue/weex.sh diff --git a/README.md b/README.md index 4443b31..91706b0 100644 --- a/README.md +++ b/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/) diff --git a/bin/xlsx.njs b/bin/xlsx.njs index 7eb9f5d..8f20ec6 100755 --- a/bin/xlsx.njs +++ b/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); } }); diff --git a/demos/altjs/.gitignore b/demos/altjs/.gitignore index 96be628..c0858f1 100644 --- a/demos/altjs/.gitignore +++ b/demos/altjs/.gitignore @@ -1,6 +1,8 @@ jvm-npm.js sheetjs.* +duk* *.class *.jar rhino -xlsx.swift.js +xlsx.*.js +payload.js diff --git a/demos/altjs/Makefile b/demos/altjs/Makefile index b4b1526..920aa62 100644 --- a/demos/altjs/Makefile +++ b/demos/altjs/Makefile @@ -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 diff --git a/demos/altjs/README.md b/demos/altjs/README.md index ea7157d..6bfca90 100644 --- a/demos/altjs/README.md +++ b/demos/altjs/README.md @@ -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); +``` diff --git a/demos/altjs/chakra.js b/demos/altjs/chakra.js new file mode 100644 index 0000000..7f9f8c1 --- /dev/null +++ b/demos/altjs/chakra.js @@ -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]])); diff --git a/demos/altjs/duktape.sh b/demos/altjs/duktape.sh new file mode 100755 index 0000000..58d7cdf --- /dev/null +++ b/demos/altjs/duktape.sh @@ -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 + + diff --git a/demos/altjs/global.js b/demos/altjs/global.js new file mode 100644 index 0000000..00bae5d --- /dev/null +++ b/demos/altjs/global.js @@ -0,0 +1,3 @@ +/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ +var global = (function(){ return this; }).call(null); + diff --git a/demos/altjs/sheetjs.duk.c b/demos/altjs/sheetjs.duk.c new file mode 100644 index 0000000..951058a --- /dev/null +++ b/demos/altjs/sheetjs.duk.c @@ -0,0 +1,101 @@ +/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ +#include +#include +#include +#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; +} diff --git a/demos/altjs/skookum.js b/demos/altjs/skookum.js deleted file mode 100644 index 61c325a..0000000 --- a/demos/altjs/skookum.js +++ /dev/null @@ -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]]); diff --git a/demos/angular2/README.md b/demos/angular2/README.md index d5db372..0cfe04c 100644 --- a/demos/angular2/README.md +++ b/demos/angular2/README.md @@ -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>`) 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 diff --git a/demos/angular2/src/app/sheetjs.component.ts b/demos/angular2/src/app/sheetjs.component.ts index 8c57fe2..9948f3d 100644 --- a/demos/angular2/src/app/sheetjs.component.ts +++ b/demos/angular2/src/app/sheetjs.component.ts @@ -24,8 +24,8 @@ function s2ab(s: string): ArrayBuffer { + {{val}} +
- {{val}} -
diff --git a/demos/electron/Makefile b/demos/electron/Makefile new file mode 100644 index 0000000..885c707 --- /dev/null +++ b/demos/electron/Makefile @@ -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 . diff --git a/demos/electron/README.md b/demos/electron/README.md new file mode 100644 index 0000000..e1b0c0a --- /dev/null +++ b/demos/electron/README.md @@ -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); +``` diff --git a/demos/electron/index.html b/demos/electron/index.html new file mode 100644 index 0000000..b959a3b --- /dev/null +++ b/demos/electron/index.html @@ -0,0 +1,37 @@ + + + + + + +SheetJS Electron Demo + + + +
+SheetJS Electron Demo
+
+Source Code Repo
+Issues?  Something look weird?  Click here and report an issue
+
+
+
Drop a spreadsheet file here to see sheet data
+ ... or click here to select a file + +
+
+
+ + + diff --git a/demos/electron/index.js b/demos/electron/index.js new file mode 100644 index 0000000..8fd9962 --- /dev/null +++ b/demos/electron/index.js @@ -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); +})(); diff --git a/demos/electron/main.js b/demos/electron/main.js new file mode 100644 index 0000000..202100b --- /dev/null +++ b/demos/electron/main.js @@ -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(); }); diff --git a/demos/electron/package.json b/demos/electron/package.json new file mode 100644 index 0000000..6241da2 --- /dev/null +++ b/demos/electron/package.json @@ -0,0 +1,10 @@ +{ + "name": "sheetjs-electron", + "author": "sheetjs", + "version": "0.0.0", + "main": "main.js", + "dependencies": { + "electron": "~1.7.x", + "xlsx": "*" + } +} diff --git a/demos/nwjs/Makefile b/demos/nwjs/Makefile new file mode 100644 index 0000000..cb96941 --- /dev/null +++ b/demos/nwjs/Makefile @@ -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 . diff --git a/demos/nwjs/README.md b/demos/nwjs/README.md new file mode 100644 index 0000000..ea677ca --- /dev/null +++ b/demos/nwjs/README.md @@ -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/) + + diff --git a/demos/nwjs/index.html b/demos/nwjs/index.html new file mode 100644 index 0000000..2fc74ed --- /dev/null +++ b/demos/nwjs/index.html @@ -0,0 +1,37 @@ + + + + + + +SheetJS NW.js Demo + + + +
+SheetJS NW.js Demo
+
+Source Code Repo
+Issues?  Something look weird?  Click here and report an issue
+
+
Drop a spreadsheet file here to see sheet data
+ ... or click here to select a file + +
+
+
+ + + + diff --git a/demos/nwjs/index.js b/demos/nwjs/index.js new file mode 100644 index 0000000..5216cb5 --- /dev/null +++ b/demos/nwjs/index.js @@ -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); +})(); diff --git a/demos/nwjs/package.json b/demos/nwjs/package.json new file mode 100644 index 0000000..ee8597d --- /dev/null +++ b/demos/nwjs/package.json @@ -0,0 +1,10 @@ +{ + "name": "sheetjs-nwjs", + "author": "sheetjs", + "version": "0.0.0", + "main": "index.html", + "dependencies": { + "nw": "~0.24.4", + "xlsx": "*" + } +} diff --git a/demos/nwjs/xlsx.full.min.js b/demos/nwjs/xlsx.full.min.js new file mode 120000 index 0000000..dbca48d --- /dev/null +++ b/demos/nwjs/xlsx.full.min.js @@ -0,0 +1 @@ +../../dist/xlsx.full.min.js \ No newline at end of file diff --git a/demos/react/.gitignore b/demos/react/.gitignore new file mode 100644 index 0000000..6a9a216 --- /dev/null +++ b/demos/react/.gitignore @@ -0,0 +1,2 @@ +SheetJS +.next diff --git a/demos/react/Makefile b/demos/react/Makefile new file mode 100644 index 0000000..724fa6f --- /dev/null +++ b/demos/react/Makefile @@ -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 - diff --git a/demos/react/README.md b/demos/react/README.md new file mode 100644 index 0000000..2afe557 --- /dev/null +++ b/demos/react/README.md @@ -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 + +``` + +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 + + + +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 diff --git a/demos/react/index.html b/demos/react/index.html new file mode 100644 index 0000000..4a1f85b --- /dev/null +++ b/demos/react/index.html @@ -0,0 +1,40 @@ + + + + + + +SheetJS React Demo + + + + + + + + + + +
+ + + + + diff --git a/demos/react/native.sh b/demos/react/native.sh new file mode 100755 index 0000000..5287ffd --- /dev/null +++ b/demos/react/native.sh @@ -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 -; diff --git a/demos/react/pages/.gitignore b/demos/react/pages/.gitignore new file mode 100644 index 0000000..7902a8c --- /dev/null +++ b/demos/react/pages/.gitignore @@ -0,0 +1 @@ +sheetjs.js diff --git a/demos/react/pages/index.js b/demos/react/pages/index.js new file mode 100644 index 0000000..f7eac77 --- /dev/null +++ b/demos/react/pages/index.js @@ -0,0 +1,26 @@ +import Head from 'next/head' +import SheetJSApp from './sheetjs.js' +export default () => ( +
+ + + SheetJS React Demo + + + + + + + + + + +
+) diff --git a/demos/react/preact.html b/demos/react/preact.html new file mode 100644 index 0000000..3e9bb6a --- /dev/null +++ b/demos/react/preact.html @@ -0,0 +1,42 @@ + + + + + + +SheetJS React Demo + + + + + + + + + + + + +
+ + + + + diff --git a/demos/react/react-native.js b/demos/react/react-native.js new file mode 100644 index 0000000..fff0f68 --- /dev/null +++ b/demos/react/react-native.js @@ -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 ( + + + SheetJS React Native Demo + Import Data + + +
+ +
+ +); }; +}; + +if(typeof module !== 'undefined') module.exports = SheetJSApp diff --git a/demos/server/Makefile b/demos/server/Makefile new file mode 100644 index 0000000..813bf22 --- /dev/null +++ b/demos/server/Makefile @@ -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 diff --git a/demos/server/README.md b/demos/server/README.md new file mode 100644 index 0000000..5b68ae3 --- /dev/null +++ b/demos/server/README.md @@ -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=` returns the internal storage in the specified type +- `POST /?f=` reads the local file and updates the internal storage +- `GET /?f=` 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. + + diff --git a/demos/server/_cors.js b/demos/server/_cors.js new file mode 100644 index 0000000..22b04c2 --- /dev/null +++ b/demos/server/_cors.js @@ -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; diff --git a/demos/server/_logit.js b/demos/server/_logit.js new file mode 100644 index 0000000..94f1a44 --- /dev/null +++ b/demos/server/_logit.js @@ -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; diff --git a/demos/server/_request.js b/demos/server/_request.js new file mode 100644 index 0000000..1e3aee0 --- /dev/null +++ b/demos/server/_request.js @@ -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})); +}); diff --git a/demos/server/express.js b/demos/server/express.js new file mode 100644 index 0000000..2b50552 --- /dev/null +++ b/demos/server/express.js @@ -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); }); diff --git a/demos/server/hapi.js b/demos/server/hapi.js new file mode 100644 index 0000000..e486384 --- /dev/null +++ b/demos/server/hapi.js @@ -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); +}); diff --git a/demos/server/koa.js b/demos/server/koa.js new file mode 100644 index 0000000..1f1176d --- /dev/null +++ b/demos/server/koa.js @@ -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); }); diff --git a/demos/server/koasub.js b/demos/server/koasub.js new file mode 100644 index 0000000..1eeb989 --- /dev/null +++ b/demos/server/koasub.js @@ -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"); +} diff --git a/demos/server/micro.js b/demos/server/micro.js new file mode 100644 index 0000000..8163247 --- /dev/null +++ b/demos/server/micro.js @@ -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"); +}; diff --git a/demos/vue/.gitignore b/demos/vue/.gitignore new file mode 100644 index 0000000..fe26609 --- /dev/null +++ b/demos/vue/.gitignore @@ -0,0 +1,2 @@ +SheetJS +.nuxt diff --git a/demos/vue/Makefile b/demos/vue/Makefile new file mode 100644 index 0000000..c6f5418 --- /dev/null +++ b/demos/vue/Makefile @@ -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 - diff --git a/demos/vue/README.md b/demos/vue/README.md index ee8f3d9..19a29ae 100644 --- a/demos/vue/README.md +++ b/demos/vue/README.md @@ -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 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 */ +``` diff --git a/demos/vue/index.html b/demos/vue/index.html index 288492a..3957bfe 100644 --- a/demos/vue/index.html +++ b/demos/vue/index.html @@ -1,7 +1,7 @@ - + SheetJS + VueJS2 @@ -38,6 +38,17 @@ This demo shows a sample Vue component "html-preview" that:
Sample Spreadsheet +
diff --git a/demos/vue/native.vue b/demos/vue/native.vue new file mode 100644 index 0000000..3751e25 --- /dev/null +++ b/demos/vue/native.vue @@ -0,0 +1,66 @@ + + + + + + diff --git a/demos/vue/package.json b/demos/vue/package.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/demos/vue/package.json @@ -0,0 +1 @@ +{} diff --git a/demos/vue/pages/index.vue b/demos/vue/pages/index.vue new file mode 100644 index 0000000..7494bdf --- /dev/null +++ b/demos/vue/pages/index.vue @@ -0,0 +1,102 @@ +/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ + + + diff --git a/demos/vue/static/xlsx.full.min.js b/demos/vue/static/xlsx.full.min.js new file mode 120000 index 0000000..152af21 --- /dev/null +++ b/demos/vue/static/xlsx.full.min.js @@ -0,0 +1 @@ +../xlsx.full.min.js \ No newline at end of file diff --git a/demos/vue/weex.sh b/demos/vue/weex.sh new file mode 100644 index 0000000..b01937f --- /dev/null +++ b/demos/vue/weex.sh @@ -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 diff --git a/demos/xhr/README.md b/demos/xhr/README.md index 4dec917..16555ee 100644 --- a/demos/xhr/README.md +++ b/demos/xhr/README.md @@ -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 -## 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); +}); +``` diff --git a/demos/xhr/server.js b/demos/xhr/server.js index 819e0aa..70e2315 100644 --- a/demos/xhr/server.js +++ b/demos/xhr/server.js @@ -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) { diff --git a/docbits/10_install.md b/docbits/10_install.md index e04b09e..41cdc54 100644 --- a/docbits/10_install.md +++ b/docbits/10_install.md @@ -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/) diff --git a/misc/docs/README.md b/misc/docs/README.md index 2ffea32..b150c95 100644 --- a/misc/docs/README.md +++ b/misc/docs/README.md @@ -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/)