exceljs
Version:
Excel Workbook Manager - Read and Write xlsx and csv Files.
2,014 lines (1,957 loc) • 1.74 MB
JavaScript
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ExcelJS = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
"use strict";
const fs = require('fs');
const fastCsv = require('fast-csv');
const customParseFormat = require('dayjs/plugin/customParseFormat');
const utc = require('dayjs/plugin/utc');
const dayjs = require('dayjs').extend(customParseFormat).extend(utc);
const StreamBuf = require('../utils/stream-buf');
const {
fs: {
exists
}
} = require('../utils/utils');
/* eslint-disable quote-props */
const SpecialValues = {
true: true,
false: false,
'#N/A': {
error: '#N/A'
},
'#REF!': {
error: '#REF!'
},
'#NAME?': {
error: '#NAME?'
},
'#DIV/0!': {
error: '#DIV/0!'
},
'#NULL!': {
error: '#NULL!'
},
'#VALUE!': {
error: '#VALUE!'
},
'#NUM!': {
error: '#NUM!'
}
};
/* eslint-ensable quote-props */
class CSV {
constructor(workbook) {
this.workbook = workbook;
this.worksheet = null;
}
async readFile(filename, options) {
options = options || {};
if (!(await exists(filename))) {
throw new Error(`File not found: ${filename}`);
}
const stream = fs.createReadStream(filename);
const worksheet = await this.read(stream, options);
stream.close();
return worksheet;
}
read(stream, options) {
options = options || {};
return new Promise((resolve, reject) => {
const worksheet = this.workbook.addWorksheet(options.sheetName);
const dateFormats = options.dateFormats || ['YYYY-MM-DD[T]HH:mm:ssZ', 'YYYY-MM-DD[T]HH:mm:ss', 'MM-DD-YYYY', 'YYYY-MM-DD'];
const map = options.map || function (datum) {
if (datum === '') {
return null;
}
const datumNumber = Number(datum);
if (!Number.isNaN(datumNumber) && datumNumber !== Infinity) {
return datumNumber;
}
const dt = dateFormats.reduce((matchingDate, currentDateFormat) => {
if (matchingDate) {
return matchingDate;
}
const dayjsObj = dayjs(datum, currentDateFormat, true);
if (dayjsObj.isValid()) {
return dayjsObj;
}
return null;
}, null);
if (dt) {
return new Date(dt.valueOf());
}
const special = SpecialValues[datum];
if (special !== undefined) {
return special;
}
return datum;
};
const csvStream = fastCsv.parse(options.parserOptions).on('data', data => {
worksheet.addRow(data.map(map));
}).on('end', () => {
csvStream.emit('worksheet', worksheet);
});
csvStream.on('worksheet', resolve).on('error', reject);
stream.pipe(csvStream);
});
}
/**
* @deprecated since version 4.0. You should use `CSV#read` instead. Please follow upgrade instruction: https://github.com/exceljs/exceljs/blob/master/UPGRADE-4.0.md
*/
createInputStream() {
throw new Error('`CSV#createInputStream` is deprecated. You should use `CSV#read` instead. This method will be removed in version 5.0. Please follow upgrade instruction: https://github.com/exceljs/exceljs/blob/master/UPGRADE-4.0.md');
}
write(stream, options) {
return new Promise((resolve, reject) => {
options = options || {};
// const encoding = options.encoding || 'utf8';
// const separator = options.separator || ',';
// const quoteChar = options.quoteChar || '\'';
const worksheet = this.workbook.getWorksheet(options.sheetName || options.sheetId);
const csvStream = fastCsv.format(options.formatterOptions);
stream.on('finish', () => {
resolve();
});
csvStream.on('error', reject);
csvStream.pipe(stream);
const {
dateFormat,
dateUTC
} = options;
const map = options.map || (value => {
if (value) {
if (value.text || value.hyperlink) {
return value.hyperlink || value.text || '';
}
if (value.formula || value.result) {
return value.result || '';
}
if (value instanceof Date) {
if (dateFormat) {
return dateUTC ? dayjs.utc(value).format(dateFormat) : dayjs(value).format(dateFormat);
}
return dateUTC ? dayjs.utc(value).format() : dayjs(value).format();
}
if (value.error) {
return value.error;
}
if (typeof value === 'object') {
return JSON.stringify(value);
}
}
return value;
});
const includeEmptyRows = options.includeEmptyRows === undefined || options.includeEmptyRows;
let lastRow = 1;
if (worksheet) {
worksheet.eachRow((row, rowNumber) => {
if (includeEmptyRows) {
while (lastRow++ < rowNumber - 1) {
csvStream.write([]);
}
}
const {
values
} = row;
values.shift();
csvStream.write(values.map(map));
lastRow = rowNumber;
});
}
csvStream.end();
});
}
writeFile(filename, options) {
options = options || {};
const streamOptions = {
encoding: options.encoding || 'utf8'
};
const stream = fs.createWriteStream(filename, streamOptions);
return this.write(stream, options);
}
async writeBuffer(options) {
const stream = new StreamBuf();
await this.write(stream, options);
return stream.read();
}
}
module.exports = CSV;
},{"../utils/stream-buf":24,"../utils/utils":27,"dayjs":230,"dayjs/plugin/customParseFormat":231,"dayjs/plugin/utc":232,"fast-csv":263,"fs":216}],2:[function(require,module,exports){
'use strict';
const colCache = require('../utils/col-cache');
class Anchor {
constructor(worksheet, address) {
let offset = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
this.worksheet = worksheet;
if (!address) {
this.nativeCol = 0;
this.nativeColOff = 0;
this.nativeRow = 0;
this.nativeRowOff = 0;
} else if (typeof address === 'string') {
const decoded = colCache.decodeAddress(address);
this.nativeCol = decoded.col + offset;
this.nativeColOff = 0;
this.nativeRow = decoded.row + offset;
this.nativeRowOff = 0;
} else if (address.nativeCol !== undefined) {
this.nativeCol = address.nativeCol || 0;
this.nativeColOff = address.nativeColOff || 0;
this.nativeRow = address.nativeRow || 0;
this.nativeRowOff = address.nativeRowOff || 0;
} else if (address.col !== undefined) {
this.col = address.col + offset;
this.row = address.row + offset;
} else {
this.nativeCol = 0;
this.nativeColOff = 0;
this.nativeRow = 0;
this.nativeRowOff = 0;
}
}
static asInstance(model) {
return model instanceof Anchor || model == null ? model : new Anchor(model);
}
get col() {
return this.nativeCol + Math.min(this.colWidth - 1, this.nativeColOff) / this.colWidth;
}
set col(v) {
this.nativeCol = Math.floor(v);
this.nativeColOff = Math.floor((v - this.nativeCol) * this.colWidth);
}
get row() {
return this.nativeRow + Math.min(this.rowHeight - 1, this.nativeRowOff) / this.rowHeight;
}
set row(v) {
this.nativeRow = Math.floor(v);
this.nativeRowOff = Math.floor((v - this.nativeRow) * this.rowHeight);
}
get colWidth() {
return this.worksheet && this.worksheet.getColumn(this.nativeCol + 1) && this.worksheet.getColumn(this.nativeCol + 1).isCustomWidth ? Math.floor(this.worksheet.getColumn(this.nativeCol + 1).width * 10000) : 640000;
}
get rowHeight() {
return this.worksheet && this.worksheet.getRow(this.nativeRow + 1) && this.worksheet.getRow(this.nativeRow + 1).height ? Math.floor(this.worksheet.getRow(this.nativeRow + 1).height * 10000) : 180000;
}
get model() {
return {
nativeCol: this.nativeCol,
nativeColOff: this.nativeColOff,
nativeRow: this.nativeRow,
nativeRowOff: this.nativeRowOff
};
}
set model(value) {
this.nativeCol = value.nativeCol;
this.nativeColOff = value.nativeColOff;
this.nativeRow = value.nativeRow;
this.nativeRowOff = value.nativeRowOff;
}
}
module.exports = Anchor;
},{"../utils/col-cache":19}],3:[function(require,module,exports){
"use strict";
/* eslint-disable max-classes-per-file */
const colCache = require('../utils/col-cache');
const _ = require('../utils/under-dash');
const Enums = require('./enums');
const {
slideFormula
} = require('../utils/shared-formula');
const Note = require('./note');
// Cell requirements
// Operate inside a worksheet
// Store and retrieve a value with a range of types: text, number, date, hyperlink, reference, formula, etc.
// Manage/use and manipulate cell format either as local to cell or inherited from column or row.
class Cell {
constructor(row, column, address) {
if (!row || !column) {
throw new Error('A Cell needs a Row');
}
this._row = row;
this._column = column;
colCache.validateAddress(address);
this._address = address;
// TODO: lazy evaluation of this._value
this._value = Value.create(Cell.Types.Null, this);
this.style = this._mergeStyle(row.style, column.style, {});
this._mergeCount = 0;
}
get worksheet() {
return this._row.worksheet;
}
get workbook() {
return this._row.worksheet.workbook;
}
// help GC by removing cyclic (and other) references
destroy() {
delete this.style;
delete this._value;
delete this._row;
delete this._column;
delete this._address;
}
// =========================================================================
// Styles stuff
get numFmt() {
return this.style.numFmt;
}
set numFmt(value) {
this.style.numFmt = value;
}
get font() {
return this.style.font;
}
set font(value) {
this.style.font = value;
}
get alignment() {
return this.style.alignment;
}
set alignment(value) {
this.style.alignment = value;
}
get border() {
return this.style.border;
}
set border(value) {
this.style.border = value;
}
get fill() {
return this.style.fill;
}
set fill(value) {
this.style.fill = value;
}
get protection() {
return this.style.protection;
}
set protection(value) {
this.style.protection = value;
}
_mergeStyle(rowStyle, colStyle, style) {
const numFmt = rowStyle && rowStyle.numFmt || colStyle && colStyle.numFmt;
if (numFmt) style.numFmt = numFmt;
const font = rowStyle && rowStyle.font || colStyle && colStyle.font;
if (font) style.font = font;
const alignment = rowStyle && rowStyle.alignment || colStyle && colStyle.alignment;
if (alignment) style.alignment = alignment;
const border = rowStyle && rowStyle.border || colStyle && colStyle.border;
if (border) style.border = border;
const fill = rowStyle && rowStyle.fill || colStyle && colStyle.fill;
if (fill) style.fill = fill;
const protection = rowStyle && rowStyle.protection || colStyle && colStyle.protection;
if (protection) style.protection = protection;
return style;
}
// =========================================================================
// return the address for this cell
get address() {
return this._address;
}
get row() {
return this._row.number;
}
get col() {
return this._column.number;
}
get $col$row() {
return `$${this._column.letter}$${this.row}`;
}
// =========================================================================
// Value stuff
get type() {
return this._value.type;
}
get effectiveType() {
return this._value.effectiveType;
}
toCsvString() {
return this._value.toCsvString();
}
// =========================================================================
// Merge stuff
addMergeRef() {
this._mergeCount++;
}
releaseMergeRef() {
this._mergeCount--;
}
get isMerged() {
return this._mergeCount > 0 || this.type === Cell.Types.Merge;
}
merge(master, ignoreStyle) {
this._value.release();
this._value = Value.create(Cell.Types.Merge, this, master);
if (!ignoreStyle) {
this.style = master.style;
}
}
unmerge() {
if (this.type === Cell.Types.Merge) {
this._value.release();
this._value = Value.create(Cell.Types.Null, this);
this.style = this._mergeStyle(this._row.style, this._column.style, {});
}
}
isMergedTo(master) {
if (this._value.type !== Cell.Types.Merge) return false;
return this._value.isMergedTo(master);
}
get master() {
if (this.type === Cell.Types.Merge) {
return this._value.master;
}
return this; // an unmerged cell is its own master
}
get isHyperlink() {
return this._value.type === Cell.Types.Hyperlink;
}
get hyperlink() {
return this._value.hyperlink;
}
// return the value
get value() {
return this._value.value;
}
// set the value - can be number, string or raw
set value(v) {
// special case - merge cells set their master's value
if (this.type === Cell.Types.Merge) {
this._value.master.value = v;
return;
}
this._value.release();
// assign value
this._value = Value.create(Value.getType(v), this, v);
}
get note() {
return this._comment && this._comment.note;
}
set note(note) {
this._comment = new Note(note);
}
get text() {
return this._value.toString();
}
get html() {
return _.escapeHtml(this.text);
}
toString() {
return this.text;
}
_upgradeToHyperlink(hyperlink) {
// if this cell is a string, turn it into a Hyperlink
if (this.type === Cell.Types.String) {
this._value = Value.create(Cell.Types.Hyperlink, this, {
text: this._value.value,
hyperlink
});
}
}
// =========================================================================
// Formula stuff
get formula() {
return this._value.formula;
}
get result() {
return this._value.result;
}
get formulaType() {
return this._value.formulaType;
}
// =========================================================================
// Name stuff
get fullAddress() {
const {
worksheet
} = this._row;
return {
sheetName: worksheet.name,
address: this.address,
row: this.row,
col: this.col
};
}
get name() {
return this.names[0];
}
set name(value) {
this.names = [value];
}
get names() {
return this.workbook.definedNames.getNamesEx(this.fullAddress);
}
set names(value) {
const {
definedNames
} = this.workbook;
definedNames.removeAllNames(this.fullAddress);
value.forEach(name => {
definedNames.addEx(this.fullAddress, name);
});
}
addName(name) {
this.workbook.definedNames.addEx(this.fullAddress, name);
}
removeName(name) {
this.workbook.definedNames.removeEx(this.fullAddress, name);
}
removeAllNames() {
this.workbook.definedNames.removeAllNames(this.fullAddress);
}
// =========================================================================
// Data Validation stuff
get _dataValidations() {
return this.worksheet.dataValidations;
}
get dataValidation() {
return this._dataValidations.find(this.address);
}
set dataValidation(value) {
this._dataValidations.add(this.address, value);
}
// =========================================================================
// Model stuff
get model() {
const {
model
} = this._value;
model.style = this.style;
if (this._comment) {
model.comment = this._comment.model;
}
return model;
}
set model(value) {
this._value.release();
this._value = Value.create(value.type, this);
this._value.model = value;
if (value.comment) {
switch (value.comment.type) {
case 'note':
this._comment = Note.fromModel(value.comment);
break;
}
}
if (value.style) {
this.style = value.style;
} else {
this.style = {};
}
}
}
Cell.Types = Enums.ValueType;
// =============================================================================
// Internal Value Types
class NullValue {
constructor(cell) {
this.model = {
address: cell.address,
type: Cell.Types.Null
};
}
get value() {
return null;
}
set value(value) {
// nothing to do
}
get type() {
return Cell.Types.Null;
}
get effectiveType() {
return Cell.Types.Null;
}
get address() {
return this.model.address;
}
set address(value) {
this.model.address = value;
}
toCsvString() {
return '';
}
release() {}
toString() {
return '';
}
}
class NumberValue {
constructor(cell, value) {
this.model = {
address: cell.address,
type: Cell.Types.Number,
value
};
}
get value() {
return this.model.value;
}
set value(value) {
this.model.value = value;
}
get type() {
return Cell.Types.Number;
}
get effectiveType() {
return Cell.Types.Number;
}
get address() {
return this.model.address;
}
set address(value) {
this.model.address = value;
}
toCsvString() {
return this.model.value.toString();
}
release() {}
toString() {
return this.model.value.toString();
}
}
class StringValue {
constructor(cell, value) {
this.model = {
address: cell.address,
type: Cell.Types.String,
value
};
}
get value() {
return this.model.value;
}
set value(value) {
this.model.value = value;
}
get type() {
return Cell.Types.String;
}
get effectiveType() {
return Cell.Types.String;
}
get address() {
return this.model.address;
}
set address(value) {
this.model.address = value;
}
toCsvString() {
return `"${this.model.value.replace(/"/g, '""')}"`;
}
release() {}
toString() {
return this.model.value;
}
}
class RichTextValue {
constructor(cell, value) {
this.model = {
address: cell.address,
type: Cell.Types.String,
value
};
}
get value() {
return this.model.value;
}
set value(value) {
this.model.value = value;
}
toString() {
return this.model.value.richText.map(t => t.text).join('');
}
get type() {
return Cell.Types.RichText;
}
get effectiveType() {
return Cell.Types.RichText;
}
get address() {
return this.model.address;
}
set address(value) {
this.model.address = value;
}
toCsvString() {
return `"${this.text.replace(/"/g, '""')}"`;
}
release() {}
}
class DateValue {
constructor(cell, value) {
this.model = {
address: cell.address,
type: Cell.Types.Date,
value
};
}
get value() {
return this.model.value;
}
set value(value) {
this.model.value = value;
}
get type() {
return Cell.Types.Date;
}
get effectiveType() {
return Cell.Types.Date;
}
get address() {
return this.model.address;
}
set address(value) {
this.model.address = value;
}
toCsvString() {
return this.model.value.toISOString();
}
release() {}
toString() {
return this.model.value.toString();
}
}
class HyperlinkValue {
constructor(cell, value) {
this.model = {
address: cell.address,
type: Cell.Types.Hyperlink,
text: value ? value.text : undefined,
hyperlink: value ? value.hyperlink : undefined
};
if (value && value.tooltip) {
this.model.tooltip = value.tooltip;
}
}
get value() {
const v = {
text: this.model.text,
hyperlink: this.model.hyperlink
};
if (this.model.tooltip) {
v.tooltip = this.model.tooltip;
}
return v;
}
set value(value) {
this.model = {
text: value.text,
hyperlink: value.hyperlink
};
if (value.tooltip) {
this.model.tooltip = value.tooltip;
}
}
get text() {
return this.model.text;
}
set text(value) {
this.model.text = value;
}
/*
get tooltip() {
return this.model.tooltip;
}
set tooltip(value) {
this.model.tooltip = value;
} */
get hyperlink() {
return this.model.hyperlink;
}
set hyperlink(value) {
this.model.hyperlink = value;
}
get type() {
return Cell.Types.Hyperlink;
}
get effectiveType() {
return Cell.Types.Hyperlink;
}
get address() {
return this.model.address;
}
set address(value) {
this.model.address = value;
}
toCsvString() {
return this.model.hyperlink;
}
release() {}
toString() {
return this.model.text;
}
}
class MergeValue {
constructor(cell, master) {
this.model = {
address: cell.address,
type: Cell.Types.Merge,
master: master ? master.address : undefined
};
this._master = master;
if (master) {
master.addMergeRef();
}
}
get value() {
return this._master.value;
}
set value(value) {
if (value instanceof Cell) {
if (this._master) {
this._master.releaseMergeRef();
}
value.addMergeRef();
this._master = value;
} else {
this._master.value = value;
}
}
isMergedTo(master) {
return master === this._master;
}
get master() {
return this._master;
}
get type() {
return Cell.Types.Merge;
}
get effectiveType() {
return this._master.effectiveType;
}
get address() {
return this.model.address;
}
set address(value) {
this.model.address = value;
}
toCsvString() {
return '';
}
release() {
this._master.releaseMergeRef();
}
toString() {
return this.value.toString();
}
}
class FormulaValue {
constructor(cell, value) {
this.cell = cell;
this.model = {
address: cell.address,
type: Cell.Types.Formula,
shareType: value ? value.shareType : undefined,
ref: value ? value.ref : undefined,
formula: value ? value.formula : undefined,
sharedFormula: value ? value.sharedFormula : undefined,
result: value ? value.result : undefined
};
}
_copyModel(model) {
const copy = {};
const cp = name => {
const value = model[name];
if (value) {
copy[name] = value;
}
};
cp('formula');
cp('result');
cp('ref');
cp('shareType');
cp('sharedFormula');
return copy;
}
get value() {
return this._copyModel(this.model);
}
set value(value) {
this.model = this._copyModel(value);
}
validate(value) {
switch (Value.getType(value)) {
case Cell.Types.Null:
case Cell.Types.String:
case Cell.Types.Number:
case Cell.Types.Date:
break;
case Cell.Types.Hyperlink:
case Cell.Types.Formula:
default:
throw new Error('Cannot process that type of result value');
}
}
get dependencies() {
// find all the ranges and cells mentioned in the formula
const ranges = this.formula.match(/([a-zA-Z0-9]+!)?[A-Z]{1,3}\d{1,4}:[A-Z]{1,3}\d{1,4}/g);
const cells = this.formula.replace(/([a-zA-Z0-9]+!)?[A-Z]{1,3}\d{1,4}:[A-Z]{1,3}\d{1,4}/g, '').match(/([a-zA-Z0-9]+!)?[A-Z]{1,3}\d{1,4}/g);
return {
ranges,
cells
};
}
get formula() {
return this.model.formula || this._getTranslatedFormula();
}
set formula(value) {
this.model.formula = value;
}
get formulaType() {
if (this.model.formula) {
return Enums.FormulaType.Master;
}
if (this.model.sharedFormula) {
return Enums.FormulaType.Shared;
}
return Enums.FormulaType.None;
}
get result() {
return this.model.result;
}
set result(value) {
this.model.result = value;
}
get type() {
return Cell.Types.Formula;
}
get effectiveType() {
const v = this.model.result;
if (v === null || v === undefined) {
return Enums.ValueType.Null;
}
if (v instanceof String || typeof v === 'string') {
return Enums.ValueType.String;
}
if (typeof v === 'number') {
return Enums.ValueType.Number;
}
if (v instanceof Date) {
return Enums.ValueType.Date;
}
if (v.text && v.hyperlink) {
return Enums.ValueType.Hyperlink;
}
if (v.formula) {
return Enums.ValueType.Formula;
}
return Enums.ValueType.Null;
}
get address() {
return this.model.address;
}
set address(value) {
this.model.address = value;
}
_getTranslatedFormula() {
if (!this._translatedFormula && this.model.sharedFormula) {
const {
worksheet
} = this.cell;
const master = worksheet.findCell(this.model.sharedFormula);
this._translatedFormula = master && slideFormula(master.formula, master.address, this.model.address);
}
return this._translatedFormula;
}
toCsvString() {
return `${this.model.result || ''}`;
}
release() {}
toString() {
return this.model.result ? this.model.result.toString() : '';
}
}
class SharedStringValue {
constructor(cell, value) {
this.model = {
address: cell.address,
type: Cell.Types.SharedString,
value
};
}
get value() {
return this.model.value;
}
set value(value) {
this.model.value = value;
}
get type() {
return Cell.Types.SharedString;
}
get effectiveType() {
return Cell.Types.SharedString;
}
get address() {
return this.model.address;
}
set address(value) {
this.model.address = value;
}
toCsvString() {
return this.model.value.toString();
}
release() {}
toString() {
return this.model.value.toString();
}
}
class BooleanValue {
constructor(cell, value) {
this.model = {
address: cell.address,
type: Cell.Types.Boolean,
value
};
}
get value() {
return this.model.value;
}
set value(value) {
this.model.value = value;
}
get type() {
return Cell.Types.Boolean;
}
get effectiveType() {
return Cell.Types.Boolean;
}
get address() {
return this.model.address;
}
set address(value) {
this.model.address = value;
}
toCsvString() {
return this.model.value ? 1 : 0;
}
release() {}
toString() {
return this.model.value.toString();
}
}
class ErrorValue {
constructor(cell, value) {
this.model = {
address: cell.address,
type: Cell.Types.Error,
value
};
}
get value() {
return this.model.value;
}
set value(value) {
this.model.value = value;
}
get type() {
return Cell.Types.Error;
}
get effectiveType() {
return Cell.Types.Error;
}
get address() {
return this.model.address;
}
set address(value) {
this.model.address = value;
}
toCsvString() {
return this.toString();
}
release() {}
toString() {
return this.model.value.error.toString();
}
}
class JSONValue {
constructor(cell, value) {
this.model = {
address: cell.address,
type: Cell.Types.String,
value: JSON.stringify(value),
rawValue: value
};
}
get value() {
return this.model.rawValue;
}
set value(value) {
this.model.rawValue = value;
this.model.value = JSON.stringify(value);
}
get type() {
return Cell.Types.String;
}
get effectiveType() {
return Cell.Types.String;
}
get address() {
return this.model.address;
}
set address(value) {
this.model.address = value;
}
toCsvString() {
return this.model.value;
}
release() {}
toString() {
return this.model.value;
}
}
// Value is a place to hold common static Value type functions
const Value = {
getType(value) {
if (value === null || value === undefined) {
return Cell.Types.Null;
}
if (value instanceof String || typeof value === 'string') {
return Cell.Types.String;
}
if (typeof value === 'number') {
return Cell.Types.Number;
}
if (typeof value === 'boolean') {
return Cell.Types.Boolean;
}
if (value instanceof Date) {
return Cell.Types.Date;
}
if (value.text && value.hyperlink) {
return Cell.Types.Hyperlink;
}
if (value.formula || value.sharedFormula) {
return Cell.Types.Formula;
}
if (value.richText) {
return Cell.Types.RichText;
}
if (value.sharedString) {
return Cell.Types.SharedString;
}
if (value.error) {
return Cell.Types.Error;
}
return Cell.Types.JSON;
},
// map valueType to constructor
types: [{
t: Cell.Types.Null,
f: NullValue
}, {
t: Cell.Types.Number,
f: NumberValue
}, {
t: Cell.Types.String,
f: StringValue
}, {
t: Cell.Types.Date,
f: DateValue
}, {
t: Cell.Types.Hyperlink,
f: HyperlinkValue
}, {
t: Cell.Types.Formula,
f: FormulaValue
}, {
t: Cell.Types.Merge,
f: MergeValue
}, {
t: Cell.Types.JSON,
f: JSONValue
}, {
t: Cell.Types.SharedString,
f: SharedStringValue
}, {
t: Cell.Types.RichText,
f: RichTextValue
}, {
t: Cell.Types.Boolean,
f: BooleanValue
}, {
t: Cell.Types.Error,
f: ErrorValue
}].reduce((p, t) => {
p[t.t] = t.f;
return p;
}, []),
create(type, cell, value) {
const T = this.types[type];
if (!T) {
throw new Error(`Could not create Value of type ${type}`);
}
return new T(cell, value);
}
};
module.exports = Cell;
},{"../utils/col-cache":19,"../utils/shared-formula":23,"../utils/under-dash":26,"./enums":7,"./note":9}],4:[function(require,module,exports){
'use strict';
const _ = require('../utils/under-dash');
const Enums = require('./enums');
const colCache = require('../utils/col-cache');
const DEFAULT_COLUMN_WIDTH = 9;
// Column defines the column properties for 1 column.
// This includes header rows, widths, key, (style), etc.
// Worksheet will condense the columns as appropriate during serialization
class Column {
constructor(worksheet, number, defn) {
this._worksheet = worksheet;
this._number = number;
if (defn !== false) {
// sometimes defn will follow
this.defn = defn;
}
}
get number() {
return this._number;
}
get worksheet() {
return this._worksheet;
}
get letter() {
return colCache.n2l(this._number);
}
get isCustomWidth() {
return this.width !== undefined && this.width !== DEFAULT_COLUMN_WIDTH;
}
get defn() {
return {
header: this._header,
key: this.key,
width: this.width,
style: this.style,
hidden: this.hidden,
outlineLevel: this.outlineLevel
};
}
set defn(value) {
if (value) {
this.key = value.key;
this.width = value.width !== undefined ? value.width : DEFAULT_COLUMN_WIDTH;
this.outlineLevel = value.outlineLevel;
if (value.style) {
this.style = value.style;
} else {
this.style = {};
}
// headers must be set after style
this.header = value.header;
this._hidden = !!value.hidden;
} else {
delete this._header;
delete this._key;
delete this.width;
this.style = {};
this.outlineLevel = 0;
}
}
get headers() {
return this._header && this._header instanceof Array ? this._header : [this._header];
}
get header() {
return this._header;
}
set header(value) {
if (value !== undefined) {
this._header = value;
this.headers.forEach((text, index) => {
this._worksheet.getCell(index + 1, this.number).value = text;
});
} else {
this._header = undefined;
}
}
get key() {
return this._key;
}
set key(value) {
const column = this._key && this._worksheet.getColumnKey(this._key);
if (column === this) {
this._worksheet.deleteColumnKey(this._key);
}
this._key = value;
if (value) {
this._worksheet.setColumnKey(this._key, this);
}
}
get hidden() {
return !!this._hidden;
}
set hidden(value) {
this._hidden = value;
}
get outlineLevel() {
return this._outlineLevel || 0;
}
set outlineLevel(value) {
this._outlineLevel = value;
}
get collapsed() {
return !!(this._outlineLevel && this._outlineLevel >= this._worksheet.properties.outlineLevelCol);
}
toString() {
return JSON.stringify({
key: this.key,
width: this.width,
headers: this.headers.length ? this.headers : undefined
});
}
equivalentTo(other) {
return this.width === other.width && this.hidden === other.hidden && this.outlineLevel === other.outlineLevel && _.isEqual(this.style, other.style);
}
get isDefault() {
if (this.isCustomWidth) {
return false;
}
if (this.hidden) {
return false;
}
if (this.outlineLevel) {
return false;
}
const s = this.style;
if (s && (s.font || s.numFmt || s.alignment || s.border || s.fill || s.protection)) {
return false;
}
return true;
}
get headerCount() {
return this.headers.length;
}
eachCell(options, iteratee) {
const colNumber = this.number;
if (!iteratee) {
iteratee = options;
options = null;
}
this._worksheet.eachRow(options, (row, rowNumber) => {
iteratee(row.getCell(colNumber), rowNumber);
});
}
get values() {
const v = [];
this.eachCell((cell, rowNumber) => {
if (cell && cell.type !== Enums.ValueType.Null) {
v[rowNumber] = cell.value;
}
});
return v;
}
set values(v) {
if (!v) {
return;
}
const colNumber = this.number;
let offset = 0;
if (v.hasOwnProperty('0')) {
// assume contiguous array, start at row 1
offset = 1;
}
v.forEach((value, index) => {
this._worksheet.getCell(index + offset, colNumber).value = value;
});
}
// =========================================================================
// styles
_applyStyle(name, value) {
this.style[name] = value;
this.eachCell(cell => {
cell[name] = value;
});
return value;
}
get numFmt() {
return this.style.numFmt;
}
set numFmt(value) {
this._applyStyle('numFmt', value);
}
get font() {
return this.style.font;
}
set font(value) {
this._applyStyle('font', value);
}
get alignment() {
return this.style.alignment;
}
set alignment(value) {
this._applyStyle('alignment', value);
}
get protection() {
return this.style.protection;
}
set protection(value) {
this._applyStyle('protection', value);
}
get border() {
return this.style.border;
}
set border(value) {
this._applyStyle('border', value);
}
get fill() {
return this.style.fill;
}
set fill(value) {
this._applyStyle('fill', value);
}
// =============================================================================
// static functions
static toModel(columns) {
// Convert array of Column into compressed list cols
const cols = [];
let col = null;
if (columns) {
columns.forEach((column, index) => {
if (column.isDefault) {
if (col) {
col = null;
}
} else if (!col || !column.equivalentTo(col)) {
col = {
min: index + 1,
max: index + 1,
width: column.width !== undefined ? column.width : DEFAULT_COLUMN_WIDTH,
style: column.style,
isCustomWidth: column.isCustomWidth,
hidden: column.hidden,
outlineLevel: column.outlineLevel,
collapsed: column.collapsed
};
cols.push(col);
} else {
col.max = index + 1;
}
});
}
return cols.length ? cols : undefined;
}
static fromModel(worksheet, cols) {
cols = cols || [];
const columns = [];
let count = 1;
let index = 0;
/**
* sort cols by min
* If it is not sorted, the subsequent column configuration will be overwritten
* */
cols = cols.sort(function (pre, next) {
return pre.min - next.min;
});
while (index < cols.length) {
const col = cols[index++];
while (count < col.min) {
columns.push(new Column(worksheet, count++));
}
while (count <= col.max) {
columns.push(new Column(worksheet, count++, col));
}
}
return columns.length ? columns : null;
}
}
module.exports = Column;
},{"../utils/col-cache":19,"../utils/under-dash":26,"./enums":7}],5:[function(require,module,exports){
"use strict";
class DataValidations {
constructor(model) {
this.model = model || {};
}
add(address, validation) {
return this.model[address] = validation;
}
find(address) {
return this.model[address];
}
remove(address) {
this.model[address] = undefined;
}
}
module.exports = DataValidations;
},{}],6:[function(require,module,exports){
'use strict';
const _ = require('../utils/under-dash');
const colCache = require('../utils/col-cache');
const CellMatrix = require('../utils/cell-matrix');
const Range = require('./range');
const rangeRegexp = /[$](\w+)[$](\d+)(:[$](\w+)[$](\d+))?/;
class DefinedNames {
constructor() {
this.matrixMap = {};
}
getMatrix(name) {
const matrix = this.matrixMap[name] || (this.matrixMap[name] = new CellMatrix());
return matrix;
}
// add a name to a cell. locStr in the form SheetName!$col$row or SheetName!$c1$r1:$c2:$r2
add(locStr, name) {
const location = colCache.decodeEx(locStr);
this.addEx(location, name);
}
addEx(location, name) {
const matrix = this.getMatrix(name);
if (location.top) {
for (let col = location.left; col <= location.right; col++) {
for (let row = location.top; row <= location.bottom; row++) {
const address = {
sheetName: location.sheetName,
address: colCache.n2l(col) + row,
row,
col
};
matrix.addCellEx(address);
}
}
} else {
matrix.addCellEx(location);
}
}
remove(locStr, name) {
const location = colCache.decodeEx(locStr);
this.removeEx(location, name);
}
removeEx(location, name) {
const matrix = this.getMatrix(name);
matrix.removeCellEx(location);
}
removeAllNames(location) {
_.each(this.matrixMap, matrix => {
matrix.removeCellEx(location);
});
}
forEach(callback) {
_.each(this.matrixMap, (matrix, name) => {
matrix.forEach(cell => {
callback(name, cell);
});
});
}
// get all the names of a cell
getNames(addressStr) {
return this.getNamesEx(colCache.decodeEx(addressStr));
}
getNamesEx(address) {
return _.map(this.matrixMap, (matrix, name) => matrix.findCellEx(address) && name).filter(Boolean);
}
_explore(matrix, cell) {
cell.mark = false;
const {
sheetName
} = cell;
const range = new Range(cell.row, cell.col, cell.row, cell.col, sheetName);
let x;
let y;
// grow vertical - only one col to worry about
function vGrow(yy, edge) {
const c = matrix.findCellAt(sheetName, yy, cell.col);
if (!c || !c.mark) {
return false;
}
range[edge] = yy;
c.mark = false;
return true;
}
for (y = cell.row - 1; vGrow(y, 'top'); y--);
for (y = cell.row + 1; vGrow(y, 'bottom'); y++);
// grow horizontal - ensure all rows can grow
function hGrow(xx, edge) {
const cells = [];
for (y = range.top; y <= range.bottom; y++) {
const c = matrix.findCellAt(sheetName, y, xx);
if (c && c.mark) {
cells.push(c);
} else {
return false;
}
}
range[edge] = xx;
for (let i = 0; i < cells.length; i++) {
cells[i].mark = false;
}
return true;
}
for (x = cell.col - 1; hGrow(x, 'left'); x--);
for (x = cell.col + 1; hGrow(x, 'right'); x++);
return range;
}
getRanges(name, matrix) {
matrix = matrix || this.matrixMap[name];
if (!matrix) {
return {
name,
ranges: []
};
}
// mark and sweep!
matrix.forEach(cell => {
cell.mark = true;
});
const ranges = matrix.map(cell => cell.mark && this._explore(matrix, cell)).filter(Boolean).map(range => range.$shortRange);
return {
name,
ranges
};
}
normaliseMatrix(matrix, sheetName) {
// some of the cells might have shifted on specified sheet
// need to reassign rows, cols
matrix.forEachInSheet(sheetName, (cell, row, col) => {
if (cell) {
if (cell.row !== row || cell.col !== col) {
cell.row = row;
cell.col = col;
cell.address = colCache.n2l(col) + row;
}
}
});
}
spliceRows(sheetName, start, numDelete, numInsert) {
_.each(this.matrixMap, matrix => {
matrix.spliceRows(sheetName, start, numDelete, numInsert);
this.normaliseMatrix(matrix, sheetName);
});
}
spliceColumns(sheetName, start, numDelete, numInsert) {
_.each(this.matrixMap, matrix => {
matrix.spliceColumns(sheetName, start, numDelete, numInsert);
this.normaliseMatrix(matrix, sheetName);
});
}
get model() {
// To get names per cell - just iterate over all names finding cells if they exist
return _.map(this.matrixMap, (matrix, name) => this.getRanges(name, matrix)).filter(definedName => definedName.ranges.length);
}
set model(value) {
// value is [ { name, ranges }, ... ]
const matrixMap = this.matrixMap = {};
value.forEach(definedName => {
const matrix = matrixMap[definedName.name] = new CellMatrix();
definedName.ranges.forEach(rangeStr => {
if (rangeRegexp.test(rangeStr.split('!').pop() || '')) {
matrix.addCell(rangeStr);
}
});
});
}
}
module.exports = DefinedNames;
},{"../utils/cell-matrix":18,"../utils/col-cache":19,"../utils/under-dash":26,"./range":10}],7:[function(require,module,exports){
'use strict';
module.exports = {
ValueType: {
Null: 0,
Merge: 1,
Number: 2,
String: 3,
Date: 4,
Hyperlink: 5,
Formula: 6,
SharedString: 7,
RichText: 8,
Boolean: 9,
Error: 10
},
FormulaType: {
None: 0,
Master: 1,
Shared: 2
},
RelationshipType: {
None: 0,
OfficeDocument: 1,
Worksheet: 2,
CalcChain: 3,
SharedStrings: 4,
Styles: 5,
Theme: 6,
Hyperlink: 7
},
DocumentType: {
Xlsx: 1
},
ReadingOrder: {
LeftToRight: 1,
RightToLeft: 2
},
ErrorValue: {
NotApplicable: '#N/A',
Ref: '#REF!',
Name: '#NAME?',
DivZero: '#DIV/0!',
Null: '#NULL!',
Value: '#VALUE!',
Num: '#NUM!'
}
};
},{}],8:[function(require,module,exports){
"use strict";
const colCache = require('../utils/col-cache');
const Anchor = require('./anchor');
class Image {
constructor(worksheet, model) {
this.worksheet = worksheet;
this.model = model;
}
get model() {
switch (this.type) {
case 'background':
return {
type: this.type,
imageId: this.imageId
};
case 'image':
return {
type: this.type,
imageId: this.imageId,
hyperlinks: this.range.hyperlinks,
range: {
tl: this.range.tl.model,
br: this.range.br && this.range.br.model,
ext: this.range.ext,
editAs: this.range.editAs
}
};
default:
throw new Error('Invalid Image Type');
}
}
set model(_ref) {
let {
type,
imageId,
range,
hyperlinks
} = _ref;
this.type = type;
this.imageId = imageId;
if (type === 'image') {
if (typeof range === 'string') {
const decoded = colCache.decode(range);
this.range = {
tl: new Anchor(this.worksheet, {
col: decoded.left,
row: decoded.top
}, -1),
br: new Anchor(this.worksheet, {
col: decoded.right,
row: decoded.bottom
}, 0),
editAs: 'oneCell'
};
} else {
this.range = {
tl: new Anchor(this.worksheet, range.tl, 0),
br: range.br && new Anchor(this.worksheet, range.br, 0),
ext: range.ext,
editAs: range.editAs,
hyperlinks: hyperlinks || range.hyperlinks
};
}
}
}
}
module.exports = Image;
},{"../utils/col-cache":19,"./anchor":2}],9:[function(require,module,exports){
"use strict";
const _ = require('../utils/under-dash');
class Note {
constructor(note) {
this.note = note;
}
get model() {
let value = null;
switch (typeof this.note) {
case 'string':
value = {
type: 'note',
note: {
texts: [{
text: this.note
}]
}
};
break;
default:
value = {
type: 'note',
note: this.note
};
break;
}
// Suitable for all cell comments
return _.deepMerge({}, Note.DEFAULT_CONFIGS, value);
}
set model(value) {
const {
note
} = value;
const {
texts
} = note;
if (texts.length === 1 && Object.keys(texts[0]).length === 1) {
this.note = texts[0].text;
} else {
this.note = note;
}
}
static fromModel(model) {
const note = new Note();
note.model = model;
return note;
}
}
Note.DEFAULT_CONFIGS = {
note: {
margins: {
insetmode: 'auto',
inset: [0.13, 0.13, 0.25, 0.25]
},
protection: {
locked: 'True',
lockText: 'True'
},
editAs: 'absolute'
}
};
module.exports = Note;
},{"../utils/under-dash":26}],10:[function(require,module,exports){
"use strict";
const colCache = require('../utils/col-cache');
// used by worksheet to calculate sheet dimensions
class Range {
constructor() {
this.decode(arguments);
}
setTLBR(t, l, b, r, s) {
if (arguments.length < 4) {
// setTLBR(tl, br, s)
const tl = colCache.decodeAddress(t);
const br = colCache.decodeAddress(l);
this.model = {
top: Math.min(tl.row, br.row),
left: Math.min(tl.col, br.col),
bottom: Math.max(tl.row, br.row),
right: Math.max(tl.col, br.col),
sheetName: b
};
this.setTLBR(tl.row, tl.col, br.row, br.col, s);
} else {
// setTLBR(t, l, b, r, s)
this.model = {
top: Math.min(t, b),
left: Math.min(l, r),
bottom: Math.max(t, b),
right: Math.max(l, r),
sheetName: s
};
}
}
decode(argv) {
switch (argv.length) {
case 5:
// [t,l,b,r,s]
this.setTLBR(argv[0], argv[1], argv[2], argv[3], argv[4]);
break;
case 4:
// [t,l,b,r]
this.setTLBR(argv[0], argv[1], argv[2], argv[3]);
break;
case 3:
// [tl,br,s]
this.setTLBR(argv[0], argv[1], argv[2]);
break;
case 2:
// [tl,br]
this.setTLBR(argv[0], argv[1]);
break;
case 1:
{
const value = argv[0];
if (value instanceof Range) {
// copy constructor
this.model = {
top: value.model.top,
left: value.model.left,
bottom: value.model.bottom,
right: value.model.right,
sheetName: value.sheetName
};
} else if (value instanceof Array) {
// an arguments array
this.decode(value);
} else if (value.top && value.left && value.bottom && value.right) {
// a model
this.model = {
top: value.top,
left: value.left,
bottom: value.bottom,
right: value.right,
sheetName: value.sheetName
};
} else {
// [sheetName!]tl:br
const tlbr = colCache.decodeEx(value);
if (tlbr.top) {
this.model = {
top: tlbr.top,
left: tlbr.left,
bottom: tlbr.bottom,
right: tlbr.right,
sheetName: tlbr.sheetName
};
} else {
this.model = {
top: tlbr.row,
left: tlbr.col,
bottom: tlbr.row,
right: tlbr.col,
sheetName: tlbr.sheetName
};
}
}
break;
}
case 0:
this.model = {
top: 0,
left: 0,
bottom: 0,
right: 0
};
break;
default:
throw new Error(`Invalid number of arguments to _getDimensions() - ${argv.length}`);
}
}
get top() {
return this.model.top || 1;
}
set top(value) {
this.model.top = value;
}
get left() {
return this.model.left || 1;
}
set left(value) {
this.model.left = value;
}
get bottom() {
return this.model.bottom || 1;
}
set bottom(value) {
this.model.bottom = value;
}
get right() {
return this.model.right || 1;
}
set right(value) {
this.model.right = value;
}
get sheetName() {
return this.model.sheetName;
}
set sheetName(value) {
this.model.sheetName = value;
}
get _