| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961 |
- "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;
- //# sourceMappingURL=cell.js.map
|