js-xls-rails 0.5.0 → 0.6.9

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e52ed40aeb2c846ad73d0768f4991f6787bcb29e
4
- data.tar.gz: 31a8c821c07e70e54098e846a770a0651ba1209e
3
+ metadata.gz: 92c7ac66d0955c7a92d4aaea434b283a4fac8171
4
+ data.tar.gz: 3a053528aeb1d0fca9665a906a8201dd23895db2
5
5
  SHA512:
6
- metadata.gz: 848731767864e368d3d3b6f267aae3317db16d4a5a18222acfad33b250443903fe07b7f9dc523bf917f54e137fc1c6b12e683a4fc7523e12a7834e9839b015af
7
- data.tar.gz: 0fb9156551c66343a4aaed33379176f17c93dd601a470834b886f13d836e24e4ab21d4de7c9b6da3482259998cf8bbe36ae2b97fee9e8b976f4d80cbc03ee84e
6
+ metadata.gz: 1967ca1d89c011d4677adb481e93382c06510124d239c67e55e8f30289a806d5806b649be57a566d7d175ce9b56f820e10da71a096624a62d214d71b343b79c4
7
+ data.tar.gz: f4f7741e282d3bd7446f6f64ee314a0e9cdc7dcc81bb43dc5c892034295a0fa34bb5d4fa3572ae3243e175e90633b00f2512f7d89b428ed8e8379eaf3a2fe5b0
@@ -1,5 +1,5 @@
1
1
  module Jsxls
2
2
  module Rails
3
- VERSION = "0.5.0"
3
+ VERSION = "0.6.9"
4
4
  end
5
5
  end
@@ -1,8 +1,9 @@
1
- /* xls.js (C) 2013 SheetJS -- http://sheetjs.com */
1
+ /* xls.js (C) 2013-2014 SheetJS -- http://sheetjs.com */
2
2
  /* vim: set ts=2: */
3
3
  /*jshint eqnull:true, funcscope:true */
4
4
  var XLS = {};
5
5
  (function(XLS){
6
+ XLS.version = '0.6.9';
6
7
  if(typeof module !== "undefined" && typeof require !== 'undefined') {
7
8
  if(typeof cptable === 'undefined') var cptable = require('codepage');
8
9
  var current_codepage = 1252, current_cptable = cptable[1252];
@@ -12,41 +13,8 @@ function reset_cp() {
12
13
  }
13
14
  function _getchar(x) { return String.fromCharCode(x); }
14
15
 
15
- /* Buffer.concat was added in the 0.8 series, so this is for older versions */
16
- if(typeof Buffer !== "undefined" && !Buffer.concat)
17
- Buffer.concat = function(list, length) {
18
- if (!Array.isArray(list)) {
19
- throw new TypeError('Usage: Buffer.concat(list, [length])');
20
- }
21
-
22
- if (list.length === 0) {
23
- return new Buffer(0);
24
- } else if (list.length === 1) {
25
- return list[0];
26
- }
27
- var i, buf;
28
- if (typeof length !== 'number') {
29
- length = 0;
30
- for (i = 0; i < list.length; i++) {
31
- buf = list[i];
32
- length += buf.length;
33
- }
34
- }
35
-
36
- var buffer = new Buffer(length);
37
- var pos = 0;
38
- for (i = 0; i < list.length; i++) {
39
- buf = list[i];
40
- buf.copy(buffer, pos);
41
- pos += buf.length;
42
- }
43
- return buffer;
44
- };
45
-
46
- var Buffers = Array;
47
-
48
16
  function readIEEE754(buf, idx, isLE, nl, ml) {
49
- if(isLE === undefined) isLE = true;
17
+ if(typeof isLE === 'undefined') isLE = true;
50
18
  if(!nl) nl = 8;
51
19
  if(!ml && nl === 8) ml = 52;
52
20
  var e, m, el = nl * 8 - ml - 1, eMax = (1 << el) - 1, eBias = eMax >> 1;
@@ -111,6 +79,7 @@ function s2a(s) {
111
79
  return w;
112
80
  }
113
81
 
82
+ var __toBuffer;
114
83
  if(typeof Buffer !== "undefined") {
115
84
  Buffer.prototype.hexlify= function() { return this.toString('hex'); };
116
85
  Buffer.prototype.utf16le= function(s,e){return this.toString('utf16le',s,e).replace(/\u0000/,'').replace(/[\u0001-\u0006]/,'!');};
@@ -122,7 +91,6 @@ if(typeof Buffer !== "undefined") {
122
91
  if(len === 0) return "";
123
92
  if(typeof current_cptable === "undefined") return this.utf8(i+4,i+4+len-1);
124
93
  var t = Array(this.slice(i+4,i+4+len-1));
125
- //1console.log("start", this.l, len, t);
126
94
  var c, j = i+4, o = "", cc;
127
95
  for(;j!=i+4+len;++j) {
128
96
  c = this.readUInt8(j);
@@ -134,27 +102,33 @@ if(typeof Buffer !== "undefined") {
134
102
  if(typeof cc === 'undefined') throw "Unrecognized character " + c.toString(16);
135
103
  if(c === 0) break;
136
104
  o += cc;
137
- //1console.log(cc, cc.charCodeAt(0), o, this.l);
138
105
  }
139
106
  return o;
140
107
  };
108
+ __toBuffer = function(bufs) { return Buffer.concat(bufs[0]); };
109
+ } else {
110
+ __toBuffer = function(bufs) {
111
+ var x = [];
112
+ for(var i = 0; i != bufs[0].length; ++i) { x = x.concat(bufs[0][i]); }
113
+ return x;
114
+ };
141
115
  }
142
116
 
143
- Array.prototype.readUInt8 = function(idx) { return this[idx]; };
144
- Array.prototype.readUInt16LE = function(idx) { return this[idx+1]*(1<<8)+this[idx]; };
145
- Array.prototype.readInt16LE = function(idx) { var u = this.readUInt16LE(idx); if(!(u & 0x8000)) return u; return (0xffff - u + 1) * -1; };
146
- Array.prototype.readUInt32LE = function(idx) { return this[idx+3]*(1<<24)+this[idx+2]*(1<<16)+this[idx+1]*(1<<8)+this[idx]; };
147
- Array.prototype.readInt32LE = function(idx) { var u = this.readUInt32LE(idx); if(!(u & 0x80000000)) return u; return (0xffffffff - u + 1) * -1; };
148
- Array.prototype.readDoubleLE = function(idx) { return readIEEE754(this, idx||0);};
117
+ var __readUInt8 = function(b, idx) { return b.readUInt8 ? b.readUInt8(idx) : b[idx]; };
118
+ var __readUInt16LE = function(b, idx) { return b.readUInt16LE ? b.readUInt16LE(idx) : b[idx+1]*(1<<8)+b[idx]; };
119
+ var __readInt16LE = function(b, idx) { var u = __readUInt16LE(b,idx); if(!(u & 0x8000)) return u; return (0xffff - u + 1) * -1; };
120
+ var __readUInt32LE = function(b, idx) { return b.readUInt32LE ? b.readUInt32LE(idx) : b[idx+3]*(1<<24)+b[idx+2]*(1<<16)+b[idx+1]*(1<<8)+b[idx]; };
121
+ var __readInt32LE = function(b, idx) { if(b.readInt32LE) return b.readInt32LE(idx); var u = __readUInt32LE(b,idx); if(!(u & 0x80000000)) return u; return (0xffffffff - u + 1) * -1; };
122
+ var __readDoubleLE = function(b, idx) { return b.readDoubleLE ? b.readDoubleLE(idx) : readIEEE754(b, idx||0);};
149
123
 
150
- Array.prototype.hexlify = function() { return this.map(function(x){return (x<16?"0":"") + x.toString(16);}).join(""); };
124
+ var __hexlify = function(b) { return b.map(function(x){return (x<16?"0":"") + x.toString(16);}).join(""); };
151
125
 
152
- Array.prototype.utf16le = function(s,e) { var str = ""; for(var i=s; i<e; i+=2) str += String.fromCharCode(this.readUInt16LE(i)); return str.replace(/\u0000/,'').replace(/[\u0001-\u0006]/,'!'); };
126
+ var __utf16le = function(b,s,e) { if(b.utf16le) return b.utf16le(s,e); var str = ""; for(var i=s; i<e; i+=2) str += String.fromCharCode(__readUInt16LE(b,i)); return str.replace(/\u0000/,'').replace(/[\u0001-\u0006]/,'!'); };
153
127
 
154
- Array.prototype.utf8 = function(s,e) { var str = ""; for(var i=s; i<e; i++) str += String.fromCharCode(this.readUInt8(i)); return str; };
128
+ var __utf8 = function(b,s,e) { if(b.utf8) return b.utf8(s,e); var str = ""; for(var i=s; i<e; i++) str += String.fromCharCode(__readUInt8(b,i)); return str; };
155
129
 
156
- Array.prototype.lpstr = function(i) { var len = this.readUInt32LE(i); return len > 0 ? this.utf8(i+4,i+4+len-1) : "";};
157
- Array.prototype.lpwstr = function(i) { var len = 2*this.readUInt32LE(i); return this.utf8(i+4,i+4+len-1);};
130
+ var __lpstr = function(b,i) { if(b.lpstr) return b.lpstr(i); var len = __readUInt32LE(b,i); return len > 0 ? __utf8(b, i+4,i+4+len-1) : "";};
131
+ var __lpwstr = function(b,i) { if(b.lpwstr) return b.lpwstr(i); var len = 2*__readUInt32LE(b,i); return __utf8(b, i+4,i+4+len-1);};
158
132
 
159
133
  function bconcat(bufs) { return (typeof Buffer !== 'undefined') ? Buffer.concat(bufs) : [].concat.apply([], bufs); }
160
134
 
@@ -162,69 +136,64 @@ function ReadShift(size, t) {
162
136
  var o, w, vv, i, loc; t = t || 'u';
163
137
  if(size === 'ieee754') { size = 8; t = 'f'; }
164
138
  switch(size) {
165
- case 1: o = this.readUInt8(this.l); break;
166
- case 2: o=t==='u'?this.readUInt16LE(this.l):this.readInt16LE(this.l);break;
167
- case 4: o = this.readUInt32LE(this.l); break;
168
- case 8: if(t === 'f') { o = this.readDoubleLE(this.l); break; }
139
+ case 1: o = __readUInt8(this, this.l); break;
140
+ case 2: o=(t==='u' ? __readUInt16LE : __readInt16LE)(this, this.l); break;
141
+ case 4: o = __readUInt32LE(this, this.l); break;
142
+ case 8: if(t === 'f') { o = __readDoubleLE(this, this.l); break; }
169
143
  /* falls through */
170
144
  case 16: o = this.toString('hex', this.l,this.l+size); break;
171
145
 
172
- case 'utf8': size = t; o = this.utf8(this.l, this.l + size); break;
173
- case 'utf16le': size = 2*t; o = this.utf16le(this.l, this.l + size); break;
146
+ case 'utf8': size = t; o = __utf8(this, this.l, this.l + size); break;
147
+ case 'utf16le': size=2*t; o = __utf16le(this, this.l, this.l + size); break;
174
148
 
175
149
  /* [MS-OLEDS] 2.1.4 LengthPrefixedAnsiString */
176
- case 'lpstr': o = this.lpstr(this.l); size = 5 + o.length; break;
150
+ case 'lpstr': o = __lpstr(this, this.l); size = 5 + o.length; break;
177
151
 
178
- case 'lpwstr': o = this.lpwstr(this.l); size = 5 + o.length; if(o[o.length-1] == '\u0000') size += 2; break;
152
+ case 'lpwstr': o = __lpwstr(this, this.l); size = 5 + o.length; if(o[o.length-1] == '\u0000') size += 2; break;
179
153
 
180
154
  /* sbcs and dbcs support continue records in the SST way TODO codepages */
181
155
  /* TODO: DBCS http://msdn.microsoft.com/en-us/library/cc194788.aspx */
182
156
  case 'dbcs': size = 2*t; o = ""; loc = this.l;
183
157
  for(i = 0; i != t; ++i) {
184
158
  if(this.lens && this.lens.indexOf(loc) !== -1) {
185
- w = this.readUInt8(loc);
159
+ w = __readUInt8(this, loc);
186
160
  this.l = loc + 1;
187
161
  vv = ReadShift.call(this, w ? 'dbcs' : 'sbcs', t-i);
188
162
  return o + vv;
189
163
  }
190
- o += _getchar(this.readUInt16LE(loc));
164
+ o += _getchar(__readUInt16LE(this, loc));
191
165
  loc+=2;
192
166
  } break;
193
167
 
194
168
  case 'sbcs': size = t; o = ""; loc = this.l;
195
169
  for(i = 0; i != t; ++i) {
196
170
  if(this.lens && this.lens.indexOf(loc) !== -1) {
197
- w = this.readUInt8(loc);
171
+ w = __readUInt8(this, loc);
198
172
  this.l = loc + 1;
199
173
  vv = ReadShift.call(this, w ? 'dbcs' : 'sbcs', t-i);
200
174
  return o + vv;
201
175
  }
202
- o += _getchar(this.readUInt8(loc));
176
+ o += _getchar(__readUInt8(this, loc));
203
177
  loc+=1;
204
178
  } break;
205
179
 
206
180
  case 'cstr': size = 0; o = "";
207
- while((w=this.readUInt8(this.l + size++))!==0) o+= _getchar(w);
181
+ while((w=__readUInt8(this, this.l + size++))!==0) o+= _getchar(w);
208
182
  break;
209
183
  case 'wstr': size = 0; o = "";
210
- while((w=this.readUInt16LE(this.l +size))!==0){o+= _getchar(w);size+=2;}
184
+ while((w=__readUInt16LE(this,this.l +size))!==0){o+= _getchar(w);size+=2;}
211
185
  size+=2; break;
212
186
  }
213
187
  this.l+=size; return o;
214
188
  }
215
189
 
216
190
  function CheckField(hexstr, fld) {
217
- var m = this.slice(this.l, this.l+hexstr.length/2).hexlify('hex');
191
+ var b = this.slice(this.l, this.l+hexstr.length/2);
192
+ var m = b.hexlify ? b.hexlify() : __hexlify(b);
218
193
  if(m !== hexstr) throw (fld||"") + 'Expected ' + hexstr + ' saw ' + m;
219
194
  this.l += hexstr.length/2;
220
195
  }
221
196
 
222
- function WarnField(hexstr, fld) {
223
- var m = this.slice(this.l, this.l+hexstr.length/2).hexlify('hex');
224
- if(m !== hexstr) console.error((fld||"") + 'Expected ' + hexstr +' saw ' + m);
225
- this.l += hexstr.length/2;
226
- }
227
-
228
197
  function prep_blob(blob, pos) {
229
198
  blob.read_shift = ReadShift.bind(blob);
230
199
  blob.chk = CheckField;
@@ -233,14 +202,14 @@ function prep_blob(blob, pos) {
233
202
  return [read, chk];
234
203
  }
235
204
 
236
- /* ssf.js (C) 2013 SheetJS -- http://sheetjs.com */
205
+ /* ssf.js (C) 2013-2014 SheetJS -- http://sheetjs.com */
237
206
  var SSF = {};
238
207
  var make_ssf = function(SSF){
239
- String.prototype.reverse=function(){return this.split("").reverse().join("");};
240
- var _strrev = function(x) { return String(x).reverse(); };
208
+ var _strrev = function(x) { return String(x).split("").reverse().join("");};
241
209
  function fill(c,l) { return new Array(l+1).join(c); }
242
210
  function pad(v,d,c){var t=String(v);return t.length>=d?t:(fill(c||0,d-t.length)+t);}
243
211
  function rpad(v,d,c){var t=String(v);return t.length>=d?t:(t+fill(c||0,d-t.length));}
212
+ SSF.version = '0.5.8';
244
213
  /* Options */
245
214
  var opts_fmt = {};
246
215
  function fixopts(o){for(var y in opts_fmt) if(o[y]===undefined) o[y]=opts_fmt[y];}
@@ -249,6 +218,7 @@ opts_fmt.date1904 = 0;
249
218
  opts_fmt.output = "";
250
219
  opts_fmt.mode = "";
251
220
  var table_fmt = {
221
+ 0: 'General',
252
222
  1: '0',
253
223
  2: '0.00',
254
224
  3: '#,##0',
@@ -275,7 +245,9 @@ var table_fmt = {
275
245
  46: '[h]:mm:ss',
276
246
  47: 'mmss.0',
277
247
  48: '##0.0E+0',
278
- 49: '@'
248
+ 49: '@',
249
+ 56: '"上午/下午 "hh"時"mm"分"ss"秒 "',
250
+ 65535: 'General'
279
251
  };
280
252
  var days = [
281
253
  ['Sun', 'Sunday'],
@@ -305,12 +277,12 @@ var frac = function frac(x, D, mixed) {
305
277
  var B = x * sgn;
306
278
  var P_2 = 0, P_1 = 1, P = 0;
307
279
  var Q_2 = 1, Q_1 = 0, Q = 0;
308
- var A = B|0;
280
+ var A = Math.floor(B);
309
281
  while(Q_1 < D) {
310
- A = B|0;
282
+ A = Math.floor(B);
311
283
  P = A * P_1 + P_2;
312
284
  Q = A * Q_1 + Q_2;
313
- if((B - A) < 0.0000000001) break;
285
+ if((B - A) < 0.0000000005) break;
314
286
  B = 1 / (B - A);
315
287
  P_2 = P_1; P_1 = P;
316
288
  Q_2 = Q_1; Q_1 = Q;
@@ -318,6 +290,7 @@ var frac = function frac(x, D, mixed) {
318
290
  if(Q > D) { Q = Q_1; P = P_1; }
319
291
  if(Q > D) { Q = Q_2; P = P_2; }
320
292
  if(!mixed) return [0, sgn * P, Q];
293
+ if(Q===0) throw "Unexpected state: "+P+" "+P_1+" "+P_2+" "+Q+" "+Q_1+" "+Q_2;
321
294
  var q = Math.floor(sgn * P/Q);
322
295
  return [q, sgn*P - q*Q, Q];
323
296
  };
@@ -331,25 +304,26 @@ var general_fmt = function(v) {
331
304
  else if(V >= 0.0001 && V < 0.001) o = v.toPrecision(6);
332
305
  else if(V >= Math.pow(10,10) && V < Math.pow(10,11)) o = v.toFixed(10).substr(0,12);
333
306
  else if(V > Math.pow(10,-9) && V < Math.pow(10,11)) {
334
- o = v.toFixed(12).replace(/(\.[0-9]*[1-9])0*$/,"$1").replace(/\.$/,"");
307
+ o = v.toFixed(12).replace(/(\.[0-9]*[1-9])0*$/,"$1").replace(/\.$/,"");
335
308
  if(o.length > 11+(v<0?1:0)) o = v.toPrecision(10);
336
309
  if(o.length > 11+(v<0?1:0)) o = v.toExponential(5);
337
310
  }
338
311
  else {
339
312
  o = v.toFixed(11).replace(/(\.[0-9]*[1-9])0*$/,"$1");
340
- if(o.length > 11 + (v<0?1:0)) o = v.toPrecision(6);
313
+ if(o.length > 11 + (v<0?1:0)) o = v.toPrecision(6);
341
314
  }
342
315
  o = o.replace(/(\.[0-9]*[1-9])0+e/,"$1e").replace(/\.0*e/,"e");
343
316
  return o.replace("e","E").replace(/\.0*$/,"").replace(/\.([0-9]*[^0])0*$/,".$1").replace(/(E[+-])([0-9])$/,"$1"+"0"+"$2");
344
317
  }
345
318
  if(typeof v === 'string') return v;
346
- throw "unsupported value in General format: " + v;
319
+ throw new Error("unsupported value in General format: " + v);
347
320
  };
348
321
  SSF._general = general_fmt;
349
322
  var parse_date_code = function parse_date_code(v,opts) {
350
- var date = Math.floor(v), time = Math.round(86400 * (v - date)), dow=0;
323
+ var date = Math.floor(v), time = Math.floor(86400 * (v - date)+1e-6), dow=0;
351
324
  var dout=[], out={D:date, T:time, u:86400*(v-date)-time}; fixopts(opts = (opts||{}));
352
325
  if(opts.date1904) date += 1462;
326
+ if(date > 2958465) return null;
353
327
  if(date === 60) {dout = [1900,2,29]; dow=3;}
354
328
  else if(date === 0) {dout = [1900,1,0]; dow=6;}
355
329
  else {
@@ -359,7 +333,7 @@ var parse_date_code = function parse_date_code(v,opts) {
359
333
  d.setDate(d.getDate() + date - 1);
360
334
  dout = [d.getFullYear(), d.getMonth()+1,d.getDate()];
361
335
  dow = d.getDay();
362
- if(opts.mode === 'excel' && date < 60) dow = (dow + 6) % 7;
336
+ if(/* opts.mode === 'excel' && */ date < 60) dow = (dow + 6) % 7;
363
337
  }
364
338
  out.y = dout[0]; out.m = dout[1]; out.d = dout[2];
365
339
  out.S = time % 60; time = Math.floor(time / 60);
@@ -369,61 +343,63 @@ var parse_date_code = function parse_date_code(v,opts) {
369
343
  return out;
370
344
  };
371
345
  SSF.parse_date_code = parse_date_code;
346
+ /*jshint -W086 */
372
347
  var write_date = function(type, fmt, val) {
373
348
  if(val < 0) return "";
349
+ var o;
374
350
  switch(type) {
375
351
  case 'y': switch(fmt) { /* year */
376
352
  case 'y': case 'yy': return pad(val.y % 100,2);
377
- default: return val.y;
378
- } break;
353
+ default: return pad(val.y % 10000,4);
354
+ }
379
355
  case 'm': switch(fmt) { /* month */
380
356
  case 'm': return val.m;
381
357
  case 'mm': return pad(val.m,2);
382
358
  case 'mmm': return months[val.m-1][1];
383
- case 'mmmm': return months[val.m-1][2];
384
359
  case 'mmmmm': return months[val.m-1][0];
385
- default: throw 'bad month format: ' + fmt;
386
- } break;
360
+ default: return months[val.m-1][2];
361
+ }
387
362
  case 'd': switch(fmt) { /* day */
388
363
  case 'd': return val.d;
389
364
  case 'dd': return pad(val.d,2);
390
365
  case 'ddd': return days[val.q][0];
391
- case 'dddd': return days[val.q][1];
392
- default: throw 'bad day format: ' + fmt;
393
- } break;
366
+ default: return days[val.q][1];
367
+ }
394
368
  case 'h': switch(fmt) { /* 12-hour */
395
369
  case 'h': return 1+(val.H+11)%12;
396
370
  case 'hh': return pad(1+(val.H+11)%12, 2);
397
371
  default: throw 'bad hour format: ' + fmt;
398
- } break;
372
+ }
399
373
  case 'H': switch(fmt) { /* 24-hour */
400
374
  case 'h': return val.H;
401
375
  case 'hh': return pad(val.H, 2);
402
376
  default: throw 'bad hour format: ' + fmt;
403
- } break;
377
+ }
404
378
  case 'M': switch(fmt) { /* minutes */
405
379
  case 'm': return val.M;
406
380
  case 'mm': return pad(val.M, 2);
407
381
  default: throw 'bad minute format: ' + fmt;
408
- } break;
382
+ }
409
383
  case 's': switch(fmt) { /* seconds */
410
- case 's': return val.S;
411
- case 'ss': return pad(val.S, 2);
412
- case 'ss.0': return pad(val.S,2) + "." + Math.round(10*val.u);
384
+ case 's': return Math.round(val.S+val.u);
385
+ case 'ss': return pad(Math.round(val.S+val.u), 2);
386
+ case 'ss.0': o = pad(Math.round(10*(val.S+val.u)),3); return o.substr(0,2)+"." + o.substr(2);
387
+ case 'ss.00': o = pad(Math.round(100*(val.S+val.u)),4); return o.substr(0,2)+"." + o.substr(2);
388
+ case 'ss.000': o = pad(Math.round(1000*(val.S+val.u)),5); return o.substr(0,2)+"." + o.substr(2);
413
389
  default: throw 'bad second format: ' + fmt;
414
- } break;
390
+ }
415
391
  case 'Z': switch(fmt) {
416
- case '[h]': return val.D*24+val.H;
392
+ case '[h]': case '[hh]': o = val.D*24+val.H; break;
393
+ case '[m]': case '[mm]': o = (val.D*24+val.H)*60+val.M; break;
394
+ case '[s]': case '[ss]': o = ((val.D*24+val.H)*60+val.M)*60+Math.round(val.S+val.u); break;
417
395
  default: throw 'bad abstime format: ' + fmt;
418
- } break;
396
+ } return fmt.length === 3 ? o : pad(o, 2);
419
397
  /* TODO: handle the ECMA spec format ee -> yy */
420
398
  case 'e': { return val.y; } break;
421
- case 'A': return (val.h>=12 ? 'P' : 'A') + fmt.substr(1);
422
- default: throw 'bad format type ' + type + ' in ' + fmt;
423
399
  }
424
400
  };
425
- String.prototype.reverse = function() { return this.split("").reverse().join(""); };
426
- var commaify = function(s) { return s.reverse().replace(/.../g,"$&,").reverse().replace(/^,/,""); };
401
+ /*jshint +W086 */
402
+ var commaify = function(s) { return _strrev(_strrev(s).replace(/.../g,"$&,")).replace(/^,/,""); };
427
403
  var write_num = function(type, fmt, val) {
428
404
  if(type === '(') {
429
405
  var ffmt = fmt.replace(/\( */,"").replace(/ \)/,"").replace(/\)/,"");
@@ -435,37 +411,60 @@ var write_num = function(type, fmt, val) {
435
411
  if(mul !== 0) return write_num(type, fmt, val * Math.pow(10,2*mul)) + fill("%",mul);
436
412
  if(fmt.indexOf("E") > -1) {
437
413
  var idx = fmt.indexOf("E") - fmt.indexOf(".") - 1;
438
- if(fmt == '##0.0E+0') {
439
- var ee = Number(val.toExponential(0).substr(3))%3;
440
- o = (val/Math.pow(10,ee%3)).toPrecision(idx+1+(ee%3)).replace(/^([+-]?)([0-9]*)\.([0-9]*)[Ee]/,function($$,$1,$2,$3) { return $1 + $2 + $3.substr(0,ee) + "." + $3.substr(ee) + "E"; });
414
+ if(fmt.match(/^#+0.0E\+0$/)) {
415
+ var period = fmt.indexOf("."); if(period === -1) period=fmt.indexOf('E');
416
+ var ee = (Number(val.toExponential(0).substr(2+(val<0))))%period;
417
+ if(ee < 0) ee += period;
418
+ o = (val/Math.pow(10,ee)).toPrecision(idx+1+(period+ee)%period);
419
+ if(!o.match(/[Ee]/)) {
420
+ var fakee = (Number(val.toExponential(0).substr(2+(val<0))));
421
+ if(o.indexOf(".") === -1) o = o[0] + "." + o.substr(1) + "E+" + (fakee - o.length+ee);
422
+ else o += "E+" + (fakee - ee);
423
+ while(o.substr(0,2) === "0.") {
424
+ o = o[0] + o.substr(2,period) + "." + o.substr(2+period);
425
+ o = o.replace(/^0+([1-9])/,"$1").replace(/^0+\./,"0.");
426
+ }
427
+ o = o.replace(/\+-/,"-");
428
+ }
429
+ o = o.replace(/^([+-]?)([0-9]*)\.([0-9]*)[Ee]/,function($$,$1,$2,$3) { return $1 + $2 + $3.substr(0,(period+ee)%period) + "." + $3.substr(ee) + "E"; });
441
430
  } else o = val.toExponential(idx);
442
431
  if(fmt.match(/E\+00$/) && o.match(/e[+-][0-9]$/)) o = o.substr(0,o.length-1) + "0" + o[o.length-1];
443
432
  if(fmt.match(/E\-/) && o.match(/e\+/)) o = o.replace(/e\+/,"e");
444
433
  return o.replace("e","E");
445
434
  }
446
435
  if(fmt[0] === "$") return "$"+write_num(type,fmt.substr(fmt[1]==' '?2:1),val);
447
- var r, ff, aval = val < 0 ? -val : val, sign = val < 0 ? "-" : "";
448
- if((r = fmt.match(/# (\?+) \/ (\d+)/))) {
449
- var den = Number(r[2]), rnd = Math.round(aval * den), base = Math.floor(rnd/den);
436
+ var r, rr, ff, aval = val < 0 ? -val : val, sign = val < 0 ? "-" : "";
437
+ if((r = fmt.match(/# (\?+)([ ]?)\/([ ]?)(\d+)/))) {
438
+ var den = Number(r[4]), rnd = Math.round(aval * den), base = Math.floor(rnd/den);
450
439
  var myn = (rnd - base*den), myd = den;
451
- return sign + (base?base:"") + " " + (myn === 0 ? fill(" ", r[1].length + 1 + r[2].length) : pad(myn,r[1].length," ") + "/" + pad(myd,r[2].length));
440
+ return sign + (base?base:"") + " " + (myn === 0 ? fill(" ", r[1].length + 1 + r[4].length) : pad(myn,r[1].length," ") + r[2] + "/" + r[3] + pad(myd,r[4].length));
441
+ }
442
+ if(fmt.match(/^#+0+$/)) fmt = fmt.replace(/#/g,"");
443
+ if(fmt.match(/^00+$/)) return (val<0?"-":"")+pad(Math.round(aval),fmt.length);
444
+ if(fmt.match(/^[#?]+$/)) return String(Math.round(val)).replace(/^0$/,"");
445
+ if((r = fmt.match(/^#*0+\.(0+)/))) {
446
+ o = Math.round(val * Math.pow(10,r[1].length));
447
+ return String(o/Math.pow(10,r[1].length)).replace(/^([^\.]+)$/,"$1."+r[1]).replace(/\.$/,"."+r[1]).replace(/\.([0-9]*)$/,function($$, $1) { return "." + $1 + fill("0", r[1].length-$1.length); });
448
+ }
449
+ if((r = fmt.match(/^(0*)\.(#*)$/))) {
450
+ o = Math.round(val*Math.pow(10,r[2].length));
451
+ return String(o * Math.pow(10,-r[2].length)).replace(/\.(\d*[1-9])0*$/,".$1").replace(/^([-]?\d*)$/,"$1.").replace(/^0\./,r[1].length?"0.":".");
452
+ }
453
+ if((r = fmt.match(/^#,##0([.]?)$/))) return sign + commaify(String(Math.round(aval)));
454
+ if((r = fmt.match(/^#,##0\.([#0]*0)$/))) {
455
+ rr = Math.round((val-Math.floor(val))*Math.pow(10,r[1].length));
456
+ return val < 0 ? "-" + write_num(type, fmt, -val) : commaify(String(Math.floor(val))) + "." + pad(rr,r[1].length,0);
457
+ }
458
+ if((r = fmt.match(/^# ([?]+)([ ]?)\/([ ]?)([?]+)/))) {
459
+ rr = Math.min(Math.max(r[1].length, r[4].length),7);
460
+ ff = frac(aval, Math.pow(10,rr)-1, true);
461
+ return sign + (ff[0]||(ff[1] ? "" : "0")) + " " + (ff[1] ? pad(ff[1],rr," ") + r[2] + "/" + r[3] + rpad(ff[2],rr," "): fill(" ", 2*rr+1 + r[2].length + r[3].length));
452
462
  }
453
- if(fmt.match(/^00*$/)) return (val<0?"-":"")+pad(Math.round(Math.abs(val)), fmt.length);
454
- if(fmt.match(/^####*$/)) return "dafuq";
455
463
  switch(fmt) {
456
- case "0": return Math.round(val);
457
- case "0.0": o = Math.round(val*10);
458
- return String(o/10).replace(/^([^\.]+)$/,"$1.0").replace(/\.$/,".0");
459
- case "0.00": o = Math.round(val*100);
460
- return String(o/100).replace(/^([^\.]+)$/,"$1.00").replace(/\.$/,".00").replace(/\.([0-9])$/,".$1"+"0");
461
- case "0.000": o = Math.round(val*1000);
462
- return String(o/1000).replace(/^([^\.]+)$/,"$1.000").replace(/\.$/,".000").replace(/\.([0-9])$/,".$1"+"00").replace(/\.([0-9][0-9])$/,".$1"+"0");
463
- case "#,##0": return sign + commaify(String(Math.round(aval)));
464
- case "#,##0.0": r = Math.round((val-Math.floor(val))*10); return val < 0 ? "-" + write_num(type, fmt, -val) : commaify(String(Math.floor(val))) + "." + r;
465
- case "#,##0.00": r = Math.round((val-Math.floor(val))*100); return val < 0 ? "-" + write_num(type, fmt, -val) : commaify(String(Math.floor(val))) + "." + (r < 10 ? "0"+r:r);
466
- case "# ? / ?": ff = frac(aval, 9, true); return sign + (ff[0]||"") + " " + (ff[1] === 0 ? " " : ff[1] + "/" + ff[2]);
467
- case "# ?? / ??": ff = frac(aval, 99, true); return sign + (ff[0]||"") + " " + (ff[1] ? pad(ff[1],2," ") + "/" + rpad(ff[2],2," ") : " ");
468
- case "# ??? / ???": ff = frac(aval, 999, true); return sign + (ff[0]||"") + " " + (ff[1] ? pad(ff[1],3," ") + "/" + rpad(ff[2],3," ") : " ");
464
+ case "0": case "#0": return Math.round(val);
465
+ case "#.##": o = Math.round(val*100);
466
+ return String(o/100).replace(/^([^\.]+)$/,"$1.").replace(/^0\.$/,".");
467
+ case "#,###": var x = commaify(String(Math.round(aval))); return x !== "0" ? sign + x : "";
469
468
  default:
470
469
  }
471
470
  throw new Error("unsupported format |" + fmt + "|");
@@ -482,7 +481,7 @@ function split_fmt(fmt) {
482
481
  j = i+1;
483
482
  }
484
483
  out.push(fmt.slice(j));
485
- if(in_str !=-1) throw "Format |" + fmt + "| unterminated string at " + in_str;
484
+ if(in_str !=-1) throw new Error("Format |" + fmt + "| unterminated string at " + in_str);
486
485
  return out;
487
486
  }
488
487
  SSF._split = split_fmt;
@@ -493,6 +492,10 @@ function eval_fmt(fmt, v, opts, flen) {
493
492
  /* Tokenize */
494
493
  while(i < fmt.length) {
495
494
  switch((c = fmt[i])) {
495
+ case 'G': /* General */
496
+ if(fmt.substr(i, i+6).toLowerCase() !== "general")
497
+ throw new Error('unrecognized character ' + fmt[i] + ' in ' +fmt);
498
+ out.push({t:'G',v:'General'}); i+=7; break;
496
499
  case '"': /* Literal text */
497
500
  for(o="";fmt[++i] !== '"' && i < fmt.length;) o += fmt[i];
498
501
  out.push({t:'t', v:o}); ++i; break;
@@ -502,28 +505,39 @@ function eval_fmt(fmt, v, opts, flen) {
502
505
  case '@': /* Text Placeholder */
503
506
  out.push({t:'T', v:v}); ++i; break;
504
507
  /* Dates */
508
+ case 'M': case 'D': case 'Y': case 'H': case 'S': case 'E':
509
+ c = c.toLowerCase();
510
+ /* falls through */
505
511
  case 'm': case 'd': case 'y': case 'h': case 's': case 'e':
506
512
  if(v < 0) return "";
507
513
  if(!dt) dt = parse_date_code(v, opts);
508
- o = fmt[i]; while(fmt[++i] === c) o+=c;
514
+ if(!dt) return "";
515
+ o = fmt[i]; while((fmt[++i]||"").toLowerCase() === c) o+=c;
509
516
  if(c === 's' && fmt[i] === '.' && fmt[i+1] === '0') { o+='.'; while(fmt[++i] === '0') o+= '0'; }
510
517
  if(c === 'm' && lst.toLowerCase() === 'h') c = 'M'; /* m = minute */
511
518
  if(c === 'h') c = hr;
519
+ o = o.toLowerCase();
512
520
  q={t:c, v:o}; out.push(q); lst = c; break;
513
521
  case 'A':
514
522
  if(!dt) dt = parse_date_code(v, opts);
523
+ if(!dt) return "";
515
524
  q={t:c,v:"A"};
516
525
  if(fmt.substr(i, 3) === "A/P") {q.v = dt.H >= 12 ? "P" : "A"; q.t = 'T'; hr='h';i+=3;}
517
526
  else if(fmt.substr(i,5) === "AM/PM") { q.v = dt.H >= 12 ? "PM" : "AM"; q.t = 'T'; i+=5; hr='h'; }
518
- else q.t = "t";
527
+ else { q.t = "t"; i++; }
519
528
  out.push(q); lst = c; break;
520
529
  case '[': /* TODO: Fix this -- ignore all conditionals and formatting */
521
530
  o = c;
522
- while(fmt[i++] !== ']') o += fmt[i];
523
- if(o == "[h]") out.push({t:'Z', v:o});
531
+ while(fmt[i++] !== ']' && i < fmt.length) o += fmt[i];
532
+ if(o.substr(-1) !== ']') throw 'unterminated "[" block: |' + o + '|';
533
+ if(o.match(/\[[HhMmSs]*\]/)) {
534
+ if(!dt) dt = parse_date_code(v, opts);
535
+ if(!dt) return "";
536
+ out.push({t:'Z', v:o.toLowerCase()});
537
+ } else { o=""; }
524
538
  break;
525
539
  /* Numbers */
526
- case '0': case '#':
540
+ case '0': case '#': case '.':
527
541
  o = c; while("0#?.,E+-%".indexOf(c=fmt[++i]) > -1) o += c;
528
542
  out.push({t:'n', v:o}); break;
529
543
  case '?':
@@ -536,7 +550,7 @@ function eval_fmt(fmt, v, opts, flen) {
536
550
  out.push({t:'D', v:o}); break;
537
551
  case ' ': out.push({t:c,v:c}); ++i; break;
538
552
  default:
539
- if("$-+/():!^&'~{}<>=".indexOf(c) === -1)
553
+ if(",$-+/():!^&'~{}<>=€".indexOf(c) === -1)
540
554
  throw 'unrecognized character ' + fmt[i] + ' in ' + fmt;
541
555
  out.push({t:'t', v:c}); ++i; break;
542
556
  }
@@ -554,45 +568,48 @@ function eval_fmt(fmt, v, opts, flen) {
554
568
  /* replace fields */
555
569
  for(i=0; i < out.length; ++i) {
556
570
  switch(out[i].t) {
557
- case 't': case 'T': case ' ': break;
558
- case 'd': case 'm': case 'y': case 'h': case 'H': case 'M': case 's': case 'A': case 'e': case 'Z':
571
+ case 't': case 'T': case ' ': case 'D': break;
572
+ case 'd': case 'm': case 'y': case 'h': case 'H': case 'M': case 's': case 'e': case 'Z':
559
573
  out[i].v = write_date(out[i].t, out[i].v, dt);
560
574
  out[i].t = 't'; break;
561
- case 'n': case '(':
575
+ case 'n': case '(': case '?':
562
576
  var jj = i+1;
563
- while(out[jj] && ("? D".indexOf(out[jj].t) > -1 || out[i].t == '(' && (out[jj].t == ')' || out[jj].t == 'n') || out[jj].t == 't' && (out[jj].v == '/' || out[jj].v == '$' || (out[jj].v == ' ' && (out[jj+1]||{}).t == '?')))) {
564
- if(out[jj].v!==' ') out[i].v += ' ' + out[jj].v;
577
+ while(out[jj] && ("?D".indexOf(out[jj].t) > -1 || (" t".indexOf(out[jj].t) > -1 && "?t".indexOf((out[jj+1]||{}).t)>-1 && (out[jj+1].t == '?' || out[jj+1].v == '/')) || out[i].t == '(' && (out[jj].t == ')' || out[jj].t == 'n') || out[jj].t == 't' && (out[jj].v == '/' || '$€'.indexOf(out[jj].v) > -1 || (out[jj].v == ' ' && (out[jj+1]||{}).t == '?')))) {
578
+ out[i].v += out[jj].v;
565
579
  delete out[jj]; ++jj;
566
580
  }
567
- out[i].v = write_num(out[i].t, out[i].v, v);
581
+ out[i].v = write_num(out[i].t, out[i].v, (flen >1 && v < 0 && i>0 && out[i-1].v == "-" ? -v:v));
568
582
  out[i].t = 't';
569
- i = jj; break;
570
- default: throw "unrecognized type " + out[i].t;
583
+ i = jj-1; break;
584
+ case 'G': out[i].t = 't'; out[i].v = general_fmt(v,opts); break;
571
585
  }
572
586
  }
573
-
574
587
  return out.map(function(x){return x.v;}).join("");
575
588
  }
576
589
  SSF._eval = eval_fmt;
577
590
  function choose_fmt(fmt, v, o) {
578
- if(typeof fmt === 'number') fmt = table_fmt[fmt];
591
+ if(typeof fmt === 'number') fmt = ((o&&o.table) ? o.table : table_fmt)[fmt];
579
592
  if(typeof fmt === "string") fmt = split_fmt(fmt);
580
593
  var l = fmt.length;
594
+ if(l<4 && fmt[l-1].indexOf("@")>-1) --l;
581
595
  switch(fmt.length) {
582
- case 1: fmt = [fmt[0], fmt[0], fmt[0], "@"]; break;
583
- case 2: fmt = [fmt[0], fmt[fmt[1] === "@"?0:1], fmt[0], "@"]; break;
596
+ case 1: fmt = fmt[0].indexOf("@")>-1 ? ["General", "General", "General", fmt[0]] : [fmt[0], fmt[0], fmt[0], "@"]; break;
597
+ case 2: fmt = fmt[1].indexOf("@")>-1 ? [fmt[0], fmt[0], fmt[0], fmt[1]] : [fmt[0], fmt[1], fmt[0], "@"]; break;
598
+ case 3: fmt = fmt[2].indexOf("@")>-1 ? [fmt[0], fmt[1], fmt[0], fmt[2]] : [fmt[0], fmt[1], fmt[2], "@"]; break;
584
599
  case 4: break;
585
600
  default: throw "cannot find right format for |" + fmt + "|";
586
601
  }
587
602
  if(typeof v !== "number") return [fmt.length, fmt[3]];
588
603
  return [l, v > 0 ? fmt[0] : v < 0 ? fmt[1] : fmt[2]];
589
604
  }
590
-
591
605
  var format = function format(fmt,v,o) {
592
606
  fixopts(o = (o||{}));
593
- if(fmt === 0) return general_fmt(v, o);
594
- if(typeof fmt === 'number') fmt = table_fmt[fmt];
607
+ if(typeof fmt === "string" && fmt.toLowerCase() === "general") return general_fmt(v, o);
608
+ if(typeof fmt === 'number') fmt = (o.table || table_fmt)[fmt];
595
609
  var f = choose_fmt(fmt, v, o);
610
+ if(f[1].toLowerCase() === "general") return general_fmt(v,o);
611
+ if(v === true) v = "TRUE"; if(v === false) v = "FALSE";
612
+ if(v === "" || typeof v === "undefined") return "";
596
613
  return eval_fmt(f[1], v, o, f[0]);
597
614
  };
598
615
 
@@ -600,6 +617,8 @@ SSF._choose = choose_fmt;
600
617
  SSF._table = table_fmt;
601
618
  SSF.load = function(fmt, idx) { table_fmt[idx] = fmt; };
602
619
  SSF.format = format;
620
+ SSF.get_table = function() { return table_fmt; };
621
+ SSF.load_table = function(tbl) { for(var i=0; i!=0x0188; ++i) if(tbl[i]) SSF.load(tbl[i], i); };
603
622
  };
604
623
  make_ssf(SSF);
605
624
  /* [MS-OLEPS] v20130118 */
@@ -746,7 +765,7 @@ function parse_VtStringBase(blob, stringType, pad) {
746
765
  else return parse_VtStringBase(blob, blob.read_shift(2), pad);
747
766
  }
748
767
 
749
- function parse_VtString(blob, t, pad) { return parse_VtStringBase(blob, t, 4); }
768
+ function parse_VtString(blob, t, pad) { return parse_VtStringBase(blob, t, pad === false ? null : 4); }
750
769
  function parse_VtUnalignedString(blob, t) { if(!t) throw new Error("dafuq?"); return parse_VtStringBase(blob, t, 0); }
751
770
 
752
771
  /* [MS-OSHARED] 2.3.3.1.9 VtVecUnalignedLpstrValue */
@@ -802,7 +821,7 @@ function parse_dictionary(blob,CodePage) {
802
821
  function parse_BLOB(blob) {
803
822
  var size = blob.read_shift(4);
804
823
  var bytes = blob.slice(blob.l,blob.l+size);
805
- if(blob.l % 4) blob.l += (4 - (blob.l % 4)) % 4;
824
+ if(size % 4 > 0) blob.l += (4 - (size % 4)) % 4;
806
825
  return bytes;
807
826
  }
808
827
 
@@ -828,14 +847,14 @@ function parse_VtVector(blob, cb) {
828
847
  }
829
848
 
830
849
  /* [MS-OLEPS] 2.15 TypedPropertyValue */
831
- function parse_TypedPropertyValue(blob, type) {
850
+ function parse_TypedPropertyValue(blob, type, _opts) {
832
851
  var read = ReadShift.bind(blob), chk = CheckField.bind(blob);
833
- var t = read(2), ret;
852
+ var t = read(2), ret, opts = _opts||{};
834
853
  read(2);
835
854
  if(type !== VT_VARIANT)
836
855
  if(t !== type && VT_CUSTOM.indexOf(type)===-1) throw new Error('Expected type ' + type + ' saw ' + t);
837
856
  switch(type === VT_VARIANT ? t : type) {
838
- case VT_I2: ret = read(2, 'i'); read(2); return ret;
857
+ case VT_I2: ret = read(2, 'i'); if(!opts.raw) read(2); return ret;
839
858
  case VT_I4: ret = read(4, 'i'); return ret;
840
859
  case VT_BOOL: return read(4) !== 0x0;
841
860
  case VT_UI4: ret = read(4); return ret;
@@ -844,7 +863,7 @@ function parse_TypedPropertyValue(blob, type) {
844
863
  case VT_FILETIME: return parse_FILETIME(blob);
845
864
  case VT_BLOB: return parse_BLOB(blob);
846
865
  case VT_CF: return parse_ClipboardData(blob);
847
- case VT_STRING: return parse_VtString(blob, t, 4).replace(/\u0000/g,'');
866
+ case VT_STRING: return parse_VtString(blob, t, !opts.raw && 4).replace(/\u0000/g,'');
848
867
  case VT_USTR: return parse_VtUnalignedString(blob, t, 4).replace(/\u0000/g,'');
849
868
  case VT_VECTOR | VT_VARIANT: return parse_VtVecHeadingPair(blob);
850
869
  case VT_VECTOR | VT_LPSTR: return parse_VtVecUnalignedLpstr(blob);
@@ -880,16 +899,25 @@ function parse_PropertySet(blob, PIDSI) {
880
899
  var PropH = {};
881
900
  for(i = 0; i != NumProps; ++i) {
882
901
  if(blob.l !== Props[i][1]) {
883
- throw new Error("Read Error: Expected address " + Props[i][1] + ' at ' + blob.l + ' :' + i);
902
+ var fail = true;
903
+ if(i>0 && PIDSI) switch(PIDSI[Props[i-1][0]].t) {
904
+ case VT_I2: if(blob.l +2 === Props[i][1]) { blob.l+=2; fail = false; } break;
905
+ case VT_STRING: if(blob.l <= Props[i][1]) { blob.l=Props[i][1]; fail = false; } break;
906
+ case VT_VECTOR | VT_VARIANT: if(blob.l <= Props[i][1]) { blob.l=Props[i][1]; fail = false; } break;
907
+ }
908
+ if(!PIDSI && blob.l <= Props[i][1]) { fail=false; blob.l = Props[i][1]; }
909
+ if(fail) throw new Error("Read Error: Expected address " + Props[i][1] + ' at ' + blob.l + ' :' + i);
884
910
  }
885
911
  if(PIDSI) {
886
912
  var piddsi = PIDSI[Props[i][0]];
887
- PropH[piddsi.n] = parse_TypedPropertyValue(blob, piddsi.t);
913
+ PropH[piddsi.n] = parse_TypedPropertyValue(blob, piddsi.t, {raw:true});
888
914
  if(piddsi.n == "CodePage") switch(PropH[piddsi.n]) {
889
915
  /* TODO: Generate files under every codepage */
890
916
  case 10000: break; // OSX Roman
891
917
  case 1252: break; // Windows Latin
892
918
 
919
+ case 0: PropH[piddsi.n] = 1252; break; // Unknown -> default
920
+
893
921
  case 874: // SB Windows Thai
894
922
  case 1250: // SB Windows Central Europe
895
923
  case 1251: // SB Windows Cyrillic
@@ -971,14 +999,16 @@ function parse_PropertySetStream(file, PIDSI) {
971
999
  rval.FMTID = FMTID0;
972
1000
  //rval.PSet0 = PSet0;
973
1001
  if(NumSets === 1) return rval;
974
- var PSet1 = parse_PropertySet(blob, null);
1002
+ if(blob.l !== Offset1) throw "Length mismatch 2: " + blob.l + " !== " + Offset1;
1003
+ var PSet1;
1004
+ try { PSet1 = parse_PropertySet(blob, null); } catch(e) { }
975
1005
  for(y in PSet1) rval[y] = PSet1[y];
976
1006
  rval.FMTID = [FMTID0, FMTID1]; // TODO: verify FMTID0/1
977
1007
  return rval;
978
1008
  }
979
1009
  /* [MS-CFB] v20130118 */
980
- if(typeof module !== "undefined" && typeof require !== 'undefined') CFB = require('cfb');
981
- else var CFB = (function(){
1010
+ /*if(typeof module !== "undefined" && typeof require !== 'undefined') CFB = require('cfb');
1011
+ else*/ var CFB = (function(){
982
1012
  var exports = {};
983
1013
  function parse(file) {
984
1014
 
@@ -1004,7 +1034,6 @@ var fat_addrs = []; // locations of FAT sectors
1004
1034
  var blob = file.slice(0,512);
1005
1035
  prep_blob(blob);
1006
1036
  var read = ReadShift.bind(blob), chk = CheckField.bind(blob);
1007
- //var wrn = WarnField.bind(blob);
1008
1037
  var j = 0, q;
1009
1038
 
1010
1039
  // header signature 8
@@ -1014,7 +1043,6 @@ chk(HEADER_SIGNATURE, 'Header Signature: ');
1014
1043
  chk(HEADER_CLSID, 'CLSID: ');
1015
1044
 
1016
1045
  // minor version 2
1017
- //wrn(HEADER_MINOR_VERSION, 'Minor Version: ');
1018
1046
  read(2);
1019
1047
 
1020
1048
  // major version 3
@@ -1100,10 +1128,10 @@ function sleuth_fat(idx, cnt) {
1100
1128
  if(idx !== FREESECT) {
1101
1129
  var sector = sectors[idx];
1102
1130
  for(var i = 0; i != ssz/4-1; ++i) {
1103
- if((q = sector.readUInt32LE(i*4)) === ENDOFCHAIN) break;
1131
+ if((q = __readUInt32LE(sector,i*4)) === ENDOFCHAIN) break;
1104
1132
  fat_addrs.push(q);
1105
1133
  }
1106
- sleuth_fat(sector.readUInt32LE(ssz-4),cnt - 1);
1134
+ sleuth_fat(__readUInt32LE(sector,ssz-4),cnt - 1);
1107
1135
  }
1108
1136
  }
1109
1137
  sleuth_fat(difat_start, ndfs);
@@ -1117,7 +1145,7 @@ function get_buffer(byte_addr, bytes) {
1117
1145
  }
1118
1146
 
1119
1147
  function get_buffer_u32(byte_addr) {
1120
- return get_buffer(byte_addr,4).readUInt32LE(0);
1148
+ return __readUInt32LE(get_buffer(byte_addr,4), 0);
1121
1149
  }
1122
1150
 
1123
1151
  function get_next_sector(idx) { return get_buffer_u32(idx); }
@@ -1130,7 +1158,7 @@ for(i=0; i != sectors.length; ++i) {
1130
1158
  if(chkd[k]) continue;
1131
1159
  for(j=k; j<=MAXREGSECT; buf.push(j),j=get_next_sector(j)) chkd[j] = true;
1132
1160
  sector_list[k] = {nodes: buf};
1133
- sector_list[k].data = Buffers(buf.map(get_sector)).toBuffer();
1161
+ sector_list[k].data = __toBuffer(Array(buf.map(get_sector)));
1134
1162
  }
1135
1163
  sector_list[dir_start].name = "!Directory";
1136
1164
  if(nmfs > 0 && minifat_start !== ENDOFCHAIN) sector_list[minifat_start].name = "!MiniFAT";
@@ -1147,7 +1175,7 @@ function read_directory(idx) {
1147
1175
  read = ReadShift.bind(blob);
1148
1176
  var namelen = read(2);
1149
1177
  if(namelen === 0) return;
1150
- var name = blob.utf16le(0,namelen-(Paths.length?2:0)); // OLE
1178
+ var name = __utf16le(blob,0,namelen-(Paths.length?2:0)); // OLE
1151
1179
  Paths.push(name);
1152
1180
  var o = { name: name };
1153
1181
  o.type = EntryTypes[read(1)];
@@ -1181,12 +1209,12 @@ function read_directory(idx) {
1181
1209
  }
1182
1210
  if(o.ctime) {
1183
1211
  var ct = blob.slice(blob.l-24, blob.l-16);
1184
- var c2 = (ct.readUInt32LE(4)/1e7)*Math.pow(2,32)+ct.readUInt32LE(0)/1e7;
1212
+ var c2 = (__readUInt32LE(ct,4)/1e7)*Math.pow(2,32)+__readUInt32LE(ct,0)/1e7;
1185
1213
  o.ct = new Date((c2 - 11644473600)*1000);
1186
1214
  }
1187
1215
  if(o.mtime) {
1188
1216
  var mt = blob.slice(blob.l-16, blob.l-8);
1189
- var m2 = (mt.readUInt32LE(4)/1e7)*Math.pow(2,32)+mt.readUInt32LE(0)/1e7;
1217
+ var m2 = (__readUInt32LE(mt,4)/1e7)*Math.pow(2,32)+__readUInt32LE(mt,0)/1e7;
1190
1218
  o.mt = new Date((m2 - 11644473600)*1000);
1191
1219
  }
1192
1220
  files[name] = o;
@@ -1248,16 +1276,16 @@ var rval = {
1248
1276
  find: find_path
1249
1277
  };
1250
1278
 
1251
- //for(var name in files) {
1252
- // switch(name) {
1253
- // /* [MS-OSHARED] 2.3.3.2.2 Document Summary Information Property Set */
1254
- // case '!DocumentSummaryInformation':
1255
- // rval.DocSummary = parse_PropertySetStream(files[name], DocSummaryPIDDSI); break;
1256
- // /* [MS-OSHARED] 2.3.3.2.1 Summary Information Property Set*/
1257
- // case '!SummaryInformation':
1258
- // rval.Summary = parse_PropertySetStream(files[name], SummaryPIDSI); break;
1259
- // }
1260
- //}
1279
+ for(var name in files) {
1280
+ switch(name) {
1281
+ /* [MS-OSHARED] 2.3.3.2.2 Document Summary Information Property Set */
1282
+ case '!DocumentSummaryInformation':
1283
+ try { rval.DocSummary = parse_PropertySetStream(files[name], DocSummaryPIDDSI); } catch(e) { } break;
1284
+ /* [MS-OSHARED] 2.3.3.2.1 Summary Information Property Set*/
1285
+ case '!SummaryInformation':
1286
+ try { rval.Summary = parse_PropertySetStream(files[name], SummaryPIDSI); } catch(e) { } break;
1287
+ }
1288
+ }
1261
1289
 
1262
1290
  return rval;
1263
1291
  } // parse
@@ -1304,10 +1332,6 @@ return exports;
1304
1332
  }
1305
1333
 
1306
1334
  if(typeof require !== 'undefined' && typeof exports !== 'undefined') {
1307
- Buffers = Array;
1308
- Buffers.prototype.toBuffer = function() {
1309
- return Buffer.concat(this[0]);
1310
- };
1311
1335
  var fs = require('fs');
1312
1336
  //exports.read = CFB.read;
1313
1337
  //exports.parse = CFB.parse;
@@ -1319,13 +1343,6 @@ if(typeof require !== 'undefined' && typeof exports !== 'undefined') {
1319
1343
  };
1320
1344
  if(typeof module !== 'undefined' && require.main === module)
1321
1345
  exports.main(process.argv.slice(2));
1322
- } else {
1323
- Buffers = Array;
1324
- Buffers.prototype.toBuffer = function() {
1325
- var x = [];
1326
- for(var i = 0; i != this[0].length; ++i) { x = x.concat(this[0][i]); }
1327
- return x;
1328
- };
1329
1346
  }
1330
1347
 
1331
1348
  /* sections refer to MS-XLS unless otherwise stated */
@@ -1382,13 +1399,16 @@ function parse_XLUnicodeRichExtendedString(blob) {
1382
1399
  var fHighByte = flags & 0x1, fExtSt = flags & 0x4, fRichSt = flags & 0x8;
1383
1400
  var width = 1 + (flags & 0x1); // 0x0 -> utf8, 0x1 -> dbcs
1384
1401
  var cRun, cbExtRst;
1402
+ var z = {};
1385
1403
  if(fRichSt) cRun = read_shift(2);
1386
1404
  if(fExtSt) cbExtRst = read_shift(4);
1387
1405
  var encoding = (flags & 0x1) ? 'dbcs' : 'sbcs';
1388
1406
  var msg = cch === 0 ? "" : read_shift(encoding, cch);
1389
1407
  if(fRichSt) blob.l += 4 * cRun; //TODO: parse this
1390
1408
  if(fExtSt) blob.l += cbExtRst; //TODO: parse this
1391
- return msg;
1409
+ z.t = msg;
1410
+ if(!fRichSt) { z.raw = "<t>" + z.t + "</t>"; z.r = z.t; }
1411
+ return z;
1392
1412
  }
1393
1413
 
1394
1414
  /* 2.5.296 XLUnicodeStringNoCch */
@@ -1396,7 +1416,7 @@ function parse_XLUnicodeStringNoCch(blob, cch) {
1396
1416
  var read = blob.read_shift.bind(blob);
1397
1417
  var fHighByte = read(1);
1398
1418
  var retval;
1399
- if(fHighByte===0) { retval = blob.utf8(blob.l, blob.l+cch); blob.l += cch; }
1419
+ if(fHighByte===0) { retval = __utf8(blob,blob.l, blob.l+cch); blob.l += cch; }
1400
1420
  else { retval = blob.read_shift('dbcs', cch); }
1401
1421
  return retval;
1402
1422
  }
@@ -1451,7 +1471,7 @@ function parse_RkNumber(blob) {
1451
1471
  var fX100 = b[0] & 1, fInt = b[0] & 2;
1452
1472
  blob.l+=4;
1453
1473
  b[0] &= ~3;
1454
- var RK = fInt === 0 ? [0,0,0,0,b[0],b[1],b[2],b[3]].readDoubleLE(0) : b.readInt32LE(0)>>2;
1474
+ var RK = fInt === 0 ? __readDoubleLE([0,0,0,0,b[0],b[1],b[2],b[3]],0) : __readInt32LE(b,0)>>2;
1455
1475
  return fX100 ? RK/100 : RK;
1456
1476
  }
1457
1477
 
@@ -1628,6 +1648,14 @@ function parse_LabelSst(blob, length) {
1628
1648
  return cell;
1629
1649
  }
1630
1650
 
1651
+ /* 2.4.148 */
1652
+ function parse_Label(blob, length) {
1653
+ var cell = parse_Cell(blob, 6);
1654
+ var str = parse_XLUnicodeString(blob, length-6);
1655
+ cell.val = str;
1656
+ return cell;
1657
+ }
1658
+
1631
1659
  /* 2.4.126 Number Formats */
1632
1660
  function parse_Format(blob, length) {
1633
1661
  var ifmt = blob.read_shift(2);
@@ -1674,7 +1702,7 @@ function parse_XF(blob, length) {
1674
1702
  o.ifnt = read(2); o.ifmt = read(2); o.flags = read(2);
1675
1703
  o.fStyle = (o.flags >> 2) & 0x01;
1676
1704
  length -= 6;
1677
- o.data = o.fStyle ? parse_StyleXF(blob, length) : parse_CellXF(blob, length);
1705
+ o.data = o.fStyle ? parse_StyleXF(blob, length) : parse_CellXF(blob, length);
1678
1706
  return o;
1679
1707
  }
1680
1708
 
@@ -1734,7 +1762,7 @@ function parse_ExternName(blob, length, opts) {
1734
1762
  };
1735
1763
  if(opts.sbcch === 0x3A01) body = parse_AddinUdf(blob, length-2);
1736
1764
  //else throw new Error("unsupported SupBook cch: " + opts.sbcch);
1737
- o.body = blob.read_shift(length-2);
1765
+ o.body = body || blob.read_shift(length-2);
1738
1766
  return o;
1739
1767
  }
1740
1768
 
@@ -1792,6 +1820,21 @@ function parse_MTRSettings(blob, length) {
1792
1820
  return [fMTREnabled, fUserSetThreadCount, cUserThreadCount];
1793
1821
  }
1794
1822
 
1823
+ /* 2.5.186 */
1824
+ function parse_NoteSh(blob, length) {
1825
+ var row = blob.read_shift(2), col = blob.read_shift(2);
1826
+ var flags = blob.read_shift(2), idObj = blob.read_shift(2);
1827
+ var stAuthor = parse_XLUnicodeString(blob);
1828
+ blob.read_shift(1);
1829
+ return stAuthor;
1830
+ }
1831
+
1832
+ /* 2.4.179 */
1833
+ function parse_Note(blob, length) {
1834
+ /* TODO: Support revisions */
1835
+ return parse_NoteSh(blob, length);
1836
+ }
1837
+
1795
1838
  var parse_Backup = parsebool; /* 2.4.14 */
1796
1839
  var parse_Blank = parse_Cell; /* 2.4.20 Just the cell */
1797
1840
  var parse_BottomMargin = parse_Xnum; /* 2.4.27 */
@@ -1847,7 +1890,6 @@ var parse_WriteProtect = parsenoop; /* 2.4.350 empty record */
1847
1890
  /* ---- */
1848
1891
  var parse_VerticalPageBreaks = parsenoop;
1849
1892
  var parse_HorizontalPageBreaks = parsenoop;
1850
- var parse_Note = parsenoop;
1851
1893
  var parse_Selection = parsenoop;
1852
1894
  var parse_Continue = parsenoop;
1853
1895
  var parse_Pane = parsenoop;
@@ -1967,7 +2009,6 @@ var parse_CodeName = parse_XLUnicodeString;
1967
2009
  var parse_SXFDBType = parsenoop;
1968
2010
  var parse_ObNoMacros = parsenoop;
1969
2011
  var parse_Dv = parsenoop;
1970
- var parse_Label = parsenoop;
1971
2012
  var parse_Index = parsenoop;
1972
2013
  var parse_Table = parsenoop;
1973
2014
  var parse_Window2 = parsenoop;
@@ -2292,8 +2333,8 @@ function parse_PtgArray(blob, length) {
2292
2333
 
2293
2334
  /* 2.5.198.33 */
2294
2335
  function parse_PtgAttrBaxcel(blob, length) {
2295
- bitSemi = blob[blob.l+1] & 0x01; /* 1 = volatile */
2296
- bitBaxcel = 1;
2336
+ var bitSemi = blob[blob.l+1] & 0x01; /* 1 = volatile */
2337
+ var bitBaxcel = 1;
2297
2338
  blob.l += 4;
2298
2339
  return [bitSemi, bitBaxcel];
2299
2340
  }
@@ -2310,21 +2351,21 @@ function parse_PtgAttrChoose(blob, length) {
2310
2351
 
2311
2352
  /* 2.5.198.35 */
2312
2353
  function parse_PtgAttrGoto(blob, length) {
2313
- bitGoto = (blob[blob.l+1] & 0xFF) ? 1 : 0;
2354
+ var bitGoto = (blob[blob.l+1] & 0xFF) ? 1 : 0;
2314
2355
  blob.l += 2;
2315
2356
  return [bitGoto, blob.read_shift(2)];
2316
2357
  }
2317
2358
 
2318
2359
  /* 2.5.198.36 */
2319
2360
  function parse_PtgAttrIf(blob, length) {
2320
- bitIf = (blob[blob.l+1] & 0xFF) ? 1 : 0;
2361
+ var bitIf = (blob[blob.l+1] & 0xFF) ? 1 : 0;
2321
2362
  blob.l += 2;
2322
2363
  return [bitIf, blob.read_shift(2)];
2323
2364
  }
2324
2365
 
2325
2366
  /* 2.5.198.37 */
2326
2367
  function parse_PtgAttrSemi(blob, length) {
2327
- bitSemi = (blob[blob.l+1] & 0xFF) ? 1 : 0;
2368
+ var bitSemi = (blob[blob.l+1] & 0xFF) ? 1 : 0;
2328
2369
  blob.l += 4;
2329
2370
  return [bitSemi];
2330
2371
  }
@@ -2654,7 +2695,7 @@ function parse_Formula(blob, length) {
2654
2695
  /* 2.5.133 */
2655
2696
  function parse_FormulaValue(blob) {
2656
2697
  var b;
2657
- if(blob.readUInt16LE(blob.l + 6) !== 0xFFFF) return parse_Xnum(blob);
2698
+ if(__readUInt16LE(blob,blob.l + 6) !== 0xFFFF) return parse_Xnum(blob);
2658
2699
  switch(blob[blob.l]) {
2659
2700
  case 0x00: blob.l += 8; return "String";
2660
2701
  case 0x01: b = blob[blob.l+2] === 0x1; blob.l += 8; return b;
@@ -4486,6 +4527,23 @@ var RecordEnum = {
4486
4527
  0x0000: {}
4487
4528
  };
4488
4529
 
4530
+ function fixopts(opts) {
4531
+ var defaults = [
4532
+ ['cellNF', false], /* emit cell number format string as .z */
4533
+ ['cellFormula', true], /* emit formulae as .f */
4534
+
4535
+ ['sheetRows', 0, 'n'], /* read n rows (0 = read all rows) */
4536
+
4537
+ ['bookSheets', false], /* only try to get sheet names (no Sheets) */
4538
+ ['bookProps', false], /* only try to get properties (no Sheets) */
4539
+
4540
+ ['WTF', false] /* WTF mode (throws errors) */
4541
+ ];
4542
+ defaults.forEach(function(d) {
4543
+ if(typeof opts[d[0]] === 'undefined') opts[d[0]] = d[1];
4544
+ if(d[2] === 'n') opts[d[0]] = Number(opts[d[0]]);
4545
+ });
4546
+ }
4489
4547
  /* [MS-OLEDS] 2.3.8 CompObjStream */
4490
4548
  function parse_compobj(obj) {
4491
4549
  var v = {};
@@ -4493,12 +4551,12 @@ function parse_compobj(obj) {
4493
4551
 
4494
4552
  /* [MS-OLEDS] 2.3.7 CompObjHeader -- All fields MUST be ignored */
4495
4553
  var l = 28, m;
4496
- m = o.lpstr(l);
4497
- l += 4 + o.readUInt32LE(l);
4554
+ m = __lpstr(o, l);
4555
+ l += 4 + __readUInt32LE(o,l);
4498
4556
  v.UserType = m;
4499
4557
 
4500
4558
  /* [MS-OLEDS] 2.3.1 ClipboardFormatOrAnsiString */
4501
- m = o.readUInt32LE(l); l+= 4;
4559
+ m = __readUInt32LE(o,l); l+= 4;
4502
4560
  switch(m) {
4503
4561
  case 0x00000000: break;
4504
4562
  case 0xffffffff: case 0xfffffffe: l+=4; break;
@@ -4507,14 +4565,15 @@ function parse_compobj(obj) {
4507
4565
  l += m;
4508
4566
  }
4509
4567
 
4510
- m = o.lpstr(l); l += m.length === 0 ? 0 : 5 + m.length; v.Reserved1 = m;
4568
+ m = __lpstr(o, l); l += m.length === 0 ? 0 : 5 + m.length; v.Reserved1 = m;
4511
4569
 
4512
- if((m = o.readUInt32LE(l)) !== 0x71b2e9f4) return v;
4570
+ if((m = __readUInt32LE(o,l)) !== 0x71b2e9f4) return v;
4513
4571
  throw "Unsupported Unicode Extension";
4514
4572
  }
4515
4573
 
4516
-
4517
- function parse_xlscfb(cfb) {
4574
+ function parse_xlscfb(cfb, options) {
4575
+ if(!options) options = {};
4576
+ fixopts(options);
4518
4577
  reset_cp();
4519
4578
  var CompObj = cfb.find('!CompObj');
4520
4579
  var Summary = cfb.find('!SummaryInformation');
@@ -4529,12 +4588,12 @@ function slurp(R, blob, length, opts) {
4529
4588
  var l = length;
4530
4589
  var bufs = [blob.slice(blob.l,blob.l+l)];
4531
4590
  blob.l += length;
4532
- var next = (RecordEnum[blob.readUInt16LE(blob.l)]);
4591
+ var next = (RecordEnum[__readUInt16LE(blob,blob.l)]);
4533
4592
  while(next && next.n === 'Continue') {
4534
- l = blob.readUInt16LE(blob.l+2);
4593
+ l = __readUInt16LE(blob,blob.l+2);
4535
4594
  bufs.push(blob.slice(blob.l+4,blob.l+4+l));
4536
4595
  blob.l += 4+l;
4537
- next = (RecordEnum[blob.readUInt16LE(blob.l)]);
4596
+ next = (RecordEnum[__readUInt16LE(blob, blob.l)]);
4538
4597
  }
4539
4598
  var b = bconcat(bufs);
4540
4599
  prep_blob(b);
@@ -4544,7 +4603,7 @@ function slurp(R, blob, length, opts) {
4544
4603
  }
4545
4604
 
4546
4605
  // 2.3.2
4547
- function parse_workbook(blob) {
4606
+ function parse_workbook(blob, options) {
4548
4607
  var wb = {opts:{}};
4549
4608
  var Sheets = {};
4550
4609
  var out = [];
@@ -4560,11 +4619,13 @@ function parse_workbook(blob) {
4560
4619
  var lastcell, last_cell;
4561
4620
  var shared_formulae = {};
4562
4621
  var temp_val;
4622
+ var cell_valid = true;
4563
4623
  var XFs = []; /* XF records */
4564
- function addline(cell, line) {
4624
+ function addline(cell, line, options) {
4565
4625
  lastcell = cell;
4566
4626
  last_cell = encode_cell(cell);
4567
- out[last_cell] = line;
4627
+ if(options.sheetRows && lastcell.r >= options.sheetRows) cell_valid = false;
4628
+ else out[last_cell] = line;
4568
4629
  }
4569
4630
  var opts = {
4570
4631
  enc: false, // encrypted
@@ -4590,6 +4651,9 @@ function parse_workbook(blob) {
4590
4651
  var length = (blob.l === blob.length ? 0 : read(2)), y;
4591
4652
  var R = RecordEnum[RecordType];
4592
4653
  if(R && R.f) {
4654
+ if(options.bookSheets) {
4655
+ if(last_Rn === 'BoundSheet8' && R.n !== 'BoundSheet8') break;
4656
+ }
4593
4657
  last_Rn = R.n;
4594
4658
  if(R.r === 2 || R.r == 12) {
4595
4659
  var rt = read(2); length -= 2;
@@ -4745,39 +4809,72 @@ function parse_workbook(blob) {
4745
4809
  } break;
4746
4810
  case 'BOF': {
4747
4811
  if(file_depth++) break;
4812
+ cell_valid = true;
4748
4813
  out = {};
4749
4814
  cur_sheet = (Directory[s] || {name:""}).name;
4750
4815
  lst.push([R.n, s, val, Directory[s]]);
4751
4816
  } break;
4752
4817
  case 'Number': {
4753
4818
  temp_val = {ixfe: val.ixfe, XF: XFs[val.ixfe], v:val.val, t:'n'};
4754
- addline({c:val.c, r:val.r}, temp_val);
4819
+ if(temp_val.XF) try {
4820
+ temp_val.w=SSF.format(temp_val.XF.ifmt||0, temp_val.v);
4821
+ if(options.cellNF) temp_val.z = SSF._table[temp_val.XF.ifmt||0];
4822
+ } catch(e) { if(opts.WTF) throw e; }
4823
+ addline({c:val.c, r:val.r}, temp_val, options);
4755
4824
  } break;
4756
4825
  case 'BoolErr': {
4757
4826
  temp_val = {ixfe: val.ixfe, XF: XFs[val.ixfe], v:val.val, t:val.t};
4758
- addline({c:val.c, r:val.r}, temp_val);
4827
+ if(temp_val.XF) try {
4828
+ temp_val.w=SSF.format(temp_val.XF.ifmt||0, temp_val.v);
4829
+ if(options.cellNF) temp_val.z = SSF._table[temp_val.XF.ifmt||0];
4830
+ } catch(e) { if(opts.WTF) throw e; }
4831
+ addline({c:val.c, r:val.r}, temp_val, options);
4759
4832
  } break;
4760
4833
  case 'RK': {
4761
- temp_val = {ixfe: val.ixfe, XF: XFs[val.ixfe], v:val.rknum, t:'n'};
4762
- addline({c:val.c, r:val.r}, temp_val);
4834
+ temp_val = {ixfe: val.ixfe, XF: XFs[val.ixfe], v:val.rknum, t:'n'};
4835
+ if(temp_val.XF) try {
4836
+ temp_val.w=SSF.format(temp_val.XF.ifmt||0, temp_val.v);
4837
+ if(options.cellNF) temp_val.z = SSF._table[temp_val.XF.ifmt||0];
4838
+ } catch(e) { if(opts.WTF) throw e; }
4839
+ addline({c:val.c, r:val.r}, temp_val, options);
4763
4840
  } break;
4764
4841
  case 'MulRk': {
4765
4842
  for(var j = val.c; j <= val.C; ++j) {
4766
4843
  var ixfe = val.rkrec[j-val.c][0];
4767
- addline({c:j, r:val.r}, {ixfe: ixfe, XF: XFs[ixfe], v:val.rkrec[j-val.c][1], t:'n'});
4844
+ temp_val= {ixfe:ixfe, XF:XFs[ixfe], v:val.rkrec[j-val.c][1], t:'n'};
4845
+ if(temp_val.XF) try {
4846
+ temp_val.w=SSF.format(temp_val.XF.ifmt||0, temp_val.v);
4847
+ if(options.cellNF) temp_val.z = SSF._table[temp_val.XF.ifmt||0];
4848
+ } catch(e) { if(opts.WTF) throw e; }
4849
+ addline({c:j, r:val.r}, temp_val, options);
4768
4850
  }
4769
4851
  } break;
4770
4852
  case 'Formula': {
4771
4853
  switch(val.val) {
4772
4854
  case 'String': last_formula = val; break;
4773
4855
  case 'Array Formula': throw "Array Formula unsupported";
4774
- default: addline(val.cell, {v:val.val, f:stringify_formula(val.formula, range, val.cell, supbooks), ixfe: val.cell.ixfe, XF:XFs[val.cell.ixfe], t:'n'}); // TODO: infer type from formula
4856
+ default: // TODO: infer type from formula
4857
+ temp_val = {v:val.val, ixfe:val.cell.ixfe, t:'n'};
4858
+ temp_val.XF = XFs[temp_val.ixfe];
4859
+ if(options.cellFormula) temp_val.f = stringify_formula(val.formula,range,val.cell,supbooks);
4860
+ if(temp_val.XF) try {
4861
+ temp_val.w=SSF.format(temp_val.XF.ifmt||0, temp_val.v);
4862
+ if(options.cellNF) temp_val.z = SSF._table[temp_val.XF.ifmt||0];
4863
+ } catch(e) { if(opts.WTF) throw e; }
4864
+ addline(val.cell, temp_val, options);
4775
4865
  }
4776
4866
  } break;
4777
4867
  case 'String': {
4778
4868
  if(last_formula) {
4779
4869
  last_formula.val = val;
4780
- addline(last_formula.cell, {v:last_formula.val, f:stringify_formula(last_formula.formula, range, last_formula.cell, supbooks), ixfe: last_formula.cell.ixfe, t:'s'});
4870
+ temp_val = {v:last_formula.val, ixfe:last_formula.cell.ixfe, t:'s'};
4871
+ temp_val.XF = XFs[temp_val.ixfe];
4872
+ if(options.cellFormula) temp_val.f = stringify_formula(last_formula.formula, range, last_formula.cell, supbooks);
4873
+ if(temp_val.XF) try {
4874
+ temp_val.w=SSF.format(temp_val.XF.ifmt||0, temp_val.v);
4875
+ if(options.cellNF) temp_val.z = SSF._table[temp_val.XF.ifmt||0];
4876
+ } catch(e) { if(opts.WTF) throw e; }
4877
+ addline(last_formula.cell, temp_val, options);
4781
4878
  last_formula = null;
4782
4879
  }
4783
4880
  } break;
@@ -4785,11 +4882,27 @@ function parse_workbook(blob) {
4785
4882
  /* console.error(val); */
4786
4883
  } break;
4787
4884
  case 'ShrFmla': {
4788
- out[last_cell].f = stringify_formula(val[0], range, lastcell, supbooks);
4885
+ if(!cell_valid) break;
4886
+ if(options.cellFormula) out[last_cell].f = stringify_formula(val[0], range, lastcell, supbooks);
4789
4887
  shared_formulae[last_cell] = val[0];
4790
4888
  } break;
4791
4889
  case 'LabelSst': {
4792
- addline({c:val.c, r:val.r}, {v:JSON.stringify(sst[val.isst]), ixfe:val.ixfe, t:'s'});
4890
+ temp_val={v:sst[val.isst].t, ixfe:val.ixfe, t:'s'};
4891
+ temp_val.XF = XFs[temp_val.ixfe];
4892
+ if(temp_val.XF) try {
4893
+ temp_val.w=SSF.format(temp_val.XF.ifmt||0, temp_val.v);
4894
+ if(options.cellNF) temp_val.z = SSF._table[temp_val.XF.ifmt||0];
4895
+ } catch(e) { if(opts.WTF) throw e; }
4896
+ addline({c:val.c, r:val.r}, temp_val, options);
4897
+ } break;
4898
+ case 'Label': {
4899
+ /* Some writers erroneously write Label */
4900
+ temp_val = {v:val.val, ixfe:val.ixfe, XF:XFs[val.ixfe], t:'s'};
4901
+ if(temp_val.XF) try {
4902
+ temp_val.w=SSF.format(temp_val.XF.ifmt||0, temp_val.v);
4903
+ if(options.cellNF) temp_val.z = SSF._table[temp_val.XF.ifmt||0];
4904
+ } catch(e) { if(opts.WTF) throw e; }
4905
+ addline({c:val.c, r:val.r}, temp_val, options);
4793
4906
  } break;
4794
4907
  case 'Dimensions': {
4795
4908
  range = val;
@@ -4824,7 +4937,6 @@ function parse_workbook(blob) {
4824
4937
  case 'GUIDTypeLib': {
4825
4938
 
4826
4939
  } break;
4827
- case 'Note': break;
4828
4940
 
4829
4941
  case 'MergeCells': break;
4830
4942
 
@@ -4856,6 +4968,7 @@ function parse_workbook(blob) {
4856
4968
  case 'CondFmt': case 'CF': case 'CF12': case 'CFEx': break;
4857
4969
 
4858
4970
  /* Comments */
4971
+ case 'Note': break;
4859
4972
  case 'NameCmt': break;
4860
4973
 
4861
4974
  /* Chart */
@@ -4873,7 +4986,7 @@ function parse_workbook(blob) {
4873
4986
  case 'DataFormat': case 'SerToCrt': case 'FontX': break;
4874
4987
  case 'CatSerRange': case 'AxcExt': case 'SerFmt': break;
4875
4988
  case 'ShtProps': break;
4876
- case 'DefaultText': case 'Text': case 'Label': case 'CatLab': break;
4989
+ case 'DefaultText': case 'Text': case 'CatLab': break;
4877
4990
  case 'DataLabExtContents': break;
4878
4991
  case 'Legend': case 'LegendException': break;
4879
4992
  case 'Pie': case 'Scatter': break;
@@ -4948,90 +5061,93 @@ function parse_workbook(blob) {
4948
5061
  //lst.filter(function(x) { return x[0] === 'Formula';}).forEach(function(x){console.log(x[2].cell,x[2].formula);});
4949
5062
  wb.Directory=sheetnamesraw;
4950
5063
  wb.SheetNames=sheetnamesraw;
4951
- wb.Sheets=Sheets;
5064
+ if(!options.bookSheets) wb.Sheets=Sheets;
4952
5065
  wb.Preamble=Preamble;
4953
5066
  wb.Strings = sst;
5067
+ wb.SSF = SSF.get_table();
4954
5068
  if(opts.enc) wb.Encryption = opts.enc;
4955
5069
  return wb;
4956
5070
  }
4957
- if(Workbook) WorkbookP = parse_workbook(Workbook.content);
4958
- else throw new Error("Cannot find Workbook stream");
4959
5071
  if(CompObj) CompObjP = parse_compobj(CompObj);
4960
-
5072
+ if(options.bookProps && !options.bookSheets) WorkbookP = {};
5073
+ else {
5074
+ if(Workbook) WorkbookP = parse_workbook(Workbook.content, options);
5075
+ else throw new Error("Cannot find Workbook stream");
5076
+ }
5077
+
5078
+ var props = {};
5079
+ for(var y in cfb.Summary) props[y] = cfb.Summary[y];
5080
+ for(y in cfb.DocSummary) props[y] = cfb.DocSummary[y];
5081
+ WorkbookP.Props = WorkbookP.Custprops = props; /* TODO: split up properties */
5082
+ if(options.bookFiles) WorkbookP.cfb = cfb;
5083
+ WorkbookP.CompObjP = CompObjP;
4961
5084
  return WorkbookP;
4962
5085
  }
4963
5086
 
4964
- function sheet_to_row_object_array(sheet){
4965
- var val, rowObject, range, columnHeaders, emptyRow, C;
4966
- var outSheet = [];
4967
- if (sheet["!ref"]) {
4968
- range = decode_range(sheet["!ref"]);
4969
-
4970
- columnHeaders = {};
4971
- for (C = range.s.c; C <= range.e.c; ++C) {
4972
- val = sheet[encode_cell({
4973
- c: C,
4974
- r: range.s.r
4975
- })];
4976
- if(val){
4977
- switch(val.t) {
4978
- case 's': case 'str': columnHeaders[C] = JSON.parse(val.v); break;
4979
- case 'n': columnHeaders[C] = val.v; break;
4980
- }
4981
- }
4982
- }
5087
+ function format_cell(cell, v) {
5088
+ if(!cell) return "";
5089
+ if(typeof cell.w !== 'undefined') return cell.w;
5090
+ if(typeof v === 'undefined') v = cell.v;
5091
+ if(!cell.XF) return v;
5092
+ try { cell.w = SSF.format(cell.XF.ifmt||0, v); } catch(e) { return v; }
5093
+ return cell.w;
5094
+ }
4983
5095
 
4984
- for (var R = range.s.r + 1; R <= range.e.r; ++R) {
4985
- emptyRow = true;
4986
- //Row number is recorded in the prototype
4987
- //so that it doesn't appear when stringified.
4988
- rowObject = Object.create({ __rowNum__ : R });
4989
- for (C = range.s.c; C <= range.e.c; ++C) {
4990
- val = sheet[encode_cell({
4991
- c: C,
4992
- r: R
4993
- })];
4994
- var v = (val || {}).v;
4995
- if(val !== undefined) switch(val.t){
4996
- case 's': case 'str':
4997
- if(v !== undefined) v = JSON.parse(v);
4998
- /* falls through */
4999
- case 'b': case 'n':
5000
- if(v !== undefined) {
5001
- rowObject[columnHeaders[C]] = v;
5002
- emptyRow = false;
5003
- }
5004
- break;
5005
- case 'e': break; /* throw */
5006
- default: throw 'unrecognized type ' + val.t;
5007
- }
5096
+ function sheet_to_row_object_array(sheet, opts){
5097
+ var val, row, r, hdr = {}, isempty, R, C, v;
5098
+ var out = [];
5099
+ opts = opts || {};
5100
+ if(!sheet || !sheet["!ref"]) return out;
5101
+ r = utils.decode_range(sheet["!ref"]);
5102
+ for(R=r.s.r, C = r.s.c; C <= r.e.c; ++C) {
5103
+ val = sheet[utils.encode_cell({c:C,r:R})];
5104
+ if(!val) continue;
5105
+ hdr[C] = format_cell(val);
5106
+ }
5107
+
5108
+ for (R = r.s.r + 1; R <= r.e.r; ++R) {
5109
+ isempty = true;
5110
+ /* row index available as __rowNum__ */
5111
+ row = Object.create({ __rowNum__ : R });
5112
+ for (C = r.s.c; C <= r.e.c; ++C) {
5113
+ val = sheet[utils.encode_cell({c: C,r: R})];
5114
+ if(!val || !val.t) continue;
5115
+ v = (val || {}).v;
5116
+ switch(val.t){
5117
+ case 'e': continue; /* TODO: emit error text? */
5118
+ case 's': case 'str': break;
5119
+ case 'b': case 'n': break;
5120
+ default: throw 'unrecognized type ' + val.t;
5008
5121
  }
5009
- if(!emptyRow) {
5010
- outSheet.push(rowObject);
5122
+ if(typeof v !== 'undefined') {
5123
+ row[hdr[C]] = opts.raw ? v||val.v : format_cell(val, v);
5124
+ isempty = false;
5011
5125
  }
5012
5126
  }
5127
+ if(!isempty) out.push(row);
5013
5128
  }
5014
- return outSheet;
5015
- }
5016
-
5017
- function sheet_to_csv(sheet) {
5018
- var out = "";
5019
- if(sheet["!ref"]) {
5020
- var r = utils.decode_range(sheet["!ref"]);
5021
- for(var R = r.s.r; R <= r.e.r; ++R) {
5022
- var row = [];
5023
- for(var C = r.s.c; C <= r.e.c; ++C) {
5024
- var val = sheet[utils.encode_cell({c:C,r:R})];
5025
- if(!val) { row.push(""); continue; }
5026
- var fmt = 0;
5027
- if(val.XF) {
5028
- //console.log(val.XF, SSF._table[val.XF.ifmt], val.v)
5029
- val.v = SSF.format(val.XF.ifmt||0, val.v);
5030
- }
5031
- row.push(String(val.v).replace(/\\n/g,"\n").replace(/\\t/g,"\t").replace(/\\\\/g,"\\").replace(/\\\"/g,"\"\""));
5129
+ return out;
5130
+ }
5131
+
5132
+ function sheet_to_csv(sheet, opts) {
5133
+ var out = "", txt = "";
5134
+ opts = opts || {};
5135
+ if(!sheet || !sheet["!ref"]) return out;
5136
+ var r = utils.decode_range(sheet["!ref"]);
5137
+ var fs = opts.FS||",", rs = opts.RS||"\n";
5138
+
5139
+ for(var R = r.s.r; R <= r.e.r; ++R) {
5140
+ var row = [];
5141
+ for(var C = r.s.c; C <= r.e.c; ++C) {
5142
+ var val = sheet[utils.encode_cell({c:C,r:R})];
5143
+ if(!val) { row.push(""); continue; }
5144
+ txt = String(format_cell(val));
5145
+ if(txt.indexOf(fs) !== -1 || txt.indexOf(rs) !== -1 || txt.indexOf("\"") !== -1){
5146
+ txt = "\""+txt.replace(/"/g, '""') +"\"";
5032
5147
  }
5033
- out += row.join(",") + "\n";
5148
+ row.push(txt);
5034
5149
  }
5150
+ out += row.join(fs) + (rs);
5035
5151
  }
5036
5152
  return out;
5037
5153
  }
@@ -5043,7 +5159,8 @@ function get_formulae(ws) {
5043
5159
  var x = ws[y];
5044
5160
  var val = "";
5045
5161
  if(x.f) val = x.f;
5046
- else if(typeof x.v === 'number') val = x.v;
5162
+ else if(typeof x.w !== 'undefined') val = "'" + x.w;
5163
+ else if(typeof x.v === 'undefined') continue;
5047
5164
  else val = x.v;
5048
5165
  cmds.push(y + "=" + val);
5049
5166
  }
@@ -5063,13 +5180,14 @@ var utils = {
5063
5180
  sheet_to_csv: sheet_to_csv,
5064
5181
  make_csv: sheet_to_csv,
5065
5182
  get_formulae: get_formulae,
5183
+ format_cell: format_cell,
5066
5184
  sheet_to_row_object_array: sheet_to_row_object_array
5067
5185
  };
5068
5186
 
5069
5187
  function xlsread(f, options) {
5070
- return parse_xlscfb(CFB.read(f, options));
5188
+ return parse_xlscfb(CFB.read(f, options), options);
5071
5189
  }
5072
- var readFile = function(f) { return parse_xlscfb(CFB.read(f, {type:'file'})); };
5190
+ var readFile = function(f,o){return parse_xlscfb(CFB.read(f,{type:'file'}),o);};
5073
5191
  function decode_row(rowstr) { return Number(unfix_row(rowstr)) - 1; }
5074
5192
  function encode_row(row) { return "" + (row + 1); }
5075
5193
  function fix_row(cstr) { return cstr.replace(/([A-Z]|^)([0-9]+)$/,"$1$$$2"); }
@@ -5121,13 +5239,5 @@ XLS.read = xlsread;
5121
5239
  XLS.readFile = readFile;
5122
5240
  XLS.utils = utils;
5123
5241
  XLS.CFB = CFB;
5124
- if(typeof module !== 'undefined' && require.main === module ) {
5125
- var wb = readFile(process.argv[2] || 'Book1.xls');
5126
- var target_sheet = process.argv[3] || '';
5127
- if(target_sheet === '') target_sheet = wb.Directory[0];
5128
- var ws = wb.Sheets[target_sheet];
5129
- console.log(target_sheet);
5130
- console.log(make_csv(ws));
5131
- //console.log(get_formulae(ws));
5132
- }
5242
+ XLS.SSF = SSF;
5133
5243
  })(typeof exports !== 'undefined' ? exports : XLS);