version bump 0.12.5: ancillary utility update

- add BOM to `stream.to_csv` (fixes #1024 h/t @hr5959)
- `utils.format_cell` type (h/t @victorj2307)
- duktape niggles
- demo cleanup
This commit is contained in:
SheetJS 2018-03-12 22:51:54 -04:00
parent 73a5e50f21
commit 5de62a947f
66 changed files with 681 additions and 355 deletions

View File

@ -106,6 +106,7 @@ ui-grid
angular-cli
- demos/database/README.md
Knex
LowDB
MariaDB
MySQL

View File

@ -162,6 +162,7 @@ In the browser, just add a script tag:
| `unpkg` | <https://unpkg.com/xlsx/> |
| `jsDelivr` | <https://jsdelivr.com/package/npm/xlsx> |
| `CDNjs` | <http://cdnjs.com/libraries/xlsx> |
| `packd` | <https://bundle.run/xlsx?name=XLSX> |
`unpkg` makes the latest version available at:

View File

@ -1 +1 @@
XLSX.version = '0.12.4';
XLSX.version = '0.12.5';

View File

@ -14,7 +14,8 @@ function write_comments_vml(rId, comments) {
];
while(_shapeid < rId * 1000) _shapeid += 1000;
comments.map(function(x) { return decode_cell(x[0]); }).forEach(function(c) { o = o.concat([
comments.forEach(function(x) { var c = decode_cell(x[0]);
o = o.concat([
'<v:shape' + wxt_helper({
id:'_x0000_s' + (++_shapeid),
type:"#_x0000_t202",

View File

@ -36,13 +36,11 @@ function write_comments_xml(data/*::, opts*/) {
var iauthor/*:Array<string>*/ = [];
o.push("<authors>");
data.map(function(x) { return x[1]; }).forEach(function(comment) {
comment.map(function(x) { return escapexml(x.a); }).forEach(function(a) {
if(iauthor.indexOf(a) > -1) return;
iauthor.push(a);
o.push("<author>" + a + "</author>");
});
});
data.forEach(function(x) { x[1].forEach(function(w) { var a = escapexml(w.a);
if(iauthor.indexOf(a) > -1) return;
iauthor.push(a);
o.push("<author>" + a + "</author>");
}); });
o.push("</authors>");
o.push("<commentList>");
data.forEach(function(d) {

View File

@ -163,6 +163,8 @@ function xlml_clean_comment(comment/*:any*/) {
function xlml_normalize(d)/*:string*/ {
if(has_buf &&/*::typeof Buffer !== "undefined" && d != null && d instanceof Buffer &&*/ Buffer.isBuffer(d)) return d.toString('utf8');
if(typeof d === 'string') return d;
/* duktape */
if(typeof Uint8Array !== 'undefined' && d instanceof Uint8Array) return utf8read(a2s(ab2a(d)));
throw new Error("Bad input format: expected Buffer or string");
}

View File

@ -15,7 +15,9 @@ if(has_buf && typeof require != 'undefined') (function() {
var rowinfo/*:Array<RowInfo>*/ = o.skipHidden && sheet["!rows"] || [];
for(var C = r.s.c; C <= r.e.c; ++C) if (!((colinfo[C]||{}).hidden)) cols[C] = encode_col(C);
var R = r.s.r;
var BOM = false;
stream._read = function() {
if(!BOM) { BOM = true; return stream.push("\uFEFF"); }
if(R > r.e.r) return stream.push(null);
while(R <= r.e.r) {
++R;

View File

@ -4,5 +4,6 @@ duk*
*.class
*.jar
rhino
shim.min.js
xlsx.*.js
payload.js

View File

@ -5,12 +5,16 @@ all: duktape nashorn rhinojs swift
base:
if [ ! -e sheetjs.xlsx ]; then node ../../tests/write.js; fi
if [ ! -e xlsx.full.min.js ]; then cp ../../dist/xlsx.full.min.js .; fi
if [ ! -e shim.min.js ]; then cp ../../dist/shim.min.js .; fi
.PHONY: duktape
duktape: base ## duktape demo
.PHONY: duk
duk: base
bash ./duktape.sh
gcc -std=c99 -Wall -osheetjs.duk sheetjs.duk.c duktape.c -lm
./sheetjs.duk
.PHONY: duktape
duktape: duk ## duktape demo
for ext in xlsx xlsb biff8.xls xml.xls; do ./sheetjs.duk sheetjs.$$ext; done
.PHONY: nashorn
nashorn: base ## nashorn demo

View File

@ -2,95 +2,95 @@
import JavaScriptCore;
enum SJSError: Error {
case badJSContext;
case badJSWorkbook;
case badJSWorksheet;
case badJSContext;
case badJSWorkbook;
case badJSWorksheet;
};
class SJSWorksheet {
var context: JSContext!;
var wb: JSValue!; var ws: JSValue!;
var idx: Int32;
var context: JSContext!;
var wb: JSValue!; var ws: JSValue!;
var idx: Int32;
func toCSV() throws -> String {
let XLSX: JSValue! = context.objectForKeyedSubscript("XLSX");
let utils: JSValue! = XLSX.objectForKeyedSubscript("utils");
let sheet_to_csv: JSValue! = utils.objectForKeyedSubscript("sheet_to_csv");
return sheet_to_csv.call(withArguments: [ws]).toString();
}
func toCSV() throws -> String {
let XLSX: JSValue! = context.objectForKeyedSubscript("XLSX");
let utils: JSValue! = XLSX.objectForKeyedSubscript("utils");
let sheet_to_csv: JSValue! = utils.objectForKeyedSubscript("sheet_to_csv");
return sheet_to_csv.call(withArguments: [ws]).toString();
}
init(ctx: JSContext, workbook: JSValue, worksheet: JSValue, idx: Int32) throws {
self.context = ctx; self.wb = workbook; self.ws = worksheet; self.idx = idx;
}
init(ctx: JSContext, workbook: JSValue, worksheet: JSValue, idx: Int32) throws {
self.context = ctx; self.wb = workbook; self.ws = worksheet; self.idx = idx;
}
}
class SJSWorkbook {
var context: JSContext!;
var wb: JSValue!; var SheetNames: JSValue!; var Sheets: JSValue!;
var context: JSContext!;
var wb: JSValue!; var SheetNames: JSValue!; var Sheets: JSValue!;
func getSheetAtIndex(idx: Int32) throws -> SJSWorksheet {
let SheetName: String = SheetNames.atIndex(Int(idx)).toString();
let ws: JSValue! = Sheets.objectForKeyedSubscript(SheetName);
return try SJSWorksheet(ctx: context, workbook: wb, worksheet: ws, idx: idx);
}
func getSheetAtIndex(idx: Int32) throws -> SJSWorksheet {
let SheetName: String = SheetNames.atIndex(Int(idx)).toString();
let ws: JSValue! = Sheets.objectForKeyedSubscript(SheetName);
return try SJSWorksheet(ctx: context, workbook: wb, worksheet: ws, idx: idx);
}
func writeBStr(bookType: String = "xlsx") throws -> String {
let XLSX: JSValue! = context.objectForKeyedSubscript("XLSX");
context.evaluateScript(String(format: "var writeopts = {type:'binary', bookType:'%@'}", bookType));
let writeopts: JSValue! = context.objectForKeyedSubscript("writeopts");
let writefunc: JSValue! = XLSX.objectForKeyedSubscript("write");
return writefunc.call(withArguments: [wb, writeopts]).toString();
}
func writeBStr(bookType: String = "xlsx") throws -> String {
let XLSX: JSValue! = context.objectForKeyedSubscript("XLSX");
context.evaluateScript(String(format: "var writeopts = {type:'binary', bookType:'%@'}", bookType));
let writeopts: JSValue! = context.objectForKeyedSubscript("writeopts");
let writefunc: JSValue! = XLSX.objectForKeyedSubscript("write");
return writefunc.call(withArguments: [wb, writeopts]).toString();
}
init(ctx: JSContext, wb: JSValue) throws {
self.context = ctx;
self.wb = wb;
self.SheetNames = wb.objectForKeyedSubscript("SheetNames");
self.Sheets = wb.objectForKeyedSubscript("Sheets");
}
init(ctx: JSContext, wb: JSValue) throws {
self.context = ctx;
self.wb = wb;
self.SheetNames = wb.objectForKeyedSubscript("SheetNames");
self.Sheets = wb.objectForKeyedSubscript("Sheets");
}
}
class SheetJSCore {
var context: JSContext!;
var XLSX: JSValue!;
var context: JSContext!;
var XLSX: JSValue!;
func init_context() throws -> JSContext {
var context: JSContext!
do {
context = JSContext();
context.exceptionHandler = { ctx, X in if let e = X { print(e.toString()); }; };
context.evaluateScript("var global = (function(){ return this; }).call(null);");
context.evaluateScript("if(typeof wbs == 'undefined') wbs = [];");
let src = try String(contentsOfFile: "xlsx.full.min.js");
context.evaluateScript(src);
if context != nil { return context!; }
} catch { print(error.localizedDescription); }
throw SJSError.badJSContext;
}
func init_context() throws -> JSContext {
var context: JSContext!
do {
context = JSContext();
context.exceptionHandler = { ctx, X in if let e = X { print(e.toString()); }; };
context.evaluateScript("var global = (function(){ return this; }).call(null);");
context.evaluateScript("if(typeof wbs == 'undefined') wbs = [];");
let src = try String(contentsOfFile: "xlsx.full.min.js");
context.evaluateScript(src);
if context != nil { return context!; }
} catch { print(error.localizedDescription); }
throw SJSError.badJSContext;
}
func version() throws -> String {
if let version = XLSX.objectForKeyedSubscript("version") { return version.toString(); }
throw SJSError.badJSContext;
}
func version() throws -> String {
if let version = XLSX.objectForKeyedSubscript("version") { return version.toString(); }
throw SJSError.badJSContext;
}
func readFile(file: String) throws -> SJSWorkbook {
let data: String! = try String(contentsOfFile: file, encoding: String.Encoding.isoLatin1);
return try readBStr(data: data);
}
func readFile(file: String) throws -> SJSWorkbook {
let data: String! = try String(contentsOfFile: file, encoding: String.Encoding.isoLatin1);
return try readBStr(data: data);
}
func readBStr(data: String) throws -> SJSWorkbook {
context.setObject(data, forKeyedSubscript: "payload" as (NSCopying & NSObjectProtocol)!);
context.evaluateScript("var wb = XLSX.read(payload, {type:'binary'});");
let wb: JSValue! = context.objectForKeyedSubscript("wb");
if wb == nil { throw SJSError.badJSWorkbook; }
return try SJSWorkbook(ctx: context, wb: wb);
}
func readBStr(data: String) throws -> SJSWorkbook {
context.setObject(data, forKeyedSubscript: "payload" as (NSCopying & NSObjectProtocol)!);
context.evaluateScript("var wb = XLSX.read(payload, {type:'binary'});");
let wb: JSValue! = context.objectForKeyedSubscript("wb");
if wb == nil { throw SJSError.badJSWorkbook; }
return try SJSWorkbook(ctx: context, wb: wb);
}
init() throws {
do {
self.context = try init_context();
self.XLSX = self.context.objectForKeyedSubscript("XLSX");
if self.XLSX == nil { throw SJSError.badJSContext; }
} catch { print(error.localizedDescription); }
}
init() throws {
do {
self.context = try init_context();
self.XLSX = self.context.objectForKeyedSubscript("XLSX");
if self.XLSX == nil { throw SJSError.badJSContext; }
} catch { print(error.localizedDescription); }
}
}

View File

@ -5,27 +5,27 @@ import com.sheetjs.SheetJSFile;
import com.sheetjs.SheetJSSheet;
public class SheetJSRhino {
public static void main(String args[]) throws Exception {
try {
SheetJS sjs = new SheetJS();
public static void main(String args[]) throws Exception {
try {
SheetJS sjs = new SheetJS();
/* open file */
SheetJSFile xl = sjs.read_file(args[0]);
/* open file */
SheetJSFile xl = sjs.read_file(args[0]);
/* get sheetnames */
String[] sheetnames = xl.get_sheet_names();
System.err.println(sheetnames[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();
/* convert to CSV */
SheetJSSheet sheet = xl.get_sheet(0);
String csv = sheet.get_csv();
System.out.println(csv);
System.out.println(csv);
} catch(Exception e) {
throw e;
} finally {
SheetJS.close();
}
}
} catch(Exception e) {
throw e;
} finally {
SheetJS.close();
}
}
}

View File

@ -13,39 +13,39 @@ 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] < 0 ? b[i] + 256 : b[i])));
return sb.toString();
}
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] < 0 ? b[i] + 256 : 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_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 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(); }
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(); }
}

View File

@ -5,6 +5,6 @@ package com.sheetjs;
import java.lang.Exception;
public class ObjectNotFoundException extends Exception {
public ObjectNotFoundException() {}
public ObjectNotFoundException(String message) { super(message); }
public ObjectNotFoundException() {}
public ObjectNotFoundException(String message) { super(message); }
}

View File

@ -12,47 +12,47 @@ import org.mozilla.javascript.NativeObject;
import org.mozilla.javascript.Scriptable;
public class SheetJS {
public Scriptable scope;
public Context cx;
public NativeObject nXLSX;
public Scriptable scope;
public Context cx;
public NativeObject nXLSX;
public SheetJS() throws Exception {
this.cx = Context.enter();
this.scope = this.cx.initStandardObjects();
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, "<cmd>", 1, null);
/* boilerplate */
cx.setOptimizationLevel(-1);
String s = "var global = (function(){ return this; }).call(null);";
cx.evaluateString(scope, s, "<cmd>", 1, null);
/* eval library */
s = new Scanner(SheetJS.class.getResourceAsStream("/xlsx.full.min.js")).useDelimiter("\\Z").next();
//s = new Scanner(new File("xlsx.full.min.js")).useDelimiter("\\Z").next();
cx.evaluateString(scope, s, "<cmd>", 1, null);
/* eval library */
s = new Scanner(SheetJS.class.getResourceAsStream("/xlsx.full.min.js")).useDelimiter("\\Z").next();
//s = new Scanner(new File("xlsx.full.min.js")).useDelimiter("\\Z").next();
cx.evaluateString(scope, s, "<cmd>", 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;
}
/* 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);
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', 'WTF':1};", "<cmd>", 2, null);
/* options argument */
NativeObject q = (NativeObject)this.cx.evaluateString(this.scope, "q = {'type':'binary', 'WTF':1};", "<cmd>", 2, null);
/* set up function arguments */
Object args[] = {d, q};
/* set up function arguments */
Object args[] = {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, args);
/* 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, args);
return new SheetJSFile(wb, this);
}
return new SheetJSFile(wb, this);
}
public static void close() { JSHelper.close(); }
public static void close() { JSHelper.close(); }
}

View File

@ -6,19 +6,19 @@ 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);
}
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);
}
}

View File

@ -6,24 +6,24 @@ 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 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();
}
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();
}
}

View File

@ -5,17 +5,17 @@ let sheetjs = try SheetJSCore();
try print(sheetjs.version());
let filenames: [[String]] = [
["xlsx", "xlsx"],
["xlsb", "xlsb"],
["biff8.xls", "xls"],
["xml.xls", "xlml"]
["xlsx", "xlsx"],
["xlsb", "xlsb"],
["biff8.xls", "xls"],
["xml.xls", "xlml"]
];
for fn in filenames {
let wb: SJSWorkbook = try sheetjs.readFile(file: "sheetjs." + fn[0]);
let ws: SJSWorksheet = try wb.getSheetAtIndex(idx: 0);
let csv: String = try ws.toCSV();
print(csv);
let wbout: String = try wb.writeBStr(bookType: fn[1]);
try wbout.write(toFile: "sheetjsswift." + fn[0], atomically: false, encoding: String.Encoding.isoLatin1);
let wb: SJSWorkbook = try sheetjs.readFile(file: "sheetjs." + fn[0]);
let ws: SJSWorksheet = try wb.getSheetAtIndex(idx: 0);
let csv: String = try ws.toCSV();
print(csv);
let wbout: String = try wb.writeBStr(bookType: fn[1]);
try wbout.write(toFile: "sheetjsswift." + fn[0], atomically: false, encoding: String.Encoding.isoLatin1);
}

View File

@ -66,6 +66,8 @@ int main(int argc, char *argv[]) {
DOIT("var global = (function(){ return this; }).call(null);");
/* load library */
res = eval_file(ctx, "shim.min.js");
if(res != 0) FAIL("shim load")
res = eval_file(ctx, "xlsx.full.min.js");
if(res != 0) FAIL("library load")
@ -75,9 +77,9 @@ int main(int argc, char *argv[]) {
duk_pop(ctx);
/* read file */
#define INFILE "sheetjs.xlsx"
res = load_file(ctx, INFILE, "buf");
if(res != 0) FAIL("load " INFILE)
res = load_file(ctx, argv[1], "buf");
if(res != 0) FAIL("file load")
printf("Loaded file %s\n", argv[1]);
/* parse workbook */
DOIT("wb = XLSX.read(buf, {type:'buffer', cellNF:true});");

View File

@ -1,7 +1,7 @@
{
"env": { "shared-node-browser":true },
"parserOptions": {
"ecmaVersion": 2017
"ecmaVersion": 8
},
"plugins": [ "html", "json" ]
}

View File

@ -0,0 +1,73 @@
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
/* eslint-env node */
/* global Promise */
const XLSX = require('xlsx');
const assert = require('assert');
const SheetJSKnex = require("./SheetJSKnex");
const Knex = require('knex');
/* Connection to both databases are passed around */
let P = Promise.resolve([
Knex({ client: 'sqlite3', connection: { filename: './knex1.db' } }),
Knex({ client: 'sqlite3', connection: { filename: './knex2.db' } })
]);
/* Sample data table */
P = P.then(async (_) => {
const [knex] = _;
await knex.schema.dropTableIfExists('pres');
await knex.schema.createTable('pres', (table) => {
table.string('name');
table.integer('idx');
});
await knex.insert([
{ name: "Barack Obama", idx: 44 },
{ name: "Donald Trump", idx: 45 }
]).into('pres');
await knex.schema.dropTableIfExists('fmts');
await knex.schema.createTable('fmts', (table) => {
table.string('ext');
table.string('ctr');
table.integer('multi');
});
await knex.insert([
{ ext: 'XLSB', ctr: 'ZIP', multi: 1 },
{ ext: 'XLS', ctr: 'CFB', multi: 1 },
{ ext: 'XLML', ctr: '', multi: 1 },
{ ext: 'CSV', ctr: 'ZIP', multi: 0 },
]).into('fmts');
return _;
});
/* Export database to XLSX */
P = P.then(async (_) => {
const [knex] = _;
const wb = XLSX.utils.book_new();
await SheetJSKnex.book_append_knex(wb, knex, "pres");
await SheetJSKnex.book_append_knex(wb, knex, "fmts");
XLSX.writeFile(wb, "knex.xlsx");
return _;
});
/* Import XLSX to database */
P = P.then(async (_) => {
const [, knex] = _;
const wb = XLSX.readFile("knex.xlsx");
await SheetJSKnex.wb_to_knex(wb, knex);
return _;
});
/* Compare databases */
P = P.then(async (_) => {
const [k1, k2] = _;
const P1 = await k1.select("*").from('pres');
const P2 = await k2.select("*").from('pres');
const F1 = await k1.select("*").from('fmts');
const F2 = await k2.select("*").from('fmts');
assert.deepEqual(P1, P2);
assert.deepEqual(F1, F2);
});
P.then(async () => { process.exit(); });

View File

@ -27,7 +27,7 @@ var init = [
];
(async () => {
const conn1 = await mysql.createConnection({...opts, database: "sheetjs"});
const conn1 = await mysql.createConnection(Object.assign({}, opts, {database: "sheetjs"}));
for(var i = 0; i < init.length; ++i) await conn1.query(init[i]);
/* Export table to XLSX */
@ -50,7 +50,7 @@ var init = [
await conn1.close();
/* Import XLSX to table */
const conn2 = await mysql.createConnection({...opts, database: "sheetj5"});
const conn2 = await mysql.createConnection(Object.assign({}, opts, {database: "sheetj5"}));
var wb2 = XLSX.readFile("mysql.xlsx");
var queries = SheetJSSQL.book_to_sql(wb2, "MYSQL");
for(i = 0; i < queries.length; ++i) await conn2.query(queries[i]);

View File

@ -26,8 +26,8 @@ var init = [
"INSERT INTO fmts VALUES ('CSV', '', 0)",
];
var conn1 = new Client({...opts, database: "sheetjs"});
var conn2 = new Client({...opts, database: "sheetj5"});
var conn1 = new Client(Object.assign({}, opts, {database: "sheetjs"}));
var conn2 = new Client(Object.assign({}, opts, {database: "sheetj5"}));
(async () => {
await conn1.connect();
for(var i = 0; i < init.length; ++i) await conn1.query(init[i]);

View File

@ -49,8 +49,12 @@ scanned to determine the column "types", and there are third-party connectors
that can push arrays of JS objects to database tables.
The [`sexql`](http://sheetjs.com/sexql) browser demo uses WebSQL, which is
limited to the SQLite fundamental types. Its schema builder scans the first row
to find headers:
limited to the SQLite fundamental types.
<details>
<summary><b>Implementation details</b> (click to show)</summary>
The `sexql` schema builder scans the first row to find headers:
```js
if(!ws || !ws['!ref']) return;
@ -99,8 +103,11 @@ value of a column, the column is marked as `TEXT`:
}
```
</details>
The included `SheetJSSQL.js` script demonstrates SQL statement generation.
## Objects, K/V and "Schema-less" Databases
So-called "Schema-less" databases allow for arbitrary keys and values within the
@ -137,6 +144,9 @@ XXX| A | B |
The included `ObjUtils.js` script demonstrates object-workbook conversion:
<details>
<summary><b>Implementation details</b> (click to show)</summary>
```js
function deepset(obj, path, value) {
if(path.indexOf(".") == -1) return obj[path] = value;
@ -185,6 +195,8 @@ function object_to_workbook(obj) {
}
```
</details>
## Browser APIs
@ -229,7 +241,7 @@ differences surround API shape and supported data types.
[The `better-sqlite3` module](https://www.npmjs.com/package/better-sqlite3)
provides a very simple API for working with SQLite databases. `Statement#all`
runs a prepared statement and returns an array of JS objects
runs a prepared statement and returns an array of JS objects.
`SQLiteTest.js` generates a simple two-table SQLite database (`SheetJS1.db`),
exports to XLSX (`sqlite.xlsx`), imports the new XLSX file to a new database
@ -255,6 +267,15 @@ The `rows` key of the object is an array of JS objects.
tables in the `sheetjs` database, exports to XLSX, imports the new XLSX file to
the `sheetj5` database and verifies the tables are preserved.
#### Knex Query Builder
[The `knex` module](https://www.npmjs.com/package/knex) builds SQL queries. The
same exact code can be used against Oracle Database, MSSQL, and other engines.
`KnexTest.js` uses the `sqlite3` connector and follows the same procedure as the
SQLite test. The included `SheetJSKnex.js` script converts between the query
builder and the common spreadsheet format.
### Key/Value Stores
#### Redis
@ -267,7 +288,7 @@ strings in a special worksheet (`_strs`), the manifest in another worksheet
`RedisTest.js` connects to a local Redis server, populates data based on the
official Redis tutorial, exports to XLSX, flushes the server, imports the new
XLSX file and verifies the data round-tripped correctly. `SheetJSRedis.js`
includes the implementation details
includes the implementation details.
#### LowDB

View File

@ -21,7 +21,7 @@ var init = [
];
db1.exec(init.join(";"));
/* Export table to XLSX */
/* Export database to XLSX */
var wb = XLSX.utils.book_new();
function book_append_table(wb, db, name) {
var r = db.prepare('SELECT * FROM ' + name).all();
@ -32,7 +32,7 @@ book_append_table(wb, db1, "pres");
book_append_table(wb, db1, "fmts");
XLSX.writeFile(wb, "sqlite.xlsx");
/* Import XLSX to table */
/* Import XLSX to database */
var db2 = new Database('SheetJS2.db');
var wb2 = XLSX.readFile("sqlite.xlsx");
var queries = SheetJSSQL.book_to_sql(wb2, "SQLITE");
@ -46,7 +46,6 @@ var F2 = db2.prepare("SELECT * FROM fmts").all();
assert.deepEqual(P1, P2);
assert.deepEqual(F1, F2);
/* Display results */
console.log(P2);
console.log(F2);

View File

@ -0,0 +1,78 @@
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
/* eslint-env node */
var XLSX = require("xlsx");
async function book_append_knex(wb, knex, tbl) {
const aoo = await knex.select("*").from(tbl);
XLSX.utils.book_append_sheet(wb, XLSX.utils.json_to_sheet(aoo), tbl);
}
const TYPES = {
b: "boolean",
n: "float",
t: "string",
d: "dateTime"
};
async function ws_to_knex(ws, knex, n) {
if(!ws || !ws['!ref']) return;
var range = XLSX.utils.decode_range(ws['!ref']);
if(!range || !range.s || !range.e || range.s > range.e) return;
var R = range.s.r, C = range.s.c;
var names = new Array(range.e.c-range.s.c+1);
for(C = range.s.c; C<= range.e.c; ++C){
var addr = XLSX.utils.encode_cell({c:C,r:R});
names[C-range.s.c] = ws[addr] ? ws[addr].v : XLSX.utils.encode_col(C);
}
for(var i = 0; i < names.length; ++i) if(names.indexOf(names[i]) < i)
for(var j = 0; j < names.length; ++j) {
var _name = names[i] + "_" + (j+1);
if(names.indexOf(_name) > -1) continue;
names[i] = _name;
}
var types = new Array(range.e.c-range.s.c+1);
for(C = range.s.c; C<= range.e.c; ++C) {
var seen = {}, _type = "";
for(R = range.s.r+1; R<= range.e.r; ++R)
seen[(ws[XLSX.utils.encode_cell({c:C,r:R})]||{t:"z"}).t] = true;
if(seen.s || seen.str) _type = TYPES.t;
else if(seen.n + seen.b + seen.d + seen.e > 1) _type = TYPES.t;
else switch(true) {
case seen.b: _type = TYPES.b; break;
case seen.n: _type = TYPES.n; break;
case seen.e: _type = TYPES.t; break;
case seen.d: _type = TYPES.d; break;
}
types[C-range.s.c] = _type || TYPES.t;
}
await knex.schema.dropTableIfExists(n);
await knex.schema.createTable(n, (table) => { names.forEach((n, i) => { table[types[i] || "text"](n); }); });
for(R = range.s.r+1; R<= range.e.r; ++R) {
var row = {};
for(C = range.s.c; C<= range.e.c; ++C) {
var cell = ws[XLSX.utils.encode_cell({c:C,r:R})];
if(!cell) continue;
var key = names[C-range.s.c], val = cell.v;
if(types[C-range.s.c] == TYPES.n) if(cell.t == 'b' || typeof val == 'boolean' ) val = +val;
row[key] = val;
}
await knex.insert(row).into(n);;
}
}
async function wb_to_knex(wb, knex) {
for(var i = 0; i < wb.SheetNames.length; ++i) {
var n = wb.SheetNames[i];
var ws = wb.Sheets[n];
await ws_to_knex(ws, knex, n);
}
}
module.exports = {
book_append_knex,
wb_to_knex
};

View File

@ -67,6 +67,7 @@ async function wb_to_redis(wb, R) {
await aoa_to_redis[M[i].type](aoa, R, M[i].key);
}
}
module.exports = {
redis_to_wb,
wb_to_redis

View File

@ -1,4 +1,5 @@
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
/* global XLSX, require, module */
var SheetJSSQL = (function() {
var X;
@ -51,7 +52,7 @@ function sheet_to_sql(ws, sname, mode) {
var BT = mode == "PGSQL" ? "" : "`";
var Q = mode == "PGSQL" ? "'" : '"';
var R = mode == "PGSQL" ? /'/g : /"/g;
var J = mode == "PGSQL" ? /'/g : /"/g;
out.push("DROP TABLE IF EXISTS " + BT + sname + BT );
out.push("CREATE TABLE " + BT + sname + BT + " (" + names.map(function(n, i) { return BT + n + BT + " " + (types[i]||"TEXT"); }).join(", ") + ");" );
@ -64,7 +65,7 @@ function sheet_to_sql(ws, sname, mode) {
var val = cell.v;
switch(types[C-range.s.c]) {
case TYPES.n: if(cell.t == 'b' || typeof val == 'boolean' ) val = +val; break;
default: val = Q + val.toString().replace(R, Q + Q) + Q;
default: val = Q + val.toString().replace(J, Q + Q) + Q;
}
values.push(val);
}

View File

@ -1,14 +1,36 @@
<!DOCTYPE html>
<!-- xlsx.js (C) 2013-present SheetJS http://sheetjs.com -->
<!-- vim: set ts=2: -->
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>SheetJS FuseBox Test</title>
<style>
a { text-decoration: none }
</style>
</head>
<body>
<script type="text/javascript" src="/client.js"></script>
<pre>
<b><a href="http://sheetjs.com">SheetJS FuseBox Demo</a></b>
<a href="https://github.com/SheetJS/js-xlsx">Source Code Repo</a>
<a href="https://github.com/SheetJS/js-xlsx/issues">Issues? Something look weird? Click here and report an issue</a>
Original script: <a href="sheetjs.ts">sheetjs.ts</a>
<b>Console Output:</b>
</pre>
<pre id="console"></pre>
<b>
<script>
if(typeof console !== "undefined") console = {};
console.__log = console.log || function(){};
console.log = function(x) {
console.__log.apply(console, arguments);
document.getElementById('console').innerText += x + "\n";
};
console.error = console.debug = console.info = console.log
</script>
<script type="text/javascript" src="/client.js"></script>
</body>
</html>
</html>

View File

@ -114,6 +114,8 @@ Downloadify.create(element_id, {
});
```
#### Upload
The demo also includes an HTML file input element for updating the data table:
```js

View File

@ -15,7 +15,7 @@ native: ## Build react-native project
.PHONY: ios
ios: native ## react-native ios sim
cd SheetJS; react-native run-ios; cd -
cd SheetJS; react-native run-ios --simulator="iPhone X"; cd -
.PHONY: android
android: native ## react-native android sim

View File

@ -77,8 +77,7 @@ Reproducing the full project is straightforward:
react-native init SheetJS
cd SheetJS
npm i -S xlsx react react-native react-native-table-component react-native-fs
cp ../react-native.js index.ios.js
cp ../react-native.js index.android.js
cp ../react-native.js index.js
react-native link
```
@ -107,6 +106,9 @@ const wbout = XLSX.write(wb, {type:'binary', bookType:"xlsx"});
writeFile(file, wbout, 'ascii').then((r)=>{/* :) */}).catch((e)=>{/* :( */});
```
Note: for real app deployments, the `UIFileSharingEnabled` flag must be manually
set in the iOS project `Info.plist` file.
## Other Demos
#### Preact

View File

@ -1,7 +1,7 @@
#!/bin/bash
# xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
if [ ! -e SheetJS ]; then
react-native init SheetJS
react-native init --version="0.53.3" SheetJS
cd SheetJS
npm i -S xlsx react-native-table-component react-native-fs
cd -

View File

@ -2,8 +2,8 @@
import XLSX from 'xlsx';
import React, { Component } from 'react';
import { AppRegistry, StyleSheet, Text, View, Button, Alert, Image } from 'react-native';
import { Table, Row, Rows } from 'react-native-table-component';
import { AppRegistry, StyleSheet, Text, View, Button, Alert, Image, ScrollView, TouchableWithoutFeedback } from 'react-native';
import { Table, Row, Rows, TableWrapper } from 'react-native-table-component';
// react-native-fs
import { writeFile, readFile, DocumentDirectoryPath } from 'react-native-fs';
@ -21,12 +21,14 @@ 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_width = refstr => Array.from({length: XLSX.utils.decode_range(refstr).e.c + 1}, () => 60);
export default class SheetJS extends Component {
constructor(props) {
super(props);
this.state = {
data: [[1,2,3],[4,5,6]],
widthArr: [60, 60, 60],
cols: make_cols("A1:C2")
};
this.importFile = this.importFile.bind(this);
@ -46,7 +48,7 @@ export default class SheetJS extends Component {
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']), widthArr: make_width(ws['!ref']) });
}).catch((err) => { Alert.alert("importFile Error", "Error " + err.message); });
}}
]);
@ -67,7 +69,8 @@ export default class SheetJS extends Component {
}).catch((err) => { Alert.alert("exportFile Error", "Error " + err.message); });
};
render() { return (
<View style={styles.container}>
<ScrollView contentContainerStyle={styles.container} vertical={true}>
<Text style={styles.welcome}> </Text>
<Image style={{width: 128, height: 128}} source={require('./logo.png')} />
<Text style={styles.welcome}>SheetJS React Native Demo</Text>
<Text style={styles.instructions}>Import Data</Text>
@ -76,11 +79,22 @@ export default class SheetJS extends Component {
<Button disabled={!this.state.data.length} onPress={this.exportFile} title="Export data to XLSX" color="#841584" />
<Text style={styles.instructions}>Current Data</Text>
<Table style={styles.table}>
<Row data={this.state.cols} style={styles.thead} textStyle={styles.text}/>
<Rows data={this.state.data} style={styles.tr} textStyle={styles.text}/>
</Table>
</View>
<ScrollView style={styles.table} horizontal={true} >
<Table style={styles.table}>
<TableWrapper>
<Row data={this.state.cols} style={styles.thead} textStyle={styles.text} widthArr={this.state.widthArr}/>
</TableWrapper>
<TouchableWithoutFeedback>
<ScrollView vertical={true}>
<TableWrapper>
<Rows data={this.state.data} style={styles.tr} textStyle={styles.text} widthArr={this.state.widthArr}/>
</TableWrapper>
</ScrollView>
</TouchableWithoutFeedback>
</Table>
</ScrollView>
</ScrollView>
); };
};

View File

@ -1,4 +1,7 @@
.PHONY: test
.PHONY: test ctest
test:
cp ../../dist/xlsx.full.min.js .
node test.node.js