forked from sheetjs/sheetjs
demo refresh [ci skip]
This commit is contained in:
parent
c2ec7555fb
commit
edf7150ca8
17
.spelling
17
.spelling
@ -37,13 +37,19 @@ CommonJS
|
||||
Ethercalc
|
||||
ExtendScript
|
||||
FileSaver
|
||||
IndexedDB
|
||||
JavaScriptCore
|
||||
LocalStorage
|
||||
NPM
|
||||
Nuxt.js
|
||||
Redis
|
||||
RequireJS
|
||||
Rollup
|
||||
SessionStorage
|
||||
SQLite
|
||||
SystemJS
|
||||
VueJS
|
||||
WebSQL
|
||||
iOS
|
||||
nodejs
|
||||
npm
|
||||
@ -57,10 +63,12 @@ ArrayBuffer
|
||||
Base64
|
||||
Booleans
|
||||
JS
|
||||
NoSQL
|
||||
README
|
||||
UTF-16
|
||||
XHR
|
||||
XMLHttpRequest
|
||||
bundler
|
||||
bundlers
|
||||
cleanroom
|
||||
config
|
||||
@ -95,6 +103,15 @@ ui-grid
|
||||
- demos/angular2/README.md
|
||||
angular-cli
|
||||
|
||||
- demos/database/README.md
|
||||
LowDB
|
||||
MariaDB
|
||||
MySQL
|
||||
PostgreSQL
|
||||
schemaless
|
||||
schemas
|
||||
storages
|
||||
|
||||
- demos/extendscript/README.md
|
||||
Photoshop
|
||||
minifier
|
||||
|
@ -196,9 +196,12 @@ The [`demos` directory](demos/) includes sample projects for:
|
||||
- [`vue 2.x and weex`](demos/vue/)
|
||||
- [`XMLHttpRequest and fetch`](demos/xhr/)
|
||||
- [`nodejs server`](demos/server/)
|
||||
- [`databases and key/value stores`](demos/database/)
|
||||
|
||||
**Bundlers and Tooling**
|
||||
- [`browserify`](demos/browserify/)
|
||||
- [`fusebox`](demos/fusebox/)
|
||||
- [`parcel`](demos/parcel/)
|
||||
- [`requirejs`](demos/requirejs/)
|
||||
- [`rollup`](demos/rollup/)
|
||||
- [`systemjs`](demos/systemjs/)
|
||||
|
@ -25,9 +25,12 @@ can be installed with Bash on Windows or with `cygwin`.
|
||||
- [`vue 2.x and weex`](vue/)
|
||||
- [`XMLHttpRequest and fetch`](xhr/)
|
||||
- [`nodejs server`](server/)
|
||||
- [`databases and key/value stores`](database/)
|
||||
|
||||
**Bundlers and Tooling**
|
||||
- [`browserify`](browserify/)
|
||||
- [`fusebox`](fusebox/)
|
||||
- [`parcel`](parcel/)
|
||||
- [`requirejs`](requirejs/)
|
||||
- [`rollup`](rollup/)
|
||||
- [`systemjs`](systemjs/)
|
||||
|
7
demos/database/.eslintrc
Normal file
7
demos/database/.eslintrc
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"env": { "shared-node-browser":true },
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2017
|
||||
},
|
||||
"plugins": [ "html", "json" ]
|
||||
}
|
1
demos/database/.gitignore
vendored
Normal file
1
demos/database/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.db
|
59
demos/database/LocalForage.html
Normal file
59
demos/database/LocalForage.html
Normal file
@ -0,0 +1,59 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- xlsx.js (C) 2013-present SheetJS http://sheetjs.com -->
|
||||
<!-- vim: set ts=2: -->
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>SheetJS Live Demo</title>
|
||||
<style>
|
||||
a { text-decoration: none }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<pre>
|
||||
<b><a href="http://sheetjs.com">SheetJS LocalStorage Demo</a></b>
|
||||
<pre id="data_">
|
||||
Original Data:
|
||||
</pre>
|
||||
<pre id="out">
|
||||
Output:
|
||||
|
||||
</pre>
|
||||
<script src="xlsx.full.min.js"></script>
|
||||
<script src="ObjUtils.js"></script>
|
||||
<script src="https://unpkg.com/localforage/dist/localforage.min.js"></script>
|
||||
<script src="SheetJSForage.js"></script>
|
||||
<script>
|
||||
/* eslint-env browser */
|
||||
/*global XLSX, localforage */
|
||||
var data = {
|
||||
"title": "SheetDB",
|
||||
"metadata": {
|
||||
"author": "SheetJS",
|
||||
"code": 7262
|
||||
},
|
||||
"data": [
|
||||
{ "Name": "Barack Obama", "Index": 44 },
|
||||
{ "Name": "Donald Trump", "Index": 45 },
|
||||
]
|
||||
};
|
||||
document.getElementById("data_").innerText += JSON.stringify(data, 2, 2);
|
||||
|
||||
localforage.setDriver(localforage.INDEXEDDB);
|
||||
(async function() {
|
||||
await localforage.clear();
|
||||
|
||||
await localforage.load(data);
|
||||
var wb = await localforage.dump();
|
||||
console.log(wb);
|
||||
|
||||
var OUT = document.getElementById("out");
|
||||
wb.SheetNames.forEach(function(n, i) {
|
||||
OUT.innerText += "Sheet " + i + " (" + n + ")\n";
|
||||
OUT.innerText += XLSX.utils.sheet_to_csv(wb.Sheets[n]);
|
||||
OUT.innerText += "\n";
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
57
demos/database/LocalStorage.html
Normal file
57
demos/database/LocalStorage.html
Normal file
@ -0,0 +1,57 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- xlsx.js (C) 2013-present SheetJS http://sheetjs.com -->
|
||||
<!-- vim: set ts=2: -->
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>SheetJS Live Demo</title>
|
||||
<style>
|
||||
a { text-decoration: none }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<pre>
|
||||
<b><a href="http://sheetjs.com">SheetJS LocalStorage Demo</a></b>
|
||||
<pre id="data_">
|
||||
Original Data:
|
||||
</pre>
|
||||
<pre id="out">
|
||||
Output:
|
||||
|
||||
</pre>
|
||||
<script src="xlsx.full.min.js"></script>
|
||||
<script src="ObjUtils.js"></script>
|
||||
<script src="SheetJSStorage.js"></script>
|
||||
<script>
|
||||
/* eslint-env browser */
|
||||
/*global XLSX */
|
||||
var data = {
|
||||
"title": "SheetDB",
|
||||
"metadata": {
|
||||
"author": "SheetJS",
|
||||
"code": 7262
|
||||
},
|
||||
"data": [
|
||||
{ "Name": "Barack Obama", "Index": 44 },
|
||||
{ "Name": "Donald Trump", "Index": 45 },
|
||||
]
|
||||
};
|
||||
document.getElementById("data_").innerText += JSON.stringify(data, 2, 2);
|
||||
|
||||
(function() {
|
||||
localStorage.clear();
|
||||
|
||||
localStorage.load(data);
|
||||
var wb = localStorage.dump();
|
||||
console.log(wb);
|
||||
|
||||
var OUT = document.getElementById("out");
|
||||
wb.SheetNames.forEach(function(n, i) {
|
||||
OUT.innerText += "Sheet " + i + " (" + n + ")\n";
|
||||
OUT.innerText += XLSX.utils.sheet_to_csv(wb.Sheets[n]);
|
||||
OUT.innerText += "\n";
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
23
demos/database/LowDBTest.js
Normal file
23
demos/database/LowDBTest.js
Normal file
@ -0,0 +1,23 @@
|
||||
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
/* eslint-env node */
|
||||
var low = require('lowdb');
|
||||
var SheetJSAdapter = require('./SheetJSLowDB');
|
||||
var adapter = new SheetJSAdapter();
|
||||
var db = low(adapter);
|
||||
|
||||
db.defaults({ posts: [], user: {}, count: 0 }).write();
|
||||
db.get('posts').push({ id: 1, title: 'lowdb is awesome'}).write();
|
||||
db.set('user.name', 'typicode').write();
|
||||
db.update('count', function(n) { return n + 1; }).write();
|
||||
|
||||
adapter.dumpFile('ldb1.xlsx');
|
||||
|
||||
var adapter2 = new SheetJSAdapter();
|
||||
adapter2.loadFile('ldb1.xlsx');
|
||||
var db2 = low(adapter2);
|
||||
|
||||
db2.get('posts').push({ id: 2, title: 'mongodb is not'}).write();
|
||||
db2.set('user.name', 'sheetjs').write();
|
||||
db2.update('count', function(n) { return n + 1; }).write();
|
||||
|
||||
adapter2.dumpFile('ldb2.xlsx');
|
16
demos/database/Makefile
Normal file
16
demos/database/Makefile
Normal file
@ -0,0 +1,16 @@
|
||||
.PHONY: init
|
||||
init:
|
||||
rm -f node_modules/xlsx
|
||||
mkdir -p node_modules
|
||||
cd node_modules; ln -s ../../../ xlsx; cd -
|
||||
rm -f xlsx.full.min.js
|
||||
ln -s ../../dist/xlsx.full.min.js
|
||||
|
||||
FILES=$(filter-out xlsx.full.min.js,$(wildcard *.js)) $(wildcard *.html)
|
||||
.PHONY: lint
|
||||
lint: $(FILES)
|
||||
eslint $(FILES)
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f *.db *.xlsx
|
70
demos/database/MySQLTest.js
Normal file
70
demos/database/MySQLTest.js
Normal file
@ -0,0 +1,70 @@
|
||||
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
/* eslint-env node */
|
||||
var XLSX = require('xlsx');
|
||||
var assert = require('assert');
|
||||
var SheetJSSQL = require('./SheetJSSQL');
|
||||
var mysql = require('mysql2/promise');
|
||||
|
||||
/* Connection options (requires two databases sheetjs and sheetj5) */
|
||||
var opts = {
|
||||
host : 'localhost',
|
||||
user : 'SheetJS',
|
||||
password : 'SheetJS',
|
||||
};
|
||||
|
||||
/* Sample data table */
|
||||
var init = [
|
||||
"DROP TABLE IF EXISTS pres",
|
||||
"CREATE TABLE pres (name TEXT, idx TINYINT)",
|
||||
"INSERT INTO pres VALUES ('Barack Obama', 44)",
|
||||
"INSERT INTO pres VALUES ('Donald Trump', 45)",
|
||||
"DROP TABLE IF EXISTS fmts",
|
||||
"CREATE TABLE fmts (ext TEXT, ctr TEXT, multi TINYINT)",
|
||||
"INSERT INTO fmts VALUES ('XLSB', 'ZIP', 1)",
|
||||
"INSERT INTO fmts VALUES ('XLS', 'CFB', 1)",
|
||||
"INSERT INTO fmts VALUES ('XLML', '', 1)",
|
||||
"INSERT INTO fmts VALUES ('CSV', '', 0)",
|
||||
];
|
||||
|
||||
(async () => {
|
||||
const conn1 = await mysql.createConnection({...opts, database: "sheetjs"});
|
||||
for(var i = 0; i < init.length; ++i) await conn1.query(init[i]);
|
||||
|
||||
/* Export table to XLSX */
|
||||
var wb = XLSX.utils.book_new();
|
||||
|
||||
async function book_append_table(wb, name) {
|
||||
var r_f = await conn1.query('SELECT * FROM ' + name);
|
||||
var r = r_f[0];
|
||||
var ws = XLSX.utils.json_to_sheet(r);
|
||||
XLSX.utils.book_append_sheet(wb, ws, name);
|
||||
}
|
||||
|
||||
await book_append_table(wb, "pres");
|
||||
await book_append_table(wb, "fmts");
|
||||
XLSX.writeFile(wb, "mysql.xlsx");
|
||||
|
||||
/* Capture first database info and close */
|
||||
var P1 = (await conn1.query("SELECT * FROM pres"))[0];
|
||||
var F1 = (await conn1.query("SELECT * FROM fmts"))[0];
|
||||
await conn1.close();
|
||||
|
||||
/* Import XLSX to table */
|
||||
const conn2 = await mysql.createConnection({...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]);
|
||||
|
||||
/* Capture first database info and close */
|
||||
var P2 = (await conn2.query("SELECT * FROM pres"))[0];
|
||||
var F2 = (await conn2.query("SELECT * FROM fmts"))[0];
|
||||
await conn2.close();
|
||||
|
||||
/* Compare results */
|
||||
assert.deepEqual(P1, P2);
|
||||
assert.deepEqual(F1, F2);
|
||||
|
||||
/* Display results */
|
||||
console.log(P2);
|
||||
console.log(F2);
|
||||
})();
|
59
demos/database/ObjUtils.js
Normal file
59
demos/database/ObjUtils.js
Normal file
@ -0,0 +1,59 @@
|
||||
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
/*global XLSX, module, require */
|
||||
var ObjUtils = (function() {
|
||||
|
||||
var X;
|
||||
if(typeof XLSX !== "undefined") X = XLSX;
|
||||
else if(typeof require !== 'undefined') X = require('xlsx');
|
||||
else throw new Error("Could not find XLSX");
|
||||
|
||||
function walk(obj, key, arr) {
|
||||
if(Array.isArray(obj)) return;
|
||||
if(typeof obj != "object" || obj instanceof Date) { arr.push({path:key, value:obj}); return; }
|
||||
Object.keys(obj).forEach(function(k) {
|
||||
walk(obj[k], key ? key + "." + k : k, arr);
|
||||
});
|
||||
}
|
||||
|
||||
function object_to_workbook(obj) {
|
||||
var wb = X.utils.book_new();
|
||||
|
||||
var base = []; walk(obj, "", base);
|
||||
var ws = X.utils.json_to_sheet(base, {header:["path", "value"]});
|
||||
X.utils.book_append_sheet(wb, ws, "_keys");
|
||||
|
||||
Object.keys(obj).forEach(function(k) {
|
||||
if(!Array.isArray(obj[k])) return;
|
||||
X.utils.book_append_sheet(wb, X.utils.json_to_sheet(obj[k]), k);
|
||||
});
|
||||
|
||||
return wb;
|
||||
}
|
||||
|
||||
function deepset(obj, path, value) {
|
||||
if(path.indexOf(".") == -1) return obj[path] = value;
|
||||
var parts = path.split(".");
|
||||
if(!obj[parts[0]]) obj[parts[0]] = {};
|
||||
return deepset(obj[parts[0]], parts.slice(1).join("."), value);
|
||||
}
|
||||
function workbook_set_object(obj, wb) {
|
||||
var ws = wb.Sheets["_keys"]; if(ws) {
|
||||
var data = X.utils.sheet_to_json(ws, {raw:true});
|
||||
data.forEach(function(r) { deepset(obj, r.path, r.value); });
|
||||
}
|
||||
wb.SheetNames.forEach(function(n) {
|
||||
if(n == "_keys") return;
|
||||
obj[n] = X.utils.sheet_to_json(wb.Sheets[n], {raw:true});
|
||||
});
|
||||
}
|
||||
|
||||
function workbook_to_object(wb) { var obj = {}; workbook_set_object(obj, wb); return obj; }
|
||||
|
||||
return {
|
||||
workbook_to_object: workbook_to_object,
|
||||
object_to_workbook: object_to_workbook,
|
||||
workbook_set_object: workbook_set_object
|
||||
};
|
||||
})();
|
||||
|
||||
if(typeof module !== 'undefined') module.exports = ObjUtils;
|
72
demos/database/PgSQLTest.js
Normal file
72
demos/database/PgSQLTest.js
Normal file
@ -0,0 +1,72 @@
|
||||
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
/* eslint-env node */
|
||||
var XLSX = require('xlsx');
|
||||
var assert = require('assert');
|
||||
var SheetJSSQL = require('./SheetJSSQL');
|
||||
var Client = require('pg').Client;
|
||||
|
||||
/* Connection options (requires two databases sheetjs and sheetj5) */
|
||||
var opts = {
|
||||
host : 'localhost',
|
||||
user : 'SheetJS',
|
||||
password : 'SheetJS',
|
||||
};
|
||||
|
||||
/* Sample data table */
|
||||
var init = [
|
||||
"DROP TABLE IF EXISTS pres",
|
||||
"CREATE TABLE pres (name text, idx smallint)",
|
||||
"INSERT INTO pres VALUES ('Barack Obama', 44)",
|
||||
"INSERT INTO pres VALUES ('Donald Trump', 45)",
|
||||
"DROP TABLE IF EXISTS fmts",
|
||||
"CREATE TABLE fmts (ext text, ctr text, multi smallint)",
|
||||
"INSERT INTO fmts VALUES ('XLSB', 'ZIP', 1)",
|
||||
"INSERT INTO fmts VALUES ('XLS', 'CFB', 1)",
|
||||
"INSERT INTO fmts VALUES ('XLML', '', 1)",
|
||||
"INSERT INTO fmts VALUES ('CSV', '', 0)",
|
||||
];
|
||||
|
||||
var conn1 = new Client({...opts, database: "sheetjs"});
|
||||
var conn2 = new Client({...opts, database: "sheetj5"});
|
||||
(async () => {
|
||||
await conn1.connect();
|
||||
for(var i = 0; i < init.length; ++i) await conn1.query(init[i]);
|
||||
|
||||
/* Export table to XLSX */
|
||||
var wb = XLSX.utils.book_new();
|
||||
|
||||
async function book_append_table(wb, name) {
|
||||
var r_f = await conn1.query('SELECT * FROM ' + name);
|
||||
var r = r_f.rows;
|
||||
var ws = XLSX.utils.json_to_sheet(r);
|
||||
XLSX.utils.book_append_sheet(wb, ws, name);
|
||||
}
|
||||
|
||||
await book_append_table(wb, "pres");
|
||||
await book_append_table(wb, "fmts");
|
||||
XLSX.writeFile(wb, "pgsql.xlsx");
|
||||
|
||||
/* Capture first database info and close */
|
||||
var P1 = (await conn1.query("SELECT * FROM pres")).rows;
|
||||
var F1 = (await conn1.query("SELECT * FROM fmts")).rows;
|
||||
await conn1.end();
|
||||
|
||||
/* Import XLSX to table */
|
||||
await conn2.connect();
|
||||
var wb2 = XLSX.readFile("pgsql.xlsx");
|
||||
var queries = SheetJSSQL.book_to_sql(wb2, "PGSQL");
|
||||
for(i = 0; i < queries.length; ++i) { console.log(queries[i]); await conn2.query(queries[i]); }
|
||||
|
||||
/* Capture first database info and close */
|
||||
var P2 = (await conn2.query("SELECT * FROM pres")).rows;
|
||||
var F2 = (await conn2.query("SELECT * FROM fmts")).rows;
|
||||
await conn2.end();
|
||||
|
||||
/* Compare results */
|
||||
assert.deepEqual(P1, P2);
|
||||
assert.deepEqual(F1, F2);
|
||||
|
||||
/* Display results */
|
||||
console.log(P2);
|
||||
console.log(F2);
|
||||
})();
|
279
demos/database/README.md
Normal file
279
demos/database/README.md
Normal file
@ -0,0 +1,279 @@
|
||||
# Databases
|
||||
|
||||
"Database" is a catch-all term referring to traditional RDBMS as well as K/V
|
||||
stores, document databases, and other "NoSQL" storages. There are many external
|
||||
database systems as well as browser APIs like WebSQL and `localStorage`
|
||||
|
||||
This demo discusses general strategies and provides examples for a variety of
|
||||
database systems. The examples are merely intended to demonstrate very basic
|
||||
functionality.
|
||||
|
||||
|
||||
## Structured Tables
|
||||
|
||||
Database tables are a common import and export target for spreadsheets. One
|
||||
common representation of a database table is an array of JS objects whose keys
|
||||
are column headers and whose values are the underlying data values. For example,
|
||||
|
||||
| Name | Index |
|
||||
| :----------- | ----: |
|
||||
| Barack Obama | 44 |
|
||||
| Donald Trump | 45 |
|
||||
|
||||
is naturally represented as an array of objects
|
||||
|
||||
```js
|
||||
[
|
||||
{ Name: "Barack Obama", Index: 44 },
|
||||
{ Name: "Donald Trump", Index: 45 }
|
||||
]
|
||||
```
|
||||
|
||||
The `sheet_to_json` and `json_to_sheet` helper functions work with objects of
|
||||
similar shape, converting to and from worksheet objects. The corresponding
|
||||
worksheet would include a header row for the labels:
|
||||
|
||||
```
|
||||
XXX| A | B |
|
||||
---+--------------+-------+
|
||||
1 | Name | Index |
|
||||
2 | Barack Obama | 44 |
|
||||
3 | Donald Trump | 45 |
|
||||
```
|
||||
|
||||
|
||||
## Building Schemas from Worksheets
|
||||
|
||||
The `sheet_to_json` helper function generates arrays of JS objects that can be
|
||||
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:
|
||||
|
||||
```js
|
||||
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);
|
||||
}
|
||||
```
|
||||
|
||||
After finding the headers, a deduplication step ensures that data is not lost.
|
||||
Duplicate headers will be suffixed with `_1`, `_2`, etc.
|
||||
|
||||
```js
|
||||
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;
|
||||
}
|
||||
```
|
||||
|
||||
A column-major walk helps determine the data type. For SQLite the only relevant
|
||||
data types are `REAL` and `TEXT`. If a string or date or error is seen in any
|
||||
value of a column, the column is marked as `TEXT`:
|
||||
|
||||
```js
|
||||
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 = "TEXT";
|
||||
else if(seen.n + seen.b + seen.d + seen.e > 1) _type = "TEXT";
|
||||
else switch(true) {
|
||||
case seen.b:
|
||||
case seen.n: _type = "REAL"; break;
|
||||
case seen.e: _type = "TEXT"; break;
|
||||
case seen.d: _type = "TEXT"; break;
|
||||
}
|
||||
types[C-range.s.c] = _type || "TEXT";
|
||||
}
|
||||
```
|
||||
|
||||
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
|
||||
entries in the database. K/V stores and Objects add additional restrictions.
|
||||
|
||||
There is no natural way to translate arbitrarily shaped schemas to worksheets
|
||||
in a workbook. One common trick is to dedicate one worksheet to holding named
|
||||
keys. For example, considering the JS object:
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "SheetDB",
|
||||
"metadata": {
|
||||
"author": "SheetJS",
|
||||
"code": 7262
|
||||
},
|
||||
"data": [
|
||||
{ "Name": "Barack Obama", "Index": 44 },
|
||||
{ "Name": "Donald Trump", "Index": 45 },
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
A dedicated worksheet should store the one-off named values:
|
||||
|
||||
```
|
||||
XXX| A | B |
|
||||
---+-----------------+---------+
|
||||
1 | Path | Value |
|
||||
2 | title | SheetDB |
|
||||
3 | metadata.author | SheetJS |
|
||||
4 | metadata.code | 7262 |
|
||||
```
|
||||
|
||||
The included `ObjUtils.js` script demonstrates object-workbook conversion:
|
||||
|
||||
```js
|
||||
function deepset(obj, path, value) {
|
||||
if(path.indexOf(".") == -1) return obj[path] = value;
|
||||
var parts = path.split(".");
|
||||
if(!obj[parts[0]]) obj[parts[0]] = {};
|
||||
return deepset(obj[parts[0]], parts.slice(1).join("."), value);
|
||||
}
|
||||
function workbook_to_object(wb) {
|
||||
var out = {};
|
||||
|
||||
/* assign one-off keys */
|
||||
var ws = wb.Sheets["_keys"]; if(ws) {
|
||||
var data = XLSX.utils.sheet_to_json(ws, {raw:true});
|
||||
data.forEach(function(r) { deepset(out, r.path, r.value); });
|
||||
}
|
||||
|
||||
/* assign arrays from worksheet tables */
|
||||
wb.SheetNames.forEach(function(n) {
|
||||
if(n == "_keys") return;
|
||||
out[n] = XLSX.utils.sheet_to_json(wb.Sheets[n], {raw:true});
|
||||
});
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
function walk(obj, key, arr) {
|
||||
if(Array.isArray(obj)) return;
|
||||
if(typeof obj != "object") { arr.push({path:key, value:obj}); return; }
|
||||
Object.keys(obj).forEach(function(k) { walk(obj[k], key?key+"."+k:k, arr); });
|
||||
}
|
||||
function object_to_workbook(obj) {
|
||||
var wb = XLSX.utils.book_new();
|
||||
|
||||
/* keyed entries */
|
||||
var base = []; walk(obj, "", base);
|
||||
var ws = XLSX.utils.json_to_sheet(base, {header:["path", "value"]});
|
||||
XLSX.utils.book_append_sheet(wb, ws, "_keys");
|
||||
|
||||
/* arrays */
|
||||
Object.keys(obj).forEach(function(k) {
|
||||
if(!Array.isArray(obj[k])) return;
|
||||
XLSX.utils.book_append_sheet(wb, XLSX.utils.json_to_sheet(obj[k]), k);
|
||||
});
|
||||
|
||||
return wb;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Browser APIs
|
||||
|
||||
#### WebSQL
|
||||
|
||||
WebSQL is a popular SQL-based in-browser database available on Chrome / Safari.
|
||||
In practice, it is powered by SQLite, and most simple SQLite-compatible queries
|
||||
work as-is in WebSQL.
|
||||
|
||||
The public demo <http://sheetjs.com/sexql> generates a database from workbook.
|
||||
|
||||
#### LocalStorage and SessionStorage
|
||||
|
||||
The Storage API, encompassing `localStorage` and `sessionStorage`, describes
|
||||
simple key-value stores that only support string values and keys. Objects can be
|
||||
stored as JSON using `JSON.stringify` and `JSON.parse` to set and get keys.
|
||||
|
||||
`SheetJSStorage.js` extends the `Storage` prototype with a `load` function to
|
||||
populate the db based on an object and a `dump` function to generate a workbook
|
||||
from the data in the storage. `LocalStorage.html` tests `localStorage`.
|
||||
|
||||
#### IndexedDB
|
||||
|
||||
IndexedDB is a more complex storage solution, but the `localForage` wrapper
|
||||
supplies a Promise-based interface mimicking the `Storage` API.
|
||||
|
||||
`SheetJSForage.js` extends the `localforage` object with a `load` function to
|
||||
populate the db based on an object and a `dump` function to generate a workbook
|
||||
from the data in the storage. `LocalForage.html` forces IndexedDB mode.
|
||||
|
||||
|
||||
## External Database Demos
|
||||
|
||||
### SQL Databases
|
||||
|
||||
There are nodejs connector libraries for all of the popular RDBMS systems. They
|
||||
have facilities for connecting to a database, executing queries, and obtaining
|
||||
results as arrays of JS objects that can be passed to `json_to_sheet`. The main
|
||||
differences surround API shape and supported data types.
|
||||
|
||||
#### SQLite
|
||||
|
||||
[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
|
||||
|
||||
`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
|
||||
(`SheetJS2.db`) and verifies the tables are preserved.
|
||||
|
||||
#### MySQL / MariaDB
|
||||
|
||||
[The `mysql2` module](https://www.npmjs.com/package/mysql2) supplies a callback
|
||||
API as well as a Promise wrapper. `Connection#query` runs a statement and
|
||||
returns an array whose first element is an array of JS objects.
|
||||
|
||||
`MySQLTest.js` connects to the MySQL instance running on `localhost`, builds two
|
||||
tables in the `sheetjs` database, exports to XLSX, imports the new XLSX file to
|
||||
the `sheetj5` database and verifies the tables are preserved.
|
||||
|
||||
#### PostgreSQL
|
||||
|
||||
[The `pg` module](https://www.npmjs.com/package/pg) supplies a Promise wrapper.
|
||||
Like with `mysql2`, `Client#query` runs a statement and returns a result object.
|
||||
The `rows` key of the object is an array of JS objects.
|
||||
|
||||
`PgSQLTest.js` connects to the PostgreSQL server on `localhost`, builds two
|
||||
tables in the `sheetjs` database, exports to XLSX, imports the new XLSX file to
|
||||
the `sheetj5` database and verifies the tables are preserved.
|
||||
|
||||
### Key/Value Stores
|
||||
|
||||
#### Redis
|
||||
|
||||
Redis is a powerful data structure server that can store simple strings, sets,
|
||||
sorted sets, hashes and lists. One simple database representation stores the
|
||||
strings in a special worksheet (`_strs`), the manifest in another worksheet
|
||||
(`_manifest`), and each object in its own worksheet (`obj##`).
|
||||
|
||||
`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
|
||||
|
||||
#### LowDB
|
||||
|
||||
LowDB is a small schemaless database powered by `lodash`. `_.get` and `_.set`
|
||||
helper functions make storing metadata a breeze. The included `SheetJSLowDB.js`
|
||||
script demonstrates a simple adapter that can load and dump data.
|
||||
|
||||
|
||||
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-xlsx?pixel)](https://github.com/SheetJS/js-xlsx)
|
50
demos/database/RedisTest.js
Normal file
50
demos/database/RedisTest.js
Normal file
@ -0,0 +1,50 @@
|
||||
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
/* eslint-env node */
|
||||
var XLSX = require("xlsx");
|
||||
var SheetJSRedis = require("./SheetJSRedis");
|
||||
var assert = require('assert');
|
||||
var redis = require("redis"), util = require("util");
|
||||
var client = redis.createClient();
|
||||
|
||||
|
||||
/* Sample data */
|
||||
var init = [
|
||||
["FLUSHALL", []],
|
||||
["SADD", ["birdpowers", "flight", "pecking"]],
|
||||
["SET", ["foo", "bar"]],
|
||||
["SET", ["baz", 0]],
|
||||
["RPUSH", ["friends", "sam", "alice", "bob"]],
|
||||
["ZADD", ["hackers", 1906, 'Grace Hopper', 1912, 'Alan Turing', 1916, 'Claude Shannon', 1940, 'Alan Kay', 1953, 'Richard Stallman', 1957, 'Sophie Wilson', 1965, 'Yukihiro Matsumoto', 1969, 'Linus Torvalds']],
|
||||
["SADD", ["superpowers", "flight", 'x-ray vision']],
|
||||
["HMSET", ["user:1000", "name", 'John Smith', "email", 'john.smith@example.com', "password", "s3cret", "visits", 1]],
|
||||
["HMSET", ["user:1001", "name", 'Mary Jones', "email", 'mjones@example.com', "password", "hidden"]]
|
||||
];
|
||||
|
||||
const R = (()=>{
|
||||
const Rcache = {};
|
||||
const R_ = (n) => Rcache[n] || (Rcache[n] = util.promisify(client[n]).bind(client));
|
||||
return (n) => R_(n.toLowerCase());
|
||||
})();
|
||||
|
||||
(async () => {
|
||||
for(var i = 0; i < init.length; ++i) await R(init[i][0])(init[i][1]);
|
||||
|
||||
/* Export database to XLSX */
|
||||
var wb = await SheetJSRedis.redis_to_wb(R);
|
||||
XLSX.writeFile(wb, "redis.xlsx");
|
||||
|
||||
/* Import XLSX to database */
|
||||
await R("flushall")();
|
||||
var wb2 = XLSX.readFile("redis.xlsx");
|
||||
await SheetJSRedis.wb_to_redis(wb2, R);
|
||||
|
||||
/* Verify */
|
||||
assert.equal(await R("get")("foo"), "bar");
|
||||
assert.equal(await R("lindex")("friends", 1), "alice");
|
||||
assert.equal(await R("zscore")("hackers", "Claude Shannon"), 1916);
|
||||
assert.equal(await R("hget")("user:1000", "name"), "John Smith");
|
||||
assert.equal(await R("sismember")("superpowers", "flight"), "1");
|
||||
assert.equal(await R("sismember")("birdpowers", "pecking"), "1");
|
||||
|
||||
client.quit();
|
||||
})();
|
52
demos/database/SQLiteTest.js
Normal file
52
demos/database/SQLiteTest.js
Normal file
@ -0,0 +1,52 @@
|
||||
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
/* eslint-env node */
|
||||
var XLSX = require('xlsx');
|
||||
var assert = require('assert');
|
||||
var SheetJSSQL = require('./SheetJSSQL');
|
||||
var Database = require('better-sqlite3');
|
||||
var db1 = new Database('SheetJS1.db');
|
||||
|
||||
/* Sample data table */
|
||||
var init = [
|
||||
"DROP TABLE IF EXISTS pres",
|
||||
"CREATE TABLE pres (name TEXT, idx INTEGER)",
|
||||
"INSERT INTO pres VALUES ('Barack Obama', 44)",
|
||||
"INSERT INTO pres VALUES ('Donald Trump', 45)",
|
||||
"DROP TABLE IF EXISTS fmts",
|
||||
"CREATE TABLE fmts (ext TEXT, ctr TEXT, multi INTEGER)",
|
||||
"INSERT INTO fmts VALUES ('XLSB', 'ZIP', 1)",
|
||||
"INSERT INTO fmts VALUES ('XLS', 'CFB', 1)",
|
||||
"INSERT INTO fmts VALUES ('XLML', '', 1)",
|
||||
"INSERT INTO fmts VALUES ('CSV', '', 0)",
|
||||
];
|
||||
db1.exec(init.join(";"));
|
||||
|
||||
/* Export table to XLSX */
|
||||
var wb = XLSX.utils.book_new();
|
||||
function book_append_table(wb, db, name) {
|
||||
var r = db.prepare('SELECT * FROM ' + name).all();
|
||||
var ws = XLSX.utils.json_to_sheet(r);
|
||||
XLSX.utils.book_append_sheet(wb, ws, name);
|
||||
}
|
||||
book_append_table(wb, db1, "pres");
|
||||
book_append_table(wb, db1, "fmts");
|
||||
XLSX.writeFile(wb, "sqlite.xlsx");
|
||||
|
||||
/* Import XLSX to table */
|
||||
var db2 = new Database('SheetJS2.db');
|
||||
var wb2 = XLSX.readFile("sqlite.xlsx");
|
||||
var queries = SheetJSSQL.book_to_sql(wb2, "SQLITE");
|
||||
queries.forEach(function(q) { db2.exec(q); });
|
||||
|
||||
/* Compare databases */
|
||||
var P1 = db1.prepare("SELECT * FROM pres").all();
|
||||
var P2 = db2.prepare("SELECT * FROM pres").all();
|
||||
var F1 = db1.prepare("SELECT * FROM fmts").all();
|
||||
var F2 = db2.prepare("SELECT * FROM fmts").all();
|
||||
assert.deepEqual(P1, P2);
|
||||
assert.deepEqual(F1, F2);
|
||||
|
||||
/* Display results */
|
||||
console.log(P2);
|
||||
console.log(F2);
|
||||
|
20
demos/database/SheetJSForage.js
Normal file
20
demos/database/SheetJSForage.js
Normal file
@ -0,0 +1,20 @@
|
||||
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
/*global ObjUtils, localforage */
|
||||
localforage.load = async function foo(data) {
|
||||
var keys = Object.keys(data);
|
||||
for(var i = 0; i < keys.length; ++i) {
|
||||
var key = keys[i], val = JSON.stringify(data[keys[i]])
|
||||
await localforage.setItem(key, val);
|
||||
}
|
||||
};
|
||||
|
||||
localforage.dump = async function() {
|
||||
var obj = {};
|
||||
var length = await localforage.length();
|
||||
for(var i = 0; i < length; ++i) {
|
||||
var key = await this.key(i);
|
||||
var val = await this.getItem(key);
|
||||
obj[key] = JSON.parse(val);
|
||||
}
|
||||
return ObjUtils.object_to_workbook(obj);
|
||||
};
|
20
demos/database/SheetJSLowDB.js
Normal file
20
demos/database/SheetJSLowDB.js
Normal file
@ -0,0 +1,20 @@
|
||||
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
/* eslint-env node */
|
||||
|
||||
var XLSX = require('xlsx');
|
||||
var ObjUtils = require('./ObjUtils');
|
||||
|
||||
function SheetJSAdapter() { this.defaultValue = {}; };
|
||||
|
||||
SheetJSAdapter.prototype.read = function() { return this.defaultValue; };
|
||||
SheetJSAdapter.prototype.write = function(/*data*/) {};
|
||||
|
||||
SheetJSAdapter.prototype.dumpRaw = function() { return ObjUtils.object_to_workbook(this.defaultValue); };
|
||||
SheetJSAdapter.prototype.dump = function(options) { XLSX.write(this.dumpRaw(), options); };
|
||||
SheetJSAdapter.prototype.dumpFile = function(path, options) { XLSX.writeFile(this.dumpRaw(), path, options); };
|
||||
|
||||
SheetJSAdapter.prototype.loadRaw = function(wb) { ObjUtils.workbook_set_object(this.defaultValue, wb); };
|
||||
SheetJSAdapter.prototype.load = function(data, options) { this.loadRaw(XLSX.read(data, options)); };
|
||||
SheetJSAdapter.prototype.loadFile = function(path, options) { this.loadRaw(XLSX.readFile(path, options)); };
|
||||
|
||||
if(typeof module !== 'undefined') module.exports = SheetJSAdapter;
|
73
demos/database/SheetJSRedis.js
Normal file
73
demos/database/SheetJSRedis.js
Normal file
@ -0,0 +1,73 @@
|
||||
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
/* eslint-env node */
|
||||
var XLSX = require("xlsx");
|
||||
|
||||
const pair = (arr) => arr.map((x,i)=>!(i%2)&&[x,+arr[i+1]]).filter(x=>x);
|
||||
const keyify = (obj) => Object.keys(obj).map(x => [x, obj[x]]);
|
||||
|
||||
async function redis_to_wb(R) {
|
||||
var wb = XLSX.utils.book_new();
|
||||
var manifest = [], strs = [];
|
||||
|
||||
/* store strings in strs and keep note of other objects in manifest */
|
||||
var keys = await R("keys")("*"), type = "";
|
||||
for(var i = 0; i < keys.length; ++i) {
|
||||
type = await R("type")(keys[i]);
|
||||
switch(type) {
|
||||
case "string": strs.push({key:keys[i], value: await R("get")(keys[i])}); break;
|
||||
case "list": case "zset": case "set": case "hash": manifest.push({key:keys[i], type:type}); break;
|
||||
default: throw new Error("bad type " + type);
|
||||
}
|
||||
}
|
||||
|
||||
/* add worksheets if relevant */
|
||||
if(strs.length > 0) {
|
||||
var wss = XLSX.utils.json_to_sheet(strs, {header: ["key", "value"], skipHeader:1});
|
||||
XLSX.utils.book_append_sheet(wb, wss, "_strs");
|
||||
}
|
||||
if(manifest.length > 0) {
|
||||
var wsm = XLSX.utils.json_to_sheet(manifest, {header: ["key", "type"]});
|
||||
XLSX.utils.book_append_sheet(wb, wsm, "_manifest");
|
||||
}
|
||||
for(i = 0; i < manifest.length; ++i) {
|
||||
var sn = "obj" + i;
|
||||
var aoa, key = manifest[i].key;
|
||||
switch((type=manifest[i].type)) {
|
||||
case "list":
|
||||
aoa = (await R("lrange")(key, 0, -1)).map(x => [x]); break;
|
||||
case "set":
|
||||
aoa = (await R("smembers")(key)).map(x => [x]); break;
|
||||
case "zset":
|
||||
aoa = pair(await R("zrange")(key, 0, -1, "withscores")); break;
|
||||
case "hash":
|
||||
aoa = keyify(await R("hgetall")(key)); break;
|
||||
default: throw new Error("bad type " + type);
|
||||
}
|
||||
XLSX.utils.book_append_sheet(wb, XLSX.utils.aoa_to_sheet(aoa), sn);
|
||||
}
|
||||
return wb;
|
||||
}
|
||||
|
||||
/* convert worksheet aoa to specific redis type */
|
||||
const aoa_to_redis = {
|
||||
list: async (aoa, R, key) => await R("RPUSH")([key].concat(aoa.map(x=>x[0]))),
|
||||
zset: async (aoa, R, key) => await R("ZADD" )([key].concat(aoa.reduce((acc,x)=>acc.concat([+x[1], x[0]]), []))),
|
||||
hash: async (aoa, R, key) => await R("HMSET")([key].concat(aoa.reduce((acc,x)=>acc.concat(x), []))),
|
||||
set: async (aoa, R, key) => await R("SADD" )([key].concat(aoa.map(x=>x[0])))
|
||||
};
|
||||
async function wb_to_redis(wb, R) {
|
||||
if(wb.Sheets._strs) {
|
||||
var strs = XLSX.utils.sheet_to_json(wb.Sheets._strs, {header:1});
|
||||
for(var i = 0; i < strs.length; ++i) await R("SET")(strs[i]);
|
||||
}
|
||||
if(!wb.Sheets._manifest) return;
|
||||
var M = XLSX.utils.sheet_to_json(wb.Sheets._manifest);
|
||||
for(i = 0; i < M.length; ++i) {
|
||||
var aoa = XLSX.utils.sheet_to_json(wb.Sheets["obj" + i], {header:1});
|
||||
await aoa_to_redis[M[i].type](aoa, R, M[i].key);
|
||||
}
|
||||
}
|
||||
module.exports = {
|
||||
redis_to_wb,
|
||||
wb_to_redis
|
||||
};
|
88
demos/database/SheetJSSQL.js
Normal file
88
demos/database/SheetJSSQL.js
Normal file
@ -0,0 +1,88 @@
|
||||
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
var SheetJSSQL = (function() {
|
||||
|
||||
var X;
|
||||
if(typeof XLSX !== "undefined") X = XLSX;
|
||||
else if(typeof require !== 'undefined') X = require('xlsx');
|
||||
else throw new Error("Could not find XLSX");
|
||||
|
||||
var _TYPES = {
|
||||
"PGSQL": { t:"text", n:"float8", d:"timestamp", b:"boolean" },
|
||||
"MYSQL": { t:"TEXT", n:"REAL", d:"DATETIME", b:"TINYINT" },
|
||||
"SQLITE": { t:"TEXT", n:"REAL", d:"TEXT", b:"REAL" }
|
||||
}
|
||||
function sheet_to_sql(ws, sname, mode) {
|
||||
var TYPES = _TYPES[mode || "SQLITE"]
|
||||
if(!ws || !ws['!ref']) return;
|
||||
var range = X.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 = X.utils.encode_cell({c:C,r:R});
|
||||
names[C-range.s.c] = ws[addr] ? ws[addr].v : X.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[X.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;
|
||||
}
|
||||
|
||||
var out = [];
|
||||
|
||||
var BT = mode == "PGSQL" ? "" : "`";
|
||||
var Q = mode == "PGSQL" ? "'" : '"';
|
||||
var R = 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(", ") + ");" );
|
||||
|
||||
for(R = range.s.r+1; R<= range.e.r; ++R) {
|
||||
var fields = [], values = [];
|
||||
for(C = range.s.c; C<= range.e.c; ++C) {
|
||||
var cell = ws[X.utils.encode_cell({c:C,r:R})];
|
||||
if(!cell) continue;
|
||||
fields.push(BT + names[C-range.s.c] + BT);
|
||||
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;
|
||||
}
|
||||
values.push(val);
|
||||
}
|
||||
out.push("INSERT INTO " + BT +sname+ BT + " (" + fields.join(", ") + ") VALUES (" + values.join(",") + ");");
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
function book_to_sql(wb, mode) {
|
||||
return wb.SheetNames.reduce(function(acc, n) {
|
||||
return acc.concat(sheet_to_sql(wb.Sheets[n], n, mode));
|
||||
}, []);
|
||||
}
|
||||
|
||||
return {
|
||||
book_to_sql: book_to_sql,
|
||||
sheet_to_sql: sheet_to_sql
|
||||
};
|
||||
})();
|
||||
if(typeof module !== 'undefined') module.exports = SheetJSSQL;
|
18
demos/database/SheetJSStorage.js
Normal file
18
demos/database/SheetJSStorage.js
Normal file
@ -0,0 +1,18 @@
|
||||
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
/* eslint-env browser */
|
||||
/*global ObjUtils */
|
||||
Storage.prototype.load = function(data) {
|
||||
var self = this;
|
||||
Object.keys(data).forEach(function(k) {
|
||||
self.setItem(k, JSON.stringify(data[k]));
|
||||
});
|
||||
};
|
||||
|
||||
Storage.prototype.dump = function() {
|
||||
var obj = {};
|
||||
for(var i = 0; i < this.length; ++i) {
|
||||
var key = this.key(i);
|
||||
obj[key] = JSON.parse(this.getItem(key));
|
||||
}
|
||||
return ObjUtils.object_to_workbook(obj);
|
||||
};
|
1
demos/database/xlsx.full.min.js
vendored
Symbolic link
1
demos/database/xlsx.full.min.js
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../../dist/xlsx.full.min.js
|
@ -48,7 +48,7 @@ The native shims must be suppressed for browser usage:
|
||||
```js
|
||||
const fuse = FuseBox.init({
|
||||
homeDir: ".",
|
||||
target: "node",
|
||||
target: "browser",
|
||||
natives: {
|
||||
Buffer: false,
|
||||
stream: false,
|
||||
|
14
demos/fusebox/index.html
Normal file
14
demos/fusebox/index.html
Normal file
@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript" src="/client.js"></script>
|
||||
</body>
|
||||
</html>
|
2
demos/parcel/.gitignore
vendored
Normal file
2
demos/parcel/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
.cache
|
||||
dist
|
7
demos/parcel/Makefile
Normal file
7
demos/parcel/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
.PHONY: app
|
||||
app:
|
||||
parcel build index.html --public-url ./
|
||||
|
||||
.PHONY: ctest
|
||||
ctest:
|
||||
parcel index.html
|
14
demos/parcel/README.md
Normal file
14
demos/parcel/README.md
Normal file
@ -0,0 +1,14 @@
|
||||
# Parcel
|
||||
|
||||
Parcel Bundler starting from version 1.5.0 should play nice with this library
|
||||
out of the box. The standard import form can be used in JS files:
|
||||
|
||||
```js
|
||||
import XLSX from 'xlsx'
|
||||
```
|
||||
|
||||
Errors of the form `Could not statically evaluate fs call` stem from a parcel
|
||||
[bug](https://github.com/parcel-bundler/parcel/pull/523#issuecomment-357486164).
|
||||
Upgrade to version 1.5.0 or later.
|
||||
|
||||
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-xlsx?pixel)](https://github.com/SheetJS/js-xlsx)
|
50
demos/parcel/index.html
Normal file
50
demos/parcel/index.html
Normal file
@ -0,0 +1,50 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- xlsx.js (C) 2013-present SheetJS http://sheetjs.com -->
|
||||
<!-- vim: set ts=2: -->
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>SheetJS Live Demo</title>
|
||||
<style>
|
||||
#drop{
|
||||
border:2px dashed #bbb;
|
||||
-moz-border-radius:5px;
|
||||
-webkit-border-radius:5px;
|
||||
border-radius:5px;
|
||||
padding:25px;
|
||||
text-align:center;
|
||||
font:20pt bold,"Vollkorn";color:#bbb
|
||||
}
|
||||
#b64data{
|
||||
width:100%;
|
||||
}
|
||||
a { text-decoration: none }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<pre>
|
||||
<b><a href="http://sheetjs.com">SheetJS Data Preview Live Demo</a></b>
|
||||
(Base64 text works back to IE6; drag and drop works back to IE10)
|
||||
|
||||
<a href="https://github.com/SheetJS/js-xlsx">Source Code Repo</a>
|
||||
<a href="https://github.com/SheetJS/js-xlsx/issues">Issues? Something look weird? Click here and report an issue</a>
|
||||
Output Format: <select name="format" onchange="setfmt()">
|
||||
<option value="csv" selected> CSV</option>
|
||||
<option value="json"> JSON</option>
|
||||
<option value="form"> FORMULAE</option>
|
||||
<option value="html"> HTML</option>
|
||||
</select><br />
|
||||
<div id="drop">Drop a spreadsheet file here to see sheet data</div>
|
||||
<input type="file" name="xlfile" id="xlf" /> ... or click here to select a file
|
||||
|
||||
<textarea id="b64data">... or paste a base64-encoding here</textarea>
|
||||
<input type="button" id="dotext" value="Click here to process the base64 text" onclick="b64it();"/><br />
|
||||
<b>Advanced Demo Options:</b>
|
||||
Use readAsBinaryString: (when available) <input type="checkbox" name="userabs" checked>
|
||||
</pre>
|
||||
<pre id="out"></pre>
|
||||
<div id="htmlout"></div>
|
||||
<br />
|
||||
<script src="./index.js"></script>
|
||||
</body>
|
||||
</html>
|
136
demos/parcel/index.js
Normal file
136
demos/parcel/index.js
Normal file
@ -0,0 +1,136 @@
|
||||
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
/*jshint browser:true */
|
||||
import X from '../../'
|
||||
|
||||
console.log(X.version);
|
||||
|
||||
var global_wb;
|
||||
|
||||
var process_wb = (function() {
|
||||
var OUT = document.getElementById('out');
|
||||
var HTMLOUT = document.getElementById('htmlout');
|
||||
|
||||
var get_format = (function() {
|
||||
var radios = document.getElementsByName( "format" );
|
||||
return function() {
|
||||
for(var i = 0; i < radios.length; ++i) if(radios[i].checked || radios.length === 1) return radios[i].value;
|
||||
};
|
||||
})();
|
||||
|
||||
var to_json = function to_json(workbook) {
|
||||
var result = {};
|
||||
workbook.SheetNames.forEach(function(sheetName) {
|
||||
var roa = X.utils.sheet_to_json(workbook.Sheets[sheetName]);
|
||||
if(roa.length) result[sheetName] = roa;
|
||||
});
|
||||
return JSON.stringify(result, 2, 2);
|
||||
};
|
||||
|
||||
var to_csv = function to_csv(workbook) {
|
||||
var result = [];
|
||||
workbook.SheetNames.forEach(function(sheetName) {
|
||||
var csv = X.utils.sheet_to_csv(workbook.Sheets[sheetName]);
|
||||
if(csv.length){
|
||||
result.push("SHEET: " + sheetName);
|
||||
result.push("");
|
||||
result.push(csv);
|
||||
}
|
||||
});
|
||||
return result.join("\n");
|
||||
};
|
||||
|
||||
var to_fmla = function to_fmla(workbook) {
|
||||
var result = [];
|
||||
workbook.SheetNames.forEach(function(sheetName) {
|
||||
var formulae = X.utils.get_formulae(workbook.Sheets[sheetName]);
|
||||
if(formulae.length){
|
||||
result.push("SHEET: " + sheetName);
|
||||
result.push("");
|
||||
result.push(formulae.join("\n"));
|
||||
}
|
||||
});
|
||||
return result.join("\n");
|
||||
};
|
||||
|
||||
var to_html = function to_html(workbook) {
|
||||
HTMLOUT.innerHTML = "";
|
||||
workbook.SheetNames.forEach(function(sheetName) {
|
||||
var htmlstr = X.write(workbook, {sheet:sheetName, type:'binary', bookType:'html'});
|
||||
HTMLOUT.innerHTML += htmlstr;
|
||||
});
|
||||
return "";
|
||||
};
|
||||
|
||||
return function process_wb(wb) {
|
||||
global_wb = wb;
|
||||
var output = "";
|
||||
switch(get_format()) {
|
||||
case "form": output = to_fmla(wb); break;
|
||||
case "html": output = to_html(wb); break;
|
||||
case "json": output = to_json(wb); break;
|
||||
default: output = to_csv(wb);
|
||||
}
|
||||
if(OUT.innerText === undefined) OUT.textContent = output;
|
||||
else OUT.innerText = output;
|
||||
if(typeof console !== 'undefined') console.log("output", new Date());
|
||||
};
|
||||
})();
|
||||
|
||||
var setfmt = window.setfmt = function setfmt() { if(global_wb) process_wb(global_wb); };
|
||||
|
||||
var b64it = window.b64it = (function() {
|
||||
var tarea = document.getElementById('b64data');
|
||||
return function b64it() {
|
||||
if(typeof console !== 'undefined') console.log("onload", new Date());
|
||||
var wb = X.read(tarea.value, {type:'base64', WTF:false});
|
||||
process_wb(wb);
|
||||
};
|
||||
})();
|
||||
|
||||
var do_file = (function() {
|
||||
var rABS = typeof FileReader !== "undefined" && (FileReader.prototype||{}).readAsBinaryString;
|
||||
var domrabs = document.getElementsByName("userabs")[0];
|
||||
if(!rABS) domrabs.disabled = !(domrabs.checked = false);
|
||||
|
||||
return function do_file(files) {
|
||||
rABS = domrabs.checked;
|
||||
var f = files[0];
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
if(typeof console !== 'undefined') console.log("onload", new Date(), rABS);
|
||||
var data = e.target.result;
|
||||
if(!rABS) data = new Uint8Array(data);
|
||||
process_wb(X.read(data, {type: rABS ? 'binary' : 'array'}));
|
||||
};
|
||||
if(rABS) reader.readAsBinaryString(f);
|
||||
else reader.readAsArrayBuffer(f);
|
||||
};
|
||||
})();
|
||||
|
||||
(function() {
|
||||
var drop = document.getElementById('drop');
|
||||
if(!drop.addEventListener) return;
|
||||
|
||||
function handleDrop(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
do_file(e.dataTransfer.files);
|
||||
}
|
||||
|
||||
function handleDragover(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
e.dataTransfer.dropEffect = 'copy';
|
||||
}
|
||||
|
||||
drop.addEventListener('dragenter', handleDragover, false);
|
||||
drop.addEventListener('dragover', handleDragover, false);
|
||||
drop.addEventListener('drop', handleDrop, false);
|
||||
})();
|
||||
|
||||
(function() {
|
||||
var xlf = document.getElementById('xlf');
|
||||
if(!xlf.addEventListener) return;
|
||||
function handleFile(e) { do_file(e.target.files); }
|
||||
xlf.addEventListener('change', handleFile, false);
|
||||
})();
|
@ -10,9 +10,12 @@ The [`demos` directory](demos/) includes sample projects for:
|
||||
- [`vue 2.x and weex`](demos/vue/)
|
||||
- [`XMLHttpRequest and fetch`](demos/xhr/)
|
||||
- [`nodejs server`](demos/server/)
|
||||
- [`databases and key/value stores`](demos/database/)
|
||||
|
||||
**Bundlers and Tooling**
|
||||
- [`browserify`](demos/browserify/)
|
||||
- [`fusebox`](demos/fusebox/)
|
||||
- [`parcel`](demos/parcel/)
|
||||
- [`requirejs`](demos/requirejs/)
|
||||
- [`rollup`](demos/rollup/)
|
||||
- [`systemjs`](demos/systemjs/)
|
||||
|
@ -187,9 +187,12 @@ The [`demos` directory](demos/) includes sample projects for:
|
||||
- [`vue 2.x and weex`](demos/vue/)
|
||||
- [`XMLHttpRequest and fetch`](demos/xhr/)
|
||||
- [`nodejs server`](demos/server/)
|
||||
- [`databases and key/value stores`](demos/database/)
|
||||
|
||||
**Bundlers and Tooling**
|
||||
- [`browserify`](demos/browserify/)
|
||||
- [`fusebox`](demos/fusebox/)
|
||||
- [`parcel`](demos/parcel/)
|
||||
- [`requirejs`](demos/requirejs/)
|
||||
- [`rollup`](demos/rollup/)
|
||||
- [`systemjs`](demos/systemjs/)
|
||||
|
Loading…
Reference in New Issue
Block a user