提交 1a8f9726 编写于 作者: S SheetJS

demo refresh [ci skip]

上级 d0265005
# xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com
SheetJS
js-xlsx
xls
xlsb
xlsx
# Excel-related terms
A1-style
AutoFilter
ECMA-376
FoxPro
Multiplan
OData
OpenDocument
OpenFormula
PivotTable
Quattro
SpreadsheetML
Unhide
Visicalc
chartsheet
chartsheets
dBASE
tooltip
tooltips
# Third-party
Browserify
CDNjs
CommonJS
ExtendScript
FileSaver
JavaScriptCore
NPM
Nuxt.js
RequireJS
Rollup
SystemJS
VueJS
iOS
nodejs
npm
unpkg
webpack
weex
# Other terms
APIs
Base64
Booleans
JS
README
UTF-16
XHR
XMLHttpRequest
bundlers
cleanroom
config
customizable
datagrid
deduplication
embeddable
filesystem
javascript
metadata
natively
prepend
prepended
repo
runtime
submodule
transpiled
- demos/altjs/README.md
ChakraCore
Duktape
Nashorn
- demos/angular/README.md
angular-ui-grid
ui-grid
- demos/angular2/README.md
angular-cli
- demos/extendscript/README.md
Photoshop
minifier
- demos/headless/README.md
PhantomJS
SlimerJS
wkhtmltopdf
- demos/nwjs/README.md
NW.js
- demos/react/README.md
Next.js
Preact
- demos/server/README.md
hapi
- demos/xhr/README.md
axios
superagent
...@@ -209,6 +209,13 @@ book: readme graph ## Update summary for documentation ...@@ -209,6 +209,13 @@ book: readme graph ## Update summary for documentation
markdown-toc README.md | sed 's/(#/(README.md#/g'>> misc/docs/SUMMARY.md markdown-toc README.md | sed 's/(#/(README.md#/g'>> misc/docs/SUMMARY.md
<README.md grep -vE "(details|summary)>" > misc/docs/README.md <README.md grep -vE "(details|summary)>" > misc/docs/README.md
DEMOMDS=$(sort $(wildcard demos/*/README.md))
MDLINT=$(DEMODS) $(READEPS) demos/README.md
.PHONY: mdlint
mdlint: $(MDLINT) ## Check markdown documents
alex $^
mdspell -a -n -x -r --en-us $^
.PHONY: help .PHONY: help
help: help:
@grep -hE '(^[a-zA-Z_-][ a-zA-Z_-]*:.*?|^#[#*])' $(MAKEFILE_LIST) | bash misc/help.sh @grep -hE '(^[a-zA-Z_-][ a-zA-Z_-]*:.*?|^#[#*])' $(MAKEFILE_LIST) | bash misc/help.sh
......
此差异已折叠。
...@@ -8,5 +8,37 @@ works extremely well in common use cases: script tag insertion and node require. ...@@ -8,5 +8,37 @@ works extremely well in common use cases: script tag insertion and node require.
Systems like webpack try to be clever by performing simple static analysis to Systems like webpack try to be clever by performing simple static analysis to
pull in code. However, they do not support dynamic type tests, breaking pull in code. However, they do not support dynamic type tests, breaking
compatibility with traditional scripts. Configuration is required. The demos compatibility with traditional scripts. Configuration is required. The demos
cover basic configuration steps for various systems and should work as laid out. cover basic configuration steps for various systems and should "just work".
Mobile app and other larger demos do not include the full build structure. The
demos have `Makefile` scripts that show how to reproduce the full projects. The
scripts have been tested against iOS and OSX. For Windows platforms, GNU make
can be installed with Bash on Windows or with `cygwin`.
### Included Demos
**Frameworks and APIs**
- [`angular 1.x`](angular/)
- [`angular 2.x / 4.x`](angular2/)
- [`meteor`](meteor/)
- [`react and react-native`](react/)
- [`vue 2.x and weex`](vue/)
- [`XMLHttpRequest and fetch`](xhr/)
- [`nodejs server`](server/)
**Bundlers and Tooling**
- [`browserify`](browserify/)
- [`requirejs`](requirejs/)
- [`rollup`](rollup/)
- [`systemjs`](systemjs/)
- [`webpack 2.x`](webpack/)
**Platforms and Integrations**
- [`electron application`](electron/)
- [`nw.js application`](nwjs/)
- [`Adobe ExtendScript`](extendscript/)
- [`Headless Browsers`](headless/)
- [`canvas-datagrid`](datagrid/)
- [`Swift JSC and other engines`](altjs/)
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-xlsx?pixel)](https://github.com/SheetJS/js-xlsx)
...@@ -17,9 +17,52 @@ var global = (function(){ return this; }).call(null); ...@@ -17,9 +17,52 @@ var global = (function(){ return this; }).call(null);
iOS and OSX ship with the JavaScriptCore framework, enabling easy JS access from iOS and OSX ship with the JavaScriptCore framework, enabling easy JS access from
Swift and Objective-C. Hybrid function invocation is tricky, but explicit data Swift and Objective-C. Hybrid function invocation is tricky, but explicit data
passing is straightforward. passing is straightforward. The demo shows a standalone example for OSX. For
playgrounds, the library should be copied to shared playground data directory
(usually `~/Documents/Shared Playground Data`):
```swift
/* This only works in a playground, see SheetJSCore.swift for standalone use */
import JavaScriptCore;
import PlaygroundSupport;
/* build path variable for the library */
let shared_dir = PlaygroundSupport.playgroundSharedDataDirectory;
let lib_path = shared_dir.appendingPathComponent("xlsx.full.min.js");
/* prepare JS context */
var context:JSContext! = JSContext();
var src = "var global = (function(){ return this; }).call(null);";
context.evaluateScript(src);
/* load library */
var lib = try? String(contentsOf: lib_path);
context.evaluateScript(lib);
let XLSX: JSValue! = context.objectForKeyedSubscript("XLSX");
/* to verify the library was loaded, get the version string */
let XLSXversion: JSValue! = XLSX.objectForKeyedSubscript("version")
var version = XLSXversion.toString();
```
Binary strings can be passed back and forth using `String.Encoding.ascii`. Binary strings can be passed back and forth using `String.Encoding.isoLatin1`:
```swift
/* parse sheetjs.xls */
let file_path = shared_dir.appendingPathComponent("sheetjs.xls");
let data:String! = try String(contentsOf: file_path, encoding:String.Encoding.isoLatin1);
context.setObject(data, forKeyedSubscript:"payload" as (NSCopying & NSObjectProtocol)!);
src = "var wb = XLSX.read(payload, {type:'binary'});";
context.evaluateScript(src);
/* write to sheetjs.xlsx */
let out_path = shared_dir.appendingPathComponent("sheetjs.xlsx");
src = "var out = XLSX.write(wb, {type:'binary', bookType:'xlsx'})";
context.evaluateScript(src);
let outvalue: JSValue! = context.objectForKeyedSubscript("out");
var out:String! = outvalue.toString();
try? out.write(to: out_path, atomically: false, encoding: String.Encoding.isoLatin1);
```
## Nashorn ## Nashorn
...@@ -42,7 +85,7 @@ array and calls `XLSX.read` with type `"array"`. ...@@ -42,7 +85,7 @@ array and calls `XLSX.read` with type `"array"`.
`SheetJSRhino` class and `com.sheetjs` package show a complete JAR deployment, `SheetJSRhino` class and `com.sheetjs` package show a complete JAR deployment,
including the full XLSX source. including the full XLSX source.
Due to code generation errors, optimization must be disabled: Due to code generation errors, optimization must be turned off:
```java ```java
Context context = Context.enter(); Context context = Context.enter();
...@@ -55,7 +98,7 @@ context.setOptimizationLevel(-1); ...@@ -55,7 +98,7 @@ context.setOptimizationLevel(-1);
ChakraCore is an embeddable JS engine written in C++. The library and binary ChakraCore is an embeddable JS engine written in C++. The library and binary
distributions include a command-line tool `chakra` for running JS scripts. 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 The simplest way to interact with the engine is to pass Base64 strings. The make
target builds a very simple payload with the data. target builds a very simple payload with the data.
...@@ -77,3 +120,5 @@ duk_size_t sz; ...@@ -77,3 +120,5 @@ duk_size_t sz;
char *buf = (char *)duk_get_buffer_data(ctx, -1, sz); char *buf = (char *)duk_get_buffer_data(ctx, -1, sz);
duk_pop(ctx); duk_pop(ctx);
``` ```
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-xlsx?pixel)](https://github.com/SheetJS/js-xlsx)
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
/* This only works in a playground, see SheetJSCore.swift for standalone use */
import JavaScriptCore;
import PlaygroundSupport;
/* build path variable for the library */
let shared_dir = PlaygroundSupport.playgroundSharedDataDirectory;
let lib_path = shared_dir.appendingPathComponent("xlsx.full.min.js");
/* prepare JS context */
var context:JSContext! = JSContext();
var src = "var global = (function(){ return this; }).call(null);";
context.evaluateScript(src);
/* load library */
var lib = try? String(contentsOf: lib_path);
context.evaluateScript(lib);
let XLSX: JSValue! = context.objectForKeyedSubscript("XLSX");
/* to verify the library was loaded, get the version string */
let XLSXversion: JSValue! = XLSX.objectForKeyedSubscript("version")
var version = XLSXversion.toString();
/* parse sheetjs.xls */
let file_path = shared_dir.appendingPathComponent("sheetjs.xls");
let data:String! = try String(contentsOf: file_path, encoding:String.Encoding.isoLatin1);
context.setObject(data, forKeyedSubscript:"payload" as (NSCopying & NSObjectProtocol)!);
src = "var wb = XLSX.read(payload, {type:'binary'});";
context.evaluateScript(src);
/* write to sheetjs.xlsx */
let out_path = shared_dir.appendingPathComponent("sheetjs.xlsx");
src = "var out = XLSX.write(wb, {type:'binary', bookType:'xlsx'})";
context.evaluateScript(src);
let outvalue: JSValue! = context.objectForKeyedSubscript("out");
var out:String! = outvalue.toString();
try? out.write(to: out_path, atomically: false, encoding: String.Encoding.isoLatin1);
#!/usr/bin/env xcrun swift #!/usr/bin/env xcrun swift
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
import JavaScriptCore; import JavaScriptCore;
class SheetJS { class SheetJS {
...@@ -30,7 +30,7 @@ class SheetJS { ...@@ -30,7 +30,7 @@ class SheetJS {
} }
func readFileToCSV(file: String) throws -> String { func readFileToCSV(file: String) throws -> String {
let data:String! = try String(contentsOfFile: file, encoding:String.Encoding.ascii); let data:String! = try String(contentsOfFile: file, encoding:String.Encoding.isoLatin1);
self.context.setObject(data, forKeyedSubscript:"payload" as (NSCopying & NSObjectProtocol)!); self.context.setObject(data, forKeyedSubscript:"payload" as (NSCopying & NSObjectProtocol)!);
let src = [ let src = [
......
# Angular 1 # Angular 1
The `xlsx.core.min.js` and `xlsx.full.min.js` scripts are designed to be dropped The `xlsx.core.min.js` and `xlsx.full.min.js` scripts are designed to be dropped
into web pages with script tags e.g. into web pages with script tags:
```html ```html
<script src="xlsx.full.min.js"></script> <script src="xlsx.full.min.js"></script>
...@@ -12,21 +12,94 @@ as you would with any other browser-friendly library. To make this meaningful, ...@@ -12,21 +12,94 @@ as you would with any other browser-friendly library. To make this meaningful,
we chose to show an integration with a common angular table component. we chose to show an integration with a common angular table component.
This demo uses angular-ui-grid to display a data table. The ui-grid does not This demo uses angular-ui-grid to display a data table. The ui-grid does not
provide any way to hook into the import button, so the demo includes a simple provide any way to modify the import button, so the demo includes a simple
directive for a HTML File Input control. It also includes a sample service for directive for a HTML File Input control. It also includes a sample service for
export which adds an item to the export menu. export which adds an item to the export menu.
## Import Directive ## Import Directive
`SheetJSImportDirective` follows the prescription from the README for File input A general import directive is fairly straightforward:
controls using `readAsBinaryString`, converting to a suitable representation
and updating the scope. - Define the `importSheetJs` directive in the app:
```js
app.directive("importSheetJs", [SheetJSImportDirective]);
```
- Add the attribute `import-sheet-js=""` to the file input element:
```html
<input type="file" import-sheet-js="" multiple="false" />
```
- Define the directive:
```js
var SheetJSImportDirective = function() {
return {
scope: { },
link: function ($scope, $elm, $attrs) {
$elm.on('change', function (changeEvent) {
var reader = new FileReader();
reader.onload = function (e) {
/* read workbook */
var bstr = e.target.result;
var workbook = XLSX.read(bstr, {type:'binary'});
/* DO SOMETHING WITH workbook HERE */
};
reader.readAsBinaryString(changeEvent.target.files[0]);
});
}
};
};
```
The demo `SheetJSImportDirective` follows the prescription from the README for
File input controls using `readAsBinaryString`, converting to a suitable
representation and updating the scope.
## Export Service ## Export Service
An export can be triggered at any point! Depending on how data is represented,
a workbook object can be built using the utility functions. For example, using
an array of objects:
```js
/* starting from this data */
var data = [
{ name: "Barack Obama", pres: 44 },
{ name: "Donald Trump", pres: 45 }
];
/* generate a worksheet */
var ws = XLSX.utils.json_to_sheet(data);
/* add to workbook */
var wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "Presidents");
/* write workbook (use type 'binary') */
var wbout = XLSX.write(wb, {bookType:'xlsx', type:'binary'});
/* generate a download */
function s2ab(s) {
var buf = new ArrayBuffer(s.length);
var view = new Uint8Array(buf);
for (var i=0; i!=s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
return buf;
}
saveAs(new Blob([s2ab(wbout)],{type:"application/octet-stream"}), "sheetjs.xlsx");
```
`SheetJSExportService` exposes export functions for `XLSB` and `XLSX`. Other `SheetJSExportService` exposes export functions for `XLSB` and `XLSX`. Other
formats are easily supported by changing the `bookType` variable. It grabs formats are easily supported by changing the `bookType` variable. It grabs
values from the grid, builds an array of arrays, generates a workbook and uses values from the grid, builds an array of arrays, generates a workbook and uses
FileSaver to generate a download. By setting the `filename` and `sheetname` FileSaver to generate a download. By setting the `filename` and `sheetname`
options in the ui-grid options, the output can be controlled. options in the ui-grid options, the output can be controlled.
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-xlsx?pixel)](https://github.com/SheetJS/js-xlsx)
...@@ -103,5 +103,5 @@ var SheetJSImportDirective = function() { ...@@ -103,5 +103,5 @@ var SheetJSImportDirective = function() {
reader.readAsBinaryString(changeEvent.target.files[0]); reader.readAsBinaryString(changeEvent.target.files[0]);
}); });
} }
} };
} };
...@@ -10,6 +10,61 @@ This demo uses an array of arrays (type `Array<Array<any>>`) as the core state. ...@@ -10,6 +10,61 @@ 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 component template includes a file input element, a table that updates with
the data, and a button to export the data. the data, and a button to export the data.
## Array of Arrays
`Array<Array<any>>` neatly maps to a table with `ngFor`:
```html
<table class="sjs-table">
<tr *ngFor="let row of data">
<td *ngFor="let val of row">
{{val}}
</td>
</tr>
</table>
```
The `aoa_to_sheet` utility function returns a worksheet. Exporting is simple:
```typescript
/* generate worksheet */
const ws: XLSX.WorkSheet = XLSX.utils.aoa_to_sheet(this.data);
/* generate workbook and add the worksheet */
const wb: XLSX.WorkBook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'Sheet1');
/* save to file */
const wbout: string = XLSX.write(wb, { bookType: 'xlsx', type: 'binary' });
saveAs(new Blob([s2ab(wbout)]), 'SheetJS.xlsx');
```
`sheet_to_json` with the option `header:1` makes importing simple:
```typescript
/* <input type="file" (change)="onFileChange($event)" multiple="false" /> */
/* ... (within the component class definition) ... */
onFileChange(evt: any) {
/* wire up file reader */
const target: DataTransfer = <DataTransfer>(evt.target);
if (target.files.length !== 1) throw new Error('Cannot use multiple files');
const reader: FileReader = new FileReader();
reader.onload = (e: any) => {
/* read workbook */
const bstr: string = e.target.result;
const wb: XLSX.WorkBook = XLSX.read(bstr, {type: 'binary'});
/* grab first sheet */
const wsname: string = wb.SheetNames[0];
const ws: XLSX.WorkSheet = wb.Sheets[wsname];
/* save data */
this.data = <AOA>(XLSX.utils.sheet_to_json(ws, {header: 1}));
};
reader.readAsBinaryString(target.files[0]);
}
```
## Switching between Angular versions ## Switching between Angular versions
Modules that work with Angular 2 largely work as-is with Angular 4. Switching Modules that work with Angular 2 largely work as-is with Angular 4. Switching
...@@ -33,15 +88,14 @@ $ npm install ...@@ -33,15 +88,14 @@ $ npm install
$ ng serve $ ng serve
``` ```
## XLSX Symlink ## XLSX Symbolic Link
In this tree, `node_modules/xlsx` is a symlink pointing back to the root. This In this tree, `node_modules/xlsx` is a link pointing back to the root. This
enables testing the development version of the library. In order to use this enables testing the development version of the library. In order to use this
demo in other applications, add the `xlsx` dependency: demo in other applications, add the `xlsx` dependency:
```bash ```bash
$ npm install --save xlsx $ npm install --save xlsx
``` ```
## SystemJS Configuration ## SystemJS Configuration
...@@ -53,16 +107,18 @@ SystemJS example shows the required meta and map settings: ...@@ -53,16 +107,18 @@ SystemJS example shows the required meta and map settings:
```js ```js
SystemJS.config({ SystemJS.config({
meta: { meta: {
'xlsx': { 'xlsx': {
exports: 'XLSX' // <-- tell SystemJS to expose the XLSX variable exports: 'XLSX' // <-- tell SystemJS to expose the XLSX variable
} }
}, },
map: { map: {
'xlsx': 'xlsx.full.min.js', // <-- make sure xlsx.full.min.js is in same dir 'xlsx': 'xlsx.full.min.js', // <-- make sure xlsx.full.min.js is in same dir
'fs': '', // <--| 'fs': '', // <--|
'crypto': '', // <--| suppress native node modules 'crypto': '', // <--| suppress native node modules
'stream': '' // <--| 'stream': '' // <--|
} }
}); });
``` ```
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-xlsx?pixel)](https://github.com/SheetJS/js-xlsx)
...@@ -9,11 +9,9 @@ import { saveAs } from 'file-saver'; ...@@ -9,11 +9,9 @@ import { saveAs } from 'file-saver';
type AOA = Array<Array<any>>; type AOA = Array<Array<any>>;
function s2ab(s: string): ArrayBuffer { function s2ab(s: string): ArrayBuffer {
const buf = new ArrayBuffer(s.length); const buf: ArrayBuffer = new ArrayBuffer(s.length);
const view = new Uint8Array(buf); const view: Uint8Array = new Uint8Array(buf);
for (let i = 0; i !== s.length; ++i) { for (let i = 0; i !== s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
view[i] = s.charCodeAt(i) & 0xFF;
};
return buf; return buf;
} }
...@@ -33,41 +31,40 @@ function s2ab(s: string): ArrayBuffer { ...@@ -33,41 +31,40 @@ function s2ab(s: string): ArrayBuffer {
}) })
export class SheetJSComponent { export class SheetJSComponent {
data: AOA = [[1,2],[3,4]]; data: AOA = [ [1, 2], [3, 4] ];
wopts: XLSX.WritingOptions = { bookType:'xlsx', type:'binary' }; wopts: XLSX.WritingOptions = { bookType: 'xlsx', type: 'binary' };
fileName: string = "SheetJS.xlsx"; fileName: string = 'SheetJS.xlsx';
onFileChange(evt: any) { onFileChange(evt: any) {
/* wire up file reader */ /* wire up file reader */
const target: DataTransfer = <DataTransfer>(evt.target); const target: DataTransfer = <DataTransfer>(evt.target);
if(target.files.length != 1) { throw new Error("Cannot upload multiple files on the entry") }; if (target.files.length !== 1) throw new Error('Cannot use multiple files');
const reader = new FileReader(); const reader: FileReader = new FileReader();
reader.onload = (e: any) => { reader.onload = (e: any) => {
/* read workbook */ /* read workbook */
const bstr = e.target.result; const bstr: string = e.target.result;
const wb = XLSX.read(bstr, {type:'binary'}); const wb: XLSX.WorkBook = XLSX.read(bstr, {type: 'binary'});
/* grab first sheet */ /* grab first sheet */
const wsname = wb.SheetNames[0]; const wsname: string = wb.SheetNames[0];
const ws = wb.Sheets[wsname]; const ws: XLSX.WorkSheet = wb.Sheets[wsname];
/* save data */ /* save data */
this.data = <AOA>(XLSX.utils.sheet_to_json(ws, {header:1})); this.data = <AOA>(XLSX.utils.sheet_to_json(ws, {header: 1}));
}; };
reader.readAsBinaryString(target.files[0]); reader.readAsBinaryString(target.files[0]);
} }
export(): void { export(): void {
/* generate worksheet */ /* generate worksheet */
const ws = XLSX.utils.aoa_to_sheet(this.data); const ws: XLSX.WorkSheet = XLSX.utils.aoa_to_sheet(this.data);
/* generate workbook and add the worksheet */ /* generate workbook and add the worksheet */
const wb = XLSX.utils.book_new(); const wb: XLSX.WorkBook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'Sheet1'); XLSX.utils.book_append_sheet(wb, ws, 'Sheet1');
/* save to file */ /* save to file */
const wbout = XLSX.write(wb, this.wopts); const wbout: string = XLSX.write(wb, this.wopts);
console.log(this.fileName);
saveAs(new Blob([s2ab(wbout)]), this.fileName); saveAs(new Blob([s2ab(wbout)]), this.fileName);
} }
} }
import 'core-js/es6/reflect'; import 'core-js/es6/reflect';
import 'core-js/es7/reflect'; import 'core-js/es7/reflect';
import 'zone.js/dist/zone'; import 'zone.js/dist/zone';
\ No newline at end of file
browserify.js browserify.js
browserify.min.js browserify.min.js
worker.js
worker.min.js
TOOL=browserify TOOL=browserify
.PHONY: all .PHONY: all
all: $(TOOL).min.js all: $(TOOL).min.js worker.min.js
$(TOOL).min.js: $(TOOL).js $(TOOL).min.js: $(TOOL).js
uglifyjs $< > $@ uglifyjs $< > $@
.PHONY: $(TOOL).js .PHONY: $(TOOL).js
$(TOOL).js: $(TOOL).js: app.js
browserify -r './main.js:xlsx' > $@ browserify $< > $@
worker.min.js: worker.js
uglifyjs $< > $@
.PHONY: worker.js
worker.js: xlsxworker.js
browserify $< > $@
# Browserify # Browserify
The library is compatible with browserify and should just work out of the box. The library is compatible with Browserify and should just work out of the box.
This demo uses the `require` form to expose the whole library, enabling client This demo uses the `require` form to expose the whole library, enabling client
code to just `require('xlsx')`. The included demo and Makefile do just that. code to access the library with `var XLSX = require('xlsx')`. The JS code from
the root demo was moved to a separate `app.js` script. That script is bundled:
```bash
browserify app.js > browserify.js
uglifyjs browserify.js > browserify.min.js
```
### Worker Scripts
Browserify can also bundle worker scripts! Instead of using `importScripts`,
the worker script should require the module:
```diff
-importScripts('dist/xlsx.full.min.js');
+var XLSX = require('xlsx');
```
The same process generates the worker script:
```bash
browserify xlsxworker.js > worker.js
uglifyjs worker.js > worker.min.js
```
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-xlsx?pixel)](https://github.com/SheetJS/js-xlsx)
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
var XLSX = require('../../'); // test against development version
//var XLSX = require('xlsx'); // use in production
/*jshint browser:true */
/*global require */
var X = require('xlsx');
var XW = {
/* worker message */
msg: 'xlsx',
/* worker scripts */
worker: './worker.min.js'
};
var global_wb;
var process_wb = (function() {
var OUT = document.getElementById('out');
var HTMLOUT = document.getElementById('htmlout');
var get_format = (function() {
var radios = document.getElementsByName( "format" );
return function() {
for(var i = 0; i < radios.length; ++i) if(radios[i].checked || radios.length === 1) return radios[i].value;
};
})();
var to_json = function to_json(workbook) {
var result = {};
workbook.SheetNames.forEach(function(sheetName) {
var roa = X.utils.sheet_to_json(workbook.Sheets[sheetName]);
if(roa.length) result[sheetName] = roa;
});
return JSON.stringify(result, 2, 2);
};
var to_csv = function to_csv(workbook) {
var result = [];
workbook.SheetNames.forEach(function(sheetName) {
var csv = X.utils.sheet_to_csv(workbook.Sheets[sheetName]);
if(csv.length){
result.push("SHEET: " + sheetName);
result.push("");
result.push(csv);
}
});
return result.join("\n");
};
var to_fmla = function to_fmla(workbook) {
var result = [];
workbook.SheetNames.forEach(function(sheetName) {
var formulae = X.utils.get_formulae(workbook.Sheets[sheetName]);
if(formulae.length){
result.push("SHEET: " + sheetName);
result.push("");
result.push(formulae.join("\n"));
}
});
return result.join("\n");
};
var to_html = function to_html(workbook) {
HTMLOUT.innerHTML = "";
workbook.SheetNames.forEach(function(sheetName) {
var htmlstr = X.write(workbook, {sheet:sheetName, type:'binary', bookType:'html'});
HTMLOUT.innerHTML += htmlstr;
});
return "";
};
return function process_wb(wb) {
global_wb = wb;
var output = "";
switch(get_format()) {
case "form": output = to_fmla(wb); break;
case "html": output = to_html(wb); break;
case "json": output = to_json(wb); break;
default: output = to_csv(wb);
}
if(OUT.innerText === undefined) OUT.textContent = output;
else OUT.innerText = output;
if(typeof console !== 'undefined') console.log("output", new Date());
};
})();
var setfmt = window.setfmt = function setfmt() { if(global_wb) process_wb(global_wb); };
var b64it = window.b64it = (function() {
var tarea = document.getElementById('b64data');
return function b64it() {
if(typeof console !== 'undefined') console.log("onload", new Date());
var wb = X.read(tarea.value, {type:'base64', WTF:false});
process_wb(wb);
};
})();
var do_file = (function() {
var rABS = typeof FileReader !== "undefined" && (FileReader.prototype||{}).readAsBinaryString;
var domrabs = document.getElementsByName("userabs")[0];
if(!rABS) domrabs.disabled = !(domrabs.checked = false);
var use_worker = typeof Worker !== 'undefined';
var domwork = document.getElementsByName("useworker")[0];
if(!use_worker) domwork.disabled = !(domwork.checked = false);
var xw = function xw(data, cb) {
var worker = new Worker(XW.worker);
worker.onmessage = function(e) {
switch(e.data.t) {
case 'ready': break;
case 'e': console.error(e.data.d); break;
case XW.msg: cb(JSON.parse(e.data.d)); break;
}
};
worker.postMessage({d:data,b:rABS?'binary':'array'});
};
return function do_file(files) {
rABS = domrabs.checked;
use_worker = domwork.checked;
var f = files[0];
var reader = new FileReader();
reader.onload = function(e) {
if(typeof console !== 'undefined') console.log("onload", new Date(), rABS, use_worker);
var data = e.target.result;
if(!rABS) data = new Uint8Array(data);
if(use_worker) xw(data, process_wb);
else process_wb(X.read(data, {type: rABS ? 'binary' : 'array'}));
};
if(rABS) reader.readAsBinaryString(f);
else reader.readAsArrayBuffer(f);
};
})();
(function() {
var drop = document.getElementById('drop');
if(!drop.addEventListener) return;
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');
if(!xlf.addEventListener) return;
function handleFile(e) { do_file(e.target.files); }
xlf.addEventListener('change', handleFile, false);
})();
...@@ -47,167 +47,8 @@ Use readAsBinaryString: (when available) <input type="checkbox" name="userabs" c ...@@ -47,167 +47,8 @@ Use readAsBinaryString: (when available) <input type="checkbox" name="userabs" c
<div id="htmlout"></div> <div id="htmlout"></div>
<br /> <br />
<script src="browserify.min.js"></script> <script src="browserify.min.js"></script>
<script>
/*jshint browser:true */
/*global require */
var X = require('xlsx');
var XW = {
/* worker message */
msg: 'xlsx',
/* worker scripts */
worker: './xlsxworker.js'
};
var global_wb;
var process_wb = (function() {
var OUT = document.getElementById('out');
var HTMLOUT = document.getElementById('htmlout');
var get_format = (function() {
var radios = document.getElementsByName( "format" );
return function() {
for(var i = 0; i < radios.length; ++i) if(radios[i].checked || radios.length === 1) return radios[i].value;
};
})();
var to_json = function to_json(workbook) {
var result = {};
workbook.SheetNames.forEach(function(sheetName) {
var roa = X.utils.sheet_to_json(workbook.Sheets[sheetName]);
if(roa.length) result[sheetName] = roa;
});
return JSON.stringify(result, 2, 2);
};
var to_csv = function to_csv(workbook) {
var result = [];
workbook.SheetNames.forEach(function(sheetName) {
var csv = X.utils.sheet_to_csv(workbook.Sheets[sheetName]);
if(csv.length){
result.push("SHEET: " + sheetName);
result.push("");
result.push(csv);
}
});
return result.join("\n");
};
var to_fmla = function to_fmla(workbook) {
var result = [];
workbook.SheetNames.forEach(function(sheetName) {
var formulae = X.utils.get_formulae(workbook.Sheets[sheetName]);
if(formulae.length){
result.push("SHEET: " + sheetName);
result.push("");
result.push(formulae.join("\n"));
}
});
return result.join("\n");
};
var to_html = function to_html(workbook) {
HTMLOUT.innerHTML = "";
workbook.SheetNames.forEach(function(sheetName) {
var htmlstr = X.write(workbook, {sheet:sheetName, type:'binary', bookType:'html'});
HTMLOUT.innerHTML += htmlstr;
});
return "";
};
return function process_wb(wb) {
global_wb = wb;
var output = "";
switch(get_format()) {
case "form": output = to_fmla(wb); break;
case "html": output = to_html(wb); break;
case "json": output = to_json(wb); break;
default: output = to_csv(wb);
}
if(OUT.innerText === undefined) OUT.textContent = output;
else OUT.innerText = output;
if(typeof console !== 'undefined') console.log("output", new Date());
};
})();
var setfmt = window.setfmt = function setfmt() { if(global_wb) process_wb(global_wb); };
var b64it = window.b64it = (function() {
var tarea = document.getElementById('b64data');
return function b64it() {
if(typeof console !== 'undefined') console.log("onload", new Date());
var wb = X.read(tarea.value, {type:'base64', WTF:false});
process_wb(wb);
};
})();
var do_file = (function() {
var rABS = typeof FileReader !== "undefined" && (FileReader.prototype||{}).readAsBinaryString;
var domrabs = document.getElementsByName("userabs")[0];
if(!rABS) domrabs.disabled = !(domrabs.checked = false);
var use_worker = typeof Worker !== 'undefined';
var domwork = document.getElementsByName("useworker")[0];
if(!use_worker) domwork.disabled = !(domwork.checked = false);
var xw = function xw(data, cb) {
var worker = new Worker(XW.worker);
worker.onmessage = function(e) {
switch(e.data.t) {
case 'ready': break;
case 'e': console.error(e.data.d); break;
case XW.msg: cb(JSON.parse(e.data.d)); break;
}
};
worker.postMessage({d:data,b:rABS?'binary':'array'});
};
return function do_file(files) {
rABS = domrabs.checked;
use_worker = domwork.checked;
var f = files[0];
var reader = new FileReader();
reader.onload = function(e) {
if(typeof console !== 'undefined') console.log("onload", new Date(), rABS, use_worker);
var data = e.target.result;
if(!rABS) data = new Uint8Array(data);
if(use_worker) xw(data, process_wb);
else process_wb(X.read(data, {type: rABS ? 'binary' : 'array'}));
};
if(rABS) reader.readAsBinaryString(f);
else reader.readAsArrayBuffer(f);
};
})();
(function() {
var drop = document.getElementById('drop');
if(!drop.addEventListener) return;
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');
if(!xlf.addEventListener) return;
function handleFile(e) { do_file(e.target.files); }
xlf.addEventListener('change', handleFile, false);
})();
</script>
<script type="text/javascript"> <script type="text/javascript">
/* eslint no-use-before-define:0 */
var _gaq = _gaq || []; var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-36810333-1']); _gaq.push(['_setAccount', 'UA-36810333-1']);
_gaq.push(['_trackPageview']); _gaq.push(['_trackPageview']);
......
B/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ B/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
......
...@@ -5,6 +5,8 @@ with other JS libraries such as data grids for previewing data. After extensive ...@@ -5,6 +5,8 @@ with other JS libraries such as data grids for previewing data. After extensive
testing, [`canvas-datagrid`](https://tonygermaneri.github.io/canvas-datagrid/) testing, [`canvas-datagrid`](https://tonygermaneri.github.io/canvas-datagrid/)
stood out as a very high-performance grid with an incredibly simple API. stood out as a very high-performance grid with an incredibly simple API.
This demo is available at <http://oss.sheetjs.com/js-xlsx/datagrid.html>
## Obtaining the Library ## Obtaining the Library
The [`canvas-datagrid` npm nodule](http://npm.im/canvas-datagrid) includes a The [`canvas-datagrid` npm nodule](http://npm.im/canvas-datagrid) includes a
...@@ -27,8 +29,8 @@ Grid initialization is a one-liner: ...@@ -27,8 +29,8 @@ Grid initialization is a one-liner:
```js ```js
var grid = canvasDatagrid({ var grid = canvasDatagrid({
parentNode: document.getElementById('gridctr'), parentNode: document.getElementById('gridctr'),
data: [] data: []
}); });
``` ```
...@@ -44,15 +46,30 @@ features to support multiple worksheets. ...@@ -44,15 +46,30 @@ features to support multiple worksheets.
## Editing ## Editing
The library handles the whole edit cycle. No intervention is necessary. `canvas-datagrid` handles the entire edit cycle. No intervention is necessary.
## Saving Data ## Saving Data
`grid.data` is immediately readable and can be converted back to a worksheet: `grid.data` is immediately readable and can be converted back to a worksheet.
Some versions return an array-like object without the length, so a little bit of
preparation may be needed:
```js ```js
/* converts an array of array-like objects into an array of arrays */
function prep(arr) {
var out = [];
for(var i = 0; i < arr.length; ++i) {
if(!arr[i]) continue;
if(Array.isArray(arr[i])) { out[i] = arr[i]; continue };
var o = new Array();
Object.keys(arr[i]).forEach(function(k) { o[+k] = arr[i][k] });
out[i] = o;
}
return out;
}
/* build worksheet from the grid data */ /* build worksheet from the grid data */
var ws = XLSX.utils.aoa_to_sheet(grid.data); var ws = XLSX.utils.aoa_to_sheet(prep(grid.data));
/* build up workbook */ /* build up workbook */
var wb = XLSX.utils.book_new(); var wb = XLSX.utils.book_new();
...@@ -65,3 +82,5 @@ XLSX.utils.book_append_sheet(wb, ws, 'SheetJS'); ...@@ -65,3 +82,5 @@ XLSX.utils.book_append_sheet(wb, ws, 'SheetJS');
This demo barely scratches the surface. The underlying grid component includes This demo barely scratches the surface. The underlying grid component includes
many additional features including massive data streaming, sorting and styling. many additional features including massive data streaming, sorting and styling.
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-xlsx?pixel)](https://github.com/SheetJS/js-xlsx)
...@@ -33,11 +33,10 @@ a { text-decoration: none } ...@@ -33,11 +33,10 @@ a { text-decoration: none }
<div id="drop">Drop a spreadsheet file here to see sheet data</div> <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 <input type="file" name="xlfile" id="xlf" /> ... or click here to select a file
<textarea id="b64data">... or paste a base64-encoding here</textarea> <textarea id="b64data">... or paste a base64-encoding here</textarea>
<input type="button" id="dotext" value="Click here to process the base64 text" onclick="b64it();"/><br />
<b>Advanced Demo Options:</b> <b>Advanced Demo Options:</b>
Use readAsBinaryString: (when available) <input type="checkbox" name="userabs" checked> Use readAsBinaryString: (when available) <input type="checkbox" name="userabs" checked>
</pre> </pre>
<p><input type="submit" value="Export to XLSX!" id="xport" onclick="doit();" disabled="true"></p> <p><input type="submit" value="Export to XLSX!" id="xport" onclick="export_xlsx();" disabled="true"></p>
<div id="htmlout"></div> <div id="htmlout"></div>
<br /> <br />
<script src="https://unpkg.com/canvas-datagrid/dist/canvas-datagrid.js"></script> <script src="https://unpkg.com/canvas-datagrid/dist/canvas-datagrid.js"></script>
...@@ -52,147 +51,117 @@ Use readAsBinaryString: (when available) <input type="checkbox" name="userabs" c ...@@ -52,147 +51,117 @@ Use readAsBinaryString: (when available) <input type="checkbox" name="userabs" c
/*global XLSX */ /*global XLSX */
var X = XLSX; var X = XLSX;
var rABS = typeof FileReader !== "undefined" && typeof FileReader.prototype !== "undefined" && typeof FileReader.prototype.readAsBinaryString !== "undefined"; var cDg;
if(!rABS) {
document.getElementsByName("userabs")[0].disabled = true;
document.getElementsByName("userabs")[0].checked = false;
}
var wtf_mode = false;
function fixdata(data) { var process_wb = (function() {
var o = "", l = 0, w = 10240; var XPORT = document.getElementById('xport');
for(; l<data.byteLength/w; ++l) o+=String.fromCharCode.apply(null,new Uint8Array(data.slice(l*w,l*w+w))); var HTMLOUT = document.getElementById('htmlout');
o+=String.fromCharCode.apply(null, new Uint8Array(data.slice(l*w)));
return o;
}
function ab2str(data) { return function process_wb(wb) {
var o = "", l = 0, w = 10240; /* get data */
for(; l<data.byteLength/w; ++l) o+=String.fromCharCode.apply(null,new Uint16Array(data.slice(l*w,l*w+w))); var ws = wb.Sheets[wb.SheetNames[0]];
o+=String.fromCharCode.apply(null, new Uint16Array(data.slice(l*w))); var data = XLSX.utils.sheet_to_json(ws, {header:1});
return o;
}
function s2ab(s) { /* update canvas-datagrid */
var b = new ArrayBuffer(s.length), v = new Uint8Array(b); if(!cDg) cDg = canvasDatagrid({ parentNode:HTMLOUT, data:data });
for (var i=0; i != s.length; ++i) v[i] = s.charCodeAt(i) & 0xFF; cDg.data = data;
return b; XPORT.disabled = false;
}
function get_radio_value( radioName ) { /* create schema (for A,B,C column headings) */
var radios = document.getElementsByName( radioName ); var range = XLSX.utils.decode_range(ws['!ref']);
for( var i = 0; i < radios.length; i++ ) { for(var i = range.s.c; i <= range.e.c; ++i) cDg.schema[i - range.s.c].title = XLSX.utils.encode_col(i);
if( radios[i].checked || radios.length === 1 ) {
return radios[i].value;
}
}
}
var tarea = document.getElementById('b64data'); if(typeof console !== 'undefined') console.log("output", new Date());
function b64it() { };
if(typeof console !== 'undefined') console.log("onload", new Date()); })();
var wb = X.read(tarea.value, {type: 'base64',WTF:wtf_mode});
process_wb(wb);
}
window.b64it = b64it;
var global_wb; var do_file = (function() {
var cDg; var rABS = typeof FileReader !== "undefined" && (FileReader.prototype||{}).readAsBinaryString;
function process_wb(wb) { var domrabs = document.getElementsByName("userabs")[0];
global_wb = wb; if(!rABS) domrabs.disabled = !(domrabs.checked = false);
var ws = wb.Sheets[wb.SheetNames[0]];
var data = XLSX.utils.sheet_to_json(ws, {header:1});
if(!cDg) cDg = canvasDatagrid({ parentNode:document.getElementById('htmlout'), data:data });
else cDg.data = data;
var range = XLSX.utils.decode_range(ws['!ref']);
for(var i = range.s.c; i <= range.e.c; ++i) cDg.schema[i - range.s.c].title = XLSX.utils.encode_col(i);
document.getElementById('xport').disabled = false;
if(typeof console !== 'undefined') console.log("output", new Date());
}
function doit() {
var new_wb = XLSX.utils.book_new();
var new_ws = XLSX.utils.aoa_to_sheet(cDg.data);
XLSX.utils.book_append_sheet(new_wb, new_ws, 'SheetJS');
var wbout = XLSX.write(new_wb, {bookType:'xlsx', bookSST:true, type:'binary'});
var fname = 'sheetjs.xlsx';
try {
saveAs(new Blob([s2ab(wbout)],{type:"application/octet-stream"}), fname);
} catch(e) { if(typeof console != 'undefined') console.log(e, wbout); }
}
var drop = document.getElementById('drop'); return function do_file(files) {
function handleDrop(e) { rABS = domrabs.checked;
e.stopPropagation(); var f = files[0];
e.preventDefault();
rABS = document.getElementsByName("userabs")[0].checked;
var files = e.dataTransfer.files;
var f = files[0];
{
var reader = new FileReader(); var reader = new FileReader();
//var name = f.name;
reader.onload = function(e) { reader.onload = function(e) {
if(typeof console !== 'undefined') console.log("onload", new Date(), rABS); if(typeof console !== 'undefined') console.log("onload", new Date(), rABS);
var data = e.target.result; var data = e.target.result;
{ if(!rABS) data = new Uint8Array(data);
var wb; process_wb(X.read(data, {type: rABS ? 'binary' : 'array'}));
if(rABS) {
wb = X.read(data, {type: 'binary'});
} else {
var arr = fixdata(data);
wb = X.read(btoa(arr), {type: 'base64'});
}
process_wb(wb);
}
}; };
if(rABS) reader.readAsBinaryString(f); if(rABS) reader.readAsBinaryString(f);
else reader.readAsArrayBuffer(f); else reader.readAsArrayBuffer(f);
};
})();
(function() {
var drop = document.getElementById('drop');
if(!drop.addEventListener) return;
function handleDrop(e) {
e.stopPropagation();
e.preventDefault();
do_file(e.dataTransfer.files);
} }
}
function handleDragover(e) { function handleDragover(e) {
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
e.dataTransfer.dropEffect = 'copy'; e.dataTransfer.dropEffect = 'copy';
} }
if(drop.addEventListener) {
drop.addEventListener('dragenter', handleDragover, false); drop.addEventListener('dragenter', handleDragover, false);
drop.addEventListener('dragover', handleDragover, false); drop.addEventListener('dragover', handleDragover, false);
drop.addEventListener('drop', handleDrop, false); drop.addEventListener('drop', handleDrop, false);
} })();
(function() {
var xlf = document.getElementById('xlf');
if(!xlf.addEventListener) return;
function handleFile(e) { do_file(e.target.files); }
xlf.addEventListener('change', handleFile, false);
})();
var export_xlsx = (function() {
function prep(arr) {
var out = [];
for(var i = 0; i < arr.length; ++i) {
if(!arr[i]) continue;
if(Array.isArray(arr[i])) { out[i] = arr[i]; continue };
var o = new Array();
Object.keys(arr[i]).forEach(function(k) { o[+k] = arr[i][k] });
out[i] = o;
}
return out;
}
var xlf = document.getElementById('xlf'); function s2ab(s) {
function handleFile(e) { var b = new ArrayBuffer(s.length), v = new Uint8Array(b);
rABS = document.getElementsByName("userabs")[0].checked; for (var i=0; i != s.length; ++i) v[i] = s.charCodeAt(i) & 0xFF;
var files = e.target.files; return b;
var f = files[0];
{
var reader = new FileReader();
//var name = f.name;
reader.onload = function(e) {
if(typeof console !== 'undefined') console.log("onload", new Date(), rABS);
var data = e.target.result;
{
var wb;
if(rABS) {
wb = X.read(data, {type: 'binary'});
} else {
var arr = fixdata(data);
wb = X.read(btoa(arr), {type: 'base64'});
}
process_wb(wb);
}
};
if(rABS) reader.readAsBinaryString(f);
else reader.readAsArrayBuffer(f);
} }
}
if(xlf.addEventListener) xlf.addEventListener('change', handleFile, false); return function export_xlsx() {
if(!cDg) return;
/* convert canvas-datagrid data to worksheet */
var new_ws = XLSX.utils.aoa_to_sheet(prep(cDg.data));
/* build workbook */
var new_wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(new_wb, new_ws, 'SheetJS');
/* write file and trigger a download */
var wbout = XLSX.write(new_wb, {bookType:'xlsx', bookSST:true, type:'binary'});
var fname = 'sheetjs.xlsx';
try {
saveAs(new Blob([s2ab(wbout)],{type:"application/octet-stream"}), fname);
} catch(e) { if(typeof console != 'undefined') console.log(e, wbout); }
};
})();
</script> </script>
<script type="text/javascript"> <script type="text/javascript">
/* eslint no-use-before-define:0 */
var _gaq = _gaq || []; var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-36810333-1']); _gaq.push(['_setAccount', 'UA-36810333-1']);
_gaq.push(['_trackPageview']); _gaq.push(['_trackPageview']);
......
# Electron # Electron
This library is compatible with Electron and should just work out of the box. 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 demonstration uses Electron 1.7.5. The library is added via `require` from
the render process. It can also be required from the main process, as shown in 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. this demo to render a version string in the About dialog on OSX.
...@@ -9,11 +9,28 @@ The standard HTML5 `FileReader` techniques from the browser apply to Electron. ...@@ -9,11 +9,28 @@ 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 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/) the [SheetJS Data Preview Live Demo](http://oss.sheetjs.com/js-xlsx/)
The core data in this demo is an editable HTML table. The readers build up the
table using `sheet_to_html` (with `editable:true` option) and the writers scrape
the table using `table_to_book`.
## Reading and Writing Files
Since electron provides an `fs` implementation, `readFile` and `writeFile` can Since electron provides an `fs` implementation, `readFile` and `writeFile` can
be used in conjunction with the standard dialogs. For example: be used in conjunction with the standard dialog windows. For example:
```js ```js
/* from app code, require('electron').remote calls back to main process */
var dialog = require('electron').remote.dialog; var dialog = require('electron').remote.dialog;
var o = (dialog.showOpenDialog({ properties: ['openFile'] })||[''])[0];
var workbook = X.readFile(o); /* show a file-open dialog and read the first selected file */
var o = dialog.showOpenDialog({ properties: ['openFile'] });
var workbook = X.readFile(o[0]);
/* show a file-save dialog and write the workbook */
var o = dialog.showSaveDialog();
XLSX.writeFile(workbook, o);
``` ```
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-xlsx?pixel)](https://github.com/SheetJS/js-xlsx)
...@@ -30,6 +30,7 @@ a { text-decoration: none } ...@@ -30,6 +30,7 @@ a { text-decoration: none }
<input type="file" name="xlfile" id="xlf" /> ... or click here to select a file <input type="file" name="xlfile" id="xlf" /> ... or click here to select a file
</pre> </pre>
<p><input type="submit" value="Export Data!" id="xport" onclick="export_xlsx();" disabled="true"></p>
<div id="htmlout"></div> <div id="htmlout"></div>
<br /> <br />
<script src="index.js"></script> <script src="index.js"></script>
......
var X = require('xlsx'); var XLSX = require('xlsx');
var electron = require('electron').remote; var electron = require('electron').remote;
var process_wb = (function() { var process_wb = (function() {
var HTMLOUT = document.getElementById('htmlout'); var HTMLOUT = document.getElementById('htmlout');
var XPORT = document.getElementById('xport');
return function process_wb(wb) { return function process_wb(wb) {
XPORT.disabled = false;
HTMLOUT.innerHTML = ""; HTMLOUT.innerHTML = "";
wb.SheetNames.forEach(function(sheetName) { wb.SheetNames.forEach(function(sheetName) {
var htmlstr = X.write(wb, {sheet:sheetName, type:'binary', bookType:'html'}); var htmlstr = XLSX.utils.sheet_to_html(wb.Sheets[sheetName],{editable:true});
HTMLOUT.innerHTML += htmlstr; HTMLOUT.innerHTML += htmlstr;
}); });
}; };
...@@ -30,7 +32,7 @@ var do_file = (function() { ...@@ -30,7 +32,7 @@ var do_file = (function() {
reader.onload = function(e) { reader.onload = function(e) {
var data = e.target.result; var data = e.target.result;
data = new Uint8Array(data); data = new Uint8Array(data);
process_wb(X.read(data, {type: 'array'})); process_wb(XLSX.read(data, {type: 'array'}));
}; };
reader.readAsArrayBuffer(f); reader.readAsArrayBuffer(f);
}; };
...@@ -67,7 +69,7 @@ var do_file = (function() { ...@@ -67,7 +69,7 @@ var do_file = (function() {
}], }],
properties: ['openFile'] properties: ['openFile']
}); });
if(o.length > 0) process_wb(X.readFile(o[0])); if(o.length > 0) process_wb(XLSX.readFile(o[0]));
} }
readf.addEventListener('click', handleF, false); readf.addEventListener('click', handleF, false);
})(); })();
...@@ -77,3 +79,21 @@ var do_file = (function() { ...@@ -77,3 +79,21 @@ var do_file = (function() {
function handleFile(e) { do_file(e.target.files); } function handleFile(e) { do_file(e.target.files); }
xlf.addEventListener('change', handleFile, false); xlf.addEventListener('change', handleFile, false);
})(); })();
var export_xlsx = (function() {
var HTMLOUT = document.getElementById('htmlout');
var XTENSION = "xls|xlsx|xlsm|xlsb|xml|csv|txt|dif|sylk|slk|prn|ods|fods|htm|html".split("|")
return function() {
var wb = XLSX.utils.table_to_book(HTMLOUT);
var o = electron.dialog.showSaveDialog({
title: 'Save file as',
filters: [{
name: "Spreadsheets",
extensions: XTENSION
}]
});
console.log(o);
XLSX.writeFile(wb, o);
electron.dialog.showMessageBox({ message: "Exported data to " + o, buttons: ["OK"] });
};
})();
# ExtendScript demos # Adobe ExtendScript
The main file is `test.jsx`. Target-specific files prepend target directives. ExtendScript adds some features to a limited form of ECMAScript version 3. With
the included shim, the library can run within Photoshop and other Adobe apps!
The main file is `test.jsx`. Target-specific files prepend target directives.
Copy the `test.jsx` file as well as the `shim.js` and `xlsx.core.min.js` files Copy the `test.jsx` file as well as the `shim.js` and `xlsx.core.min.js` files
to wherever you want the scripts to reside. The demo shows opening a file and to wherever you want the scripts to reside.
converting to an array of arrays.
The demo shows opening a file and converting to an array of arrays:
```js
/* include library */
#include "shim.js"
#include "xlsx.core.min.js"
/* get data as binary string */
var filename = "sheetjs.xlsx";
var base = new File($.fileName);
var infile = File(base.path + "/" + filename);
infile.open("r");
infile.encoding = "binary";
var data = infile.read();
/* parse data */
var workbook = XLSX.read(data, {type:"binary"});
/* DO SOMETHING WITH workbook HERE */
```
NOTE: [We forked the minifier](https://www.npmjs.com/package/@sheetjs/uglify-js) NOTE: [We forked the minifier](https://www.npmjs.com/package/@sheetjs/uglify-js)
and included a bugfix for ExtendScript's misparsing of switch statements. and included a patch for ExtendScript's switch statement semicolon issue.
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-xlsx?pixel)](https://github.com/SheetJS/js-xlsx)
# Headless Browsers # Headless Browsers
The library, intentionally conservative in the use of ES5+ features, plays nicely The library, eschewing unstable and nascent ECMAScript features, plays nicely
with most headless browsers. This demo shows a few common headless scenarios. with most headless browsers. This demo shows a few common headless scenarios.
## PhantomJS ## PhantomJS
This was tested in phantomjs 2.1.1, installed using the node module: This was tested in PhantomJS 2.1.1, installed using the node module:
```bash ```bash
$ npm install -g phantomjs $ npm install -g phantomjs
...@@ -17,12 +17,12 @@ $ phantomjs phantomjs.js ...@@ -17,12 +17,12 @@ $ phantomjs phantomjs.js
This was tested in wkhtmltopdf 0.12.4, installed using the official binaries: This was tested in wkhtmltopdf 0.12.4, installed using the official binaries:
```bash ```bash
$ wkhtmltopdf --javascript-delay 60000 http://localhost:8000/ test.pdf $ wkhtmltopdf --javascript-delay 20000 http://oss.sheetjs.com/js-xlsx/tests/ test.pdf
``` ```
## Puppeteer ## Puppeteer
This was tested in puppeteer 0.9.0 and Chromium r494755, installed using node: This was tested in puppeteer 0.9.0 and Chromium revision 494755:
```bash ```bash
$ npm install puppeteer $ npm install puppeteer
...@@ -34,10 +34,11 @@ the webpage. The `dist` versions are suitable for web pages. ...@@ -34,10 +34,11 @@ the webpage. The `dist` versions are suitable for web pages.
## SlimerJS ## SlimerJS
This was tested in slimerjs 0.10.3 and FF 52.0, installed using `brew` on OSX: This was tested in SlimerJS 0.10.3 and FF 52.0, installed using `brew` on OSX:
```bash ```bash
$ brew install slimerjs $ brew install slimerjs
$ slimerjs slimerjs.js $ slimerjs slimerjs.js
``` ```
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-xlsx?pixel)](https://github.com/SheetJS/js-xlsx)
...@@ -3,7 +3,7 @@ var fs = require('fs'); ...@@ -3,7 +3,7 @@ var fs = require('fs');
var xlsx = require('../../xlsx'); var xlsx = require('../../xlsx');
var page = require('webpage').create(); var page = require('webpage').create();
page.open('http://www.google.com', function(status) { page.open('http://oss.sheetjs.com/js-xlsx/tests/', function(status) {
var data = fs.read('sheetjs.xlsx', {mode: 'rb', charset: 'utf8'}); var data = fs.read('sheetjs.xlsx', {mode: 'rb', charset: 'utf8'});
var workbook = xlsx.read(data, {type: 'binary'}); var workbook = xlsx.read(data, {type: 'binary'});
......
...@@ -5,7 +5,7 @@ const puppeteer = require('puppeteer'); ...@@ -5,7 +5,7 @@ const puppeteer = require('puppeteer');
const browser = await puppeteer.launch(); const browser = await puppeteer.launch();
const page = await browser.newPage(); const page = await browser.newPage();
await page.goto('http://localhost:8000', {waitUntil: 'load'}); await page.goto('http://oss.sheetjs.com/js-xlsx/tests/', {waitUntil: 'load'});
await page.waitFor(30*1000); await page.waitFor(30*1000);
await page.pdf({path: 'test.pdf', format: 'A4'}); await page.pdf({path: 'test.pdf', format: 'A4'});
......
...@@ -3,7 +3,7 @@ var fs = require('fs'); ...@@ -3,7 +3,7 @@ var fs = require('fs');
var xlsx = require('../../dist/xlsx.full.min'); var xlsx = require('../../dist/xlsx.full.min');
var page = require('webpage').create(); var page = require('webpage').create();
page.open('http://www.google.com', function(status) { page.open('http://oss.sheetjs.com/js-xlsx/tests/', function(status) {
var data = fs.read('sheetjs.xlsx', {mode: 'rb', charset: 'utf8'}); var data = fs.read('sheetjs.xlsx', {mode: 'rb', charset: 'utf8'});
var workbook = xlsx.read(data, {type: 'binary'}); var workbook = xlsx.read(data, {type: 'binary'});
......
...@@ -4,6 +4,11 @@ start: ...@@ -4,6 +4,11 @@ start:
.PHONY: init .PHONY: init
init: init:
if [ ! -e .meteor ]; then meteor create .; fi;
@npm install babel-runtime meteor-node-stubs @npm install babel-runtime meteor-node-stubs
@meteor add pfafman:filesaver @meteor add pfafman:filesaver check
@mkdir -p node_modules; cd node_modules; ln -s ../../../ xlsx; cd - @mkdir -p node_modules; cd node_modules; ln -s ../../../ xlsx; cd -
.PHONY: lint
lint:
@meteor npm run lint
\ No newline at end of file
...@@ -5,34 +5,70 @@ tables in the browser, streaming write in nodejs), the core is ES3/ES5 and can ...@@ -5,34 +5,70 @@ tables in the browser, streaming write in nodejs), the core is ES3/ES5 and can
be used in any reasonably compliant JS implementation. It should play nice with be used in any reasonably compliant JS implementation. It should play nice with
meteor out of the box. meteor out of the box.
Using the npm module, the library can be imported from client or server side:
```js
import XLSX from 'xlsx'
```
All of the functions and utilities are available in both realms. Since the core
data representations are simple JS objects, the workbook object can be passed on
the wire, enabling hybrid workflows where the server processes data and client
finishes the work.
## This demonstration ## This demonstration
You can split the work between the client and server side as you see fit. The Note: the obvious extremes of pure-client code and pure-server code are covered
obvious extremes of pure-client code and pure-server code are straightforward. in other demos.
This demo tries to split the work to demonstrate that the workbook object can be
passed on the wire.
The read demo: ### Reading Data
The parse demo:
- accepts files from the client side - accepts files from the client side
- sends binary string to server - sends binary string to server
- processes data on server side - processes data on server side
- sends workbook object to client - sends workbook object to client
- renders HTML and adds to a DOM element - renders HTML and adds to a DOM element
The logic from within the `FileReader` is split as follows:
```js
// CLIENT SIDE
const bstr = e.target.result;
// SERVER SIDE
const wb = XLSX.read(bstr, { type: 'binary' });
// CLIENT SIDE
const ws = wb.Sheets[wb.SheetNames[0]];
const html = XLSX.utils.sheet_to_html(ws, { editable: true });
document.getElementById('out').innerHTML = html;
```
### Writing Data
The write demo: The write demo:
- generates workbook on server side - grabs HTML from the client side
- sends HTML string to server
- processes data on server side
- sends workbook object to client - sends workbook object to client
- generates file on client side - generates file on client side and triggers a download
- triggers a download.
This demo uses the FileSaver.js library for writing files, installed through the The logic from within the `click` event is split as follows:
[`pfafman:filesaver` wrapper](https://atmospherejs.com/pfafman/filesaver):
```bash ```js
meteor add pfafman:filesaver // CLIENT SIDE
const html = document.getElementById('out').innerHTML;
// SERVER SIDE
const wb = XLSX.read(html, { type: 'binary' });
// CLIENT SIDE
const o = XLSX.write(wb, { bookType: 'xlsx', type: 'binary' });
saveAs(new Blob([s2ab(o)], {type:'application/octet-stream'}), 'sheetjs.xlsx');
``` ```
This demo uses the FileSaver library for writing files, installed through the
[`pfafman:filesaver` wrapper](https://atmospherejs.com/pfafman/filesaver).
## Setup ## Setup
This tree does not include the `.meteor` structure. Rebuild the project with: This tree does not include the `.meteor` structure. Rebuild the project with:
...@@ -45,12 +81,14 @@ meteor ...@@ -45,12 +81,14 @@ meteor
``` ```
## Environment-specific features ## Environment-Specific Features
File-related operations (e.g. `XLSX.readFile` and `XLSX.writeFile`) will not be File-related operations like `XLSX.readFile` and `XLSX.writeFile` will not be
available in client-side code. If you need to read a local file from the client, available in client-side code. If you need to read a local file from the client,
use a file input or drag-and-drop. use a file input or drag-and-drop.
Browser-specific operations (e.g. `XLSX.utils.table_to_book`) are limited to Browser-specific operations like `XLSX.utils.table_to_book` are limited to
client side code. You should never have to read from DOM elements on the server client side code. You should never have to read from DOM elements on the server
side, but you can use a third-party virtual DOM to provide the required API. side, but you can use a third-party virtual DOM to provide the required API.
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-xlsx?pixel)](https://github.com/SheetJS/js-xlsx)
...@@ -6,22 +6,13 @@ ...@@ -6,22 +6,13 @@
<pre> <pre>
<b><a href="//sheetjs.com">SheetJS Meteor Demo</a></b> <b><a href="//sheetjs.com">SheetJS Meteor Demo</a></b>
<b>Meteor Read Demo</b> {{> sheetjs}}
{{> read}}
<b>Meteor Write Demo</b>
{{> write}}
</pre> </pre>
</body> </body>
<template name="read"> <template name="sheetjs">
<label for="upload">Parse File: </label><input type="file" id="upload" /> <label for="upload">Parse File: </label><input type="file" id="upload" />
<div id="out"></div> <div id="out"></div>
</template> <button id="dnload" disabled="true">Generate Worksheet</button>
<template name="write">
<label for="dnload">Write File: </label><button id="dnload">Generate Worksheet</button>
</template> </template>
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
import XLSX from 'xlsx';
/* note: saveAs is made available via the smart package */
/* global saveAs */
import { Meteor } from 'meteor/meteor';
import { Template } from 'meteor/templating'; import { Template } from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var';
import './main.html'; import './main.html';
const XLSX = require('xlsx'); Template.sheetjs.events({
'change input' (event) {
Template.read.events({ /* "Browser file upload form element" from SheetJS README */
'change input' (evt, instance) { const file = event.currentTarget.files[0];
/* "Browser file upload form element" from SheetJS README */ const reader = new FileReader();
const file = evt.currentTarget.files[0]; reader.onload = function(e) {
const reader = new FileReader(); const data = e.target.result;
reader.onload = function(e) { const name = file.name;
const data = e.target.result; /* Meteor magic */
const name = file.name; Meteor.call('upload', data, name, function(err, wb) {
/* Meteor magic */ if (err) throw err;
Meteor.call('upload', data, name, function(err, wb) { /* load the first worksheet */
if(err) console.error(err); const ws = wb.Sheets[wb.SheetNames[0]];
else { /* generate HTML table and enable export */
/* do something here -- this just dumps an array of arrays to console */ const html = XLSX.utils.sheet_to_html(ws, { editable: true });
console.log(XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]], {header:1})); document.getElementById('out').innerHTML = html;
document.getElementById('out').innerHTML = (XLSX.utils.sheet_to_html(wb.Sheets[wb.SheetNames[0]])); document.getElementById('dnload').disabled = false;
} });
}); };
}; reader.readAsBinaryString(file);
reader.readAsBinaryString(file); },
}, 'click button' () {
}); const html = document.getElementById('out').innerHTML;
Meteor.call('download', html, function(err, wb) {
Template.write.events({ if (err) throw err;
'click button' (evt, instance) { /* "Browser download file" from SheetJS README */
Meteor.call('download', function(err, wb) { const wbout = XLSX.write(wb, { bookType: 'xlsx', type: 'binary' });
if(err) console.error(err); saveAs(new Blob([s2ab(wbout)], { type: 'application/octet-stream' }), 'sheetjs.xlsx');
else { });
console.log(wb); },
/* "Browser download file" from SheetJS README */
var wopts = { bookType:'xlsx', bookSST:false, type:'binary' };
var wbout = XLSX.write(wb, wopts);
saveAs(new Blob([s2ab(wbout)],{type:"application/octet-stream"}), "meteor.xlsx");
}
});
},
}); });
/* eslint no-bitwise:0, no-plusplus:0 */
function s2ab(s) { function s2ab(s) {
var buf = new ArrayBuffer(s.length); const buf = new ArrayBuffer(s.length);
var view = new Uint8Array(buf); const view = new Uint8Array(buf);
for (var i=0; i!=s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF; for (let i = 0; i !== s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
return buf; return buf;
} }
{ {
"name": "meteor-xlsx", "name": "meteor-xlsx",
"version": "0.0.0",
"private": true, "private": true,
"scripts": { "scripts": {
"lint": "eslint .",
"start": "meteor run" "start": "meteor run"
}, },
"eslintConfig": {
"extends": "@meteorjs/eslint-config-meteor"
},
"dependencies": { "dependencies": {
"babel-runtime": "^6.20.0", "babel-runtime": "^6.20.0",
"meteor-node-stubs": "~0.2.4" "meteor-node-stubs": "~0.2.4"
},
"devDependencies": {
"@meteorjs/eslint-config-meteor": "^1.0.5",
"babel-eslint": "^7.2.3",
"eslint": "^3.19.0",
"eslint-config-airbnb": "^13.0.0",
"eslint-import-resolver-meteor": "^0.3.4",
"eslint-plugin-import": "^2.7.0",
"eslint-plugin-jsx-a11y": "^2.2.3",
"eslint-plugin-meteor": "^4.1.4",
"eslint-plugin-react": "^6.10.3"
} }
} }
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
import { Meteor } from 'meteor/meteor'; import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
const XLSX = require('xlsx'); import XLSX from 'xlsx';
Meteor.methods({ Meteor.methods({
upload: (bstr, name) => { upload: (bstr, name) => {
/* read the data and return the workbook object to the frontend */ /* read the data and return the workbook object to the frontend */
return XLSX.read(bstr, {type:'binary'}); check(bstr, String);
}, check(name, String);
download: () => { return XLSX.read(bstr, { type: 'binary' });
/* generate a workbook object and return to the frontend */ },
const data = [ download: (html) => {
["a", "b", "c"], check(html, String);
[ 1 , 2 , 3 ] let wb;
]; if (html.length > 3) {
const ws = XLSX.utils.aoa_to_sheet(data); /* parse workbook if html is available */
const wb = {SheetNames: ["Sheet1"], Sheets:{Sheet1:ws }}; wb = XLSX.read(html, { type: 'binary' });
return wb; } else {
} /* generate a workbook object otherwise */
const data = [['a', 'b', 'c'], [1, 2, 3]];
const ws = XLSX.utils.aoa_to_sheet(data);
wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'SheetJS');
}
return wb;
},
}); });
Meteor.startup(() => { }); Meteor.startup(() => { });
...@@ -3,8 +3,34 @@ ...@@ -3,8 +3,34 @@
This library is compatible with NW.js and should just work out of the box. 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 demonstration uses NW.js 0.24 with the dist script.
The standard HTML5 `FileReader` techniques from the browser apply to NW.js. ## Reading data
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 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/) the [SheetJS Data Preview Live Demo](http://oss.sheetjs.com/js-xlsx/).
## Writing data
File input elements with the attribute `nwsaveas` show UI for saving a file. The
standard trick is to generate a hidden file input DOM element and "click" it.
Since NW.js does not present a `writeFileSync` in the `fs` package, a manual
step is required:
```js
/* from within the input change callback, `this.value` is the file name */
var filename = this.value, bookType = (filename.match(/[^\.]*$/)||["xlsx"])[0];
/* convert the TABLE element back to a workbook */
var wb = XLSX.utils.table_to_book(HTMLOUT);
/* write to buffer */
var wbout = XLSX.write(wb, {type:'buffer', bookType:bookType});
/* use the async fs.writeFile to save the data */
fs.writeFile(filename, wbout, function(err) {
if(!err) return alert("Saved to " + filename);
alert("Error: " + (err.message || err));
});
```
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-xlsx?pixel)](https://github.com/SheetJS/js-xlsx)
...@@ -29,6 +29,7 @@ a { text-decoration: none } ...@@ -29,6 +29,7 @@ a { text-decoration: none }
<input type="file" name="xlfile" id="xlf" /> ... or click here to select a file <input type="file" name="xlfile" id="xlf" /> ... or click here to select a file
</pre> </pre>
<p><input type="submit" value="Export Data!" id="xport" onclick="export_xlsx();" disabled="true"></p>
<div id="htmlout"></div> <div id="htmlout"></div>
<br /> <br />
<script src="xlsx.full.min.js"></script> <script src="xlsx.full.min.js"></script>
......
var X = XLSX; var fs = require('fs');
var process_wb = (function() { var process_wb = (function() {
var HTMLOUT = document.getElementById('htmlout'); var HTMLOUT = document.getElementById('htmlout');
var XPORT = document.getElementById('xport');
return function process_wb(wb) { return function process_wb(wb) {
XPORT.disabled = false;
HTMLOUT.innerHTML = ""; HTMLOUT.innerHTML = "";
wb.SheetNames.forEach(function(sheetName) { wb.SheetNames.forEach(function(sheetName) {
var htmlstr = X.write(wb, {sheet:sheetName, type:'binary', bookType:'html'}); var htmlstr = XLSX.utils.sheet_to_html(wb.Sheets[sheetName],{editable:true});
HTMLOUT.innerHTML += htmlstr; HTMLOUT.innerHTML += htmlstr;
}); });
}; };
...@@ -29,7 +31,7 @@ var do_file = (function() { ...@@ -29,7 +31,7 @@ var do_file = (function() {
reader.onload = function(e) { reader.onload = function(e) {
var data = e.target.result; var data = e.target.result;
data = new Uint8Array(data); data = new Uint8Array(data);
process_wb(X.read(data, {type: 'array'})); process_wb(XLSX.read(data, {type: 'array'}));
}; };
reader.readAsArrayBuffer(f); reader.readAsArrayBuffer(f);
}; };
...@@ -60,3 +62,25 @@ var do_file = (function() { ...@@ -60,3 +62,25 @@ var do_file = (function() {
function handleFile(e) { do_file(e.target.files); } function handleFile(e) { do_file(e.target.files); }
xlf.addEventListener('change', handleFile, false); xlf.addEventListener('change', handleFile, false);
})(); })();
var export_xlsx = (function() {
/* pre-build the nwsaveas input element */
var HTMLOUT = document.getElementById('htmlout');
var input = document.createElement('input');
input.style.display = 'none';
input.setAttribute('nwsaveas', 'sheetjs.xlsx');
input.setAttribute('type', 'file');
document.body.appendChild(input);
input.addEventListener('cancel',function(){ alert("Save was canceled!"); });
input.addEventListener('change',function(e){
var filename=this.value, bookType=(filename.match(/[^\.]*$/)||["xlsx"])[0];
var wb = XLSX.utils.table_to_book(HTMLOUT);
var wbout = XLSX.write(wb, {type:'buffer', bookType:bookType});
fs.writeFile(filename, wbout, function(err) {
if(!err) return alert("Saved to " + filename);
alert("Error: " + (err.message || err));
});
});
return function() { input.click(); };
})();
...@@ -3,10 +3,9 @@ react: ## Simple server for react and clones ...@@ -3,10 +3,9 @@ react: ## Simple server for react and clones
python -mSimpleHTTPServer python -mSimpleHTTPServer
.PHONY: next .PHONY: next
next: ## next.js demo next: init ## next.js demo
# next doesn't support jsx extension
mkdir -p pages mkdir -p pages
cp sheetjs.jsx pages/sheetjs.js cat nexthdr.js sheetjs.jsx > pages/sheetjs.js
next next
.PHONY: native .PHONY: native
...@@ -20,3 +19,9 @@ ios: native ## react-native ios sim ...@@ -20,3 +19,9 @@ ios: native ## react-native ios sim
.PHONY: android .PHONY: android
android: native ## react-native android sim android: native ## react-native android sim
cd SheetJS; react-native run-android; cd - cd SheetJS; react-native run-android; cd -
.PHONY: init
init: ## set up node_modules and symlink
mkdir -p node_modules
cd node_modules; if [ ! -e xlsx ]; then ln -s ../../../ xlsx; fi; cd -
if [ ! -e node_modules/file-saver ]; then npm install file-saver; fi
# React # React
The `xlsx.core.min.js` and `xlsx.full.min.js` scripts are designed to be dropped The `xlsx.core.min.js` and `xlsx.full.min.js` scripts are designed to be dropped
into web pages with script tags e.g. into web pages with script tags:
```html ```html
<script src="xlsx.full.min.js"></script> <script src="xlsx.full.min.js"></script>
...@@ -30,11 +30,7 @@ state in this demo is shaped like the following object: ...@@ -30,11 +30,7 @@ state in this demo is shaped like the following object:
```js ```js
{ {
cols: [ cols: [{ name: "A", key: 0 }, { name: "B", key: 1 }, { name: "C", key: 2 }],
{ name: "A", key: 0 },
{ name: "B", key: 1 },
{ name: "C", key: 2 },
],
data: [ data: [
[ "id", "name", "value" ], [ "id", "name", "value" ],
[ 1, "sheetjs", 7262 ] [ 1, "sheetjs", 7262 ]
...@@ -43,7 +39,32 @@ state in this demo is shaped like the following object: ...@@ -43,7 +39,32 @@ state in this demo is shaped like the following object:
} }
``` ```
The appropriate state model is application-specific. `sheet_to_json` and `aoa_to_sheet` utility functions can convert between arrays
of arrays and worksheets:
```js
/* convert from workbook to array of arrays */
var first_worksheet = workbook.Sheets[workbook.SheetNames[0]];
var data = XLSX.utils.sheet_to_json(first_worksheet, {header:1});
/* convert from array of arrays to workbook */
var worksheet = XLSX.utils.aoa_to_sheet(data);
var new_workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(new_workbook, worksheet, "SheetJS");
```
The column objects can be generated with the `encode_col` utility function:
```js
function make_cols(refstr/*:string*/) {
var o = [];
var range = XLSX.utils.decode_range(refstr);
for(var i = 0; i <= range.e.c; ++i) {
o.push({name: XLSX.utils.encode_col(i), key:i});
}
return o;
}
```
## React Native ## React Native
...@@ -52,6 +73,7 @@ The appropriate state model is application-specific. ...@@ -52,6 +73,7 @@ The appropriate state model is application-specific.
Reproducing the full project is straightforward: Reproducing the full project is straightforward:
```bash ```bash
# see native.sh
react-native init SheetJS react-native init SheetJS
cd SheetJS cd SheetJS
npm i -S xlsx react react-native react-native-table-component react-native-fs npm i -S xlsx react react-native react-native-table-component react-native-fs
...@@ -60,9 +82,44 @@ cp ../react-native.js index.android.js ...@@ -60,9 +82,44 @@ cp ../react-native.js index.android.js
react-native link react-native link
``` ```
This uses `react-native-fs` to read and write files on devices. The app will `react-native-table-component` draws the data table. `react-native-fs` reads
prompt before reading and after writing data. The printed location will be: 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 - android: path in the device filesystem
- iOS simulator: local path to file - iOS simulator: local path to file
- iOS device: a path accessible from iTunes App Documents view - iOS device: a path accessible from iTunes App Documents view
`react-native-fs` supports `"ascii"` encoding for `readFile` and `writeFile`.
In practice, that encoding uses binary strings compatible with `"binary"` type:
```js
import { writeFile, readFile } from 'react-native-fs';
/* read a workbook */
readFile(file, 'ascii').then((res) => {
const workbook = XLSX.read(res, {type:'binary'});
/* DO SOMETHING WITH workbook HERE */
});
/* write a workbook */
const wbout = XLSX.write(wb, {type:'binary', bookType:"xlsx"});
writeFile(file, wbout, 'ascii').then((r)=>{/* :) */}).catch((e)=>{/* :( */});
```
## Other Demos
#### Preact
`preact-compat` is an easy-to-use compatibility layer that provides equivalents
for `React` and `ReactDOM`. The `preact` demo uses the same JSX component code!
[The docs](https://npm.im/preact-compat#use-without-webpackbrowserify) explain
how to convert the in-browser React demo to Preact.
#### Server-Rendered React Components with Next.js
The demo uses the same component code as the in-browser version, but the build
step adds a small header that imports the library. The import is not needed in
deployments that use script tags to include the library.
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-xlsx?pixel)](https://github.com/SheetJS/js-xlsx)
#!/bin/bash #!/bin/bash
# xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
if [ ! -e SheetJS ]; then if [ ! -e SheetJS ]; then
react-native init SheetJS react-native init SheetJS
cd SheetJS cd SheetJS
...@@ -13,5 +13,5 @@ fi ...@@ -13,5 +13,5 @@ fi
cp react-native.js SheetJS/index.ios.js cp react-native.js SheetJS/index.ios.js
cp react-native.js SheetJS/index.android.js cp react-native.js SheetJS/index.android.js
cd SheetJS; cd SheetJS;
react-native link RNFB_ANDROID_PERMISSIONS=true react-native link
cd -; cd -;
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
var XLSX = require('../../'); // test against development version import * as XLSX from 'xlsx';
//var XLSX = require('xlsx'); // use in production import { saveAs } from 'file-saver';
module.exports = XLSX;
...@@ -6,16 +6,11 @@ export default () => ( ...@@ -6,16 +6,11 @@ export default () => (
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>SheetJS React Demo</title> <title>SheetJS React Demo</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" /> <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>{` <style jsx>{`
body, #app { height: 100%; }; body, #app { height: 100%; };
`}</style> `}</style>
</Head> </Head>
<div class="container-fluid"> <div className="container-fluid">
<h1><a href="http://sheetjs.com">SheetJS React Demo</a></h1> <h1><a href="http://sheetjs.com">SheetJS React Demo</a></h1>
<br /> <br />
<a href="https://github.com/SheetJS/js-xlsx">Source Code Repo</a><br /> <a href="https://github.com/SheetJS/js-xlsx">Source Code Repo</a><br />
......
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
import * as XLSX from 'xlsx'; import * as XLSX from 'xlsx';
import React, { Component } from 'react'; import React, { Component } from 'react';
import { AppRegistry, StyleSheet, Text, View, Button, Alert, Image } from 'react-native'; import { AppRegistry, StyleSheet, Text, View, Button, Alert, Image } from 'react-native';
import { Table, Row, Rows } from 'react-native-table-component'; import { Table, Row, Rows } from 'react-native-table-component';
import { writeFile, readFile, DocumentDirectoryPath } from 'react-native-fs'
// react-native-fs
import { writeFile, readFile, DocumentDirectoryPath } from 'react-native-fs';
const DDP = DocumentDirectoryPath + "/"; const DDP = DocumentDirectoryPath + "/";
const input = res => res;
const output = str => str;
// react-native-fetch-blob
/*
import RNFetchBlob from 'react-native-fetch-blob';
const { writeFile, readFile, dirs:{ DocumentDir } } = RNFetchBlob.fs;
const DDP = DocumentDir + "/";
const input = res => res.map(x => String.fromCharCode(x)).join("");
const output = str => str.split("").map(x => x.charCodeAt(0));
*/
const make_cols = refstr => Array.from({length: XLSX.utils.decode_range(refstr).e.c + 1}, (x,i) => XLSX.utils.encode_col(i)); const make_cols = refstr => Array.from({length: XLSX.utils.decode_range(refstr).e.c + 1}, (x,i) => XLSX.utils.encode_col(i));
...@@ -26,22 +37,32 @@ export default class SheetJS extends Component { ...@@ -26,22 +37,32 @@ export default class SheetJS extends Component {
{text: 'Cancel', onPress: () => {}, style: 'cancel' }, {text: 'Cancel', onPress: () => {}, style: 'cancel' },
{text: 'Import', onPress: () => { {text: 'Import', onPress: () => {
readFile(DDP + "sheetjs.xlsx", 'ascii').then((res) => { readFile(DDP + "sheetjs.xlsx", 'ascii').then((res) => {
const wb = XLSX.read(res, {type:'binary'}); /* parse file */
const wb = XLSX.read(input(res), {type:'binary'});
/* convert first worksheet to AOA */
const wsname = wb.SheetNames[0]; const wsname = wb.SheetNames[0];
const ws = wb.Sheets[wsname]; const ws = wb.Sheets[wsname];
const data = XLSX.utils.sheet_to_json(ws, {header:1}); const data = XLSX.utils.sheet_to_json(ws, {header:1});
/* update state */
this.setState({ data: data, cols: make_cols(ws['!ref']) }); this.setState({ data: data, cols: make_cols(ws['!ref']) });
}).catch((err) => { Alert.alert("importFile Error", "Error " + err.message); }); }).catch((err) => { Alert.alert("importFile Error", "Error " + err.message); });
}} }}
]); ]);
} }
exportFile() { exportFile() {
/* convert AOA back to worksheet */
const ws = XLSX.utils.aoa_to_sheet(this.state.data); const ws = XLSX.utils.aoa_to_sheet(this.state.data);
/* build new workbook */
const wb = XLSX.utils.book_new(); const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "SheetJS"); XLSX.utils.book_append_sheet(wb, ws, "SheetJS");
const wbout = XLSX.write(wb, {type:"binary", bookType:"xlsx"});
/* write file */
const wbout = XLSX.write(wb, {type:'binary', bookType:"xlsx"});
const file = DDP + "sheetjsw.xlsx"; const file = DDP + "sheetjsw.xlsx";
writeFile(file, wbout, 'ascii').then((res) =>{ writeFile(file, output(wbout), 'ascii').then((res) =>{
Alert.alert("exportFile success", "Exported to " + file); Alert.alert("exportFile success", "Exported to " + file);
}).catch((err) => { Alert.alert("exportFile Error", "Error " + err.message); }); }).catch((err) => { Alert.alert("exportFile Error", "Error " + err.message); });
}; };
......
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
const SheetJSFT = [ /* Notes:
"xlsx", "xlsb", "xlsm", "xls", "xml", "csv", "txt", "ods", "fods", "uos", "sylk", "dif", "dbf", "prn", "qpw", "123", "wb*", "wq*", "html", "htm" - usage: `ReactDOM.render( <SheetJSApp />, document.getElementById('app') );`
].map(function(x) { return "." + x; }).join(","); - xlsx.full.min.js is loaded in the head of the HTML page
- this script should be referenced with type="text/babel"
- babel.js in-browser transpiler should be loaded before this script
*/
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", K: 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
/* -------------------------------------------------------------------------- */
/* /*
Simple HTML5 file drag-and-drop wrapper Simple HTML5 file drag-and-drop wrapper
...@@ -49,9 +107,6 @@ class DataInput extends React.Component { ...@@ -49,9 +107,6 @@ class DataInput extends React.Component {
); }; ); };
} }
/* 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 Simple HTML Table
usage: <OutTable data={data} cols={cols} /> usage: <OutTable data={data} cols={cols} />
...@@ -64,10 +119,10 @@ class OutTable extends React.Component { ...@@ -64,10 +119,10 @@ class OutTable extends React.Component {
<div className="table-responsive"> <div className="table-responsive">
<table className="table table-striped"> <table className="table table-striped">
<thead> <thead>
<tr>{this.props.cols.map((c) => <th>{c.name}</th>)}</tr> <tr>{this.props.cols.map((c) => <th key={c.key}>{c.name}</th>)}</tr>
</thead> </thead>
<tbody> <tbody>
{this.props.data.map(r => <tr> {this.props.data.map((r,i) => <tr key={i}>
{this.props.cols.map(c => <td key={c.key}>{ r[c.key] }</td>)} {this.props.cols.map(c => <td key={c.key}>{ r[c.key] }</td>)}
</tr>)} </tr>)}
</tbody> </tbody>
...@@ -76,64 +131,18 @@ class OutTable extends React.Component { ...@@ -76,64 +131,18 @@ class OutTable extends React.Component {
); }; ); };
}; };
/* list of supported file types */
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(",");
/* see Browser download file example in docs */ /* see Browser download file example in docs */
function s2ab(s) { function s2ab(s/*:string*/)/*:ArrayBuffer*/ {
const buf = new ArrayBuffer(s.length); const buf = new ArrayBuffer(s.length);
const view = new Uint8Array(buf); const view = new Uint8Array(buf);
for (let i=0; i!=s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF; for (let i=0; i!=s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
return buf; return buf;
} }
class SheetJSApp extends React.Component { /* generate an array of column objects */
constructor(props) { 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}));
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
xlsx.full* xlsx.full*
r.js r.js
require.js require.js
requirejs-built.js app-built.js
...@@ -7,4 +7,4 @@ $(TOOL).js: ...@@ -7,4 +7,4 @@ $(TOOL).js:
if [ ! -e require.js ]; then curl -O http://requirejs.org/docs/release/2.3.3/comments/require.js; fi if [ ! -e require.js ]; then curl -O http://requirejs.org/docs/release/2.3.3/comments/require.js; fi
if [ ! -e r.js ]; then curl -O http://requirejs.org/docs/release/2.3.3/r.js; fi if [ ! -e r.js ]; then curl -O http://requirejs.org/docs/release/2.3.3/r.js; fi
rm -f xlsx.full.min.js; ln -s ../../dist/xlsx.full.min.js rm -f xlsx.full.min.js; ln -s ../../dist/xlsx.full.min.js
node r.js -o build.js node r.js -o build.js paths.requireLib=./require include=requireLib
...@@ -2,10 +2,75 @@ ...@@ -2,10 +2,75 @@
The minified dist files trip up the RequireJS mechanism. To bypass, the scripts The minified dist files trip up the RequireJS mechanism. To bypass, the scripts
automatically expose an `XLSX` variable that can be used if the require callback automatically expose an `XLSX` variable that can be used if the require callback
argument is `_XLSX` rather than `XLSX`: argument is `_XLSX` rather than `XLSX`. This trick is employed in the included
`xlsx-shim.js` script:
```js ```js
require(["xlsx.full.min"], function(_XLSX) { /* ... */ }); /* xlsx-shim.js */
define(['xlsx'], function (_XLSX) {
return XLSX;
});
``` ```
This demo uses the `r.js` optimizer to build a source file. The require config should set `xlsx` path to the appropriate dist file:
```js
paths: {
xlsx: "xlsx.full.min"
},
```
Once that is set, app code can freely require `"xlsx-shim"`:
```js
require(["xlsx-shim"], function(XLSX) {
/* use XLSX here */
});
```
## Deployments
`browser.html` demonstrates a dynamic deployment, using the in-browser config:
```html
<script src="require.js"></script>
<script>
require.config({
baseUrl: ".",
name: "app",
paths: {
xlsx: "xlsx.full.min"
}
});
</script>
<script src="app.js"></script>
```
`optimizer.html` demonstrates an optimized deployment using `build.js` config:
```js
/* build config */
({
baseUrl: ".",
name: "app",
paths: {
xlsx: "xlsx.full.min"
},
out: "app-built.js"
})
```
The optimizer is invoked with:
```bash
node r.js -o build.js paths.requireLib=./require include=requireLib
```
That step creates a file `app-built.js` that can be included in a page:
```html
<!-- final bundle includes require.js, xlsx-shim, library and app code -->
<script src="app-built.js"></script>
```
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-xlsx?pixel)](https://github.com/SheetJS/js-xlsx)
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
require(["xlsx.full.min"], function(_XLSX) { require(["xlsx-shim"], function(XLSX) {
console.log(XLSX);
var X = XLSX; var X = XLSX;
var global_wb; var global_wb;
......
<!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 Live 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
}
#b64data{
width:100%;
}
a { text-decoration: none }
</style>
</head>
<body>
<pre>
<b><a href="http://sheetjs.com">SheetJS Data Preview Live Demo</a></b>
(Base64 text works back to IE6; drag and drop works back to IE10)
<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>
Output Format: <select name="format" onchange="setfmt()">
<option value="csv" selected> CSV</option>
<option value="json"> JSON</option>
<option value="form"> FORMULAE</option>
<option value="html"> HTML</option>
</select><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
<textarea id="b64data">... or paste a base64-encoding here</textarea>
<input type="button" id="dotext" value="Click here to process the base64 text" onclick="b64it();"/><br />
<b>Advanced Demo Options:</b>
Use Web Workers: (when available) <input type="checkbox" name="useworker" checked>
Use readAsBinaryString: (when available) <input type="checkbox" name="userabs" checked>
</pre>
<pre id="out"></pre>
<div id="htmlout"></div>
<br />
<script>
var XW = {
/* worker message */
msg: 'xlsx',
/* worker scripts */
worker: './xlsxworker.js'
};
</script>
<script src="require.js"></script>
<script>
require.config({
baseUrl: ".",
name: "app",
paths: {
xlsx: "xlsx.full.min"
}
});
</script>
<script src="app.js"></script>
</body>
</html>
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
({ ({
baseUrl: ".", baseUrl: ".",
name: "requirejs", name: "app",
out: "requirejs-built.js" paths: {
xlsx: "xlsx.full.min"
},
out: "app-built.js"
}) })
...@@ -46,7 +46,6 @@ Use readAsBinaryString: (when available) <input type="checkbox" name="userabs" c ...@@ -46,7 +46,6 @@ Use readAsBinaryString: (when available) <input type="checkbox" name="userabs" c
<pre id="out"></pre> <pre id="out"></pre>
<div id="htmlout"></div> <div id="htmlout"></div>
<br /> <br />
<script src="require.js"></script>
<script> <script>
var XW = { var XW = {
/* worker message */ /* worker message */
...@@ -55,6 +54,6 @@ var XW = { ...@@ -55,6 +54,6 @@ var XW = {
worker: './xlsxworker.js' worker: './xlsxworker.js'
}; };
</script> </script>
<script src="requirejs-built.js"></script> <script src="app-built.js"></script>
</body> </body>
</html> </html>
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
define(['xlsx'], function (_XLSX) {
/* work around require.js */
return XLSX;
});
rollup.js rollup.js
rollup.min.js rollup.min.js
rollup.node.js rollup.node.js
worker.js
worker.min.js
TOOL=rollup TOOL=rollup
.PHONY: all .PHONY: all
all: $(TOOL).min.js all: $(TOOL).min.js worker.min.js
$(TOOL).min.js: $(TOOL).js $(TOOL).min.js: $(TOOL).js
uglifyjs $< > $@ uglifyjs $< > $@
...@@ -12,6 +12,10 @@ $(TOOL).js: ...@@ -12,6 +12,10 @@ $(TOOL).js:
node -e 'require("./rollup.node")' node -e 'require("./rollup.node")'
# browser # browser
rollup -c rollup -c
rollup -c rollup.config.worker.js
worker.min.js: worker.js
uglifyjs $< > $@
.PHONY: init .PHONY: init
init: init:
......
...@@ -2,7 +2,11 @@ ...@@ -2,7 +2,11 @@
This library presents itself as a CommonJS library, so some configuration is This library presents itself as a CommonJS library, so some configuration is
required. The examples at <https://rollupjs.org> can be followed pretty much in required. The examples at <https://rollupjs.org> can be followed pretty much in
verbatim. This sample demonstrates a rollup for browser as well as for node. verbatim. This sample demonstrates a bundle for browser as well as for node.
This demo uses the `import` form to expose the whole library, enabling client
code to access the library with `import XLSX from 'xlsx'`. The JS code from
the root demo was moved to a separate `app.js` script.
## Required Plugins ## Required Plugins
...@@ -24,3 +28,17 @@ export default { ...@@ -24,3 +28,17 @@ export default {
}; };
``` ```
For the browser deployments, the output format is `'iife'`. For node, the
output format is `'cjs'`.
### Worker Scripts
Rollup can also bundle worker scripts! Instead of using `importScripts`, the
worker script should import the module:
```diff
-importScripts('dist/xlsx.full.min.js');
+import XLSX from 'xlsx';
```
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-xlsx?pixel)](https://github.com/SheetJS/js-xlsx)
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
/*jshint browser:true */ /*jshint browser:true */
/*global XLSX */ /*global XLSX */
import XLSX from 'xlsx';
var X = XLSX; var X = XLSX;
var global_wb; var global_wb;
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
import resolve from 'rollup-plugin-node-resolve'; import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs'; import commonjs from 'rollup-plugin-commonjs';
export default { export default {
entry: 'main.js', entry: 'app.js',
dest: 'rollup.js', dest: 'rollup.js',
plugins: [ plugins: [
resolve({ resolve({
...@@ -11,6 +11,5 @@ export default { ...@@ -11,6 +11,5 @@ export default {
}), }),
commonjs() commonjs()
], ],
moduleName: 'XLSX',
format: 'iife' format: 'iife'
}; };
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
export default {
entry: 'xlsxworker.js',
dest: 'worker.js',
plugins: [
resolve({
module: false,
browser: true,
}),
commonjs()
],
format: 'iife'
};
...@@ -46,15 +46,14 @@ Use readAsBinaryString: (when available) <input type="checkbox" name="userabs" c ...@@ -46,15 +46,14 @@ Use readAsBinaryString: (when available) <input type="checkbox" name="userabs" c
<pre id="out"></pre> <pre id="out"></pre>
<div id="htmlout"></div> <div id="htmlout"></div>
<br /> <br />
<script src="rollup.min.js"></script>
<script> <script>
var XW = { var XW = {
/* worker message */ /* worker message */
msg: 'xlsx', msg: 'xlsx',
/* worker scripts */ /* worker scripts */
worker: './xlsxworker.js' worker: './worker.js'
}; };
</script> </script>
<script src="app.js"></script> <script src="rollup.min.js"></script>
</body> </body>
</html> </html>
B/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ B/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
......
...@@ -19,3 +19,8 @@ micro: init ## micro demo ...@@ -19,3 +19,8 @@ micro: init ## micro demo
.PHONY: koa .PHONY: koa
koa: init ## koa demo koa: init ## koa demo
node koa.js node koa.js
.PHONY: hapi
hapi: init ## hapi demo
cp ../../dist/xlsx.full.min.js .
node hapi.js
...@@ -25,6 +25,15 @@ request(url, {encoding: null}, function(err, res, data) { ...@@ -25,6 +25,15 @@ request(url, {encoding: null}, function(err, res, data) {
}); });
``` ```
The `readFile` / `writeFile` functions wrap `fs.{read,write}FileSync`:
```js
/* equivalent to `var wb = XLSX.readFile("sheetjs.xlsx");` */
var buf = fs.readFileSync("sheetjs.xlsx");
var wb = XLSX.read(buf, {type:'buffer'});
```
### Example servers ### Example servers
Each example server is expected to hold an array-of-arrays in memory. They are Each example server is expected to hold an array-of-arrays in memory. They are
...@@ -82,11 +91,11 @@ The main server script is `koa.js` and the worker script is `koasub.js`. State ...@@ -82,11 +91,11 @@ The main server script is `koa.js` and the worker script is `koasub.js`. State
is maintained in the worker script. is maintained in the worker script.
## xlsx script with micro ## command-line utility with micro
The node module ships with the `xlsx` bin script. For global installs, symlinks The npm module ships with the `xlsx` command line tool. For global installs, the
are configured to enable running `xlsx` from anywhere. For local installs, the script `bin/xlsx.njs` is added to a directory in `PATH`. For local installs, the
appropriate symlink is set up in `node_modules/.bin/`. appropriate script or symbolic link is set up in `node_modules/.bin/`.
The `--arrays` option directs `xlsx` to generate an array of arrays that can be 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 parsed by the server. To generate files, the `json2csv` module exports the JS
...@@ -94,3 +103,26 @@ array of arrays to a CSV, the server writes the file, and the `xlsx` command is ...@@ -94,3 +103,26 @@ array of arrays to a CSV, the server writes the file, and the `xlsx` command is
used to generate files of different formats. used to generate files of different formats.
## tiny-worker with hapi
`tiny-worker` provides a Web Worker-like interface. Binary strings and simple
objects are readily passed across the Worker divide.
The main server script is `hapi.js` and the worker script is `worker.js`. State
is maintained in the server script.
Note: due to an issue with hapi payload parsing, the route `POST /file` is used
to handle the case of reading from file, so the cURL test is:
```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/file?f=sheetjs.xlsx
# write sheetjs.xlsb in the XLSB format
curl -X GET http://localhost:7262/?f=sheetjs.xlsb
```
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-xlsx?pixel)](https://github.com/SheetJS/js-xlsx)
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
var Hapi = require('hapi'), server = new Hapi.Server(); var Hapi = require('hapi'), server = new Hapi.Server();
var logit = require('./_logit'); var logit = require('./_logit');
var Worker = require('webworker-threads').Worker; var Worker = require('tiny-worker');
var fs = require('fs');
var data = "a,b,c\n1,2,3".split("\n").map(x => x.split(",")); var data = "a,b,c\n1,2,3".split("\n").map(x => x.split(","));
function get_data(req, res, type) { function get_data(req, res, type) {
var work = new Worker(function(){ var work = new Worker('worker.js');
var XLSX = require('xlsx'); work.onmessage = function(e) {
this.onmessage = function(e) { if(e.data.err) console.log(e.data.err);
console.log("get data " + e.data); return res(e.data.data);
var ws = XLSX.utils.aoa_to_sheet(e.data[1]); };
var wb = XLSX.utils.book_new(); work.postMessage({action:"write", type:type, data:data});
XLSX.utils.book_append_sheet(wb, ws, "SheetJS"); }
console.log("prepared wb");
postMessage(XLSX.write(wb, {type:'binary', bookType:type})); function get_file(req, res, file) {
console.log("sent data"); var work = new Worker('worker.js');
}; work.onmessage = function(e) {
}); fs.writeFileSync(file, e.data.data, 'binary');
work.onmessage = function(e) { console.log(e); res(e); }; return res("wrote to " + file + "\n");
work.postMessage([type, data]); };
work.postMessage({action:"write", file:file, data:data});
}
function post_file(req, res, file) {
var work = new Worker('worker.js');
work.onmessage = function(e) {
data = e.data.data;
return res("read from " + file + "\n");
};
work.postMessage({action:"read", file:file});
}
function post_data(req, res, type) {
var keys = Object.keys(req.payload), k = keys[0];
post_file(req, res, req.payload[k].path);
} }
var port = 7262; var port = 7262;
server.connection({ host:'localhost', port: port}); server.connection({ host:'localhost', port: port});
server.route({ method: 'GET', path: '/', handler: function(req, res) { server.route({ method: 'GET', path: '/',
handler: function(req, res) {
logit(req.raw.req, req.raw.res);
if(req.query.t) get_data(req, res, req.query.t);
else if(req.query.f) get_file(req, res, req.query.f);
else res('Forbidden').code(403);
}});
server.route({ method: 'POST', path: '/',
config:{payload:{ output: 'file', parse: true, allow: 'multipart/form-data'}},
handler: function(req, res) {
logit(req.raw.req, req.raw.res); logit(req.raw.req, req.raw.res);
if(req.query.t) return get_data(req, res, req.query.t); if(req.query.f) return post_file(req, res, req.query.f);
else if(req.query.f) return get_file(req, res, req.query.f); return post_data(req, res);
return res('Forbidden').code(403);
}}); }});
server.route({ method: 'POST', path: '/', handler: function(req, res) { server.route({ method: 'POST', path: '/file',
handler: function(req, res) {
logit(req.raw.req, req.raw.res); logit(req.raw.req, req.raw.res);
if(req.query.f) return post_file(req, res, req.query.f); if(req.query.f) return post_file(req, res, req.query.f);
return post_data(req, res); return post_data(req, res);
......
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
var XLSX = require('xlsx');
var fs = require('fs');
onmessage = function(e) {
try { switch(e.data.action) {
case 'write':
var ws = XLSX.utils.aoa_to_sheet(e.data.data);
var wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "SheetJS");
postMessage({data: XLSX.write(wb, {type:'binary', bookType:e.data.type || e.data.file.match(/\.([^\.]*)$/)[1]})});
break;
case 'read':
var wb;
if(e.data.file) wb = XLSX.readFile(e.data.file);
else wb = XLSX.read(e.data.data);
var ws = wb.Sheets[wb.SheetNames[0]];
postMessage({data: XLSX.utils.sheet_to_json(ws, {header:1})});
break;
default: throw "unknown action";
}} catch(e) { postMessage({err:e.message || e}); }
};
.PHONY: test .PHONY: test
test: test:
cp ../../dist/xlsx.full.min.js . cp ../../dist/xlsx.full.min.js .
node systemjsnode.js node test.node.js
...@@ -35,7 +35,29 @@ var j = XLSX.utils.sheet_to_json(w.Sheets[w.SheetNames[0]], {header:1}); ...@@ -35,7 +35,29 @@ var j = XLSX.utils.sheet_to_json(w.Sheets[w.SheetNames[0]], {header:1});
console.log(j); console.log(j);
``` ```
The file functions `readFile` and `writeFile` are not available in the browser. Note: The `readFile` and `writeFile` functions are not available in the browser.
## Web Workers
Web Workers can load the SystemJS library with `importScripts`, but the imported
code cannot assign the original worker's `onmessage` callback. This demo works
around the limitation by exposing the desired function as a global:
```js
/* main worker script */
importScripts('system.js');
SystemJS.config({ /* ... browser config ... */ });
onmessage = function(evt) {
SystemJS.import('xlsxworker.js').then(function() { _cb(evt); });
};
/* xlsxworker.js */
var XLSX = require('xlsx');
_cb = function (evt) { /* ... do work here ... */ };
```
## Node ## Node
...@@ -64,3 +86,4 @@ SystemJS.import('xlsx').then(function(XLSX) { ...@@ -64,3 +86,4 @@ SystemJS.import('xlsx').then(function(XLSX) {
}); });
``` ```
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-xlsx?pixel)](https://github.com/SheetJS/js-xlsx)
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
/*jshint browser:true */
/*global XLSX */
var XLSX = require('xlsx'); var XLSX = require('xlsx');
console.log(XLSX); var X = XLSX;
var w = XLSX.read('abc,def\nghi,jkl', {type:'binary'});
var j = XLSX.utils.sheet_to_json(w.Sheets[w.SheetNames[0]], {header:1}); var global_wb;
console.log(j);
var process_wb = (function() {
var OUT = document.getElementById('out');
var HTMLOUT = document.getElementById('htmlout');
var get_format = (function() {
var radios = document.getElementsByName( "format" );
return function() {
for(var i = 0; i < radios.length; ++i) if(radios[i].checked || radios.length === 1) return radios[i].value;
};
})();
var to_json = function to_json(workbook) {
var result = {};
workbook.SheetNames.forEach(function(sheetName) {
var roa = X.utils.sheet_to_json(workbook.Sheets[sheetName]);
if(roa.length) result[sheetName] = roa;
});
return JSON.stringify(result, 2, 2);
};
var to_csv = function to_csv(workbook) {
var result = [];
workbook.SheetNames.forEach(function(sheetName) {
var csv = X.utils.sheet_to_csv(workbook.Sheets[sheetName]);
if(csv.length){
result.push("SHEET: " + sheetName);
result.push("");
result.push(csv);
}
});
return result.join("\n");
};
var to_fmla = function to_fmla(workbook) {
var result = [];
workbook.SheetNames.forEach(function(sheetName) {
var formulae = X.utils.get_formulae(workbook.Sheets[sheetName]);
if(formulae.length){
result.push("SHEET: " + sheetName);
result.push("");
result.push(formulae.join("\n"));
}
});
return result.join("\n");
};
var to_html = function to_html(workbook) {
HTMLOUT.innerHTML = "";
workbook.SheetNames.forEach(function(sheetName) {
var htmlstr = X.write(workbook, {sheet:sheetName, type:'binary', bookType:'html'});
HTMLOUT.innerHTML += htmlstr;
});
return "";
};
return function process_wb(wb) {
global_wb = wb;
var output = "";
switch(get_format()) {
case "form": output = to_fmla(wb); break;
case "html": output = to_html(wb); break;
case "json": output = to_json(wb); break;
default: output = to_csv(wb);
}
if(OUT.innerText === undefined) OUT.textContent = output;
else OUT.innerText = output;
if(typeof console !== 'undefined') console.log("output", new Date());
};
})();
var setfmt = window.setfmt = function setfmt() { if(global_wb) process_wb(global_wb); };
var b64it = window.b64it = (function() {
var tarea = document.getElementById('b64data');
return function b64it() {
if(typeof console !== 'undefined') console.log("onload", new Date());
var wb = X.read(tarea.value, {type:'base64', WTF:false});
process_wb(wb);
};
})();
var do_file = (function() {
var rABS = typeof FileReader !== "undefined" && (FileReader.prototype||{}).readAsBinaryString;
var domrabs = document.getElementsByName("userabs")[0];
if(!rABS) domrabs.disabled = !(domrabs.checked = false);
var use_worker = typeof Worker !== 'undefined';
var domwork = document.getElementsByName("useworker")[0];
if(!use_worker) domwork.disabled = !(domwork.checked = false);
var xw = function xw(data, cb) {
var worker = new Worker(XW.worker);
worker.onmessage = function(e) {
switch(e.data.t) {
case 'ready': break;
case 'e': console.error(e.data.d); break;
case XW.msg: cb(JSON.parse(e.data.d)); break;
}
};
worker.postMessage({d:data,b:rABS?'binary':'array'});
};
return function do_file(files) {
rABS = domrabs.checked;
use_worker = domwork.checked;
var f = files[0];
var reader = new FileReader();
reader.onload = function(e) {
if(typeof console !== 'undefined') console.log("onload", new Date(), rABS, use_worker);
var data = e.target.result;
if(!rABS) data = new Uint8Array(data);
if(use_worker) xw(data, process_wb);
else process_wb(X.read(data, {type: rABS ? 'binary' : 'array'}));
};
if(rABS) reader.readAsBinaryString(f);
else reader.readAsArrayBuffer(f);
};
})();
(function() {
var drop = document.getElementById('drop');
if(!drop.addEventListener) return;
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');
if(!xlf.addEventListener) return;
function handleFile(e) { do_file(e.target.files); }
xlf.addEventListener('change', handleFile, false);
})();
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
var XLSX = require('xlsx');
console.log(XLSX);
var w = XLSX.read('abc,def\nghi,jkl', {type:'binary'});
var j = XLSX.utils.sheet_to_json(w.Sheets[w.SheetNames[0]], {header:1});
console.log(j);
<!-- xlsx.js (C) 2013-present SheetJS http://sheetjs.com -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.20.16/system.js"></script>
<script>
SystemJS.config({
meta: {
'xlsx': {
exports: 'XLSX' // <-- tell SystemJS to expose the XLSX variable
}
},
map: {
'xlsx': 'xlsx.full.min.js', // <-- make sure xlsx.full.min.js is in same dir
'fs': '', // <--|
'crypto': '', // <--| suppress native node modules
'stream': '' // <--|
}
});
SystemJS.import('main.simple.js');
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.20.16/system.js"></script>
<script>
SystemJS.config({
meta: {
'xlsx': {
exports: 'XLSX'
}
},
map: {
'xlsx': 'xlsx.full.min.js',
'fs': '',
'crypto': '',
'stream': ''
}
});
SystemJS.import('main.js');
</script>
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
importScripts('https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.20.19/system.js');
SystemJS.config({
meta: {
'xlsx': {
exports: 'XLSX' // <-- tell SystemJS to expose the XLSX variable
}
},
map: {
'xlsx': 'xlsx.full.min.js', // <-- make sure xlsx.full.min.js is in same dir
'fs': '', // <--|
'crypto': '', // <--| suppress native node modules
'stream': '' // <--|
}
});
onmessage = function(evt) {
/* the real action is in the _cb function from xlsxworker.js */
SystemJS.import('xlsxworker.js').then(function() { _cb(evt); });
};
B/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ B/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
# VueJS 2 # VueJS 2
The `xlsx.core.min.js` and `xlsx.full.min.js` scripts are designed to be dropped The `xlsx.core.min.js` and `xlsx.full.min.js` scripts are designed to be dropped
into web pages with script tags e.g. into web pages with script tags:
```html ```html
<script src="xlsx.full.min.js"></script> <script src="xlsx.full.min.js"></script>
``` ```
Strictly speaking, there should be no need for a Vue.JS demo! You can proceed The library can also be imported directly from single-file components with:
as you would with any other browser-friendly library.
```js
import XLSX from 'xlsx';
```
This demo directly generates HTML using `sheet_to_html` and adds an element to 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. a pre-generated template. It also has a button for exporting as XLSX.
Other scripts in this demo show: Other scripts in this demo show:
- server-rendered VueJS component (with `nuxt.js`) - server-rendered VueJS component (with `nuxt.js`)
- `weex` deployment for iOS - `weex` deployment for iOS
## Single File Components ## Internal State
The plain JS demo embeds state in the DOM. Other demos use proper 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 ]
]
}
```
`sheet_to_json` and `aoa_to_sheet` utility functions can convert between arrays
of arrays and worksheets:
```js
/* convert from workbook to array of arrays */
var first_worksheet = workbook.Sheets[workbook.SheetNames[0]];
var data = XLSX.utils.sheet_to_json(first_worksheet, {header:1});
/* convert from array of arrays to workbook */
var worksheet = XLSX.utils.aoa_to_sheet(data);
var new_workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(new_workbook, worksheet, "SheetJS");
```
The column objects can be generated with the `encode_col` utility function:
For Single File Components, a simple `import XLSX from 'xlsx'` should suffice. ```js
The webpack demo includes a sample `webpack.config.js`. function make_cols(refstr/*:string*/) {
var o = [];
var range = XLSX.utils.decode_range(refstr);
for(var i = 0; i <= range.e.c; ++i) {
o.push({name: XLSX.utils.encode_col(i), key:i});
}
return o;
}
```
## WeeX ## WeeX
<img src="screen.png" width="400px"/>
Reproducing the full project is a little bit tricky. The included `weex.sh`
script performs the necessary installation steps.
WeeX is a framework for building real mobile apps, akin to React Native. The 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 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 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 documents to <https://hastebin.com> and download a precomputed
[Base64-encoded workbook](http://sheetjs.com/sheetjs.xlsx.b64). [Base64-encoded workbook](http://sheetjs.com/sheetjs.xlsx.b64).
Using NodeJS it is straightforward to convert to/from base64: Using NodeJS it is straightforward to convert to/from Base64:
```js ```js
/* convert sheetjs.xlsx -> sheetjs.xlsx.b64 */ /* convert sheetjs.xlsx -> sheetjs.xlsx.b64 */
...@@ -42,24 +91,9 @@ var str = fs.readFileSync("sheetjs.xls.b64").toString(); ...@@ -42,24 +91,9 @@ var str = fs.readFileSync("sheetjs.xls.b64").toString();
fs.writeFileSync("sheetjs.xls", new Buffer(str, "base64")); fs.writeFileSync("sheetjs.xls", new Buffer(str, "base64"));
``` ```
## Nuxt and State ## Other Demos
The `nuxt.js` demo uses the same state approach as the React next.js demo: #### Server-Rendered VueJS Components with Nuxt.js
```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 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: be explicitly included in the layout HTML (as script tag) and in the component:
...@@ -69,3 +103,5 @@ const _XLSX = require('xlsx'); ...@@ -69,3 +103,5 @@ const _XLSX = require('xlsx');
const X = typeof XLSX !== 'undefined' ? XLSX : _XLSX; const X = typeof XLSX !== 'undefined' ? XLSX : _XLSX;
/* use the variable X rather than XLSX in the component */ /* use the variable X rather than XLSX in the component */
``` ```
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-xlsx?pixel)](https://github.com/SheetJS/js-xlsx)
...@@ -29,8 +29,6 @@ function s2ab(s) { ...@@ -29,8 +29,6 @@ function s2ab(s) {
Vue.component('html-preview', { Vue.component('html-preview', {
template: SJSTemplate, template: SJSTemplate,
methods: { methods: {
// as per: https://github.com/SheetJS/js-xlsx/wiki/Reading-XLSX-from-FileReader.readAsArrayBuffer()
// changing the readAsBinaryString (deprecated) to readAsArrayBuffer()
onchange: function(evt) { onchange: function(evt) {
var file; var file;
var files = evt.target.files; var files = evt.target.files;
...@@ -38,7 +36,7 @@ Vue.component('html-preview', { ...@@ -38,7 +36,7 @@ Vue.component('html-preview', {
if (!files || files.length == 0) return; if (!files || files.length == 0) return;
file = files[0]; file = files[0];
var reader = new FileReader(); var reader = new FileReader();
reader.onload = function (e) { reader.onload = function (e) {
// pre-process data // pre-process data
...@@ -48,7 +46,7 @@ Vue.component('html-preview', { ...@@ -48,7 +46,7 @@ Vue.component('html-preview', {
for (var i = 0; i < length; i++) { for (var i = 0; i < length; i++) {
binary += String.fromCharCode(bytes[i]); binary += String.fromCharCode(bytes[i]);
} }
/* read workbook */ /* read workbook */
var wb = XLSX.read(binary, {type: 'binary'}); var wb = XLSX.read(binary, {type: 'binary'});
...@@ -57,7 +55,7 @@ Vue.component('html-preview', { ...@@ -57,7 +55,7 @@ Vue.component('html-preview', {
var ws = wb.Sheets[wsname]; var ws = wb.Sheets[wsname];
/* generate HTML */ /* generate HTML */
var HTML = XLSX.utils.sheet_to_html(ws); var HTML = XLSX.utils.sheet_to_html(ws);
/* update table */ /* update table */
document.getElementById('out-table').innerHTML = HTML; document.getElementById('out-table').innerHTML = HTML;
......
...@@ -9,9 +9,9 @@ ...@@ -9,9 +9,9 @@
<text :style="{ color: data.length ? '#841584' : '#CDCDCD', disabled: !data.length }" @click="exportFile">Upload XLSX</text> <text :style="{ color: data.length ? '#841584' : '#CDCDCD', disabled: !data.length }" @click="exportFile">Upload XLSX</text>
<text style="instructions">Current Data</text> <text style="instructions">Current Data</text>
<scroller class="scroller"> <scroller class="scroller">
<div class="row" v-for="(row, ridx) in data"> <div class="row" v-for="(row, ridx) in data" :key="ridx">
<text>ROW {{ridx + 1}}</text> <text>ROW {{ridx + 1}}</text>
<text v-for="(cell, cidx) in row">CELL {{get_label(ridx, cidx)}}:{{cell}}</text> <text v-for="(cell, cidx) in row" :key="cidx">CELL {{get_label(ridx, cidx)}}:{{cell}}</text>
</div> </div>
</scroller> </scroller>
</div> </div>
......
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ <!-- xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com -->
<template> <template>
<div @drop="_drop" @dragenter="_suppress" @dragover="_suppress"> <div @drop="_drop" @dragenter="_suppress" @dragover="_suppress">
<div class="row"><div class="col-xs-12"> <div class="row"><div class="col-xs-12">
...@@ -16,11 +16,13 @@ ...@@ -16,11 +16,13 @@
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-striped"> <table class="table table-striped">
<thead><tr> <thead><tr>
<th v-for="c in cols">{{c.name}}</th> <th v-for="c in cols" :key="c.key">{{c.name}}</th>
</tr></thead> </tr></thead>
<tbody><tr v-for="r in data"> <tbody>
<td v-for="c in cols"> {{ r[c.key] }}</td> <tr v-for="(r, key) in data" :key="key">
</tr></tbody> <td v-for="c in cols" :key="c.key"> {{ r[c.key] }}</td>
</tr>
</tbody>
</table> </table>
</div> </div>
</div></div> </div></div>
......
...@@ -5,9 +5,9 @@ if [ ! -e SheetJS ]; then ...@@ -5,9 +5,9 @@ if [ ! -e SheetJS ]; then
cd SheetJS cd SheetJS
npm install npm install
weexpack platform add ios weexpack platform add ios
sed -i 's/ATSDK-Weex/ATSDK/g' platforms/ios/Podfile # see https://github.com/weexteam/weex-pack/issues/133#issuecomment-295806132
sed -i.bak 's/ATSDK-Weex/ATSDK/g' platforms/ios/Podfile
cd - cd -
# weexpack run ios
fi fi
cp native.vue SheetJS/src/index.vue cp native.vue SheetJS/src/index.vue
if [ ! -e SheetJS/web/bootstrap.min.css ]; then if [ ! -e SheetJS/web/bootstrap.min.css ]; then
......
webpack.js main.js
webpack.min.js main.min.js
*.out.js *.out.js
TOOL=webpack TOOL=webpack
WPOPTS=--display-modules --display-reasons --profile WPOPTS=--display-modules --display-reasons --profile
.PHONY: all .PHONY: all
all: $(TOOL).min.js core.out.js full.out.js all: main.min.js core.out.js full.out.js app.out.js
$(TOOL).min.js: $(TOOL).js main.min.js: main.out.js
uglifyjs $< > $@ uglifyjs $< > $@
.PHONY: $(TOOL).js .PHONY: main.out.js core.out.js full.out.js
$(TOOL).js: main.out.js core.out.js full.out.js: %.out.js: %.js
webpack main.js --output-filename $@ $(WPOPTS)
.PHONY: core.out.js full.out.js
core.out.js full.out.js: %.out.js: %.js
webpack $< --output-filename $@ $(WPOPTS) webpack $< --output-filename $@ $(WPOPTS)
.PHONY: app.out.js
app.out.js: webpack.app.js app.js appworker.js
webpack --config $< $(WPOPTS)
...@@ -4,28 +4,40 @@ This library is built with some dynamic logic to determine if it is invoked in a ...@@ -4,28 +4,40 @@ This library is built with some dynamic logic to determine if it is invoked in a
script tag or in nodejs. Webpack does not understand those feature tests, so by script tag or in nodejs. Webpack does not understand those feature tests, so by
default it will do some strange things. default it will do some strange things.
## Suppressing the Node shims ## Basic Usage
The library properly guards against accidental leakage of node features in the `webpack.app.js` demonstrates bundling an entire app script in a bundle. For
browser but webpack disregards those. The config should explicitly suppress: basic projects requiring the module from the npm package, it is sufficient to
suppress the node shims:
```js ```js
/* webpack config for app.out.js */
{
/* entry point app.js */
entry: './app.js',
/* write to app.out.js */
output: { path:__dirname, filename: './app.out.js' },
/* suppress node shims */
node: { node: {
fs: false, fs: false,
process: false, process: false,
Buffer: false Buffer: false
} }
}
``` ```
## Exporting the XLSX variable ## Suppressing the Node shims
This library will not assign to module.exports if it is run in the browser. To The library properly guards against accidental leakage of node features in the
convince webpack, set `output` in the webpack config: browser but webpack disregards those. The config should explicitly suppress:
```js ```js
output: { node: {
libraryTarget: 'var', fs: false,
library: 'XLSX' process: false,
Buffer: false
} }
``` ```
...@@ -41,6 +53,8 @@ the module can be omitted by aliasing the dependency: ...@@ -41,6 +53,8 @@ the module can be omitted by aliasing the dependency:
}, },
``` ```
Alternatively, bundling the `xlsx.core.min.js` script always omits dependencies.
## Bower and minified versions ## Bower and minified versions
Webpack may show a message like "This seems to be a pre-built javascript file" Webpack may show a message like "This seems to be a pre-built javascript file"
...@@ -55,3 +69,44 @@ harmless. To suppress the message, set `module.noParse` in the webpack config: ...@@ -55,3 +69,44 @@ harmless. To suppress the message, set `module.noParse` in the webpack config:
] ]
} }
``` ```
## Other Demos
This demo also attempts to demonstrate bundling of the library as well as the
core and full distribution versions. `app.js` is the common app code (it will
not be bundled). The individual bundles merely wrap and reflect `XLSX`. The
app code uses the bundles with script tag inclusion in the main HTML files. The
worker scripts use the bundles with `importScripts` references.
| required script | HTML page | entry | worker script |
|----------------:|------------:|----------:|----------------:|
| main `xlsx` lib | `main.html` | `main.js` | `mainworker.js` |
| `xlsx.core.min` | `core.html` | `core.js` | `coreworker.js` |
| `xlsx.full.min` | `full.html` | `full.js` | `fullworker.js` |
The entry points in the demo merely require and re-export the library:
```js
/* main.js */
var XLSX = require('../../');
console.log("it works!");
module.exports = XLSX;
```
The main advantage of reflecting the library is deduplication: the library code
is only downloaded once. The basic example builds a separate worker script and
eventually ships the library twice.
### Reflecting the XLSX variable
This library will not assign to `module.exports` if it is run in the browser. To
convince webpack, the demo webpack config sets `output`:
```js
output: {
libraryTarget: 'var',
library: 'XLSX'
}
```
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-xlsx?pixel)](https://github.com/SheetJS/js-xlsx)
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
/*jshint browser:true */ /*jshint browser:true */
/*global XLSX */ /*global XLSX */
var X = XLSX; var X = typeof require !== "undefined" && require('../../') || XLSX;
var global_wb; var global_wb;
......
B/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ B/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
......
...@@ -2,10 +2,10 @@ ...@@ -2,10 +2,10 @@
importScripts('core.out.js'); importScripts('core.out.js');
postMessage({t:"ready"}); postMessage({t:"ready"});
onmessage = function (oEvent) { onmessage = function (evt) {
var v; var v;
try { try {
v = XLSX.read(oEvent.data.d, {type: oEvent.data.b}); v = XLSX.read(evt.data.d, {type: evt.data.b});
postMessage({t:"xlsx", d:JSON.stringify(v)}); postMessage({t:"xlsx", d:JSON.stringify(v)});
} catch(e) { postMessage({t:"e",d:e.stack||e}); } } catch(e) { postMessage({t:"e",d:e.stack||e}); }
}; };
...@@ -2,10 +2,10 @@ ...@@ -2,10 +2,10 @@
importScripts('full.out.js'); importScripts('full.out.js');
postMessage({t:"ready"}); postMessage({t:"ready"});
onmessage = function (oEvent) { onmessage = function (evt) {
var v; var v;
try { try {
v = XLSX.read(oEvent.data.d, {type: oEvent.data.b}); v = XLSX.read(evt.data.d, {type: evt.data.b});
postMessage({t:"xlsx", d:JSON.stringify(v)}); postMessage({t:"xlsx", d:JSON.stringify(v)});
} catch(e) { postMessage({t:"e",d:e.stack||e}); } } catch(e) { postMessage({t:"e",d:e.stack||e}); }
}; };
...@@ -46,13 +46,13 @@ Use readAsBinaryString: (when available) <input type="checkbox" name="userabs" c ...@@ -46,13 +46,13 @@ Use readAsBinaryString: (when available) <input type="checkbox" name="userabs" c
<pre id="out"></pre> <pre id="out"></pre>
<div id="htmlout"></div> <div id="htmlout"></div>
<br /> <br />
<script src="webpack.min.js"></script> <script src="main.min.js"></script>
<script> <script>
var XW = { var XW = {
/* worker message */ /* worker message */
msg: 'xlsx', msg: 'xlsx',
/* worker scripts */ /* worker scripts */
worker: './xlsxworker.js' worker: './mainworker.js'
}; };
</script> </script>
<script src="app.js"></script> <script src="app.js"></script>
......
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
importScripts('main.min.js');
postMessage({t:"ready"});
onmessage = function (evt) {
var v;
try {
v = XLSX.read(evt.data.d, {type: evt.data.b});
postMessage({t:"xlsx", d:JSON.stringify(v)});
} catch(e) { postMessage({t:"e",d:e.stack||e}); }
};
此差异已折叠。
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ /* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
module.exports = { module.exports = {
/* ensure that the XLSX variable is exported */
output: { output: {
libraryTarget: 'var', libraryTarget: 'var',
library: 'XLSX' library: 'XLSX'
...@@ -17,6 +18,7 @@ module.exports = { ...@@ -17,6 +18,7 @@ module.exports = {
alias: { "./dist/cpexcel.js": "" } alias: { "./dist/cpexcel.js": "" }
}, },
*/ */
/* suppress node shims */
node: { node: {
fs: false, fs: false,
process: false, process: false,
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册