diff --git a/.flowconfig b/.flowconfig
index 21826f2..8462e3d 100644
--- a/.flowconfig
+++ b/.flowconfig
@@ -1,6 +1,7 @@
[ignore]
.*/node_modules/.*
.*/dist/.*
+.*/tmp/.*
.*/test.js
.*/bits/.*
@@ -11,8 +12,6 @@
.*/demo/browser.js
.*/shim.js
-.*/odsbits/.*
-.*/ods.js
.*/xlsx.js
.*/xlsxworker.js
.*/xlsxworker1.js
@@ -27,7 +26,6 @@ xlsxworker.flow.js
xlsxworker1.flow.js
xlsxworker2.flow.js
xlsx.flow.js
-ods.flow.js
.*/bin/.*.njs
.*/demo/browser.flow.js
diff --git a/.gitignore b/.gitignore
index e6b6c42..77bb8fe 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,3 +18,4 @@ tmp
*.htm
*.html
*.sheetjs
+*.exe
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..ccbfb4f
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,12 @@
+# CHANGELOG
+
+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.
+
+
+## 0.9.0 (2017-03-09)
+
+* Removed ods.js source. The xlsx.js source absorbed the ODS logic and exposes
+ the ODS variable, so projects should remove references to ods.js
+
diff --git a/Makefile b/Makefile
index 71d6138..4e50086 100644
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@ LIB=xlsx
FMT=xlsx xlsm xlsb ods xls xml misc full
REQS=jszip.js
ADDONS=dist/cpexcel.js
-AUXTARGETS=ods.js
+AUXTARGETS=
CMDS=bin/xlsx.njs
HTMLLINT=index.html
@@ -62,23 +62,18 @@ dist: dist-deps $(TARGET) bower.json ## Prepare JS files for distribution
cat <(head -n 1 bits/00_header.js) $(REQS) $(ADDONS) $(TARGET) $(AUXTARGETS) > demos/requirejs/$(LIB).full.js
.PHONY: dist-deps
-dist-deps: ods.js ## Copy dependencies for distribution
+dist-deps: ## Copy dependencies for distribution
cp node_modules/codepage/dist/cpexcel.full.js dist/cpexcel.js
cp jszip.js dist/jszip.js
- cp ods.js dist/ods.js
- uglifyjs $(UGLIFYOPTS) ods.js -o dist/ods.min.js --source-map dist/ods.min.map --preamble "$$(head -n 1 bits/00_header.js)"
- misc/strip_sourcemap.sh dist/ods.min.js
.PHONY: aux
aux: $(AUXTARGETS)
-.PHONY: ods
-ods: ods.js
-
-ODSDEPS=$(sort $(wildcard odsbits/*.js))
-ods.flow.js: $(ODSDEPS) ## Build ODS support library
- cat $^ | tr -d '\15\32' > $@
+.PHONY: nexe
+nexe: xlsx.exe
+xlsx.exe: bin/xlsx.js xlsx.js
+ nexe -i bin/xlsx.njs -o xlsx.exe
## Testing
@@ -92,6 +87,9 @@ TESTFMT=$(patsubst %,test_%,$(FMT))
$(TESTFMT): test_%:
FMTS=$* make test
+.PHONY: demos
+demos: demo-browserify demo-webpack demo-requirejs
+
.PHONY: demo-browserify
demo-browserify: ## Run browserify demo build
make -C demos/browserify
@@ -102,6 +100,11 @@ demo-webpack: ## Run webpack demo build
make -C demos/webpack
@echo "start a local server and go to demos/webpack/webpack.html"
+.PHONY: demo-requirejs
+demo-requirejs: ## Run requirejs demo build
+ make -C demos/requirejs
+ @echo "start a local server and go to demos/requirejs/requirejs.html"
+
## Code Checking
.PHONY: lint
diff --git a/README.md b/README.md
index 8195c4b..eb71e00 100644
--- a/README.md
+++ b/README.md
@@ -67,8 +67,6 @@ be included directly:
```html
-
-
```
An appropriate version for each dependency is included in the dist/ directory.
diff --git a/bits/00_header.js b/bits/00_header.js
index ff45f1f..8eefc1d 100644
--- a/bits/00_header.js
+++ b/bits/00_header.js
@@ -2,5 +2,6 @@
/* vim: set ts=2: */
/*jshint -W041 */
/*jshint funcscope:true, eqnull:true */
+/*exported XLSX */
var XLSX = {};
(function make_xlsx(XLSX){
diff --git a/bits/01_version.js b/bits/01_version.js
index d3e71bd..636a129 100644
--- a/bits/01_version.js
+++ b/bits/01_version.js
@@ -1 +1 @@
-XLSX.version = '0.8.8';
+XLSX.version = '0.9.0';
diff --git a/bits/20_jsutils.js b/bits/20_jsutils.js
index f7348d3..d12c438 100644
--- a/bits/20_jsutils.js
+++ b/bits/20_jsutils.js
@@ -35,6 +35,33 @@ function datenum(v/*:Date*/, date1904/*:?boolean*/)/*:number*/ {
return (epoch + 2209161600000) / (24 * 60 * 60 * 1000);
}
+/* ISO 8601 Duration */
+function parse_isodur(s) {
+ var sec = 0, mt = 0, time = false;
+ var m = s.match(/P([0-9\.]+Y)?([0-9\.]+M)?([0-9\.]+D)?T([0-9\.]+H)?([0-9\.]+M)?([0-9\.]+S)?/);
+ if(!m) throw new Error("|" + s + "| is not an ISO8601 Duration");
+ for(var i = 1; i != m.length; ++i) {
+ if(!m[i]) continue;
+ mt = 1;
+ if(i > 3) time = true;
+ switch(m[i].substr(m[i].length-1)) {
+ case 'Y':
+ throw new Error("Unsupported ISO Duration Field: " + m[i].substr(m[i].length-1));
+ case 'D': mt *= 24;
+ /* falls through */
+ case 'H': mt *= 60;
+ /* falls through */
+ case 'M':
+ if(!time) throw new Error("Unsupported ISO Duration Field: M");
+ else mt *= 60;
+ /* falls through */
+ case 'S': break;
+ }
+ sec += mt * parseInt(m[i], 10);
+ }
+ return sec;
+}
+
function cc2str(arr/*:Array*/)/*:string*/ {
var o = "";
for(var i = 0; i != arr.length; ++i) o += String.fromCharCode(arr[i]);
diff --git a/bits/21_ziputils.js b/bits/21_ziputils.js
index 52b11a6..b44752f 100644
--- a/bits/21_ziputils.js
+++ b/bits/21_ziputils.js
@@ -22,6 +22,7 @@ function getdatabin(data) {
function getdata(data) { return (data && data.name.slice(-4) === ".bin") ? getdatabin(data) : getdatastr(data); }
/* Part 2 Section 10.1.2 "Mapping Content Types" Names are case-insensitive */
+/* OASIS does not comment on filename case sensitivity */
function safegetzipfile(zip, file/*:string*/) {
var k = keys(zip.files);
var f = file.toLowerCase(), g = f.replace(/\//g,'\\');
diff --git a/bits/22_xmlutils.js b/bits/22_xmlutils.js
index 1d4d9cd..6b4c23a 100644
--- a/bits/22_xmlutils.js
+++ b/bits/22_xmlutils.js
@@ -1,4 +1,4 @@
-var attregexg=/([\w:]+)=((?:")([^"]*)(?:")|(?:')([^']*)(?:'))/g;
+var attregexg=/([^\s?>\/]+)=((?:")([^"]*)(?:")|(?:')([^']*)(?:'))/g;
var tagregex=/<[^>]*>/g;
var nsregex=/<\w*:/, nsregex2 = /<(\/?)\w+:/;
function parsexmltag(tag/*:string*/, skip_root/*:?boolean*/)/*:any*/ {
@@ -7,14 +7,21 @@ function parsexmltag(tag/*:string*/, skip_root/*:?boolean*/)/*:any*/ {
for(; eq !== tag.length; ++eq) if((c = tag.charCodeAt(eq)) === 32 || c === 10 || c === 13) break;
if(!skip_root) z[0] = tag.substr(0, eq);
if(eq === tag.length) return z;
- var m = tag.match(attregexg), j=0, w="", v="", i=0, q="", cc="";
+ var m = tag.match(attregexg), j=0, v="", i=0, q="", cc="";
if(m) for(i = 0; i != m.length; ++i) {
cc = m[i];
for(c=0; c != cc.length; ++c) if(cc.charCodeAt(c) === 61) break;
q = cc.substr(0,c); v = cc.substring(c+2, cc.length-1);
for(j=0;j!=q.length;++j) if(q.charCodeAt(j) === 58) break;
- if(j===q.length) z[q] = v;
- else z[(j===5 && q.substr(0,5)==="xmlns"?"xmlns":"")+q.substr(j+1)] = v;
+ if(j===q.length) {
+ //if(q.indexOf("_") > 0) q = q.substr(0, q.indexOf("_")); // from ods
+ z[q] = v;
+ }
+ else {
+ var k = (j===5 && q.substr(0,5)==="xmlns"?"xmlns":"")+q.substr(j+1);
+ //if(z[k] && q.substr(j-3,3) == "ext") continue; // from ods
+ z[k] = v;
+ }
}
return z;
}
diff --git a/odsbits/30_manifest.js b/bits/32_odmanrdf.js
similarity index 57%
rename from odsbits/30_manifest.js
rename to bits/32_odmanrdf.js
index f4fbdac..84a7647 100644
--- a/odsbits/30_manifest.js
+++ b/bits/32_odmanrdf.js
@@ -1,3 +1,4 @@
+/* Open Document Format for Office Applications (OpenDocument) Version 1.2 */
/* Part 3 Section 4 Manifest File */
var CT_ODS = "application/vnd.oasis.opendocument.spreadsheet";
function parse_manifest(d, opts) {
@@ -27,3 +28,30 @@ function write_manifest(manifest/*:Array >*/, opts)/*:string*/ {
o.push('');
return o.join("");
}
+
+/* Part 3 Section 6 Metadata Manifest File */
+function write_rdf_type(file/*:string*/, res/*:string*/, tag/*:?string*/) {
+ return [
+ ' \n',
+ ' \n',
+ ' \n'
+ ].join("");
+}
+function write_rdf_has(base/*:string*/, file/*:string*/) {
+ return [
+ ' \n',
+ ' \n',
+ ' \n'
+ ].join("");
+}
+function write_rdf(rdf, opts) {
+ var o = [XML_HEADER];
+ o.push('\n');
+ for(var i = 0; i != rdf.length; ++i) {
+ o.push(write_rdf_type(rdf[i][0], rdf[i][1]));
+ o.push(write_rdf_has("",rdf[i][0]));
+ }
+ o.push(write_rdf_type("","Document", "pkg"));
+ o.push('');
+ return o.join("");
+}
diff --git a/odsbits/50_formula.js b/bits/65_fods.js
similarity index 100%
rename from odsbits/50_formula.js
rename to bits/65_fods.js
diff --git a/bits/67_wsxml.js b/bits/67_wsxml.js
index cdbfec2..8f0fb8b 100644
--- a/bits/67_wsxml.js
+++ b/bits/67_wsxml.js
@@ -249,6 +249,10 @@ return function parse_ws_xml_data(sdata, s, opts, guess) {
case 'n': p.v = parseFloat(p.v); break;
case 's':
sstr = strs[parseInt(p.v, 10)];
+ if(typeof p.v == 'undefined') {
+ if(!opts.sheetStubs) continue;
+ p.t = "stub";
+ }
p.v = sstr.t;
p.r = sstr.r;
if(opts.cellHTML) p.h = sstr.h;
diff --git a/bits/75_xlml.js b/bits/75_xlml.js
index 5cdbee3..99e4747 100644
--- a/bits/75_xlml.js
+++ b/bits/75_xlml.js
@@ -85,6 +85,7 @@ function process_style_xlml(styles, stag, opts) {
function parse_xlml_data(xml, ss, data, cell/*:any*/, base, styles, csty, row, arrayf, o)/*:Workbook*/ {
var nf = "General", sid = cell.StyleID, S = {}; o = o || {};
var interiors = [];
+ var i = 0;
if(sid === undefined && row) sid = row.StyleID;
if(sid === undefined && csty) sid = csty.StyleID;
while(styles[sid] !== undefined) {
@@ -150,13 +151,15 @@ function xlml_clean_comment(comment/*:any*/) {
}
function xlml_normalize(d)/*:string*/ {
- if(has_buf && Buffer.isBuffer(d)) return d.toString('utf8');
+ if(has_buf &&/*::typeof Buffer !== "undefined" && d != null &&*/ Buffer.isBuffer(d)) return d.toString('utf8');
if(typeof d === 'string') return d;
throw new Error("Bad input format: expected Buffer or string");
}
/* TODO: Everything */
-var xlmlregex = /<(\/?)([a-z0-9]*:|)(\w+)[^>]*>/mg;
+/* UOS uses CJK in tags */
+var xlmlregex = /<(\/?)([^\s?>\/:]*:|)([^\s?>]*[^\s?>\/])[^>]*>/mg;
+//var xlmlregex = /<(\/?)([a-z0-9]*:|)(\w+)[^>]*>/mg;
function parse_xlml_xml(d, opts)/*:Workbook*/ {
var str = debom(xlml_normalize(d));
if(str.substr(0,1000).indexOf("= 0) return parse_html(str, opts);
diff --git a/odsbits/60_content.js b/bits/80_parseods.js
similarity index 98%
rename from odsbits/60_content.js
rename to bits/80_parseods.js
index 41824d5..e408e4d 100644
--- a/odsbits/60_content.js
+++ b/bits/80_parseods.js
@@ -1,5 +1,9 @@
var parse_content_xml = (function() {
+ var parse_text_p = function(text, tag) {
+ return unescapexml(text.replace(//g," ").replace(/<[^>]*>/g,""));
+ };
+
var number_formats = {
/* ods name: [short ssf fmt, long ssf fmt] */
day: ["d", "dd"],
@@ -35,7 +39,7 @@ var parse_content_xml = (function() {
case 'table': case '工作表': // 9.1.2
if(Rn[1]==='/') {
- if(range.e.c >= range.s.c && range.e.r >= range.s.r) ws['!ref'] = get_utils().encode_range(range);
+ if(range.e.c >= range.s.c && range.e.r >= range.s.r) ws['!ref'] = encode_range(range);
if(merges.length) ws['!merges'] = merges;
sheetag.name = utf8read(sheetag['名称'] || sheetag.name);
SheetNames.push(sheetag.name);
@@ -76,7 +80,7 @@ var parse_content_xml = (function() {
mR = parseInt(ctag['number-matrix-rows-spanned'],10) || 0;
mC = parseInt(ctag['number-matrix-columns-spanned'],10) || 0;
mrange = {s: {r:R,c:C}, e:{r:R + mR-1,c:C + mC-1}};
- q.F = get_utils().encode_range(mrange);
+ q.F = encode_range(mrange);
arrayf.push([mrange, q.F]);
}
if(ctag.formula) q.f = ods_to_csf_formula(ctag.formula);
@@ -119,8 +123,8 @@ var parse_content_xml = (function() {
if(textp) q.w = textp;
if(!isstub || opts.cellStubs) {
if(!(opts.sheetRows && opts.sheetRows < R)) {
- ws[get_utils().encode_cell({r:R,c:C})] = q;
- while(--rept > 0) ws[get_utils().encode_cell({r:R,c:++C})] = dup(q);
+ ws[encode_cell({r:R,c:C})] = q;
+ while(--rept > 0) ws[encode_cell({r:R,c:++C})] = dup(q);
if(range.e.c <= C) range.e.c = C;
}
} else { C += rept; rept = 0; }
diff --git a/odsbits/70_content.js b/bits/81_writeods.js
similarity index 97%
rename from odsbits/70_content.js
rename to bits/81_writeods.js
index 323f34f..df30661 100644
--- a/odsbits/70_content.js
+++ b/bits/81_writeods.js
@@ -4,13 +4,13 @@ var write_content_xml/*:{(wb:any, opts:any):string}*/ = (function() {
/* Section 9 Tables */
var o = [];
o.push(' \n');
- var R=0,C=0, range = get_utils().decode_range(ws['!ref']);
+ var R=0,C=0, range = decode_range(ws['!ref']);
for(R = 0; R < range.s.r; ++R) o.push(' \n');
for(; R <= range.e.r; ++R) {
o.push(' \n');
for(C=0; C < range.s.c; ++C) o.push(null_cell_xml);
for(; C <= range.e.c; ++C) {
- var ref = get_utils().encode_cell({r:R, c:C}), cell = ws[ref];
+ var ref = encode_cell({r:R, c:C}), cell = ws[ref];
if(cell) switch(cell.t) {
case 'b': o.push(' ' + (cell.v ? 'TRUE' : 'FALSE') + '\n'); break;
case 'n': o.push(' ' + (cell.w||cell.v) + '\n'); break;
diff --git a/bits/83_ods.js b/bits/83_ods.js
index 0333b9b..e0e0702 100644
--- a/bits/83_ods.js
+++ b/bits/83_ods.js
@@ -1,21 +1,44 @@
-/* Helper functions to call out to ODS */
+/* Part 3: Packages */
+function parse_ods(zip/*:ZIPFile*/, opts/*:?ParseOpts*/) {
+ opts = opts || ({}/*:any*/);
+ var ods = !!safegetzipfile(zip, 'objectdata');
+ if(ods) var manifest = parse_manifest(getzipdata(zip, 'META-INF/manifest.xml'), opts);
+ var content = getzipdata(zip, 'content.xml');
+ if(!content) throw new Error("Missing content.xml in " + (ods ? "ODS" : "UOF")+ " file");
+ return parse_content_xml(ods ? content : utf8read(content), opts);
+}
+function parse_fods(data/*:string*/, opts/*:?ParseOpts*/) {
+ return parse_content_xml(data, opts);
+}
-function get_ods() {
- if(typeof module !== "undefined" && typeof require !== 'undefined' && typeof ODS === 'undefined') ODS = require('./ods.js');
- return ODS;
-}
-function parse_ods(zip, opts) {
- get_ods();
- if(typeof ODS === 'undefined' || !ODS.parse_ods) throw new Error("Unsupported ODS");
- return ODS.parse_ods(zip, opts);
-}
-function write_ods(wb, opts) {
- get_ods();
- if(typeof ODS === 'undefined' || !ODS.write_ods) throw new Error("Unsupported ODS");
- return ODS.write_ods(wb, opts);
-}
-function parse_fods(data, opts) {
- get_ods();
- if(typeof ODS === 'undefined' || !ODS.parse_fods) throw new Error("Unsupported ODS");
- return ODS.parse_fods(data, opts);
+function write_ods(wb/*:any*/, opts/*:any*/) {
+ if(opts.bookType == "fods") return write_content_xml(wb, opts);
+
+ /*:: if(!jszip) throw new Error("JSZip is not available"); */
+ var zip = new jszip();
+ var f = "";
+
+ var manifest/*:Array >*/ = [];
+ var rdf = [];
+
+ /* 3:3.3 and 2:2.2.4 */
+ f = "mimetype";
+ zip.file(f, "application/vnd.oasis.opendocument.spreadsheet");
+
+ /* Part 1 Section 2.2 Documents */
+ f = "content.xml";
+ zip.file(f, write_content_xml(wb, opts));
+ manifest.push([f, "text/xml"]);
+ rdf.push([f, "ContentFile"]);
+
+ /* Part 3 Section 6 Metadata Manifest File */
+ f = "manifest.rdf";
+ zip.file(f, write_rdf(rdf, opts));
+ manifest.push([f, "application/rdf+xml"]);
+
+ /* Part 3 Section 4 Manifest File */
+ f = "META-INF/manifest.xml";
+ zip.file(f, write_manifest(manifest, opts));
+
+ return zip;
}
diff --git a/bits/98_exports.js b/bits/98_exports.js
index df1acd6..8e9fd8a 100644
--- a/bits/98_exports.js
+++ b/bits/98_exports.js
@@ -1,4 +1,7 @@
XLSX.parse_xlscfb = parse_xlscfb;
+XLSX.parse_ods = parse_ods;
+XLSX.parse_fods = parse_fods;
+XLSX.write_ods = write_ods;
XLSX.parse_zip = parse_zip;
XLSX.read = readSync; //xlsread
XLSX.readFile = readFileSync; //readFile
diff --git a/bits/99_footer.js b/bits/99_footer.js
index 50e5f5e..0670c54 100644
--- a/bits/99_footer.js
+++ b/bits/99_footer.js
@@ -1,2 +1,5 @@
})(typeof exports !== 'undefined' ? exports : XLSX);
+/*exported XLS */
var XLS = XLSX;
+/*exported ODS */
+var ODS = XLSX;
diff --git a/demos/browserify/browserify.html b/demos/browserify/browserify.html
index 2b40711..2c72df8 100644
--- a/demos/browserify/browserify.html
+++ b/demos/browserify/browserify.html
@@ -41,6 +41,8 @@ Use readAsBinaryString: (when available)
-
-