forked from sheetjs/sheetjs
		
	more formats from js-harb
- clarify usage of Props and Custprops (fixes #274 h/t @michahell) - SYLK from js-harb - DIF from js-harb - HTML empty string bug fix
This commit is contained in:
		
							parent
							
								
									e42cf43c02
								
							
						
					
					
						commit
						b93569badf
					
				@ -39,3 +39,5 @@ test.js
 | 
			
		||||
bits/
 | 
			
		||||
docbits/
 | 
			
		||||
tests/
 | 
			
		||||
_book
 | 
			
		||||
book.json
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										85
									
								
								README.md
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										85
									
								
								README.md
									
									
									
									
									
								
							@ -38,9 +38,10 @@ with a unified JS representation, and ES3/ES5 browser compatibility back to IE6.
 | 
			
		||||
    + [Data Types](#data-types)
 | 
			
		||||
    + [Dates](#dates)
 | 
			
		||||
  * [Sheet Objects](#sheet-objects)
 | 
			
		||||
  * [Worksheet Object](#worksheet-object)
 | 
			
		||||
  * [Chartsheet Object](#chartsheet-object)
 | 
			
		||||
    + [Worksheet Object](#worksheet-object)
 | 
			
		||||
    + [Chartsheet Object](#chartsheet-object)
 | 
			
		||||
  * [Workbook Object](#workbook-object)
 | 
			
		||||
    + [Workbook File Properties](#workbook-file-properties)
 | 
			
		||||
  * [Document Features](#document-features)
 | 
			
		||||
    + [Formulae](#formulae)
 | 
			
		||||
    + [Column Properties](#column-properties)
 | 
			
		||||
@ -65,9 +66,12 @@ with a unified JS representation, and ES3/ES5 browser compatibility back to IE6.
 | 
			
		||||
  * [Excel 2003-2004 (SpreadsheetML)](#excel-2003-2004-spreadsheetml)
 | 
			
		||||
  * [Excel 2007+ Binary (XLSB, BIFF12)](#excel-2007-binary-xlsb-biff12)
 | 
			
		||||
  * [OpenDocument Spreadsheet (ODS/FODS) and Uniform Office Spreadsheet (UOS1/2)](#opendocument-spreadsheet-odsfods-and-uniform-office-spreadsheet-uos12)
 | 
			
		||||
  * [dBASE and Visual FoxPro (DBF)](#dbase-and-visual-foxpro-dbf)
 | 
			
		||||
  * [Comma-Separated Values](#comma-separated-values)
 | 
			
		||||
  * [HTML](#html)
 | 
			
		||||
  * [Comma-Separated Values (CSV)](#comma-separated-values-csv)
 | 
			
		||||
  * [Other Single-Worksheet Formats](#other-single-worksheet-formats)
 | 
			
		||||
    + [dBASE and Visual FoxPro (DBF)](#dbase-and-visual-foxpro-dbf)
 | 
			
		||||
    + [Symbolic Link (SYLK)](#symbolic-link-sylk)
 | 
			
		||||
    + [Data Interchange Format (DIF)](#data-interchange-format-dif)
 | 
			
		||||
    + [HTML](#html)
 | 
			
		||||
- [Testing](#testing)
 | 
			
		||||
  * [Tested Environments](#tested-environments)
 | 
			
		||||
  * [Test Files](#test-files)
 | 
			
		||||
@ -545,7 +549,7 @@ Special sheet keys (accessible as `sheet[key]`, each starting with `!`):
 | 
			
		||||
  When reading a worksheet with the `sheetRows` property set, the ref parameter
 | 
			
		||||
  will use the restricted range.  The original range is set at `ws['!fullref']`
 | 
			
		||||
 | 
			
		||||
### Worksheet Object
 | 
			
		||||
#### Worksheet Object
 | 
			
		||||
 | 
			
		||||
In addition to the base sheet keys, worksheets also add:
 | 
			
		||||
 | 
			
		||||
@ -560,7 +564,7 @@ In addition to the base sheet keys, worksheets also add:
 | 
			
		||||
  will write all cells in the merge range if they exist, so be sure that only
 | 
			
		||||
  the first cell (upper-left) in the range is set.
 | 
			
		||||
 | 
			
		||||
### Chartsheet Object
 | 
			
		||||
#### Chartsheet Object
 | 
			
		||||
 | 
			
		||||
Chartsheets are represented as standard sheets.  They are distinguished with the
 | 
			
		||||
`!type` property set to `"chart"`.
 | 
			
		||||
@ -576,7 +580,7 @@ first row of the chartsheet is the underlying header.
 | 
			
		||||
 | 
			
		||||
`wb.Props` is an object storing the standard properties.  `wb.Custprops` stores
 | 
			
		||||
custom properties.  Since the XLS standard properties deviate from the XLSX
 | 
			
		||||
standard, XLS parsing stores core properties in both places.  .
 | 
			
		||||
standard, XLS parsing stores core properties in both places.
 | 
			
		||||
 | 
			
		||||
`wb.WBProps` includes more workbook-level properties:
 | 
			
		||||
 | 
			
		||||
@ -585,6 +589,38 @@ standard, XLS parsing stores core properties in both places.  .
 | 
			
		||||
  The workbook's epoch can be determined by examining the workbook's
 | 
			
		||||
  `wb.WBProps.date1904` property.
 | 
			
		||||
 | 
			
		||||
#### Workbook File Properties
 | 
			
		||||
 | 
			
		||||
The various file formats use different internal names for file properties.  The
 | 
			
		||||
workbook `Props` object normalizes the names:
 | 
			
		||||
 | 
			
		||||
| JS Name     | Excel Description              |
 | 
			
		||||
|:------------|:-------------------------------|
 | 
			
		||||
| Title       | Summary tab "Title"            |
 | 
			
		||||
| Subject     | Summary tab "Subject"          |
 | 
			
		||||
| Author      | Summary tab "Author"           |
 | 
			
		||||
| Manager     | Summary tab "Manager"          |
 | 
			
		||||
| Company     | Summary tab "Company"          |
 | 
			
		||||
| Category    | Summary tab "Category"         |
 | 
			
		||||
| Keywords    | Summary tab "Keywords"         |
 | 
			
		||||
| Comments    | Summary tab "Comments"         |
 | 
			
		||||
| LastAuthor  | Statistics tab "Last saved by" |
 | 
			
		||||
| CreatedDate | Statistics tab "Created"       |
 | 
			
		||||
 | 
			
		||||
For example, to set the workbook title property:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
if(!wb.Props) wb.Props = {};
 | 
			
		||||
wb.Props.Title = "Insert Title Here";
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Custom properties are added in the workbook `Custprops` object:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
if(!wb.Custprops) wb.Custprops = {};
 | 
			
		||||
wb.Custprops["Custom Property"] = "Custom Value";
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Document Features
 | 
			
		||||
 | 
			
		||||
Even for basic features like date storage, the official Excel formats store the
 | 
			
		||||
@ -818,6 +854,8 @@ file but Excel will know how to handle it.  This library applies similar logic:
 | 
			
		||||
| `0x09` | BIFF Stream   | BIFF 2/3/4/5                                        |
 | 
			
		||||
| `0x3C` | XML/HTML      | SpreadsheetML / Flat ODS / UOS1 / HTML / plaintext  |
 | 
			
		||||
| `0x50` | ZIP Archive   | XLSB or XLSX/M or ODS or UOS2 or plaintext          |
 | 
			
		||||
| `0x49` | Plain Text    | SYLK or plaintext                                   |
 | 
			
		||||
| `0x54` | Plain Text    | DIF or plaintext                                    |
 | 
			
		||||
| `0xFE` | UTF8 Text     | SpreadsheetML or Flat ODS or UOS1 or plaintext      |
 | 
			
		||||
 | 
			
		||||
DBF files are detected based on the first byte as well as the third and fourth
 | 
			
		||||
@ -858,6 +896,8 @@ output formats.  The specific file type is controlled with `bookType` option:
 | 
			
		||||
| `biff2`  | `.xls`   |   none    | single | Excel 2.0 Worksheet format        |
 | 
			
		||||
| `fods`   | `.fods`  |   none    | multi  | Flat OpenDocument Spreadsheet     |
 | 
			
		||||
| `csv`    | `.csv`   |   none    | single | Comma Separated Values            |
 | 
			
		||||
| `sylk`   | `.sylk`  |   none    | single | Symbolic Link (SYLK)              |
 | 
			
		||||
| `dif`    | `.dif`   |   none    | single | Data Interchange Format (DIF)     |
 | 
			
		||||
 | 
			
		||||
- `compression` only applies to formats with ZIP containers.
 | 
			
		||||
- Formats that only support a single sheet require a `sheet` option specifying
 | 
			
		||||
@ -1088,6 +1128,8 @@ Despite the library name `xlsx`, it supports numerous spreadsheet file formats:
 | 
			
		||||
| Excel 2.0/2.1 (XLS BIFF2)                                    |  :o:  |  :o:  |
 | 
			
		||||
| **Excel Supported Text Formats**                             |:-----:|:-----:|
 | 
			
		||||
| Delimiter-Separated Values (CSV/TSV/DSV)                     |       |  :o:  |
 | 
			
		||||
| Data Interchange Format (DIF)                                |  :o:  |  :o:  |
 | 
			
		||||
| Symbolic Link (SYLK/SLK)                                     |  :o:  |  :o:  |
 | 
			
		||||
| **Other Workbook/Worksheet Formats**                         |:-----:|:-----:|
 | 
			
		||||
| OpenDocument Spreadsheet (ODS)                               |  :o:  |  :o:  |
 | 
			
		||||
| Flat XML ODF Spreadsheet (FODS)                              |  :o:  |  :o:  |
 | 
			
		||||
@ -1153,7 +1195,17 @@ UOS is a very similar format, and it comes in 2 varieties corresponding to ODS
 | 
			
		||||
and FODS respectively.  For the most part, the difference between the formats
 | 
			
		||||
lies in the names of tags and attributes.
 | 
			
		||||
 | 
			
		||||
### dBASE and Visual FoxPro (DBF)
 | 
			
		||||
### Comma-Separated Values (CSV)
 | 
			
		||||
 | 
			
		||||
Excel CSV deviates from RFC4180 in a number of important ways.  The generated
 | 
			
		||||
CSV files should generally work in Excel although they may not work in RFC4180
 | 
			
		||||
compatible readers.
 | 
			
		||||
 | 
			
		||||
### Other Single-Worksheet Formats
 | 
			
		||||
 | 
			
		||||
Many older formats supported only one worksheet:
 | 
			
		||||
 | 
			
		||||
#### dBASE and Visual FoxPro (DBF)
 | 
			
		||||
 | 
			
		||||
DBF is really a typed table format: each column can only hold one data type and
 | 
			
		||||
each record omits type information.  The parser generates a header row and
 | 
			
		||||
@ -1162,13 +1214,18 @@ inserts records starting at the second row of the worksheet.
 | 
			
		||||
Multi-file extensions like external memos and tables are currently unsupported,
 | 
			
		||||
limited by the general ability to read arbitrary files in the web browser.
 | 
			
		||||
 | 
			
		||||
### Comma-Separated Values
 | 
			
		||||
#### Symbolic Link (SYLK)
 | 
			
		||||
 | 
			
		||||
Excel CSV deviates from RFC4180 in a number of important ways.  The generated
 | 
			
		||||
CSV files should generally work in Excel although they may not work in RFC4180
 | 
			
		||||
compatible readers.
 | 
			
		||||
There is no real documentation.  All knowledge was gathered by saving files in
 | 
			
		||||
various versions of Excel to deduce the meaning of fields.
 | 
			
		||||
 | 
			
		||||
### HTML
 | 
			
		||||
#### Data Interchange Format (DIF)
 | 
			
		||||
 | 
			
		||||
There is no unified definition.  Visicalc DIF differs from Lotus DIF, and both
 | 
			
		||||
differ from Excel DIF.  Where ambiguous, the parser/writer follows the expected
 | 
			
		||||
behavior from Excel.
 | 
			
		||||
 | 
			
		||||
#### HTML
 | 
			
		||||
 | 
			
		||||
Excel HTML worksheets include special metadata encoded in styles.  For example,
 | 
			
		||||
`mso-number-format` is a localized string containing the number format.  Despite
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										12
									
								
								bin/xlsx.njs
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										12
									
								
								bin/xlsx.njs
									
									
									
									
									
								
							@ -27,6 +27,8 @@ program
 | 
			
		||||
	.option('-j, --json', 'emit formatted JSON (all fields text)')
 | 
			
		||||
	.option('-J, --raw-js', 'emit raw JS object (raw numbers)')
 | 
			
		||||
	.option('-A, --arrays', 'emit rows as JS objects (raw numbers)')
 | 
			
		||||
	.option('-D, --dif', 'emit data interchange format (dif)')
 | 
			
		||||
	.option('-K, --sylk', 'emit symbolic link (sylk)')
 | 
			
		||||
 | 
			
		||||
	.option('-F, --field-sep <sep>', 'CSV field separator', ",")
 | 
			
		||||
	.option('-R, --row-sep <sep>', 'CSV row separator', "\n")
 | 
			
		||||
@ -162,9 +164,13 @@ try {
 | 
			
		||||
if(program.perf) process.exit(0);
 | 
			
		||||
 | 
			
		||||
/* single worksheet XLS formats */
 | 
			
		||||
['biff2'].forEach(function(m) { if(program[m]) {
 | 
			
		||||
		wopts.bookType = m;
 | 
			
		||||
		X.writeFile(wb, sheetname || ((filename || "") + ".xls"), wopts);
 | 
			
		||||
[
 | 
			
		||||
	['biff2', '.xls'],
 | 
			
		||||
	['sylk', '.slk'],
 | 
			
		||||
	['dif', '.dif']
 | 
			
		||||
].forEach(function(m) { if(program[m[0]]) {
 | 
			
		||||
		wopts.bookType = m[0];
 | 
			
		||||
		X.writeFile(wb, sheetname || ((filename || "") + m[1]), wopts);
 | 
			
		||||
		process.exit(0);
 | 
			
		||||
} });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										177
									
								
								bits/40_harb.js
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										177
									
								
								bits/40_harb.js
									
									
									
									
									
								
							@ -182,3 +182,180 @@ function dbf_to_workbook(buf, opts)/*:Workbook*/ {
 | 
			
		||||
		to_sheet: dbf_to_sheet
 | 
			
		||||
	};
 | 
			
		||||
})();
 | 
			
		||||
 | 
			
		||||
var SYLK = (function() {
 | 
			
		||||
	/* TODO: find an actual specification */
 | 
			
		||||
	function sylk_to_aoa(d/*:RawData*/, opts)/*:AOA*/ {
 | 
			
		||||
		switch(opts.type) {
 | 
			
		||||
			case 'base64': return sylk_to_aoa_str(Base64.decode(d), opts);
 | 
			
		||||
			case 'binary': return sylk_to_aoa_str(d, opts);
 | 
			
		||||
			case 'buffer': return sylk_to_aoa_str(d.toString('binary'), opts);
 | 
			
		||||
			case 'array': return sylk_to_aoa_str(cc2str(d), opts);
 | 
			
		||||
		}
 | 
			
		||||
		throw new Error("Unrecognized type " + opts.type);
 | 
			
		||||
	}
 | 
			
		||||
	function sylk_to_aoa_str(str/*:string*/, opts)/*:AOA*/ {
 | 
			
		||||
		var records = str.split(/[\n\r]+/), R = -1, C = -1, ri = 0, rj = 0, arr = [];
 | 
			
		||||
		var formats = [];
 | 
			
		||||
		var next_cell_format = null;
 | 
			
		||||
		for (; ri !== records.length; ++ri) {
 | 
			
		||||
			var record = records[ri].trim().split(";");
 | 
			
		||||
			var RT = record[0], val;
 | 
			
		||||
			if(RT === 'P') for(rj=1; rj<record.length; ++rj) switch(record[rj].charAt(0)) {
 | 
			
		||||
				case 'P':
 | 
			
		||||
					formats.push(record[rj].substr(1));
 | 
			
		||||
					break;
 | 
			
		||||
			}
 | 
			
		||||
			else if(RT !== 'C' && RT !== 'F') continue;
 | 
			
		||||
			else for(rj=1; rj<record.length; ++rj) switch(record[rj].charAt(0)) {
 | 
			
		||||
				case 'Y':
 | 
			
		||||
					R = parseInt(record[rj].substr(1))-1; C = 0;
 | 
			
		||||
					for(var j = arr.length; j <= R; ++j) arr[j] = [];
 | 
			
		||||
					break;
 | 
			
		||||
				case 'X': C = parseInt(record[rj].substr(1))-1; break;
 | 
			
		||||
				case 'K':
 | 
			
		||||
					val = record[rj].substr(1);
 | 
			
		||||
					if(val.charAt(0) === '"') val = val.substr(1,val.length - 2);
 | 
			
		||||
					else if(val === 'TRUE') val = true;
 | 
			
		||||
					else if(val === 'FALSE') val = false;
 | 
			
		||||
					else if(+val === +val) {
 | 
			
		||||
						val = +val;
 | 
			
		||||
						if(next_cell_format !== null && next_cell_format.match(/[ymdhmsYMDHMS]/)) val = numdate(val);
 | 
			
		||||
					}
 | 
			
		||||
					arr[R][C] = val;
 | 
			
		||||
					next_cell_format = null;
 | 
			
		||||
					break;
 | 
			
		||||
				case 'P':
 | 
			
		||||
					if(RT !== 'F') break;
 | 
			
		||||
					next_cell_format = formats[parseInt(record[rj].substr(1))];
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return arr;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	function sylk_to_sheet(str/*:string*/, opts)/*:Worksheet*/ { return aoa_to_sheet(sylk_to_aoa(str, opts), opts); }
 | 
			
		||||
 | 
			
		||||
	function sylk_to_workbook(str/*:string*/, opts)/*:Workbook*/ { return sheet_to_workbook(sylk_to_sheet(str, opts), opts); }
 | 
			
		||||
 | 
			
		||||
	function write_ws_cell_sylk(cell/*:Cell*/, ws/*:Worksheet*/, R/*:number*/, C/*:number*/, opts)/*:string*/ {
 | 
			
		||||
		var o = "C;Y" + (R+1) + ";X" + (C+1) + ";K";
 | 
			
		||||
		switch(cell.t) {
 | 
			
		||||
			case 'n': o += cell.v; break;
 | 
			
		||||
			case 'b': o += cell.v ? "TRUE" : "FALSE"; break;
 | 
			
		||||
			case 'e': o += cell.w || cell.v; break;
 | 
			
		||||
			case 'd': o += '"' + (cell.w || cell.v) + '"'; break;
 | 
			
		||||
			case 's': o += '"' + cell.v.replace(/"/g,"") + '"'; break;
 | 
			
		||||
		}
 | 
			
		||||
		return o;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	function sheet_to_sylk(ws/*:Worksheet*/, opts/*:?any*/)/*:string*/ {
 | 
			
		||||
		var preamble/*:Array<string>*/ = ["ID;PWXL;N;E"], o/*:Array<string>*/ = [];
 | 
			
		||||
		preamble.push("P;PGeneral");
 | 
			
		||||
		var r = decode_range(ws['!ref']), cell/*:Cell*/;
 | 
			
		||||
		for(var R = r.s.r; R <= r.e.r; ++R) {
 | 
			
		||||
			for(var C = r.s.c; C <= r.e.c; ++C) {
 | 
			
		||||
				var coord = encode_cell({r:R,c:C});
 | 
			
		||||
				if(!(cell = ws[coord]) || cell.v == null) continue;
 | 
			
		||||
				o.push(write_ws_cell_sylk(cell, ws, R, C, opts));
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		preamble.push("F;P0;DG0G8;M255");
 | 
			
		||||
		var RS = "\r\n";
 | 
			
		||||
		return preamble.join(RS) + RS + o.join(RS) + RS + "E" + RS;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return {
 | 
			
		||||
		to_workbook: sylk_to_workbook,
 | 
			
		||||
		to_sheet: sylk_to_sheet,
 | 
			
		||||
		from_sheet: sheet_to_sylk
 | 
			
		||||
	};
 | 
			
		||||
})();
 | 
			
		||||
 | 
			
		||||
var DIF = (function() {
 | 
			
		||||
	function dif_to_aoa(d/*:RawData*/, opts)/*:AOA*/ {
 | 
			
		||||
		switch(opts.type) {
 | 
			
		||||
			case 'base64': return dif_to_aoa_str(Base64.decode(d), opts);
 | 
			
		||||
			case 'binary': return dif_to_aoa_str(d, opts);
 | 
			
		||||
			case 'buffer': return dif_to_aoa_str(d.toString('binary'), opts);
 | 
			
		||||
			case 'array': return dif_to_aoa_str(cc2str(d), opts);
 | 
			
		||||
		}
 | 
			
		||||
		throw new Error("Unrecognized type " + opts.type);
 | 
			
		||||
	}
 | 
			
		||||
	function dif_to_aoa_str(str/*:string*/, opts)/*:AOA*/ {
 | 
			
		||||
		var records = str.split('\n'), R = -1, C = -1, ri = 0, arr = [];
 | 
			
		||||
		for (; ri !== records.length; ++ri) {
 | 
			
		||||
			if (records[ri].trim() === 'BOT') { arr[++R] = []; C = 0; continue; }
 | 
			
		||||
			if (R < 0) continue;
 | 
			
		||||
			var metadata = records[ri].trim().split(",");
 | 
			
		||||
			var type = metadata[0], value = metadata[1];
 | 
			
		||||
			++ri;
 | 
			
		||||
			var data = records[ri].trim();
 | 
			
		||||
			switch (+type) {
 | 
			
		||||
				case -1:
 | 
			
		||||
					if (data === 'BOT') { arr[++R] = []; C = 0; continue; }
 | 
			
		||||
					else if (data !== 'EOD') throw new Error("Unrecognized DIF special command " + data);
 | 
			
		||||
					break;
 | 
			
		||||
				case 0:
 | 
			
		||||
					if(data === 'TRUE') arr[R][C] = true;
 | 
			
		||||
					else if(data === 'FALSE') arr[R][C] = false;
 | 
			
		||||
					else if(+value == +value) arr[R][C] = +value;
 | 
			
		||||
					else if(!isNaN(new Date(value).getDate())) arr[R][C] = new Date(value);
 | 
			
		||||
					else arr[R][C] = value;
 | 
			
		||||
					++C; break;
 | 
			
		||||
				case 1:
 | 
			
		||||
					data = data.substr(1,data.length-2);
 | 
			
		||||
					arr[R][C++] = data !== '' ? data : null;
 | 
			
		||||
					break;
 | 
			
		||||
			}
 | 
			
		||||
			if (data === 'EOD') break;
 | 
			
		||||
		}
 | 
			
		||||
		return arr;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	function dif_to_sheet(str/*:string*/, opts)/*:Worksheet*/ { return aoa_to_sheet(dif_to_aoa(str, opts), opts); }
 | 
			
		||||
	function dif_to_workbook(str/*:string*/, opts)/*:Workbook*/ { return sheet_to_workbook(dif_to_sheet(str, opts), opts); }
 | 
			
		||||
 | 
			
		||||
	var sheet_to_dif = (function() {
 | 
			
		||||
		var push_field = function pf(o/*:Array<string>*/, topic/*:string*/, v/*:number*/, n/*:number*/, s/*:string*/) {
 | 
			
		||||
			o.push(topic);
 | 
			
		||||
			o.push(v + "," + n);
 | 
			
		||||
			o.push('"' + s.replace(/"/g,'""') + '"');
 | 
			
		||||
		};
 | 
			
		||||
		var push_value = function po(o/*:Array<string>*/, type/*:number*/, v/*:number*/, s/*:string*/) {
 | 
			
		||||
			o.push(type + "," + v);
 | 
			
		||||
			o.push(type == 1 ? '"' + s.replace(/"/g,'""') + '"' : s);
 | 
			
		||||
		};
 | 
			
		||||
		return function sheet_to_dif(ws/*:Worksheet*/, opts/*:?any*/)/*:string*/ {
 | 
			
		||||
			var o/*:Array<string>*/ = [];
 | 
			
		||||
			var r = decode_range(ws['!ref']), cell/*:Cell*/;
 | 
			
		||||
			push_field(o, "TABLE", 0, 1, "sheetjs");
 | 
			
		||||
			push_field(o, "VECTORS", 0, r.e.r - r.s.r + 1,"");
 | 
			
		||||
			push_field(o, "TUPLES", 0, r.e.c - r.s.c + 1,"");
 | 
			
		||||
			push_field(o, "DATA", 0, 0,"");
 | 
			
		||||
			for(var R = r.s.r; R <= r.e.r; ++R) {
 | 
			
		||||
				push_value(o, -1, 0, "BOT");
 | 
			
		||||
				for(var C = r.s.c; C <= r.e.c; ++C) {
 | 
			
		||||
					var coord = encode_cell({r:R,c:C});
 | 
			
		||||
					if(!(cell = ws[coord]) || cell.v == null) { push_value(o, 1, 0, ""); continue;}
 | 
			
		||||
					switch(cell.t) {
 | 
			
		||||
						case 'n': push_value(o, 0, (/*cell.w ||*/ cell.v), "V"); break;
 | 
			
		||||
						case 'b': push_value(o, 0, cell.v ? 1 : 0, cell.v ? "TRUE" : "FALSE"); break;
 | 
			
		||||
						case 's': push_value(o, 1, 0, cell.v); break;
 | 
			
		||||
						default: push_value(o, 1, 0, "");
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			push_value(o, -1, 0, "EOD");
 | 
			
		||||
			var RS = "\r\n";
 | 
			
		||||
			var oo = o.join(RS);
 | 
			
		||||
			//while((oo.length & 0x7F) != 0) oo += "\0";
 | 
			
		||||
			return oo;
 | 
			
		||||
		};
 | 
			
		||||
	})();
 | 
			
		||||
	return {
 | 
			
		||||
		to_workbook: dif_to_workbook,
 | 
			
		||||
		to_sheet: dif_to_sheet,
 | 
			
		||||
		from_sheet: sheet_to_dif
 | 
			
		||||
	};
 | 
			
		||||
})();
 | 
			
		||||
 | 
			
		||||
@ -53,12 +53,13 @@ function parse_dom_table(table/*:HTMLElement*/, opts/*:?any*/)/*:Worksheet*/ {
 | 
			
		||||
			CS = +elt.getAttribute("colspan") || 1;
 | 
			
		||||
			if((RS = +elt.getAttribute("rowspan"))>0) merges.push({s:{r:R,c:C},e:{r:R + RS - 1, c:C + CS - 1}});
 | 
			
		||||
			var o = {t:'s', v:v};
 | 
			
		||||
			if(!isNaN(Number(v))) o = {t:'n', v:Number(v)};
 | 
			
		||||
			if(v != null && v.length && !isNaN(Number(v))) o = {t:'n', v:Number(v)};
 | 
			
		||||
			ws[encode_cell({c:C, r:R})] = o;
 | 
			
		||||
			C += CS;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	ws['!merges'] = merges;
 | 
			
		||||
	ws['!ref'] = encode_range(range);
 | 
			
		||||
	return ws;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -5,3 +5,15 @@ function write_csv_str(wb/*:Workbook*/, o/*:WriteOpts*/) {
 | 
			
		||||
	if(idx == 0 && !!o.sheet && wb.SheetNames[0] != o.sheet) throw new Error("Sheet not found: " + o.sheet);
 | 
			
		||||
	return sheet_to_csv(wb.Sheets[wb.SheetNames[idx]], o);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function write_obj_str(factory) {
 | 
			
		||||
	return function write_str(wb/*:Workbook*/, o/*:WriteOpts*/) {
 | 
			
		||||
		var idx = 0;
 | 
			
		||||
		for(var i=0;i<wb.SheetNames.length;++i) if(wb.SheetNames[i] == o.sheet) idx=i;
 | 
			
		||||
		if(idx == 0 && !!o.sheet && wb.SheetNames[0] != o.sheet) throw new Error("Sheet not found: " + o.sheet);
 | 
			
		||||
		return factory.from_sheet(wb.Sheets[wb.SheetNames[idx]], o);
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var write_slk_str = write_obj_str(SYLK);
 | 
			
		||||
var write_dif_str = write_obj_str(DIF);
 | 
			
		||||
 | 
			
		||||
@ -34,7 +34,7 @@ function write_zip(wb/*:Workbook*/, opts/*:WriteOpts*/)/*:ZIP*/ {
 | 
			
		||||
	/*::if(!wb.Props) throw "unreachable"; */
 | 
			
		||||
	f = "docProps/app.xml";
 | 
			
		||||
	if(!wb.Workbook || !wb.Workbook.Sheets) wb.Props.SheetNames = wb.SheetNames;
 | 
			
		||||
	/*:: else if(!wb.Props) throw "unreachable"; */
 | 
			
		||||
	// $FlowIgnore
 | 
			
		||||
	else wb.Props.SheetNames = wb.Workbook.Sheets.filter(function(x) { return x.Hidden != 2; }).map(function(x) { return x.name; });
 | 
			
		||||
	wb.Props.Worksheets = wb.Props.SheetNames.length;
 | 
			
		||||
	zip.file(f, write_ext_props(wb.Props, opts));
 | 
			
		||||
 | 
			
		||||
@ -33,6 +33,8 @@ function readSync(data/*:RawData*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
 | 
			
		||||
		case 0xD0: return parse_xlscfb(CFB.read(d, o), o);
 | 
			
		||||
		case 0x09: return parse_xlscfb(s2a(o.type === 'base64' ? Base64.decode(d) : d), o);
 | 
			
		||||
		case 0x3C: return parse_xlml(d, o);
 | 
			
		||||
		case 0x49: if(n[1] == 0x44) return SYLK.to_workbook(d, o); break;
 | 
			
		||||
		case 0x54: if(n[1] == 0x41 && n[2] == 0x42 && n[3] == 0x4C) return DIF.to_workbook(d, o); break;
 | 
			
		||||
		case 0x50: if(n[1] == 0x4B && n[2] < 0x20 && n[3] < 0x20) return read_zip(d, o); break;
 | 
			
		||||
		case 0xEF: return parse_xlml(d, o);
 | 
			
		||||
		case 0x03: case 0x83: case 0x8B: return DBF.to_workbook(d, o);
 | 
			
		||||
 | 
			
		||||
@ -48,7 +48,10 @@ function writeSync(wb/*:Workbook*/, opts/*:?WriteOpts*/) {
 | 
			
		||||
	switch(o.bookType || 'xlsx') {
 | 
			
		||||
		case 'xml':
 | 
			
		||||
		case 'xlml': return write_string_type(write_xlml(wb, o), o);
 | 
			
		||||
		case 'slk':
 | 
			
		||||
		case 'sylk': return write_string_type(write_slk_str(wb, o), o);
 | 
			
		||||
		case 'csv': return write_string_type(write_csv_str(wb, o), o);
 | 
			
		||||
		case 'dif': return write_string_type(write_dif_str(wb, o), o);
 | 
			
		||||
		case 'fods': return write_string_type(write_ods(wb, o), o);
 | 
			
		||||
		case 'biff2': return write_binary_type(write_biff_buf(wb, o), o);
 | 
			
		||||
		case 'xlsx':
 | 
			
		||||
@ -66,11 +69,14 @@ function resolve_book_type(o/*?WriteFileOpts*/) {
 | 
			
		||||
		case '.xlsb': o.bookType = 'xlsb'; break;
 | 
			
		||||
		case '.fods': o.bookType = 'fods'; break;
 | 
			
		||||
		case '.xlml': o.bookType = 'xlml'; break;
 | 
			
		||||
		case '.sylk': o.bookType = 'sylk'; break;
 | 
			
		||||
	default: switch(o.file.slice(-4).toLowerCase()) {
 | 
			
		||||
		case '.xls': o.bookType = 'biff2'; break;
 | 
			
		||||
		case '.xml': o.bookType = 'xml'; break;
 | 
			
		||||
		case '.ods': o.bookType = 'ods'; break;
 | 
			
		||||
		case '.csv': o.bookType = 'csv'; break;
 | 
			
		||||
		case '.dif': o.bookType = 'dif'; break;
 | 
			
		||||
		case '.slk': o.bookType = 'sylk'; break;
 | 
			
		||||
	}}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
### Worksheet Object
 | 
			
		||||
#### Worksheet Object
 | 
			
		||||
 | 
			
		||||
In addition to the base sheet keys, worksheets also add:
 | 
			
		||||
 | 
			
		||||
@ -13,7 +13,7 @@ In addition to the base sheet keys, worksheets also add:
 | 
			
		||||
  will write all cells in the merge range if they exist, so be sure that only
 | 
			
		||||
  the first cell (upper-left) in the range is set.
 | 
			
		||||
 | 
			
		||||
### Chartsheet Object
 | 
			
		||||
#### Chartsheet Object
 | 
			
		||||
 | 
			
		||||
Chartsheets are represented as standard sheets.  They are distinguished with the
 | 
			
		||||
`!type` property set to `"chart"`.
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@
 | 
			
		||||
 | 
			
		||||
`wb.Props` is an object storing the standard properties.  `wb.Custprops` stores
 | 
			
		||||
custom properties.  Since the XLS standard properties deviate from the XLSX
 | 
			
		||||
standard, XLS parsing stores core properties in both places.  .
 | 
			
		||||
standard, XLS parsing stores core properties in both places.
 | 
			
		||||
 | 
			
		||||
`wb.WBProps` includes more workbook-level properties:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										32
									
								
								docbits/56_wbprops.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										32
									
								
								docbits/56_wbprops.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,32 @@
 | 
			
		||||
#### Workbook File Properties
 | 
			
		||||
 | 
			
		||||
The various file formats use different internal names for file properties.  The
 | 
			
		||||
workbook `Props` object normalizes the names:
 | 
			
		||||
 | 
			
		||||
| JS Name     | Excel Description              |
 | 
			
		||||
|:------------|:-------------------------------|
 | 
			
		||||
| Title       | Summary tab "Title"            |
 | 
			
		||||
| Subject     | Summary tab "Subject"          |
 | 
			
		||||
| Author      | Summary tab "Author"           |
 | 
			
		||||
| Manager     | Summary tab "Manager"          |
 | 
			
		||||
| Company     | Summary tab "Company"          |
 | 
			
		||||
| Category    | Summary tab "Category"         |
 | 
			
		||||
| Keywords    | Summary tab "Keywords"         |
 | 
			
		||||
| Comments    | Summary tab "Comments"         |
 | 
			
		||||
| LastAuthor  | Statistics tab "Last saved by" |
 | 
			
		||||
| CreatedDate | Statistics tab "Created"       |
 | 
			
		||||
 | 
			
		||||
For example, to set the workbook title property:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
if(!wb.Props) wb.Props = {};
 | 
			
		||||
wb.Props.Title = "Insert Title Here";
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Custom properties are added in the workbook `Custprops` object:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
if(!wb.Custprops) wb.Custprops = {};
 | 
			
		||||
wb.Custprops["Custom Property"] = "Custom Value";
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
@ -65,6 +65,8 @@ file but Excel will know how to handle it.  This library applies similar logic:
 | 
			
		||||
| `0x09` | BIFF Stream   | BIFF 2/3/4/5                                        |
 | 
			
		||||
| `0x3C` | XML/HTML      | SpreadsheetML / Flat ODS / UOS1 / HTML / plaintext  |
 | 
			
		||||
| `0x50` | ZIP Archive   | XLSB or XLSX/M or ODS or UOS2 or plaintext          |
 | 
			
		||||
| `0x49` | Plain Text    | SYLK or plaintext                                   |
 | 
			
		||||
| `0x54` | Plain Text    | DIF or plaintext                                    |
 | 
			
		||||
| `0xFE` | UTF8 Text     | SpreadsheetML or Flat ODS or UOS1 or plaintext      |
 | 
			
		||||
 | 
			
		||||
DBF files are detected based on the first byte as well as the third and fourth
 | 
			
		||||
 | 
			
		||||
@ -33,6 +33,8 @@ output formats.  The specific file type is controlled with `bookType` option:
 | 
			
		||||
| `biff2`  | `.xls`   |   none    | single | Excel 2.0 Worksheet format        |
 | 
			
		||||
| `fods`   | `.fods`  |   none    | multi  | Flat OpenDocument Spreadsheet     |
 | 
			
		||||
| `csv`    | `.csv`   |   none    | single | Comma Separated Values            |
 | 
			
		||||
| `sylk`   | `.sylk`  |   none    | single | Symbolic Link (SYLK)              |
 | 
			
		||||
| `dif`    | `.dif`   |   none    | single | Data Interchange Format (DIF)     |
 | 
			
		||||
 | 
			
		||||
- `compression` only applies to formats with ZIP containers.
 | 
			
		||||
- Formats that only support a single sheet require a `sheet` option specifying
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,8 @@ Despite the library name `xlsx`, it supports numerous spreadsheet file formats:
 | 
			
		||||
| Excel 2.0/2.1 (XLS BIFF2)                                    |  :o:  |  :o:  |
 | 
			
		||||
| **Excel Supported Text Formats**                             |:-----:|:-----:|
 | 
			
		||||
| Delimiter-Separated Values (CSV/TSV/DSV)                     |       |  :o:  |
 | 
			
		||||
| Data Interchange Format (DIF)                                |  :o:  |  :o:  |
 | 
			
		||||
| Symbolic Link (SYLK/SLK)                                     |  :o:  |  :o:  |
 | 
			
		||||
| **Other Workbook/Worksheet Formats**                         |:-----:|:-----:|
 | 
			
		||||
| OpenDocument Spreadsheet (ODS)                               |  :o:  |  :o:  |
 | 
			
		||||
| Flat XML ODF Spreadsheet (FODS)                              |  :o:  |  :o:  |
 | 
			
		||||
@ -80,7 +82,17 @@ UOS is a very similar format, and it comes in 2 varieties corresponding to ODS
 | 
			
		||||
and FODS respectively.  For the most part, the difference between the formats
 | 
			
		||||
lies in the names of tags and attributes.
 | 
			
		||||
 | 
			
		||||
### dBASE and Visual FoxPro (DBF)
 | 
			
		||||
### Comma-Separated Values (CSV)
 | 
			
		||||
 | 
			
		||||
Excel CSV deviates from RFC4180 in a number of important ways.  The generated
 | 
			
		||||
CSV files should generally work in Excel although they may not work in RFC4180
 | 
			
		||||
compatible readers.
 | 
			
		||||
 | 
			
		||||
### Other Single-Worksheet Formats
 | 
			
		||||
 | 
			
		||||
Many older formats supported only one worksheet:
 | 
			
		||||
 | 
			
		||||
#### dBASE and Visual FoxPro (DBF)
 | 
			
		||||
 | 
			
		||||
DBF is really a typed table format: each column can only hold one data type and
 | 
			
		||||
each record omits type information.  The parser generates a header row and
 | 
			
		||||
@ -89,13 +101,18 @@ inserts records starting at the second row of the worksheet.
 | 
			
		||||
Multi-file extensions like external memos and tables are currently unsupported,
 | 
			
		||||
limited by the general ability to read arbitrary files in the web browser.
 | 
			
		||||
 | 
			
		||||
### Comma-Separated Values
 | 
			
		||||
#### Symbolic Link (SYLK)
 | 
			
		||||
 | 
			
		||||
Excel CSV deviates from RFC4180 in a number of important ways.  The generated
 | 
			
		||||
CSV files should generally work in Excel although they may not work in RFC4180
 | 
			
		||||
compatible readers.
 | 
			
		||||
There is no real documentation.  All knowledge was gathered by saving files in
 | 
			
		||||
various versions of Excel to deduce the meaning of fields.
 | 
			
		||||
 | 
			
		||||
### HTML
 | 
			
		||||
#### Data Interchange Format (DIF)
 | 
			
		||||
 | 
			
		||||
There is no unified definition.  Visicalc DIF differs from Lotus DIF, and both
 | 
			
		||||
differ from Excel DIF.  Where ambiguous, the parser/writer follows the expected
 | 
			
		||||
behavior from Excel.
 | 
			
		||||
 | 
			
		||||
#### HTML
 | 
			
		||||
 | 
			
		||||
Excel HTML worksheets include special metadata encoded in styles.  For example,
 | 
			
		||||
`mso-number-format` is a localized string containing the number format.  Despite
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										10
									
								
								formats.dot
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										10
									
								
								formats.dot
									
									
									
									
									
								
							@ -1,5 +1,5 @@
 | 
			
		||||
digraph G {
 | 
			
		||||
	graph [mindist=0.1]; 
 | 
			
		||||
	graph [mindist=0.1];
 | 
			
		||||
	csf [shape=doublecircle,label="Common\nSpreadsheet\nFormat\n(JS Object)"];
 | 
			
		||||
	subgraph XL {
 | 
			
		||||
		node  [style=filled,color=green];
 | 
			
		||||
@ -17,7 +17,7 @@ digraph G {
 | 
			
		||||
		node  [style=filled,color=yellow];
 | 
			
		||||
		ods   [label="ODS"];
 | 
			
		||||
		fods  [label="Flat\nODS"];
 | 
			
		||||
		uos   [label="UOS\n标文通"];
 | 
			
		||||
		uos   [label="UOS"];
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	subgraph OLD {
 | 
			
		||||
@ -25,6 +25,8 @@ digraph G {
 | 
			
		||||
		html  [label="HTML\nTable"];
 | 
			
		||||
		csv   [label="CSV"];
 | 
			
		||||
		dbf   [label="DBF"];
 | 
			
		||||
		dif   [label="DIF"];
 | 
			
		||||
		slk   [label="SYLK"];
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	subgraph JSXLSX {
 | 
			
		||||
@ -41,6 +43,10 @@ digraph G {
 | 
			
		||||
		xls4 -> csf
 | 
			
		||||
		xls5 -> csf
 | 
			
		||||
		xls8 -> csf
 | 
			
		||||
		csf -> slk
 | 
			
		||||
		slk -> csf
 | 
			
		||||
		csf -> dif
 | 
			
		||||
		dif -> csf
 | 
			
		||||
		csf -> csv
 | 
			
		||||
		ods -> csf
 | 
			
		||||
		csf -> ods
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								formats.png
									
									
									
									
									
								
							
							
								
									
								
								
								
								
								
									
									
								
							
						
						
									
										
											BIN
										
									
								
								formats.png
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 94 KiB  | 
@ -18,9 +18,10 @@
 | 
			
		||||
    + [Data Types](README.md#data-types)
 | 
			
		||||
    + [Dates](README.md#dates)
 | 
			
		||||
  * [Sheet Objects](README.md#sheet-objects)
 | 
			
		||||
  * [Worksheet Object](README.md#worksheet-object)
 | 
			
		||||
  * [Chartsheet Object](README.md#chartsheet-object)
 | 
			
		||||
    + [Worksheet Object](README.md#worksheet-object)
 | 
			
		||||
    + [Chartsheet Object](README.md#chartsheet-object)
 | 
			
		||||
  * [Workbook Object](README.md#workbook-object)
 | 
			
		||||
    + [Workbook File Properties](README.md#workbook-file-properties)
 | 
			
		||||
  * [Document Features](README.md#document-features)
 | 
			
		||||
    + [Formulae](README.md#formulae)
 | 
			
		||||
    + [Column Properties](README.md#column-properties)
 | 
			
		||||
@ -45,9 +46,12 @@
 | 
			
		||||
  * [Excel 2003-2004 (SpreadsheetML)](README.md#excel-2003-2004-spreadsheetml)
 | 
			
		||||
  * [Excel 2007+ Binary (XLSB, BIFF12)](README.md#excel-2007-binary-xlsb-biff12)
 | 
			
		||||
  * [OpenDocument Spreadsheet (ODS/FODS) and Uniform Office Spreadsheet (UOS1/2)](README.md#opendocument-spreadsheet-odsfods-and-uniform-office-spreadsheet-uos12)
 | 
			
		||||
  * [dBASE and Visual FoxPro (DBF)](README.md#dbase-and-visual-foxpro-dbf)
 | 
			
		||||
  * [Comma-Separated Values](README.md#comma-separated-values)
 | 
			
		||||
  * [HTML](README.md#html)
 | 
			
		||||
  * [Comma-Separated Values (CSV)](README.md#comma-separated-values-csv)
 | 
			
		||||
  * [Other Single-Worksheet Formats](README.md#other-single-worksheet-formats)
 | 
			
		||||
    + [dBASE and Visual FoxPro (DBF)](README.md#dbase-and-visual-foxpro-dbf)
 | 
			
		||||
    + [Symbolic Link (SYLK)](README.md#symbolic-link-sylk)
 | 
			
		||||
    + [Data Interchange Format (DIF)](README.md#data-interchange-format-dif)
 | 
			
		||||
    + [HTML](README.md#html)
 | 
			
		||||
- [Testing](README.md#testing)
 | 
			
		||||
  * [Tested Environments](README.md#tested-environments)
 | 
			
		||||
  * [Test Files](README.md#test-files)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										43
									
								
								test.js
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										43
									
								
								test.js
									
									
									
									
									
								
							@ -12,7 +12,7 @@ if(process.env.WTF) {
 | 
			
		||||
	opts.cellStyles = true;
 | 
			
		||||
}
 | 
			
		||||
var fullex = [".xlsb", ".xlsm", ".xlsx"/*, ".xlml"*/];
 | 
			
		||||
var ofmt = ["xlsb", "xlsm", "xlsx", "ods", "biff2", "xlml"];
 | 
			
		||||
var ofmt = ["xlsb", "xlsm", "xlsx", "ods", "biff2", "xlml", "sylk", "dif"];
 | 
			
		||||
var ex = fullex.slice(); ex = ex.concat([".ods", ".xls", ".xml", ".fods"]);
 | 
			
		||||
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;});
 | 
			
		||||
@ -951,26 +951,31 @@ describe('roundtrip features', function() {
 | 
			
		||||
	var bef = (function() { X = require(modp); });
 | 
			
		||||
	if(typeof before != 'undefined') before(bef);
 | 
			
		||||
	else it('before', bef);
 | 
			
		||||
	describe('should parse core properties and custom properties', function() {
 | 
			
		||||
		var wb1, wb2, base = './tmp/cp';
 | 
			
		||||
		var bef = (function() {
 | 
			
		||||
			wb1 = X.readFile(paths.cpxlsx);
 | 
			
		||||
			wb2 = X.readFile(paths.cpxlsb);
 | 
			
		||||
			fullex.forEach(function(p) {
 | 
			
		||||
				X.writeFile(wb1, base + '.xlsm' + p);
 | 
			
		||||
				X.writeFile(wb2, base + '.xlsb' + p);
 | 
			
		||||
			});
 | 
			
		||||
	describe('should preserve core properties', function() { [
 | 
			
		||||
		['xlml', paths.cpxml],
 | 
			
		||||
		['xlsx', paths.cpxlsx],
 | 
			
		||||
		['xlsb', paths.cpxlsb]
 | 
			
		||||
	].forEach(function(w) {
 | 
			
		||||
		it(w[0], function() {
 | 
			
		||||
				var wb1 = X.readFile(w[1]);
 | 
			
		||||
				var wb2 = X.read(X.write(wb1, {bookType:w[0], type:"buffer"}), {type:"buffer"});
 | 
			
		||||
				coreprop(wb1);
 | 
			
		||||
				coreprop(wb2);
 | 
			
		||||
		});
 | 
			
		||||
		if(typeof before != 'undefined') before(bef);
 | 
			
		||||
		else it('before', bef);
 | 
			
		||||
		fullex.forEach(function(p) { ['.xlsm','.xlsb'].forEach(function(q) {
 | 
			
		||||
			it(q + p + ' should roundtrip core and custom properties', function() {
 | 
			
		||||
				var wb = X.readFile(base + q + p);
 | 
			
		||||
				coreprop(wb);
 | 
			
		||||
				custprop(wb);
 | 
			
		||||
			}); });
 | 
			
		||||
	}); });
 | 
			
		||||
 | 
			
		||||
	describe('should preserve custom properties', function() { [
 | 
			
		||||
		['xlml', paths.cpxml],
 | 
			
		||||
		['xlsx', paths.cpxlsx],
 | 
			
		||||
		['xlsb', paths.cpxlsb]
 | 
			
		||||
	].forEach(function(w) {
 | 
			
		||||
		it(w[0], function() {
 | 
			
		||||
				var wb1 = X.readFile(w[1]);
 | 
			
		||||
				var wb2 = X.read(X.write(wb1, {bookType:w[0], type:"buffer"}), {type:"buffer"});
 | 
			
		||||
				custprop(wb1);
 | 
			
		||||
				custprop(wb2);
 | 
			
		||||
		});
 | 
			
		||||
	});
 | 
			
		||||
	}); });
 | 
			
		||||
 | 
			
		||||
	describe('should preserve features', function() {
 | 
			
		||||
		it('merge cells', function() {
 | 
			
		||||
 | 
			
		||||
@ -81,12 +81,25 @@ console.log("Worksheet Model:")
 | 
			
		||||
console.log(ws);
 | 
			
		||||
 | 
			
		||||
/* TEST: hidden sheets */
 | 
			
		||||
 | 
			
		||||
wb.SheetNames.push("Hidden");
 | 
			
		||||
wb.Sheets["Hidden"] = XLSX.utils.aoa_to_sheet(["Hidden".split(""), [1,2,3]]);
 | 
			
		||||
wb.Workbook = {Sheets:[]};
 | 
			
		||||
wb.Workbook.Sheets[1] = {Hidden:1};
 | 
			
		||||
 | 
			
		||||
/* TEST: properties */
 | 
			
		||||
wb.Props = {
 | 
			
		||||
	Title: "SheetJS Test",
 | 
			
		||||
	Subject: "Tests",
 | 
			
		||||
	Author: "Devs at SheetJS",
 | 
			
		||||
	Manager: "Sheet Manager",
 | 
			
		||||
	Company: "SheetJS",
 | 
			
		||||
	Category: "Experimentation",
 | 
			
		||||
	Keywords: "Test",
 | 
			
		||||
	Comments: "Nothing to say here",
 | 
			
		||||
	LastAuthor: "Not SheetJS",
 | 
			
		||||
	CreatedDate: new Date(2017,1,19)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* write file */
 | 
			
		||||
XLSX.writeFile(wb, 'sheetjs.xlsx', {bookSST:true});
 | 
			
		||||
XLSX.writeFile(wb, 'sheetjs.xlsm');
 | 
			
		||||
@ -95,6 +108,7 @@ XLSX.writeFile(wb, 'sheetjs.xls', {bookType:'biff2'}); // no formula
 | 
			
		||||
XLSX.writeFile(wb, 'sheetjs.xml.xls', {bookType:'xlml'});
 | 
			
		||||
XLSX.writeFile(wb, 'sheetjs.ods');
 | 
			
		||||
XLSX.writeFile(wb, 'sheetjs.fods');
 | 
			
		||||
XLSX.writeFile(wb, 'sheetjs.slk');
 | 
			
		||||
XLSX.writeFile(wb, 'sheetjs.csv');
 | 
			
		||||
 | 
			
		||||
/* test by reading back files */
 | 
			
		||||
@ -105,4 +119,5 @@ XLSX.readFile('sheetjs.xls');
 | 
			
		||||
XLSX.readFile('sheetjs.xml.xls');
 | 
			
		||||
XLSX.readFile('sheetjs.ods');
 | 
			
		||||
XLSX.readFile('sheetjs.fods');
 | 
			
		||||
XLSX.readFile('sheetjs.slk');
 | 
			
		||||
//XLSX.readFile('sheetjs.csv');
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										202
									
								
								xlsx.flow.js
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										202
									
								
								xlsx.flow.js
									
									
									
									
									
								
							@ -4932,6 +4932,183 @@ function dbf_to_workbook(buf, opts)/*:Workbook*/ {
 | 
			
		||||
		to_sheet: dbf_to_sheet
 | 
			
		||||
	};
 | 
			
		||||
})();
 | 
			
		||||
 | 
			
		||||
var SYLK = (function() {
 | 
			
		||||
	/* TODO: find an actual specification */
 | 
			
		||||
	function sylk_to_aoa(d/*:RawData*/, opts)/*:AOA*/ {
 | 
			
		||||
		switch(opts.type) {
 | 
			
		||||
			case 'base64': return sylk_to_aoa_str(Base64.decode(d), opts);
 | 
			
		||||
			case 'binary': return sylk_to_aoa_str(d, opts);
 | 
			
		||||
			case 'buffer': return sylk_to_aoa_str(d.toString('binary'), opts);
 | 
			
		||||
			case 'array': return sylk_to_aoa_str(cc2str(d), opts);
 | 
			
		||||
		}
 | 
			
		||||
		throw new Error("Unrecognized type " + opts.type);
 | 
			
		||||
	}
 | 
			
		||||
	function sylk_to_aoa_str(str/*:string*/, opts)/*:AOA*/ {
 | 
			
		||||
		var records = str.split(/[\n\r]+/), R = -1, C = -1, ri = 0, rj = 0, arr = [];
 | 
			
		||||
		var formats = [];
 | 
			
		||||
		var next_cell_format = null;
 | 
			
		||||
		for (; ri !== records.length; ++ri) {
 | 
			
		||||
			var record = records[ri].trim().split(";");
 | 
			
		||||
			var RT = record[0], val;
 | 
			
		||||
			if(RT === 'P') for(rj=1; rj<record.length; ++rj) switch(record[rj].charAt(0)) {
 | 
			
		||||
				case 'P':
 | 
			
		||||
					formats.push(record[rj].substr(1));
 | 
			
		||||
					break;
 | 
			
		||||
			}
 | 
			
		||||
			else if(RT !== 'C' && RT !== 'F') continue;
 | 
			
		||||
			else for(rj=1; rj<record.length; ++rj) switch(record[rj].charAt(0)) {
 | 
			
		||||
				case 'Y':
 | 
			
		||||
					R = parseInt(record[rj].substr(1))-1; C = 0;
 | 
			
		||||
					for(var j = arr.length; j <= R; ++j) arr[j] = [];
 | 
			
		||||
					break;
 | 
			
		||||
				case 'X': C = parseInt(record[rj].substr(1))-1; break;
 | 
			
		||||
				case 'K':
 | 
			
		||||
					val = record[rj].substr(1);
 | 
			
		||||
					if(val.charAt(0) === '"') val = val.substr(1,val.length - 2);
 | 
			
		||||
					else if(val === 'TRUE') val = true;
 | 
			
		||||
					else if(val === 'FALSE') val = false;
 | 
			
		||||
					else if(+val === +val) {
 | 
			
		||||
						val = +val;
 | 
			
		||||
						if(next_cell_format !== null && next_cell_format.match(/[ymdhmsYMDHMS]/)) val = numdate(val);
 | 
			
		||||
					}
 | 
			
		||||
					arr[R][C] = val;
 | 
			
		||||
					next_cell_format = null;
 | 
			
		||||
					break;
 | 
			
		||||
				case 'P':
 | 
			
		||||
					if(RT !== 'F') break;
 | 
			
		||||
					next_cell_format = formats[parseInt(record[rj].substr(1))];
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return arr;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	function sylk_to_sheet(str/*:string*/, opts)/*:Worksheet*/ { return aoa_to_sheet(sylk_to_aoa(str, opts), opts); }
 | 
			
		||||
 | 
			
		||||
	function sylk_to_workbook(str/*:string*/, opts)/*:Workbook*/ { return sheet_to_workbook(sylk_to_sheet(str, opts), opts); }
 | 
			
		||||
 | 
			
		||||
	function write_ws_cell_sylk(cell/*:Cell*/, ws/*:Worksheet*/, R/*:number*/, C/*:number*/, opts)/*:string*/ {
 | 
			
		||||
		var o = "C;Y" + (R+1) + ";X" + (C+1) + ";K";
 | 
			
		||||
		switch(cell.t) {
 | 
			
		||||
			case 'n': o += cell.v; break;
 | 
			
		||||
			case 'b': o += cell.v ? "TRUE" : "FALSE"; break;
 | 
			
		||||
			case 'e': o += cell.w || cell.v; break;
 | 
			
		||||
			case 'd': o += '"' + (cell.w || cell.v) + '"'; break;
 | 
			
		||||
			case 's': o += '"' + cell.v.replace(/"/g,"") + '"'; break;
 | 
			
		||||
		}
 | 
			
		||||
		return o;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	function sheet_to_sylk(ws/*:Worksheet*/, opts/*:?any*/)/*:string*/ {
 | 
			
		||||
		var preamble/*:Array<string>*/ = ["ID;PWXL;N;E"], o/*:Array<string>*/ = [];
 | 
			
		||||
		preamble.push("P;PGeneral");
 | 
			
		||||
		var r = decode_range(ws['!ref']), cell/*:Cell*/;
 | 
			
		||||
		for(var R = r.s.r; R <= r.e.r; ++R) {
 | 
			
		||||
			for(var C = r.s.c; C <= r.e.c; ++C) {
 | 
			
		||||
				var coord = encode_cell({r:R,c:C});
 | 
			
		||||
				if(!(cell = ws[coord]) || cell.v == null) continue;
 | 
			
		||||
				o.push(write_ws_cell_sylk(cell, ws, R, C, opts));
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		preamble.push("F;P0;DG0G8;M255");
 | 
			
		||||
		var RS = "\r\n";
 | 
			
		||||
		return preamble.join(RS) + RS + o.join(RS) + RS + "E" + RS;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return {
 | 
			
		||||
		to_workbook: sylk_to_workbook,
 | 
			
		||||
		to_sheet: sylk_to_sheet,
 | 
			
		||||
		from_sheet: sheet_to_sylk
 | 
			
		||||
	};
 | 
			
		||||
})();
 | 
			
		||||
 | 
			
		||||
var DIF = (function() {
 | 
			
		||||
	function dif_to_aoa(d/*:RawData*/, opts)/*:AOA*/ {
 | 
			
		||||
		switch(opts.type) {
 | 
			
		||||
			case 'base64': return dif_to_aoa_str(Base64.decode(d), opts);
 | 
			
		||||
			case 'binary': return dif_to_aoa_str(d, opts);
 | 
			
		||||
			case 'buffer': return dif_to_aoa_str(d.toString('binary'), opts);
 | 
			
		||||
			case 'array': return dif_to_aoa_str(cc2str(d), opts);
 | 
			
		||||
		}
 | 
			
		||||
		throw new Error("Unrecognized type " + opts.type);
 | 
			
		||||
	}
 | 
			
		||||
	function dif_to_aoa_str(str/*:string*/, opts)/*:AOA*/ {
 | 
			
		||||
		var records = str.split('\n'), R = -1, C = -1, ri = 0, arr = [];
 | 
			
		||||
		for (; ri !== records.length; ++ri) {
 | 
			
		||||
			if (records[ri].trim() === 'BOT') { arr[++R] = []; C = 0; continue; }
 | 
			
		||||
			if (R < 0) continue;
 | 
			
		||||
			var metadata = records[ri].trim().split(",");
 | 
			
		||||
			var type = metadata[0], value = metadata[1];
 | 
			
		||||
			++ri;
 | 
			
		||||
			var data = records[ri].trim();
 | 
			
		||||
			switch (+type) {
 | 
			
		||||
				case -1:
 | 
			
		||||
					if (data === 'BOT') { arr[++R] = []; C = 0; continue; }
 | 
			
		||||
					else if (data !== 'EOD') throw new Error("Unrecognized DIF special command " + data);
 | 
			
		||||
					break;
 | 
			
		||||
				case 0:
 | 
			
		||||
					if(data === 'TRUE') arr[R][C] = true;
 | 
			
		||||
					else if(data === 'FALSE') arr[R][C] = false;
 | 
			
		||||
					else if(+value == +value) arr[R][C] = +value;
 | 
			
		||||
					else if(!isNaN(new Date(value).getDate())) arr[R][C] = new Date(value);
 | 
			
		||||
					else arr[R][C] = value;
 | 
			
		||||
					++C; break;
 | 
			
		||||
				case 1:
 | 
			
		||||
					data = data.substr(1,data.length-2);
 | 
			
		||||
					arr[R][C++] = data !== '' ? data : null;
 | 
			
		||||
					break;
 | 
			
		||||
			}
 | 
			
		||||
			if (data === 'EOD') break;
 | 
			
		||||
		}
 | 
			
		||||
		return arr;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	function dif_to_sheet(str/*:string*/, opts)/*:Worksheet*/ { return aoa_to_sheet(dif_to_aoa(str, opts), opts); }
 | 
			
		||||
	function dif_to_workbook(str/*:string*/, opts)/*:Workbook*/ { return sheet_to_workbook(dif_to_sheet(str, opts), opts); }
 | 
			
		||||
 | 
			
		||||
	var sheet_to_dif = (function() {
 | 
			
		||||
		var push_field = function pf(o/*:Array<string>*/, topic/*:string*/, v/*:number*/, n/*:number*/, s/*:string*/) {
 | 
			
		||||
			o.push(topic);
 | 
			
		||||
			o.push(v + "," + n);
 | 
			
		||||
			o.push('"' + s.replace(/"/g,'""') + '"');
 | 
			
		||||
		};
 | 
			
		||||
		var push_value = function po(o/*:Array<string>*/, type/*:number*/, v/*:number*/, s/*:string*/) {
 | 
			
		||||
			o.push(type + "," + v);
 | 
			
		||||
			o.push(type == 1 ? '"' + s.replace(/"/g,'""') + '"' : s);
 | 
			
		||||
		};
 | 
			
		||||
		return function sheet_to_dif(ws/*:Worksheet*/, opts/*:?any*/)/*:string*/ {
 | 
			
		||||
			var o/*:Array<string>*/ = [];
 | 
			
		||||
			var r = decode_range(ws['!ref']), cell/*:Cell*/;
 | 
			
		||||
			push_field(o, "TABLE", 0, 1, "sheetjs");
 | 
			
		||||
			push_field(o, "VECTORS", 0, r.e.r - r.s.r + 1,"");
 | 
			
		||||
			push_field(o, "TUPLES", 0, r.e.c - r.s.c + 1,"");
 | 
			
		||||
			push_field(o, "DATA", 0, 0,"");
 | 
			
		||||
			for(var R = r.s.r; R <= r.e.r; ++R) {
 | 
			
		||||
				push_value(o, -1, 0, "BOT");
 | 
			
		||||
				for(var C = r.s.c; C <= r.e.c; ++C) {
 | 
			
		||||
					var coord = encode_cell({r:R,c:C});
 | 
			
		||||
					if(!(cell = ws[coord]) || cell.v == null) { push_value(o, 1, 0, ""); continue;}
 | 
			
		||||
					switch(cell.t) {
 | 
			
		||||
						case 'n': push_value(o, 0, (/*cell.w ||*/ cell.v), "V"); break;
 | 
			
		||||
						case 'b': push_value(o, 0, cell.v ? 1 : 0, cell.v ? "TRUE" : "FALSE"); break;
 | 
			
		||||
						case 's': push_value(o, 1, 0, cell.v); break;
 | 
			
		||||
						default: push_value(o, 1, 0, "");
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			push_value(o, -1, 0, "EOD");
 | 
			
		||||
			var RS = "\r\n";
 | 
			
		||||
			var oo = o.join(RS);
 | 
			
		||||
			//while((oo.length & 0x7F) != 0) oo += "\0";
 | 
			
		||||
			return oo;
 | 
			
		||||
		};
 | 
			
		||||
	})();
 | 
			
		||||
	return {
 | 
			
		||||
		to_workbook: dif_to_workbook,
 | 
			
		||||
		to_sheet: dif_to_sheet,
 | 
			
		||||
		from_sheet: sheet_to_dif
 | 
			
		||||
	};
 | 
			
		||||
})();
 | 
			
		||||
/* 18.4.1 charset to codepage mapping */
 | 
			
		||||
var CS2CP = ({
 | 
			
		||||
	/*::[*/0/*::]*/:    1252, /* ANSI */
 | 
			
		||||
@ -13729,12 +13906,13 @@ function parse_dom_table(table/*:HTMLElement*/, opts/*:?any*/)/*:Worksheet*/ {
 | 
			
		||||
			CS = +elt.getAttribute("colspan") || 1;
 | 
			
		||||
			if((RS = +elt.getAttribute("rowspan"))>0) merges.push({s:{r:R,c:C},e:{r:R + RS - 1, c:C + CS - 1}});
 | 
			
		||||
			var o = {t:'s', v:v};
 | 
			
		||||
			if(!isNaN(Number(v))) o = {t:'n', v:Number(v)};
 | 
			
		||||
			if(v != null && v.length && !isNaN(Number(v))) o = {t:'n', v:Number(v)};
 | 
			
		||||
			ws[encode_cell({c:C, r:R})] = o;
 | 
			
		||||
			C += CS;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	ws['!merges'] = merges;
 | 
			
		||||
	ws['!ref'] = encode_range(range);
 | 
			
		||||
	return ws;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -14218,6 +14396,18 @@ function write_csv_str(wb/*:Workbook*/, o/*:WriteOpts*/) {
 | 
			
		||||
	if(idx == 0 && !!o.sheet && wb.SheetNames[0] != o.sheet) throw new Error("Sheet not found: " + o.sheet);
 | 
			
		||||
	return sheet_to_csv(wb.Sheets[wb.SheetNames[idx]], o);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function write_obj_str(factory) {
 | 
			
		||||
	return function write_str(wb/*:Workbook*/, o/*:WriteOpts*/) {
 | 
			
		||||
		var idx = 0;
 | 
			
		||||
		for(var i=0;i<wb.SheetNames.length;++i) if(wb.SheetNames[i] == o.sheet) idx=i;
 | 
			
		||||
		if(idx == 0 && !!o.sheet && wb.SheetNames[0] != o.sheet) throw new Error("Sheet not found: " + o.sheet);
 | 
			
		||||
		return factory.from_sheet(wb.Sheets[wb.SheetNames[idx]], o);
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var write_slk_str = write_obj_str(SYLK);
 | 
			
		||||
var write_dif_str = write_obj_str(DIF);
 | 
			
		||||
/* Part 3: Packages */
 | 
			
		||||
function parse_ods(zip/*:ZIPFile*/, opts/*:?ParseOpts*/) {
 | 
			
		||||
	opts = opts || ({}/*:any*/);
 | 
			
		||||
@ -14510,7 +14700,7 @@ function write_zip(wb/*:Workbook*/, opts/*:WriteOpts*/)/*:ZIP*/ {
 | 
			
		||||
	/*::if(!wb.Props) throw "unreachable"; */
 | 
			
		||||
	f = "docProps/app.xml";
 | 
			
		||||
	if(!wb.Workbook || !wb.Workbook.Sheets) wb.Props.SheetNames = wb.SheetNames;
 | 
			
		||||
	/*:: else if(!wb.Props) throw "unreachable"; */
 | 
			
		||||
	// $FlowIgnore
 | 
			
		||||
	else wb.Props.SheetNames = wb.Workbook.Sheets.filter(function(x) { return x.Hidden != 2; }).map(function(x) { return x.name; });
 | 
			
		||||
	wb.Props.Worksheets = wb.Props.SheetNames.length;
 | 
			
		||||
	zip.file(f, write_ext_props(wb.Props, opts));
 | 
			
		||||
@ -14606,6 +14796,8 @@ function readSync(data/*:RawData*/, opts/*:?ParseOpts*/)/*:Workbook*/ {
 | 
			
		||||
		case 0xD0: return parse_xlscfb(CFB.read(d, o), o);
 | 
			
		||||
		case 0x09: return parse_xlscfb(s2a(o.type === 'base64' ? Base64.decode(d) : d), o);
 | 
			
		||||
		case 0x3C: return parse_xlml(d, o);
 | 
			
		||||
		case 0x49: if(n[1] == 0x44) return SYLK.to_workbook(d, o); break;
 | 
			
		||||
		case 0x54: if(n[1] == 0x41 && n[2] == 0x42 && n[3] == 0x4C) return DIF.to_workbook(d, o); break;
 | 
			
		||||
		case 0x50: if(n[1] == 0x4B && n[2] < 0x20 && n[3] < 0x20) return read_zip(d, o); break;
 | 
			
		||||
		case 0xEF: return parse_xlml(d, o);
 | 
			
		||||
		case 0x03: case 0x83: case 0x8B: return DBF.to_workbook(d, o);
 | 
			
		||||
@ -14669,7 +14861,10 @@ function writeSync(wb/*:Workbook*/, opts/*:?WriteOpts*/) {
 | 
			
		||||
	switch(o.bookType || 'xlsx') {
 | 
			
		||||
		case 'xml':
 | 
			
		||||
		case 'xlml': return write_string_type(write_xlml(wb, o), o);
 | 
			
		||||
		case 'slk':
 | 
			
		||||
		case 'sylk': return write_string_type(write_slk_str(wb, o), o);
 | 
			
		||||
		case 'csv': return write_string_type(write_csv_str(wb, o), o);
 | 
			
		||||
		case 'dif': return write_string_type(write_dif_str(wb, o), o);
 | 
			
		||||
		case 'fods': return write_string_type(write_ods(wb, o), o);
 | 
			
		||||
		case 'biff2': return write_binary_type(write_biff_buf(wb, o), o);
 | 
			
		||||
		case 'xlsx':
 | 
			
		||||
@ -14687,11 +14882,14 @@ function resolve_book_type(o/*?WriteFileOpts*/) {
 | 
			
		||||
		case '.xlsb': o.bookType = 'xlsb'; break;
 | 
			
		||||
		case '.fods': o.bookType = 'fods'; break;
 | 
			
		||||
		case '.xlml': o.bookType = 'xlml'; break;
 | 
			
		||||
		case '.sylk': o.bookType = 'sylk'; break;
 | 
			
		||||
	default: switch(o.file.slice(-4).toLowerCase()) {
 | 
			
		||||
		case '.xls': o.bookType = 'biff2'; break;
 | 
			
		||||
		case '.xml': o.bookType = 'xml'; break;
 | 
			
		||||
		case '.ods': o.bookType = 'ods'; break;
 | 
			
		||||
		case '.csv': o.bookType = 'csv'; break;
 | 
			
		||||
		case '.dif': o.bookType = 'dif'; break;
 | 
			
		||||
		case '.slk': o.bookType = 'sylk'; break;
 | 
			
		||||
	}}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										203
									
								
								xlsx.js
									
									
									
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										203
									
								
								xlsx.js
									
									
									
									
									
								
							@ -4878,6 +4878,183 @@ function dbf_to_workbook(buf, opts) {
 | 
			
		||||
		to_sheet: dbf_to_sheet
 | 
			
		||||
	};
 | 
			
		||||
})();
 | 
			
		||||
 | 
			
		||||
var SYLK = (function() {
 | 
			
		||||
	/* TODO: find an actual specification */
 | 
			
		||||
	function sylk_to_aoa(d, opts) {
 | 
			
		||||
		switch(opts.type) {
 | 
			
		||||
			case 'base64': return sylk_to_aoa_str(Base64.decode(d), opts);
 | 
			
		||||
			case 'binary': return sylk_to_aoa_str(d, opts);
 | 
			
		||||
			case 'buffer': return sylk_to_aoa_str(d.toString('binary'), opts);
 | 
			
		||||
			case 'array': return sylk_to_aoa_str(cc2str(d), opts);
 | 
			
		||||
		}
 | 
			
		||||
		throw new Error("Unrecognized type " + opts.type);
 | 
			
		||||
	}
 | 
			
		||||
	function sylk_to_aoa_str(str, opts) {
 | 
			
		||||
		var records = str.split(/[\n\r]+/), R = -1, C = -1, ri = 0, rj = 0, arr = [];
 | 
			
		||||
		var formats = [];
 | 
			
		||||
		var next_cell_format = null;
 | 
			
		||||
		for (; ri !== records.length; ++ri) {
 | 
			
		||||
			var record = records[ri].trim().split(";");
 | 
			
		||||
			var RT = record[0], val;
 | 
			
		||||
			if(RT === 'P') for(rj=1; rj<record.length; ++rj) switch(record[rj].charAt(0)) {
 | 
			
		||||
				case 'P':
 | 
			
		||||
					formats.push(record[rj].substr(1));
 | 
			
		||||
					break;
 | 
			
		||||
			}
 | 
			
		||||
			else if(RT !== 'C' && RT !== 'F') continue;
 | 
			
		||||
			else for(rj=1; rj<record.length; ++rj) switch(record[rj].charAt(0)) {
 | 
			
		||||
				case 'Y':
 | 
			
		||||
					R = parseInt(record[rj].substr(1))-1; C = 0;
 | 
			
		||||
					for(var j = arr.length; j <= R; ++j) arr[j] = [];
 | 
			
		||||
					break;
 | 
			
		||||
				case 'X': C = parseInt(record[rj].substr(1))-1; break;
 | 
			
		||||
				case 'K':
 | 
			
		||||
					val = record[rj].substr(1);
 | 
			
		||||
					if(val.charAt(0) === '"') val = val.substr(1,val.length - 2);
 | 
			
		||||
					else if(val === 'TRUE') val = true;
 | 
			
		||||
					else if(val === 'FALSE') val = false;
 | 
			
		||||
					else if(+val === +val) {
 | 
			
		||||
						val = +val;
 | 
			
		||||
						if(next_cell_format !== null && next_cell_format.match(/[ymdhmsYMDHMS]/)) val = numdate(val);
 | 
			
		||||
					}
 | 
			
		||||
					arr[R][C] = val;
 | 
			
		||||
					next_cell_format = null;
 | 
			
		||||
					break;
 | 
			
		||||
				case 'P':
 | 
			
		||||
					if(RT !== 'F') break;
 | 
			
		||||
					next_cell_format = formats[parseInt(record[rj].substr(1))];
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return arr;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	function sylk_to_sheet(str, opts) { return aoa_to_sheet(sylk_to_aoa(str, opts), opts); }
 | 
			
		||||
 | 
			
		||||
	function sylk_to_workbook(str, opts) { return sheet_to_workbook(sylk_to_sheet(str, opts), opts); }
 | 
			
		||||
 | 
			
		||||
	function write_ws_cell_sylk(cell, ws, R, C, opts) {
 | 
			
		||||
		var o = "C;Y" + (R+1) + ";X" + (C+1) + ";K";
 | 
			
		||||
		switch(cell.t) {
 | 
			
		||||
			case 'n': o += cell.v; break;
 | 
			
		||||
			case 'b': o += cell.v ? "TRUE" : "FALSE"; break;
 | 
			
		||||
			case 'e': o += cell.w || cell.v; break;
 | 
			
		||||
			case 'd': o += '"' + (cell.w || cell.v) + '"'; break;
 | 
			
		||||
			case 's': o += '"' + cell.v.replace(/"/g,"") + '"'; break;
 | 
			
		||||
		}
 | 
			
		||||
		return o;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	function sheet_to_sylk(ws, opts) {
 | 
			
		||||
		var preamble = ["ID;PWXL;N;E"], o = [];
 | 
			
		||||
		preamble.push("P;PGeneral");
 | 
			
		||||
		var r = decode_range(ws['!ref']), cell;
 | 
			
		||||
		for(var R = r.s.r; R <= r.e.r; ++R) {
 | 
			
		||||
			for(var C = r.s.c; C <= r.e.c; ++C) {
 | 
			
		||||
				var coord = encode_cell({r:R,c:C});
 | 
			
		||||
				if(!(cell = ws[coord]) || cell.v == null) continue;
 | 
			
		||||
				o.push(write_ws_cell_sylk(cell, ws, R, C, opts));
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		preamble.push("F;P0;DG0G8;M255");
 | 
			
		||||
		var RS = "\r\n";
 | 
			
		||||
		return preamble.join(RS) + RS + o.join(RS) + RS + "E" + RS;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return {
 | 
			
		||||
		to_workbook: sylk_to_workbook,
 | 
			
		||||
		to_sheet: sylk_to_sheet,
 | 
			
		||||
		from_sheet: sheet_to_sylk
 | 
			
		||||
	};
 | 
			
		||||
})();
 | 
			
		||||
 | 
			
		||||
var DIF = (function() {
 | 
			
		||||
	function dif_to_aoa(d, opts) {
 | 
			
		||||
		switch(opts.type) {
 | 
			
		||||
			case 'base64': return dif_to_aoa_str(Base64.decode(d), opts);
 | 
			
		||||
			case 'binary': return dif_to_aoa_str(d, opts);
 | 
			
		||||
			case 'buffer': return dif_to_aoa_str(d.toString('binary'), opts);
 | 
			
		||||
			case 'array': return dif_to_aoa_str(cc2str(d), opts);
 | 
			
		||||
		}
 | 
			
		||||
		throw new Error("Unrecognized type " + opts.type);
 | 
			
		||||
	}
 | 
			
		||||
	function dif_to_aoa_str(str, opts) {
 | 
			
		||||
		var records = str.split('\n'), R = -1, C = -1, ri = 0, arr = [];
 | 
			
		||||
		for (; ri !== records.length; ++ri) {
 | 
			
		||||
			if (records[ri].trim() === 'BOT') { arr[++R] = []; C = 0; continue; }
 | 
			
		||||
			if (R < 0) continue;
 | 
			
		||||
			var metadata = records[ri].trim().split(",");
 | 
			
		||||
			var type = metadata[0], value = metadata[1];
 | 
			
		||||
			++ri;
 | 
			
		||||
			var data = records[ri].trim();
 | 
			
		||||
			switch (+type) {
 | 
			
		||||
				case -1:
 | 
			
		||||
					if (data === 'BOT') { arr[++R] = []; C = 0; continue; }
 | 
			
		||||
					else if (data !== 'EOD') throw new Error("Unrecognized DIF special command " + data);
 | 
			
		||||
					break;
 | 
			
		||||
				case 0:
 | 
			
		||||
					if(data === 'TRUE') arr[R][C] = true;
 | 
			
		||||
					else if(data === 'FALSE') arr[R][C] = false;
 | 
			
		||||
					else if(+value == +value) arr[R][C] = +value;
 | 
			
		||||
					else if(!isNaN(new Date(value).getDate())) arr[R][C] = new Date(value);
 | 
			
		||||
					else arr[R][C] = value;
 | 
			
		||||
					++C; break;
 | 
			
		||||
				case 1:
 | 
			
		||||
					data = data.substr(1,data.length-2);
 | 
			
		||||
					arr[R][C++] = data !== '' ? data : null;
 | 
			
		||||
					break;
 | 
			
		||||
			}
 | 
			
		||||
			if (data === 'EOD') break;
 | 
			
		||||
		}
 | 
			
		||||
		return arr;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	function dif_to_sheet(str, opts) { return aoa_to_sheet(dif_to_aoa(str, opts), opts); }
 | 
			
		||||
	function dif_to_workbook(str, opts) { return sheet_to_workbook(dif_to_sheet(str, opts), opts); }
 | 
			
		||||
 | 
			
		||||
	var sheet_to_dif = (function() {
 | 
			
		||||
		var push_field = function pf(o, topic, v, n, s) {
 | 
			
		||||
			o.push(topic);
 | 
			
		||||
			o.push(v + "," + n);
 | 
			
		||||
			o.push('"' + s.replace(/"/g,'""') + '"');
 | 
			
		||||
		};
 | 
			
		||||
		var push_value = function po(o, type, v, s) {
 | 
			
		||||
			o.push(type + "," + v);
 | 
			
		||||
			o.push(type == 1 ? '"' + s.replace(/"/g,'""') + '"' : s);
 | 
			
		||||
		};
 | 
			
		||||
		return function sheet_to_dif(ws, opts) {
 | 
			
		||||
			var o = [];
 | 
			
		||||
			var r = decode_range(ws['!ref']), cell;
 | 
			
		||||
			push_field(o, "TABLE", 0, 1, "sheetjs");
 | 
			
		||||
			push_field(o, "VECTORS", 0, r.e.r - r.s.r + 1,"");
 | 
			
		||||
			push_field(o, "TUPLES", 0, r.e.c - r.s.c + 1,"");
 | 
			
		||||
			push_field(o, "DATA", 0, 0,"");
 | 
			
		||||
			for(var R = r.s.r; R <= r.e.r; ++R) {
 | 
			
		||||
				push_value(o, -1, 0, "BOT");
 | 
			
		||||
				for(var C = r.s.c; C <= r.e.c; ++C) {
 | 
			
		||||
					var coord = encode_cell({r:R,c:C});
 | 
			
		||||
					if(!(cell = ws[coord]) || cell.v == null) { push_value(o, 1, 0, ""); continue;}
 | 
			
		||||
					switch(cell.t) {
 | 
			
		||||
						case 'n': push_value(o, 0, (/*cell.w ||*/ cell.v), "V"); break;
 | 
			
		||||
						case 'b': push_value(o, 0, cell.v ? 1 : 0, cell.v ? "TRUE" : "FALSE"); break;
 | 
			
		||||
						case 's': push_value(o, 1, 0, cell.v); break;
 | 
			
		||||
						default: push_value(o, 1, 0, "");
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			push_value(o, -1, 0, "EOD");
 | 
			
		||||
			var RS = "\r\n";
 | 
			
		||||
			var oo = o.join(RS);
 | 
			
		||||
			//while((oo.length & 0x7F) != 0) oo += "\0";
 | 
			
		||||
			return oo;
 | 
			
		||||
		};
 | 
			
		||||
	})();
 | 
			
		||||
	return {
 | 
			
		||||
		to_workbook: dif_to_workbook,
 | 
			
		||||
		to_sheet: dif_to_sheet,
 | 
			
		||||
		from_sheet: sheet_to_dif
 | 
			
		||||
	};
 | 
			
		||||
})();
 | 
			
		||||
/* 18.4.1 charset to codepage mapping */
 | 
			
		||||
var CS2CP = ({
 | 
			
		||||
0:    1252, /* ANSI */
 | 
			
		||||
@ -13670,12 +13847,13 @@ function parse_dom_table(table, opts) {
 | 
			
		||||
			CS = +elt.getAttribute("colspan") || 1;
 | 
			
		||||
			if((RS = +elt.getAttribute("rowspan"))>0) merges.push({s:{r:R,c:C},e:{r:R + RS - 1, c:C + CS - 1}});
 | 
			
		||||
			var o = {t:'s', v:v};
 | 
			
		||||
			if(!isNaN(Number(v))) o = {t:'n', v:Number(v)};
 | 
			
		||||
			if(v != null && v.length && !isNaN(Number(v))) o = {t:'n', v:Number(v)};
 | 
			
		||||
			ws[encode_cell({c:C, r:R})] = o;
 | 
			
		||||
			C += CS;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	ws['!merges'] = merges;
 | 
			
		||||
	ws['!ref'] = encode_range(range);
 | 
			
		||||
	return ws;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -14159,6 +14337,18 @@ function write_csv_str(wb, o) {
 | 
			
		||||
	if(idx == 0 && !!o.sheet && wb.SheetNames[0] != o.sheet) throw new Error("Sheet not found: " + o.sheet);
 | 
			
		||||
	return sheet_to_csv(wb.Sheets[wb.SheetNames[idx]], o);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function write_obj_str(factory) {
 | 
			
		||||
	return function write_str(wb, o) {
 | 
			
		||||
		var idx = 0;
 | 
			
		||||
		for(var i=0;i<wb.SheetNames.length;++i) if(wb.SheetNames[i] == o.sheet) idx=i;
 | 
			
		||||
		if(idx == 0 && !!o.sheet && wb.SheetNames[0] != o.sheet) throw new Error("Sheet not found: " + o.sheet);
 | 
			
		||||
		return factory.from_sheet(wb.Sheets[wb.SheetNames[idx]], o);
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var write_slk_str = write_obj_str(SYLK);
 | 
			
		||||
var write_dif_str = write_obj_str(DIF);
 | 
			
		||||
/* Part 3: Packages */
 | 
			
		||||
function parse_ods(zip, opts) {
 | 
			
		||||
	opts = opts || ({});
 | 
			
		||||
@ -14448,7 +14638,8 @@ var zip = new jszip();
 | 
			
		||||
 | 
			
		||||
f = "docProps/app.xml";
 | 
			
		||||
	if(!wb.Workbook || !wb.Workbook.Sheets) wb.Props.SheetNames = wb.SheetNames;
 | 
			
		||||
else wb.Props.SheetNames = wb.Workbook.Sheets.filter(function(x) { return x.Hidden != 2; }).map(function(x) { return x.name; });
 | 
			
		||||
	// $FlowIgnore
 | 
			
		||||
	else wb.Props.SheetNames = wb.Workbook.Sheets.filter(function(x) { return x.Hidden != 2; }).map(function(x) { return x.name; });
 | 
			
		||||
	wb.Props.Worksheets = wb.Props.SheetNames.length;
 | 
			
		||||
	zip.file(f, write_ext_props(wb.Props, opts));
 | 
			
		||||
	ct.extprops.push(f);
 | 
			
		||||
@ -14542,6 +14733,8 @@ function readSync(data, opts) {
 | 
			
		||||
		case 0xD0: return parse_xlscfb(CFB.read(d, o), o);
 | 
			
		||||
		case 0x09: return parse_xlscfb(s2a(o.type === 'base64' ? Base64.decode(d) : d), o);
 | 
			
		||||
		case 0x3C: return parse_xlml(d, o);
 | 
			
		||||
		case 0x49: if(n[1] == 0x44) return SYLK.to_workbook(d, o); break;
 | 
			
		||||
		case 0x54: if(n[1] == 0x41 && n[2] == 0x42 && n[3] == 0x4C) return DIF.to_workbook(d, o); break;
 | 
			
		||||
		case 0x50: if(n[1] == 0x4B && n[2] < 0x20 && n[3] < 0x20) return read_zip(d, o); break;
 | 
			
		||||
		case 0xEF: return parse_xlml(d, o);
 | 
			
		||||
		case 0x03: case 0x83: case 0x8B: return DBF.to_workbook(d, o);
 | 
			
		||||
@ -14605,7 +14798,10 @@ function writeSync(wb, opts) {
 | 
			
		||||
	switch(o.bookType || 'xlsx') {
 | 
			
		||||
		case 'xml':
 | 
			
		||||
		case 'xlml': return write_string_type(write_xlml(wb, o), o);
 | 
			
		||||
		case 'slk':
 | 
			
		||||
		case 'sylk': return write_string_type(write_slk_str(wb, o), o);
 | 
			
		||||
		case 'csv': return write_string_type(write_csv_str(wb, o), o);
 | 
			
		||||
		case 'dif': return write_string_type(write_dif_str(wb, o), o);
 | 
			
		||||
		case 'fods': return write_string_type(write_ods(wb, o), o);
 | 
			
		||||
		case 'biff2': return write_binary_type(write_biff_buf(wb, o), o);
 | 
			
		||||
		case 'xlsx':
 | 
			
		||||
@ -14623,11 +14819,14 @@ function resolve_book_type(o/*?WriteFileOpts*/) {
 | 
			
		||||
		case '.xlsb': o.bookType = 'xlsb'; break;
 | 
			
		||||
		case '.fods': o.bookType = 'fods'; break;
 | 
			
		||||
		case '.xlml': o.bookType = 'xlml'; break;
 | 
			
		||||
		case '.sylk': o.bookType = 'sylk'; break;
 | 
			
		||||
	default: switch(o.file.slice(-4).toLowerCase()) {
 | 
			
		||||
		case '.xls': o.bookType = 'biff2'; break;
 | 
			
		||||
		case '.xml': o.bookType = 'xml'; break;
 | 
			
		||||
		case '.ods': o.bookType = 'ods'; break;
 | 
			
		||||
		case '.csv': o.bookType = 'csv'; break;
 | 
			
		||||
		case '.dif': o.bookType = 'dif'; break;
 | 
			
		||||
		case '.slk': o.bookType = 'sylk'; break;
 | 
			
		||||
	}}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user