diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c7d18ae..e64bd85a 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 2387381a..4443b318 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 d4479406..f85f31af 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 b3b20753..96be6289 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 f29feec7..b4b1526d 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 d26a8c0b..ea7157dc 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 00000000..05b96bcc --- /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 00000000..04390548 --- /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 00000000..c5807baf --- /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 00000000..7fc15c79 --- /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 00000000..73d4f662 --- /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 00000000..ee8f4dcf --- /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 00000000..88a1cac9 --- /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 b8a79990..adb91193 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 0af560b6..61c325a7 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 108e2f9e..5bbe0540 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 fc9ae565..0b913a8c 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 de086460..87e0cb4d 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 db474660..cee4e2e7 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 f32b8e44..83bd3006 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 a4fa7753..5fa37f84 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 a761296d..30e66b54 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 aca19c74..a06b4592 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 50b968c4..573da944 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 27376467..db2a0efb 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 f8dbfb96..3e4b0112 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 4b68a8ce..1dc0f2db 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 6b98a352..7a2fdb25 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 50b968c4..573da944 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 2a702bcc..39042f7c 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 0d16a25a..142687f0 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 26a32374..57c9cf5d 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 987a9489..30eb65d2 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 00000000..06c7bebe --- /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 00000000..4dec9170 --- /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 00000000..be895479 --- /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 00000000..12228d43 --- /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 00000000..749e5331 --- /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 00000000..819e0aa7 --- /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 00000000..1676d1dd --- /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 00000000..d57e6691 --- /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 00000000..dbca48df --- /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 c29f17f3..e04b09e5 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 0adeaf45..ea0fa7e8 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 5f772947..2ffea324 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 a6a2c277..5352f484 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 ab8f7f11..f9a0fa48 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) */