UNPKG

exceljs

Version:

Excel Workbook Manager - Read and Write xlsx and csv Files.

2,014 lines (1,957 loc) 1.74 MB
(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 _