version bump 0.7.11: ODS hooks

- ODS hooks + very basic parser (ods.js)
- handle implicit row/col spec (h/t @EarlJS, see http://git.io/2lwOuA)
This commit is contained in:
SheetJS 2014-10-09 22:22:38 -04:00
parent 5942587509
commit 61d2e55cc6
41 changed files with 1235 additions and 68 deletions

@ -21,3 +21,5 @@ test.js
.jscs.json
.gitmodules
.travis.yml
bits/
odsbits/

@ -1,7 +1,7 @@
language: node_js
node_js:
- "0.11"
- "0.10.30"
- "0.10"
- "0.8"
before_install:
- "npm install -g mocha"

@ -1,15 +1,21 @@
LIB=xlsx
DEPS=$(sort $(wildcard bits/*.js))
TARGET=$(LIB).js
FMT=xlsx xlsm xlsb misc full
FMT=xlsx xlsm xlsb ods misc full
REQS=jszip.js
ADDONS=dist/cpexcel.js
AUXTARGETS=ods.js
ULIB=$(shell echo $(LIB) | tr a-z A-Z)
DEPS=$(sort $(wildcard bits/*.js))
TARGET=$(LIB).js
.PHONY: all
all: $(TARGET) $(AUXTARGETS)
$(TARGET): $(DEPS)
cat $^ | tr -d '\15\32' > $@
bits/01_version.js: package.json
echo "XLSX.version = '"`grep version package.json | awk '{gsub(/[^0-9a-z\.-]/,"",$$2); print $$2}'`"';" > $@
echo "$(ULIB).version = '"`grep version package.json | awk '{gsub(/[^0-9a-z\.-]/,"",$$2); print $$2}'`"';" > $@
.PHONY: clean
clean:
@ -45,8 +51,8 @@ $(TESTFMT): test_%:
.PHONY: lint
lint: $(TARGET)
jshint --show-non-errors $(TARGET)
jscs $(TARGET)
jshint --show-non-errors $(TARGET) $(AUXTARGETS)
jscs $(TARGET) $(AUXTARGETS)
.PHONY: test-osx
test-osx:
@ -85,7 +91,19 @@ dist: dist-deps $(TARGET) bower.json
uglifyjs $(REQS) $(TARGET) -o dist/$(LIB).core.min.js --source-map dist/$(LIB).core.min.map --preamble "$$(head -n 1 bits/00_header.js)"
uglifyjs $(REQS) $(ADDONS) $(TARGET) -o dist/$(LIB).full.min.js --source-map dist/$(LIB).full.min.map --preamble "$$(head -n 1 bits/00_header.js)"
.PHONY: aux
aux: $(AUXTARGETS)
.PHONY: ods
ods: ods.js
ODSDEPS=$(sort $(wildcard odsbits/*.js))
ods.js: $(ODSDEPS)
cat $(ODSDEPS) | tr -d '\15\32' > $@
cp ods.js dist/ods.js
.PHONY: dist-deps
dist-deps:
dist-deps: ods.js
cp node_modules/codepage/dist/cpexcel.full.js dist/cpexcel.js
cp jszip.js dist/jszip.js
cp ods.js dist/ods.js

@ -1,7 +1,7 @@
# xlsx
Parser and writer for Excel 2007+ (XLSX/XLSM/XLSB) files. Pure-JS cleanroom
implementation from the Office Open XML spec, [MS-XLSB], and related documents.
Parser and writer for Excel 2007+ (XLSX/XLSM/XLSB) files and parser for ODS files.
Pure-JS cleanroom implementation from the Office Open XML spec, [MS-XLSB], and related documents.
## Installation
@ -36,6 +36,8 @@ be included directly:
<!-- international support from https://github.com/sheetjs/js-codepage -->
<script src="dist/cpexcel.js"></script>
<!-- ODS support -->
<script src="dist/ods.js"></script>
An appropriate version for each dependency is included in the dist/ directory.
@ -458,6 +460,9 @@ OSP-covered specifications:
- [MS-OE376]: Office Implementation Information for ECMA-376 Standards Support
- [MS-XLDM]: Spreadsheet Data Model File Format
Open Document Format for Office Applications Version 1.2 (29 September 2011)
## Badges
[![Build Status](https://travis-ci.org/SheetJS/js-xlsx.svg?branch=master)](https://travis-ci.org/SheetJS/js-xlsx)

@ -1 +1 @@
XLSX.version = '0.7.10';
XLSX.version = '0.7.11';

@ -13,11 +13,17 @@ function getdata(data) {
return null;
}
function getzipfile(zip, file) {
function safegetzipfile(zip, file) {
var f = file; if(zip.files[f]) return zip.files[f];
f = file.toLowerCase(); if(zip.files[f]) return zip.files[f];
f = f.replace(/\//g,'\\'); if(zip.files[f]) return zip.files[f];
throw new Error("Cannot find file " + file + " in zip");
return null;
}
function getzipfile(zip, file) {
var o = safegetzipfile(zip, file);
if(o == null) throw new Error("Cannot find file " + file + " in zip");
return o;
}
function getzipdata(zip, file, safe) {

@ -148,13 +148,13 @@ function write_ws_xml_cell(cell, ref, ws, opts, idx, wb) {
}
var parse_ws_xml_data = (function parse_ws_xml_data_factory() {
var cellregex = /<(?:\w+:)?c /, rowregex = /<\/(?:\w+:)?row>/;
var cellregex = /<(?:\w+:)?c[ >]/, rowregex = /<\/(?:\w+:)?row>/;
var rregex = /r=["']([^"']*)["']/, isregex = /<is>([\S\s]*?)<\/is>/;
var match_v = matchtag("v"), match_f = matchtag("f");
return function parse_ws_xml_data(sdata, s, opts, guess) {
var ri = 0, x = "", cells = [], cref = [], idx = 0, i=0, cc=0, d="", p;
var tag;
var tag, tagr = 0, tagc = 0;
var sstr;
var fmtid = 0, fillid = 0, do_format = Array.isArray(styles.CellXf), cf;
for(var marr = sdata.split(rowregex), mt = 0, marrlen = marr.length; mt != marrlen; ++mt) {
@ -165,18 +165,19 @@ return function parse_ws_xml_data(sdata, s, opts, guess) {
/* 18.3.1.73 row CT_Row */
for(ri = 0; ri < xlen; ++ri) if(x.charCodeAt(ri) === 62) break; ++ri;
tag = parsexmltag(x.substr(0,ri), true);
var tagr = parseInt(tag.r, 10);
/* SpreadSheetGear uses implicit r/c */
tagr = typeof tag.r !== 'undefined' ? parseInt(tag.r, 10) : tagr+1; tagc = -1;
if(opts.sheetRows && opts.sheetRows < tagr) continue;
if(guess.s.r > tagr - 1) guess.s.r = tagr - 1;
if(guess.e.r < tagr - 1) guess.e.r = tagr - 1;
/* 18.3.1.4 c CT_Cell */
cells = x.substr(ri).split(cellregex);
for(ri = 1; ri != cells.length; ++ri) {
for(ri = typeof tag.r === 'undefined' ? 0 : 1; ri != cells.length; ++ri) {
x = cells[ri].trim();
if(x.length === 0) continue;
cref = x.match(rregex); idx = ri; i=0; cc=0;
x = "<c " + x;
x = "<c " + (x.substr(0,1)=="<"?">":"") + x;
if(cref !== null && cref.length === 2) {
idx = 0; d=cref[1];
for(i=0; i != d.length; ++i) {
@ -184,10 +185,11 @@ return function parse_ws_xml_data(sdata, s, opts, guess) {
idx = 26*idx + cc;
}
--idx;
}
tagc = idx;
} else ++tagc;
for(i = 0; i != x.length; ++i) if(x.charCodeAt(i) === 62) break; ++i;
tag = parsexmltag(x.substr(0,i), true);
if(!tag.r) tag.r = utils.encode_cell({r:tagr-1, c:tagc});
d = x.substr(i);
p = {t:""};

6
bits/83_parseods.js Normal file

@ -0,0 +1,6 @@
/* Helper function to call out to ODS parser */
function parse_ods(zip, opts) {
if(typeof module !== "undefined" && typeof require !== 'undefined' && typeof ODS === 'undefined') ODS = require('./dist/od' + 's');
if(typeof ODS === 'undefined' || !ODS.parse_ods) throw new Error("Unsupported ODS");
return ODS.parse_ods(zip, opts);
}

@ -19,6 +19,10 @@ function parse_zip(zip, opts) {
opts = opts || {};
fix_read_opts(opts);
reset_cp();
/* OpenDocument Part 3 Section 2.2.1 OpenDocument Package */
if(safegetzipfile(zip, 'META-INF/manifest.xml')) return parse_ods(zip, opts);
var entries = keys(zip.files).filter(nodirs).sort();
var dir = parse_ct(getzipdata(zip, '[Content_Types].xml'), opts);
var xlsb = false;

@ -2,7 +2,7 @@
"name": "js-xlsx",
"homepage": "https://github.com/SheetJS/js-xlsx",
"main": "dist/xlsx.js",
"version": "0.7.10",
"version": "0.7.11",
"ignore": [
"bin",
"bits",
@ -14,6 +14,7 @@
"xlsx",
"xlsm",
"xlsb",
"ods",
"js-xlsx"
]
}

352
dist/ods.js vendored Normal file

@ -0,0 +1,352 @@
/* ods.js (C) 2014 SheetJS -- http://sheetjs.com */
/* vim: set ts=2: */
/*jshint -W041 */
var ODS = {};
(function make_ods(ODS) {
/* Open Document Format for Office Applications (OpenDocument) Version 1.2 */
var get_utils = function() {
if(typeof XLSX !== 'undefined') return XLSX.utils;
if(typeof module !== "undefined" && typeof require !== 'undefined') return require('xl' + 'sx').utils;
throw new Error("Cannot find XLSX utils");
};
var has_buf = (typeof Buffer !== 'undefined');
function getdata(data) {
if(!data) return null;
if(data.data) return data.data;
if(data.asNodeBuffer && has_buf) return data.asNodeBuffer().toString('binary');
if(data.asBinary) return data.asBinary();
if(data._data && data._data.getContent) return cc2str(Array.prototype.slice.call(data._data.getContent(),0));
return null;
}
function safegetzipfile(zip, file) {
var f = file; if(zip.files[f]) return zip.files[f];
f = file.toLowerCase(); if(zip.files[f]) return zip.files[f];
f = f.replace(/\//g,'\\'); if(zip.files[f]) return zip.files[f];
return null;
}
function getzipfile(zip, file) {
var o = safegetzipfile(zip, file);
if(o == null) throw new Error("Cannot find file " + file + " in zip");
return o;
}
function getzipdata(zip, file, safe) {
if(!safe) return getdata(getzipfile(zip, file));
if(!file) return null;
try { return getzipdata(zip, file); } catch(e) { return null; }
}
var _fs, jszip;
if(typeof JSZip !== 'undefined') jszip = JSZip;
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
if(has_buf && typeof jszip === 'undefined') jszip = require('js'+'zip');
if(typeof jszip === 'undefined') jszip = require('./js'+'zip').JSZip;
_fs = require('f'+'s');
}
}
var attregexg=/\b[\w:-]+=["'][^"]*['"]/g;
var tagregex=/<[^>]*>/g;
var nsregex=/<\w*:/, nsregex2 = /<(\/?)\w+:/;
function parsexmltag(tag, skip_root) {
var z = [];
var eq = 0, c = 0;
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="";
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;
}
return z;
}
function strip_ns(x) { return x.replace(nsregex2, "<$1"); }
var encodings = {
'&quot;': '"',
'&apos;': "'",
'&gt;': '>',
'&lt;': '<',
'&amp;': '&'
};
var rencoding = {
'"': '&quot;',
"'": '&apos;',
'>': '&gt;',
'<': '&lt;',
'&': '&amp;'
};
var rencstr = "&<>'\"".split("");
// TODO: CP remap (need to read file version to determine OS)
var encregex = /&[a-z]*;/g, coderegex = /_x([\da-fA-F]+)_/g;
function unescapexml(text){
var s = text + '';
return s.replace(encregex, function($$) { return encodings[$$]; }).replace(coderegex,function(m,c) {return String.fromCharCode(parseInt(c,16));});
}
var decregex=/[&<>'"]/g, charegex = /[\u0000-\u0008\u000b-\u001f]/g;
function escapexml(text){
var s = text + '';
return s.replace(decregex, function(y) { return rencoding[y]; }).replace(charegex,function(s) { return "_x" + ("000"+s.charCodeAt(0).toString(16)).substr(-4) + "_";});
}
function parsexmlbool(value, tag) {
switch(value) {
case '1': case 'true': case 'TRUE': return true;
/* case '0': case 'false': case 'FALSE':*/
default: return false;
}
}
function datenum(v) {
var epoch = Date.parse(v);
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;
}
/* copied from js-xls (C) SheetJS Apache2 license */
function xlml_normalize(d) {
if(has_buf && Buffer.isBuffer(d)) return d.toString('utf8');
if(typeof d === 'string') return d;
throw "badf";
}
var xlmlregex = /<(\/?)([a-z0-9]*:|)([\w-]+)[^>]*>/mg;
/* Part 3 Section 4 Manifest File */
var CT_ODS = "application/vnd.oasis.opendocument.spreadsheet";
var parse_manifest = function(d, opts) {
var str = xlml_normalize(d);
var Rn;
var FEtag;
while((Rn = xlmlregex.exec(str))) switch(Rn[3]) {
case 'manifest': break; // 4.2 <manifest:manifest>
case 'file-entry': // 4.3 <manifest:file-entry>
FEtag = parsexmltag(Rn[0]);
if(FEtag.path == '/' && FEtag.type !== CT_ODS) throw new Error("This OpenDocument is not a spreadsheet");
break;
case 'encryption-data': // 4.4 <manifest:encryption-data>
case 'algorithm': // 4.5 <manifest:algorithm>
case 'start-key-generation': // 4.6 <manifest:start-key-generation>
case 'key-derivation': // 4.7 <manifest:key-derivation>
throw new Error("Unsupported ODS Encryption");
default: throw Rn;
}
};
var parse_text_p = function(text, tag) {
return text;
};
var parse_content_xml = (function() {
var number_formats = {
/* ods name: [short ssf fmt, long ssf fmt] */
day: ["d", "dd"],
month: ["m", "mm"],
year: ["y", "yy"],
hours: ["h", "hh"],
minutes: ["m", "mm"],
seconds: ["s", "ss"],
"am-pm": ["A/P", "AM/PM"],
"day-of-week": ["ddd", "dddd"]
};
return function pcx(d, opts) {
var str = xlml_normalize(d);
var state = [], tmp;
var tag;
var NFtag, NF, pidx;
var sheetag;
var Sheets = {}, SheetNames = [], ws = {};
var Rn, q;
var ctag;
var textp, textpidx, textptag;
var R, C, range = {s: {r:1000000,c:10000000}, e: {r:0, c:0}};
var number_format_map = {};
while((Rn = xlmlregex.exec(str))) switch(Rn[3]) {
case 'table': // 9.1.2 <table:table>
if(Rn[1]==='/') {
if(range.e.c >= range.s.c && range.e.r >= range.s.r) ws['!ref'] = get_utils().encode_range(range);
SheetNames.push(sheetag.name);
Sheets[sheetag.name] = ws;
}
else if(Rn[0].charAt(Rn[0].length-2) !== '/') {
sheetag = parsexmltag(Rn[0]);
R = C = -1;
range.s.r = range.s.c = 10000000; range.e.r = range.e.c = 0;
ws = {};
}
break;
case 'table-row':
if(Rn[1] === '/') break;
++R; C = -1; break; // 9.1.3 <table:table-row>
case 'table-cell':
if(Rn[0].charAt(Rn[0].length-2) === '/') { ++C; break; } /* stub */
if(Rn[1]!=='/') {
++C;
if(C > range.e.c) range.e.c = C;
if(R > range.e.r) range.e.r = R;
if(C < range.s.c) range.s.c = C;
if(R < range.s.r) range.s.r = R;
ctag = parsexmltag(Rn[0]);
q = {t:ctag['value-type'], v:null};
/* 19.385 office:value-type */
switch(q.t) {
case 'boolean': q.t = 'b'; q.v = parsexmlbool(ctag['boolean-value']); break;
case 'float': q.t = 'n'; q.v = parseFloat(ctag.value); break;
case 'percentage': q.t = 'n'; q.v = parseFloat(ctag.value); break;
case 'date': q.t = 'n'; q.v = datenum(ctag['date-value']); q.z = 'm/d/yy'; break;
case 'time': q.t = 'n'; q.v = parse_isodur(ctag['time-value'])/86400; break;
case 'string': q.t = 'str'; break;
default: throw new Error('Unsupported value type ' + q.t);
}
} else {
if(q.t === 'str') q.v = textp;
if(textp) q.w = textp;
ws[get_utils().encode_cell({r:R,c:C})] = q;
q = null;
}
break; // 9.1.4 <table:table-cell>
case 'document-content': // 3.1.3.2 <office:document-content>
case 'spreadsheet': // 3.7 <office:spreadsheet>
case 'scripts': // 3.12 <office:scripts>
case 'font-face-decls': // 3.14 <office:font-face-decls>
if(Rn[1]==='/'){if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp;}
else if(Rn[0].charAt(Rn[0].length-2) !== '/') state.push([Rn[3], true]);
break;
case 'number-style': // 16.27.2 <number:number-style>
case 'percentage-style': // 16.27.9 <number:percentage-style>
case 'date-style': // 16.27.10 <number:date-style>
case 'time-style': // 16.27.18 <number:time-style>
if(Rn[1]==='/'){
number_format_map[NFtag.name] = NF;
if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp;
} else if(Rn[0].charAt(Rn[0].length-2) !== '/') {
NF = "";
NFtag = parsexmltag(Rn[0]);
state.push([Rn[3], true]);
} break;
case 'script': break; // 3.13 <office:script>
case 'automatic-styles': break; // 3.15.3 <office:automatic-styles>
case 'style': break; // 16.2 <style:style>
case 'font-face': break; // 16.21 <style:font-face>
case 'paragraph-properties': break; // 17.6 <style:paragraph-properties>
case 'table-properties': break; // 17.15 <style:table-properties>
case 'table-column-properties': break; // 17.16 <style:table-column-properties>
case 'table-row-properties': break; // 17.17 <style:table-row-properties>
case 'table-cell-properties': break; // 17.18 <style:table-cell-properties>
case 'number': // 16.27.3 <number:number>
switch(state[state.length-1][0]) {
case 'time-style':
case 'date-style':
tag = parsexmltag(Rn[0]);
NF += number_formats[Rn[3]][tag.style==='long'?1:0]; break;
} break;
case 'day': // 16.27.11 <number:day>
case 'month': // 16.27.12 <number:month>
case 'year': // 16.27.13 <number:year>
case 'era': // 16.27.14 <number:era>
case 'day-of-week': // 16.27.15 <number:day-of-week>
case 'week-of-year': // 16.27.16 <number:week-of-year>
case 'quarter': // 16.27.17 <number:quarter>
case 'hours': // 16.27.19 <number:hours>
case 'minutes': // 16.27.20 <number:minutes>
case 'seconds': // 16.27.21 <number:seconds>
case 'am-pm': // 16.27.22 <number:am-pm>
switch(state[state.length-1][0]) {
case 'time-style':
case 'date-style':
tag = parsexmltag(Rn[0]);
NF += number_formats[Rn[3]][tag.style==='long'?1:0]; break;
} break;
case 'boolean-style': break; // 16.27.23 <number:boolean-style>
case 'boolean': break; // 16.27.24 <number:boolean>
case 'text-style': break; // 16.27.25 <number:text-style>
case 'text': // 16.27.26 <number:text>
if(Rn[0].substr(-2) === "/>") break;
else if(Rn[1]==="/") switch(state[state.length-1][0]) {
case 'number-style':
case 'date-style':
case 'time-style':
NF += str.slice(pidx, Rn.index);
break;
}
else pidx = Rn.index + Rn[0].length;
break;
case 'text-content': break; // 16.27.27 <number:text-content>
case 'text-properties': break; // 16.27.27 <style:text-properties>
case 'body': break; // 3.3 16.9.6 19.726.3
case 'forms': break; // 12.25.2 13.2
case 'table-column': break; // 9.1.6 <table:table-column>
case 'graphic-properties': break;
case 'calculation-settings': break; // 9.4.1 <table:calculation-settings>
case 'named-expressions': break; // 9.4.11 <table:named-expressions>
case 'named-range': break; // 9.4.11 <table:named-range>
case 'span': break; // <text:span>
case 'p':
if(Rn[1]==='/') textp = parse_text_p(str.slice(textpidx,Rn.index), textptag);
else { textptag = parsexmltag(Rn[0]); textpidx = Rn.index + Rn[0].length; }
break; // <text:p>
case 's': break; // <text:s>
case 'date': break; // <*:date>
case 'annotation': break;
default: throw Rn;
}
var out = {
Sheets: Sheets,
SheetNames: SheetNames
};
return out;
};
})();
/* Part 3: Packages */
var parse_ods = function(zip, opts) {
//var manifest = parse_manifest(getzipdata(zip, 'META-INF/manifest.xml'));
return parse_content_xml(getzipdata(zip, 'content.xml'));
};
ODS.parse_ods = parse_ods;
})(typeof exports !== 'undefined' ? exports : ODS);

10
dist/xlsx.core.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

38
dist/xlsx.js vendored

@ -3,7 +3,7 @@
/*jshint -W041 */
var XLSX = {};
(function(XLSX){
XLSX.version = '0.7.10';
XLSX.version = '0.7.11';
var current_codepage = 1252, current_cptable;
if(typeof module !== "undefined" && typeof require !== 'undefined') {
if(typeof cptable === 'undefined') cptable = require('./dist/cpexcel');
@ -847,11 +847,17 @@ function getdata(data) {
return null;
}
function getzipfile(zip, file) {
function safegetzipfile(zip, file) {
var f = file; if(zip.files[f]) return zip.files[f];
f = file.toLowerCase(); if(zip.files[f]) return zip.files[f];
f = f.replace(/\//g,'\\'); if(zip.files[f]) return zip.files[f];
throw new Error("Cannot find file " + file + " in zip");
return null;
}
function getzipfile(zip, file) {
var o = safegetzipfile(zip, file);
if(o == null) throw new Error("Cannot find file " + file + " in zip");
return o;
}
function getzipdata(zip, file, safe) {
@ -2934,13 +2940,13 @@ function write_ws_xml_cell(cell, ref, ws, opts, idx, wb) {
}
var parse_ws_xml_data = (function parse_ws_xml_data_factory() {
var cellregex = /<(?:\w+:)?c /, rowregex = /<\/(?:\w+:)?row>/;
var cellregex = /<(?:\w+:)?c[ >]/, rowregex = /<\/(?:\w+:)?row>/;
var rregex = /r=["']([^"']*)["']/, isregex = /<is>([\S\s]*?)<\/is>/;
var match_v = matchtag("v"), match_f = matchtag("f");
return function parse_ws_xml_data(sdata, s, opts, guess) {
var ri = 0, x = "", cells = [], cref = [], idx = 0, i=0, cc=0, d="", p;
var tag;
var tag, tagr = 0, tagc = 0;
var sstr;
var fmtid = 0, fillid = 0, do_format = Array.isArray(styles.CellXf), cf;
for(var marr = sdata.split(rowregex), mt = 0, marrlen = marr.length; mt != marrlen; ++mt) {
@ -2951,18 +2957,19 @@ return function parse_ws_xml_data(sdata, s, opts, guess) {
/* 18.3.1.73 row CT_Row */
for(ri = 0; ri < xlen; ++ri) if(x.charCodeAt(ri) === 62) break; ++ri;
tag = parsexmltag(x.substr(0,ri), true);
var tagr = parseInt(tag.r, 10);
/* SpreadSheetGear uses implicit r/c */
tagr = typeof tag.r !== 'undefined' ? parseInt(tag.r, 10) : tagr+1; tagc = -1;
if(opts.sheetRows && opts.sheetRows < tagr) continue;
if(guess.s.r > tagr - 1) guess.s.r = tagr - 1;
if(guess.e.r < tagr - 1) guess.e.r = tagr - 1;
/* 18.3.1.4 c CT_Cell */
cells = x.substr(ri).split(cellregex);
for(ri = 1, cellen = cells.length; ri != cellen; ++ri) {
for(ri = typeof tag.r === 'undefined' ? 0 : 1; ri != cells.length; ++ri) {
x = cells[ri].trim();
if(x.length === 0) continue;
cref = x.match(rregex); idx = ri; i=0; cc=0;
x = "<c " + x;
x = "<c " + (x.substr(0,1)=="<"?">":"") + x;
if(cref !== null && cref.length === 2) {
idx = 0; d=cref[1];
for(i=0; i != d.length; ++i) {
@ -2970,10 +2977,11 @@ return function parse_ws_xml_data(sdata, s, opts, guess) {
idx = 26*idx + cc;
}
--idx;
}
tagc = idx;
} else ++tagc;
for(i = 0; i != x.length; ++i) if(x.charCodeAt(i) === 62) break; ++i;
tag = parsexmltag(x.substr(0,i), true);
if(!tag.r) tag.r = utils.encode_cell({r:tagr-1, c:tagc});
d = x.substr(i);
p = {t:""};
@ -4804,6 +4812,12 @@ var RecordEnum = {
};
var evert_RE = evert_key(RecordEnum, 'n');
/* Helper function to call out to ODS parser */
function parse_ods(zip, opts) {
if(typeof module !== "undefined" && typeof require !== 'undefined' && typeof ODS === 'undefined') ODS = require('./dist/od' + 's');
if(typeof ODS === 'undefined' || !ODS.parse_ods) throw new Error("Unsupported ODS");
return ODS.parse_ods(zip, opts);
}
function fix_opts_func(defaults) {
return function fix_opts(opts) {
for(var i = 0; i != defaults.length; ++i) {
@ -4861,6 +4875,10 @@ function parse_zip(zip, opts) {
opts = opts || {};
fix_read_opts(opts);
reset_cp();
/* OpenDocument Part 3 Section 2.2.1 OpenDocument Package */
if(safegetzipfile(zip, 'META-INF/manifest.xml')) return parse_ods(zip, opts);
var entries = keys(zip.files).filter(nodirs).sort();
var dir = parse_ct(getzipdata(zip, '[Content_Types].xml'), opts);
var xlsb = false;

10
dist/xlsx.min.js vendored

File diff suppressed because one or more lines are too long

2
dist/xlsx.min.map vendored

File diff suppressed because one or more lines are too long

@ -32,7 +32,7 @@ Output Format:
<input type="radio" name="format" value="json"> JSON<br>
<input type="radio" name="format" value="form"> FORMULAE<br> -->
<div id="drop">Drop an XLSX or XLSM or XLSB file here to see sheet data</div>
<div id="drop">Drop an XLSX / XLSM / XLSB / ODS file here to see sheet data</div>
<p><input type="file" name="xlfile" id="xlf" /> ... or click here to select a file</p>
<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 />
@ -47,6 +47,8 @@ Use readAsBinaryString: (when available) <input type="checkbox" name="userabs" c
<script src="shim.js"></script>
<script src="jszip.js"></script>
<script src="xlsx.js"></script>
<!-- uncomment the next line here and in xlsxworker.js for ODS support -->
<script src="dist/ods.js"></script>
<script>
var rABS = typeof FileReader !== "undefined" && typeof FileReader.prototype !== "undefined" && typeof FileReader.prototype.readAsBinaryString !== "undefined";
if(!rABS) {

@ -14,6 +14,7 @@
"xlsx",
"xlsm",
"xlsb",
"ods",
"js-xlsx"
]
}

352
ods.js Normal file

@ -0,0 +1,352 @@
/* ods.js (C) 2014 SheetJS -- http://sheetjs.com */
/* vim: set ts=2: */
/*jshint -W041 */
var ODS = {};
(function make_ods(ODS) {
/* Open Document Format for Office Applications (OpenDocument) Version 1.2 */
var get_utils = function() {
if(typeof XLSX !== 'undefined') return XLSX.utils;
if(typeof module !== "undefined" && typeof require !== 'undefined') return require('xl' + 'sx').utils;
throw new Error("Cannot find XLSX utils");
};
var has_buf = (typeof Buffer !== 'undefined');
function getdata(data) {
if(!data) return null;
if(data.data) return data.data;
if(data.asNodeBuffer && has_buf) return data.asNodeBuffer().toString('binary');
if(data.asBinary) return data.asBinary();
if(data._data && data._data.getContent) return cc2str(Array.prototype.slice.call(data._data.getContent(),0));
return null;
}
function safegetzipfile(zip, file) {
var f = file; if(zip.files[f]) return zip.files[f];
f = file.toLowerCase(); if(zip.files[f]) return zip.files[f];
f = f.replace(/\//g,'\\'); if(zip.files[f]) return zip.files[f];
return null;
}
function getzipfile(zip, file) {
var o = safegetzipfile(zip, file);
if(o == null) throw new Error("Cannot find file " + file + " in zip");
return o;
}
function getzipdata(zip, file, safe) {
if(!safe) return getdata(getzipfile(zip, file));
if(!file) return null;
try { return getzipdata(zip, file); } catch(e) { return null; }
}
var _fs, jszip;
if(typeof JSZip !== 'undefined') jszip = JSZip;
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
if(has_buf && typeof jszip === 'undefined') jszip = require('js'+'zip');
if(typeof jszip === 'undefined') jszip = require('./js'+'zip').JSZip;
_fs = require('f'+'s');
}
}
var attregexg=/\b[\w:-]+=["'][^"]*['"]/g;
var tagregex=/<[^>]*>/g;
var nsregex=/<\w*:/, nsregex2 = /<(\/?)\w+:/;
function parsexmltag(tag, skip_root) {
var z = [];
var eq = 0, c = 0;
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="";
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;
}
return z;
}
function strip_ns(x) { return x.replace(nsregex2, "<$1"); }
var encodings = {
'&quot;': '"',
'&apos;': "'",
'&gt;': '>',
'&lt;': '<',
'&amp;': '&'
};
var rencoding = {
'"': '&quot;',
"'": '&apos;',
'>': '&gt;',
'<': '&lt;',
'&': '&amp;'
};
var rencstr = "&<>'\"".split("");
// TODO: CP remap (need to read file version to determine OS)
var encregex = /&[a-z]*;/g, coderegex = /_x([\da-fA-F]+)_/g;
function unescapexml(text){
var s = text + '';
return s.replace(encregex, function($$) { return encodings[$$]; }).replace(coderegex,function(m,c) {return String.fromCharCode(parseInt(c,16));});
}
var decregex=/[&<>'"]/g, charegex = /[\u0000-\u0008\u000b-\u001f]/g;
function escapexml(text){
var s = text + '';
return s.replace(decregex, function(y) { return rencoding[y]; }).replace(charegex,function(s) { return "_x" + ("000"+s.charCodeAt(0).toString(16)).substr(-4) + "_";});
}
function parsexmlbool(value, tag) {
switch(value) {
case '1': case 'true': case 'TRUE': return true;
/* case '0': case 'false': case 'FALSE':*/
default: return false;
}
}
function datenum(v) {
var epoch = Date.parse(v);
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;
}
/* copied from js-xls (C) SheetJS Apache2 license */
function xlml_normalize(d) {
if(has_buf && Buffer.isBuffer(d)) return d.toString('utf8');
if(typeof d === 'string') return d;
throw "badf";
}
var xlmlregex = /<(\/?)([a-z0-9]*:|)([\w-]+)[^>]*>/mg;
/* Part 3 Section 4 Manifest File */
var CT_ODS = "application/vnd.oasis.opendocument.spreadsheet";
var parse_manifest = function(d, opts) {
var str = xlml_normalize(d);
var Rn;
var FEtag;
while((Rn = xlmlregex.exec(str))) switch(Rn[3]) {
case 'manifest': break; // 4.2 <manifest:manifest>
case 'file-entry': // 4.3 <manifest:file-entry>
FEtag = parsexmltag(Rn[0]);
if(FEtag.path == '/' && FEtag.type !== CT_ODS) throw new Error("This OpenDocument is not a spreadsheet");
break;
case 'encryption-data': // 4.4 <manifest:encryption-data>
case 'algorithm': // 4.5 <manifest:algorithm>
case 'start-key-generation': // 4.6 <manifest:start-key-generation>
case 'key-derivation': // 4.7 <manifest:key-derivation>
throw new Error("Unsupported ODS Encryption");
default: throw Rn;
}
};
var parse_text_p = function(text, tag) {
return text;
};
var parse_content_xml = (function() {
var number_formats = {
/* ods name: [short ssf fmt, long ssf fmt] */
day: ["d", "dd"],
month: ["m", "mm"],
year: ["y", "yy"],
hours: ["h", "hh"],
minutes: ["m", "mm"],
seconds: ["s", "ss"],
"am-pm": ["A/P", "AM/PM"],
"day-of-week": ["ddd", "dddd"]
};
return function pcx(d, opts) {
var str = xlml_normalize(d);
var state = [], tmp;
var tag;
var NFtag, NF, pidx;
var sheetag;
var Sheets = {}, SheetNames = [], ws = {};
var Rn, q;
var ctag;
var textp, textpidx, textptag;
var R, C, range = {s: {r:1000000,c:10000000}, e: {r:0, c:0}};
var number_format_map = {};
while((Rn = xlmlregex.exec(str))) switch(Rn[3]) {
case 'table': // 9.1.2 <table:table>
if(Rn[1]==='/') {
if(range.e.c >= range.s.c && range.e.r >= range.s.r) ws['!ref'] = get_utils().encode_range(range);
SheetNames.push(sheetag.name);
Sheets[sheetag.name] = ws;
}
else if(Rn[0].charAt(Rn[0].length-2) !== '/') {
sheetag = parsexmltag(Rn[0]);
R = C = -1;
range.s.r = range.s.c = 10000000; range.e.r = range.e.c = 0;
ws = {};
}
break;
case 'table-row':
if(Rn[1] === '/') break;
++R; C = -1; break; // 9.1.3 <table:table-row>
case 'table-cell':
if(Rn[0].charAt(Rn[0].length-2) === '/') { ++C; break; } /* stub */
if(Rn[1]!=='/') {
++C;
if(C > range.e.c) range.e.c = C;
if(R > range.e.r) range.e.r = R;
if(C < range.s.c) range.s.c = C;
if(R < range.s.r) range.s.r = R;
ctag = parsexmltag(Rn[0]);
q = {t:ctag['value-type'], v:null};
/* 19.385 office:value-type */
switch(q.t) {
case 'boolean': q.t = 'b'; q.v = parsexmlbool(ctag['boolean-value']); break;
case 'float': q.t = 'n'; q.v = parseFloat(ctag.value); break;
case 'percentage': q.t = 'n'; q.v = parseFloat(ctag.value); break;
case 'date': q.t = 'n'; q.v = datenum(ctag['date-value']); q.z = 'm/d/yy'; break;
case 'time': q.t = 'n'; q.v = parse_isodur(ctag['time-value'])/86400; break;
case 'string': q.t = 'str'; break;
default: throw new Error('Unsupported value type ' + q.t);
}
} else {
if(q.t === 'str') q.v = textp;
if(textp) q.w = textp;
ws[get_utils().encode_cell({r:R,c:C})] = q;
q = null;
}
break; // 9.1.4 <table:table-cell>
case 'document-content': // 3.1.3.2 <office:document-content>
case 'spreadsheet': // 3.7 <office:spreadsheet>
case 'scripts': // 3.12 <office:scripts>
case 'font-face-decls': // 3.14 <office:font-face-decls>
if(Rn[1]==='/'){if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp;}
else if(Rn[0].charAt(Rn[0].length-2) !== '/') state.push([Rn[3], true]);
break;
case 'number-style': // 16.27.2 <number:number-style>
case 'percentage-style': // 16.27.9 <number:percentage-style>
case 'date-style': // 16.27.10 <number:date-style>
case 'time-style': // 16.27.18 <number:time-style>
if(Rn[1]==='/'){
number_format_map[NFtag.name] = NF;
if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp;
} else if(Rn[0].charAt(Rn[0].length-2) !== '/') {
NF = "";
NFtag = parsexmltag(Rn[0]);
state.push([Rn[3], true]);
} break;
case 'script': break; // 3.13 <office:script>
case 'automatic-styles': break; // 3.15.3 <office:automatic-styles>
case 'style': break; // 16.2 <style:style>
case 'font-face': break; // 16.21 <style:font-face>
case 'paragraph-properties': break; // 17.6 <style:paragraph-properties>
case 'table-properties': break; // 17.15 <style:table-properties>
case 'table-column-properties': break; // 17.16 <style:table-column-properties>
case 'table-row-properties': break; // 17.17 <style:table-row-properties>
case 'table-cell-properties': break; // 17.18 <style:table-cell-properties>
case 'number': // 16.27.3 <number:number>
switch(state[state.length-1][0]) {
case 'time-style':
case 'date-style':
tag = parsexmltag(Rn[0]);
NF += number_formats[Rn[3]][tag.style==='long'?1:0]; break;
} break;
case 'day': // 16.27.11 <number:day>
case 'month': // 16.27.12 <number:month>
case 'year': // 16.27.13 <number:year>
case 'era': // 16.27.14 <number:era>
case 'day-of-week': // 16.27.15 <number:day-of-week>
case 'week-of-year': // 16.27.16 <number:week-of-year>
case 'quarter': // 16.27.17 <number:quarter>
case 'hours': // 16.27.19 <number:hours>
case 'minutes': // 16.27.20 <number:minutes>
case 'seconds': // 16.27.21 <number:seconds>
case 'am-pm': // 16.27.22 <number:am-pm>
switch(state[state.length-1][0]) {
case 'time-style':
case 'date-style':
tag = parsexmltag(Rn[0]);
NF += number_formats[Rn[3]][tag.style==='long'?1:0]; break;
} break;
case 'boolean-style': break; // 16.27.23 <number:boolean-style>
case 'boolean': break; // 16.27.24 <number:boolean>
case 'text-style': break; // 16.27.25 <number:text-style>
case 'text': // 16.27.26 <number:text>
if(Rn[0].substr(-2) === "/>") break;
else if(Rn[1]==="/") switch(state[state.length-1][0]) {
case 'number-style':
case 'date-style':
case 'time-style':
NF += str.slice(pidx, Rn.index);
break;
}
else pidx = Rn.index + Rn[0].length;
break;
case 'text-content': break; // 16.27.27 <number:text-content>
case 'text-properties': break; // 16.27.27 <style:text-properties>
case 'body': break; // 3.3 16.9.6 19.726.3
case 'forms': break; // 12.25.2 13.2
case 'table-column': break; // 9.1.6 <table:table-column>
case 'graphic-properties': break;
case 'calculation-settings': break; // 9.4.1 <table:calculation-settings>
case 'named-expressions': break; // 9.4.11 <table:named-expressions>
case 'named-range': break; // 9.4.11 <table:named-range>
case 'span': break; // <text:span>
case 'p':
if(Rn[1]==='/') textp = parse_text_p(str.slice(textpidx,Rn.index), textptag);
else { textptag = parsexmltag(Rn[0]); textpidx = Rn.index + Rn[0].length; }
break; // <text:p>
case 's': break; // <text:s>
case 'date': break; // <*:date>
case 'annotation': break;
default: throw Rn;
}
var out = {
Sheets: Sheets,
SheetNames: SheetNames
};
return out;
};
})();
/* Part 3: Packages */
var parse_ods = function(zip, opts) {
//var manifest = parse_manifest(getzipdata(zip, 'META-INF/manifest.xml'));
return parse_content_xml(getzipdata(zip, 'content.xml'));
};
ODS.parse_ods = parse_ods;
})(typeof exports !== 'undefined' ? exports : ODS);

6
odsbits/00_header.js Normal file

@ -0,0 +1,6 @@
/* ods.js (C) 2014 SheetJS -- http://sheetjs.com */
/* vim: set ts=2: */
/*jshint -W041 */
var ODS = {};
(function make_ods(ODS) {
/* Open Document Format for Office Applications (OpenDocument) Version 1.2 */

5
odsbits/10_utils.js Normal file

@ -0,0 +1,5 @@
var get_utils = function() {
if(typeof XLSX !== 'undefined') return XLSX.utils;
if(typeof module !== "undefined" && typeof require !== 'undefined') return require('xl' + 'sx').utils;
throw new Error("Cannot find XLSX utils");
};

1
odsbits/20_jsutils.js Normal file

@ -0,0 +1 @@
var has_buf = (typeof Buffer !== 'undefined');

37
odsbits/21_ziputils.js Normal file

@ -0,0 +1,37 @@
function getdata(data) {
if(!data) return null;
if(data.data) return data.data;
if(data.asNodeBuffer && has_buf) return data.asNodeBuffer().toString('binary');
if(data.asBinary) return data.asBinary();
if(data._data && data._data.getContent) return cc2str(Array.prototype.slice.call(data._data.getContent(),0));
return null;
}
function safegetzipfile(zip, file) {
var f = file; if(zip.files[f]) return zip.files[f];
f = file.toLowerCase(); if(zip.files[f]) return zip.files[f];
f = f.replace(/\//g,'\\'); if(zip.files[f]) return zip.files[f];
return null;
}
function getzipfile(zip, file) {
var o = safegetzipfile(zip, file);
if(o == null) throw new Error("Cannot find file " + file + " in zip");
return o;
}
function getzipdata(zip, file, safe) {
if(!safe) return getdata(getzipfile(zip, file));
if(!file) return null;
try { return getzipdata(zip, file); } catch(e) { return null; }
}
var _fs, jszip;
if(typeof JSZip !== 'undefined') jszip = JSZip;
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
if(has_buf && typeof jszip === 'undefined') jszip = require('js'+'zip');
if(typeof jszip === 'undefined') jszip = require('./js'+'zip').JSZip;
_fs = require('f'+'s');
}
}

89
odsbits/22_xmlutils.js Normal file

@ -0,0 +1,89 @@
var attregexg=/\b[\w:-]+=["'][^"]*['"]/g;
var tagregex=/<[^>]*>/g;
var nsregex=/<\w*:/, nsregex2 = /<(\/?)\w+:/;
function parsexmltag(tag, skip_root) {
var z = [];
var eq = 0, c = 0;
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="";
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;
}
return z;
}
function strip_ns(x) { return x.replace(nsregex2, "<$1"); }
var encodings = {
'&quot;': '"',
'&apos;': "'",
'&gt;': '>',
'&lt;': '<',
'&amp;': '&'
};
var rencoding = {
'"': '&quot;',
"'": '&apos;',
'>': '&gt;',
'<': '&lt;',
'&': '&amp;'
};
var rencstr = "&<>'\"".split("");
// TODO: CP remap (need to read file version to determine OS)
var encregex = /&[a-z]*;/g, coderegex = /_x([\da-fA-F]+)_/g;
function unescapexml(text){
var s = text + '';
return s.replace(encregex, function($$) { return encodings[$$]; }).replace(coderegex,function(m,c) {return String.fromCharCode(parseInt(c,16));});
}
var decregex=/[&<>'"]/g, charegex = /[\u0000-\u0008\u000b-\u001f]/g;
function escapexml(text){
var s = text + '';
return s.replace(decregex, function(y) { return rencoding[y]; }).replace(charegex,function(s) { return "_x" + ("000"+s.charCodeAt(0).toString(16)).substr(-4) + "_";});
}
function parsexmlbool(value, tag) {
switch(value) {
case '1': case 'true': case 'TRUE': return true;
/* case '0': case 'false': case 'FALSE':*/
default: return false;
}
}
function datenum(v) {
var epoch = Date.parse(v);
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;
}

8
odsbits/23_xlml.js Normal file

@ -0,0 +1,8 @@
/* copied from js-xls (C) SheetJS Apache2 license */
function xlml_normalize(d) {
if(has_buf && Buffer.isBuffer(d)) return d.toString('utf8');
if(typeof d === 'string') return d;
throw "badf";
}
var xlmlregex = /<(\/?)([a-z0-9]*:|)([\w-]+)[^>]*>/mg;

20
odsbits/30_manifest.js Normal file

@ -0,0 +1,20 @@
/* Part 3 Section 4 Manifest File */
var CT_ODS = "application/vnd.oasis.opendocument.spreadsheet";
var parse_manifest = function(d, opts) {
var str = xlml_normalize(d);
var Rn;
var FEtag;
while((Rn = xlmlregex.exec(str))) switch(Rn[3]) {
case 'manifest': break; // 4.2 <manifest:manifest>
case 'file-entry': // 4.3 <manifest:file-entry>
FEtag = parsexmltag(Rn[0]);
if(FEtag.path == '/' && FEtag.type !== CT_ODS) throw new Error("This OpenDocument is not a spreadsheet");
break;
case 'encryption-data': // 4.4 <manifest:encryption-data>
case 'algorithm': // 4.5 <manifest:algorithm>
case 'start-key-generation': // 4.6 <manifest:start-key-generation>
case 'key-derivation': // 4.7 <manifest:key-derivation>
throw new Error("Unsupported ODS Encryption");
default: throw Rn;
}
};

3
odsbits/40_helpers.js Normal file

@ -0,0 +1,3 @@
var parse_text_p = function(text, tag) {
return text;
};

176
odsbits/60_content.js Normal file

@ -0,0 +1,176 @@
var parse_content_xml = (function() {
var number_formats = {
/* ods name: [short ssf fmt, long ssf fmt] */
day: ["d", "dd"],
month: ["m", "mm"],
year: ["y", "yy"],
hours: ["h", "hh"],
minutes: ["m", "mm"],
seconds: ["s", "ss"],
"am-pm": ["A/P", "AM/PM"],
"day-of-week": ["ddd", "dddd"]
};
return function pcx(d, opts) {
var str = xlml_normalize(d);
var state = [], tmp;
var tag;
var NFtag, NF, pidx;
var sheetag;
var Sheets = {}, SheetNames = [], ws = {};
var Rn, q;
var ctag;
var textp, textpidx, textptag;
var R, C, range = {s: {r:1000000,c:10000000}, e: {r:0, c:0}};
var number_format_map = {};
while((Rn = xlmlregex.exec(str))) switch(Rn[3]) {
case 'table': // 9.1.2 <table:table>
if(Rn[1]==='/') {
if(range.e.c >= range.s.c && range.e.r >= range.s.r) ws['!ref'] = get_utils().encode_range(range);
SheetNames.push(sheetag.name);
Sheets[sheetag.name] = ws;
}
else if(Rn[0].charAt(Rn[0].length-2) !== '/') {
sheetag = parsexmltag(Rn[0]);
R = C = -1;
range.s.r = range.s.c = 10000000; range.e.r = range.e.c = 0;
ws = {};
}
break;
case 'table-row':
if(Rn[1] === '/') break;
++R; C = -1; break; // 9.1.3 <table:table-row>
case 'table-cell':
if(Rn[0].charAt(Rn[0].length-2) === '/') { ++C; break; } /* stub */
if(Rn[1]!=='/') {
++C;
if(C > range.e.c) range.e.c = C;
if(R > range.e.r) range.e.r = R;
if(C < range.s.c) range.s.c = C;
if(R < range.s.r) range.s.r = R;
ctag = parsexmltag(Rn[0]);
q = {t:ctag['value-type'], v:null};
/* 19.385 office:value-type */
switch(q.t) {
case 'boolean': q.t = 'b'; q.v = parsexmlbool(ctag['boolean-value']); break;
case 'float': q.t = 'n'; q.v = parseFloat(ctag.value); break;
case 'percentage': q.t = 'n'; q.v = parseFloat(ctag.value); break;
case 'date': q.t = 'n'; q.v = datenum(ctag['date-value']); q.z = 'm/d/yy'; break;
case 'time': q.t = 'n'; q.v = parse_isodur(ctag['time-value'])/86400; break;
case 'string': q.t = 'str'; break;
default: throw new Error('Unsupported value type ' + q.t);
}
} else {
if(q.t === 'str') q.v = textp;
if(textp) q.w = textp;
ws[get_utils().encode_cell({r:R,c:C})] = q;
q = null;
}
break; // 9.1.4 <table:table-cell>
case 'document-content': // 3.1.3.2 <office:document-content>
case 'spreadsheet': // 3.7 <office:spreadsheet>
case 'scripts': // 3.12 <office:scripts>
case 'font-face-decls': // 3.14 <office:font-face-decls>
if(Rn[1]==='/'){if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp;}
else if(Rn[0].charAt(Rn[0].length-2) !== '/') state.push([Rn[3], true]);
break;
case 'number-style': // 16.27.2 <number:number-style>
case 'percentage-style': // 16.27.9 <number:percentage-style>
case 'date-style': // 16.27.10 <number:date-style>
case 'time-style': // 16.27.18 <number:time-style>
if(Rn[1]==='/'){
number_format_map[NFtag.name] = NF;
if((tmp=state.pop())[0]!==Rn[3]) throw "Bad state: "+tmp;
} else if(Rn[0].charAt(Rn[0].length-2) !== '/') {
NF = "";
NFtag = parsexmltag(Rn[0]);
state.push([Rn[3], true]);
} break;
case 'script': break; // 3.13 <office:script>
case 'automatic-styles': break; // 3.15.3 <office:automatic-styles>
case 'style': break; // 16.2 <style:style>
case 'font-face': break; // 16.21 <style:font-face>
case 'paragraph-properties': break; // 17.6 <style:paragraph-properties>
case 'table-properties': break; // 17.15 <style:table-properties>
case 'table-column-properties': break; // 17.16 <style:table-column-properties>
case 'table-row-properties': break; // 17.17 <style:table-row-properties>
case 'table-cell-properties': break; // 17.18 <style:table-cell-properties>
case 'number': // 16.27.3 <number:number>
switch(state[state.length-1][0]) {
case 'time-style':
case 'date-style':
tag = parsexmltag(Rn[0]);
NF += number_formats[Rn[3]][tag.style==='long'?1:0]; break;
} break;
case 'day': // 16.27.11 <number:day>
case 'month': // 16.27.12 <number:month>
case 'year': // 16.27.13 <number:year>
case 'era': // 16.27.14 <number:era>
case 'day-of-week': // 16.27.15 <number:day-of-week>
case 'week-of-year': // 16.27.16 <number:week-of-year>
case 'quarter': // 16.27.17 <number:quarter>
case 'hours': // 16.27.19 <number:hours>
case 'minutes': // 16.27.20 <number:minutes>
case 'seconds': // 16.27.21 <number:seconds>
case 'am-pm': // 16.27.22 <number:am-pm>
switch(state[state.length-1][0]) {
case 'time-style':
case 'date-style':
tag = parsexmltag(Rn[0]);
NF += number_formats[Rn[3]][tag.style==='long'?1:0]; break;
} break;
case 'boolean-style': break; // 16.27.23 <number:boolean-style>
case 'boolean': break; // 16.27.24 <number:boolean>
case 'text-style': break; // 16.27.25 <number:text-style>
case 'text': // 16.27.26 <number:text>
if(Rn[0].substr(-2) === "/>") break;
else if(Rn[1]==="/") switch(state[state.length-1][0]) {
case 'number-style':
case 'date-style':
case 'time-style':
NF += str.slice(pidx, Rn.index);
break;
}
else pidx = Rn.index + Rn[0].length;
break;
case 'text-content': break; // 16.27.27 <number:text-content>
case 'text-properties': break; // 16.27.27 <style:text-properties>
case 'body': break; // 3.3 16.9.6 19.726.3
case 'forms': break; // 12.25.2 13.2
case 'table-column': break; // 9.1.6 <table:table-column>
case 'graphic-properties': break;
case 'calculation-settings': break; // 9.4.1 <table:calculation-settings>
case 'named-expressions': break; // 9.4.11 <table:named-expressions>
case 'named-range': break; // 9.4.11 <table:named-range>
case 'span': break; // <text:span>
case 'p':
if(Rn[1]==='/') textp = parse_text_p(str.slice(textpidx,Rn.index), textptag);
else { textptag = parsexmltag(Rn[0]); textpidx = Rn.index + Rn[0].length; }
break; // <text:p>
case 's': break; // <text:s>
case 'date': break; // <*:date>
case 'annotation': break;
default: throw Rn;
}
var out = {
Sheets: Sheets,
SheetNames: SheetNames
};
return out;
};
})();

5
odsbits/80_parse.js Normal file

@ -0,0 +1,5 @@
/* Part 3: Packages */
var parse_ods = function(zip, opts) {
//var manifest = parse_manifest(getzipdata(zip, 'META-INF/manifest.xml'));
return parse_content_xml(getzipdata(zip, 'content.xml'));
};

1
odsbits/98_exports.js Normal file

@ -0,0 +1 @@
ODS.parse_ods = parse_ods;

1
odsbits/99_footer.js Normal file

@ -0,0 +1 @@
})(typeof exports !== 'undefined' ? exports : ODS);

@ -1,9 +1,9 @@
{
"name": "xlsx",
"version": "0.7.10",
"version": "0.7.11",
"author": "sheetjs",
"description": "Excel 2007+ spreadsheet (XLSB/XLSX/XLSM) parser and writer",
"keywords": [ "excel", "xlsx", "xlsb", "xlsm", "office", "spreadsheet" ],
"description": "Excel 2007+ spreadsheet (XLSB/XLSX/XLSM) and ODS parser and writer",
"keywords": [ "excel", "xlsx", "xlsb", "xlsm", "ods", "office", "spreadsheet" ],
"bin": {
"xlsx": "./bin/xlsx.njs"
},

@ -15,8 +15,7 @@ var ex = fullex;
if(process.env.FMTS === "full") process.env.FMTS = ex.join(":");
if(process.env.FMTS) ex=process.env.FMTS.split(":").map(function(x){return x[0]==="."?x:"."+x;});
var exp = ex.map(function(x){ return x + ".pending"; });
var ow = 5;
function test_file(x){return ex.indexOf(x.substr(-ow))>=0||exp.indexOf(x.substr(-8-ow))>=0;}
function test_file(x){ return ex.indexOf(x.substr(-5))>=0||exp.indexOf(x.substr(-13))>=0 || ex.indexOf(x.substr(-4))>=0||exp.indexOf(x.substr(-12))>=0; }
var files = (fs.existsSync('tests.lst') ? fs.readFileSync('tests.lst', 'utf-8').split("\n") : fs.readdirSync('test_files')).filter(test_file);
var fileA = (fs.existsSync('testA.lst') ? fs.readFileSync('testA.lst', 'utf-8').split("\n") : []).filter(test_file);
@ -92,8 +91,8 @@ function parsetest(x, wb, full, ext) {
if(!full) return;
var getfile = function(dir, x, i, type) {
var name = (dir + x + '.' + i + type);
if(x.substr(-ow) === ".xlsb") {
root = x.slice(0,-ow);
if(x.substr(-5) === ".xlsb") {
root = x.slice(0,-5);
if(!fs.existsSync(name)) name=(dir + root + '.xlsx.' + i + type);
if(!fs.existsSync(name)) name=(dir + root + '.xlsm.' + i + type);
if(!fs.existsSync(name)) name=(dir + root + '.xls.' + i + type);
@ -689,7 +688,6 @@ describe('invalid files', function() {
describe('parse', function() { [
['passwords', 'excel-reader-xlsx_error03.xlsx'],
['XLS files', 'roo_type_excel.xlsx'],
['ODS files', 'roo_type_openoffice.xlsx'],
['DOC files', 'word_doc.doc']
].forEach(function(w) { it('should fail on ' + w[0], function() {
assert.throws(function() { X.readFile(dir + w[1]); });

@ -1 +1 @@
Subproject commit 5308987eaeb01103964363cdc745b8340795d63c
Subproject commit 7940cf6da4eb8a4775f5d28a61799772a1e26e6a

@ -329,3 +329,27 @@ number_format_russian.xlsm
numfmt_1_russian.xlsm
openpyxl_r_vba-test.xlsm
pivot_table_test.xlsm
roo_Bibelbund.ods
roo_Bibelbund1.ods
roo_bbu.ods
roo_boolean.ods
roo_borders.ods
roo_comments.ods
roo_datetime.ods
roo_dreimalvier.ods
roo_emptysheets.ods
roo_formula.ods
roo_html-escape.ods
roo_matrix.ods
roo_named_cells.ods
roo_numbers1.ods
roo_only_one_sheet.ods
roo_paragraph.ods
roo_ric.ods
roo_simple_spreadsheet.ods
roo_simple_spreadsheet_from_italo.ods
roo_style.ods
roo_time-test.ods
roo_type_excel.ods.pending
roo_type_excelx.ods
roo_whitespace.ods

38
xlsx.js

@ -3,7 +3,7 @@
/*jshint -W041 */
var XLSX = {};
(function(XLSX){
XLSX.version = '0.7.10';
XLSX.version = '0.7.11';
var current_codepage = 1252, current_cptable;
if(typeof module !== "undefined" && typeof require !== 'undefined') {
if(typeof cptable === 'undefined') cptable = require('./dist/cpexcel');
@ -847,11 +847,17 @@ function getdata(data) {
return null;
}
function getzipfile(zip, file) {
function safegetzipfile(zip, file) {
var f = file; if(zip.files[f]) return zip.files[f];
f = file.toLowerCase(); if(zip.files[f]) return zip.files[f];
f = f.replace(/\//g,'\\'); if(zip.files[f]) return zip.files[f];
throw new Error("Cannot find file " + file + " in zip");
return null;
}
function getzipfile(zip, file) {
var o = safegetzipfile(zip, file);
if(o == null) throw new Error("Cannot find file " + file + " in zip");
return o;
}
function getzipdata(zip, file, safe) {
@ -2934,13 +2940,13 @@ function write_ws_xml_cell(cell, ref, ws, opts, idx, wb) {
}
var parse_ws_xml_data = (function parse_ws_xml_data_factory() {
var cellregex = /<(?:\w+:)?c /, rowregex = /<\/(?:\w+:)?row>/;
var cellregex = /<(?:\w+:)?c[ >]/, rowregex = /<\/(?:\w+:)?row>/;
var rregex = /r=["']([^"']*)["']/, isregex = /<is>([\S\s]*?)<\/is>/;
var match_v = matchtag("v"), match_f = matchtag("f");
return function parse_ws_xml_data(sdata, s, opts, guess) {
var ri = 0, x = "", cells = [], cref = [], idx = 0, i=0, cc=0, d="", p;
var tag;
var tag, tagr = 0, tagc = 0;
var sstr;
var fmtid = 0, fillid = 0, do_format = Array.isArray(styles.CellXf), cf;
for(var marr = sdata.split(rowregex), mt = 0, marrlen = marr.length; mt != marrlen; ++mt) {
@ -2951,18 +2957,19 @@ return function parse_ws_xml_data(sdata, s, opts, guess) {
/* 18.3.1.73 row CT_Row */
for(ri = 0; ri < xlen; ++ri) if(x.charCodeAt(ri) === 62) break; ++ri;
tag = parsexmltag(x.substr(0,ri), true);
var tagr = parseInt(tag.r, 10);
/* SpreadSheetGear uses implicit r/c */
tagr = typeof tag.r !== 'undefined' ? parseInt(tag.r, 10) : tagr+1; tagc = -1;
if(opts.sheetRows && opts.sheetRows < tagr) continue;
if(guess.s.r > tagr - 1) guess.s.r = tagr - 1;
if(guess.e.r < tagr - 1) guess.e.r = tagr - 1;
/* 18.3.1.4 c CT_Cell */
cells = x.substr(ri).split(cellregex);
for(ri = 1; ri != cells.length; ++ri) {
for(ri = typeof tag.r === 'undefined' ? 0 : 1; ri != cells.length; ++ri) {
x = cells[ri].trim();
if(x.length === 0) continue;
cref = x.match(rregex); idx = ri; i=0; cc=0;
x = "<c " + x;
x = "<c " + (x.substr(0,1)=="<"?">":"") + x;
if(cref !== null && cref.length === 2) {
idx = 0; d=cref[1];
for(i=0; i != d.length; ++i) {
@ -2970,10 +2977,11 @@ return function parse_ws_xml_data(sdata, s, opts, guess) {
idx = 26*idx + cc;
}
--idx;
}
tagc = idx;
} else ++tagc;
for(i = 0; i != x.length; ++i) if(x.charCodeAt(i) === 62) break; ++i;
tag = parsexmltag(x.substr(0,i), true);
if(!tag.r) tag.r = utils.encode_cell({r:tagr-1, c:tagc});
d = x.substr(i);
p = {t:""};
@ -4804,6 +4812,12 @@ var RecordEnum = {
};
var evert_RE = evert_key(RecordEnum, 'n');
/* Helper function to call out to ODS parser */
function parse_ods(zip, opts) {
if(typeof module !== "undefined" && typeof require !== 'undefined' && typeof ODS === 'undefined') ODS = require('./dist/od' + 's');
if(typeof ODS === 'undefined' || !ODS.parse_ods) throw new Error("Unsupported ODS");
return ODS.parse_ods(zip, opts);
}
function fix_opts_func(defaults) {
return function fix_opts(opts) {
for(var i = 0; i != defaults.length; ++i) {
@ -4861,6 +4875,10 @@ function parse_zip(zip, opts) {
opts = opts || {};
fix_read_opts(opts);
reset_cp();
/* OpenDocument Part 3 Section 2.2.1 OpenDocument Package */
if(safegetzipfile(zip, 'META-INF/manifest.xml')) return parse_ods(zip, opts);
var entries = keys(zip.files).filter(nodirs).sort();
var dir = parse_ct(getzipdata(zip, '[Content_Types].xml'), opts);
var xlsb = false;

@ -3,6 +3,8 @@
//importScripts('dist/cpexcel.js');
importScripts('jszip.js');
importScripts('xlsx.js');
/* uncomment the next line for ODS support */
importScripts('dist/ods.js');
postMessage({t:"ready"});
onmessage = function (oEvent) {

@ -3,6 +3,8 @@
//importScripts('dist/cpexcel.js');
importScripts('jszip.js');
importScripts('xlsx.js');
/* uncomment the next line for ODS support */
importScripts('dist/ods.js');
postMessage({t:"ready"});
function ab2str(data) {

@ -3,6 +3,8 @@
//importScripts('dist/cpexcel.js');
importScripts('jszip.js');
importScripts('xlsx.js');
/* uncomment the next line for ODS support */
importScripts('dist/ods.js');
postMessage({t:"ready"});
function ab2str(data) {