diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c7d18a..e64bd85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ This log is intended to keep track of backwards-incompatible changes, including but not limited to API changes and file location changes. Minor behavioral changes may not be included if they are not expected to break existing code. -## Unreleased (2017-08-??) +## 0.11.3 (2017-08-19) * XLS cell ixfe/XF removed diff --git a/README.md b/README.md index 2387381..4443b31 100644 --- a/README.md +++ b/README.md @@ -167,24 +167,25 @@ CDNjs automatically pulls the latest version and makes all versions available at The `demos` directory includes sample projects for: -**Frameworks** +**JS Frameworks and APIs** - [`angular 1.x`](demos/angular/) - [`angular 2.x / 4.x`](demos/angular2/) - [`meteor`](demos/meteor/) -- [`vue 2`](demos/vue/) +- [`vue 2.x`](demos/vue/) +- [`XMLHttpRequest and fetch`](demos/xhr/) **JS Bundlers and Tooling** - [`browserify`](demos/browserify/) - [`requirejs`](demos/requirejs/) - [`rollup`](demos/rollup/) - [`systemjs`](demos/systemjs/) -- [`webpack`](demos/webpack/) +- [`webpack 2.x`](demos/webpack/) **JS Platforms and Integrations** - [`Adobe ExtendScript`](demos/extendscript/) - [`Headless Browsers`](demos/headless/) - [`canvas-datagrid`](demos/datagrid/) -- [`Other JS engines`](demos/altjs/) +- [`Swift JSC and other engines`](demos/altjs/) ### Optional Modules @@ -616,6 +617,29 @@ saveAs(new Blob([s2ab(wbout)],{type:"application/octet-stream"}), "test.xlsx"); ``` +
+ Browser upload to server (click to show) + +A complete example using XHR is [included in the xhr demo](demos/xhr/), along +with examples for fetch and wrapper libraries. This example assumes the server +can handle Base64-encoded files (see the demo for a basic nodejs server): + +```js +/* in this example, send a base64 string to the server */ +var wopts = { bookType:'xlsx', bookSST:false, type:'base64' }; + +var wbout = XLSX.write(workbook,wopts); + +var oReq = new XMLHttpRequest(); +oReq.open("POST", "/upload", true); +var formdata = new FormData(); +formdata.append('file', 'test.xlsx'); // <-- server expects `file` to hold name +formdata.append('data', wbout); // <-- `data` holds the base64-encoded data +oReq.send(formdata); +``` + +
+ ### Writing Examples - exporting an HTML table diff --git a/bits/23_binutils.js b/bits/23_binutils.js index d447940..f85f31a 100644 --- a/bits/23_binutils.js +++ b/bits/23_binutils.js @@ -14,8 +14,8 @@ function write_double_le(b, v/*:number*/, idx/*:number*/) { var av = bs ? -v : v; if(!isFinite(av)) { e = 0x7ff; m = isNaN(v) ? 0x6969 : 0; } else { - e = Math.floor(Math.log(av) * Math.LOG2E); - m = v * Math.pow(2, 52 - e); + e = Math.floor(Math.log(av) / Math.LN2); + m = av * Math.pow(2, 52 - e); if(e <= -1023 && (!isFinite(m) || m < Math.pow(2,52))) { e = -1022; } else { m -= Math.pow(2,52); e+=1023; } } diff --git a/demos/altjs/.gitignore b/demos/altjs/.gitignore index b3b2075..96be628 100644 --- a/demos/altjs/.gitignore +++ b/demos/altjs/.gitignore @@ -1,2 +1,6 @@ jvm-npm.js sheetjs.* +*.class +*.jar +rhino +xlsx.swift.js diff --git a/demos/altjs/Makefile b/demos/altjs/Makefile index f29feec..b4b1526 100644 --- a/demos/altjs/Makefile +++ b/demos/altjs/Makefile @@ -1,5 +1,5 @@ .PHONY: all -all: duktape nashorn +all: duktape nashorn rhinojs swift .PHONY: base base: @@ -13,3 +13,32 @@ duktape: base ## duktape / skookum demo nashorn: base ## nashorn demo if [ ! -e jvm-npm.js ]; then curl -O https://rawgit.com/nodyn/jvm-npm/master/src/main/javascript/jvm-npm.js; fi jjs nashorn.js + +.PHONY: swift +swift: base ## swift demo + if [ ! -e xlsx.swift.js ]; then cp ../../dist/xlsx.full.min.js xlsx.swift.js; fi + ./SheetJSCore.swift + +.PHONY: rhinojs ## rhino demo +rhinojs: base SheetJSRhino.class + java -cp .:SheetJS.jar:rhino.jar SheetJSRhino sheetjs.xlsx + java -cp .:SheetJS.jar:rhino.jar SheetJSRhino sheetjs.xlsb + java -cp .:SheetJS.jar:rhino.jar SheetJSRhino sheetjs.xls + java -cp .:SheetJS.jar:rhino.jar SheetJSRhino sheetjs.xml.xls + +RHDEPS=$(filter-out SheetJSRhino.class,$(patsubst %.java,%.class,$(wildcard com/sheetjs/*.java))) +$(RHDEPS): %.class: %.java rhino.jar + javac -cp .:SheetJS.jar:rhino.jar $*.java + +SheetJSRhino.class: $(RHDEPS) + jar -cf SheetJS.jar $^ ../../dist/xlsx.full.min.js + javac -cp .:SheetJS.jar:rhino.jar SheetJSRhino.java + +rhino.jar: + if [ ! -e rhino ]; then git clone https://github.com/mozilla/rhino; fi + if [ ! -e rhino/build/rhino*/js.jar ]; then cd rhino; ant jar; fi + cp rhino/build/rhino*/js.jar rhino.jar + +.PHONY: clean +clean: + rm SheetJS.jar *.class com/sheetjs/*.class diff --git a/demos/altjs/README.md b/demos/altjs/README.md index d26a8c0..ea7157d 100644 --- a/demos/altjs/README.md +++ b/demos/altjs/README.md @@ -6,6 +6,21 @@ optimize for low overhead and others optimize for ease of embedding within other applications. Since it was designed for ES3 engines, the library can be used in those settings! This demo tries to demonstrate a few alternative deployments. +Some engines provide no default global object. To create a global reference: + +```js +var global = (function(){ return this; }).call(null); +``` + + +## Swift + JavaScriptCore + +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 +passing is straightforward. + +Binary strings can be passed back and forth using `String.Encoding.ascii`. + ## Nashorn @@ -21,6 +36,20 @@ a byte array. To use in `XLSX.read`, the demo copies the bytes into a plain JS array and calls `XLSX.read` with type `"array"`. +## Rhino + +[Rhino](http://www.mozilla.org/rhino) is an ES3+ engine written in Java. The +`SheetJSRhino` class and `com.sheetjs` package show a complete JAR deployment, +including the full XLSX source. + +Due to code generation errors, optimization must be disabled: + +```java +Context context = Context.enter(); +context.setOptimizationLevel(-1); +``` + + ## duktape and skookum [Duktape](http://duktape.org/) is an embeddable JS engine written in C. The diff --git a/demos/altjs/SheetJSCore.swift b/demos/altjs/SheetJSCore.swift new file mode 100755 index 0000000..05b96bc --- /dev/null +++ b/demos/altjs/SheetJSCore.swift @@ -0,0 +1,62 @@ +#!/usr/bin/env xcrun swift + +import JavaScriptCore; + +class SheetJS { + var context: JSContext!; + var XLSX: JSValue!; + + enum SJSError: Error { + case badJSContext; + }; + + func init_context() throws -> JSContext { + var context: JSContext! + do { + context = JSContext(); + context.exceptionHandler = { ctx, X in if let e = X { print(e.toString()); }; } + var src = "var global = (function(){ return this; }).call(null);"; + context.evaluateScript(src); + src = try String(contentsOfFile: "xlsx.swift.js"); + context.evaluateScript(src); + if context != nil { return context!; } + } catch { print(error.localizedDescription); } + throw SheetJS.SJSError.badJSContext; + } + + func version() throws -> String { + if let version = XLSX.objectForKeyedSubscript("version") { return version.toString(); } + throw SheetJS.SJSError.badJSContext; + } + + func readFileToCSV(file: String) throws -> String { + let data:String! = try String(contentsOfFile: file, encoding:String.Encoding.ascii); + self.context.setObject(data, forKeyedSubscript:"payload" as (NSCopying & NSObjectProtocol)!); + + let src = [ + "var wb = XLSX.read(payload, {type:'binary'});", + "var ws = wb.Sheets[wb.SheetNames[0]];", + "var result = XLSX.utils.sheet_to_csv(ws);" + ].joined(separator: "\n"); + self.context.evaluateScript(src); + + return context.objectForKeyedSubscript("result").toString(); + } + + init() throws { + do { + self.context = try init_context(); + self.XLSX = context.objectForKeyedSubscript("XLSX"); + if self.XLSX == nil { + throw SheetJS.SJSError.badJSContext; + } + } catch { print(error.localizedDescription); } + } +} + +let sheetjs = try SheetJS(); +try print(sheetjs.version()); +try print(sheetjs.readFileToCSV(file:"sheetjs.xlsx")); +try print(sheetjs.readFileToCSV(file:"sheetjs.xlsb")); +try print(sheetjs.readFileToCSV(file:"sheetjs.xls")); +try print(sheetjs.readFileToCSV(file:"sheetjs.xml.xls")); diff --git a/demos/altjs/SheetJSRhino.java b/demos/altjs/SheetJSRhino.java new file mode 100644 index 0000000..0439054 --- /dev/null +++ b/demos/altjs/SheetJSRhino.java @@ -0,0 +1,31 @@ +/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ +/* vim: set ts=2: */ +import com.sheetjs.SheetJS; +import com.sheetjs.SheetJSFile; +import com.sheetjs.SheetJSSheet; + +public class SheetJSRhino { + public static void main(String args[]) throws Exception { + try { + SheetJS sjs = new SheetJS(); + + /* open file */ + SheetJSFile xl = sjs.read_file(args[0]); + + /* get sheetnames */ + String[] sheetnames = xl.get_sheet_names(); + System.err.println(sheetnames[0]); + + /* convert to CSV */ + SheetJSSheet sheet = xl.get_sheet(0); + String csv = sheet.get_csv(); + + System.out.println(csv); + + } catch(Exception e) { + throw e; + } finally { + SheetJS.close(); + } + } +} diff --git a/demos/altjs/com/sheetjs/JSHelper.java b/demos/altjs/com/sheetjs/JSHelper.java new file mode 100644 index 0000000..c5807ba --- /dev/null +++ b/demos/altjs/com/sheetjs/JSHelper.java @@ -0,0 +1,51 @@ +/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ +/* vim: set ts=2: */ +package com.sheetjs; + +import java.lang.Integer; +import java.lang.StringBuilder; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.NativeArray; +import org.mozilla.javascript.NativeObject; +import org.mozilla.javascript.Scriptable; + +public class JSHelper { + static String read_file(String file) throws IOException { + byte[] b = Files.readAllBytes(Paths.get(file)); + System.out.println(b.length); + StringBuilder sb = new StringBuilder(); + for(int i = 0; i < b.length; ++i) sb.append(Character.toString((char)b[i])); + return sb.toString(); + } + + static Object get_object(String path, Object base) throws ObjectNotFoundException { + int idx = path.indexOf("."); + Scriptable b = (Scriptable)base; + if(idx == -1) return b.get(path, b); + Object o = b.get(path.substring(0,idx), b); + if(o == Scriptable.NOT_FOUND) throw new ObjectNotFoundException("not found: |" + path.substring(0,idx) + "|" + Integer.toString(idx)); + return get_object(path.substring(idx+1), (NativeObject)o); + } + + static Object[] get_array(String path, Object base) throws ObjectNotFoundException { + NativeArray arr = (NativeArray)get_object(path, base); + Object[] out = new Object[(int)arr.getLength()]; + int idx; + for(Object o : arr.getIds()) out[idx = (Integer)o] = arr.get(idx, arr); + return out; + } + + static String[] get_string_array(String path, Object base) throws ObjectNotFoundException { + NativeArray arr = (NativeArray)get_object(path, base); + String[] out = new String[(int)arr.getLength()]; + int idx; + for(Object o : arr.getIds()) out[idx = (Integer)o] = arr.get(idx, arr).toString(); + return out; + } + + public static void close() { Context.exit(); } + +} diff --git a/demos/altjs/com/sheetjs/ObjectNotFoundException.java b/demos/altjs/com/sheetjs/ObjectNotFoundException.java new file mode 100644 index 0000000..7fc15c7 --- /dev/null +++ b/demos/altjs/com/sheetjs/ObjectNotFoundException.java @@ -0,0 +1,10 @@ +/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ +/* vim: set ts=2: */ +package com.sheetjs; + +import java.lang.Exception; + +public class ObjectNotFoundException extends Exception { + public ObjectNotFoundException() {} + public ObjectNotFoundException(String message) { super(message); } +} diff --git a/demos/altjs/com/sheetjs/SheetJS.java b/demos/altjs/com/sheetjs/SheetJS.java new file mode 100644 index 0000000..73d4f66 --- /dev/null +++ b/demos/altjs/com/sheetjs/SheetJS.java @@ -0,0 +1,58 @@ +/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ +/* vim: set ts=2: */ +package com.sheetjs; + +import java.lang.Integer; +import java.util.Scanner; +import java.io.IOException; +import java.io.File; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.Function; +import org.mozilla.javascript.NativeObject; +import org.mozilla.javascript.Scriptable; + +public class SheetJS { + public Scriptable scope; + public Context cx; + public NativeObject nXLSX; + + public SheetJS() throws Exception { + this.cx = Context.enter(); + this.scope = this.cx.initStandardObjects(); + + /* boilerplate */ + cx.setOptimizationLevel(-1); + String s = "var global = (function(){ return this; }).call(null);"; + cx.evaluateString(scope, s, "", 1, null); + + /* eval library */ + s = new Scanner(SheetJS.class.getResourceAsStream("/dist/xlsx.full.min.js")).useDelimiter("\\Z").next(); + //s = new Scanner(new File("xlsx.full.min.js")).useDelimiter("\\Z").next(); + cx.evaluateString(scope, s, "", 1, null); + + /* grab XLSX variable */ + Object XLSX = scope.get("XLSX", scope); + if(XLSX == Scriptable.NOT_FOUND) throw new Exception("XLSX not found"); + this.nXLSX = (NativeObject)XLSX; + } + + public SheetJSFile read_file(String filename) throws IOException, ObjectNotFoundException { + /* open file */ + String d = JSHelper.read_file(filename); + + /* options argument */ + NativeObject q = (NativeObject)this.cx.evaluateString(this.scope, "q = {'type':'binary'};", "", 2, null); + + /* set up function arguments */ + Object functionArgs[] = {d, q}; + + /* call read -> wb workbook */ + Function readfunc = (Function)JSHelper.get_object("XLSX.read",this.scope); + NativeObject wb = (NativeObject)readfunc.call(this.cx, this.scope, this.nXLSX, functionArgs); + + return new SheetJSFile(wb, this); + } + + public static void close() { JSHelper.close(); } +} + diff --git a/demos/altjs/com/sheetjs/SheetJSFile.java b/demos/altjs/com/sheetjs/SheetJSFile.java new file mode 100644 index 0000000..ee8f4dc --- /dev/null +++ b/demos/altjs/com/sheetjs/SheetJSFile.java @@ -0,0 +1,24 @@ +/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ +/* vim: set ts=2: */ +package com.sheetjs; + +import org.mozilla.javascript.NativeObject; +import org.mozilla.javascript.Function; + +public class SheetJSFile { + public NativeObject wb; + public SheetJS sheetjs; + public SheetJSFile() {} + public SheetJSFile(NativeObject wb, SheetJS sheetjs) { this.wb = wb; this.sheetjs = sheetjs; } + public String[] get_sheet_names() { + try { + return JSHelper.get_string_array("SheetNames", this.wb); + } catch(ObjectNotFoundException e) { + return null; + } + } + public SheetJSSheet get_sheet(int idx) throws ObjectNotFoundException { + return new SheetJSSheet(this, idx); + } +} + diff --git a/demos/altjs/com/sheetjs/SheetJSSheet.java b/demos/altjs/com/sheetjs/SheetJSSheet.java new file mode 100644 index 0000000..88a1cac --- /dev/null +++ b/demos/altjs/com/sheetjs/SheetJSSheet.java @@ -0,0 +1,29 @@ +/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ +/* vim: set ts=2: */ +package com.sheetjs; + +import org.mozilla.javascript.Function; +import org.mozilla.javascript.NativeObject; + +public class SheetJSSheet { + public NativeObject ws; + public SheetJSFile wb; + public SheetJSSheet(SheetJSFile wb, int idx) throws ObjectNotFoundException { + this.wb = wb; + this.ws = (NativeObject)JSHelper.get_object("Sheets." + wb.get_sheet_names()[idx],wb.wb); + } + public String get_range() throws ObjectNotFoundException { + return JSHelper.get_object("!ref",this.ws).toString(); + } + public String get_string_value(String address) throws ObjectNotFoundException { + return JSHelper.get_object(address + ".v",this.ws).toString(); + } + + public String get_csv() throws ObjectNotFoundException { + Function csvify = (Function)JSHelper.get_object("XLSX.utils.sheet_to_csv",this.wb.sheetjs.scope); + Object csvArgs[] = {this.ws}; + Object csv = csvify.call(this.wb.sheetjs.cx, this.wb.sheetjs.scope, this.wb.sheetjs.scope, csvArgs); + return csv.toString(); + } +} + diff --git a/demos/altjs/nashorn.js b/demos/altjs/nashorn.js index b8a7999..adb9119 100644 --- a/demos/altjs/nashorn.js +++ b/demos/altjs/nashorn.js @@ -1,4 +1,5 @@ #!/usr/bin/env jjs +/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ /* read file */ var path = java.nio.file.Paths.get('sheetjs.xlsx'); var fileArray = java.nio.file.Files.readAllBytes(path); diff --git a/demos/altjs/skookum.js b/demos/altjs/skookum.js index 0af560b..61c325a 100644 --- a/demos/altjs/skookum.js +++ b/demos/altjs/skookum.js @@ -1,4 +1,5 @@ #!/usr/bin/env sjs +/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ var XLSX = require('../../xlsx.js'); diff --git a/demos/browserify/main.js b/demos/browserify/main.js index 108e2f9..5bbe054 100644 --- a/demos/browserify/main.js +++ b/demos/browserify/main.js @@ -1,3 +1,4 @@ +/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ var XLSX = require('../../'); // test against development version //var XLSX = require('xlsx'); // use in production module.exports = XLSX; diff --git a/demos/headless/phantomjs.js b/demos/headless/phantomjs.js index fc9ae56..0b913a8 100644 --- a/demos/headless/phantomjs.js +++ b/demos/headless/phantomjs.js @@ -1,3 +1,4 @@ +/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ var fs = require('fs'); var xlsx = require('../../xlsx'); var page = require('webpage').create(); diff --git a/demos/headless/puppeteer.js b/demos/headless/puppeteer.js index de08646..87e0cb4 100644 --- a/demos/headless/puppeteer.js +++ b/demos/headless/puppeteer.js @@ -1,3 +1,4 @@ +/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ const puppeteer = require('puppeteer'); (async () => { diff --git a/demos/headless/slimerjs.js b/demos/headless/slimerjs.js index db47466..cee4e2e 100644 --- a/demos/headless/slimerjs.js +++ b/demos/headless/slimerjs.js @@ -1,3 +1,4 @@ +/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ var fs = require('fs'); var xlsx = require('../../dist/xlsx.full.min'); var page = require('webpage').create(); diff --git a/demos/meteor/client/main.js b/demos/meteor/client/main.js index f32b8e4..83bd300 100644 --- a/demos/meteor/client/main.js +++ b/demos/meteor/client/main.js @@ -1,3 +1,4 @@ +/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ import { Template } from 'meteor/templating'; import { ReactiveVar } from 'meteor/reactive-var'; diff --git a/demos/meteor/server/main.js b/demos/meteor/server/main.js index a4fa775..5fa37f8 100644 --- a/demos/meteor/server/main.js +++ b/demos/meteor/server/main.js @@ -1,3 +1,4 @@ +/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ import { Meteor } from 'meteor/meteor'; const XLSX = require('xlsx'); diff --git a/demos/requirejs/build.js b/demos/requirejs/build.js index a761296..30e66b5 100644 --- a/demos/requirejs/build.js +++ b/demos/requirejs/build.js @@ -1,3 +1,4 @@ +/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ ({ baseUrl: ".", name: "requirejs", diff --git a/demos/requirejs/requirejs.js b/demos/requirejs/requirejs.js index aca19c7..a06b459 100644 --- a/demos/requirejs/requirejs.js +++ b/demos/requirejs/requirejs.js @@ -1,3 +1,4 @@ +/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ require(["xlsx.full.min"], function(_XLSX) { var X = XLSX; diff --git a/demos/rollup/app.js b/demos/rollup/app.js index 50b968c..573da94 100644 --- a/demos/rollup/app.js +++ b/demos/rollup/app.js @@ -1,3 +1,4 @@ +/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ /*jshint browser:true */ /*global XLSX */ var X = XLSX; diff --git a/demos/systemjs/app.node.js b/demos/systemjs/app.node.js index 2737646..db2a0ef 100644 --- a/demos/systemjs/app.node.js +++ b/demos/systemjs/app.node.js @@ -1,3 +1,4 @@ +/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ var XLSX_1 = require('../../xlsx.js'); var XLSX_2 = require('../../dist/xlsx.core.min.js'); var XLSX_3 = require('../../dist/xlsx.full.min.js'); diff --git a/demos/systemjs/main.js b/demos/systemjs/main.js index f8dbfb9..3e4b011 100644 --- a/demos/systemjs/main.js +++ b/demos/systemjs/main.js @@ -1,3 +1,4 @@ +/* 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'}); diff --git a/demos/systemjs/systemjsnode.js b/demos/systemjs/systemjsnode.js index 4b68a8c..1dc0f2d 100644 --- a/demos/systemjs/systemjsnode.js +++ b/demos/systemjs/systemjsnode.js @@ -1,3 +1,4 @@ +/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ var SystemJS = require('systemjs'); SystemJS.config({ meta: { diff --git a/demos/vue/SheetJS-vue.js b/demos/vue/SheetJS-vue.js index 6b98a35..7a2fdb2 100644 --- a/demos/vue/SheetJS-vue.js +++ b/demos/vue/SheetJS-vue.js @@ -1,3 +1,4 @@ +/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ var 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(","); diff --git a/demos/webpack/app.js b/demos/webpack/app.js index 50b968c..573da94 100644 --- a/demos/webpack/app.js +++ b/demos/webpack/app.js @@ -1,3 +1,4 @@ +/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ /*jshint browser:true */ /*global XLSX */ var X = XLSX; diff --git a/demos/webpack/core.js b/demos/webpack/core.js index 2a702bc..39042f7 100644 --- a/demos/webpack/core.js +++ b/demos/webpack/core.js @@ -1,3 +1,4 @@ +/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ var XLSX = require('./xlsx.core.min'); console.log("it works!"); module.exports = XLSX; diff --git a/demos/webpack/full.js b/demos/webpack/full.js index 0d16a25..142687f 100644 --- a/demos/webpack/full.js +++ b/demos/webpack/full.js @@ -1,3 +1,4 @@ +/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ var XLSX = require('./xlsx.full.min'); console.log("it works!"); module.exports = XLSX; diff --git a/demos/webpack/main.js b/demos/webpack/main.js index 26a3237..57c9cf5 100644 --- a/demos/webpack/main.js +++ b/demos/webpack/main.js @@ -1,3 +1,4 @@ +/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ var XLSX = require('../../'); console.log("it works!"); module.exports = XLSX; diff --git a/demos/webpack/webpack.config.js b/demos/webpack/webpack.config.js index 987a948..30eb65d 100644 --- a/demos/webpack/webpack.config.js +++ b/demos/webpack/webpack.config.js @@ -1,3 +1,4 @@ +/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ module.exports = { output: { libraryTarget: 'var', diff --git a/demos/xhr/Makefile b/demos/xhr/Makefile new file mode 100644 index 0000000..06c7beb --- /dev/null +++ b/demos/xhr/Makefile @@ -0,0 +1,7 @@ +.PHONY: serve +serve: + npm start + +.PHONY: init +init: + if [ ! -e sheetjs.xlsx ]; then ln -s ../../sheetjs.xlsx; fi diff --git a/demos/xhr/README.md b/demos/xhr/README.md new file mode 100644 index 0000000..4dec917 --- /dev/null +++ b/demos/xhr/README.md @@ -0,0 +1,34 @@ +# XMLHttpRequest and Friends + +`XMLHttpRequest` and `fetch` browser APIs enable binary data transfer between +web browser clients and web servers. Since this library works in web browsers, +server conversion work can be offloaded to the client! This demo shows a few +common scenarios involving browser APIs and popular wrapper libraries. + +## Sample Server + +The `server.js` nodejs server serves static files on `GET` request. On a `POST` +request to `/upload`, the server processes the body and looks for the `file` and +`data` fields. It will write the Base64-decoded data from `data` to the file +name specified in `file`. + +To start the demo, run `npm start` and navigate to + +## XMLHttpRequest (xhr.html) + +For downloading data, the `arraybuffer` response type generates an `ArrayBuffer` +that can be viewed as an `Uint8Array` and fed to `XLSX.read` using `array` type. + +For uploading data, this demo populates a `FormData` object with string data +generated with the `base64` output type. + +## axios (axios.html) and superagent (superagent.html) + +The codes are structurally similar to the XMLHttpRequest example. `axios` uses +a Promise-based API while `superagent` opts for a more traditional chain. + +## fetch (fetch.html) + +For downloading data, `response.blob()` resolves to a `Blob` object that can be +converted to `ArrayBuffer` using a `FileReader`. + diff --git a/demos/xhr/axios.html b/demos/xhr/axios.html new file mode 100644 index 0000000..be89547 --- /dev/null +++ b/demos/xhr/axios.html @@ -0,0 +1,67 @@ + + + + + + +SheetJS Live Demo + + + +
+SheetJS Data Preview Live Demo
+
+Source Code Repo
+Issues?  Something look weird?  Click here and report an issue
+
+
+
+
+ +
+
+
+ + + + + + diff --git a/demos/xhr/fetch.html b/demos/xhr/fetch.html new file mode 100644 index 0000000..12228d4 --- /dev/null +++ b/demos/xhr/fetch.html @@ -0,0 +1,76 @@ + + + + + + +SheetJS Live Demo + + + +
+SheetJS Data Preview Live Demo
+
+Source Code Repo
+Issues?  Something look weird?  Click here and report an issue
+
+
+
+
+ +
+
+
+ + + + + diff --git a/demos/xhr/package.json b/demos/xhr/package.json new file mode 100644 index 0000000..749e533 --- /dev/null +++ b/demos/xhr/package.json @@ -0,0 +1,14 @@ +{ + "name": "sheetjs-xhr-demo", + "author": "sheetjs", + "version": "0.72.62", + "dependencies": { + "printj": "1.1.0", + "express": "4.15.4", + "express-formidable": "1.0.0", + "serve-index": "1.9.0" + }, + "scripts": { + "start": "node server.js 7262" + } +} diff --git a/demos/xhr/server.js b/demos/xhr/server.js new file mode 100644 index 0000000..819e0aa --- /dev/null +++ b/demos/xhr/server.js @@ -0,0 +1,34 @@ +#!/usr/bin/env node +/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ + +var fs = require('fs'), path = require('path'); +var express = require('express'), app = express(); +var sprintf = require('printj').sprintf; + +var port = +process.argv[2] || +process.env.PORT || 7262; +var basepath = process.cwd(); + +function doit(cb) { + return function(req, res, next) { + cb(req, res); + next(); + }; +} + +app.use(doit(function(req, res) { + console.log(sprintf("%s %s %d", req.method, req.url, res.statusCode)); +})); +app.use(doit(function(req, res) { + res.header('Access-Control-Allow-Origin', '*'); +})); +app.use(require('express-formidable')()); +app.post('/upload', function(req, res) { + fs.writeFile(req.fields.file, req.fields.data, 'base64', function(err, r) { + res.end("wrote to " + req.fields.file); + }); +}); +app.use(express.static(path.resolve(basepath))); +app.use(require('serve-index')(basepath, {'icons':true})); + +app.listen(port, function() { console.log('Serving HTTP on port ' + port); }); + diff --git a/demos/xhr/superagent.html b/demos/xhr/superagent.html new file mode 100644 index 0000000..1676d1d --- /dev/null +++ b/demos/xhr/superagent.html @@ -0,0 +1,70 @@ + + + + + + +SheetJS Live Demo + + + +
+SheetJS Data Preview Live Demo
+
+Source Code Repo
+Issues?  Something look weird?  Click here and report an issue
+
+
+
+
+ +
+
+
+ + + + + + diff --git a/demos/xhr/xhr.html b/demos/xhr/xhr.html new file mode 100644 index 0000000..d57e669 --- /dev/null +++ b/demos/xhr/xhr.html @@ -0,0 +1,77 @@ + + + + + + +SheetJS Live Demo + + + +
+SheetJS Data Preview Live Demo
+
+Source Code Repo
+Issues?  Something look weird?  Click here and report an issue
+
+
+
+
+ +
+
+
+ + + + + diff --git a/demos/xhr/xlsx.full.min.js b/demos/xhr/xlsx.full.min.js new file mode 120000 index 0000000..dbca48d --- /dev/null +++ b/demos/xhr/xlsx.full.min.js @@ -0,0 +1 @@ +../../dist/xlsx.full.min.js \ No newline at end of file diff --git a/docbits/10_install.md b/docbits/10_install.md index c29f17f..e04b09e 100644 --- a/docbits/10_install.md +++ b/docbits/10_install.md @@ -25,24 +25,25 @@ CDNjs automatically pulls the latest version and makes all versions available at The `demos` directory includes sample projects for: -**Frameworks** +**JS Frameworks and APIs** - [`angular 1.x`](demos/angular/) - [`angular 2.x / 4.x`](demos/angular2/) - [`meteor`](demos/meteor/) -- [`vue 2`](demos/vue/) +- [`vue 2.x`](demos/vue/) +- [`XMLHttpRequest and fetch`](demos/xhr/) **JS Bundlers and Tooling** - [`browserify`](demos/browserify/) - [`requirejs`](demos/requirejs/) - [`rollup`](demos/rollup/) - [`systemjs`](demos/systemjs/) -- [`webpack`](demos/webpack/) +- [`webpack 2.x`](demos/webpack/) **JS Platforms and Integrations** - [`Adobe ExtendScript`](demos/extendscript/) - [`Headless Browsers`](demos/headless/) - [`canvas-datagrid`](demos/datagrid/) -- [`Other JS engines`](demos/altjs/) +- [`Swift JSC and other engines`](demos/altjs/) ### Optional Modules diff --git a/docbits/30_export.md b/docbits/30_export.md index 0adeaf4..ea0fa7e 100644 --- a/docbits/30_export.md +++ b/docbits/30_export.md @@ -40,6 +40,29 @@ saveAs(new Blob([s2ab(wbout)],{type:"application/octet-stream"}), "test.xlsx"); ``` +
+ Browser upload to server (click to show) + +A complete example using XHR is [included in the xhr demo](demos/xhr/), along +with examples for fetch and wrapper libraries. This example assumes the server +can handle Base64-encoded files (see the demo for a basic nodejs server): + +```js +/* in this example, send a base64 string to the server */ +var wopts = { bookType:'xlsx', bookSST:false, type:'base64' }; + +var wbout = XLSX.write(workbook,wopts); + +var oReq = new XMLHttpRequest(); +oReq.open("POST", "/upload", true); +var formdata = new FormData(); +formdata.append('file', 'test.xlsx'); // <-- server expects `file` to hold name +formdata.append('data', wbout); // <-- `data` holds the base64-encoded data +oReq.send(formdata); +``` + +
+ ### Writing Examples - exporting an HTML table diff --git a/misc/docs/README.md b/misc/docs/README.md index 5f77294..2ffea32 100644 --- a/misc/docs/README.md +++ b/misc/docs/README.md @@ -161,24 +161,25 @@ CDNjs automatically pulls the latest version and makes all versions available at The `demos` directory includes sample projects for: -**Frameworks** +**JS Frameworks and APIs** - [`angular 1.x`](demos/angular/) - [`angular 2.x / 4.x`](demos/angular2/) - [`meteor`](demos/meteor/) -- [`vue 2`](demos/vue/) +- [`vue 2.x`](demos/vue/) +- [`XMLHttpRequest and fetch`](demos/xhr/) **JS Bundlers and Tooling** - [`browserify`](demos/browserify/) - [`requirejs`](demos/requirejs/) - [`rollup`](demos/rollup/) - [`systemjs`](demos/systemjs/) -- [`webpack`](demos/webpack/) +- [`webpack 2.x`](demos/webpack/) **JS Platforms and Integrations** - [`Adobe ExtendScript`](demos/extendscript/) - [`Headless Browsers`](demos/headless/) - [`canvas-datagrid`](demos/datagrid/) -- [`Other JS engines`](demos/altjs/) +- [`Swift JSC and other engines`](demos/altjs/) ### Optional Modules @@ -565,6 +566,26 @@ function s2ab(s) { saveAs(new Blob([s2ab(wbout)],{type:"application/octet-stream"}), "test.xlsx"); ``` + +A complete example using XHR is [included in the xhr demo](demos/xhr/), along +with examples for fetch and wrapper libraries. This example assumes the server +can handle Base64-encoded files (see the demo for a basic nodejs server): + +```js +/* in this example, send a base64 string to the server */ +var wopts = { bookType:'xlsx', bookSST:false, type:'base64' }; + +var wbout = XLSX.write(workbook,wopts); + +var oReq = new XMLHttpRequest(); +oReq.open("POST", "/upload", true); +var formdata = new FormData(); +formdata.append('file', 'test.xlsx'); // <-- server expects `file` to hold name +formdata.append('data', wbout); // <-- `data` holds the base64-encoded data +oReq.send(formdata); +``` + + ### Writing Examples - exporting an HTML table diff --git a/xlsx.flow.js b/xlsx.flow.js index a6a2c27..5352f48 100644 --- a/xlsx.flow.js +++ b/xlsx.flow.js @@ -1434,7 +1434,7 @@ var ENDOFCHAIN = -2; var HEADER_SIGNATURE = 'd0cf11e0a1b11ae1'; var HEADER_CLSID = '00000000000000000000000000000000'; var consts = { - /* 2.1 Compund File Sector Numbers and Types */ + /* 2.1 Compound File Sector Numbers and Types */ MAXREGSECT: -6, DIFSECT: -4, FATSECT: -3, @@ -1907,8 +1907,8 @@ function write_double_le(b, v/*:number*/, idx/*:number*/) { var av = bs ? -v : v; if(!isFinite(av)) { e = 0x7ff; m = isNaN(v) ? 0x6969 : 0; } else { - e = Math.floor(Math.log(av) * Math.LOG2E); - m = v * Math.pow(2, 52 - e); + e = Math.floor(Math.log(av) / Math.LN2); + m = av * Math.pow(2, 52 - e); if(e <= -1023 && (!isFinite(m) || m < Math.pow(2,52))) { e = -1022; } else { m -= Math.pow(2,52); e+=1023; } } @@ -5423,7 +5423,7 @@ function dbf_to_aoa(buf, opts)/*:AOA*/ { case 'L': switch(s.toUpperCase()) { case 'Y': case 'T': out[R][C] = true; break; case 'N': case 'F': out[R][C] = false; break; - case ' ': case '?': out[R][C] = false; break; /* NOTE: technically unitialized */ + case ' ': case '?': out[R][C] = false; break; /* NOTE: technically uninitialized */ default: throw new Error("DBF Unrecognized L:|" + s + "|"); } break; case 'M': /* TODO: handle memo files */ @@ -6658,7 +6658,7 @@ function parse_EncryptionInfo(blob, length/*:?number*/) { case 0x03: return parse_EncInfoExt(blob, vers); case 0x04: return parse_EncInfoAgl(blob, vers); } - throw new Error("ECMA-376 Encryped file unrecognized Version: " + vers.Minor); + throw new Error("ECMA-376 Encrypted file unrecognized Version: " + vers.Minor); } /* [MS-OFFCRYPTO] 2.3.4.5 EncryptionInfo Stream (Standard Encryption) */ diff --git a/xlsx.js b/xlsx.js index ab8f7f1..f9a0fa4 100644 --- a/xlsx.js +++ b/xlsx.js @@ -1372,7 +1372,7 @@ var ENDOFCHAIN = -2; var HEADER_SIGNATURE = 'd0cf11e0a1b11ae1'; var HEADER_CLSID = '00000000000000000000000000000000'; var consts = { - /* 2.1 Compund File Sector Numbers and Types */ + /* 2.1 Compound File Sector Numbers and Types */ MAXREGSECT: -6, DIFSECT: -4, FATSECT: -3, @@ -1843,8 +1843,8 @@ function write_double_le(b, v, idx) { var av = bs ? -v : v; if(!isFinite(av)) { e = 0x7ff; m = isNaN(v) ? 0x6969 : 0; } else { - e = Math.floor(Math.log(av) * Math.LOG2E); - m = v * Math.pow(2, 52 - e); + e = Math.floor(Math.log(av) / Math.LN2); + m = av * Math.pow(2, 52 - e); if(e <= -1023 && (!isFinite(m) || m < Math.pow(2,52))) { e = -1022; } else { m -= Math.pow(2,52); e+=1023; } } @@ -5349,7 +5349,7 @@ function dbf_to_aoa(buf, opts) { case 'L': switch(s.toUpperCase()) { case 'Y': case 'T': out[R][C] = true; break; case 'N': case 'F': out[R][C] = false; break; - case ' ': case '?': out[R][C] = false; break; /* NOTE: technically unitialized */ + case ' ': case '?': out[R][C] = false; break; /* NOTE: technically uninitialized */ default: throw new Error("DBF Unrecognized L:|" + s + "|"); } break; case 'M': /* TODO: handle memo files */ @@ -6583,7 +6583,7 @@ function parse_EncryptionInfo(blob, length) { case 0x03: return parse_EncInfoExt(blob, vers); case 0x04: return parse_EncInfoAgl(blob, vers); } - throw new Error("ECMA-376 Encryped file unrecognized Version: " + vers.Minor); + throw new Error("ECMA-376 Encrypted file unrecognized Version: " + vers.Minor); } /* [MS-OFFCRYPTO] 2.3.4.5 EncryptionInfo Stream (Standard Encryption) */