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) - -