????
Current Path : C:/inetpub/vhost/binhdinhinvest.gdtvietnam.com/api/node_modules/excel4node/source/lib/cell/ |
Current File : C:/inetpub/vhost/binhdinhinvest.gdtvietnam.com/api/node_modules/excel4node/source/lib/cell/index.js |
const deepmerge = require('deepmerge'); const Cell = require('./cell.js'); const Row = require('../row/row.js'); const Comment = require('../classes/comment'); const Column = require('../column/column.js'); const Style = require('../style/style.js'); const utils = require('../utils.js'); const util = require('util'); const validXmlRegex = /[\u0009\u000a\u000d\u0020-\uD7FF\uE000-\uFFFD]/u; /** * The list of valid characters is * #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] * * We need to test codepoints numerically, instead of regex characters above 65536 (0x10000), */ function removeInvalidXml(str) { return Array.from(str).map(c => { const cp = c.codePointAt(0); if (cp >= 65536 && cp <= 1114111) { return c } else if (c.match(validXmlRegex)) { return c; } else { return ''; } }).join(''); } function stringSetter(val) { let logger = this.ws.wb.logger; if (typeof (val) !== 'string') { logger.warn('Value sent to String function of cells %s was not a string, it has type of %s', JSON.stringify(this.excelRefs), typeof (val)); val = ''; } val = removeInvalidXml(val); if (!this.merged) { this.cells.forEach((c) => { c.string(this.ws.wb.getStringIndex(val)); }); } else { let c = this.cells[0]; c.string(this.ws.wb.getStringIndex(val)); } return this; } function complexStringSetter(val) { if (!this.merged) { this.cells.forEach((c) => { c.string(this.ws.wb.getStringIndex(val)); }); } else { let c = this.cells[0]; c.string(this.ws.wb.getStringIndex(val)); } return this; } function numberSetter(val) { if (val === undefined || parseFloat(val) !== val) { throw new TypeError(util.format('Value sent to Number function of cells %s was not a number, it has type of %s and value of %s', JSON.stringify(this.excelRefs), typeof (val), val )); } val = parseFloat(val); if (!this.merged) { this.cells.forEach((c, i) => { c.number(val); }); } else { var c = this.cells[0]; c.number(val); } return this; } function booleanSetter(val) { if (val !== true && val !== false) { let valString = val.toString().toLowerCase(); if (valString === "true") { val = true; } else if (valString === "false") { val = false; } else { throw new TypeError(util.format('Value sent to Bool function of cells %s was not a bool, it has type of %s and value of %s', JSON.stringify(this.excelRefs), typeof (val), val )); } } if (!this.merged) { this.cells.forEach((c, i) => { c.bool(val ? '1' : '0'); }); } else { var c = this.cells[0]; c.bool(val ? '1' : '0'); } return this; } function formulaSetter(val) { if (typeof (val) !== 'string') { throw new TypeError(util.format('Value sent to Formula function of cells %s was not a string, it has type of %s', JSON.stringify(this.excelRefs), typeof (val))); } if (this.merged !== true) { this.cells.forEach((c, i) => { c.formula(val); }); } else { var c = this.cells[0]; c.formula(val); } return this; } function dateSetter(val) { let thisDate = new Date(val); if (isNaN(thisDate.getTime())) { throw new TypeError(util.format('Invalid date sent to date function of cells. %s could not be converted to a date.', val)); } if (this.merged !== true) { this.cells.forEach((c, i) => { c.date(thisDate); }); } else { var c = this.cells[0]; c.date(thisDate); } const dtStyle = new Style(this.ws.wb, { numberFormat: '[$-409]' + this.ws.wb.opts.dateFormat }); return styleSetter.bind(this)(dtStyle); } function styleSetter(val) { let thisStyle; if (val instanceof Style) { thisStyle = val.toObject(); } else if (val instanceof Object) { thisStyle = val; } else { throw new TypeError(util.format('Parameter sent to Style function must be an instance of a Style or a style configuration object')); } let borderEdges = {}; if (thisStyle.border && thisStyle.border.outline) { borderEdges.left = this.firstCol; borderEdges.right = this.lastCol; borderEdges.top = this.firstRow; borderEdges.bottom = this.lastRow; } this.cells.forEach((c) => { if (thisStyle.border && thisStyle.border.outline) { let thisCellsBorder = {}; if (c.row === borderEdges.top && thisStyle.border.top) { thisCellsBorder.top = thisStyle.border.top; } if (c.row === borderEdges.bottom && thisStyle.border.bottom) { thisCellsBorder.bottom = thisStyle.border.bottom; } if (c.col === borderEdges.left && thisStyle.border.left) { thisCellsBorder.left = thisStyle.border.left; } if (c.col === borderEdges.right && thisStyle.border.right) { thisCellsBorder.right = thisStyle.border.right; } thisStyle.border = thisCellsBorder; } if (c.s === 0) { let thisCellStyle = this.ws.wb.createStyle(thisStyle); c.style(thisCellStyle.ids.cellXfs); } else { let curStyle = this.ws.wb.styles[c.s]; let newStyleOpts = deepmerge(curStyle.toObject(), thisStyle); let mergedStyle = this.ws.wb.createStyle(newStyleOpts); c.style(mergedStyle.ids.cellXfs); } }); return this; } function hyperlinkSetter(url, displayStr, tooltip) { this.excelRefs.forEach((ref) => { displayStr = typeof displayStr === 'string' ? displayStr : url; this.ws.hyperlinkCollection.add({ location: url, display: displayStr, tooltip: tooltip, ref: ref }); }); stringSetter.bind(this)(displayStr); return styleSetter.bind(this)({ font: { color: 'Blue', underline: true } }); } function commentSetter(comment, options) { if (this.merged !== true) { this.cells.forEach((c, i) => { this.ws.comments[c.r] = new Comment(c.r, comment, options) }); } else { var c = this.cells[0]; this.ws.comments[c.r] = new Comment(c.r, comment, options) } return this; } function mergeCells(cellBlock) { let excelRefs = cellBlock.excelRefs; if (excelRefs instanceof Array && excelRefs.length > 0) { excelRefs.sort(utils.sortCellRefs); let cellRange = excelRefs[0] + ':' + excelRefs[excelRefs.length - 1]; let rangeCells = excelRefs; let okToMerge = true; cellBlock.ws.mergedCells.forEach((cr) => { // Check to see if currently merged cells contain cells in new merge request let curCells = utils.getAllCellsInExcelRange(cr); let intersection = utils.arrayIntersectSafe(rangeCells, curCells); if (intersection.length > 0) { okToMerge = false; cellBlock.ws.wb.logger.error(`Invalid Range for: ${cellRange}. Some cells in this range are already included in another merged cell range: ${cr}.`); } }); if (okToMerge) { cellBlock.ws.mergedCells.push(cellRange); } } else { throw new TypeError(util.format('excelRefs variable sent to mergeCells function must be an array with length > 0')); } } /** * @class cellBlock */ class cellBlock { constructor() { this.ws; this.cells = []; this.excelRefs = []; this.merged = false; } get matrix() { let matrix = []; let tmpObj = {}; this.cells.forEach((c) => { if (!tmpObj[c.row]) { tmpObj[c.row] = []; } tmpObj[c.row].push(c); }); let rows = Object.keys(tmpObj); rows.forEach((r) => { tmpObj[r].sort((a, b) => { return a.col - b.col; }); matrix.push(tmpObj[r]); }); return matrix; } get firstRow() { let firstRow; this.cells.forEach((c) => { if (c.row < firstRow || firstRow === undefined) { firstRow = c.row; } }); return firstRow; } get lastRow() { let lastRow; this.cells.forEach((c) => { if (c.row > lastRow || lastRow === undefined) { lastRow = c.row; } }); return lastRow; } get firstCol() { let firstCol; this.cells.forEach((c) => { if (c.col < firstCol || firstCol === undefined) { firstCol = c.col; } }); return firstCol; } get lastCol() { let lastCol; this.cells.forEach((c) => { if (c.col > lastCol || lastCol === undefined) { lastCol = c.col; } }); return lastCol; } } /** * Module repesenting a Cell Accessor * @alias Worksheet.cell * @namespace * @func Worksheet.cell * @desc Access a range of cells in order to manipulate values * @param {Number} row1 Row of top left cell * @param {Number} col1 Column of top left cell * @param {Number} row2 Row of bottom right cell (optional) * @param {Number} col2 Column of bottom right cell (optional) * @param {Boolean} isMerged Merged the cell range into a single cell * @returns {cellBlock} */ function cellAccessor(row1, col1, row2, col2, isMerged) { let theseCells = new cellBlock(); theseCells.ws = this; row2 = row2 ? row2 : row1; col2 = col2 ? col2 : col1; if (row2 > this.lastUsedRow) { this.lastUsedRow = row2; } if (col2 > this.lastUsedCol) { this.lastUsedCol = col2; } for (let r = row1; r <= row2; r++) { for (let c = col1; c <= col2; c++) { let ref = `${utils.getExcelAlpha(c)}${r}`; if (!this.cells[ref]) { this.cells[ref] = new Cell(r, c); } if (!this.rows[r]) { this.rows[r] = new Row(r, this); } if (this.rows[r].cellRefs.indexOf(ref) < 0) { this.rows[r].cellRefs.push(ref); } theseCells.cells.push(this.cells[ref]); theseCells.excelRefs.push(ref); } } if (isMerged) { theseCells.merged = true; mergeCells(theseCells); } return theseCells; } /** * @alias cellBlock.string * @func cellBlock.string * @param {String} val Value of String * @returns {cellBlock} Block of cells with attached methods */ cellBlock.prototype.string = function (val) { if (val instanceof Array) { return complexStringSetter.bind(this)(val); } else { return stringSetter.bind(this)(val); } }; /** * @alias cellBlock.style * @func cellBlock.style * @param {Object} style One of a Style instance or an object with Style parameters * @returns {cellBlock} Block of cells with attached methods */ cellBlock.prototype.style = styleSetter; /** * @alias cellBlock.number * @func cellBlock.number * @param {Number} val Value of Number * @returns {cellBlock} Block of cells with attached methods */ cellBlock.prototype.number = numberSetter; /** * @alias cellBlock.bool * @func cellBlock.bool * @param {Boolean} val Value of Boolean * @returns {cellBlock} Block of cells with attached methods */ cellBlock.prototype.bool = booleanSetter; /** * @alias cellBlock.formula * @func cellBlock.formula * @param {String} val Excel style formula as string * @returns {cellBlock} Block of cells with attached methods */ cellBlock.prototype.formula = formulaSetter; /** * @alias cellBlock.date * @func cellBlock.date * @param {Date} val Value of Date * @returns {cellBlock} Block of cells with attached methods */ cellBlock.prototype.date = dateSetter; /** * @alias cellBlock.link * @func cellBlock.link * @param {String} url Value of Hyperlink URL * @param {String} displayStr Value of String representation of URL * @param {String} tooltip Value of text to display as hover * @returns {cellBlock} Block of cells with attached methods */ cellBlock.prototype.link = hyperlinkSetter; cellBlock.prototype.comment = commentSetter; module.exports = cellAccessor;