/* WorksheetView.js * * Author: Dmitry.Sokolov@avsmedia.net * Date: Nov 21, 2011 */ ( /** * @param {jQuery} $ * @param {Window} window * @param {undefined} undefined */ function ($, window, undefined) { /* * Import * ----------------------------------------------------------------------------- */ var asc = window["Asc"]; var asc_calcnpt = asc.calcNearestPt; var asc_getcvt = asc.getCvtRatio; var asc_getprop = asc.getProperty; var asc_floor = asc.floor; var asc_ceil = asc.ceil; var asc_round = asc.round; var asc_n2css = asc.numberToCSSColor; var asc_n2Color = asc.numberToAscColor; var asc_obj2Color = asc.colorObjToAscColor; var asc_typeof = asc.typeOf; var asc_incDecFonSize = asc.incDecFonSize; var asc_debug = asc.outputDebugStr; var asc_Range = asc.Range; var asc_FP = asc.FontProperties; var asc_parsecolor = asc.parseColor; var asc_clone = asc.clone; var asc_AF = asc.AutoFilters; var asc_CCellFlag = asc.asc_CCellFlag; var asc_CFont = asc.asc_CFont; var asc_CFill = asc.asc_CFill; var asc_CBorder = asc.asc_CBorder; var asc_CBorders = asc.asc_CBorders; var asc_CCellInfo = asc.asc_CCellInfo; var asc_CCellRect = asc.asc_CCellRect; var asc_CHyperlink = asc.asc_CHyperlink; var asc_CPageOptions = asc.asc_CPageOptions; var asc_CPageSetup = asc.asc_CPageSetup; var asc_CPageMargins = asc.asc_CPageMargins; var asc_CPagePrint = asc.CPagePrint; var asc_CCollaborativeRange = asc.asc_CCollaborativeRange; var asc_CCellCommentator = asc.asc_CCellCommentator; /* * Constants * ----------------------------------------------------------------------------- */ /** * header styles * @const */ var kHeaderDefault = 0; var kHeaderActive = 1; var kHeaderHighlighted = 2; var kHeaderSelected = 3; /** * text alignment style * @const */ var khaLeft = "left"; var khaCenter = "center"; var khaRight = "right"; var khaJustify= "justify"; var kvaTop = "top"; var kvaCenter = "center"; var kvaBottom = "bottom"; /** * cell border styles * @const */ var kcbNone = "none"; var kcbThick = "thick"; var kcbThin = "thin"; var kcbMedium = "medium"; var kcbDashDot = "dashDot"; var kcbDashDotDot = "dashDotDot"; var kcbDashed = "dashed"; var kcbDotted = "dotted"; var kcbDouble = "double"; var kcbHair = "hair"; var kcbMediumDashDot = "mediumDashDot"; var kcbMediumDashDotDot = "mediumDashDotDot"; var kcbMediumDashed = "mediumDashed"; var kcbSlantDashDot = "slantDashDot"; var kcbThinBorders = [kcbThin, kcbDashDot, kcbDashDotDot, kcbDashed, kcbDotted, kcbHair]; var kcbMediumBorders = [kcbMedium, kcbMediumDashDot, kcbMediumDashDotDot, kcbMediumDashed, kcbSlantDashDot]; var kcbThickBorders = [kcbThick, kcbDouble]; /** * cursor styles * @const */ var kCurDefault = "default"; var kCurCorner = "pointer"; var kCurCells = "cell"; var kCurColSelect = "pointer"; var kCurColResize = "col-resize"; var kCurRowSelect = "pointer"; var kCurRowResize = "row-resize"; // Курсор для автозаполнения var kCurFillHandle = "crosshair"; // Курсор для гиперссылки var kCurHyperlink = "pointer"; // Курсор для комментария var kCurComment = "cell"; // Курсор для перемещения области выделения var kCurMove = "move"; var kCurSEResize = "se-resize"; var kCurNEResize = "ne-resize"; /** * cell border id * @const */ var kcbidLeft = 1; var kcbidRight = 2; var kcbidTop = 3; var kcbidBottom = 4; var kcbidDiagonal = 5; var kcbidDiagonalDown = 6; var kcbidDiagonalUp = 7; var kNewLine = "\n"; function calcDecades(num) { return Math.abs(num) < 10 ? 1 : 1 + calcDecades( asc_floor(num * 0.1) ); } function CacheElement() { if ( !(this instanceof CacheElement) ) { return new CacheElement(); } this.columnsWithText = {}; // Колонки, в которых есть текст this.columns = {}; this.erasedRB = {}; this.erasedLB = {}; return this; } /** * @param {String} style * @param {Number} color * @param {Number} width * @param {Boolean} isErased * @param {Boolean} isActive */ function CellBorder(style, color, width, isErased, isActive) { if ( !(this instanceof CellBorder) ) { return new CellBorder(style, color, width, isErased, isActive); } /** @type {String} */ this.s = style !== undefined ? style : kcbNone; /** @type {Number} */ this.c = color !== undefined ? color.getRgb() : 0; /** @type {Number} */ this.w = width !== undefined ? width : 0; /** @type {Boolean} */ this.isErased = isErased !== undefined ? isErased : false; /** @type {Boolean} */ this.isActive = isActive !== undefined ? isActive : true; return this; } function WorksheetViewSettings() { if ( !(this instanceof WorksheetViewSettings) ) { return new WorksheetViewSettings(); } this.header = { fontName: "Calibri", fontSize: 11, style: [ // Old header colors /*{ // kHeaderDefault background: "#DFE3E8", border: "#B1B5BA", color: "#363636" }, { // kHeaderActive background: "#FFDD62", border: "#C28A30", color: "#363636" }, { // kHeaderHighlighted background: "#FFEDA9", border: "#E8BF3A", color: "#656A70" }, { // kHeaderSelected background: "#AAAAAA", border: "#75777A", color: "#363636" }*/ // New header colors { // kHeaderDefault background: "#F4F4F4", border: "#D5D5D5", color: "#363636" }, { // kHeaderActive background: "#C1C1C1", border: "#929292", color: "#363636" }, { // kHeaderHighlighted background: "#DFDFDF", border: "#AFAFAF", color: "#656A70" }, { // kHeaderSelected background: "#AAAAAA", border: "#75777A", color: "#363636" } ], cornerColor: "#C1C1C1" }; this.cells = { fontName: "Calibri", fontSize: 11, defaultState: { background: "#FFF", border: "#DADCDD", color: "#000", colorNumber : 0 }, padding: 2/*px horizontal padding*/ }; this.activeCellBorderColor = "rgba(105,119,62,0.7)"; this.activeCellBackground = "rgba(157,185,85,.2)"; //this.activeCellBorderColor = "rgba(0,0,255,.5)"; //this.activeCellBackground = "rgba(190,190,255,.5)"; // this.formulaRangeBorderColor = "rgba(0,0,255,1)"; this.formulaRangeBorderColor = [ "rgba(0,53,214,1)", "rgba(216,0,0,1)", "rgba(214,160,0,1)", "rgba(107,214,0,1)", "rgba(0,214,53,1)", "rgba(0,214,214,1)", "rgba(107,0,214,1)", "rgba(214,0,160,1)" ]; // Цвет заливки границы выделения области автозаполнения this.fillHandleBorderColorSelect = "rgba(255,255,255,1)"; //this.fillHandleBorderColorSelect = "rgba(255,255,0,1)"; return this; } function Cache() { if ( !(this instanceof Cache) ) { return new Cache(); } this.rows = {}; this.mergedCells = { index: {}, ranges: [] }; this.sectors = []; this.reset = function () { this.rows = {}; this.mergedCells.index = {}; this.mergedCells.ranges = []; this.sectors = []; }; // Structure of cache // // cache : { // // rows : { // 0 : { // columns : { // 0 : { // borders : { // b : CellBorder, // l : CellBorder, // r : CellBorder, // t : CellBorder, // dd : CellBorder, // du : CellBorder // }, // text : { // cellHA : String, // cellVA : String, // cellW : Number, // color : String, // metrics : TextMetrics, // sideL : Number, // sideR : Number, // state : StringRenderInternalState // } // } // }, // erasedLB : { // 1 : true, 2 : true // }, // erasedRB : { // 0 : true, 1 : true // } // } // }, // // mergedCells : { // index : { // "row-col" : Number // }, // ranges: [ // { // c1 : Number, // r1 : Number, // c2 : Number, // r2 : Number // } // ] // }, // // sectors: [ // 0 : Range // ] // // } } /** * Widget for displaying and editing Worksheet object * ----------------------------------------------------------------------------- * @param {Worksheet} model Worksheet * @param {Array} buffers DrawingContext + Overlay * @param {StringRender} stringRender StringRender * @param {Number} maxDigitWidth Максимальный размер цифры * @param {asc_CCollaborativeEditing} collaborativeEditing * @param {Object} settings Settings * * @constructor * @memberOf Asc */ function WorksheetView(model, buffers, stringRender, maxDigitWidth, collaborativeEditing, settings) { if ( !(this instanceof WorksheetView) ) { return new WorksheetView(model, buffers, stringRender, maxDigitWidth, collaborativeEditing, settings); } this.settings = $.extend(true, {}, this.defaults, settings); var cells = this.settings.cells; cells.fontName = model.workbook.getDefaultFont(); cells.fontSize = model.workbook.getDefaultSize(); this.vspRatio = 1.275; this.model = model; this.buffers = buffers; this.drawingCtx = this.buffers.main; this.overlayCtx = this.buffers.overlay; this.shapeCtx = this.buffers.shapeCtx; this.shapeOverlayCtx = this.buffers.shapeOverlayCtx; this.stringRender = stringRender; // Флаг, сигнализирует о том, что мы сменили zoom, но это не активный лист (поэтому как только будем показывать, нужно перерисовать и пересчитать кеш) this.updateZoom = false; var cnv = $('<canvas width="2" height="2"/>')[0]; var ctx = cnv.getContext("2d"); ctx.clearRect(0, 0, 2, 2); ctx.fillStyle = "#000"; ctx.fillRect(0, 0, 1, 1); ctx.fillRect(1, 1, 1, 1); this.ptrnLineDotted1 = ctx.createPattern(cnv, "repeat"); this.cache = new Cache(); //---member declaration--- // Максимальная ширина числа из 0,1,2...,9, померенная в нормальном шрифте(дефалтовый для книги) в px(целое) // Ecma-376 Office Open XML Part 1, пункт 18.3.1.13 this.maxDigitWidth = maxDigitWidth; this.nBaseColWidth = 8; // Число символов для дефалтовой ширины (по умолчинию 8) this.defaultColWidthChars = 0; this.defaultColWidth = 0; this.defaultRowHeight = 0; this.defaultRowDescender = 0; this.headersLeft = 0; this.headersTop = 0; this.headersWidth = 0; this.headersHeight = 0; this.cellsLeft = 0; this.cellsTop = 0; this.cols = []; this.rows = []; this.width_1px = 0; this.width_2px = 0; this.width_3px = 0; this.width_padding = 0; this.height_1px = 0; this.height_2px = 0; this.height_3px = 0; this.highlightedCol = -1; this.highlightedRow = -1; this.visibleRange = asc_Range(0, 0, 0, 0); this.activeRange = asc_Range(0, 0, 0, 0); this.activeRange.type = c_oAscSelectionType.RangeCells; this.activeRange.startCol = 0; // Активная ячейка в выделении this.activeRange.startRow = 0; // Активная ячейка в выделении this.isChanged = false; this.isCellEditMode = false; this.isFormulaEditMode = false; this.isChartAreaEditMode = false; this.lockDraw = false; this.isUpdateSelection = false; this.isSelectionDialogMode = false; this.copyOfActiveRange = null; this.startCellMoveResizeRange = null; this.startCellMoveResizeRange2 = null; // Координаты ячейки начала перемещения диапазона this.startCellMoveRange = null; // Дипазон перемещения this.activeMoveRange = null; // Координаты fillHandle ("квадрата" автозаполнения) this.fillHandleL = 0; this.fillHandleT = 0; this.fillHandleR = 0; this.fillHandleB = 0; // Range fillHandle this.activeFillHandle = null; // Горизонтальное (0) или вертикальное (1) направление автозаполнения this.fillHandleDirection = -1; // Зона автозаполнения this.fillHandleArea = -1; this.nRowsCount = 0; this.nColsCount = 0; // Массив ячеек для текущей формулы this.arrActiveFormulaRanges = []; this.arrActiveChartsRanges = []; // Массив видимых hyperlink-ов this.visibleHyperlinks = []; //------------------------ this.collaborativeEditing = collaborativeEditing; // Auto filters this.autoFilters = new asc_AF(); this.cellCommentator = new asc_CCellCommentator(this); this._init(); return this; } WorksheetView.prototype = { /** @type WorksheetView */ constructor: WorksheetView, defaults: WorksheetViewSettings(), option: function (name, value) { var old = asc_getprop(name, this.settings); if (name !== undefined && value !== undefined) { var i = name.lastIndexOf("."); if (i < 0) { this.settings[name] = value; } else { var p = asc_getprop(name.slice(0, i), this.settings); if (p === undefined) {return false;} p[ name.slice(i + 1) ] = value; } return true; } return old; }, getVisibleRange: function () { return this.visibleRange; }, updateVisibleRange: function () { return this._updateCellsRange(this.getVisibleRange()); }, getFirstVisibleCol: function () { return this.visibleRange.c1; }, getLastVisibleCol: function () { return this.visibleRange.c2; }, getFirstVisibleRow: function () { return this.visibleRange.r1; }, getLastVisibleRow: function () { return this.visibleRange.r2; }, getHorizontalScrollRange: function () { var ctxW = this.drawingCtx.getWidth() - this.cellsLeft; for (var w = 0, i = this.cols.length - 1; i >= 0; --i) { w += this.cols[i].width; if (w > ctxW) {break;} } return i; // Диапазон скрола должен быть меньше количества столбцов, чтобы не было прибавления столбцов при перетаскивании бегунка }, getVerticalScrollRange: function () { var ctxH = this.drawingCtx.getHeight() - this.cellsTop; for (var h = 0, i = this.rows.length - 1; i >= 0; --i) { h += this.rows[i].height; if (h > ctxH) {break;} } return i; // Диапазон скрола должен быть меньше количества строк, чтобы не было прибавления строк при перетаскивании бегунка }, getCellsOffset: function (units) { var u = units >= 0 && units <= 3 ? units : 0; return { left: this.cellsLeft * asc_getcvt( 1/*pt*/, u, this._getPPIX() ), top: this.cellsTop * asc_getcvt( 1/*pt*/, u, this._getPPIY() ) }; }, getCellLeft: function (column, units) { if (column >= 0 && column < this.cols.length) { var u = units >= 0 && units <= 3 ? units : 0; return this.cols[column].left * asc_getcvt( 1/*pt*/, u, this._getPPIX() ); } return null; }, getCellTop: function (row, units) { if (row >= 0 && row < this.rows.length) { var u = units >= 0 && units <= 3 ? units : 0; return this.rows[row].top * asc_getcvt( 1/*pt*/, u, this._getPPIY() ); } return null; }, getColumnWidth: function (index, units) { if (index >= 0 && index < this.cols.length) { var u = units >= 0 && units <= 3 ? units : 0; return this.cols[index].width * asc_getcvt( 1/*pt*/, u, this._getPPIX() ); } return null; }, getColumnWidthInSymbols: function (index) { if (index >= 0 && index < this.cols.length) { return this.cols[index].charCount; } return null; }, getRowHeight: function (index, units) { if (index >= 0 && index < this.rows.length) { var u = units >= 0 && units <= 3 ? units : 0; return this.rows[index].height * asc_getcvt( 1/*pt*/, u, this._getPPIY() ); } return null; }, getSelectedColumnIndex: function () { return this.activeRange.startCol; }, getSelectedRowIndex: function () { return this.activeRange.startRow; }, getSelectedRange: function () { return this._getRange(this.activeRange.c1, this.activeRange.r1, this.activeRange.c2, this.activeRange.r2); }, resize: function () { this._initCellsArea(true); this._normalizeViewRange(); this._cleanCellsTextMetricsCache(); this._prepareCellTextMetricsCache(this.visibleRange); return this; }, getZoom: function () { return this.drawingCtx.getZoom(); }, changeZoom: function (isUpdate) { if (isUpdate) { this.cleanSelection(); this._initConstValues(); this._initCellsArea(false); this._normalizeViewRange(); this._cleanCellsTextMetricsCache(); this._shiftVisibleRange(); this._prepareCellTextMetricsCache(this.visibleRange); this._shiftVisibleRange(); this.cellCommentator.updateCommentPosition(); this.updateZoom = false; } else { this.updateZoom = true; } return this; }, getCellTextMetrics: function (col, row) { var ct = this._getCellTextCache(col, row); return ct ? $.extend({}, ct.metrics) : undefined; }, getSheetViewSettings: function () { return this.model.getSheetViewSettings(); }, // mouseX - это разница стартовых координат от мыши при нажатии и границы changeColumnWidth: function (col, x2, mouseX) { var t = this; x2 *= asc_getcvt( 0/*px*/, 1/*pt*/, t._getPPIX() ); // Учитываем координаты точки, где мы начали изменение размера x2 += mouseX; var offsetX = t.cols[t.visibleRange.c1].left - t.cellsLeft; var x1 = t.cols[col].left - offsetX - this.width_1px; var w = Math.max(x2 - x1 - this.width_1px, 0); var cc = Math.min(t._colWidthToCharCount(w), /*max col width*/255); var cw = t._charCountToModelColWidth(cc, true, /*noPad*/true); var onChangeWidthCallback = function (isSuccess) { if (false === isSuccess) return; t.model.setColWidth(cw, col, col); t._cleanCache(asc_Range(0, 0, t.cols.length - 1, t.rows.length - 1)); t.changeWorksheet("update"); t._updateVisibleColsCount(); }; return this._isLockedAll (onChangeWidthCallback); }, // mouseY - это разница стартовых координат от мыши при нажатии и границы changeRowHeight: function (row, y2, mouseY) { var t = this; y2 *= asc_getcvt( 0/*px*/, 1/*pt*/, t._getPPIY() ); // Учитываем координаты точки, где мы начали изменение размера y2 += mouseY; var offsetY = t.rows[t.visibleRange.r1].top - t.cellsTop; var y1 = t.rows[row].top - offsetY - this.height_1px; var onChangeHeightCallback = function (isSuccess) { if (false === isSuccess) return; t.model.setRowHeight(Math.min(t.maxRowHeight, Math.max(y2 - y1 + t.height_1px, 0)), row, row); t._cleanCache(asc_Range(0, row, t.cols.length - 1, row)); t.changeWorksheet("update"); t._updateVisibleRowsCount(); }; return this._isLockedAll (onChangeHeightCallback); }, // Проверяет, есть ли числовые значения в диапазоне _hasNumberValueInActiveRange: function() { var cell, cellType, isNumberFormat; var mergedRange = this._getMergedCellsRange(this.activeRange.c1, this.activeRange.r1); var result = null; if (this._rangeIsSingleCell(this.activeRange) || mergedRange && mergedRange.isEqual(this.activeRange)) { // Для одной ячейки не стоит ничего делать return result; } for (var c = this.activeRange.c1; c <= this.activeRange.c2; ++c) { for (var r = this.activeRange.r1; r <= this.activeRange.r2; ++r) { cell = this._getCellTextCache(c, r); if (cell) { // Нашли не пустую ячейку, проверим формат cellType = cell.cellType; isNumberFormat = (null == cellType || CellValueType.Number === cellType); if (isNumberFormat) { if (null === result) { result = {}; result.arrCols = []; result.arrRows = []; } result.arrCols.push(c); result.arrRows.push(r); } } } } if (null !== result) { function cmpNum(a, b) {return a - b;} // Делаем массивы уникальными и сортируем $.unique(result.arrCols); $.unique(result.arrRows); result.arrCols = result.arrCols.sort(cmpNum); result.arrRows = result.arrRows.sort(cmpNum); } return result; }, // Автодополняет формулу диапазоном, если это возможно autoCompletFormula: function (functionName) { var t = this; this.activeRange.normalize(); var ar = this.activeRange; var arCopy = null; var arHistorySelect = ar.clone(true); var vr = this.visibleRange; // Первая верхняя не числовая ячейка var topCell = null; // Первая левая не числовая ячейка var leftCell = null; var r = ar.startRow - 1; var c = ar.startCol - 1; var cell, cellType, isNumberFormat; var result = {}; // Проверим, есть ли числовые значения в диапазоне var hasNumber = this._hasNumberValueInActiveRange(); var val, text; if (hasNumber) { var i; // Есть ли значения в последней строке и столбце var hasNumberInLastColumn = (ar.c2 === hasNumber.arrCols[hasNumber.arrCols.length - 1]); var hasNumberInLastRow = (ar.r2 === hasNumber.arrRows[hasNumber.arrRows.length - 1]); // Нужно уменьшить зону выделения (если она реально уменьшилась) var startCol = hasNumber.arrCols[0]; var startRow = hasNumber.arrRows[0]; // Старые границы диапазона var startColOld = ar.c1; var startRowOld = ar.r1; // Нужно ли перерисовывать var bIsUpdate = false; if (startColOld !== startCol || startRowOld !== startRow) { bIsUpdate = true; } if (true === hasNumberInLastRow && true === hasNumberInLastColumn) { bIsUpdate = true; } if (bIsUpdate) { this.cleanSelection(); ar.c1 = startCol; ar.r1 = startRow; if (false === ar.contains(ar.startCol, ar.startRow)) { // Передвинуть первую ячейку в выделении ar.startCol = startCol; ar.startRow = startRow; } if (true === hasNumberInLastRow && true === hasNumberInLastColumn) { // Мы расширяем диапазон if (1 === hasNumber.arrRows.length) { // Одна строка или только в последней строке есть значения... (увеличиваем вправо) ar.c2 += 1; } else { // Иначе вводим в строку вниз ar.r2 += 1; } } this._drawSelection(); } arCopy = ar.clone(true); var functionAction = null; var changedRange = null; if (false === hasNumberInLastColumn && false === hasNumberInLastRow) { // Значений нет ни в последней строке ни в последнем столбце (значит нужно сделать формулы в каждой последней ячейке) changedRange = [new asc_Range(hasNumber.arrCols[0], arCopy.r2, hasNumber.arrCols[hasNumber.arrCols.length - 1], arCopy.r2), new asc_Range(arCopy.c2, hasNumber.arrRows[0], arCopy.c2, hasNumber.arrRows[hasNumber.arrRows.length - 1])]; functionAction = function () { // Пройдемся по последней строке for (i = 0; i < hasNumber.arrCols.length; ++i) { c = hasNumber.arrCols[i]; cell = t._getVisibleCell(c, arCopy.r2); text = t._getCellTitle (c, arCopy.r1) + ":" + t._getCellTitle (c, arCopy.r2 - 1); val = "=" + functionName + "(" + text + ")"; // ToDo - при вводе формулы в заголовок автофильтра надо писать "0" cell.setValue(val); } // Пройдемся по последнему столбцу for (i = 0; i < hasNumber.arrRows.length; ++i) { r = hasNumber.arrRows[i]; cell = t._getVisibleCell(arCopy.c2, r); text = t._getCellTitle (arCopy.c1, r) + ":" + t._getCellTitle (arCopy.c2 - 1, r); val = "=" + functionName + "(" + text + ")"; cell.setValue(val); } // Значение в правой нижней ячейке cell = t._getVisibleCell(arCopy.c2, arCopy.r2); text = t._getCellTitle (arCopy.c1, arCopy.r2) + ":" + t._getCellTitle (arCopy.c2 - 1, arCopy.r2); val = "=" + functionName + "(" + text + ")"; cell.setValue(val); }; } else if (true === hasNumberInLastRow && false === hasNumberInLastColumn) { // Есть значения только в последней строке (значит нужно заполнить только последнюю колонку) changedRange = new asc_Range(arCopy.c2, hasNumber.arrRows[0], arCopy.c2, hasNumber.arrRows[hasNumber.arrRows.length - 1]); functionAction = function () { // Пройдемся по последнему столбцу for (i = 0; i < hasNumber.arrRows.length; ++i) { r = hasNumber.arrRows[i]; cell = t._getVisibleCell(arCopy.c2, r); text = t._getCellTitle (arCopy.c1, r) + ":" + t._getCellTitle (arCopy.c2 - 1, r); val = "=" + functionName + "(" + text + ")"; cell.setValue(val); } }; } else if (false === hasNumberInLastRow && true === hasNumberInLastColumn) { // Есть значения только в последнем столбце (значит нужно заполнить только последнюю строчку) changedRange = new asc_Range(hasNumber.arrCols[0], arCopy.r2, hasNumber.arrCols[hasNumber.arrCols.length - 1], arCopy.r2); functionAction = function () { // Пройдемся по последней строке for (i = 0; i < hasNumber.arrCols.length; ++i) { c = hasNumber.arrCols[i]; cell = t._getVisibleCell(c, arCopy.r2); text = t._getCellTitle (c, arCopy.r1) + ":" + t._getCellTitle (c, arCopy.r2 - 1); val = "=" + functionName + "(" + text + ")"; cell.setValue(val); } }; } else { // Есть значения и в последнем столбце, и в последней строке if (1 === hasNumber.arrRows.length) { changedRange = new asc_Range(arCopy.c2, arCopy.r2, arCopy.c2, arCopy.r2); functionAction = function () { // Одна строка или только в последней строке есть значения... cell = t._getVisibleCell(arCopy.c2, arCopy.r2); // ToDo вводить в первое свободное место, а не сразу за диапазоном text = t._getCellTitle (arCopy.c1, arCopy.r2) + ":" + t._getCellTitle (arCopy.c2 - 1, arCopy.r2); val = "=" + functionName + "(" + text + ")"; cell.setValue(val); }; } else { changedRange = new asc_Range(hasNumber.arrCols[0], arCopy.r2, hasNumber.arrCols[hasNumber.arrCols.length - 1], arCopy.r2); functionAction = function () { // Иначе вводим в строку вниз for (i = 0; i < hasNumber.arrCols.length; ++i) { c = hasNumber.arrCols[i]; cell = t._getVisibleCell(c, arCopy.r2); // ToDo вводить в первое свободное место, а не сразу за диапазоном text = t._getCellTitle (c, arCopy.r1) + ":" + t._getCellTitle (c, arCopy.r2 - 1); val = "=" + functionName + "(" + text + ")"; cell.setValue(val); } }; } } var onAutoCompletFormula = function (isSuccess) { if (false === isSuccess) return; History.Create_NewPoint(); History.SetSelection(arHistorySelect); History.StartTransaction(); if ($.isFunction(functionAction)) {functionAction();} History.EndTransaction(); }; // Можно ли применять автоформулу this._isLockedCells (changedRange, /*subType*/null, onAutoCompletFormula); result.notEditCell = true; return result; } // Ищем первую ячейку с числом for (; r >= vr.r1; --r) { cell = this._getCellTextCache(ar.startCol, r); if (cell) { // Нашли не пустую ячейку, проверим формат cellType = cell.cellType; isNumberFormat = (null === cellType || CellValueType.Number === cellType); if (isNumberFormat) { // Это число, мы нашли то, что искали topCell = asc_clone(cell); topCell.r = r; topCell.c = ar.startCol; // смотрим вторую ячейку if (topCell.isFormula && r-1 >= vr.r1) { cell = this._getCellTextCache(ar.startCol, r-1); if (cell && cell.isFormula) { topCell.isFormulaSeq = true; } } break; } } } // Проверим, первой все равно должна быть колонка if (null === topCell || topCell.r !== ar.startRow - 1 || topCell.isFormula && !topCell.isFormulaSeq) { for (; c >= vr.c1; --c) { cell = this._getCellTextCache(c, ar.startRow); if (cell) { // Нашли не пустую ячейку, проверим формат cellType = cell.cellType; isNumberFormat = (null === cellType || CellValueType.Number === cellType); if (isNumberFormat) { // Это число, мы нашли то, что искали leftCell = asc_clone(cell); leftCell.r = ar.startRow; leftCell.c = c; break; } } if (null !== topCell) { // Если это не первая ячейка слева от текущей и мы нашли верхнюю, то дальше не стоит искать break; } } } if (leftCell) { // Идем влево до первой не числовой ячейки --c; for (; c >= 0; --c) { cell = this._getCellTextCache(c, ar.startRow); if (!cell) { // Могут быть еще не закешированные данные this._addCellTextToCache (c, ar.startRow); cell = this._getCellTextCache (c, ar.startRow); if (!cell) break; } cellType = cell.cellType; isNumberFormat = (null === cellType || CellValueType.Number === cellType); if (!isNumberFormat) break; } // Мы ушли чуть дальше ++c; // Диапазон или только 1 ячейка if (ar.startCol - 1 !== c) { // Диапазон result = asc_Range(c, leftCell.r, ar.startCol - 1, leftCell.r); } else { // Одна ячейка result = asc_Range(c, leftCell.r, c, leftCell.r); } result.type = c_oAscSelectionType.RangeCells; this._fixSelectionOfMergedCells(result); result.normalize(); if (result.c1 === result.c2 && result.r1 === result.r2) result.text = this._getCellTitle (result.c1, result.r1); else result.text = this._getCellTitle (result.c1, result.r1) + ":" + this._getCellTitle (result.c2, result.r2); return result; } if (topCell) { // Идем вверх до первой не числовой ячейки --r; for (; r >= 0; --r) { cell = this._getCellTextCache(ar.startCol, r); if (!cell) { // Могут быть еще не закешированные данные this._addCellTextToCache (ar.startCol, r); cell = this._getCellTextCache (ar.startCol, r); if (!cell) break; } cellType = cell.cellType; isNumberFormat = (null === cellType || CellValueType.Number === cellType); if (!isNumberFormat) break; } // Мы ушли чуть дальше ++r; // Диапазон или только 1 ячейка if (ar.startRow - 1 !== r) { // Диапазон result = asc_Range(topCell.c, r, topCell.c, ar.startRow - 1); } else { // Одна ячейка result = asc_Range(topCell.c, r, topCell.c, r); } result.type = c_oAscSelectionType.RangeCells; this._fixSelectionOfMergedCells(result); result.normalize(); if (result.c1 === result.c2 && result.r1 === result.r2) result.text = this._getCellTitle(result.c1, result.r1); else result.text = this._getCellTitle(result.c1, result.r1) + ":" + this._getCellTitle(result.c2, result.r2); return result; } }, // ToDo переделать на полную отрисовку на нашем контексте getDrawingContextCharts: function () { return this._trigger("getDCForCharts"); }, // ----- Initialization ----- _init: function () { this._initConstValues(); this._initWorksheetDefaultWidth(); this._initCellsArea(true); this.autoFilters.addFiltersAfterOpen(this); this._initConditionalFormatting(); this._cleanCellsTextMetricsCache(); this._prepareCellTextMetricsCache(this.visibleRange); // initializing is completed this._trigger("initialized"); }, _initConditionalFormatting: function () { var oGradient = null; var aCFs = this.model.aConditionalFormatting; var aRules, oRule; var oRuleElement = null; var min = Number.MAX_VALUE; var max = -Number.MAX_VALUE; var tmp; var arrayCells = []; for (var i in aCFs) { if (!aCFs.hasOwnProperty(i) ) continue; aRules = aCFs[i].aRules; if (0 >= aRules.length) continue; for (var j in aRules) { if (!aRules.hasOwnProperty(j)) continue; oRule = aRules[j]; // ToDo aboveAverage, beginsWith, cellIs, containsBlanks, containsErrors, // ToDo containsText, dataBar, duplicateValues, endsWith, expression, iconSet, notContainsBlanks, // ToDo notContainsErrors, notContainsText, timePeriod, top10, uniqueValues (page 2679) switch (oRule.Type) { case "colorScale": if (1 !== oRule.aRuleElements.length) break; oRuleElement = oRule.aRuleElements[0]; if (!(oRuleElement instanceof asc.CColorScale)) break; aCFs[i].SqRefRange._setPropertyNoEmpty(null, null, function (c) { if (CellValueType.Number === c.getType() && false === c.isEmptyTextString()) { tmp = parseFloat(c.getValueWithoutFormat()); if (isNaN(tmp)) return; arrayCells.push({cell: c, val: tmp}); min = Math.min(min, tmp); max = Math.max(max, tmp); } }); // ToDo CFVO Type (formula, max, min, num, percent, percentile) (page 2681) // ToDo support 3 colors in rule if (0 < arrayCells.length && 2 === oRuleElement.aColors.length) { oGradient = new asc.CGradient(oRuleElement.aColors[0], oRuleElement.aColors[1]); oGradient.init(min, max); for (var cell in arrayCells) { if (arrayCells.hasOwnProperty(cell)) { var dxf = new CellXfs(); dxf.fill = new Fill({bg:oGradient.calculateColor(arrayCells[cell].val)}); arrayCells[cell].cell.setConditionalFormattingStyle(dxf); } } } arrayCells.splice(0, arrayCells.length); min = Number.MAX_VALUE; max = -Number.MAX_VALUE; break; } } } }, _prepareComments: function () { var commentList = []; // для отправки за один раз for(var i = 0, length = this.model.aComments.length; i < length; ++i) { var comment = this.model.aComments[i]; this.cellCommentator.addCommentSerialize(comment); commentList.push(comment); } if ( commentList.length ) this.model.workbook.handlers.trigger("asc_onAddComments", commentList); }, _prepareDrawingObjects: function () { if ( !this.settings.objectRender ) { this.objectRender = new DrawingObjects(); this.objectRender.init(this); } else this.objectRender = this.settings.objectRender; }, _initWorksheetDefaultWidth: function () { this.nBaseColWidth = this.model.nBaseColWidth || this.nBaseColWidth; // Теперь рассчитываем число px var defaultColWidthChars = this._charCountToModelColWidth(this.nBaseColWidth); this.defaultColWidthPx = this._modelColWidthToColWidth(defaultColWidthChars) * asc_getcvt(1/*pt*/, 0/*px*/, 96); // Делаем кратным 8 (http://support.microsoft.com/kb/214123) this.defaultColWidthPx = asc_ceil(this.defaultColWidthPx / 8) * 8; this.defaultColWidthChars = this._colWidthToCharCount(this.defaultColWidthPx * asc_getcvt(0/*px*/, 1/*pt*/, 96)); gc_dDefaultColWidthCharsAttribute = this._charCountToModelColWidth(this.defaultColWidthChars, true); this.defaultColWidth = this._modelColWidthToColWidth(gc_dDefaultColWidthCharsAttribute); // ToDo разобраться со значениями this.maxRowHeight = asc_calcnpt(409, this._getPPIY()); this.defaultRowDescender = this._calcRowDescender(this.settings.cells.fontSize); this.defaultRowHeight = asc_calcnpt(this.settings.cells.fontSize * this.vspRatio, this._getPPIY()) + this.height_1px; gc_dDefaultRowHeightAttribute = this.model.getDefaultHeight() || this.defaultRowHeight; }, _initConstValues: function () { var ppiX = this._getPPIX(); var ppiY = this._getPPIY(); this.width_1px = asc_calcnpt(0, ppiX, 1/*px*/); this.width_2px = asc_calcnpt(0, ppiX, 2/*px*/); this.width_3px = asc_calcnpt(0, ppiX, 3/*px*/); this.width_padding = asc_calcnpt(0, ppiX, this.settings.cells.padding/*px*/); this.height_1px = asc_calcnpt(0, ppiY, 1/*px*/); this.height_2px = asc_calcnpt(0, ppiY, 2/*px*/); this.height_3px = asc_calcnpt(0, ppiY, 3/*px*/); }, _initCellsArea: function (fullRecalc) { // calculate rows heights and visible rows this._calcHeaderRowHeight(); this._calcRowHeights(fullRecalc ? 1 : 0); this.visibleRange.r2 = 0; this._calcVisibleRows(); this._updateVisibleRowsCount(/*skipScrolReinit*/true); // calculate columns widths and visible columns this._calcHeaderColumnWidth(); this._calcColumnWidths(fullRecalc ? 1 : 0); this.visibleRange.c2 = 0; this._calcVisibleColumns(); this._updateVisibleColsCount(/*skipScrolReinit*/true); this._updateHyperlinksCache(); }, /** * Вычисляет ширину столбца для заданного количества символов * @param {Number} count Количество символов * @param {Boolean} displayWidth При расчете использовать целое число пикселов * @param {Boolean} noPad При расчете не учитывать отступ 5px в ячейке * @returns {Number} Ширина столбца в символах */ _charCountToModelColWidth: function (count, displayWidth, noPad) { if (count <= 0) { return 0; } var maxw = displayWidth ? asc_round(this.maxDigitWidth) : this.maxDigitWidth; return asc_floor( ( count * maxw + (!noPad ? 5 : 0) ) / maxw * 256 ) / 256; }, /** * Вычисляет ширину столбца в пунктах * @param {Number} mcw Количество символов * @param {Boolean} displayWidth При расчете использовать целое число пикселов * @returns {Number} Ширина столбца в пунктах (pt) */ _modelColWidthToColWidth: function (mcw, displayWidth) { var maxw = displayWidth ? asc_round(this.maxDigitWidth) : this.maxDigitWidth; var px = asc_floor( (( 256 * mcw + asc_floor(128 / maxw) ) / 256) * maxw ); return px * asc_getcvt( 0/*px*/, 1/*pt*/, 96 ); }, /** * Вычисляет количество символов по ширине столбца * @param {Number} w Ширина столбца в пунктах * @returns {Number} Количество символов */ _colWidthToCharCount: function (w) { var px = w * asc_getcvt( 1/*pt*/, 0/*px*/, 96 ); return px <= 5 ? 0 : asc_floor( (px - 5) / asc_round(this.maxDigitWidth) * 100 + 0.5 ) / 100; }, /** * Вычисляет ширину столбца для отрисовки * @param {Number} w Ширина столбца в символах * @returns {Number} Ширина столбца в пунктах (pt) */ _calcColWidth: function (w) { var t = this; var res = {}; var useDefault = w === undefined || w === null || w === -1; var width; res.width = useDefault ? t.defaultColWidth : (width = t._modelColWidthToColWidth(w), (width < t.width_1px ? 0 : width)); res.innerWidth = Math.max(res.width - this.width_padding * 2 - this.width_1px, 0); res.charCount = t._colWidthToCharCount(res.width); return res; }, /** * Вычисляет Descender строки * @param {Number} fontSize * @returns {Number} */ _calcRowDescender: function (fontSize) { return asc_calcnpt(fontSize * (this.vspRatio - 1), this._getPPIY()); }, /** Вычисляет ширину колонки заголовков (в pt) */ _calcHeaderColumnWidth: function () { if (false === this.model.sheetViews[0].asc_getShowRowColHeaders()) this.headersWidth = 0; else { // Ширина колонки заголовков считается - max число знаков в строке - перевести в символы - перевести в пикселы var numDigit = Math.max( calcDecades(this.visibleRange.r2 + 1), 3); var nCharCount = this._charCountToModelColWidth(numDigit); this.headersWidth = this._modelColWidthToColWidth(nCharCount); } //var w = this.emSize * Math.max( calcDecades(this.visibleRange.r2 + 1), 3) * 1.25; //this.headersWidth = asc_calcnpt(w, this._getPPIX()); this.cellsLeft = this.headersLeft + this.headersWidth; }, /** Вычисляет высоту строки заголовков (в pt) */ _calcHeaderRowHeight: function () { if (false === this.model.sheetViews[0].asc_getShowRowColHeaders()) this.headersHeight = 0; else this.headersHeight = this.model.getDefaultHeight() || this.defaultRowHeight; //this.headersHeight = asc_calcnpt( this.settings.header.fontSize * this.vspRatio, this._getPPIY() ); this.cellsTop = this.headersTop + this.headersHeight; }, /** * Вычисляет ширину и позицию колонок (в pt) * @param {Number} fullRecalc 0 - без пересчета; 1 - пересчитываем все; 2 - пересчитываем новые строки */ _calcColumnWidths: function (fullRecalc) { var x = this.cellsLeft; var visibleW = this.drawingCtx.getWidth(); var obr = this.objectRender ? this.objectRender.getDrawingAreaMetrics() : {maxCol: 0, maxRow: 0}; var l = Math.max(this.model.getColsCount(), this.nColsCount, obr.maxCol); var i = 0, w, column, isBestFit, hiddenW = 0; // Берем дефалтовую ширину документа var defaultWidth = this.model.getDefaultWidth(); defaultWidth = (typeof defaultWidth === "number" && defaultWidth >= 0) ? defaultWidth : -1; if (1 === fullRecalc) { this.cols = []; } else if (2 === fullRecalc) { i = this.cols.length; x = this.cols[i - 1].left + this.cols[i - 1].width; } for (; ((0 !== fullRecalc) ? i < l || x + hiddenW < visibleW : i < this.cols.length) && i <= gc_nMaxCol0; ++i) { // Получаем свойства колонки column = this.model._getColNoEmptyWithAll(i); if (!column) { w = defaultWidth; // Используем дефолтное значение isBestFit = true; // Это уже оптимальная ширина } else if (column.hd) { w = 0; // Если столбец скрытый, ширину выставляем 0 isBestFit = false; hiddenW += this._calcColWidth(column.width).width; } else { w = column.width || defaultWidth; isBestFit = !!(column.BestFit || (null === column.BestFit && null === column.CustomWidth)); } this.cols[i] = this._calcColWidth(w); this.cols[i].isCustomWidth = !isBestFit; this.cols[i].left = x; x += this.cols[i].width; } }, /** * Вычисляет высоту и позицию строк (в pt) * @param {Number} fullRecalc 0 - без пересчета; 1 - пересчитываем все; 2 - пересчитываем новые строки */ _calcRowHeights: function (fullRecalc) { var y = this.cellsTop; var visibleH = this.drawingCtx.getHeight(); var obr = this.objectRender ? this.objectRender.getDrawingAreaMetrics() : {maxCol: 0, maxRow: 0}; var l = Math.max(this.model.getRowsCount() , this.nRowsCount, obr.maxRow); var defaultH = this.model.getDefaultHeight() || this.defaultRowHeight; var i = 0, h, isCustomHeight, row, hiddenH = 0; if (1 === fullRecalc) { this.rows = []; } else if (2 === fullRecalc) { i = this.rows.length; y = this.rows[i - 1].top + this.rows[i - 1].height; } for (; (0 !== fullRecalc) ? i < l || y + hiddenH < visibleH : i < this.rows.length; ++i) { row = this.model._getRowNoEmpty(i); if (!row) { h = -1; // Будет использоваться дефолтная высота строки isCustomHeight = false; } else if (row.hd) { h = 0; // Скрытая строка, высоту выставляем 0 isCustomHeight = true; hiddenH += row.h > 0 ? row.h - this.height_1px : defaultH; } else { isCustomHeight = !!row.CustomHeight; h = (row.h > 0 && isCustomHeight) ? row.h : -1; // Берем высоту из модели, если она custom(баг 15618), либо дефолтную } h = h < 0 ? defaultH : h; this.rows[i] = { top: y, height: h, descender: this.defaultRowDescender, isCustomHeight: isCustomHeight, isDefaultHeight: !(row && row.h > 0 && isCustomHeight) // Высота строки, вычисленная на основе текста }; y += this.rows[i].height; } }, /** Вычисляет диапазон индексов видимых колонок */ _calcVisibleColumns: function () { var l = this.cols.length; var w = this.drawingCtx.getWidth(); var sumW = this.cellsLeft; for (var i = this.visibleRange.c1, f = false; i < l && sumW < w; ++i) { sumW += this.cols[i].width; f = true; } this.visibleRange.c2 = i - (f ? 1 : 0); }, /** Вычисляет диапазон индексов видимых колонок */ _calcVisibleRows: function () { var l = this.rows.length; var h = this.drawingCtx.getHeight(); var sumH = this.cellsTop; for (var i = this.visibleRange.r1, f = false; i < l && sumH < h; ++i) { sumH += this.rows[i].height; f = true; } this.visibleRange.r2 = i - (f ? 1 : 0); }, /** Обновляет позицию колонок (в pt) */ _updateColumnPositions: function () { var x = this.cellsLeft; for (var l = this.cols.length, i = 0; i < l; ++i) { this.cols[i].left = x; x += this.cols[i].width; } }, /** Обновляет позицию строк (в pt) */ _updateRowPositions: function () { var y = this.cellsTop; for (var l = this.rows.length, i = 0; i < l; ++i) { this.rows[i].top = y; y += this.rows[i].height; } }, /** * Добавляет колонки, пока общая ширина листа не превысит rightSide * @param {Number} rightSide Правая граница */ _appendColumns: function (rightSide) { var i = this.cols.length; var lc = this.cols[i - 1]; var done = false; for (var x = lc.left + lc.width; x < rightSide || !done; ++i) { if (x >= rightSide) { // add +1 column at the end and exit cycle done = true; } this.cols[i] = this._calcColWidth( this.model.getColWidth(i) ); this.cols[i].left = x; x += this.cols[i].width; this.isChanged = true; } }, /** Устанаваливает видимый диапазон ячеек максимально возможным */ _normalizeViewRange: function () { var t = this; var vr = t.visibleRange; var w = t.drawingCtx.getWidth() - t.cellsLeft; var h = t.drawingCtx.getHeight() - t.cellsTop; var c = t.cols; var r = t.rows; var vw = c[vr.c2].left + c[vr.c2].width - c[vr.c1].left; var vh = r[vr.r2].top + r[vr.r2].height - r[vr.r1].top; var i; if (vw < w) { for (i = vr.c1 - 1; i >= 0; --i) { vw += c[i].width; if (vw > w) {break;} } vr.c1 = i + 1; if (vr.c1 >= vr.c2) {vr.c1 = vr.c2 - 1;} if (vr.c1 < 0) {vr.c1 = 0;} } if (vh < h) { for (i = vr.r1 - 1; i >= 0; --i) { vh += r[i].height; if (vh > h) {break;} } vr.r1 = i + 1; if (vr.r1 >= vr.r2) {vr.r1 = vr.r2 - 1;} if (vr.r1 < 0) {vr.r1 = 0;} } }, _shiftVisibleRange: function () { var t = this; var vr = t.visibleRange; var arn = t.activeRange.clone(true); var i; do { if (arn.r2 > vr.r2) { i = arn.r2 - vr.r2; vr.r1 += i; vr.r2 += i; t._calcVisibleRows(); continue; } if (t._isRowDrawnPartially(arn.r2, vr.r1)) { vr.r1 += 1; t._calcVisibleRows(); } if (arn.r1 < vr.r1) { i = arn.r1 - vr.r1; vr.r1 += i; vr.r2 += i; t._calcVisibleRows(); } break; } while (1); do { if (arn.c2 > vr.c2) { i = arn.c2 - vr.c2; vr.c1 += i; vr.c2 += i; t._calcVisibleColumns(); continue; } if (t._isColDrawnPartially(arn.c2, vr.c1)) { vr.c1 += 1; t._calcVisibleColumns(); } if (arn.c1 < vr.c1) { i = arn.c1 - vr.c1; vr.c1 += i; vr.c2 += i; if (vr.c1 < 0) { vr.c1 = 0; vr.c2 -= vr.c1; } t._calcVisibleColumns(); } break; } while (1); }, // Обновляем cache hyperlinks _updateHyperlinksCache: function () { // ToDo можно обновлять не весь cache гиперлинков, а только часть var t = this; var vr = t.visibleRange; var range = t.model.getRange3(vr.r1, vr.c1, vr.r2, vr.c2); var hyperlinks = range.getHyperlinks (); this.visibleHyperlinks = []; if (null === hyperlinks) return; for (var i = 0; i < hyperlinks.length; ++i) { // Гиперлинк var oHyperlink = new asc_CHyperlink(); // Range для гиперссылки var hyperlinkTmp = hyperlinks[i].hyperlink; var hyperlinkRange = hyperlinkTmp.Ref.getBBox0(); oHyperlink.asc_setHyperlinkRange (hyperlinkRange); oHyperlink.asc_setHyperlinkCol (hyperlinks[i].col); oHyperlink.asc_setHyperlinkRow (hyperlinks[i].row); // Тип гиперссылки var type = (null !== hyperlinkTmp.Hyperlink) ? c_oAscHyperlinkType.WebLink : c_oAscHyperlinkType.RangeLink; oHyperlink.asc_setType (type); if (c_oAscHyperlinkType.RangeLink === type) { // // ToDo переделать это место (парсить должны в момент открытия и добавления ссылки) var result = parserHelp.parse3DRef (hyperlinkTmp.Location); if (null !== result) { oHyperlink.asc_setSheet (result.sheet); oHyperlink.asc_setRange (result.range); } } oHyperlink.asc_setLocation (hyperlinkTmp.Location); oHyperlink.asc_setTooltip (hyperlinkTmp.Tooltip); oHyperlink.asc_setHyperlinkUrl (hyperlinkTmp.Hyperlink); // Добавляем гиперссылку в кеш this.visibleHyperlinks[i] = oHyperlink; } }, // Получаем индекс гиперлинка по ячейке _getHyperlinkIndex: function (c, r) { for (var i = this.visibleHyperlinks.length - 1; i >= 0; --i) { var col = this.visibleHyperlinks[i].asc_getHyperlinkCol(); var row = this.visibleHyperlinks[i].asc_getHyperlinkRow(); if (col === c && row === r) return i; } return null; }, // ----- Drawing for print ----- calcPagesPrint: function (pageOptions, printOnlySelection, indexWorksheet, layoutPageType) { var maxCols = this.model.getColsCount(); var maxRows = this.model.getRowsCount(); var lastC = -1, lastR = -1; var activeRange = printOnlySelection ? this.activeRange : null; if (null === activeRange) { for (var c = 0; c < maxCols; ++c) { for (var r = 0; r < maxRows; ++r) { if (!this._isCellEmptyOrMergedOrBackgroundColorOrBorders(c, r)) { var ct = this._getCellTextCache(c, r); if (ct === undefined) { // Мы печатаем и могут быть невидимые области, попробуем добавить текст и взять его снова this._addCellTextToCache (c, r); ct = this._getCellTextCache(c, r); } var rightSide = 0; if (ct !== undefined) { var isMerged = ct.flags.isMerged, isWrapped = ct.flags.wrapText; if (!isMerged && !isWrapped) rightSide = ct.sideR; } lastC = Math.max(lastC, c + rightSide); lastR = Math.max(lastR, r); } } } maxCols = lastC+1; maxRows = lastR+1; // Получаем максимальную колонку/строку для изображений/чатов var maxObjectsCoord = this.objectRender.getDrawingAreaMetrics(); if (maxObjectsCoord) { maxCols = Math.max (maxCols, maxObjectsCoord.maxCol); maxRows = Math.max (maxRows, maxObjectsCoord.maxRow); } } else { maxCols = activeRange.c2 + 1; maxRows = activeRange.r2 + 1; } var pageMargins, pageSetup, pageGridLines, pageHeadings; if (pageOptions instanceof asc_CPageOptions) { pageMargins = pageOptions.asc_getPageMargins(); pageSetup = pageOptions.asc_getPageSetup(); pageGridLines = pageOptions.asc_getGridLines(); pageHeadings = pageOptions.asc_getHeadings(); } var pageWidth, pageHeight, pageOrientation; if (pageSetup instanceof asc_CPageSetup) { pageWidth = pageSetup.asc_getWidth(); pageHeight = pageSetup.asc_getHeight(); pageOrientation = pageSetup.asc_getOrientation(); } var pageLeftField, pageRightField, pageTopField, pageBottomField; if (pageMargins instanceof asc_CPageMargins) { pageLeftField = pageMargins.asc_getLeft(); pageRightField = pageMargins.asc_getRight(); pageTopField = pageMargins.asc_getTop(); pageBottomField = pageMargins.asc_getBottom(); } if (null === pageGridLines || undefined === pageGridLines) { pageGridLines = c_oAscPrintDefaultSettings.PageGridLines; } if (null === pageHeadings || undefined === pageHeadings) { pageHeadings = c_oAscPrintDefaultSettings.PageHeadings; } if (null === pageWidth || undefined === pageWidth) { pageWidth = c_oAscPrintDefaultSettings.PageWidth; } if (null === pageHeight || undefined === pageHeight) { pageHeight = c_oAscPrintDefaultSettings.PageHeight; } if (null === pageOrientation || undefined === pageOrientation) { pageOrientation = c_oAscPrintDefaultSettings.PageOrientation; } if (null === pageLeftField || undefined === pageLeftField) { pageLeftField = c_oAscPrintDefaultSettings.PageLeftField; } if (null === pageRightField || undefined === pageRightField) { pageRightField = c_oAscPrintDefaultSettings.PageRightField; } if (null === pageTopField || undefined === pageTopField) { pageTopField = c_oAscPrintDefaultSettings.PageTopField; } if (null === pageBottomField || undefined === pageBottomField) { pageBottomField = c_oAscPrintDefaultSettings.PageBottomField; } if (c_oAscPageOrientation.PageLandscape === pageOrientation) { var tmp = pageWidth; pageWidth = pageHeight; pageHeight = tmp; } var arrResult = []; if (0 === maxCols || 0 === maxRows) { // Ничего нет, возвращаем пустой массив return null; } else { var pageWidthWithFields = pageWidth - pageLeftField - pageRightField; var pageHeightWithFields = pageHeight - pageTopField - pageBottomField; var leftFieldInPt = pageLeftField / vector_koef; var topFieldInPt = pageTopField / vector_koef; var rightFieldInPt = pageRightField / vector_koef; var bottomFieldInPt = pageBottomField / vector_koef; if (pageHeadings) { // Рисуем заголовки, нужно чуть сдвинуться leftFieldInPt += this.cellsLeft; topFieldInPt += this.cellsTop; } var pageWidthWithFieldsHeadings = (pageWidth - pageRightField) / vector_koef - leftFieldInPt; var pageHeightWithFieldsHeadings = (pageHeight - pageBottomField) / vector_koef - topFieldInPt; var currentColIndex = (null !== activeRange) ? activeRange.c1 : 0; var currentWidth = 0; var currentRowIndex = (null !== activeRange) ? activeRange.r1 : 0; var currentHeight = 0; var isCalcColumnsWidth = true; var bIsAddOffset = false; var nCountOffset = 0; while (true) { if (currentColIndex === maxCols && currentRowIndex === maxRows) break; var newPagePrint = new asc_CPagePrint(); var colIndex = currentColIndex, rowIndex = currentRowIndex; newPagePrint.indexWorksheet = indexWorksheet; newPagePrint.pageWidth = pageWidth; newPagePrint.pageHeight = pageHeight; newPagePrint.pageClipRectLeft = pageLeftField / vector_koef; newPagePrint.pageClipRectTop = pageTopField / vector_koef; newPagePrint.pageClipRectWidth = pageWidthWithFields / vector_koef; newPagePrint.pageClipRectHeight = pageHeightWithFields / vector_koef; newPagePrint.leftFieldInPt = leftFieldInPt; newPagePrint.topFieldInPt = topFieldInPt; newPagePrint.rightFieldInPt = rightFieldInPt; newPagePrint.bottomFieldInPt = bottomFieldInPt; for (rowIndex = currentRowIndex; rowIndex < maxRows; ++rowIndex) { var currentRowHeight = this.rows[rowIndex].height; if (currentHeight + currentRowHeight > pageHeightWithFieldsHeadings) { // Закончили рисовать страницу break; } if (isCalcColumnsWidth) { for (colIndex = currentColIndex; colIndex < maxCols; ++colIndex) { var currentColWidth = this.cols[colIndex].width; if (bIsAddOffset) { newPagePrint.startOffset = ++nCountOffset; newPagePrint.startOffsetPt = (pageWidthWithFieldsHeadings * newPagePrint.startOffset); currentColWidth -= newPagePrint.startOffsetPt; } if (c_oAscLayoutPageType.FitToWidth !== layoutPageType && currentWidth + currentColWidth > pageWidthWithFieldsHeadings && colIndex !== currentColIndex) break; currentWidth += currentColWidth; if (c_oAscLayoutPageType.FitToWidth !== layoutPageType && currentWidth > pageWidthWithFieldsHeadings && colIndex === currentColIndex) { // Смещаем в селедующий раз ячейку bIsAddOffset = true; ++colIndex; break; } else bIsAddOffset = false; } isCalcColumnsWidth = false; if (pageHeadings) { currentWidth += this.cellsLeft; } if (c_oAscLayoutPageType.FitToWidth === layoutPageType) { newPagePrint.pageClipRectWidth = Math.max(currentWidth, newPagePrint.pageClipRectWidth); newPagePrint.pageWidth = newPagePrint.pageClipRectWidth * vector_koef + (pageLeftField + pageRightField); } else { newPagePrint.pageClipRectWidth = Math.min(currentWidth, newPagePrint.pageClipRectWidth); } } currentHeight += currentRowHeight; currentWidth = 0; } // Нужно будет пересчитывать колонки isCalcColumnsWidth = true; // Рисуем сетку if (pageGridLines) { newPagePrint.pageGridLines = true; } if (pageHeadings) { // Нужно отрисовать заголовки newPagePrint.pageHeadings = true; } newPagePrint.pageRange = asc_Range(currentColIndex, currentRowIndex, colIndex - 1, rowIndex - 1); if (bIsAddOffset) { // Мы еще не дорисовали колонку colIndex -= 1; } else { nCountOffset = 0; } if (colIndex < maxCols) { // Мы еще не все колонки отрисовали currentColIndex = colIndex; currentHeight = 0; } else { // Мы дорисовали все колонки, нужна новая строка и стартовая колонка currentColIndex = (null !== activeRange) ? activeRange.c1 : 0; currentRowIndex = rowIndex; currentHeight = 0; } if (rowIndex === maxRows) { // Мы вышли, т.к. дошли до конца отрисовки по строкам if (colIndex < maxCols) { currentColIndex = colIndex; currentHeight = 0; } else { // Мы дошли до конца отрисовки currentColIndex = colIndex; currentRowIndex = rowIndex; } } arrResult.push(newPagePrint); } } return arrResult; }, drawForPrint: function (drawingCtx, printPagesData) { var isAppBridge = (undefined != window['appBridge']); if (null === printPagesData) { // Напечатаем пустую страницу drawingCtx.BeginPage (c_oAscPrintDefaultSettings.PageWidth, c_oAscPrintDefaultSettings.PageHeight); drawingCtx.EndPage(); } else { drawingCtx.BeginPage (printPagesData.pageWidth, printPagesData.pageHeight); drawingCtx.AddClipRect (printPagesData.pageClipRectLeft, printPagesData.pageClipRectTop, printPagesData.pageClipRectWidth, printPagesData.pageClipRectHeight); if (isAppBridge) {window['appBridge']['dummyCommandUpdate'] ();} var offsetCols = printPagesData.startOffsetPt; var range = printPagesData.pageRange; for (var row = range.r1; row <= range.r2; ++row) { var rangeTmpRow = asc_Range(range.c1, row, range.c2, row); if (isAppBridge) {window['appBridge']['dummyCommandUpdate'] ();} // Рисуем сетку if (printPagesData.pageGridLines) { this._drawGrid(drawingCtx, rangeTmpRow, this.cols[range.c1].left - printPagesData.leftFieldInPt + offsetCols, this.rows[range.r1].top - printPagesData.topFieldInPt, printPagesData.pageWidth / vector_koef, printPagesData.pageHeight / vector_koef); } if (isAppBridge) {window['appBridge']['dummyCommandUpdate'] ();} // Рисуем строку для печати var mergedCells = {}; $.extend (mergedCells, this._drawRowBG(drawingCtx, row, range.c1, range.c2, this.cols[range.c1].left - printPagesData.leftFieldInPt + offsetCols, this.rows[range.r1].top - printPagesData.topFieldInPt, false), this._drawRowText(drawingCtx, row, range.c1, range.c2, this.cols[range.c1].left - printPagesData.leftFieldInPt + offsetCols, this.rows[range.r1].top - printPagesData.topFieldInPt)); if (isAppBridge) {window['appBridge']['dummyCommandUpdate'] ();} // draw merged cells at last stage to fix cells background issue for (var i in mergedCells) if (mergedCells.hasOwnProperty(i)) { var mc = mergedCells[i]; this._drawRowBG(drawingCtx, mc.row, mc.col, mc.col, this.cols[range.c1].left - printPagesData.leftFieldInPt + offsetCols, this.rows[range.r1].top - printPagesData.topFieldInPt, true); this._drawCellText(drawingCtx, mc.col, mc.row, range.c1, range.c2, this.cols[range.c1].left - printPagesData.leftFieldInPt + offsetCols, this.rows[range.r1].top - printPagesData.topFieldInPt, true); if (isAppBridge) {window['appBridge']['dummyCommandUpdate'] ();} } if (isAppBridge) {window['appBridge']['dummyCommandUpdate'] ();} // Отрисовываем бордеры this._drawCellsBorders (drawingCtx, range, /*mergedCellsStage*/undefined, this.cols[range.c1].left - printPagesData.leftFieldInPt + offsetCols, this.rows[range.r1].top - printPagesData.topFieldInPt); if (isAppBridge) {window['appBridge']['dummyCommandUpdate'] ();} } if (printPagesData.pageHeadings) { // Нужно отрисовать заголовки this._drawColumnHeaders (drawingCtx, range.c1, range.c2, /*style*/ undefined, this.cols[range.c1].left - printPagesData.leftFieldInPt + offsetCols, printPagesData.topFieldInPt - this.cellsTop); this._drawRowHeaders (drawingCtx, range.r1, range.r2, /*style*/ undefined, printPagesData.leftFieldInPt - this.cellsLeft, this.rows[range.r1].top - printPagesData.topFieldInPt); } if (isAppBridge) {window['appBridge']['dummyCommandUpdate'] ();} // Отрисовываем картинки и графики (для этого должны выставить видимую область) // Сохраняем копию и на время меняем область (стоит рисовать от входных параметров функции, а не от методов класса) var tmpVisibleRange = this.visibleRange.clone(true); this.visibleRange.c1 = range.c1; this.visibleRange.c2 = range.c2; this.visibleRange.r1 = range.r1; this.visibleRange.r2 = range.r2; var drawingPrintOptions = { ctx: drawingCtx, margin: { left: printPagesData.leftFieldInPt - this.cellsLeft, top: printPagesData.topFieldInPt - this.cellsTop, right: printPagesData.rightFieldInPt, bottom: printPagesData.bottomFieldInPt } }; this.objectRender.showDrawingObjects(false, drawingPrintOptions); this.visibleRange = tmpVisibleRange.clone(true); if (isAppBridge) {window['appBridge']['dummyCommandUpdate'] ();} drawingCtx.RemoveClipRect(); drawingCtx.EndPage(); } }, // ----- Drawing ----- draw: function (lockDraw) { if( lockDraw ) return this; this._clean(); this._drawCorner(); this._drawColumnHeaders(/*drawingCtx*/ undefined); this._drawRowHeaders(/*drawingCtx*/ undefined); this._drawGrid(/*drawingCtx*/ undefined); this._drawCells(); this._drawCellsBorders(/*drawingCtx*/undefined); this._fixSelectionOfMergedCells(); this._fixSelectionOfHiddenCells(undefined, undefined, /*isDraw*/true); if (this.overlayCtx) { this._drawSelection(); } this.objectRender.showDrawingObjects(true); return this; }, _clean: function () { this.drawingCtx .setFillStyle(this.settings.cells.defaultState.background) .fillRect(0, 0, this.drawingCtx.getWidth(), this.drawingCtx.getHeight()); if (this.overlayCtx) { this.overlayCtx.clear(); } }, drawHighlightedHeaders: function (col, row) { this._activateOverlayCtx(); if (col >= 0 && col !== this.highlightedCol) { this._doCleanHighlightedHeaders(); this.highlightedCol = col; this._drawColumnHeaders(/*drawingCtx*/ undefined, col, col, kHeaderHighlighted); } else if (row >= 0 && row !== this.highlightedRow) { this._doCleanHighlightedHeaders(); this.highlightedRow = row; this._drawRowHeaders(/*drawingCtx*/ undefined, row, row, kHeaderHighlighted); } this._deactivateOverlayCtx(); return this; }, cleanHighlightedHeaders: function () { this._activateOverlayCtx(); this._doCleanHighlightedHeaders(); this._deactivateOverlayCtx(); return this; }, _activateOverlayCtx: function () { this.drawingCtx = this.buffers.overlay; }, _deactivateOverlayCtx: function () { this.drawingCtx = this.buffers.main; }, _doCleanHighlightedHeaders: function () { var hlc = this.highlightedCol, hlr = this.highlightedRow, arn = this.activeRange.clone(true); if (hlc >= 0) { if (hlc >= arn.c1 && hlc <= arn.c2) { this._drawColumnHeaders(/*drawingCtx*/ undefined, hlc, hlc, kHeaderActive); } else { this._cleanColumnHeaders(hlc); if (hlc + 1 === arn.c1) { this._drawColumnHeaders(/*drawingCtx*/ undefined, hlc + 1, hlc + 1, kHeaderActive); } else if (hlc - 1 === arn.c2) { this._drawColumnHeaders(/*drawingCtx*/ undefined, hlc - 1, hlc - 1, kHeaderActive); } } this.highlightedCol = -1; } if (hlr >= 0) { if (hlr >= arn.r1 && hlr <= arn.r2) { this._drawRowHeaders(/*drawingCtx*/ undefined, hlr, hlr, kHeaderActive); } else { this._cleanRowHeades(hlr); if (hlr + 1 === arn.r1) { this._drawRowHeaders(/*drawingCtx*/ undefined, hlr + 1, hlr + 1, kHeaderActive); } else if (hlr - 1 === arn.r2) { this._drawRowHeaders(/*drawingCtx*/ undefined, hlr - 1, hlr - 1, kHeaderActive); } } this.highlightedRow = -1; } }, _drawActiveHeaders: function () { var arn = this.activeRange.clone(true), vr = this.visibleRange, c1 = Math.max(vr.c1, arn.c1), c2 = Math.min(vr.c2, arn.c2), r1 = Math.max(vr.r1, arn.r1), r2 = Math.min(vr.r2, arn.r2); this._activateOverlayCtx(); this._drawColumnHeaders(/*drawingCtx*/ undefined, c1, c2, kHeaderActive); this._drawRowHeaders(/*drawingCtx*/ undefined, r1, r2, kHeaderActive); this._deactivateOverlayCtx(); }, _drawCorner: function () { if (false === this.model.sheetViews[0].asc_getShowRowColHeaders()) return; var x1 = this.headersLeft + this.headersWidth - this.headersHeight; var x2 = this.headersLeft + this.headersWidth; var y1 = this.headersTop; var y2 = this.headersTop + this.headersHeight; var dx = 3; var dy = 2; this._drawHeader(/*drawingCtx*/ undefined, "", this.headersLeft, this.headersTop, this.headersWidth, this.headersHeight, kHeaderDefault, true, -1); this.drawingCtx .beginPath() .moveTo(x2, y1, -dx-1.5, dy) .lineTo(x2, y2, -dx-1.5, -dy-1) .lineTo(x2, y2, -dx-1, -dy-1.5) .lineTo(x1, y2, 0, -dy-1.5) .lineTo(x1, y2, 0.5, -dy-1.5) .lineTo(x2, y1, -dx-1.5, dy+0.5) .lineTo(x2, y1, -dx-1.5, dy) .setFillPattern(this.settings.header.cornerColor) .fill(); }, /** Рисует заголовки видимых колонок */ _drawColumnHeaders: function (drawingCtx, start, end, style, offsetXForDraw, offsetYForDraw) { if (undefined === drawingCtx && false === this.model.sheetViews[0].asc_getShowRowColHeaders()) return; var hdr = this.settings.header; var cells = this.settings.cells; var vr = this.visibleRange; var offsetX = (offsetXForDraw) ? offsetXForDraw : this.cols[vr.c1].left - this.cellsLeft; var offsetY = (offsetYForDraw) ? offsetYForDraw : this.headersTop; if (asc_typeof(start) !== "number") {start = vr.c1;} if (asc_typeof(end) !== "number") {end = vr.c2;} if (style === undefined) {style = kHeaderDefault;} this._setFont(drawingCtx, hdr.fontName, hdr.fontSize); this.stringRender.setDefaultFont(new asc_FP(hdr.fontName, hdr.fontSize)); // draw column headers for (var i = start; i <= end; ++i) { this._drawHeader(drawingCtx, this._getColumnTitle(i), this.cols[i].left - offsetX, offsetY, this.cols[i].width, this.headersHeight, style, true, i); } this.stringRender.setDefaultFont(new asc_FP(cells.fontName, cells.fontSize)); }, /** Рисует заголовки видимых строк */ _drawRowHeaders: function (drawingCtx, start, end, style, offsetXForDraw, offsetYForDraw) { if (undefined === drawingCtx && false === this.model.sheetViews[0].asc_getShowRowColHeaders()) return; var hdr = this.settings.header; var cells = this.settings.cells; var vr = this.visibleRange; var offsetX = (offsetXForDraw) ? offsetXForDraw : this.headersLeft; var offsetY = (offsetYForDraw) ? offsetYForDraw : this.rows[vr.r1].top - this.cellsTop; if (asc_typeof(start) !== "number") {start = vr.r1;} if (asc_typeof(end) !== "number") {end = vr.r2;} if (style === undefined) {style = kHeaderDefault;} this._setFont(drawingCtx, hdr.fontName, hdr.fontSize); this.stringRender.setDefaultFont(new asc_FP(hdr.fontName, hdr.fontSize)); // draw row headers for (var i = start; i <= end; ++i) { this._drawHeader(drawingCtx, this._getRowTitle(i), offsetX, this.rows[i].top - offsetY, this.headersWidth, this.rows[i].height, style, false, i); } this.stringRender.setDefaultFont(new asc_FP(cells.fontName, cells.fontSize)); }, /** * Рисует заголовок, принимает координаты и размеры в pt * @param {DrawingContext} drawingCtx * @param {String} text Текст заголовка * @param {Number} x Координата левого угла в pt * @param {Number} y Координата левого угла в pt * @param {Number} w Ширина в pt * @param {Number} h Высота в pt * @param {Number} style Стиль заголовка (kHeaderDefault, kHeaderActive, kHeaderHighlighted, kHeaderSelected) * @param {Boolean} isColHeader Тип заголовка: true - колонка, false - строка * @param {Number} index Индекс столбца/строки или -1 */ _drawHeader: function (drawingCtx, text, x, y, w, h, style, isColHeader, index) { // Для отрисовки невидимого столбца/строки var isZeroHeader = false; if (isColHeader) { if (w < this.width_1px) { // Это невидимый столбец isZeroHeader = true; // Отрисуем только границу w = this.width_1px; // Возможно мы уже рисовали границу невидимого столбца (для последовательности невидимых) if (0 < index && 0 === this.cols[index - 1].width) { // Мы уже нарисовали border для невидимой границы return; } } else if (0 < index && 0 === this.cols[index - 1].width) { // Мы уже нарисовали border для невидимой границы (поэтому нужно чуть меньше рисовать для соседнего столбца) w -= this.width_1px; x += this.width_1px; } } else { if (h < this.height_1px) { // Это невидимая строка isZeroHeader = true; // Отрисуем только границу h = this.height_1px; // Возможно мы уже рисовали границу невидимой строки (для последовательности невидимых) if (0 < index && 0 === this.rows[index - 1].height) { // Мы уже нарисовали border для невидимой границы return; } } else if (0 < index && 0 === this.rows[index - 1].height) { // Мы уже нарисовали border для невидимой границы (поэтому нужно чуть меньше рисовать для соседней строки) h -= this.height_1px; y += this.height_1px; } } var ctx = (drawingCtx) ? drawingCtx : this.drawingCtx; var st = this.settings.header.style[style]; var x2 = x + w - this.width_1px; var y2 = y + h - this.height_1px; // background только для видимых if (!isZeroHeader) { // draw background ctx.setFillStyle(st.background) .fillRect(x, y, w, h); } // draw border ctx.setStrokeStyle(st.border) .setLineWidth(1) .beginPath(); if (/*style !== kHeaderDefault &&*/ !isColHeader) { ctx.moveTo(x, y, 0, -0.5) .lineTo(x2, y, 1, -0.5); } ctx.moveTo(x2, y, 0.5, 0) .lineTo(x2, y2, 0.5, 0) .moveTo(x2, y2, 1, 0.5) .lineTo(x, y2, 0, 0.5); if (/*style !== kHeaderDefault &&*/ isColHeader) { ctx.moveTo(x, y2, -0.5, 1) .lineTo(x, y, -0.5, 0); } ctx.stroke(); // Для невидимых кроме border-а ничего не рисуем if (isZeroHeader || text.length < 1) return; // draw text var sr = this.stringRender; var tm = this._roundTextMetrics( sr.measureString(text) ); var bl = y2 - (isColHeader ? this.defaultRowDescender : this.rows[index].descender); var textX = this._calcTextHorizPos(x, x2, tm, tm.width < w ? khaCenter : khaLeft); var textY = this._calcTextVertPos(y, y2, bl, tm, kvaBottom); if (drawingCtx) { ctx.AddClipRect(x, y, w, h); ctx.setFillStyle(st.color) .fillText(text, textX, textY + tm.baseline, undefined, sr.charWidths); ctx.RemoveClipRect(); } else { ctx.save() .beginPath() .rect(x, y, w, h) .clip() .setFillStyle(st.color) .fillText(text, textX, textY + tm.baseline, undefined, sr.charWidths) .restore(); } }, _cleanColumnHeaders: function (colStart, colEnd) { var offsetX = this.cols[this.visibleRange.c1].left - this.cellsLeft; if (colEnd === undefined) {colEnd = colStart;} colStart = Math.max(this.visibleRange.c1, colStart); colEnd = Math.min(this.visibleRange.c2, colEnd); for (var i = colStart; i <= colEnd; ++i) { this.drawingCtx.clearRect( this.cols[i].left - offsetX - this.width_1px, this.headersTop, this.cols[i].width + this.width_1px, this.headersHeight); } }, _cleanRowHeades: function (rowStart, rowEnd) { var offsetY = this.rows[this.visibleRange.r1].top - this.cellsTop; if (rowEnd === undefined) {rowEnd = rowStart;} rowStart = Math.max(this.visibleRange.r1, rowStart); rowEnd = Math.min(this.visibleRange.r2, rowEnd); for (var i = rowStart; i <= rowEnd; ++i) { this.drawingCtx.clearRect( this.headersLeft, this.rows[i].top - offsetY - this.height_1px, this.headersWidth, this.rows[i].height + this.height_1px); } }, _cleanColumnHeadersRect: function () { this.drawingCtx.clearRect( this.cellsLeft, this.headersTop, this.drawingCtx.getWidth() - this.cellsLeft, this.headersHeight); }, /** Рисует сетку таблицы */ _drawGrid: function (drawingCtx, range, leftFieldInPt, topFieldInPt, width, height) { // Возможно сетку не нужно рисовать (при печати свои проверки) if (undefined === drawingCtx && false === this.model.sheetViews[0].asc_getShowGridLines()) return; if (range === undefined) { range = this.visibleRange; } var ctx = (drawingCtx) ? drawingCtx : this.drawingCtx; var widthCtx = (width) ? width : ctx.getWidth(); var heightCtx = (height) ? height : ctx.getHeight(); var offsetX = (leftFieldInPt) ? leftFieldInPt : this.cols[this.visibleRange.c1].left - this.cellsLeft; var offsetY = (topFieldInPt) ? topFieldInPt : this.rows[this.visibleRange.r1].top - this.cellsTop; var x1 = this.cols[range.c1].left - offsetX; var y1 = this.rows[range.r1].top - offsetY; var x2 = Math.min(this.cols[range.c2].left - offsetX + this.cols[range.c2].width, widthCtx); var y2 = Math.min(this.rows[range.r2].top - offsetY + this.rows[range.r2].height, heightCtx); ctx.setFillStyle(this.settings.cells.defaultState.background) .fillRect(x1, y1, x2 - x1, y2 - y1) .setStrokeStyle(this.settings.cells.defaultState.border) .setLineWidth(1); for (var i = range.c1, x = x1; i <= range.c2 && x <= x2; ++i) { x += this.cols[i].width; ctx.beginPath().moveTo(x, y1, -0.5, 0).lineTo(x, y2, -0.5, 0).stroke(); } for (var j = range.r1, y = y1; j <= range.r2 && y <= y2; ++j) { y += this.rows[j].height; ctx.beginPath().moveTo(x1, y, 0, -0.5).lineTo(x2, y, 0, -0.5).stroke(); } }, /** Рисует ячейки таблицы */ _drawCells: function (range) { if (range === undefined) { range = this.visibleRange; } this._prepareCellTextMetricsCache(range); var ctx = this.drawingCtx; var offsetX = this.cols[this.visibleRange.c1].left - this.cellsLeft; var offsetY = this.rows[this.visibleRange.r1].top - this.cellsTop; var mergedCells = {}, mc, i; // set clipping rect to cells area ctx.save() .beginPath() .rect(this.cellsLeft, this.cellsTop, ctx.getWidth() - this.cellsLeft, ctx.getHeight() - this.cellsTop) .clip(); for (var row = range.r1; row <= range.r2; ++row) { $.extend( mergedCells, this._drawRowBG(/*drawingCtx*/undefined, row, range.c1, range.c2, offsetX, offsetY, false), this._drawRowText(/*drawingCtx*/undefined, row, range.c1, range.c2, offsetX, offsetY) ); } // draw merged cells at last stage to fix cells background issue for (i in mergedCells) if (mergedCells.hasOwnProperty(i)) { mc = mergedCells[i]; this._drawRowBG(/*drawingCtx*/undefined, mc.row, mc.col, mc.col, offsetX, offsetY, true); this._drawCellText(/*drawingCtx*/undefined, mc.col, mc.row, range.c1, range.c2, offsetX, offsetY, true); } // restore canvas' original clipping range ctx.restore(); }, /** Рисует фон ячеек в строке */ _drawRowBG: function (drawingCtx, row, colStart, colEnd, offsetX, offsetY, drawMergedCells) { if (this.rows[row].height < this.height_1px && true !== drawMergedCells) {return {};} for (var mergedCells = {}, col = colStart; col <= colEnd; ++col) { if (this.cols[col].width < this.width_1px && true !== drawMergedCells) {continue;} var c = this._getVisibleCell(col, row); if (!c) {continue;} var ctx = (undefined === drawingCtx) ? this.drawingCtx : drawingCtx; var bg = c.getFill(); if(null != bg) bg = bg.getRgb(); var fl = this._getCellFlags(c); var range = fl.isMerged ? this._getMergedCellsRange(col, row) : undefined; var mwidth = 0, mheight = 0; if (fl.isMerged && !drawMergedCells) { if (this._getCellTextCache(col, row, true) === undefined) { if (!range) { // Мы еще не закешировали замерженную ячейку, добавим и возьмем снова this._addMergedCellsRange (col, row); range = this._getMergedCellsRange(col, row); } mergedCells[range.r1 + "_" + range.c1] = {col: range.c1, row: range.r1}; } continue; } if (fl.isMerged) { if (col !== range.c1 || row !== range.r1) {continue;} for (var i = range.c1 + 1; i <= range.c2 && i < this.nColsCount; ++i) {mwidth += this.cols[i].width;} for (var j = range.r1 + 1; j <= range.r2 && j < this.nRowsCount; ++j) {mheight += this.rows[j].height;} } else { if (bg === null) { if (col === colEnd && col < this.cols.length - 1 && row < this.rows.length - 1) { var c2 = this._getVisibleCell(col + 1, row); if (c2) { var bg2 = c2.getFill(); if (bg2 !== null) { ctx.setFillStyle(asc_n2css(bg2.getRgb())) .fillRect( this.cols[col + 1].left - offsetX - this.width_1px, this.rows[row].top - offsetY - this.height_1px, this.width_1px, this.rows[row].height + this.height_1px); } } var c3 = this._getVisibleCell(col, row + 1); if (c3) { var bg3 = c3.getFill(); if (bg3 !== null) { ctx.setFillStyle(asc_n2css(bg3.getRgb())) .fillRect( this.cols[col].left - offsetX - this.width_1px, this.rows[row + 1].top - offsetY - this.height_1px, this.cols[col].width + this.width_1px, this.height_1px); } } } continue; } } var x = this.cols[col].left - (bg !== null ? this.width_1px : 0); var y = this.rows[row].top - (bg !== null ? this.height_1px : 0); var w = this.cols[col].width + this.width_1px * (bg !== null ? +1 : -1) + mwidth; var h = this.rows[row].height + this.height_1px * (bg !== null ? +1 : -1) + mheight; var color = bg !== null ? asc_n2css(bg) : this.settings.cells.defaultState.background; ctx.setFillStyle(color) .fillRect(x - offsetX, y - offsetY, w, h); } return mergedCells; }, /** Рисует текст ячеек в строке */ _drawRowText: function (drawingCtx, row, colStart, colEnd, offsetX, offsetY) { if (this.rows[row].height < this.height_1px) {return {};} var dependentCells = {}, mergedCells = {}, i = undefined, mc; // draw cells' text for (var col = colStart; col <= colEnd; ++col) { if (this.cols[col].width < this.width_1px) {continue;} mc = this._drawCellText(drawingCtx, col, row, colStart, colEnd, offsetX, offsetY, false); if (mc !== null) {mergedCells[mc.index] = {col: mc.col, row: mc.row};} // check if long text overlaps this cell i = this._findSourceOfCellText(col, row); if (i >= 0) { dependentCells[i] = (dependentCells[i] || []); dependentCells[i].push(col); } } // draw long text that overlaps own cell's borders for (i in dependentCells) if (dependentCells.hasOwnProperty(i)) { var arr = dependentCells[i], j = arr.length - 1; col = parseInt(i, 10); // if source cell belongs to cells range then skip it (text has been drawn already) if (col >= arr[0] && col <= arr[j]) {continue;} // draw long text fragment this._drawCellText(drawingCtx, col, row, arr[0], arr[j], offsetX, offsetY, false); } return mergedCells; }, /** Рисует текст ячейки */ _drawCellText: function (drawingCtx, col, row, colStart, colEnd, offsetX, offsetY, drawMergedCells) { var ct = this._getCellTextCache(col, row); if (ct === undefined) { if (drawingCtx) { // Мы печатаем и могут быть невидимые области, попробуем добавить текст и взять его снова this._addCellTextToCache (col, row); ct = this._getCellTextCache(col, row); if (ct === undefined) return null; } else return null; } var isMerged = ct.flags.isMerged, range = undefined, isWrapped = ct.flags.wrapText; var ctx = (undefined === drawingCtx) ? this.drawingCtx : drawingCtx; if (isMerged) { range = this._getMergedCellsRange(col, row); if (!drawMergedCells) {return {col: range.c1, row: range.r1, index: range.r1 + "_" + range.c1};} if (col !== range.c1 || row !== range.r1) {return null;} } var colL = isMerged ? range.c1 : Math.max(colStart, col - ct.sideL); var colR = isMerged ? Math.min(range.c2, this.nColsCount - 1) : Math.min(colEnd, col + ct.sideR); var rowT = isMerged ? range.r1 : row; var rowB = isMerged ? Math.min(range.r2, this.nRowsCount - 1) : row; var isTrimmedR = !isMerged && colR !== col + ct.sideR; if (!(ct.angle || 0)) { if (!isMerged && !isWrapped) { this._eraseCellRightBorder(drawingCtx, colL, colR + (isTrimmedR ? 1 : 0), row, offsetX, offsetY); } } var x1 = this.cols[colL].left - offsetX; var y1 = this.rows[rowT].top - offsetY; var w = this.cols[colR].left + this.cols[colR].width - offsetX - x1; var h = this.rows[rowB].top + this.rows[rowB].height - offsetY - y1; var x2 = x1 + w - (isTrimmedR ? 0 : this.width_1px); var y2 = y1 + h - this.height_1px; var bl = !isMerged ? (y2 - this.rows[rowB].descender) : (y2 - ct.metrics.height + ct.metrics.baseline); var x1ct = isMerged ? x1 : this.cols[col].left - offsetX; var x2ct = isMerged ? x2 : x1ct + this.cols[col].width - this.width_1px; var textX = this._calcTextHorizPos(x1ct, x2ct, ct.metrics, ct.cellHA); var textY = this._calcTextVertPos(y1, y2, bl, ct.metrics, ct.cellVA); var textW = this._calcTextWidth(x1ct, x2ct, ct.metrics, ct.cellHA); // TODO : все в отдельный метод var xb1, yb1, wb, hb, bound, colLeft, colRight, sw, sx; if (drawingCtx) { if (ct.angle || 0) { xb1 = this.cols[col].left - offsetX; yb1 = this.rows[row].top - offsetY; wb = this.cols[col].width; hb = this.rows[row].height; sx = ct.textBound.sx + xb1; sw = ct.textBound.sw + xb1; bound = this.stringRender.getTransformBound(ct.angle, xb1, yb1, wb, hb, textW, ct.cellHA, ct.cellVA); bound.x = xb1; bound.y = yb1; if (90 === ct.angle || -90 === ct.angle) { // клип по ячейке ctx.AddClipRect (xb1, yb1, wb, hb); } else { // клип по строке ctx.AddClipRect (0, yb1, this.drawingCtx.getWidth(), h); if (!isMerged && !isWrapped) { colLeft = col; if (0 !== sx) { while (true) { if (0 == colLeft) break; if (bound.sx >= this.cols[colLeft].left) break; --colLeft; } } colRight = Math.min(col, this.nColsCount - 1); if (0 !== sw) { while (true) { ++colRight; if (colRight >= this.nColsCount) { --colRight; break; } if (sw <= this.cols[colRight].left) { --colRight; break; } } } colLeft = isMerged ? range.c1 : colLeft; colRight = isMerged ? Math.min(range.c2, this.nColsCount - 1) : colRight; this._eraseCellRightBorder(drawingCtx, colLeft, colRight + (isTrimmedR ? 1 : 0), row, offsetX, offsetY); } } this.stringRender.rotateAtPoint(drawingCtx, ct.angle, xb1, yb1); this.stringRender.restoreInternalState(ct.state).renderForPrint(drawingCtx, 0, 0, textW, ct.color); this.stringRender.resetTransform(drawingCtx); } else { ctx.AddClipRect (x1, y1, w, h); this.stringRender.restoreInternalState(ct.state).renderForPrint(drawingCtx, textX, textY, textW, ct.color); } ctx.RemoveClipRect(); } else { if (ct.angle || 0) { xb1 = this.cols[col].left - offsetX; yb1 = this.rows[row].top - offsetY; wb = this.cols[col].width; hb = this.rows[row].height; sx = ct.textBound.sx + xb1; sw = ct.textBound.sw + xb1; bound = this.stringRender.getTransformBound(ct.angle, xb1, yb1, wb, hb, textW, ct.cellHA, ct.cellVA); bound.x = xb1; bound.y = yb1; this.stringRender.bound = bound; //this.stringRender.fontNeedUpdate = true; if (90 === ct.angle || -90 === ct.angle) { // клип по ячейке ctx.save().beginPath().rect(xb1, yb1, wb, hb).clip(); } else { // клип по строке ctx.save().beginPath().rect(0, y1, this.drawingCtx.getWidth(), h).clip(); if (!isMerged && !isWrapped) { colLeft = col; if (0 !== sx) { while (true) { if (0 == colLeft) break; if (sx >= this.cols[colLeft].left) break; --colLeft; } } colRight = Math.min(col, this.nColsCount - 1); if (0 !== sw) { while (true) { ++colRight; if (colRight >= this.nColsCount) { --colRight; break; } if (sw <= this.cols[colRight].left) { --colRight; break; } } } colLeft = isMerged ? range.c1 : colLeft; colRight = isMerged ? Math.min(range.c2, this.nColsCount - 1) : colRight; this._eraseCellRightBorder(drawingCtx, colLeft, colRight + (isTrimmedR ? 1 : 0), row, offsetX, offsetY); } } this.stringRender.rotateAtPoint(undefined, ct.angle, xb1, yb1); this.stringRender.restoreInternalState(ct.state).render(0, 0, textW, ct.color); ctx.restore(); this.stringRender.resetTransform(undefined); } else { ctx.save().beginPath().rect(x1, y1, w, h).clip(); this.stringRender.restoreInternalState(ct.state).render(textX, textY, textW, ct.color); ctx.restore(); } } return null; }, /** Удаляет вертикальные границы ячейки, если текст выходит за границы и соседние ячейки пусты */ _eraseCellRightBorder: function (drawingCtx, colBeg, colEnd, row, offsetX, offsetY) { if (colBeg >= colEnd) {return;} var ctx = (drawingCtx) ? drawingCtx : this.drawingCtx; ctx.setFillStyle(this.settings.cells.defaultState.background); for (var col = colBeg; col < colEnd; ++col) { var c = this._getCell(col, row); var bg = c !== undefined ? c.getFill() : null; if (bg !== null) {continue;} ctx.fillRect( this.cols[col].left + this.cols[col].width - offsetX - this.width_1px, this.rows[row].top - offsetY, this.width_1px, this.rows[row].height - this.height_1px); } }, /** Рисует рамки для ячеек */ _drawCellsBorders: function (drawingCtx, range, mergedCellsStage, leftFieldInPt, topFieldInPt) { //TODO: использовать стили линий при рисовании границ if (range === undefined) { range = this.visibleRange; } var ctx = (drawingCtx) ? drawingCtx : this.drawingCtx; var offsetX = (leftFieldInPt) ? leftFieldInPt : this.cols[this.visibleRange.c1].left - this.cellsLeft; var offsetY = (topFieldInPt) ? topFieldInPt : this.rows[this.visibleRange.r1].top - this.cellsTop; var bc = undefined; // cached border color var color = undefined; // cached border color string function drawBorderLine(border, x1, y1, x2, y2, fdx, fdy) { if (border.s !== kcbNone && !border.isErased) { if (bc !== border.c) { bc = border.c; color = asc_n2css(bc); ctx.setStrokeStyle(color); } ctx.setLineWidth(border.w) .beginPath() .moveTo(x1, y1, fdx(1), fdy(1)) .lineTo(x2, y2, fdx(2), fdy(2)) .stroke(); } } // set clipping rect to cells area if (!drawingCtx) { ctx.save() .beginPath() .rect(this.cellsLeft, this.cellsTop, ctx.getWidth() - this.cellsLeft, ctx.getHeight() - this.cellsTop) .clip(); } for (var row = range.r1; row <= range.r2 && row < this.nRowsCount; ++row) { var isFirstRow = row === range.r1, isLastRow = row === range.r2, y1 = this.rows[row].top - offsetY, y2 = y1 + this.rows[row].height - this.height_1px, mc = undefined; for (var isMerged = false, col = range.c1; col <= range.c2 && col < this.nColsCount; ++col, isMerged = false) { var isFirstCol = col === range.c1; if (!mergedCellsStage) { mc = this._getMergedCellsRange(col, row); if (mc) { if ((col === mc.c1 || isFirstCol) && (row === mc.r1 || isFirstRow)) { this._drawCellsBorders(drawingCtx, mc, true, leftFieldInPt, topFieldInPt); } isMerged = true; col = mc.c2; // Проверка на выход за границы if (col >= this.nColsCount) col = this.nColsCount - 1; } } var x1 = this.cols[col].left - offsetX; var x2 = x1 + this.cols[col].width - this.width_1px; // var dd = this._getActiveBorder(col, row, kcbidDiagonalDown); var du = this._getActiveBorder(col, row, kcbidDiagonalUp); // var lb = isFirstCol ? this._getActiveBorder(col, row, kcbidLeft) : rb; var lbPrev = isFirstCol ? this._getActiveBorder(col, row - 1, kcbidLeft) : rbPrev; var lbNext = isFirstCol ? this._getActiveBorder(col, row + 1, kcbidLeft) : rbNext; var tbPrev = isFirstCol ? this._getActiveBorder(col - 1, row, kcbidTop) : tb; var bbPrev = isFirstCol ? this._getActiveBorder(col - 1, row, kcbidBottom) : bb; var tb = isFirstCol ? this._getActiveBorder(col, row, kcbidTop) : tbNext; var bb = isFirstCol ? this._getActiveBorder(col, row, kcbidBottom) : bbNext; // var rb = this._getActiveBorder(col, row, kcbidRight); var rbPrev = this._getActiveBorder(col, row - 1, kcbidRight); var rbNext = this._getActiveBorder(col, row + 1, kcbidRight); var tbNext = this._getActiveBorder(col + 1, row, kcbidTop); var bbNext = this._getActiveBorder(col + 1, row, kcbidBottom); if (isMerged || mergedCellsStage && row !== range.r1 && row !== range.r2&& col !== range.c1 && col !== range.c2) {continue;} var hasDD = dd.w > 0 && dd.s !== kcbNone; var hasDU = du.w > 0 && du.s !== kcbNone; if ( (hasDD || hasDU) && (!mergedCellsStage || row === range.r1 && col === range.c1) ) { ctx.save() .beginPath() .rect(x1 + this.width_1px * (lb.w < 1 ? -1 : (lb.w < 3 ? 0 : +1)), y1 + this.width_1px * (tb.w < 1 ? -1 : (tb.w < 3 ? 0 : +1)), this.cols[col].width + this.width_1px * ( -1 + (lb.w < 1 ? +1 : (lb.w < 3 ? 0 : -1)) + (rb.w < 1 ? +1 : (rb.w < 2 ? 0 : -1)) ), this.rows[row].height + this.height_1px * ( -1 + (tb.w < 1 ? +1 : (tb.w < 3 ? 0 : -1)) + (bb.w < 1 ? +1 : (bb.w < 2 ? 0 : -1)) )) .clip(); if (hasDD) { // draw diagonal line l,t - r,b drawBorderLine(dd, x1, y1, x2, y2, function (point) {return point === 1 ? -0.5 : 0.5;}, function (point) {return point === 1 ? -0.5 : 0.5;}); } if (hasDU) { // draw diagonal line l,b - r,t drawBorderLine(du, x1, y2, x2, y1, function (point) {return point === 1 ? -0.5 : 0.5;}, function (point) {return point === 1 ? 0.5 : -0.5;}); } ctx.restore(); // canvas context has just been restored, so destroy border color cache bc = undefined; } function drawVerticalBorder(bor, isLeft, tb1, tb2, bb1, bb2, x1, y1, x2, y2) { if (bor.w < 1 || bor.isErased) {return;} var tbw = this._calcMaxBorderWidth(tb1, tb2); // top border width var bbw = this._calcMaxBorderWidth(bb1, bb2); // bottom border width var dy1 = tbw > bor.w ? tbw - 1 : (tbw > 1 ? -1 : 0); var dy2 = bbw > bor.w ? -2 : (bbw > 2 ? 1 : 0); drawBorderLine(bor, x1, y1, x2, y2, function (point){return isLeft ? (bor.w !== 2 ? -0.5 : -1) : (bor.w !== 2 ? 0.5 : 0);}, function (point){return point === 1 ? -1 + dy1 : 1 + dy2;}); } function drawHorizontalBorderCorner(bor, isTop, borOther, vb, isLeft, vbOther, x, y) { if (vbOther.w <= vb.w || vbOther.w < bor.w) {return;} var borw = Math.max(bor.w, borOther.w); drawBorderLine(vbOther, x, y, x, y, function (point){return isLeft ? (vbOther.w !== 2 ? -0.5 : -1) : (vbOther.w !== 2 ? 0.5 : 0);}, function (point){return (point === 1 ? (borw > 1 ? -1 : 0) : (borw < 3 ? 1 : 2)) + (isTop ? -1 : 0);}); } function drawHorizontalBorder(bor, isTop, borPrev, borNext, drawCorners, lb, lbOther, rb, rbOther, x1, y1, x2, y2) { if (bor.w > 0) { var lbw = this._calcMaxBorderWidth(lb, lbOther); var rbw = this._calcMaxBorderWidth(rb, rbOther); var dx1 = bor.w > lbw ? (lbw > 1 ? -1 : 0) : (lbw > 2 ? 2 : 1); var dx2 = bor.w > rbw ? (rbw > 2 ? 1 : 0) : (rbw > 1 ? -2 : -1); drawBorderLine(bor, x1, y1, x2, y2, function (point){return point === 1 ? -1 + dx1 : 1 + dx2;}, function (point){return isTop ? (bor.w !== 2 ? -0.5 : -1) : (bor.w !== 2 ? 0.5 : 0);}); } if (drawCorners) { if (isFirstCol) { // draw left corner drawHorizontalBorderCorner.call(this, bor, isTop, borPrev, lb, true, lbOther, x1, y1); } // draw right corner drawHorizontalBorderCorner.call(this, bor, isTop, borNext, rb, false, rbOther, x2, y2); } } if (isFirstCol) { // draw left border drawVerticalBorder.call(this, lb, true, tb, tbPrev, bb, bbPrev, x1, y1, x1, y2); // Если мы в печати и печатаем первый столбец, то нужно напечатать бордеры if (lb.w >= 1 && false == lb.isErased && drawingCtx && 0 === col) { // Иначе они будут не такой ширины drawVerticalBorder.call(this, lb, false, tb, tbPrev, bb, bbPrev, x1, y1, x1, y2); } } if (!mergedCellsStage || col === range.c2) { // draw right border drawVerticalBorder.call(this, rb, false, tb, tbNext, bb, bbNext, x2, y1, x2, y2); } if (isFirstRow) { // draw top border drawHorizontalBorder.call(this, tb, true, tbPrev, tbNext, true, lb, lbPrev, rb, rbPrev, x1, y1, x2, y1); // Если мы в печати и печатаем первую строку, то нужно напечатать бордеры if (tb.w > 0 && drawingCtx && 0 === row) { // Иначе они будут не такой ширины var tmpFirstCol = isFirstCol; isFirstCol = false; drawHorizontalBorder.call(this, tb, false, tbPrev, tbNext, true, lb, lbPrev, rb, rbPrev, x1, y1, x2, y1); //drawHorizontalBorder.call(this, emptyCellBorder, false, emptyCellBorder, tb, true, emptyCellBorder, emptyCellBorder, emptyCellBorder, rb, x1, y1, x2, y1); isFirstCol = tmpFirstCol; } } if (!mergedCellsStage || row === range.r2) { // draw bottom border drawHorizontalBorder.call(this, bb, false, bbPrev, bbNext, isLastRow, lb, lbNext, rb, rbNext, x1, y2, x2, y2); } } } if (!drawingCtx) { ctx.restore(); } }, /** * Рисует выделение вокруг ячеек * @param {Asc.Range} range */ _drawSelection: function (range) { if (!this.isSelectionDialogMode) { this._drawCollaborativeElements(/*bIsDrawObjects*/true); this._drawSelectionRange(range); } else { this._drawSelectionRange(range); } }, _drawSelectionRange: function (range) { if ( asc["editor"].isStartAddShape ) return; if (c_oAscSelectionType.RangeMax === this.activeRange.type) { this.activeRange.c2 = this.cols.length - 1; this.activeRange.r2 = this.rows.length - 1; } else if (c_oAscSelectionType.RangeCol === this.activeRange.type) { this.activeRange.r2 = this.rows.length - 1; } else if (c_oAscSelectionType.RangeRow === this.activeRange.type) { this.activeRange.c2 = this.cols.length - 1; } if (!this.isSelectionDialogMode) range = this.activeRange.intersection(range !== undefined ? range : this.visibleRange); else range = this.copyOfActiveRange.intersection(range !== undefined ? range : this.visibleRange); // Copy fill Handle var aFH = null; // Вхождение range var aFHIntersection = null; if (this.activeFillHandle !== null) { // Мы в режиме автозаполнения aFH = this.activeFillHandle.clone(true); aFHIntersection = this.activeFillHandle.intersection(this.visibleRange); } if (!range && !aFHIntersection && !this.isFormulaEditMode && !this.activeMoveRange && !this.isChartAreaEditMode) { this._drawActiveHeaders(); this._drawGraphic(); return; } var ctx = this.overlayCtx; var opt = this.settings; var offsetX = this.cols[this.visibleRange.c1].left - this.cellsLeft; var offsetY = this.rows[this.visibleRange.r1].top - this.cellsTop; var arn = (!this.isSelectionDialogMode) ? this.activeRange.clone(true) : this.copyOfActiveRange.clone(true); var x1 = (range) ? (this.cols[range.c1].left - offsetX) : 0; var x2 = (range) ? (this.cols[range.c2].left + this.cols[range.c2].width - offsetX) : 0; var y1 = (range) ? (this.rows[range.r1].top - offsetY) : 0; var y2 = (range) ? (this.rows[range.r2].top + this.rows[range.r2].height - offsetY) : 0; var drawLeftSide = (range) ? (range.c1 === arn.c1) : false; var drawRightSide = (range) ? (range.c2 === arn.c2) : false; var drawTopSide = (range) ? (range.r1 === arn.r1) : false; var drawBottomSide = (range) ? (range.r2 === arn.r2) : false; var l, t, r, b, cr, offs, offs2; // Размеры "квадрата" автозаполнения var fillHandleWidth = 2 * this.width_2px + this.width_1px; var fillHandleHeight = 2 * this.height_2px + this.height_1px; // Координаты выделения для автозаполнения var xFH1 = 0; var xFH2 = 0; var yFH1 = 0; var yFH2 = 0; // Рисуем ли мы стороны автозаполнения var drawLeftFillHandle; var drawRightFillHandle; var drawTopFillHandle; var drawBottomFillHandle; // set clipping rect to cells area ctx.save() .beginPath() .rect(this.cellsLeft, this.cellsTop, ctx.getWidth() - this.cellsLeft, ctx.getHeight() - this.cellsTop) .clip(); // draw frame around cells range l = drawLeftSide ? (-2) : 0; r = drawRightSide ? (+1) : 0; t = drawTopSide ? (-2) : 0; b = drawBottomSide ? (+1) : 0; offs = -.5; offs2 = -.5; ctx.setStrokeStyle(opt.activeCellBorderColor) .setLineWidth(3) .beginPath(); if (aFHIntersection) { // Считаем координаты автозаполнения xFH1 = this.cols[aFHIntersection.c1].left - offsetX; xFH2 = this.cols[aFHIntersection.c2].left + this.cols[aFHIntersection.c2].width - offsetX; yFH1 = this.rows[aFHIntersection.r1].top - offsetY; yFH2 = this.rows[aFHIntersection.r2].top + this.rows[aFHIntersection.r2].height - offsetY; drawLeftFillHandle = aFHIntersection.c1 === aFH.c1; drawRightFillHandle = aFHIntersection.c2 === aFH.c2; drawTopFillHandle = aFHIntersection.r1 === aFH.r1; drawBottomFillHandle = aFHIntersection.r2 === aFH.r2; // Если мы не в нулевом состоянии, то рисуем обводку автозаполнения (толстой линией) if (aFHIntersection.c1 !== aFHIntersection.c2 || aFHIntersection.r1 !== aFHIntersection.r2 || 2 !== this.fillHandleArea) { if (drawTopFillHandle) {ctx.moveTo(xFH1, yFH1, l, offs).lineTo(xFH2, yFH1, r, offs);} if (drawBottomFillHandle) {ctx.moveTo(xFH1, yFH2, l, offs2).lineTo(xFH2, yFH2, r, offs2);} if (drawLeftFillHandle) {ctx.moveTo(xFH1, yFH1, offs, t).lineTo(xFH1, yFH2, offs, b);} if (drawRightFillHandle) {ctx.moveTo(xFH2, yFH1, offs2, t).lineTo(xFH2, yFH2, offs2, b);} } // Для некоторых вариантов областей нужно дорисовывать обводку для выделенной области switch (this.fillHandleArea){ case 1: switch(this.fillHandleDirection){ case 0: // Горизонтальный if (drawLeftSide) {ctx.moveTo(x1, y1, offs, t).lineTo(x1, y2, offs, b);} break; case 1: // Вертикальный if (drawTopSide) {ctx.moveTo(x1, y1, l, offs).lineTo(x2, y1, r, offs);} break; } break; case 2: // Для внутренней области нужны все обводки if (drawTopSide) {ctx.moveTo(x1, y1, l, offs).lineTo(x2, y1, r, offs);} if (drawBottomSide) {ctx.moveTo(x1, y2, l, offs2).lineTo(x2, y2, r, offs2);} if (drawLeftSide) {ctx.moveTo(x1, y1, offs, t).lineTo(x1, y2, offs, b);} if (drawRightSide) {ctx.moveTo(x2, y1, offs2, t).lineTo(x2, y2, offs2, b);} break; case 3: switch(this.fillHandleDirection){ case 0: // Горизонтальный if (range && aFH.c2 !== range.c2){ if (drawRightSide) {ctx.moveTo(x2, y1, offs2, t).lineTo(x2, y2, offs2, b);} } break; case 1: // Вертикальный if (range && aFH.r2 !== range.r2){ if (drawBottomSide) {ctx.moveTo(x1, y2, l, offs2).lineTo(x2, y2, r, offs2);} } break; } break; } ctx.stroke(); } else { // Автозаполнения нет, просто рисуем обводку if (drawTopSide) {ctx.moveTo(x1, y1, l, offs).lineTo(x2, y1, r, offs);} if (drawBottomSide) {ctx.moveTo(x1, y2, l, offs2).lineTo(x2 - fillHandleWidth, y2, r, offs2);} if (drawLeftSide) {ctx.moveTo(x1, y1, offs, t).lineTo(x1, y2, offs, b);} if (drawRightSide) {ctx.moveTo(x2, y1, offs2, t).lineTo(x2, y2 - fillHandleHeight, offs2, b);} } ctx.stroke(); // draw cells overlay if (range) { var lRect = x1 + (drawLeftSide ? this.width_2px : 0), rRect = x2 - (drawRightSide ? this.width_3px : this.width_1px), tRect = y1 + (drawTopSide ? this.height_2px : 0), bRect = y2 - (drawBottomSide ? this.width_3px : this.height_1px); ctx.setFillStyle( opt.activeCellBackground ) .fillRect(lRect, tRect, rRect - lRect, bRect - tRect); var firstCell = (!this.isSelectionDialogMode) ? this.activeRange : this.copyOfActiveRange; cr = this._getMergedCellsRange(firstCell.startCol, firstCell.startRow); // Получаем активную ячейку в выделении cr = range.intersection(cr !== undefined ? cr : asc_Range(firstCell.startCol, firstCell.startRow, firstCell.startCol, firstCell.startRow)); if (cr !== null) { ctx.save().beginPath().rect(lRect, tRect, rRect - lRect, bRect - tRect).clip(); var _l = this.cols[cr.c1].left - offsetX - this.width_1px, _r = this.cols[cr.c2].left + this.cols[cr.c2].width - offsetX, _t = this.rows[cr.r1].top - offsetY - this.height_1px, _b = this.rows[cr.r2].top + this.rows[cr.r2].height - offsetY; ctx.clearRect(_l, _t, _r - _l, _b - _t).restore(); } // Рисуем "квадрат" для автозаполнения (располагается "квадрат" в правом нижнем углу последней ячейки выделения) cr = range.intersection(asc_Range(range.c2, range.r2, range.c2, range.r2)); if (cr !== null) { this.fillHandleL = this.cols[cr.c1].left - offsetX + this.cols[cr.c1].width - this.width_1px - this.width_2px; this.fillHandleR = this.fillHandleL + fillHandleWidth; this.fillHandleT = this.rows[cr.r1].top - offsetY + this.rows[cr.r1].height - this.height_1px - this.height_2px; this.fillHandleB = this.fillHandleT + fillHandleHeight; ctx.setFillStyle (opt.activeCellBorderColor).fillRect(this.fillHandleL, this.fillHandleT, this.fillHandleR - this.fillHandleL, this.fillHandleB - this.fillHandleT); } } // draw fill handle select if (this.activeFillHandle !== null) { if (2 === this.fillHandleArea && (aFH.c1 !== aFH.c2 || aFH.r1 !== aFH.r2)){ // Для внутренней области мы должны "залить" еще и область автозаполнения var lFH = xFH1 + (drawLeftFillHandle ? this.width_2px : 0), rFH = xFH2 - (drawRightFillHandle ? this.width_3px : this.width_1px), tFH = yFH1 + (drawTopFillHandle ? this.height_2px : 0), bFH = yFH2 - (drawBottomFillHandle ? this.width_3px : this.height_1px); ctx.setFillStyle( opt.activeCellBackground ) .fillRect(lFH, tFH, rFH - lFH, bFH - tFH); } ctx.setStrokeStyle(opt.fillHandleBorderColorSelect).setLineWidth(1).beginPath(); if (aFH.c1 !== aFH.c2 || aFH.r1 !== aFH.r2 || 2 !== this.fillHandleArea) { // Рисуем обводку для области автозаполнения, если мы выделили что-то if (drawTopFillHandle) {ctx.moveTo(xFH1 + this.width_1px, yFH1, l, offs).lineTo(xFH2 - this.width_1px, yFH1, r, offs);} if (drawBottomFillHandle) {ctx.moveTo(xFH1 + this.width_1px, yFH2, l, offs2).lineTo(xFH2 - this.width_1px, yFH2, r, offs2);} if (drawLeftFillHandle) {ctx.moveTo(xFH1, yFH1 + this.height_1px, offs, t).lineTo(xFH1, yFH2 - this.height_1px, offs, b);} if (drawRightFillHandle) {ctx.moveTo(xFH2, yFH1 + this.height_1px, offs2, t).lineTo(xFH2, yFH2 - this.height_1px, offs2, b);} } if (2 === this.fillHandleArea){ // Если мы внутри, еще рисуем обводку для выделенной области if (drawTopSide) {ctx.moveTo(x1 + this.width_1px, y1, l, offs).lineTo(x2 - this.width_1px, y1, r, offs);} if (drawBottomSide) {ctx.moveTo(x1 + this.width_1px, y2, l, offs2).lineTo(x2 - this.width_1px, y2, r, offs2);} if (drawLeftSide) {ctx.moveTo(x1, y1 + this.height_1px, offs, t).lineTo(x1, y2 - this.height_1px, offs, b);} if (drawRightSide) {ctx.moveTo(x2, y1 + this.height_1px, offs2, t).lineTo(x2, y2 - this.height_1px, offs2, b);} } ctx.stroke(); } if (this.isFormulaEditMode) { this._drawFormulaRange(this.arrActiveFormulaRanges) } if (this.isChartAreaEditMode) { this._drawFormulaRange(this.arrActiveChartsRanges) } if (this.isSelectionDialogMode) { this._drawSelectRange(this.activeRange.clone(true)); } if (null !== this.activeMoveRange) { ctx.setStrokeStyle("rgba(0,0,0,1)") .setLineWidth(1) .beginPath(); var aActiveMoveRangeIntersection = this.activeMoveRange.intersection(this.visibleRange); if (aActiveMoveRangeIntersection) { var drawLeftSideMoveRange = aActiveMoveRangeIntersection.c1 === this.activeMoveRange.c1; var drawRightSideMoveRange = aActiveMoveRangeIntersection.c2 === this.activeMoveRange.c2; var drawTopSideMoveRange = aActiveMoveRangeIntersection.r1 === this.activeMoveRange.r1; var drawBottomSideMoveRange = aActiveMoveRangeIntersection.r2 === this.activeMoveRange.r2; var xMoveRange1 = this.cols[aActiveMoveRangeIntersection.c1].left - offsetX; var xMoveRange2 = this.cols[aActiveMoveRangeIntersection.c2].left + this.cols[aActiveMoveRangeIntersection.c2].width - offsetX; var yMoveRange1 = this.rows[aActiveMoveRangeIntersection.r1].top - offsetY; var yMoveRange2 = this.rows[aActiveMoveRangeIntersection.r2].top + this.rows[aActiveMoveRangeIntersection.r2].height - offsetY; if (drawTopSideMoveRange) {ctx.moveTo(xMoveRange1, yMoveRange1, -0.5, -0.5).lineTo(xMoveRange2, yMoveRange1, -0.5, -0.5);} if (drawBottomSideMoveRange) {ctx.moveTo(xMoveRange1, yMoveRange2, -0.5, -0.5).lineTo(xMoveRange2, yMoveRange2, -0.5, -0.5);} if (drawLeftSideMoveRange) {ctx.moveTo(xMoveRange1, yMoveRange1, -0.5, -0.5).lineTo(xMoveRange1, yMoveRange2, -0.5, -0.5);} if (drawRightSideMoveRange) {ctx.moveTo(xMoveRange2, yMoveRange1, -0.5, -0.5).lineTo(xMoveRange2, yMoveRange2, -0.5, -0.5);} } ctx.stroke(); } // restore canvas' original clipping range ctx.restore(); this._drawGraphic(); if ( !this.isChartAreaEditMode ) this.objectRender.raiseLayerDrawingObjects(); this._drawActiveHeaders(); }, _drawFormulaRange: function(arr){ var ctx = this.overlayCtx, opt = this.settings, offsetX = this.cols[this.visibleRange.c1].left - this.cellsLeft, offsetY = this.rows[this.visibleRange.r1].top - this.cellsTop; ctx.setLineWidth(1); for (var i in arr) { var arFormulaTmp = arr[i].clone(true); var aFormulaIntersection = arFormulaTmp.intersection(this.visibleRange); if (aFormulaIntersection) { ctx.beginPath() .setStrokeStyle(opt.formulaRangeBorderColor[i%opt.formulaRangeBorderColor.length]) .setFillStyle(opt.formulaRangeBorderColor[i%opt.formulaRangeBorderColor.length]); var drawLeftSideFormula = aFormulaIntersection.c1 === arFormulaTmp.c1; var drawRightSideFormula = aFormulaIntersection.c2 === arFormulaTmp.c2; var drawTopSideFormula = aFormulaIntersection.r1 === arFormulaTmp.r1; var drawBottomSideFormula = aFormulaIntersection.r2 === arFormulaTmp.r2; var xFormula1 = this.cols[aFormulaIntersection.c1].left - offsetX; var xFormula2 = this.cols[aFormulaIntersection.c2].left + this.cols[aFormulaIntersection.c2].width - offsetX; var yFormula1 = this.rows[aFormulaIntersection.r1].top - offsetY; var yFormula2 = this.rows[aFormulaIntersection.r2].top + this.rows[aFormulaIntersection.r2].height - offsetY; if (drawTopSideFormula) {ctx.moveTo(xFormula1, yFormula1, -0.5, -0.5).lineTo(xFormula2+0.5, yFormula1, -0.5, -0.5);} if (drawBottomSideFormula) {ctx.moveTo(xFormula1, yFormula2, -0.5, -0.5).lineTo(xFormula2, yFormula2, -0.5, -0.5);} if (drawLeftSideFormula) {ctx.moveTo(xFormula1, yFormula1, -0.5, -0.5).lineTo(xFormula1, yFormula2, -0.5, -0.5);} if (drawRightSideFormula) {ctx.moveTo(xFormula2, yFormula1, -0.5, -0.5).lineTo(xFormula2, yFormula2, -0.5, -0.5);} if( drawLeftSideFormula && drawTopSideFormula ) ctx.rect(xFormula1, yFormula1, 2, 2, -0.5, -0.5); if( drawRightSideFormula && drawTopSideFormula ) ctx.rect(xFormula2-3, yFormula1, 1.5, 2, -0.5, -0.5); if( drawRightSideFormula && drawBottomSideFormula) ctx.rect(xFormula2-3, yFormula2-3, 2, 2, -0.5, -0.5); if( drawLeftSideFormula && drawBottomSideFormula) ctx.rect(xFormula1, yFormula2-3, 2, 2, -0.5, -0.5); ctx.closePath() .stroke() .fill(); } } }, _drawSelectRange: function (oSelectRange) { var ctx = this.overlayCtx, offsetX = this.cols[this.visibleRange.c1].left - this.cellsLeft, offsetY = this.rows[this.visibleRange.r1].top - this.cellsTop; ctx.setLineWidth(2); var oSelectRangeIntersection = oSelectRange.intersection(this.visibleRange); if (oSelectRangeIntersection) { ctx.beginPath() .setStrokeStyle(c_oAscCoAuthoringOtherBorderColor); var drawLeftSideSelectRange = oSelectRangeIntersection.c1 === oSelectRange.c1; var drawRightSideSelectRange = oSelectRangeIntersection.c2 === oSelectRange.c2; var drawTopSideSelectRange = oSelectRangeIntersection.r1 === oSelectRange.r1; var drawBottomSideSelectRange = oSelectRangeIntersection.r2 === oSelectRange.r2; var xSelectRange1 = this.cols[oSelectRangeIntersection.c1].left - offsetX; var xSelectRange2 = this.cols[oSelectRangeIntersection.c2].left + this.cols[oSelectRangeIntersection.c2].width - offsetX; var ySelectRange1 = this.rows[oSelectRangeIntersection.r1].top - offsetY; var ySelectRange2 = this.rows[oSelectRangeIntersection.r2].top + this.rows[oSelectRangeIntersection.r2].height - offsetY; if (drawTopSideSelectRange) {ctx.dashLine(xSelectRange1, ySelectRange1, xSelectRange2, ySelectRange1, c_oAscCoAuthoringDottedWidth, c_oAscCoAuthoringDottedDistance);} if (drawBottomSideSelectRange) {ctx.dashLine(xSelectRange1, ySelectRange2, xSelectRange2, ySelectRange2, c_oAscCoAuthoringDottedWidth, c_oAscCoAuthoringDottedDistance);} if (drawLeftSideSelectRange) {ctx.dashLine(xSelectRange1, ySelectRange1, xSelectRange1, ySelectRange2, c_oAscCoAuthoringDottedWidth, c_oAscCoAuthoringDottedDistance);} if (drawRightSideSelectRange) {ctx.dashLine(xSelectRange2, ySelectRange1, xSelectRange2, ySelectRange2, c_oAscCoAuthoringDottedWidth, c_oAscCoAuthoringDottedDistance);} ctx.closePath().stroke().fill(); } }, _drawCollaborativeElements: function (bIsDrawObjects) { if (this.collaborativeEditing.getCollaborativeEditing()) { //this.overlayCtx.ctx.globalAlpha = 1; this._drawCollaborativeElementsMeOther (c_oAscLockTypes.kLockTypeMine, bIsDrawObjects); this._drawCollaborativeElementsMeOther (c_oAscLockTypes.kLockTypeOther, bIsDrawObjects); this._drawCollaborativeElementsAllLock (); } }, _drawCollaborativeElementsAllLock: function () { var ctx = this.overlayCtx; var currentSheetId = this.model.getId(); var nLockAllType = this.collaborativeEditing.isLockAllOther(currentSheetId); if (c_oAscMouseMoveLockedObjectType.None !== nLockAllType) { var styleColor = (c_oAscMouseMoveLockedObjectType.TableProperties === nLockAllType) ? c_oAscCoAuthoringLockTablePropertiesBorderColor : c_oAscCoAuthoringOtherBorderColor; ctx.setStrokeStyle(styleColor).setLineWidth(2).beginPath(); var offsetX = this.cols[this.visibleRange.c1].left - this.cellsLeft; var offsetY = this.rows[this.visibleRange.r1].top - this.cellsTop; var arAllRange = asc_Range (0, 0, gc_nMaxCol0, gc_nMaxRow0); var aFormulaIntersection = arAllRange.intersection(this.visibleRange); if (aFormulaIntersection) { var drawLeftSideFormula = aFormulaIntersection.c1 === arAllRange.c1; var drawRightSideFormula = aFormulaIntersection.c2 === arAllRange.c2; var drawTopSideFormula = aFormulaIntersection.r1 === arAllRange.r1; var drawBottomSideFormula = aFormulaIntersection.r2 === arAllRange.r2; var xFormula1 = this.cols[aFormulaIntersection.c1].left - offsetX; var xFormula2 = this.cols[aFormulaIntersection.c2].left + this.cols[aFormulaIntersection.c2].width - offsetX; var yFormula1 = this.rows[aFormulaIntersection.r1].top - offsetY; var yFormula2 = this.rows[aFormulaIntersection.r2].top + this.rows[aFormulaIntersection.r2].height - offsetY; if (drawTopSideFormula) {ctx.dashLine(xFormula1, yFormula1, xFormula2, yFormula1, c_oAscCoAuthoringDottedWidth, c_oAscCoAuthoringDottedDistance);} if (drawBottomSideFormula) {ctx.dashLine(xFormula1, yFormula2, xFormula2, yFormula2, c_oAscCoAuthoringDottedWidth, c_oAscCoAuthoringDottedDistance);} if (drawLeftSideFormula) {ctx.dashLine(xFormula1, yFormula1, xFormula1, yFormula2, c_oAscCoAuthoringDottedWidth, c_oAscCoAuthoringDottedDistance);} if (drawRightSideFormula) {ctx.dashLine(xFormula2, yFormula1, xFormula2, yFormula2, c_oAscCoAuthoringDottedWidth, c_oAscCoAuthoringDottedDistance);} } ctx.stroke(); ctx.restore(); } }, _drawCollaborativeElementsMeOther: function (type, bIsDrawObjects) { var ctx = this.overlayCtx; var offsetX = this.cols[this.visibleRange.c1].left - this.cellsLeft; var offsetY = this.rows[this.visibleRange.r1].top - this.cellsTop; var i; var currentSheetId = this.model.getId(); var styleColor = (c_oAscLockTypes.kLockTypeMine === type) ? c_oAscCoAuthoringMeBorderColor : c_oAscCoAuthoringOtherBorderColor; var arrayCells = (c_oAscLockTypes.kLockTypeMine === type) ? this.collaborativeEditing.getLockCellsMe(currentSheetId) : this.collaborativeEditing.getLockCellsOther(currentSheetId); if (c_oAscLockTypes.kLockTypeMine === type) { arrayCells = arrayCells.concat(this.collaborativeEditing.getArrayInsertColumnsBySheetId(currentSheetId)); arrayCells = arrayCells.concat(this.collaborativeEditing.getArrayInsertRowsBySheetId(currentSheetId)); } if (bIsDrawObjects) { var objectState = (c_oAscLockTypes.kLockTypeMine === type) ? c_oAscObjectLockState.Off : c_oAscObjectLockState.On; var arrayObjects = (c_oAscLockTypes.kLockTypeMine === type) ? this.collaborativeEditing.getLockObjectsMe(currentSheetId) : this.collaborativeEditing.getLockObjectsOther(currentSheetId); for (i = 0; i < arrayObjects.length; ++i) { this.objectRender.setGraphicObjectLockState(arrayObjects[i], (c_oAscLockTypes.kLockTypeMine === type) ? c_oAscLockTypes.kLockTypeMine : c_oAscLockTypes.kLockTypeOther); } } // set clipping rect to cells area ctx.save() .beginPath() .rect(this.cellsLeft, this.cellsTop, ctx.getWidth() - this.cellsLeft, ctx.getHeight() - this.cellsTop) .clip(); ctx.setStrokeStyle(styleColor).setLineWidth(2).beginPath(); for (i = 0; i < arrayCells.length; ++i) { var arFormulaTmp = asc_Range (arrayCells[i].c1, arrayCells[i].r1, arrayCells[i].c2, arrayCells[i].r2); var aFormulaIntersection = arFormulaTmp.intersection(this.visibleRange); if (aFormulaIntersection) { var drawLeftSideFormula = aFormulaIntersection.c1 === arFormulaTmp.c1; var drawRightSideFormula = aFormulaIntersection.c2 === arFormulaTmp.c2; var drawTopSideFormula = aFormulaIntersection.r1 === arFormulaTmp.r1; var drawBottomSideFormula = aFormulaIntersection.r2 === arFormulaTmp.r2; var xFormula1 = this.cols[aFormulaIntersection.c1].left - offsetX; var xFormula2 = this.cols[aFormulaIntersection.c2].left + this.cols[aFormulaIntersection.c2].width - offsetX; var yFormula1 = this.rows[aFormulaIntersection.r1].top - offsetY; var yFormula2 = this.rows[aFormulaIntersection.r2].top + this.rows[aFormulaIntersection.r2].height - offsetY; if (drawTopSideFormula) {ctx.dashLine(xFormula1, yFormula1, xFormula2, yFormula1, c_oAscCoAuthoringDottedWidth, c_oAscCoAuthoringDottedDistance);} if (drawBottomSideFormula) {ctx.dashLine(xFormula1, yFormula2, xFormula2, yFormula2, c_oAscCoAuthoringDottedWidth, c_oAscCoAuthoringDottedDistance);} if (drawLeftSideFormula) {ctx.dashLine(xFormula1, yFormula1, xFormula1, yFormula2, c_oAscCoAuthoringDottedWidth, c_oAscCoAuthoringDottedDistance);} if (drawRightSideFormula) {ctx.dashLine(xFormula2, yFormula1, xFormula2, yFormula2, c_oAscCoAuthoringDottedWidth, c_oAscCoAuthoringDottedDistance);} } if ( c_oAscLockTypes.kLockTypeOther == type ) this.cellCommentator.callLockComments(arrayCells[i]); } ctx.stroke(); ctx.restore(); }, _drawGraphic: function() { this.autoFilters.drawAutoF(this); this.cellCommentator.drawCommentCells(); }, cleanSelection: function () { var ctx = this.overlayCtx; var arn = this.activeRange.clone(true); var width = ctx.getWidth(); var height = ctx.getHeight(); var offsetX = this.cols[this.visibleRange.c1].left - this.cellsLeft; var offsetY = this.rows[this.visibleRange.r1].top - this.cellsTop; var x1 = this.cols[arn.c1].left - offsetX - this.width_2px; var x2 = this.cols[arn.c2].left + this.cols[arn.c2].width - offsetX + this.width_1px + /* Это ширина "квадрата" для автофильтра от границы ячейки */this.width_2px; var y1 = this.rows[arn.r1].top - offsetY - this.height_2px; var y2 = this.rows[arn.r2].top + this.rows[arn.r2].height - offsetY + this.height_1px + /* Это высота "квадрата" для автофильтра от границы ячейки */this.height_2px; var i; this._activateOverlayCtx(); this._cleanColumnHeaders(arn.c1, arn.c2); this._cleanRowHeades(arn.r1, arn.r2); this._deactivateOverlayCtx(); // Если есть активное автозаполнения, то нужно его тоже очистить if (this.activeFillHandle !== null) { var activeFillClone = this.activeFillHandle.clone(true); activeFillClone.normalize(); // Координаты для автозаполнения var xFH1 = this.cols[activeFillClone.c1].left - offsetX - this.width_2px; var xFH2 = this.cols[activeFillClone.c2].left + this.cols[activeFillClone.c2].width - offsetX + this.width_1px + this.width_2px; var yFH1 = this.rows[activeFillClone.r1].top - offsetY - this.height_2px; var yFH2 = this.rows[activeFillClone.r2].top + this.rows[activeFillClone.r2].height - offsetY + this.height_1px + this.height_2px; // Выбираем наибольший range для очистки x1 = Math.min(x1, xFH1); x2 = Math.max(x2, xFH2); y1 = Math.min(y1, yFH1); y2 = Math.max(y2, yFH2); } if (this.collaborativeEditing.getCollaborativeEditing ()) { var currentSheetId = this.model.getId(); var nLockAllType = this.collaborativeEditing.isLockAllOther(currentSheetId); if (c_oAscMouseMoveLockedObjectType.None !== nLockAllType) { this.overlayCtx.clear(); } else { var arrayElementsMe = this.collaborativeEditing.getLockCellsMe(currentSheetId); var arrayElementsOther = this.collaborativeEditing.getLockCellsOther(currentSheetId); var arrayElements = arrayElementsMe.concat (arrayElementsOther); arrayElements = arrayElements.concat(this.collaborativeEditing.getArrayInsertColumnsBySheetId(currentSheetId)); arrayElements = arrayElements.concat(this.collaborativeEditing.getArrayInsertRowsBySheetId(currentSheetId)); for (i = 0; i < arrayElements.length; ++i) { var arFormulaTmp = asc_Range (arrayElements[i].c1, arrayElements[i].r1, arrayElements[i].c2, arrayElements[i].r2); var aFormulaIntersection = arFormulaTmp.intersection(this.visibleRange); if (aFormulaIntersection) { // Координаты для автозаполнения var xCE1 = this.cols[aFormulaIntersection.c1].left - offsetX - this.width_2px; var xCE2 = this.cols[aFormulaIntersection.c2].left + this.cols[aFormulaIntersection.c2].width - offsetX + this.width_1px + this.width_2px; var yCE1 = this.rows[aFormulaIntersection.r1].top - offsetY - this.height_2px; var yCE2 = this.rows[aFormulaIntersection.r2].top + this.rows[aFormulaIntersection.r2].height - offsetY + this.height_1px + this.height_2px; // Выбираем наибольший range для очистки x1 = Math.min(x1, xCE1); x2 = Math.max(x2, xCE2); y1 = Math.min(y1, yCE1); y2 = Math.max(y2, yCE2); } } } } if (0 < this.arrActiveFormulaRanges.length) { for (i = 0; i < this.arrActiveFormulaRanges.length; ++i) { var activeFormula = this.arrActiveFormulaRanges[i].clone(true); activeFormula = this.visibleRange.intersection(activeFormula); if (null === activeFormula) { // это ссылка из формулы на еще не добавленный рэндж continue; } // Координаты для range формулы var xF1 = this.cols[activeFormula.c1].left - offsetX - this.width_2px; var xF2 = activeFormula.c2 > this.cols.length ? width : this.cols[activeFormula.c2].left + this.cols[activeFormula.c2].width - offsetX + this.width_1px; var yF1 = this.rows[activeFormula.r1].top - offsetY - this.height_2px; var yF2 = activeFormula.r2 > this.rows.length ? height : this.rows[activeFormula.r2].top + this.rows[activeFormula.r2].height - offsetY + this.height_1px; // Выбираем наибольший range для очистки x1 = Math.min(x1, xF1); x2 = Math.max(x2, xF2); y1 = Math.min(y1, yF1); y2 = Math.max(y2, yF2); } // Вышли из редактора, очистим массив if (false === this.isFormulaEditMode) { this.arrActiveFormulaRanges = []; } } if (0 < this.arrActiveChartsRanges.length) { for (i in this.arrActiveChartsRanges ) { var activeFormula = this.arrActiveChartsRanges[i].clone(true); activeFormula = this.visibleRange.intersection(activeFormula); if (null === activeFormula) { // это ссылка из формулы на еще не добавленный рэндж continue; } // Координаты для range формулы var xF1 = this.cols[activeFormula.c1].left - offsetX - this.width_2px; var xF2 = activeFormula.c2 > this.cols.length ? width : this.cols[activeFormula.c2].left + this.cols[activeFormula.c2].width - offsetX + this.width_1px; var yF1 = this.rows[activeFormula.r1].top - offsetY - this.height_2px; var yF2 = activeFormula.r2 > this.rows.length ? height : this.rows[activeFormula.r2].top + this.rows[activeFormula.r2].height - offsetY + this.height_1px; // Выбираем наибольший range для очистки x1 = Math.min(x1, xF1); x2 = Math.max(x2, xF2); y1 = Math.min(y1, yF1); y2 = Math.max(y2, yF2); } // Вышли из редактора, очистим массив // if (false === this.isFormulaEditMode) { // this.arrActiveFormulaRanges = []; // } } if (null !== this.activeMoveRange) { var activeMoveRangeClone = this.activeMoveRange.clone(true); // Увеличиваем, если выходим за область видимости // Critical Bug 17413 while ( !this.cols[activeMoveRangeClone.c2] ) { this.expandColsOnScroll(true); this._trigger("reinitializeScrollX"); } while ( !this.rows[activeMoveRangeClone.r2] ) { this.expandRowsOnScroll(true); this._trigger("reinitializeScrollY"); } // Координаты для перемещения диапазона var xMR1 = this.cols[activeMoveRangeClone.c1].left - offsetX - this.width_2px; var xMR2 = this.cols[activeMoveRangeClone.c2].left + this.cols[activeMoveRangeClone.c2].width - offsetX + this.width_1px + this.width_2px; var yMR1 = this.rows[activeMoveRangeClone.r1].top - offsetY - this.height_2px; var yMR2 = this.rows[activeMoveRangeClone.r2].top + this.rows[activeMoveRangeClone.r2].height - offsetY + this.height_1px + this.height_2px; // Выбираем наибольший range для очистки x1 = Math.min(x1, xMR1); x2 = Math.max(x2, xMR2); y1 = Math.min(y1, yMR1); y2 = Math.max(y2, yMR2); } if (null !== this.copyOfActiveRange) { // Координаты для перемещения диапазона var xCopyAr1 = this.cols[this.copyOfActiveRange.c1].left - offsetX - this.width_2px; var xCopyAr2 = this.cols[this.copyOfActiveRange.c2].left + this.cols[this.copyOfActiveRange.c2].width - offsetX + this.width_1px + this.width_2px; var yCopyAr1 = this.rows[this.copyOfActiveRange.r1].top - offsetY - this.height_2px; var yCopyAr2 = this.rows[this.copyOfActiveRange.r2].top + this.rows[this.copyOfActiveRange.r2].height - offsetY + this.height_1px + this.height_2px; // Выбираем наибольший range для очистки x1 = Math.min(x1, xCopyAr1); x2 = Math.max(x2, xCopyAr2); y1 = Math.min(y1, yCopyAr1); y2 = Math.max(y2, yCopyAr2); } ctx.save() .beginPath() .rect(this.cellsLeft, this.cellsTop, ctx.getWidth() - this.cellsLeft, ctx.getHeight() - this.cellsTop) .clip() .clearRect(x1, y1, x2 - x1, y2 - y1) .restore(); return this; }, updateSelection: function () { this.cleanSelection(); this._drawSelection(); }, // mouseX - это разница стартовых координат от мыши при нажатии и границы drawColumnGuides: function (col, x, y, mouseX) { var t = this; x *= asc_getcvt( 0/*px*/, 1/*pt*/, t._getPPIX() ); // Учитываем координаты точки, где мы начали изменение размера x += mouseX; var ctx = t.overlayCtx; var offsetX = t.cols[t.visibleRange.c1].left - t.cellsLeft; var x1 = t.cols[col].left - offsetX - this.width_1px; var h = ctx.getHeight(); ctx.clear(); t._drawSelection(); ctx.setFillPattern(t.ptrnLineDotted1) .fillRect(x1, 0, this.width_1px, h) .fillRect(x, 0, this.width_1px, h); }, // mouseY - это разница стартовых координат от мыши при нажатии и границы drawRowGuides: function (row, x, y, mouseY) { var t = this; y *= asc_getcvt( 0/*px*/, 1/*pt*/, t._getPPIY() ); // Учитываем координаты точки, где мы начали изменение размера y += mouseY; var ctx = t.overlayCtx; var offsetY = t.rows[t.visibleRange.r1].top - t.cellsTop; var y1 = t.rows[row].top - offsetY - this.height_1px; var w = ctx.getWidth(); ctx.clear(); t._drawSelection(); ctx.setFillPattern(t.ptrnLineDotted1) .fillRect(0, y1, w, this.height_1px) .fillRect(0, y, w, this.height_1px); }, // --- Cache --- _cleanCache: function (range) { var t = this, r, c, row, id, rid; function deleteIndex(range) { for (var r = range.r1; r <= range.r2 && r < t.rows.length; ++r) { for (var c = range.c1; c <= range.c2 && c < t.cols.length; ++c) { var id = t._getMergedCellIndex(c, r); delete t.cache.mergedCells.index[id]; } } } if (range === undefined) {range = t.activeRange.clone(true);} for (r = range.r1; r <= range.r2; ++r) { row = t.cache.rows[r]; for (c = range.c1; c <= range.c2; ++c) { if (row !== undefined) { if (row.columns[c]) {delete row.columns[c];} if (row.columnsWithText[c]) {delete row.columnsWithText[c];} if (row.erasedLB[c]) {delete row.erasedLB[c];} if (row.erasedRB[c-1]) {delete row.erasedRB[c-1];} } id = t._getMergedCellIndex(c, r); rid = t.cache.mergedCells.index[id]; if (rid !== undefined) { deleteIndex(t.cache.mergedCells.ranges[rid]); delete t.cache.mergedCells.ranges[rid]; } } if (row !== undefined) { if (row.erasedLB[c]) {delete row.erasedLB[c];} if (row.erasedRB[c-1]) {delete row.erasedRB[c-1];} if (row.columns) { if (row.columns[range.c1-1] && row.columns[range.c1-1].borders) {delete row.columns[range.c1-1].borders.r;} if (row.columns[range.c2+1] && row.columns[range.c2+1].borders) {delete row.columns[range.c2+1].borders.l;} } } } row = t.cache.rows[range.r1-1]; if (row !== undefined) { for (c = range.c1; c <= range.c2; ++c) { if (row.columns[c] && row.columns[c].borders) {delete row.columns[c].borders.b;} } } row = t.cache.rows[range.r2+1]; if (row !== undefined) { for (c = range.c1; c <= range.c2; ++c) { if (row.columns[c] && row.columns[c].borders) {delete row.columns[c].borders.t;} } } }, // ----- Cell text cache ----- /** Очищает кэш метрик текста ячеек */ _cleanCellsTextMetricsCache: function () { var s = this.cache.sectors = []; var vr = this.visibleRange; var h = vr.r2 + 1 - vr.r1; var rl = this.rows.length; var rc = asc_floor(rl / h) + (rl % h > 0 ? 1 : 0); var range = new asc_Range(0, 0, this.cols.length - 1, h - 1); var j; for (j = rc; j > 0; --j, range.r1 += h, range.r2 += h) { if (j === 1 && rl % h > 0) { range.r2 = rl - 1; } s.push(range.clone()); } }, /** * Обновляет общий кэш и кэширует метрики текста ячеек для указанного диапазона * @param {Asc.Range} range Диапазон кэширования текта */ _prepareCellTextMetricsCache: function (range) { var self = this, s = this.cache.sectors; if (s.length < 1) {return;} for (var i = 0; i < s.length; ) { if ( s[i].intersection(range) !== null ) { self._calcCellsTextMetrics(s[i]); s.splice(i, 1); continue; } ++i; } }, /** * Кэширует метрики текста для диапазона ячеек * @param {Asc.Range} range description */ _calcCellsTextMetrics: function (range) { if (range === undefined) { range = asc_Range(0, 0, this.cols.length - 1, this.rows.length - 1); } for (var row = range.r1; row <= range.r2; ++row) { for (var col = range.c1; col <= range.c2; ++col) { col = this._addCellTextToCache(col, row); } } if (range.r1 <= range.r2) { this._updateRowPositions(); this._calcVisibleRows(); } this.isChanged = false; }, _fetchRowCache: function (row) { var rc = this.cache.rows[row] = ( this.cache.rows[row] || new CacheElement() ); return rc; }, _fetchCellCache: function (col, row) { var r = this._fetchRowCache(row), c = r.columns[col] = ( r.columns[col] || {} ); return c; }, _fetchCellCacheText: function (col, row) { var r = this._fetchRowCache(row), cwt = r.columnsWithText[col] = ( r.columnsWithText[col] || {} ); return cwt; }, _getRowCache: function (row) { return this.cache.rows[row]; }, _getCellCache: function (col, row) { var r = this.cache.rows[row]; return r ? r.columns[col] : undefined; }, _getCellTextCache: function (col, row, dontLookupMergedCells) { var r = this.cache.rows[row], c = r ? r.columns[col] : undefined; if (c && c.text) { return c.text; } else if (!dontLookupMergedCells) { var range = this._getMergedCellsRange(col, row); return range !== undefined ? this._getCellTextCache(range.c1, range.r1, true) : undefined; } return undefined; }, _addCellTextToCache: function (col, row, canChangeColWidth) { var self = this; function isFixedWidthCell(frag) { for (var i = 0; i < frag.length; ++i) { var f = frag[i].format; if (f && f.repeat) {return true;} } return false; } function truncFracPart(frag) { var s = frag.reduce(function (prev,val) {return prev + val.text;}, ""); // Проверка scientific format if (s.search(/E/i) >= 0) { return frag; } // Поиск десятичной точки var pos = s.search(/[,\.]/); if (pos >= 0) { frag[0].text = s.slice(0, pos); frag.splice(1, frag.length - 1); } return frag; } function makeFnIsGoodNumFormat(flags, width) { return function (str) { return self.stringRender.measureString(str, flags, width).width <= width; }; } function changeColWidth(col, width, pad) { var cc = Math.min(self._colWidthToCharCount(width + pad), /*max col width*/255); var modelw = self._charCountToModelColWidth(cc, true); var colw = self._calcColWidth(modelw); if (colw.width > self.cols[col].width) { self.cols[col].width = colw.width; self.cols[col].innerWidth = colw.innerWidth; self.cols[col].charCount = colw.charCount; History.Create_NewPoint(); History.SetSelection(null, true); History.StartTransaction(); // Выставляем, что это bestFit self.model.setColBestFit (true, modelw, col, col); History.EndTransaction(); self._updateColumnPositions(); self.isChanged = true; } } var c = this._getCell(col, row); if (c === undefined) {return col;} var bUpdateScrollX = false; var bUpdateScrollY = false; // Проверка на увеличение колличества столбцов if (col >= this.cols.length) { bUpdateScrollX = this.expandColsOnScroll(/*isNotActive*/ false, /*updateColsCount*/ true); } // Проверка на увеличение колличества строк if (row >= this.rows.length) { bUpdateScrollY = this.expandRowsOnScroll(/*isNotActive*/ false, /*updateRowsCount*/ true); } if (bUpdateScrollX && bUpdateScrollY) { this._trigger("reinitializeScroll"); } else if (bUpdateScrollX) { this._trigger("reinitializeScrollX"); } else if (bUpdateScrollY) { this._trigger("reinitializeScrollY"); } // Range для замерженной ячейки var range = null; var fl = this._getCellFlags(c); var fMergedColumns = false; // Замержены ли колонки (если да, то автоподбор ширины не должен работать) var fMergedRows = false; // Замержены ли строки (если да, то автоподбор высоты не должен работать) if (fl.isMerged) { range = this._getMergedCellsRange(col, row); if (range === undefined) { // got uncached merged cells, redirect it range = this._fetchMergedCellsRange(col, row); this._addCellTextToCache(range.c1, range.r1, canChangeColWidth); return col; } if (col !== range.c1 || row !== range.r1) {return range.c2;} // skip other merged cell from range if (range.c1 !== range.c2) fMergedColumns = true; if (range.r1 !== range.r2) fMergedRows = true; } if (this._isCellEmpty(c)) {return col;} var dDigitsCount = 0; var colWidth = 0; var cellType = c.getType(); var isNumberFormat = (!cellType || CellValueType.Number === cellType); var numFormatStr = c.getNumFormatStr(); var pad = this.width_padding * 2 + this.width_1px; var sstr, sfl, stm; if (!this.cols[col].isCustomWidth && isNumberFormat && !fMergedColumns && (c_oAscCanChangeColWidth.numbers === canChangeColWidth || c_oAscCanChangeColWidth.all === canChangeColWidth)) { colWidth = this.cols[col].innerWidth; // Измеряем целую часть числа sstr = c.getValue2(gc_nMaxDigCountView, function(){return true;}); //todo убрать Asc.clone на другой clone или изменить truncFracPart, чтобы не изменяла исходный массив if ("General" === numFormatStr) {sstr = truncFracPart(Asc.clone(sstr));} sfl = asc_clone(fl); sfl.wrapText = false; stm = this._roundTextMetrics( this.stringRender.measureString(sstr, sfl, colWidth) ); // Если целая часть числа не убирается в ячейку, то расширяем столбец if (stm.width > colWidth) {changeColWidth(col, stm.width, pad);} // Обновленная ячейка dDigitsCount = this.cols[col].charCount; colWidth = this.cols[col].innerWidth; } else if (null === range) { // Обычная ячейка dDigitsCount = this.cols[col].charCount; colWidth = this.cols[col].innerWidth; // подбираем ширину if (!this.cols[col].isCustomWidth && !fMergedColumns && !fl.wrapText && c_oAscCanChangeColWidth.all === canChangeColWidth) { sstr = c.getValue2(gc_nMaxDigCountView, function(){return true;}); stm = this._roundTextMetrics( this.stringRender.measureString(sstr, fl, colWidth) ); if (stm.width > colWidth) { changeColWidth(col, stm.width, pad); // Обновленная ячейка dDigitsCount = this.cols[col].charCount; colWidth = this.cols[col].innerWidth; } } } else { // Замерженная ячейка, нужна сумма столбцов for (var i = range.c1; i <= range.c2 && i < this.nColsCount; ++i) { colWidth += this.cols[i].width; } colWidth -= pad; dDigitsCount = gc_nMaxDigCountView; } // ToDo dDigitsCount нужно рассчитывать исходя не из дефалтового шрифта и размера, а исходя из текущего шрифта и размера ячейки var str = c.getValue2(dDigitsCount, makeFnIsGoodNumFormat(fl, colWidth)); var ha = c.getAlignHorizontalByValue().toLowerCase(); var va = c.getAlignVertical().toLowerCase(); var maxW = fl.wrapText || fl.shrinkToFit || fl.isMerged || isFixedWidthCell(str) ? this._calcMaxWidth(col, row, fl.isMerged) : undefined; var tm = this._roundTextMetrics( this.stringRender.measureString(str, fl, maxW) ); var cto = (fl.isMerged || fl.wrapText) ? { maxWidth: maxW - this.cols[col].innerWidth + this.cols[col].width, leftSide: 0, rightSide: 0 } : this._calcCellTextOffset(col, row, ha, tm.width); // check right side of cell text and append columns if it exceeds existing cells borders if (!fl.isMerged) { var rside = this.cols[col - cto.leftSide].left + tm.width; var lc = this.cols[this.cols.length - 1]; if (rside > lc.left + lc.width) { this._appendColumns(rside); cto = this._calcCellTextOffset(col, row, ha, tm.width); } } var oFontColor = c.getFontcolor(); if(null != oFontColor) oFontColor = oFontColor.getRgb(); var textBound = {}; if (c.getAngle() || 0) { textBound = this.stringRender.getTransformBound(c.getAngle(), 0, 0, this.cols[col].width, this.rows[row].height, tm.width, ha, va); } this._fetchCellCache(col, row).text = { state : this.stringRender.getInternalState(), flags : fl, color : (oFontColor || this.settings.cells.defaultState.color), metrics : tm, cellW : cto.maxWidth, cellHA : ha, cellVA : va, sideL : cto.leftSide, sideR : cto.rightSide, cellType: cellType, isFormula: c.getFormula().length > 0, angle : c.getAngle(), textBound : textBound }; this._fetchCellCacheText(col, row).hasText = true; if (cto.leftSide !== 0 || cto.rightSide !== 0) { this._addErasedBordersToCache(col - cto.leftSide, col + cto.rightSide, row); } // update row's descender if (va !== kvaTop && va !== kvaCenter && !fl.isMerged) { this.rows[row].descender = Math.max(this.rows[row].descender, tm.height - tm.baseline); } // update row's height if (!this.rows[row].isCustomHeight) { // Замерженная ячейка (с 2-мя или более строками) не влияет на высоту строк! if (!fMergedRows) { this.rows[row].height = Math.min(this.maxRowHeight, Math.max(this.rows[row].height, tm.height)); if (!this.rows[row].isDefaultHeight) { this.model.setRowHeight(this.rows[row].height + this.height_1px, row, row); } this.isChanged = true; } } // TODO: отступы if (c.getAngle() || 0) { if (this.isChanged) { if (textBound) { if (this.rows[row].height < textBound.height) { this.rows[row].height = Math.max(this.rows[row].height, textBound.height); if (!this.rows[row].isDefaultHeight) { this.model.setRowHeight(this.rows[row].height + this.height_1px, row, row); } } this.isChanged = true; } } } return col; }, _calcMaxWidth: function (col, row, isMerged) { if (!isMerged) {return this.cols[col].innerWidth;} var range = this._getMergedCellsRange(col, row), width = this.cols[range.c1].innerWidth; for (var c = range.c1 + 1; c <= range.c2 && c < this.nColsCount; ++c) { width += this.cols[c].width; } return width; }, _calcCellTextOffset: function (col, row, textAlign, textWidth) { var sideL = [0], sideR = [0], i; var maxWidth = this.cols[col].width; var ls = 0, rs = 0; var pad = this.settings.cells.padding * asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIX() ); var textW = textAlign === khaCenter ? (textWidth + maxWidth) * 0.5 : textWidth + pad; if (textAlign === khaRight || textAlign === khaCenter) { sideL = this._calcCellsWidth(col, 0, row); // condition (sideL.lenght >= 1) is always true for (i = 0; i < sideL.length && textW > sideL[i]; ++i) {/* do nothing */} ls = i !== sideL.length ? i : i - 1; } if (textAlign !== khaRight) { sideR = this._calcCellsWidth(col, this.cols.length - 1, row); // condition (sideR.lenght >= 1) is always true for (i = 0; i < sideR.length && textW > sideR[i]; ++i) {/* do nothing */} rs = i !== sideR.length ? i : i - 1; } if (textAlign === khaCenter) { maxWidth = (sideL[ls] - sideL[0]) + sideR[rs]; } else { maxWidth = textAlign === khaRight ? sideL[ls] : sideR[rs]; } return { maxWidth: maxWidth, leftSide: ls, rightSide: rs }; }, _calcCellsWidth: function (colBeg, colEnd, row) { var inc = colBeg <= colEnd ? 1 : -1, res = []; for (var i = colBeg; (colEnd - i) * inc >= 0; i += inc) { if ( i !== colBeg && !this._isCellEmptyOrMerged(i, row) ) {break;} res.push(this.cols[i].width); if (res.length > 1) {res[res.length - 1] += res[res.length - 2];} } return res; }, // Ищет текст в строке (columnsWithText - это колонки, в которых есть текст) _findSourceOfCellText: function (col, row) { var r = this._getRowCache(row); if (r) { for (var i in r.columnsWithText) { if (!r.columns[i] || 0 === this.cols[i].width) {continue;} var ct = r.columns[i].text; if (!ct) {continue;} i = parseInt(i); var lc = i - ct.sideL, rc = i + ct.sideR; if (col >= lc && col <= rc) {return i;} } } return -1; }, // ----- Merged cells cache ----- _getMergedCellIndex: function (col, row) { return row + "-" + col; }, _getMergedCellsRange: function (col, row) { var index = this._getMergedCellIndex(col, row); var rangeId = this.cache.mergedCells.index[index]; return rangeId !== undefined ? this.cache.mergedCells.ranges[rangeId] : undefined; }, _fetchMergedCellsRange: function (col, row) { var index, rangeId, mc = this._getMergedCellsRange(col, row); if (mc) {return mc;} this._addMergedCellsRange(col, row); index = this._getMergedCellIndex(col, row); rangeId = this.cache.mergedCells.index[index]; if (rangeId === undefined) { throw "Error: can not get merged cell range (col=" + col + ", row=" + row + ")"; } return this.cache.mergedCells.ranges[rangeId]; }, _addMergedCellsRange: function (col, row) { var range = this._getCell(col, row).hasMerged(), rangeId, c, r; this.cache.mergedCells.ranges.push( asc_Range(range.c1, range.r1, range.c2, range.r2) ); rangeId = this.cache.mergedCells.ranges.length - 1; for (r = range.r1; r <= range.r2 && r < this.nRowsCount; ++r) { for (c = range.c1; c <= range.c2 && c < this.nColsCount; ++c) { this.cache.mergedCells.index[ this._getMergedCellIndex(c, r) ] = rangeId; } } }, _isMergedCells: function (range) { return range.isEqual( this._getMergedCellsRange(range.c1, range.r1) ); }, // Обновляем массив мерженных индексов для новых строк _updateMergedCellsRange: function (updateRange) { for (var key in this.cache.mergedCells.ranges) { var tmpRange = this.cache.mergedCells.ranges[key]; var bIsUpdate = false; var _r = tmpRange.r1, _c = tmpRange.c1; if (tmpRange.c1 < updateRange.c1 && tmpRange.c2 > updateRange.c2) { bIsUpdate = true; _c = updateRange.c1; } if (tmpRange.r1 < updateRange.r1 && tmpRange.r2 > updateRange.r2) { bIsUpdate = true; _r = updateRange.r1; } if (bIsUpdate) { for (var r = _r; r <= tmpRange.r2 && r < this.nRowsCount; ++r) { for (var c = _c; c <= tmpRange.c2 && c < this.nColsCount; ++c) { this.cache.mergedCells.index[ this._getMergedCellIndex(c, r) ] = key; } } } } }, // ----- Cell borders cache ----- _addErasedBordersToCache: function (colBeg, colEnd, row) { var rc = this._fetchRowCache(row); for (var col = colBeg; col < colEnd; ++col) { rc.erasedRB[col] = true; rc.erasedLB[col + 1] = true; } }, _isLeftBorderErased: function (col, row) { return this._fetchRowCache(row).erasedLB[col] === true; }, _isRightBorderErased: function (col, row) { return this._fetchRowCache(row).erasedRB[col] === true; }, _getBorderPropById: function (border, border_id) { var border_prop = undefined; switch (border_id) { case kcbidLeft: border_prop = border.l; break; case kcbidRight: border_prop = border.r; break; case kcbidTop: border_prop = border.t; break; case kcbidBottom: border_prop = border.b; break; case kcbidDiagonal: border_prop = border.d; break; case kcbidDiagonalDown: border_prop = border.dd; break; case kcbidDiagonalUp: border_prop = border.du; break; } return border_prop; }, _getBordersCache: function (col, row) { var self = this; if(col < 0 || row < 0) { return { l: new CellBorder(), r: new CellBorder(), t: new CellBorder(), b: new CellBorder(), dd: new CellBorder(), du: new CellBorder() }; } function makeBorder(border, type, isActive) { return new CellBorder( self._getBorderPropById( border, type ).s, self._getBorderPropById( border, type ).c, self._calcBorderWidth( self._getBorderPropById( border, type ) ), type === kcbidLeft ? self._isLeftBorderErased(col, row) : (type === kcbidRight ? self._isRightBorderErased(col, row) : false), isActive !== undefined ? isActive : false); } var cc = self._fetchCellCache(col, row), cb = cc.borders = ( cc.borders || {} ), mc = this._getMergedCellsRange(col, row); if (!cb.l || !cb.r || !cb.t || !cb.b || !cb.dd || !cb.du) { var b = self._getVisibleCell(col, row).getBorder(); if (!cb.l) {cb.l = !mc || col === mc.c1 ? makeBorder(b, kcbidLeft) : new CellBorder();} if (!cb.r) {cb.r = !mc || col === mc.c2 ? makeBorder(b, kcbidRight) : new CellBorder();} if (!cb.t) {cb.t = !mc || row === mc.r1 ? makeBorder(b, kcbidTop) : new CellBorder();} if (!cb.b) {cb.b = !mc || row === mc.r2 ? makeBorder(b, kcbidBottom) : new CellBorder();} if (!cb.dd) { cb.dd = !mc || col === mc.c1 && row === mc.r1 ? makeBorder(b, kcbidDiagonal, true) : new CellBorder(); if (!b.dd) {cb.dd.w = 0;} } if (!cb.du) { cb.du = !mc || col === mc.c1 && row === mc.r1 ? makeBorder(b, kcbidDiagonal, true) : new CellBorder(); if (!b.du) {cb.du.w = 0;} } } return cb; }, _getActiveBorder: function (col, row, type) { var bor = this._getBordersCache(col, row); var border = this._getBorderPropById(bor, type); function calcActiveBorder(prev, next) { var ab = next && (next.s !== kcbNone || !prev) ? next : prev; if (prev && prev !== ab) { prev.s = ab.s; prev.c = ab.c; prev.w = ab.w; prev.isActive = true; } if (next && next !== ab) { next.s = ab.s; next.c = ab.c; next.w = ab.w; next.isActive = true; } ab.isActive = true; return ab; } if (!border.isActive && !border.isErased) { var side = undefined; switch (type) { case kcbidLeft: side = this._getBordersCache(col - 1, row).r; calcActiveBorder(side, bor.l); break; case kcbidRight: side = this._getBordersCache(col + 1, row).l; calcActiveBorder(bor.r, side); break; case kcbidTop: side = this._getBordersCache(col, row - 1).b; calcActiveBorder(side, bor.t); break; case kcbidBottom: side = this._getBordersCache(col, row + 1).t; calcActiveBorder(bor.b, side); break; } } return this._getBorderPropById(bor, type); }, _calcMaxBorderWidth: function (b1, b2) { return Math.max(b1.isErased ? 0 : b1.w, b2.isErased ? 0 : b2.w); }, // ----- Cells utilities ----- /** * Возвращает заголовок колонки по индексу * @param {Number} col Индекс колонки * @return {String} */ _getColumnTitle: function (col) { var q = col < 26 ? undefined : asc_floor(col / 26) - 1; var r = col % 26; var text = String.fromCharCode( ("A").charCodeAt(0) + r ); return col < 26 ? text : this._getColumnTitle(q) + text; }, /** * Возвращает заголовок строки по индексу * @param {Number} row Индекс строки * @return {String} */ _getRowTitle: function (row) { return "" + (row + 1); }, /** * Возвращает заголовок ячейки по индексу * @param {Number} col Индекс колонки * @param {Number} row Индекс строки * @return {String} */ _getCellTitle: function (col, row) { return this._getColumnTitle(col) + this._getRowTitle(row); }, /** * Возвращает ячейку таблицы (из Worksheet) * @param {Number} col Индекс колонки * @param {Number} row Индекс строки * @return {Range} */ _getCell: function (col, row) { this.nRowsCount = Math.max(this.model.getRowsCount() , this.rows.length); this.nColsCount = Math.max(this.model.getColsCount(), this.cols.length); if ( col < 0 || col >= this.nColsCount || row < 0 || row >= this.nRowsCount ) { return undefined; } return this.model.getCell3(row, col); }, _getVisibleCell: function (col, row) { return this.model.getCell3(row, col); }, _getCellFlags: function (col, row) { var c = row !== undefined ? this._getCell(col, row) : col; var fl = {wrapText: false, shrinkToFit: false, isMerged: false, textAlign: kcbNone}; if (c !== undefined) { fl.wrapText = c.getWrap(); fl.shrinkToFit = c.getShrinkToFit(); fl.isMerged = c.hasMerged() !== null; fl.textAlign = c.getAlignHorizontalByValue().toLowerCase(); } return fl; }, _isCellEmpty: function (col, row) { var c = row !== undefined ? this._getCell(col, row) : col; return c === undefined || c.getValue().search(/[^ ]/) < 0; }, _isCellEmptyOrMerged: function (col, row) { var c = row !== undefined ? this._getCell(col, row) : col; if (undefined === c) return true; var fl = this._getCellFlags(c); if (fl.isMerged) return false; return c.getValue().search(/[^ ]/) < 0; }, _isCellEmptyOrMergedOrBackgroundColorOrBorders: function (col, row) { var c = row !== undefined ? this._getCell(col, row) : col; if (undefined === c) return true; var fl = this._getCellFlags(c); if (fl.isMerged) return false; var bg = c.getFill(); if (null !== bg) return false; var cb = c.getBorder(); if ((cb.l && kcbNone !== cb.l.s) || (cb.r && kcbNone !== cb.r.s) || (cb.t && kcbNone !== cb.t.s) || (cb.b && kcbNone !== cb.b.s) || (cb.dd && kcbNone !== cb.dd.s) || (cb.du && kcbNone !== cb.du.s)) return false; return c.getValue().search(/[^ ]/) < 0; }, _isThinBorder: function (bs) { return $.inArray(bs, kcbThinBorders) >= 0; }, _isMediumBorder: function (bs) { return $.inArray(bs, kcbMediumBorders) >= 0; }, _isThickBorder: function (bs) { return $.inArray(bs, kcbThickBorders) >= 0; }, _calcBorderWidth: function (b) { var s = b.s; return b === undefined ? 0 : ( this._isThinBorder(s) ? 1 : ( this._isMediumBorder(s) ? 2 : ( this._isThickBorder(s) ? 3 : 0))); }, _getRange: function (c1, r1, c2, r2) { return this.model.getRange3(r1, c1, r2, c2); }, _selectColumnsByRange: function () { var ar = this.activeRange; if (c_oAscSelectionType.RangeMax === ar.type) return; else { this.cleanSelection(); if (c_oAscSelectionType.RangeRow === ar.type) { ar.assign(0, 0, this.cols.length - 1, this.rows.length - 1); ar.type = c_oAscSelectionType.RangeMax; } else { ar.type = c_oAscSelectionType.RangeCol; ar.assign(ar.c1, 0, ar.c2, this.rows.length - 1); } this._drawSelection(); } }, _selectRowsByRange: function () { var ar = this.activeRange; if (c_oAscSelectionType.RangeMax === ar.type) return; else { this.cleanSelection(); if (c_oAscSelectionType.RangeCol === ar.type) { ar.assign(0, 0, this.cols.length - 1, this.rows.length - 1); ar.type = c_oAscSelectionType.RangeMax; } else { ar.type = c_oAscSelectionType.RangeRow; ar.assign(0, ar.r1, this.cols.length - 1, ar.r2); } this._drawSelection(); } }, /** * Возвращает true, если диапазон больше видимой области, и операции над ним могут привести к задержкам * @param {Asc.Range} range Диапазон для проверки * @returns {Boolean} */ _isLargeRange: function (range) { var vr = this.visibleRange; return range.c2 - range.c1 + 1 > (vr.c2 - vr.c1 + 1) * 3 || range.r2 - range.r1 + 1 > (vr.r2 - vr.r1 + 1) * 3; }, /** * Возвращает true, если диапазон состоит из одной ячейки * @param {Asc.Range} range Диапазон * @returns {Boolean} */ _rangeIsSingleCell: function (range) { return range.c1 === range.c2 && range.r1 === range.r2; }, drawDepCells : function(){ var ctx = this.overlayCtx, _cc = this.cellCommentator, c,node, that = this; ctx.clear(); this._drawSelection(); function draw_arrow(context, fromx, fromy, tox, toy) { var headlen = 9, showArrow = tox > that.getCellLeft(0, 0) && toy > that.getCellTop(0, 0), dx = tox - fromx, dy = toy - fromy, tox = tox > that.getCellLeft(0, 0)? tox: that.getCellLeft(0, 0), toy = toy > that.getCellTop(0, 0)? toy: that.getCellTop(0, 0), angle = Math.atan2(dy, dx), _a = Math.PI / 18; context.save() .setLineWidth(1) .beginPath() .moveTo(_cc.pxToPt(fromx), _cc.pxToPt(fromy),-0.5,-0.5) .lineTo(_cc.pxToPt(tox), _cc.pxToPt(toy),-0.5,-0.5) // .dashLine(_cc.pxToPt(fromx-.5), _cc.pxToPt(fromy-.5), _cc.pxToPt(tox-.5), _cc.pxToPt(toy-.5), 15, 5) if( showArrow ) context .moveTo( _cc.pxToPt(tox - headlen * Math.cos(angle - _a)), _cc.pxToPt(toy - headlen * Math.sin(angle - _a)),-0.5,-0.5) .lineTo(_cc.pxToPt(tox), _cc.pxToPt(toy),-0.5,-0.5) .lineTo( _cc.pxToPt(tox - headlen * Math.cos(angle + _a)), _cc.pxToPt(toy - headlen * Math.sin(angle + _a)),-0.5,-0.5) .lineTo( _cc.pxToPt(tox - headlen * Math.cos(angle - _a)), _cc.pxToPt(toy - headlen * Math.sin(angle - _a)),-0.5,-0.5) context .setStrokeStyle("#0000FF") .setFillStyle("#0000FF") .stroke() .fill() .closePath() .restore(); } function gCM(_this,col,row){ var metrics = { top: 0, left: 0, width: 0, height: 0, result: false }; // px var fvr = _this.getFirstVisibleRow(); var fvc = _this.getFirstVisibleCol(); var mergedRange = _this._getMergedCellsRange(col, row); if (mergedRange && (fvc < mergedRange.c2) && (fvr < mergedRange.r2)) { var startCol = (mergedRange.c1 > fvc) ? mergedRange.c1 : fvc; var startRow = (mergedRange.r1 > fvr) ? mergedRange.r1 : fvr; metrics.top = _this.getCellTop(startRow, 0) - _this.getCellTop(fvr, 0) + _this.getCellTop(0, 0); metrics.left = _this.getCellLeft(startCol, 0) - _this.getCellLeft(fvc, 0) + _this.getCellLeft(0, 0); for (var i = startCol; i <= mergedRange.c2; i++) { metrics.width += _this.getColumnWidth(i, 0) } for (var i = startRow; i <= mergedRange.r2; i++) { metrics.height += _this.getRowHeight(i, 0) } metrics.result = true; } else{ metrics.top = _this.getCellTop(row, 0) - _this.getCellTop(fvr, 0) + _this.getCellTop(0, 0); metrics.left = _this.getCellLeft(col, 0) - _this.getCellLeft(fvc, 0) + _this.getCellLeft(0, 0); metrics.width = _this.getColumnWidth(col, 0); metrics.height = _this.getRowHeight(row, 0); metrics.result = true; } return metrics } for(var id in this.depDrawCells ){ c = this.depDrawCells[id].from; node = this.depDrawCells[id].to; var mainCellMetrics = gCM(this,c.getCellAddress().getCol0(),c.getCellAddress().getRow0()), nodeCellMetrics, _t1, _t2; for(var id in node){ if( !node[id].isArea ){ _t1 = gCM(this,node[id].returnCell().getCellAddress().getCol0(),node[id].returnCell().getCellAddress().getRow0()) nodeCellMetrics = { t: _t1.top, l: _t1.left, w: _t1.width, h: _t1.height, apt: _t1.top+_t1.height/2, apl: _t1.left+_t1.width/4}; } else{ var _t1 = gCM(_wsV,me[id].firstCellAddress.getCol0(),me[id].firstCellAddress.getRow0()), _t2 = gCM(_wsV,me[id].lastCellAddress.getCol0(),me[id].lastCellAddress.getRow0()); nodeCellMetrics = { t: _t1.top, l: _t1.left, w: _t2.left+_t2.width-_t1.left, h: _t2.top+_t2.height-_t1.top, apt: _t1.top+_t1.height/2, apl:_t1.left+_t1.width/4 }; } var x1 = Math.floor(nodeCellMetrics.apl), y1 = Math.floor(nodeCellMetrics.apt), x2 = Math.floor(mainCellMetrics.left+mainCellMetrics.width/4), y2 = Math.floor(mainCellMetrics.top+mainCellMetrics.height/2); if( x1<0 && x2<0 || y1<0 && y2<0) continue; if(y1<this.getCellTop(0, 0)) y1-=this.getCellTop(0, 0); if(y1<0 && y2>0){ var _x1 = Math.floor(Math.sqrt((x1-x2)*(x1-x2)*y1*y1/((y2-y1)*(y2-y1)))); // x1 -= (x1-x2>0?1:-1)*_x1; if( x1 > x2){ x1 -= _x1; } else if( x1 < x2 ){ x1 += _x1; } } else if(y1>0 && y2<0){ var _x2 = Math.floor(Math.sqrt((x1-x2)*(x1-x2)*y2*y2/((y2-y1)*(y2-y1)))); // x2 -= (x2-x1>0?1:-1)*_x2; if( x2 > x1){ x2 -= _x2; } else if( x2 < x1){ x2 += _x2; } } if(x1<0 && x2>0){ var _y1 = Math.floor(Math.sqrt((y1-y2)*(y1-y2)*x1*x1/((x2-x1)*(x2-x1)))) // y1 -= (y1-y2>0?1:-1)*_y1; if( y1 > y2){ y1 -= _y1; } else if( y1 < y2 ){ y1 += _y1; } } else if(x1>0 && x2<0){ var _y2 = Math.floor(Math.sqrt((y1-y2)*(y1-y2)*x2*x2/((x2-x1)*(x2-x1)))) // y2 -= (y2-y1>0?1:-1)*_y2; if( y2 > y1 ){ y2 -= _y2; } else if( y2 < y1 ){ y2 += _y2; } } draw_arrow(ctx, x1<this.getCellLeft(0, 0)?this.getCellLeft(0, 0):x1, y1<this.getCellTop(0, 0)?this.getCellTop(0, 0):y1, x2, y2); // draw_arrow(ctx, x1, y1, x2, y2); if( nodeCellMetrics.apl > this.getCellLeft(0, 0) && nodeCellMetrics.apt > this.getCellTop(0, 0) ) ctx.save() .beginPath() .arc(_cc.pxToPt(Math.floor(nodeCellMetrics.apl)), _cc.pxToPt(Math.floor(nodeCellMetrics.apt)), 3,0, 2 * Math.PI, false,-0.5,-0.5) .setFillStyle("#0000FF") .fill() .closePath() .setLineWidth(1) .setStrokeStyle("#0000FF") .rect( _cc.pxToPt(nodeCellMetrics.l),_cc.pxToPt(nodeCellMetrics.t),_cc.pxToPt(nodeCellMetrics.w-1),_cc.pxToPt(nodeCellMetrics.h-1), -.5, -.5 ) .stroke() .restore(); } } }, prepareDepCells: function(se){ var activeCell = this.activeRange, mc = this._getMergedCellsRange(activeCell.startCol, activeCell.startRow), c1 = mc ? mc.c1 : activeCell.startCol, r1 = mc ? mc.r1 : activeCell.startRow, c = this._getVisibleCell(c1, r1), nodes = (se == c_oAscDrawDepOptions.Master) ? this.model.workbook.dependencyFormulas.getMasterNodes(this.model.getId(),c.getName()) : this.model.workbook.dependencyFormulas.getSlaveNodes(this.model.getId(),c.getName()); if(!nodes) return; if( !this.depDrawCells ) this.depDrawCells = {}; if(se == c_oAscDrawDepOptions.Master){ c = c.getCells()[0]; var id = getVertexId(this.model.getId(),c.getName()); this.depDrawCells[id] = {from:c,to:nodes}; } else{ var to = {}, to1, id = getVertexId(this.model.getId(),c.getName()); to[getVertexId(this.model.getId(),c.getName())]= this.model.workbook.dependencyFormulas.getNode(this.model.getId(),c.getName()); to1 = this.model.workbook.dependencyFormulas.getNode(this.model.getId(),c.getName()); for(var id2 in nodes){ if( this.depDrawCells[id2] ) $.extend(this.depDrawCells[id2].to,to) else{ this.depDrawCells[id2] = {} this.depDrawCells[id2].from = nodes[id2].returnCell() this.depDrawCells[id2].to = {} this.depDrawCells[id2].to[id] = to1; } } } this.drawDepCells(); }, cleanDepCells: function(){ this.depDrawCells = null; this.drawDepCells(); }, // ----- Text drawing ----- _getPPIX: function () { return this.drawingCtx.getPPIX(); }, _getPPIY: function () { return this.drawingCtx.getPPIY(); }, _setFont: function (drawingCtx, name, size) { var ctx = (drawingCtx) ? drawingCtx : this.drawingCtx; ctx.setFont( new asc_FP(name, size) ); }, /** * @param {TextMetrics} tm * @return {TextMetrics} */ _roundTextMetrics: function (tm) { tm.width = asc_calcnpt( tm.width, this._getPPIX() ); tm.height = asc_calcnpt( tm.height, this._getPPIY() ); tm.baseline = asc_calcnpt( tm.baseline, this._getPPIY() ); if (tm.centerline !== undefined) { tm.centerline = asc_calcnpt( tm.centerline, this._getPPIY() ); } return tm; }, _calcTextHorizPos: function (x1, x2, tm, halign) { switch (halign) { case khaCenter: return asc_calcnpt(0.5 * (x1 + x2 + this.width_1px - tm.width), this._getPPIX()); case khaRight: return x2 + this.width_1px - this.width_padding - tm.width; case khaJustify: default: return x1 + this.width_padding; } }, _calcTextVertPos: function (y1, y2, baseline, tm, valign) { switch (valign) { case kvaCenter: return asc_calcnpt(0.5 * (y1 + y2 - tm.height), this._getPPIY()) - this.height_1px; case kvaTop: return y1 - this.height_1px; default: return baseline - tm.baseline; } }, _calcTextWidth: function (x1, x2, tm, halign) { switch (halign) { case khaJustify: return x2 + this.width_1px - this.width_padding * 2 - x1; default: return tm.width; } }, // ----- Events processing ----- _trigger: function (eventName) { var f = this.settings[eventName]; return f && asc_typeof(f) === "function" ? f.apply( this, Array.prototype.slice.call(arguments, 1) ) : undefined; }, // ----- Scrolling ----- _calcCellPosition: function (c, r, dc, dr) { var t = this; var vr = t.visibleRange; function findNextCell(col, row, dx, dy) { var state = t._isCellEmpty(col, row); var i = col + dx; var j = row + dy; while (i >= 0 && i < t.cols.length && j >= 0 && j < t.rows.length) { var newState = t._isCellEmpty(i, j); if (newState !== state) { var ret = {}; ret.col = state ? i : i - dx; ret.row = state ? j : j - dy; if (ret.col !== col || ret.row !== row || state) { return ret; } state = newState; } i += dx; j += dy; } // Проверки для перехода в самый конец (ToDo пока убрал, чтобы не добавлять тормозов) /*if (i === t.cols.length && state) i = gc_nMaxCol; if (j === t.rows.length && state) j = gc_nMaxRow;*/ return { col: i - dx, row: j- dy }; } function findEnd(col, row) { var nc1, nc2 = col; do { nc1 = nc2; nc2 = findNextCell(nc1, row, +1, 0).col; } while (nc1 !== nc2); return nc2; } function findEOT() { var obr = t.objectRender ? t.objectRender.getDrawingAreaMetrics() : {maxCol: 0, maxRow: 0}; var maxCols = t.model.getColsCount(); var maxRows = t.model.getRowsCount(); var lastC = -1, lastR = -1; for (var col = 0; col < maxCols; ++col) { for (var row = 0; row < maxRows; ++row) { if (!t._isCellEmpty(col, row)) { lastC = Math.max(lastC, col); lastR = Math.max(lastR, row); } } } return {col: Math.max(lastC, obr.maxCol), row: Math.max(lastR, obr.maxRow)}; } var eot = dc > +2.0001 && dc < +2.9999 && dr > +2.0001 && dr < +2.9999 ? findEOT() : null; var newCol = (function () { if (dc > +0.0001 && dc < +0.9999) { return c + (vr.c2 - vr.c1 + 1); } // PageDown if (dc < -0.0001 && dc > -0.9999) { return c - (vr.c2 - vr.c1 + 1); } // PageUp if (dc > +1.0001 && dc < +1.9999) { return findNextCell(c, r, +1, 0).col; } // Ctrl + -> if (dc < -1.0001 && dc > -1.9999) { return findNextCell(c, r, -1, 0).col; } // Ctrl + <- if (dc > +2.0001 && dc < +2.9999) { return !eot ? findEnd(c,r) : eot.col; } // End if (dc < -2.0001 && dc > -2.9999) { return 0; } // Home return c + dc; })(); var newRow = (function () { if (dr > +0.0001 && dr < +0.9999) { return r + (vr.r2 - vr.r1 + 1); } if (dr < -0.0001 && dr > -0.9999) { return r - (vr.r2 - vr.r1 + 1); } if (dr > +1.0001 && dr < +1.9999) { return findNextCell(c, r, 0, +1).row; } if (dr < -1.0001 && dr > -1.9999) { return findNextCell(c, r, 0, -1).row; } if (dr > +2.0001 && dr < +2.9999) { return !eot ? 0 : eot.row; } if (dr < -2.0001 && dr > -2.9999) { return 0; } return r + dr; })(); if (newCol >= t.cols.length && newCol <= gc_nMaxCol0) { t.nColsCount = newCol + 1; t._updateMergedCellsRange(asc_Range(t.cols.length - 1, 0, t.nColsCount - 1, t.nRowsCount)); t._calcColumnWidths(/*fullRecalc*/2); } if (newRow >= t.rows.length && newRow <= gc_nMaxRow0) { t.nRowsCount = newRow + 1; t._updateMergedCellsRange(asc_Range(0, t.rows.length - 1, t.nColsCount, t.nRowsCount - 1)); t._calcRowHeights(/*fullRecalc*/2); } return { col: newCol < 0 ? 0 : Math.min(newCol, t.cols.length - 1), row: newRow < 0 ? 0 : Math.min(newRow, t.rows.length - 1) }; }, _isColDrawnPartially: function (col, leftCol) { if (col <= leftCol) return false; var c = this.cols; return c[col].left + c[col].width - c[leftCol].left + this.cellsLeft > this.drawingCtx.getWidth(); }, _isRowDrawnPartially: function (row, topRow) { if (row <= topRow) return false; var r = this.rows; return r[row].top + r[row].height - r[topRow].top + this.cellsTop > this.drawingCtx.getHeight(); }, _isVisibleX: function (x, leftCol) { var c = this.cols; return x - c[leftCol].left + this.cellsLeft < this.drawingCtx.getWidth(); }, _isVisibleY: function (y, topRow) { var r = this.rows; return y - r[topRow].top + this.cellsTop < this.drawingCtx.getHeight(); }, _updateVisibleRowsCount: function (skipScrolReinit) { var vr = this.visibleRange; this._calcVisibleRows(); if ( this._isVisibleY(this.rows[vr.r2].top + this.rows[vr.r2].height, vr.r1) ) { do{ // Добавим еще строки, чтоб не было видно фон под таблицей this.expandRowsOnScroll(true); this._calcVisibleRows(); if (this.rows[this.rows.length - 1].height < 0.000001) {break;} } while ( this._isVisibleY(this.rows[vr.r2].top + this.rows[vr.r2].height, vr.r1) ); if (!skipScrolReinit) { this._trigger("reinitializeScrollY"); } } }, _updateVisibleColsCount: function (skipScrolReinit) { var vr = this.visibleRange; this._calcVisibleColumns(); if ( this._isVisibleX(this.cols[vr.c2].left + this.cols[vr.c2].width, vr.c1) ) { do { // Добавим еще столбцы, чтоб не было видно фон под таблицей this.expandColsOnScroll(true); this._calcVisibleColumns(); if (this.cols[this.cols.length - 1].width < 0.000001) {break;} } while ( this._isVisibleX(this.cols[vr.c2].left + this.cols[vr.c2].width, vr.c1) ); if (!skipScrolReinit) { this._trigger("reinitializeScrollX"); } } }, scrollVertical: function (delta, editor) { var vr = this.visibleRange; var start = this._calcCellPosition(vr.c1, vr.r1, 0, delta).row; if (start === vr.r1) {return this;} this.cleanSelection(); var ctx = this.drawingCtx; var ctxW = ctx.getWidth(); var ctxH = ctx.getHeight(); var dy = this.rows[start].top - this.rows[vr.r1].top; var oldEnd = vr.r2; var oldDec = Math.max(calcDecades(oldEnd + 1), 3); var oldVRE_isPartial = this._isRowDrawnPartially(vr.r2, vr.r1); if (this.isCellEditMode && editor) {editor.move(0, -dy);} vr.r1 = start; this._updateVisibleRowsCount(); var oldH = ctxH - this.cellsTop - Math.abs(dy); var y = this.cellsTop + (dy > 0 && oldH > 0 ? dy : 0); var oldW, x, dx; this.objectRender.setScrollOffset(0, dy * asc_getcvt(1, 0, this._getPPIX()) ); var widthChanged = Math.max(calcDecades(vr.r2 + 1), 3) !== oldDec; if (widthChanged) { x = this.cellsLeft; this._calcHeaderColumnWidth(); this._updateColumnPositions(); this._calcVisibleColumns(); this._drawCorner(); this._cleanColumnHeadersRect(); this._drawColumnHeaders(/*drawingCtx*/ undefined); dx = this.cellsLeft - x; oldW = ctxW - x - Math.abs(dx); } else { dx = 0; x = this.headersLeft; oldW = ctxW; } if (oldH > 0) { ctx.drawImage(ctx.getCanvas(), x, y, oldW, oldH, x + dx, y - dy, oldW, oldH); } ctx.setFillStyle(this.settings.cells.defaultState.background) .fillRect(this.headersLeft, y + (dy > 0 && oldH > 0 ? oldH - dy : 0), ctxW, ctxH - this.cellsTop - (oldH > 0 ? oldH : 0)); if ( !(dy > 0 && vr.r2 === oldEnd && !oldVRE_isPartial && dx === 0) ) { var c1 = vr.c1; var r1 = dy > 0 && oldH > 0 ? oldEnd + (oldVRE_isPartial ? 0 : 1) : vr.r1; var c2 = vr.c2; var r2 = dy > 0 || oldH <= 0 ? vr.r2 : vr.r1 - 1 - delta; /* delta < 0 here */ var range = asc_Range(c1, r1, c2, r2); if (dx === 0) { this._drawRowHeaders(/*drawingCtx*/ undefined, r1, r2); } else { // redraw all headres, because number of decades in row index has been changed this._drawRowHeaders(/*drawingCtx*/ undefined); if (dx < 0) { // draw last column var r1_ = dy > 0 ? vr.r1 : r2 + 1; var r2_ = dy > 0 ? r1 - 1 : vr.r2; var r_ = asc_Range(c2, r1_, c2, r2_); if (r2_ >= r1_) { this._drawGrid(/*drawingCtx*/ undefined, r_); this._drawCells(r_); this._drawCellsBorders(/*drawingCtx*/undefined, r_); } } } this._drawGrid(/*drawingCtx*/ undefined, range); this._drawCells(range); this._drawCellsBorders(/*drawingCtx*/undefined, range); this._fixSelectionOfMergedCells(); this._drawSelection(); if (widthChanged) {this._trigger("reinitializeScrollX");} } this._updateHyperlinksCache(); this.cellCommentator.updateCommentPosition(); this.drawDepCells(); this.objectRender.showDrawingObjects(true); return this; }, scrollHorizontal: function (delta, editor) { var vr = this.visibleRange; var start = this._calcCellPosition(vr.c1, vr.r1, delta, 0).col; if (start === vr.c1) {return this;} this.cleanSelection(); var ctx = this.drawingCtx; var ctxW = ctx.getWidth(); var ctxH = ctx.getHeight(); var dx = this.cols[start].left - this.cols[vr.c1].left; var oldEnd = vr.c2; var oldVCE_isPartial = this._isColDrawnPartially(vr.c2, vr.c1); if (this.isCellEditMode && editor) {editor.move(-dx, 0);} vr.c1 = start; this._updateVisibleColsCount(); this.objectRender.setScrollOffset( dx * asc_getcvt(1, 0, this._getPPIX()), 0 ); var oldW = ctxW - this.cellsLeft - Math.abs(dx); var x = this.cellsLeft + (dx > 0 && oldW > 0 ? dx : 0); var y = this.headersTop; if (oldW > 0) { ctx.drawImage(ctx.getCanvas(), x, y, oldW, ctxH, x - dx, y, oldW, ctxH); } ctx.setFillStyle(this.settings.cells.defaultState.background) .fillRect(x + (dx > 0 && oldW > 0 ? oldW - dx : 0), y, ctxW - this.cellsLeft - (oldW > 0 ? oldW : 0), ctxH); if ( !(dx > 0 && vr.c2 === oldEnd && !oldVCE_isPartial) ) { var c1 = dx > 0 && oldW > 0 ? oldEnd + (oldVCE_isPartial ? 0 : 1) : vr.c1; var r1 = vr.r1; var c2 = dx > 0 || oldW <= 0 ? vr.c2 : vr.c1 - 1 - delta; /* delta < 0 here */ var r2 = vr.r2; var range = asc_Range(c1, r1, c2, r2); this._drawColumnHeaders(/*drawingCtx*/ undefined, c1, c2); this._drawGrid(/*drawingCtx*/ undefined, range); this._drawCells(range); this._drawCellsBorders(/*drawingCtx*/undefined, range); this._fixSelectionOfMergedCells(); this._drawSelection(); } this.cellCommentator.updateCommentPosition(); this._updateHyperlinksCache(); this.drawDepCells(); this.objectRender.showDrawingObjects(true); return this; }, // ----- Selection ----- // dX = true - считать с половиной следующей ячейки _findColUnderCursor: function (x, canReturnNull, dX) { var c = this.visibleRange.c1; var offset = this.cols[c].left - this.cellsLeft; var c2, x1, x2; if (x >= this.cellsLeft) { for (x1 = this.cellsLeft, c2 = this.cols.length - 1; c <= c2; ++c, x1 = x2) { x2 = x1 + this.cols[c].width; if (x1 <= x && x < x2) { if (dX){ // Учитываем половину ячейки if (x1 <= x && x < x1 + this.cols[c].width / 2.0){ // Это предыдущая ячейка --c; // Можем вернуть и -1 (но это только для fillHandle) } } return {col: c, left: x1, right: x2}; } } if (!canReturnNull) {return {col: c2, left: this.cols[c2].left - offset, right: x2};} } else { for (x2 = this.cellsLeft + this.cols[c].width, c2 = 0; c >= c2; --c, x2 = x1) { x1 = this.cols[c].left - offset; if (x1 <= x && x < x2) { if (dX){ // Учитываем половину ячейки if (x1 <= x && x < x1 + this.cols[c].width / 2.0){ // Это предыдущая ячейка --c; // Можем вернуть и -1 (но это только для fillHandle) } } return {col: c, left: x1, right: x2}; } } if (!canReturnNull) { if (dX) { // Это предыдущая ячейка --c2; // Можем вернуть и -1 (но это только для fillHandle) return {col: c2}; } return {col: c2, left: x1, right: x1 + this.cols[c2].width}; } } return null; }, // dY = true - считать с половиной следующей ячейки _findRowUnderCursor: function (y, canReturnNull, dY) { var r = this.visibleRange.r1, offset = this.rows[r].top - this.cellsTop, r2, y1, y2; if (y >= this.cellsTop) { for (y1 = this.cellsTop, r2 = this.rows.length - 1; r <= r2; ++r, y1 = y2) { y2 = y1 + this.rows[r].height; if (y1 <= y && y < y2) { if (dY){ // Учитываем половину ячейки if (y1 <= y && y < y1 + this.rows[r].height / 2.0){ // Это предыдущая ячейка --r; // Можем вернуть и -1 (но это только для fillHandle) } } return {row: r, top: y1, bottom: y2}; } } if (!canReturnNull) {return {row: r2, top: this.rows[r2].top - offset, bottom: y2};} } else { for (y2 = this.cellsTop + this.rows[r].height, r2 = 0; r >= r2; --r, y2 = y1) { y1 = this.rows[r].top - offset; if (y1 <= y && y < y2) { if (dY){ // Учитываем половину ячейки if (y1 <= y && y < y1 + this.rows[r].height / 2.0){ // Это предыдущая ячейка --r; // Можем вернуть и -1 (но это только для fillHandle) } } return {row: r, top: y1, bottom: y2}; } } if (!canReturnNull) { if (dY) { // Это предыдущая ячейка --r2; // Можем вернуть и -1 (но это только для fillHandle) return {row: r2}; } return {row: r2, top: y1, bottom: y1 + this.rows[r2].height}; } } return null; }, getCursorTypeFromXY: function (x, y) { var c, r, f; var left, top, right, bottom; var sheetId = this.model.getId(); var userId = undefined; var lockRangePosLeft = undefined; var lockRangePosTop = undefined; var lockInfo = undefined; var isLocked = false; if ( asc["editor"].isStartAddShape ) return {cursor: kCurFillHandle, target: "shape", col: -1, row: -1}; var drawingInfo = this.objectRender.checkCursorDrawingObject(x, y); if (drawingInfo && drawingInfo.id) { // Возможно картинка с lock lockInfo = this.collaborativeEditing.getLockInfo(c_oAscLockTypeElem.Object, null, sheetId, drawingInfo.id); isLocked = this.collaborativeEditing.getLockIntersection(lockInfo, c_oAscLockTypes.kLockTypeOther,false); if (false !== isLocked) { // Кто-то сделал lock userId = isLocked.UserId; lockRangePosLeft = drawingInfo.object.getVisibleLeftOffset(true); lockRangePosTop = drawingInfo.object.getVisibleTopOffset(true); } return {cursor: drawingInfo.cursor, target: "shape", drawingId: drawingInfo.id, col: -1, row: -1, userId: userId, lockRangePosLeft: lockRangePosLeft, lockRangePosTop: lockRangePosTop}; } var autoFilterCursor = this.autoFilters.isButtonAFClick(x,y,this); if(autoFilterCursor) return {cursor: autoFilterCursor, target: "aFilterObject", col: -1, row: -1}; x *= asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIX() ); y *= asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIY() ); if (this.isFormulaEditMode || this.isChartAreaEditMode) { var offsetX = this.cols[this.visibleRange.c1].left - this.cellsLeft; var offsetY = this.rows[this.visibleRange.r1].top - this.cellsTop; var arr = this.isFormulaEditMode ? this.arrActiveFormulaRanges : this.arrActiveChartsRanges, targetArr = this.isFormulaEditMode ? 0 : -1; for (var i in arr) { var arFormulaTmp = arr[i].clone(true); var aFormulaIntersection = arFormulaTmp.intersection(this.visibleRange); if (aFormulaIntersection) { var drawLeftSideFormula = aFormulaIntersection.c1 === arFormulaTmp.c1; var drawRightSideFormula = aFormulaIntersection.c2 === arFormulaTmp.c2; var drawTopSideFormula = aFormulaIntersection.r1 === arFormulaTmp.r1; var drawBottomSideFormula = aFormulaIntersection.r2 === arFormulaTmp.r2; var xFormula1 = this.cols[aFormulaIntersection.c1].left - offsetX; var xFormula2 = this.cols[aFormulaIntersection.c2].left + this.cols[aFormulaIntersection.c2].width - offsetX; var yFormula1 = this.rows[aFormulaIntersection.r1].top - offsetY; var yFormula2 = this.rows[aFormulaIntersection.r2].top + this.rows[aFormulaIntersection.r2].height - offsetY; if( (x >= xFormula1 + 5 && x <= xFormula2 - 5) && ((y >= yFormula1 - this.height_2px && y <= yFormula1 + this.height_2px) || (y >= yFormula2 - this.height_2px && y <= yFormula2 + this.height_2px)) || (y >= yFormula1 + 5 && y <= yFormula2 - 5) && ((x >= xFormula1 - this.width_2px && x <= xFormula1 + this.width_2px) || (x >= xFormula2 - this.width_2px && x <= xFormula2 + this.width_2px)) ){ return {cursor: kCurMove, target: "moveResizeRange", col: -1, row: -1, formulaRange: arFormulaTmp, indexFormulaRange:i, targetArr: targetArr}; } else if( x >= xFormula1 && x < xFormula1 + 5 && y >= yFormula1 && y < yFormula1 + 5 ){ return {cursor: kCurSEResize, target: "moveResizeRange", col: aFormulaIntersection.c2, row: aFormulaIntersection.r2, formulaRange: arFormulaTmp, indexFormulaRange:i, targetArr: targetArr}; } else if ( x > xFormula2 - 5 && x <= xFormula2 && y > yFormula2 - 5 && y <= yFormula2 ){ return {cursor: kCurSEResize, target: "moveResizeRange", col: aFormulaIntersection.c1, row: aFormulaIntersection.r1, formulaRange: arFormulaTmp, indexFormulaRange:i, targetArr: targetArr}; } else if( x > xFormula2 - 5 && x <= xFormula2 && y >= yFormula1 && y < yFormula1 + 5 ){ return {cursor: kCurNEResize, target: "moveResizeRange", col: aFormulaIntersection.c1, row: aFormulaIntersection.r2, formulaRange: arFormulaTmp, indexFormulaRange:i, targetArr: targetArr}; } else if( x >= xFormula1 && x < xFormula1 + 5 && y > yFormula2 - 5 && y <= yFormula2 ){ return {cursor: kCurNEResize, target: "moveResizeRange", col: aFormulaIntersection.c2, row: aFormulaIntersection.r1, formulaRange: arFormulaTmp, indexFormulaRange:i, targetArr: targetArr}; } } } } do { // Эпсилон для fillHandle var fillHandleEpsilon = this.width_1px; if (x >= (this.fillHandleL - fillHandleEpsilon) && x <= (this.fillHandleR + fillHandleEpsilon) && y >= (this.fillHandleT - fillHandleEpsilon) && y <= (this.fillHandleB + fillHandleEpsilon) && !this.isChartAreaEditMode) { // Мы на "квадрате" для автозаполнения return {cursor: kCurFillHandle, target: "fillhandle", col: -1, row: -1}; } var offsetX = this.cols[this.visibleRange.c1].left - this.cellsLeft; var offsetY = this.rows[this.visibleRange.r1].top - this.cellsTop; var xWithOffset = x + offsetX; var yWithOffset = y + offsetY; // Навели на выделение left = this.cols[this.activeRange.c1].left; right = this.cols[this.activeRange.c2].left + this.cols[this.activeRange.c2].width; top = this.rows[this.activeRange.r1].top; bottom = this.rows[this.activeRange.r2].top + this.rows[this.activeRange.r2].height; if ((((xWithOffset >= left - this.width_2px && xWithOffset <= left + this.width_2px) || (xWithOffset >= right - this.width_2px && xWithOffset <= right + this.width_2px)) && yWithOffset >= top - this.height_2px && yWithOffset <= bottom + this.height_2px) || (((yWithOffset >= top - this.height_2px && yWithOffset <= top + this.height_2px) || (yWithOffset >= bottom - this.height_2px && yWithOffset <= bottom + this.height_2px)) && xWithOffset >= left - this.width_2px && xWithOffset <= right + this.width_2px)) { // Мы навели на границу выделения return {cursor: kCurMove, target: "moveRange", col: -1, row: -1}; } if (x < this.cellsLeft && y < this.cellsTop) { return {cursor: kCurCorner, target: "corner", col: -1, row: -1}; } if (x > this.cellsLeft && y > this.cellsTop) { c = this._findColUnderCursor(x, true); r = this._findRowUnderCursor(y, true); if (c === null || r === null) {break;} // Проверка на совместное редактирование var lockRange = undefined; var lockAllPosLeft = undefined; var lockAllPosTop = undefined; var userIdAllProps = undefined; var userIdAllSheet = undefined; var c1Recalc = null, r1Recalc = null; var selectRangeRecalc = asc_Range(c.col, r.row, c.col, r.row); // Пересчет для входящих ячеек в добавленные строки/столбцы var isIntersection = this._recalcRangeByInsertRowsAndColumns(sheetId, selectRangeRecalc); if (false === isIntersection) { lockInfo = this.collaborativeEditing.getLockInfo(c_oAscLockTypeElem.Range, /*subType*/null, sheetId, new asc_CCollaborativeRange(selectRangeRecalc.c1, selectRangeRecalc.r1, selectRangeRecalc.c2, selectRangeRecalc.r2)); isLocked = this.collaborativeEditing.getLockIntersection(lockInfo, c_oAscLockTypes.kLockTypeOther, /*bCheckOnlyLockAll*/false); if (false !== isLocked) { // Кто-то сделал lock userId = isLocked.UserId; lockRange = isLocked.Element["rangeOrObjectId"]; c1Recalc = this.collaborativeEditing.m_oRecalcIndexColumns[sheetId].getLockOther( lockRange["c1"], c_oAscLockTypes.kLockTypeOther); r1Recalc = this.collaborativeEditing.m_oRecalcIndexRows[sheetId].getLockOther( lockRange["r1"], c_oAscLockTypes.kLockTypeOther); if (null !== c1Recalc && null !== r1Recalc) { lockRangePosLeft = this.getCellLeft(c1Recalc, /*pt*/1); lockRangePosTop = this.getCellTop(r1Recalc, /*pt*/1); // Пересчитываем X и Y относительно видимой области lockRangePosLeft -= (this.cols[this.visibleRange.c1].left - this.cellsLeft); lockRangePosTop -= (this.rows[this.visibleRange.r1].top - this.cellsTop); // Пересчитываем в px lockRangePosLeft *= asc_getcvt(1/*pt*/, 0/*px*/, this._getPPIX()); lockRangePosTop *= asc_getcvt(1/*pt*/, 0/*px*/, this._getPPIY()); } } } else { lockInfo = this.collaborativeEditing.getLockInfo(c_oAscLockTypeElem.Range, /*subType*/null, sheetId, null); } // Проверим не удален ли весь лист (именно удален, т.к. если просто залочен, то не рисуем рамку вокруг) lockInfo["type"] = c_oAscLockTypeElem.Sheet; isLocked = this.collaborativeEditing.getLockIntersection(lockInfo, c_oAscLockTypes.kLockTypeOther, /*bCheckOnlyLockAll*/true); if (false !== isLocked) { // Кто-то сделал lock userIdAllSheet = isLocked.UserId; lockAllPosLeft = this.cellsLeft * asc_getcvt(1/*pt*/, 0/*px*/, this._getPPIX()); lockAllPosTop = this.cellsTop * asc_getcvt(1/*pt*/, 0/*px*/, this._getPPIY()); } // Проверим не залочены ли все свойства листа (только если не удален весь лист) if (undefined === userIdAllSheet) { lockInfo["type"] = c_oAscLockTypeElem.Range; lockInfo["subType"] = c_oAscLockTypeElemSubType.InsertRows; isLocked = this.collaborativeEditing.getLockIntersection(lockInfo, c_oAscLockTypes.kLockTypeOther, /*bCheckOnlyLockAll*/true); if (false !== isLocked) { // Кто-то сделал lock userIdAllProps = isLocked.UserId; lockAllPosLeft = this.cellsLeft * asc_getcvt(1/*pt*/, 0/*px*/, this._getPPIX()); lockAllPosTop = this.cellsTop * asc_getcvt(1/*pt*/, 0/*px*/, this._getPPIY()); } } // Проверим есть ли комменты var mergedRande = this._getMergedCellsRange(c.col, r.row); var comments = this.cellCommentator.asc_getComments(mergedRande ? mergedRande.c1 : c.col, mergedRande ? mergedRande.r1 : r.row); var coords = this.cellCommentator.getCommentsCoords(comments); var indexes = []; for (var i = 0; i < comments.length; i++) { indexes.push(comments[i].asc_getId()); } if (indexes.length <= 0) { coords = undefined; indexes = undefined; } // Проверим, может мы в гиперлинке var indexHyperlink = this._getHyperlinkIndex(c.col, r.row); var cellCursor = {cursor: kCurCells, target: "cells", col: (c ? c.col : -1), row: (r ? r.row : -1), userId: userId, lockRangePosLeft: lockRangePosLeft, lockRangePosTop: lockRangePosTop, userIdAllProps: userIdAllProps, lockAllPosLeft: lockAllPosLeft, lockAllPosTop: lockAllPosTop, userIdAllSheet: userIdAllSheet, commentIndexes: indexes, commentCoords: coords}; if (null != indexHyperlink) { return {cursor: kCurHyperlink, target: "hyperlink", hyperlink: this.visibleHyperlinks[indexHyperlink], cellCursor: cellCursor, userId: userId, lockRangePosLeft: lockRangePosLeft, lockRangePosTop: lockRangePosTop, userIdAllProps: userIdAllProps, userIdAllSheet: userIdAllSheet, lockAllPosLeft: lockAllPosLeft, lockAllPosTop: lockAllPosTop, commentIndexes: indexes, commentCoords: coords}; } return cellCursor; } if (x <= this.cellsLeft && y >= this.cellsTop) { r = this._findRowUnderCursor(y, true); if (r === null) {break;} f = r.row !== this.visibleRange.r1 && y < r.top + 3 || y >= r.bottom - 3; // ToDo В Excel зависимость epsilon от размера ячейки (у нас фиксированный 3) return { cursor: f ? kCurRowResize : kCurRowSelect, target: f ? "rowresize" : "rowheader", col: -1, row: r.row + (r.row !== this.visibleRange.r1 && f && y < r.top + 3 ? -1 : 0), mouseY: f ? ((y < r.top + 3) ? (r.top - y - this.height_1px): (r.bottom - y - this.height_1px)) : null }; } if (y <= this.cellsTop && x >= this.cellsLeft) { c = this._findColUnderCursor(x, true); if (c === null) {break;} f = c.col !== this.visibleRange.c1 && x < c.left + 3 || x >= c.right - 3; // ToDo В Excel зависимость epsilon от размера ячейки (у нас фиксированный 3) return { cursor: f ? kCurColResize : kCurColSelect, target: f ? "colresize" : "colheader", col: c.col + (c.col !== this.visibleRange.c1 && f && x < c.left + 3 ? -1 : 0), row: -1, mouseX: f ? ((x < c.left + 3) ? (c.left - x - this.width_1px): (c.right - x - this.width_1px)) : null }; } } while(0); return {cursor: kCurDefault, target: "none", col: -1, row: -1}; }, _fixSelectionOfMergedCells: function (fixedRange) { var t = this; function checkRange(range) { var c, r, res; for (r = range.r1; r <= range.r2 && r < t.nRowsCount; ++r) { for (c = range.c1; c <= range.c2 && c < t.nColsCount; ++c) { res = t._getMergedCellsRange(c, r); if (res === undefined) {continue;} res = range.union(res); if ( !range.isEqual(res) ) {return checkRange(res);} } } return range; } var ar = fixedRange ? fixedRange : ((this.isFormulaEditMode) ? t.arrActiveFormulaRanges[t.arrActiveFormulaRanges.length - 1] : t.activeRange); if( !ar ) { return; } if (ar.type && ar.type !== c_oAscSelectionType.RangeCells) { return; } var res = checkRange(ar.clone(true)); if (ar.c1 !== res.c1 && ar.c1 !== res.c2) { ar.c1 = ar.c1 <= ar.c2 ? res.c1 : Math.min(res.c2, this.nColsCount - 1); } ar.c2 = ar.c1 === res.c1 ? Math.min(res.c2, this.nColsCount - 1) : (res.c1); if (ar.r1 !== res.r1 && ar.r1 !== res.r2) { ar.r1 = ar.r1 <= ar.r2 ? res.r1 : Math.min(res.r2, this.nRowsCount - 1); } ar.r2 = ar.r1 === res.r1 ? Math.min(res.r2, this.nRowsCount - 1) : res.r1; }, /* isDraw - отрисовываем ли мы из draw (после сброса) */ _fixSelectionOfHiddenCells: function (dc, dr, isDraw) { var t = this, ar = t.activeRange, c1, c2, r1, r2, mc, i, arn = t.activeRange.clone(true); if (dc === undefined) {dc = +1;} if (dr === undefined) {dr = +1;} function findVisibleCol(from, dc, flag) { var to = dc < 0 ? -1 : t.cols.length, c; for (c = from; c !== to; c += dc) { if (t.cols[c].width > t.width_1px) {return c;} } return flag ? -1 : findVisibleCol(from, dc * -1, true); } function findVisibleRow(from, dr, flag) { var to = dr < 0 ? -1 : t.rows.length, r; for (r = from; r !== to; r += dr) { if (t.rows[r].height > t.height_1px) {return r;} } return flag ? -1 : findVisibleRow(from, dr * -1, true); } if (ar.c2 === ar.c1) { if (t.cols[ar.c1].width < t.width_1px) { c1 = c2 = findVisibleCol(ar.c1, dc); } } else { if (t.cols[ar.c2].width < t.width_1px) { // Проверка для одновременно замерженных и скрытых ячеек (A1:C1 merge, B:C hidden) for (mc = null, i = arn.r1; i <= arn.r2; ++i) { mc = t._getMergedCellsRange(ar.c2, i); if (mc) {break;} } if (!mc) {c2 = findVisibleCol(ar.c2, dc);} } } if (c1 < 0 || c2 < 0) {throw "Error: all columns are hidden";} if (ar.r2 === ar.r1) { if (t.rows[ar.r1].height < t.height_1px) { r1 = r2 = findVisibleRow(ar.r1, dr); } } else { if (t.rows[ar.r2].height < t.height_1px) { //Проверка для одновременно замерженных и скрытых ячеек (A1:A3 merge, 2:3 hidden) for (mc = null, i = arn.c1; i <= arn.c2; ++i) { mc = t._getMergedCellsRange(i, ar.r2); if (mc) {break;} } if (!mc) {r2 = findVisibleRow(ar.r2, dr);} } } if (r1 < 0 || r2 < 0) {throw "Error: all rows are hidden";} ar.assign( c1 !== undefined ? c1 : ar.c1, r1 !== undefined ? r1 : ar.r1, c2 !== undefined ? c2 : ar.c2, r2 !== undefined ? r2 : ar.r2); if (c1 >= 0) {ar.startCol = c1;} if (r1 >= 0) {ar.startRow = r1;} if (t.cols[ar.startCol].width < t.width_1px) { c1 = findVisibleCol(ar.startCol, dc); if (c1 >= 0) {ar.startCol = c1;} } if (t.rows[ar.startRow].height < t.height_1px) { r1 = findVisibleRow(ar.startRow, dr); if (r1 >= 0) {ar.startRow = r1;} } }, _moveActiveCellToXY: function (x, y) { var c, r; var xpos = x; var ypos = y; var ar = (this.isFormulaEditMode) ? this.arrActiveFormulaRanges[this.arrActiveFormulaRanges.length - 1] : this.activeRange; var cursorInfo = this.objectRender.checkCursorDrawingObject(xpos, ypos); if ( cursorInfo ) { var graphicSelectionType = this.objectRender.getGraphicSelectionType(cursorInfo.id); ar.type = graphicSelectionType; return; } x *= asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIX() ); y *= asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIY() ); if (x < this.cellsLeft && y < this.cellsTop) { ar.assign(0, 0, this.cols.length - 1, this.rows.length - 1); ar.type = c_oAscSelectionType.RangeMax; ar.startCol = 0; ar.startRow = 0; this._fixSelectionOfHiddenCells(); } else if (x < this.cellsLeft) { r = this._findRowUnderCursor(y).row; ar.assign(0, r, this.cols.length - 1, r); ar.type = c_oAscSelectionType.RangeRow; ar.startCol = 0; ar.startRow = r; this._fixSelectionOfHiddenCells(); } else if (y < this.cellsTop) { c = this._findColUnderCursor(x).col; ar.assign(c, 0, c, this.rows.length - 1); ar.type = c_oAscSelectionType.RangeCol; ar.startCol = c; ar.startRow = 0; this._fixSelectionOfHiddenCells(); } else { c = this._findColUnderCursor(x).col; r = this._findRowUnderCursor(y).row; ar.assign(c, r, c, r); ar.startCol = c; ar.startRow = r; ar.type = c_oAscSelectionType.RangeCells; this._fixSelectionOfMergedCells(); } }, _moveActiveCellToOffset: function (dc, dr) { var ar = (this.isFormulaEditMode) ? this.arrActiveFormulaRanges[this.arrActiveFormulaRanges.length - 1] : this.activeRange; var mc = this._getMergedCellsRange(ar.startCol, ar.startRow); var c = mc ? ( dc < 0 ? mc.c1 : dc > 0 ? Math.min(mc.c2, this.nColsCount - 1 - dc) : ar.startCol) : ar.startCol; var r = mc ? ( dr < 0 ? mc.r1 : dr > 0 ? Math.min(mc.r2, this.nRowsCount - 1 - dr) : ar.startRow ) : ar.startRow; var p = this._calcCellPosition(c, r, dc, dr); ar.assign(p.col, p.row, p.col, p.row); ar.type = c_oAscSelectionType.RangeCells; ar.startCol = p.col; ar.startRow = p.row; this._fixSelectionOfMergedCells(); ar.normalize(); this._fixSelectionOfHiddenCells(dc>=0?+1:-1, dr>=0?+1:-1); }, // Движение активной ячейки в выделенной области _moveActivePointInSelection: function (dc, dr) { var ar = this.activeRange; var arn = this.activeRange.clone(true); // Set active cell ar.startCol += dc; ar.startRow += dr; do { var done = true; // Обработка выхода за границы выделения if (ar.startCol < arn.c1) { ar.startCol = arn.c2; ar.startRow -= 1; if (ar.startRow < arn.r1) { ar.startRow = arn.r2; } } else if (ar.startCol > arn.c2) { ar.startCol = arn.c1; ar.startRow += 1; if (ar.startRow > arn.r2) { ar.startRow = arn.r1; } } if (ar.startRow < arn.r1){ ar.startRow = arn.r2; ar.startCol -= 1; if (ar.startCol < arn.c1) { ar.startCol = arn.c2; } } else if (ar.startRow > arn.r2){ ar.startRow = arn.r1; ar.startCol += 1; if (ar.startCol > arn.c2) { ar.startCol = arn.c1; } } // Обработка движения active point через merged cells var mergedCells = this._getMergedCellsRange(ar.startCol, ar.startRow); if (mergedCells) { if (dc > 0 && (ar.startCol > mergedCells.c1 || ar.startRow !== mergedCells.r1)) { // Движение слева направо ar.startCol = mergedCells.c2 + 1; done = false; } else if (dc < 0 && (ar.startCol < mergedCells.c2 || ar.startRow !== mergedCells.r1)) { // Движение справа налево ar.startCol = mergedCells.c1 - 1; done = false; } if (dr > 0 && (ar.startRow > mergedCells.r1 || ar.startCol !== mergedCells.c1)) { // Движение сверху вниз ar.startRow = mergedCells.r2 + 1; done = false; } else if (dr < 0 && (ar.startRow < mergedCells.r2 || ar.startCol !== mergedCells.c1)) { // Движение снизу вверх ar.startRow = mergedCells.r1 - 1; done = false; } } if (!done) { continue; } // Обработка движения через срытые столбцы/строки while (ar.startCol >= arn.c1 && ar.startCol <= arn.c2 && this.cols[ar.startCol].width < 0.000001) { ar.startCol += dc || (dr > 0 ? +1 : -1); done = false; } if (!done) { continue; } while (ar.startRow >= arn.r1 && ar.startRow <= arn.r2 && this.rows[ar.startRow].height < 0.000001) { ar.startRow += dr || (dc > 0 ? +1 : -1); done = false; } } while (!done); }, _calcSelectionEndPointByXY: function (x, y) { var ar = (this.isFormulaEditMode) ? this.arrActiveFormulaRanges[this.arrActiveFormulaRanges.length - 1] : this.activeRange; x *= asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIX() ); y *= asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIY() ); return { c2: ar.type === c_oAscSelectionType.RangeCol || ar.type === c_oAscSelectionType.RangeCells ? this._findColUnderCursor(x).col : ar.c2, r2: ar.type === c_oAscSelectionType.RangeRow || ar.type === c_oAscSelectionType.RangeCells ? this._findRowUnderCursor(y).row : ar.r2 }; }, _calcSelectionEndPointByOffset: function (dc, dr) { var ar = (this.isFormulaEditMode) ? this.arrActiveFormulaRanges[this.arrActiveFormulaRanges.length - 1] : this.activeRange; var mc = this._getMergedCellsRange(ar.c2, ar.r2); var c = mc ? ( dc <= 0 ? mc.c1 : mc.c2 ) : ar.c2; var r = mc ? ( dr <= 0 ? mc.r1 : mc.r2 ) : ar.r2; var p = this._calcCellPosition(c, r, dc, dr); return {c2: p.col, r2: p.row}; }, _calcActiveRangeOffset: function () { var vr = this.visibleRange; var ar = (this.isFormulaEditMode) ? this.arrActiveFormulaRanges[this.arrActiveFormulaRanges.length - 1] : this.activeRange; if (this.isFormulaEditMode) { // Для формул нужно сделать ограничение по range (у нас хранится полный диапазон) if (ar.c2 >= this.nColsCount || ar.r2 >= this.nRowsCount) { ar = ar.clone(true); ar.c2 = (ar.c2 >= this.nColsCount) ? this.nColsCount - 1 : ar.c2; ar.r2 = (ar.r2 >= this.nRowsCount) ? this.nRowsCount - 1 : ar.r2; } } var arn = ar.clone(true); var isMC = this._isMergedCells(arn); // var adjustRight = ar.c2 >= vr.c2 || ar.c1 >= vr.c2 && isMC; var adjustRight = ar.startCol >= vr.c2; // var adjustBottom = ar.r2 >= vr.r2 || ar.r1 >= vr.r2 && isMC; var adjustBottom = ar.startRow >= vr.r2; // var incX = ar.c1 < vr.c1 && isMC ? arn.c1 - vr.c1 : ar.c2 < vr.c1 ? ar.c2 - vr.c1 : 0; var incX = ar.startCol < vr.c1 && isMC ? arn.c1 - vr.c1 : ar.startCol < vr.c1 ? ar.startCol - vr.c1 : 0; // var incY = ar.r1 < vr.r1 && isMC ? arn.r1 - vr.r1 : ar.r2 < vr.r1 ? ar.r2 - vr.r1 : 0; var incY = ar.startRow < vr.r1 && isMC ? arn.r1 - vr.r1 : ar.startRow < vr.r1 ? ar.startRow - vr.r1 : 0; if (adjustRight) { while ( this._isColDrawnPartially(isMC ? arn.c2 : ar.c2, vr.c1 + incX) ) {++incX;} } if (adjustBottom) { while ( this._isRowDrawnPartially(isMC ? arn.r2 : ar.r2, vr.r1 + incY) ) {++incY;} } return { deltaX: ar.type === c_oAscSelectionType.RangeCol || ar.type === c_oAscSelectionType.RangeCells ? incX : 0, deltaY: ar.type === c_oAscSelectionType.RangeRow || ar.type === c_oAscSelectionType.RangeCells ? incY : 0 }; }, _calcActiveCellOffset: function () { var vr = this.visibleRange; var ar = this.activeRange; var arn = ar.clone(true); var isMC = this._isMergedCells(arn); var adjustRight = ar.startCol >= vr.c2 || ar.startCol >= vr.c2 && isMC; var adjustBottom = ar.startRow >= vr.r2 || ar.startRow >= vr.r2 && isMC; var incX = ar.startCol < vr.c1 && isMC ? arn.startCol - vr.c1 : ar.startCol < vr.c1 ? ar.startCol - vr.c1 : 0; var incY = ar.startRow < vr.r1 && isMC ? arn.startRow - vr.r1 : ar.startRow < vr.r1 ? ar.startRow - vr.r1 : 0; if (adjustRight) { while ( this._isColDrawnPartially(isMC ? arn.startCol : ar.startCol, vr.c1 + incX) ) {++incX;} } if (adjustBottom) { while ( this._isRowDrawnPartially(isMC ? arn.startRow : ar.startRow, vr.r1 + incY) ) {++incY;} } return { deltaX: ar.type === c_oAscSelectionType.RangeCol || ar.type === c_oAscSelectionType.RangeCells ? incX : 0, deltaY: ar.type === c_oAscSelectionType.RangeRow || ar.type === c_oAscSelectionType.RangeCells ? incY : 0 }; }, _calcFillHandleOffset: function (range) { var vr = this.visibleRange; var ar = range ? range : this.activeFillHandle; var arn = ar.clone(true); var isMC = this._isMergedCells(arn); var adjustRight = ar.c2 >= vr.c2 || ar.c1 >= vr.c2 && isMC; var adjustBottom = ar.r2 >= vr.r2 || ar.r1 >= vr.r2 && isMC; var incX = ar.c1 < vr.c1 && isMC ? arn.c1 - vr.c1 : ar.c2 < vr.c1 ? ar.c2 - vr.c1 : 0; var incY = ar.r1 < vr.r1 && isMC ? arn.r1 - vr.r1 : ar.r2 < vr.r1 ? ar.r2 - vr.r1 : 0; if (adjustRight) { try{ while ( this._isColDrawnPartially(isMC ? arn.c2 : ar.c2, vr.c1 + incX) ) {++incX;} } catch(e){ this.expandColsOnScroll(true); this._trigger("reinitializeScrollX"); } } if (adjustBottom) { try{ while ( this._isRowDrawnPartially(isMC ? arn.r2 : ar.r2, vr.r1 + incY) ) {++incY;} } catch(e){ this.expandRowsOnScroll(true); this._trigger("reinitializeScrollY"); } } return { deltaX: incX, deltaY: incY }; }, // Потеряем ли мы что-то при merge ячеек getSelectionMergeInfo: function (options) { var t = this; var arn = t.activeRange.clone(true); var notEmpty = false; var r, c; switch (options) { case c_oAscMergeOptions.Merge: case c_oAscMergeOptions.MergeCenter: for (r = arn.r1; r <= arn.r2; ++r) { for (c = arn.c1; c <= arn.c2; ++c) { if (false === this._isCellEmpty(c, r)) { if (notEmpty) return true; notEmpty = true; } } } break; case c_oAscMergeOptions.MergeAcross: for (r = arn.r1; r <= arn.r2; ++r) { notEmpty = false; for (c = arn.c1; c <= arn.c2; ++c) { if (false === this._isCellEmpty(c, r)) { if (notEmpty) return true; notEmpty = true; } } } break; } return false; }, getSelectionName: function (bRangeText) { var activeCell = this.activeRange; var mc = this._getMergedCellsRange(activeCell.startCol, activeCell.startRow); var c1 = mc ? mc.c1 : activeCell.startCol; var r1 = mc ? mc.r1 : activeCell.startRow; var selectionSize = !bRangeText ? "" : (function (r) { var rc = Math.abs(r.r2 - r.r1) + 1; var cc = Math.abs(r.c2 - r.c1) + 1; switch (r.type) { case c_oAscSelectionType.RangeCells: return rc + "R x " + cc + "C"; case c_oAscSelectionType.RangeCol: return cc + "C"; case c_oAscSelectionType.RangeRow: return rc + "R"; case c_oAscSelectionType.RangeMax: return gc_nMaxRow + "R x " + gc_nMaxCol + "C"; } return ""; })(activeCell); var cellName = this._getColumnTitle(c1) + this._getRowTitle(r1); return selectionSize || cellName; }, getSelectionRangeValue: function () { var sListName = this.model.getName(); return sListName + "!" + this.getActiveRange(this.activeRange.clone(true)); }, getSelectionInfo: function (bExt) { var c_opt = this.settings.cells; var activeCell = this.activeRange; var mc = this._getMergedCellsRange(activeCell.startCol, activeCell.startRow); var c1 = mc ? mc.c1 : activeCell.startCol; var r1 = mc ? mc.r1 : activeCell.startRow; var c = this._getVisibleCell(c1, r1); if (c === undefined) { asc_debug("log", "Unknown cell's info: col = " + c1 + ", row = " + r1); return {}; } var fc = c.getFontcolor(); var bg = c.getFill(); var b = this._getBordersCache(c1, r1); var fa = c.getFontAlign().toLowerCase(); var cellType = c.getType(); var isNumberFormat = (!cellType || CellValueType.Number === cellType); var isGraphicObject = this.objectRender.selectedGraphicObjectsExists(); var textPr = this.objectRender.controller.getParagraphTextPr(); var paraPr = this.objectRender.controller.getParagraphParaPr(); var cell_info = new asc_CCellInfo(); cell_info.name = this._getColumnTitle(c1) + this._getRowTitle(r1); cell_info.formula = c.getFormula(); cell_info.text = c.getValueForEdit(); this.isUpdateSelection = false; if ( isGraphicObject && textPr && paraPr ) { this.isUpdateSelection = true; var horAlign = "center"; switch (paraPr.Jc) { case align_Left: horAlign = "left"; break; case align_Right: horAlign = "right"; break; case align_Center: horAlign = "center"; break; case align_Justify: horAlign = "justify"; break; } var vertAlign = "center"; /*switch (textPr.VertAlign) { case VERTICAL_ANCHOR_TYPE_TOP: vertAlign = "top"; break; case VERTICAL_ANCHOR_TYPE_CENTER: vertAlign = "center"; break; case VERTICAL_ANCHOR_TYPE_BOTTOM: vertAlign = "bottom"; break; }*/ cell_info.halign = horAlign; cell_info.valign = vertAlign; } else { cell_info.halign = c.getAlignHorizontalByValue().toLowerCase(); cell_info.valign = c.getAlignVertical().toLowerCase(); } cell_info.isFormatTable = this.autoFilters.searchRangeInTableParts(activeCell, this); cell_info.styleName = c.getStyleName(); cell_info.flags = new asc_CCellFlag(); cell_info.flags.merge = !!this._getMergedCellsRange(c1, r1); cell_info.flags.shrinkToFit = c.getShrinkToFit(); cell_info.flags.wrapText = c.getWrap(); var graphicObjects = this.objectRender.getSelectedGraphicObjects(); if ( graphicObjects.length == 1 ) cell_info.flags.selectionType = this.objectRender.getGraphicSelectionType(graphicObjects[0].Id); else cell_info.flags.selectionType = this.activeRange.type; cell_info.flags.lockText = ("" !== cell_info.text && (isNumberFormat || "" !== cell_info.formula)); cell_info.font = new asc_CFont(); if ( isGraphicObject && textPr && paraPr ) { cell_info.font.name = textPr.FontFamily.Name; cell_info.font.size = textPr.FontSize; cell_info.font.bold = textPr.Bold; cell_info.font.italic = textPr.Italic; cell_info.font.underline = textPr.Underline; cell_info.font.strikeout = textPr.Strikeout; cell_info.font.subscript = (textPr.VertAlign == vertalign_SubScript) ? true : false; cell_info.font.superscript = (textPr.VertAlign == vertalign_SuperScript) ? true : false; if ( textPr.Color ) cell_info.font.color = CreateAscColorCustom(textPr.Color.r, textPr.Color.g, textPr.Color.b); } else { cell_info.font.name = c.getFontname(); cell_info.font.size = c.getFontsize(); cell_info.font.bold = c.getBold(); cell_info.font.italic = c.getItalic(); cell_info.font.underline = ("none" !== c.getUnderline()); // ToDo убрать, когда будет реализовано двойное подчеркивание cell_info.font.strikeout = c.getStrikeout(); cell_info.font.subscript = fa === "subscript"; cell_info.font.superscript = fa === "superscript"; cell_info.font.color = (fc ? asc_obj2Color(fc) : asc_n2Color(c_opt.defaultState.colorNumber)); } cell_info.fill = new asc_CFill((null !== bg && undefined !== bg) ? asc_obj2Color(bg) : bg); cell_info.border = new asc_CBorders(); cell_info.border.left = new asc_CBorder(b.l.w, b.l.s, b.l.c); cell_info.border.top = new asc_CBorder(b.t.w, b.t.s, b.t.c); cell_info.border.right = new asc_CBorder(b.r.w, b.r.s, b.r.c); cell_info.border.bottom = new asc_CBorder(b.b.w, b.b.s, b.b.c); cell_info.border.diagDown = new asc_CBorder(b.dd.w, b.dd.s, b.dd.c); cell_info.border.diagUp = new asc_CBorder(b.du.w, b.du.s, b.du.c); cell_info.numFormatType = c.getNumFormatType(); // Получаем гиперссылку var ar = this.activeRange.clone(); var range = this.model.getRange3(ar.r1, ar.c1, ar.r2, ar.c2); var hyperlink = range.getHyperlink (); if (null != hyperlink) { // Гиперлинк var oHyperlink = new asc_CHyperlink(); // Range для гиперссылки var hyperlinkRange = hyperlink.Ref.getBBox0(); oHyperlink.asc_setHyperlinkRange (hyperlinkRange); // Тип гиперссылки var type = (null !== hyperlink.Hyperlink) ? c_oAscHyperlinkType.WebLink : c_oAscHyperlinkType.RangeLink; oHyperlink.asc_setType (type); if (c_oAscHyperlinkType.RangeLink === type) { // // ToDo переделать это место (парсить должны в момент открытия и добавления ссылки) var result = parserHelp.parse3DRef (hyperlink.Location); if (null !== result) { oHyperlink.asc_setSheet (result.sheet); oHyperlink.asc_setRange (result.range); } } oHyperlink.asc_setLocation (hyperlink.Location); oHyperlink.asc_setTooltip (hyperlink.Tooltip); oHyperlink.asc_setHyperlinkUrl (hyperlink.Hyperlink); cell_info.hyperlink = oHyperlink; cell_info.hyperlink.asc_setText (cell_info.text); } else cell_info.hyperlink = null; if(bExt) { cell_info.innertext = c.getValue(); cell_info.numFormat = c.getNumFormatStr(); } if (false !== this.collaborativeEditing.isCoAuthoringExcellEnable()) { // Разрешено совместное редактирование var sheetId = this.model.getId(); // Пересчет для входящих ячеек в добавленные строки/столбцы var isIntersection = this._recalcRangeByInsertRowsAndColumns(sheetId, ar); if (false === isIntersection) { var lockInfo = this.collaborativeEditing.getLockInfo(c_oAscLockTypeElem.Range, /*subType*/null, sheetId, new asc_CCollaborativeRange(ar.c1, ar.r1, ar.c2, ar.r2)); if (false !== this.collaborativeEditing.getLockIntersection(lockInfo, c_oAscLockTypes.kLockTypeOther, /*bCheckOnlyLockAll*/false)) { // Уже ячейку кто-то редактирует cell_info.isLocked = true; } } } return cell_info; }, // Получаем координаты активной ячейки getActiveCellCoord: function () { var xL = this.getCellLeft(this.activeRange.startCol, /*pt*/1); var yL = this.getCellTop(this.activeRange.startRow, /*pt*/1); // Пересчитываем X и Y относительно видимой области xL -= (this.cols[this.visibleRange.c1].left - this.cellsLeft); yL -= (this.rows[this.visibleRange.r1].top - this.cellsTop); // Пересчитываем в px xL *= asc_getcvt( 1/*pt*/, 0/*px*/, this._getPPIX() ); yL *= asc_getcvt( 1/*pt*/, 0/*px*/, this._getPPIY() ); var width = this.getColumnWidth (this.activeRange.startCol, /*px*/0); var height = this.getRowHeight(this.activeRange.startRow, /*px*/0); return new asc_CCellRect (xL, yL, width, height); }, setSelection: function (range, validRange) { // Проверка на валидность range. if (validRange && (range.c2 >= this.nColsCount || range.r2 >= this.nRowsCount)) { if (range.c2 >= this.nColsCount) this.expandColsOnScroll(false, true, range.c2 + 1); if (range.r2 >= this.nRowsCount) this.expandRowsOnScroll(false, true, range.r2 + 1); } this.cleanSelection(); // Проверка на всякий случай if (!(range instanceof asc_Range)) { range = asc_Range (range.c1, range.r1, range.c2, range.r2); } if(gc_nMaxCol0 === range.c2 || gc_nMaxRow0 === range.r2) { range = range.clone(); if(gc_nMaxCol0 === range.c2) range.c2 = this.cols.length - 1; if(gc_nMaxRow0 === range.r2) range.r2 = this.rows.length - 1; } this.activeRange = range; this.activeRange.type = c_oAscSelectionType.RangeCells; this.activeRange.startCol = range.c1; this.activeRange.startRow = range.r1; // Нормализуем range this.activeRange.normalize(); this._drawSelection(); this._trigger("selectionNameChanged", this.getSelectionName(/*bRangeText*/false)); this._trigger("selectionChanged", this.getSelectionInfo()); return this._calcActiveRangeOffset(); }, changeSelectionStartPoint: function (x, y, isCoord, isSelectMode) { var ar = (this.isFormulaEditMode) ? this.arrActiveFormulaRanges[this.arrActiveFormulaRanges.length - 1]: this.activeRange; var sc = ar.startCol, sr = ar.startRow, ret = {}; this.cleanSelection(); var commentList = this.cellCommentator.getCommentsXY(x, y); if ( !commentList.length ) { this.model.workbook.handlers.trigger("asc_onHideComment"); this.cellCommentator.resetLastSelectedId(); } if (isCoord) { var drawingInfo = this.objectRender.checkCursorDrawingObject(x, y); if ( drawingInfo ) { this._drawGraphic(); this.objectRender.OnUpdateOverlay(); } else { if ( this.objectRender.controller.curState.id != STATES_ID_BEGIN_TRACK_NEW_SHAPE ) { this.objectRender.unselectDrawingObjects(); asc["editor"].isStartAddShape = false; if ( this.isUpdateSelection ) this._trigger("selectionChanged", this.getSelectionInfo()); } } // move active range to coordinates x,y this._moveActiveCellToXY(x, y); } else { // move active range to offset x,y this._moveActiveCellToOffset(x, y); ret = this._calcActiveRangeOffset(); } if (!this.isCellEditMode && (sc !== ar.startCol || sr !== ar.startRow)) { if (!this.isSelectionDialogMode) { this._trigger("selectionNameChanged", this.getSelectionName(/*bRangeText*/false)); if (!isSelectMode) this._trigger("selectionChanged", this.getSelectionInfo()); } else { // Смена диапазона this._trigger("selectionRangeChanged", this.getSelectionRangeValue()); } } if ( drawingInfo && drawingInfo.isGraphicObject ) { // отправляем евент для получения свойств картинки, шейпа или группы this._trigger("selectionChanged", this.getSelectionInfo()); } else this.drawDepCells(); return ret; }, // Смена селекта по нажатию правой кнопки мыши changeSelectionStartPointRightClick: function (x, y) { // Выделяем объект var graphicCursorInfo = this.objectRender.checkCursorDrawingObject(x, y); if ( !graphicCursorInfo ) this.objectRender.unselectDrawingObjects(); var ar = this.activeRange; // Получаем координаты левого верхнего угла выделения var xL = this.getCellLeft(ar.c1, /*pt*/1); var yL = this.getCellTop(ar.r1, /*pt*/1); // Получаем координаты правого нижнего угла выделения var xR = this.getCellLeft(ar.c2, /*pt*/1) + this.cols[ar.c2].width; var yR = this.getCellTop(ar.r2, /*pt*/1) + this.rows[ar.r2].height; // Пересчитываем координаты var _x = x * asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIX() ); var _y = y * asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIY() ); var isInSelection = false; // Проверяем попали ли мы в выделение if (_x < this.cellsLeft && _y < this.cellsTop && c_oAscSelectionType.RangeMax === ar.type) { // Выделено все isInSelection = true; } else if (_x > this.cellsLeft && _y > this.cellsTop) { // Пересчитываем X и Y относительно видимой области _x += (this.cols[this.visibleRange.c1].left - this.cellsLeft); _y += (this.rows[this.visibleRange.r1].top - this.cellsTop); if (xL <= _x && _x <= xR && yL <= _y && _y <= yR) { // Попали в выделение ячеек isInSelection = true; } } else if (x <= this.cellsLeft && y >= this.cellsTop && c_oAscSelectionType.RangeRow === ar.type) { // Выделены строки // Пересчитываем Y относительно видимой области _y += (this.rows[this.visibleRange.r1].top - this.cellsTop); if (yL <= _y && _y <= yR) { // Попали в выделение ячеек isInSelection = true; } } else if (y <= this.cellsTop && x >= this.cellsLeft && c_oAscSelectionType.RangeCol === ar.type) { // Выделены столбцы // Пересчитываем X относительно видимой области _x += (this.cols[this.visibleRange.c1].left - this.cellsLeft); if (xL <= _x && _x <= xR) { // Попали в выделение ячеек isInSelection = true; } } if (!isInSelection) { // Не попали в выделение (меняем первую точку) this.cleanSelection(); this._moveActiveCellToXY(x, y); if ( !graphicCursorInfo ) this._drawSelection(); this._trigger("selectionNameChanged", this.getSelectionName(/*bRangeText*/false)); this._trigger("selectionChanged", this.getSelectionInfo()); return false; } if ( !graphicCursorInfo ) this._drawSelection(); return true; }, changeSelectionEndPoint: function (x, y, isCoord, isSelectMode) { var ar = (this.isFormulaEditMode) ? this.arrActiveFormulaRanges[this.arrActiveFormulaRanges.length - 1] : this.activeRange; var arOld = ar.clone(); var arnOld = ar.clone(true); var ep = isCoord ? this._calcSelectionEndPointByXY(x, y) : this._calcSelectionEndPointByOffset(x, y); var epOld, ret; if (ar.c2 !== ep.c2 || ar.r2 !== ep.r2) { this.cleanSelection(); ar.assign(ar.startCol, ar.startRow, ep.c2, ep.r2); if (ar.type === c_oAscSelectionType.RangeCells) { this._fixSelectionOfMergedCells(); while (!isCoord && arnOld.isEqual( ar.clone(true) )) { ar.c2 = ep.c2; ar.r2 = ep.r2; epOld = $.extend({}, ep); ep = this._calcSelectionEndPointByOffset(x<0?-1:x>0?+1:0, y<0?-1:y>0?+1:0); ar.assign(ar.startCol, ar.startRow, ep.c2, ep.r2); this._fixSelectionOfMergedCells(); if (ep.c2 === epOld.c2 && ep.r2 === epOld.r2) {break;} } } if (!isCoord) this._fixSelectionOfHiddenCells(ar.c2 - arOld.c2 >= 0 ? +1 : -1, ar.r2 - arOld.r2 >= 0 ? +1 : -1); this._drawSelection(); this.drawDepCells(); } ret = this._calcActiveRangeOffset(); if (!this.isCellEditMode && !arnOld.isEqual(ar.clone(true))) { if (!this.isSelectionDialogMode) { this._trigger("selectionNameChanged", this.getSelectionName(/*bRangeText*/true)); if (!isSelectMode) this._trigger("selectionChanged", this.getSelectionInfo(false)); } else { // Смена диапазона this._trigger("selectionRangeChanged", this.getSelectionRangeValue()); } } return ret; }, // Окончание выделения changeSelectionDone: function () { if (this.isFormulaEditMode) { // Нормализуем range this.arrActiveFormulaRanges[this.arrActiveFormulaRanges.length - 1].normalize(); } else { // Нормализуем range this.activeRange.normalize(); } }, // Обработка движения в выделенной области changeSelectionActivePoint: function (dc, dr) { var ret; var ar = this.activeRange; var arMerge = this._getMergedCellsRange (ar.c1, ar.r1); // Если в выделенной области только одна ячейка, то просто сдвигаемся if (ar.c1 === ar.c2 && ar.r1 === ar.r2 || arMerge && ar.c1 === arMerge.c1 && ar.r1 === arMerge.r1 && ar.c2 === arMerge.c2 && ar.r2 === arMerge.r2) return this.changeSelectionStartPoint(dc, dr, /*isCoord*/false, /*isSelectMode*/false); // Очищаем выделение this.cleanSelection(); // Двигаемся по выделенной области this._moveActivePointInSelection(dc, dr); // Перерисовываем this._drawSelection(); // Смотрим, ушли ли мы за границу видимой области ret = this._calcActiveCellOffset(); // Эвент обновления this._trigger("selectionNameChanged", this.getSelectionName(/*bRangeText*/false)); this._trigger("selectionChanged", this.getSelectionInfo()); return ret; }, // ----- Changing cells ----- /* Функция для работы автозаполнения (selection). (x, y) - координаты точки мыши на области */ changeSelectionFillHandle: function (x, y) { // Возвращаемый результат var ret = null; // Если мы только первый раз попали сюда, то копируем выделенную область if (null === this.activeFillHandle) { this.activeFillHandle = this.activeRange.clone(true); // Для первого раза нормализуем (т.е. первая точка - это левый верхний угол) this.activeFillHandle.normalize(); return ret; } // Пересчитываем координаты x *= asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIX() ); y *= asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIY() ); // Очищаем выделение, будем рисовать заново this.cleanSelection(); // Копируем выделенную область var ar = this.activeRange.clone(true); // Получаем координаты левого верхнего угла выделения var xL = this.getCellLeft(ar.c1, /*pt*/1); var yL = this.getCellTop(ar.r1, /*pt*/1); // Получаем координаты правого нижнего угла выделения var xR = this.getCellLeft(ar.c2, /*pt*/1) + this.cols[ar.c2].width; var yR = this.getCellTop(ar.r2, /*pt*/1) + this.rows[ar.r2].height; // range для пересчета видимой области var activeFillHandleCopy; // Колонка по X и строка по Y var colByX = this._findColUnderCursor (x, /*canReturnNull*/false, /*dX*/true).col; var rowByY = this._findRowUnderCursor (y, /*canReturnNull*/false, /*dX*/true).row; // Колонка по X и строка по Y (без половинчатого счета). Для сдвига видимой области var colByXNoDX = this._findColUnderCursor (x, /*canReturnNull*/false, /*dX*/false).col; var rowByYNoDY = this._findRowUnderCursor (y, /*canReturnNull*/false, /*dX*/false).row; // Сдвиг в столбцах и строках от крайней точки var dCol; var dRow; // Пересчитываем X и Y относительно видимой области x += (this.cols[this.visibleRange.c1].left - this.cellsLeft); y += (this.rows[this.visibleRange.r1].top - this.cellsTop); // Вычисляем расстояние от (x, y) до (xL, yL) var dXL = x - xL; var dYL = y - yL; // Вычисляем расстояние от (x, y) до (xR, yR) var dXR = x - xR; var dYR = y - yR; var dXRMod; var dYRMod; // Определяем область попадания и точку /* (1) (2) (3) ------------|-----------------------|------------ | | (4) | (5) | (6) | | ------------|-----------------------|------------ (7) (8) (9) */ // Область точки (x, y) var _tmpArea = 0; if (dXR <= 0){ // Области (1), (2), (4), (5), (7), (8) if (dXL <= 0){ // Области (1), (4), (7) if (dYR <= 0) { // Области (1), (4) if (dYL <= 0) { // Область (1) _tmpArea = 1; } else { // Область (4) _tmpArea = 4; } } else { // Область (7) _tmpArea = 7; } } else { // Области (2), (5), (8) if (dYR <= 0) { // Области (2), (5) if (dYL <= 0) { // Область (2) _tmpArea = 2; } else { // Область (5) _tmpArea = 5; } } else { // Область (3) _tmpArea = 8; } } } else { // Области (3), (6), (9) if (dYR <= 0){ // Области (3), (6) if (dYL <= 0){ // Область (3) _tmpArea = 3; } else { // Область (6) _tmpArea = 6; } } else { // Область (9) _tmpArea = 9; } } // Проверяем, в каком направлении движение switch (_tmpArea){ case 2: case 8: // Двигаемся по вертикали. this.fillHandleDirection = 1; break; case 4: case 6: // Двигаемся по горизонтали. this.fillHandleDirection = 0; break; case 1: // Сравниваем расстояния от точки до левого верхнего угла выделения dXRMod = Math.abs(x - xL); dYRMod = Math.abs(y - yL); // Сдвиги по столбцам и строкам dCol = Math.abs(colByX - ar.c1); dRow = Math.abs(rowByY - ar.r1); // Определим направление позднее this.fillHandleDirection = -1; break; case 3: // Сравниваем расстояния от точки до правого верхнего угла выделения dXRMod = Math.abs(x - xR); dYRMod = Math.abs(y - yL); // Сдвиги по столбцам и строкам dCol = Math.abs(colByX - ar.c2); dRow = Math.abs(rowByY - ar.r1); // Определим направление позднее this.fillHandleDirection = -1; break; case 7: // Сравниваем расстояния от точки до левого нижнего угла выделения dXRMod = Math.abs(x - xL); dYRMod = Math.abs(y - yR); // Сдвиги по столбцам и строкам dCol = Math.abs(colByX - ar.c1); dRow = Math.abs(rowByY - ar.r2); // Определим направление позднее this.fillHandleDirection = -1; break; case 5: case 9: // Сравниваем расстояния от точки до правого нижнего угла выделения dXRMod = Math.abs(dXR); dYRMod = Math.abs(dYR); // Сдвиги по столбцам и строкам dCol = Math.abs(colByX - ar.c2); dRow = Math.abs(rowByY - ar.r2); // Определим направление позднее this.fillHandleDirection = -1; break; } //console.log(_tmpArea); // Возможно еще не определили направление if (-1 === this.fillHandleDirection) { // Проверим сдвиги по столбцам и строкам, если не поможет, то рассчитываем по расстоянию if (0 === dCol && 0 !== dRow) { // Двигаемся по вертикали. this.fillHandleDirection = 1; } else if (0 !== dCol && 0 === dRow) { // Двигаемся по горизонтали. this.fillHandleDirection = 0; } else if (dXRMod >= dYRMod){ // Двигаемся по горизонтали. this.fillHandleDirection = 0; } else { // Двигаемся по вертикали. this.fillHandleDirection = 1; } } // Проверяем, в каком направлении движение if (0 === this.fillHandleDirection) { // Определяем область попадания и точку /* | | | | (1) | (2) | (3) | | | | */ if (dXR <= 0){ // Область (1) или (2) if (dXL <= 0) { // Область (1) this.fillHandleArea = 1; } else { // Область (2) this.fillHandleArea = 2; } } else { // Область (3) this.fillHandleArea = 3; } // Находим колонку для точки this.activeFillHandle.c2 = colByX; switch(this.fillHandleArea) { case 1: // Первая точка (xR, yR), вторая точка (x, yL) this.activeFillHandle.c1 = ar.c2; this.activeFillHandle.r1 = ar.r2; this.activeFillHandle.r2 = ar.r1; // Когда идем назад, должна быть колонка на 1 больше this.activeFillHandle.c2 += 1; // Случай, если мы еще не вышли из внутренней области if (this.activeFillHandle.c2 == ar.c1) this.fillHandleArea = 2; break; case 2: // Первая точка (xR, yR), вторая точка (x, yL) this.activeFillHandle.c1 = ar.c2; this.activeFillHandle.r1 = ar.r2; this.activeFillHandle.r2 = ar.r1; // Когда идем назад, должна быть колонка на 1 больше this.activeFillHandle.c2 += 1; if (this.activeFillHandle.c2 > this.activeFillHandle.c1){ // Ситуация половинки последнего столбца this.activeFillHandle.c1 = ar.c1; this.activeFillHandle.r1 = ar.r1; this.activeFillHandle.c2 = ar.c1; this.activeFillHandle.r2 = ar.r1; } break; case 3: // Первая точка (xL, yL), вторая точка (x, yR) this.activeFillHandle.c1 = ar.c1; this.activeFillHandle.r1 = ar.r1; this.activeFillHandle.r2 = ar.r2; break; } // Копируем в range для пересчета видимой области activeFillHandleCopy = this.activeFillHandle.clone(); activeFillHandleCopy.c2 = colByXNoDX; } else { // Определяем область попадания и точку /* (1) ____________________________ (2) ____________________________ (3) */ if (dYR <= 0){ // Область (1) или (2) if (dYL <= 0) { // Область (1) this.fillHandleArea = 1; } else { // Область (2) this.fillHandleArea = 2; } } else { // Область (3) this.fillHandleArea = 3; } // Находим строку для точки this.activeFillHandle.r2 = rowByY; switch(this.fillHandleArea) { case 1: // Первая точка (xR, yR), вторая точка (xL, y) this.activeFillHandle.c1 = ar.c2; this.activeFillHandle.r1 = ar.r2; this.activeFillHandle.c2 = ar.c1; // Когда идем назад, должна быть строка на 1 больше this.activeFillHandle.r2 += 1; // Случай, если мы еще не вышли из внутренней области if (this.activeFillHandle.r2 == ar.r1) this.fillHandleArea = 2; break; case 2: // Первая точка (xR, yR), вторая точка (xL, y) this.activeFillHandle.c1 = ar.c2; this.activeFillHandle.r1 = ar.r2; this.activeFillHandle.c2 = ar.c1; // Когда идем назад, должна быть строка на 1 больше this.activeFillHandle.r2 += 1; if (this.activeFillHandle.r2 > this.activeFillHandle.r1){ // Ситуация половинки последней строки this.activeFillHandle.c1 = ar.c1; this.activeFillHandle.r1 = ar.r1; this.activeFillHandle.c2 = ar.c1; this.activeFillHandle.r2 = ar.r1; } break; case 3: // Первая точка (xL, yL), вторая точка (xR, y) this.activeFillHandle.c1 = ar.c1; this.activeFillHandle.r1 = ar.r1; this.activeFillHandle.c2 = ar.c2; break; } // Копируем в range для пересчета видимой области activeFillHandleCopy = this.activeFillHandle.clone(); activeFillHandleCopy.r2 = rowByYNoDY; } //console.log ("row1: " + this.activeFillHandle.r1 + " col1: " + this.activeFillHandle.c1 + " row2: " + this.activeFillHandle.r2 + " col2: " + this.activeFillHandle.c2); // Перерисовываем this._drawSelection(); // Смотрим, ушли ли мы за границу видимой области ret = this._calcFillHandleOffset(activeFillHandleCopy); return ret; }, /* Функция для применения автозаполнения */ applyFillHandle: function (x, y, ctrlPress) { var t = this; // Текущее выделение (к нему применится автозаполнение) var arn = t.activeRange.clone(true); arn.normalize(); var range = t.model.getRange3(arn.r1, arn.c1, arn.r2, arn.c2); // Были ли изменения var bIsHaveChanges = false; // Вычисляем индекс сдвига var nIndex = 0; /*nIndex*/ if (0 === this.fillHandleDirection){ // Горизонтальное движение nIndex = this.activeFillHandle.c2 - arn.c1; if (2 === this.fillHandleArea) { // Для внутренности нужно вычесть 1 из значения bIsHaveChanges = arn.c2 !== (this.activeFillHandle.c2 - 1); } else bIsHaveChanges = arn.c2 !== this.activeFillHandle.c2; } else { // Вертикальное движение nIndex = this.activeFillHandle.r2 - arn.r1; if (2 === this.fillHandleArea) { // Для внутренности нужно вычесть 1 из значения bIsHaveChanges = arn.r2 !== (this.activeFillHandle.r2 - 1); } else bIsHaveChanges = arn.r2 !== this.activeFillHandle.r2; } // Меняли ли что-то if (bIsHaveChanges && (this.activeFillHandle.r1 !== this.activeFillHandle.r2 || this.activeFillHandle.c1 !== this.activeFillHandle.c2)){ // Диапазон ячеек, который мы будем менять var changedRange = this.activeRange.clone(true); // Очищаем выделение this.cleanSelection(); if (2 === this.fillHandleArea) { // Мы внутри, будет удаление, нормируем и cбрасываем первую ячейку this.activeRange.normalize(); this.activeRange.startCol = this.activeRange.c1; this.activeRange.startRow = this.activeRange.r1; // Проверяем, удалили ли мы все (если да, то область не меняется) if (arn.c1 !== this.activeFillHandle.c2 || arn.r1 !== this.activeFillHandle.r2) { // Уменьшаем диапазон (мы удалили не все) if (0 === this.fillHandleDirection){ // Горизонтальное движение (для внутренности необходимо вычесть 1) this.activeRange.c2 = this.activeFillHandle.c2 - 1; changedRange.c1 = changedRange.c2; changedRange.c2 = this.activeFillHandle.c2; } else { // Вертикальное движение (для внутренности необходимо вычесть 1) this.activeRange.r2 = this.activeFillHandle.r2 - 1; changedRange.r1 = changedRange.r2; changedRange.r2 = this.activeFillHandle.r2; } } } else { // Мы вне выделения. Увеличиваем диапазон if (0 === this.fillHandleDirection){ // Горизонтальное движение if (1 === this.fillHandleArea){ this.activeRange.c1 = this.activeFillHandle.c2; changedRange.c2 = changedRange.c1 - 1; changedRange.c1 = this.activeFillHandle.c2; } else { this.activeRange.c2 = this.activeFillHandle.c2; changedRange.c1 = changedRange.c2 + 1; changedRange.c2 = this.activeFillHandle.c2; } } else { // Вертикальное движение if (1 === this.fillHandleArea){ this.activeRange.r1 = this.activeFillHandle.r2; changedRange.r2 = changedRange.r1 - 1; changedRange.r1 = this.activeFillHandle.r2; } else { this.activeRange.r2 = this.activeFillHandle.r2; changedRange.r1 = changedRange.r2 + 1; changedRange.r2 = this.activeFillHandle.r2; } } // После увеличения, нужно обновить больший range arn = this.activeRange.clone(true); } changedRange.normalize(); var applyFillHandleCallback = function (res) { if (res) { // Автозаполняем ячейки t.model.onStartTriggerAction(); range.promote(/*bCtrl*/ctrlPress, /*bVertical*/(1 === t.fillHandleDirection), nIndex); // Вызываем функцию пересчета для заголовков форматированной таблицы t.autoFilters._renameTableColumn(t, arn); t.model.onEndTriggerAction(); } // Сбрасываем параметры автозаполнения t.activeFillHandle = null; t.fillHandleDirection = -1; // Обновляем все данные строки (т.к. могли быть ячейки, в которые не убрался текст, выровненные по левому и по правому краю, которые нужно перерисовать) arn.c1 = 0; arn.c2 = gc_nMaxCol0; // Обновляем выделенные ячейки t.isChanged = true; t._updateCellsRange(arn); }; // Можно ли применять автозаполнение ? this._isLockedCells (changedRange, /*subType*/null, applyFillHandleCallback); } else { // Ничего не менялось, сбрасываем выделение this.cleanSelection(); // Сбрасываем параметры автозаполнения this.activeFillHandle = null; this.fillHandleDirection = -1; // Перерисовываем this._drawSelection(); } }, /* Функция для работы перемещения диапазона (selection). (x, y) - координаты точки мыши на области * ToDo нужно переделать, чтобы moveRange появлялся только после сдвига от текущей ячейки */ changeSelectionMoveRangeHandle: function (x, y, ctrlKey) { // Возвращаемый результат var ret = null; // Пересчитываем координаты x *= asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIX() ); y *= asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIY() ); var ar = this.activeRange.clone(true); // Колонка по X и строка по Y var colByX = this._findColUnderCursor (x, /*canReturnNull*/false, /*dX*/false).col; var rowByY = this._findRowUnderCursor (y, /*canReturnNull*/false, /*dY*/false).row; // Если мы только первый раз попали сюда, то копируем выделенную область if (null === this.startCellMoveRange) { // Учитываем погрешность (мы должны быть внутри диапазона при старте) if (colByX < ar.c1) { colByX = ar.c1; } else if (colByX > ar.c2) { colByX = ar.c2; } if (rowByY < ar.r1) { rowByY = ar.r1; } else if (rowByY > ar.r2) { rowByY = ar.r2; } this.startCellMoveRange = asc_Range(colByX, rowByY, colByX, rowByY); this.startCellMoveRange.isChanged = false; // Флаг, сдвигались ли мы от первоначального диапазона return ret; } // Разница, на сколько мы сдвинулись var colDelta = colByX - this.startCellMoveRange.c1; var rowDelta = rowByY - this.startCellMoveRange.r1; // Проверяем, нужно ли отрисовывать перемещение (сдвигались или нет) if (false === this.startCellMoveRange.isChanged && 0 === colDelta && 0 === rowDelta) return ret; // Выставляем флаг this.startCellMoveRange.isChanged = true; // Очищаем выделение, будем рисовать заново this.cleanSelection(); this.activeMoveRange = ar; // Для первого раза нормализуем (т.е. первая точка - это левый верхний угол) this.activeMoveRange.normalize(); // Выставляем this.activeMoveRange.c1 += colDelta; if (0 > this.activeMoveRange.c1) { colDelta -= this.activeMoveRange.c1; this.activeMoveRange.c1 = 0; } this.activeMoveRange.c2 += colDelta; this.activeMoveRange.r1 += rowDelta; if (0 > this.activeMoveRange.r1) { rowDelta -= this.activeMoveRange.r1; this.activeMoveRange.r1 = 0; } this.activeMoveRange.r2 += rowDelta; // Увеличиваем, если выходим за область видимости // Critical Bug 17413 while (!this.cols[this.activeMoveRange.c2]) { this.expandColsOnScroll(true); this._trigger("reinitializeScrollX"); } while (!this.rows[this.activeMoveRange.r2]) { this.expandRowsOnScroll(true); this._trigger("reinitializeScrollY"); } // Перерисовываем this._drawSelection(); var d = { deltaX : this.activeMoveRange.c1 < this.visibleRange.c1 ? this.activeMoveRange.c1-this.visibleRange.c1 : this.activeMoveRange.c2>this.visibleRange.c2 ? this.activeMoveRange.c2-this.visibleRange.c2 : 0, deltaY : this.activeMoveRange.r1 < this.visibleRange.r1 ? this.activeMoveRange.r1-this.visibleRange.r1 : this.activeMoveRange.r2>this.visibleRange.r2 ? this.activeMoveRange.r2-this.visibleRange.r2 : 0 }; while ( this._isColDrawnPartially( this.activeMoveRange.c2, this.visibleRange.c1 + d.deltaX) ) {++d.deltaX;} while ( this._isRowDrawnPartially( this.activeMoveRange.r2, this.visibleRange.r1 + d.deltaY) ) {++d.deltaY;} return d; }, changeSelectionMoveResizeRangeHandle: function (x, y, targetInfo) { // Возвращаемый результат if( !targetInfo ) return null; var indexFormulaRange = targetInfo.indexFormulaRange, d, ret; // Пересчитываем координаты x *= asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIX() ); y *= asc_getcvt( 0/*px*/, 1/*pt*/, this._getPPIY() ); var ar = 0 == targetInfo.targetArr ? this.arrActiveFormulaRanges[indexFormulaRange].clone(true) : this.arrActiveChartsRanges[indexFormulaRange].clone(true); // Колонка по X и строка по Y var colByX = this._findColUnderCursor (x, /*canReturnNull*/false, /*dX*/false).col; var rowByY = this._findRowUnderCursor (y, /*canReturnNull*/false, /*dY*/false).row; // Если мы только первый раз попали сюда, то копируем выделенную область if (null === this.startCellMoveResizeRange) { if( (targetInfo.cursor == kCurNEResize || targetInfo.cursor == kCurSEResize) /* && 0 == targetInfo.targetArr */ ){ this.startCellMoveResizeRange = ar.clone(true); // this.startCellMoveResizeRange2 = asc_Range(this.startCellMoveResizeRange.c1, this.startCellMoveResizeRange.r1, this.startCellMoveResizeRange.c1, this.startCellMoveResizeRange.r1,true); this.startCellMoveResizeRange2 = asc_Range(targetInfo.col, targetInfo.row, targetInfo.col, targetInfo.row,true); } else{ this.startCellMoveResizeRange = ar.clone(true); if (colByX < ar.c1) { colByX = ar.c1; } else if (colByX > ar.c2) { colByX = ar.c2; } if (rowByY < ar.r1) { rowByY = ar.r1; } else if (rowByY > ar.r2) { rowByY = ar.r2; } this.startCellMoveResizeRange2 = asc_Range(colByX, rowByY, colByX, rowByY); } return null; } // Очищаем выделение, будем рисовать заново // this.cleanSelection(); this.overlayCtx.clear(); if( targetInfo.cursor == kCurNEResize || targetInfo.cursor == kCurSEResize ){ if( colByX < this.startCellMoveResizeRange2.c1 ){ ar.c2 = this.startCellMoveResizeRange2.c1; ar.c1 = colByX; } else if( colByX > this.startCellMoveResizeRange2.c1 ){ ar.c1 = this.startCellMoveResizeRange2.c1; ar.c2 = colByX; } else{ ar.c1 = this.startCellMoveResizeRange2.c1; ar.c2 = this.startCellMoveResizeRange2.c1 } if( rowByY < this.startCellMoveResizeRange2.r1 ){ if(this.visibleRange.r2 > ar.r2) ar.r2 = this.startCellMoveResizeRange2.r2; ar.r1 = rowByY; } else if( rowByY > this.startCellMoveResizeRange2.r1 ){ if(this.visibleRange.r1 < ar.r1) ar.r1 = this.startCellMoveResizeRange2.r1; if(this.visibleRange.r2 > ar.r2) ar.r2 = rowByY; } else{ ar.r1 = this.startCellMoveResizeRange2.r1; ar.r2 = this.startCellMoveResizeRange2.r1; } } else{ this.startCellMoveResizeRange.normalize(); var colDelta = colByX - this.startCellMoveResizeRange2.c1; var rowDelta = rowByY - this.startCellMoveResizeRange2.r1; ar.c1 = this.startCellMoveResizeRange.c1+colDelta; if (0 > ar.c1) { colDelta -= ar.c1; ar.c1 = 0; } ar.c2 = this.startCellMoveResizeRange.c2+colDelta; ar.r1 = this.startCellMoveResizeRange.r1+rowDelta; if (0 > ar.r1) { rowDelta -= ar.r1; ar.r1 = 0; } ar.r2 = this.startCellMoveResizeRange.r2+rowDelta; d = { deltaX : ar.c1 <= this.visibleRange.c1 ? ar.c1-this.visibleRange.c1 : ar.c2>=this.visibleRange.c2 ? ar.c2-this.visibleRange.c2 : 0, deltaY : ar.r1 <= this.visibleRange.r1 ? ar.r1-this.visibleRange.r1 : ar.r2>=this.visibleRange.r2 ? ar.r2-this.visibleRange.r2 : 0 } } if( 0 == targetInfo.targetArr ){ var _p = this.arrActiveFormulaRanges[indexFormulaRange].cursorePos, _l = this.arrActiveFormulaRanges[indexFormulaRange].formulaRangeLength, _a = this.arrActiveFormulaRanges[indexFormulaRange].isAbsolute; this.arrActiveFormulaRanges[indexFormulaRange] = ar.clone(true); this.arrActiveFormulaRanges[indexFormulaRange].cursorePos = _p; this.arrActiveFormulaRanges[indexFormulaRange].formulaRangeLength = _l; this.arrActiveFormulaRanges[indexFormulaRange].isAbsolute = _a; ret = this.arrActiveFormulaRanges[indexFormulaRange]; } else{ this.arrActiveChartsRanges[indexFormulaRange] = ar.clone(true); this.moveRangeDrawingObjectTo = ar; } this._drawSelection(); // слой c объектами должен быть выше селекта this.objectRender.raiseLayerDrawingObjects(); return { ar: ret, d:d }; }, /* Функция для применения перемещения диапазона */ applyMoveRangeHandle: function (ctrlKey) { var t = this; if (null === t.activeMoveRange) { // Сбрасываем параметры t.startCellMoveRange = null; return; } var arnFrom = t.activeRange.clone(true); var arnTo = t.activeMoveRange.clone(true); var resmove = t.model._prepareMoveRange(arnFrom, arnTo); if( resmove == -2 ){ t.model.workbook.handlers.trigger("asc_onError", c_oAscError.ID.CannotMoveRange, c_oAscError.Level.NoCritical); // Сбрасываем параметры t.activeMoveRange = null; t.startCellMoveRange = null; t.isChanged = true; t._updateCellsRange(new Range(0, 0, arnFrom.c2 > arnTo.c2 ? arnFrom.c2 : arnTo.c2, arnFrom.r2 > arnTo.r2 ? arnFrom.r2 : arnTo.r2), /*canChangeColWidth*/c_oAscCanChangeColWidth.none); // Перерисовываем t.cleanSelection(); t._drawSelection(); return false; } else if( resmove == -1 ){ t.model.workbook.handlers.trigger("asc_onConfirmAction", c_oAscConfirm.ConfirmReplaceRange, function(can){t.moveRangeHandle(arnFrom, arnTo, can, ctrlKey)} ); } else{ t.moveRangeHandle(arnFrom, arnTo, true, ctrlKey) } }, applyMoveResizeRangeHandle:function(target){ if( -1 == target.targetArr ) this.objectRender.moveRangeDrawingObject(this.startCellMoveResizeRange, this.moveRangeDrawingObjectTo, true); this.startCellMoveResizeRange = null; this.startCellMoveResizeRange2 = null; }, moveRangeHandle: function(arnFrom, arnTo, can, copyRange){ var t = this; var onApplyMoveRangeHandleCallback = function (isSuccess) { // Очищаем выделение t.cleanSelection(); if (true === isSuccess && !arnFrom.isEqual(arnTo) && can) { t.cleanDepCells(); History.Create_NewPoint(); History.SetSelection(arnFrom.clone()); History.SetSelectionRedo(arnTo.clone()); History.StartTransaction(); t.model._moveRange(arnFrom, arnTo, copyRange); t._updateCellsRange(arnTo); t.cleanSelection(); t.activeRange = arnTo.clone(true); t.activeRange.startRow = t.activeRange.r1; t.activeRange.startCol = t.activeRange.c1; t.objectRender.moveRangeDrawingObject(arnFrom, arnTo, false); // Вызываем функцию пересчета для заголовков форматированной таблицы t.autoFilters._renameTableColumn(t, arnFrom); t.autoFilters._renameTableColumn(t, arnTo); t.autoFilters.reDrawFilter(t, arnFrom); History.EndTransaction(); } // Сбрасываем параметры t.activeMoveRange = null; t.startCellMoveRange = null; t.isChanged = true; t._updateCellsRange(new Range(0, 0, arnFrom.c2 > arnTo.c2 ? arnFrom.c2 : arnTo.c2, arnFrom.r2 > arnTo.r2 ? arnFrom.r2 : arnTo.r2), /*canChangeColWidth*/c_oAscCanChangeColWidth.none); // Перерисовываем t.cleanSelection(); t._drawSelection(); }; this._isLockedCells ([arnFrom, arnTo], null, onApplyMoveRangeHandleCallback); }, setSelectionInfo: function (prop, val, onlyActive, isLocal) { // Проверка глобального лока if (this.collaborativeEditing.getGlobalLock()) return; var t = this; var checkRange = null; var arn = t.activeRange.clone(true); arn.startCol = t.activeRange.startCol; arn.startRow = t.activeRange.startRow; arn.type = t.activeRange.type; if (onlyActive) { checkRange = new asc_Range(arn.startCol, arn.startRow, arn.startCol, arn.startRow); } else { if (c_oAscSelectionType.RangeMax === arn.type) { checkRange = new asc_Range(0, 0, gc_nMaxCol0, gc_nMaxRow0); } else if (c_oAscSelectionType.RangeCol === arn.type) { checkRange = new asc_Range(arn.c1, 0, arn.c2, gc_nMaxRow0); } else if (c_oAscSelectionType.RangeRow === arn.type) { checkRange = new asc_Range(0, arn.r1, gc_nMaxCol0, arn.r2); } else { checkRange = arn; } } var onSelectionCallback = function (isSuccess) { if (false === isSuccess) return; var range; var canChangeColWidth = c_oAscCanChangeColWidth.none; var selectionRange; var bIsUpdate = true; if (onlyActive) { range = t.model.getRange3(arn.startRow, arn.startCol, arn.startRow, arn.startCol); } else { if (c_oAscSelectionType.RangeMax === arn.type) { range = t.model.getRange3(/*arn.r1*/0, /*arn.c1*/0, gc_nMaxRow0, gc_nMaxCol0); } else if (c_oAscSelectionType.RangeCol === arn.type) { range = t.model.getRange3(/*arn.r1*/0, arn.c1, gc_nMaxRow0, arn.c2); } else if (c_oAscSelectionType.RangeRow === arn.type) { range = t.model.getRange3(arn.r1, /*arn.c1*/0, arn.r2, gc_nMaxCol0); } else { range = t.model.getRange3(arn.r1, arn.c1, arn.r2, arn.c2); } } var isLargeRange = t._isLargeRange(range), callTrigger = false; var res, flag; var mc, r, c, cell; function makeBorder(b) { var border = {}; if (b === false) { border.s = kcbNone; } else if (b) { if (b.style !== null && b.style !== undefined) {border.s = b.style;} if (b.color !== null && b.color !== undefined) { if(b.color instanceof CAscColor) border.c = CorrectAscColor(b.color); } flag = flag || (b.width === null || b.style === null || b.color === null); } return border; } selectionRange = arn.clone(); t.model.onStartTriggerAction(); History.Create_NewPoint(); History.StartTransaction(); switch (prop) { case "fn": range.setFontname(val); canChangeColWidth = c_oAscCanChangeColWidth.numbers; break; case "fs": range.setFontsize(val); canChangeColWidth = c_oAscCanChangeColWidth.numbers; break; case "b": range.setBold(val); break; case "i": range.setItalic(val); break; case "u": range.setUnderline(val); break; case "s": range.setStrikeout(val); break; case "fa": range.setFontAlign(val); break; case "a": range.setAlignHorizontal(val); break; case "va": range.setAlignVertical(val); break; case "c": range.setFontcolor(val); break; case "bc": range.setFill((val) ? (val) : null); break; case "wrap": range.setWrap(val); break; case "shrink": range.setShrinkToFit(val); break; case "value": range.setValue(val); break; case "format": range.setNumFormat(val); canChangeColWidth = c_oAscCanChangeColWidth.numbers; break; case "angle": range.setAngle(val); break; case "border": if (isLargeRange) { callTrigger = true; t._trigger("slowOperation", true); } res = {}; flag = false; // None if (val.length < 1) { range.setBorder(null); break; } // Diagonal res.d = makeBorder( val[c_oAscBorderOptions.DiagD] || val[c_oAscBorderOptions.DiagU] ); res.dd = val[c_oAscBorderOptions.DiagD] ? true : false; res.du = val[c_oAscBorderOptions.DiagU] ? true : false; // Vertical res.l = makeBorder( val[c_oAscBorderOptions.Left] ); res.iv = makeBorder( val[c_oAscBorderOptions.InnerV] ); res.r = makeBorder( val[c_oAscBorderOptions.Right] ); // Horizontal res.t = makeBorder( val[c_oAscBorderOptions.Top] ); res.ih = makeBorder( val[c_oAscBorderOptions.InnerH] ); res.b = makeBorder( val[c_oAscBorderOptions.Bottom] ); // Change border range.setBorder(res, flag); break; case "merge": if (isLargeRange) { callTrigger = true; t._trigger("slowOperation", true); } switch (val) { case c_oAscMergeOptions.Unmerge: range.unmerge(); break; case c_oAscMergeOptions.MergeCenter: range.merge(val); break; case c_oAscMergeOptions.MergeAcross: for (res = arn.r1; res <= arn.r2; ++res) { t.model.getRange3(res, arn.c1, res, arn.c2).merge(val); } break; case c_oAscMergeOptions.Merge: range.merge(val); break; } // Должны обновить больший range, т.к. мы продолжаем строки в ячейках... arn.c1 = 0; arn.c2 = gc_nMaxCol0; break; case "sort": if (isLargeRange) { callTrigger = true; t._trigger("slowOperation", true); } var changes = range.sort(val, arn.startCol); t.cellCommentator.sortComments(arn, changes); break; case "empty": if (isLargeRange) { callTrigger = true; t._trigger("slowOperation", true); } /* отключаем отрисовку на случай необходимости пересчета ячеек, заносим ячейку, при необходимости в список перерисовываемых */ lockDraw(t.model.workbook); if (val === c_oAscCleanOptions.All) range.cleanAll(); if (val & c_oAscCleanOptions.Text || val & c_oAscCleanOptions.Formula) range.cleanText(); if (val & c_oAscCleanOptions.Format) range.cleanFormat(); // Если нужно удалить автофильтры - удаляем t.autoFilters.isEmptyAutoFilters(t, arn); // Вызываем функцию пересчета для заголовков форматированной таблицы t.autoFilters._renameTableColumn(t, arn); /* возвращаем отрисовку. и перерисовываем ячейки с предварительным пересчетом */ helpFunction(t.model.workbook); unLockDraw(t.model.workbook); // Должны обновить больший range, т.к. мы продолжаем строки в ячейках... arn.c1 = t.visibleRange.c1; arn.c2 = t.visibleRange.c2; break; case "changeDigNum": res = t.cols.slice(arn.c1, arn.c2+1).reduce(function(r,c){r.push(c.charCount);return r;}, []); range.shiftNumFormat(val, res); canChangeColWidth = c_oAscCanChangeColWidth.numbers; break; case "changeFontSize": mc = t._getMergedCellsRange(arn.startCol, arn.startRow); c = mc ? mc.c1 : arn.startCol; r = mc ? mc.r1 : arn.startRow; cell = t._getVisibleCell(c, r); if (undefined !== cell) { var oldFontSize = cell.getFontsize(); var newFontSize = asc_incDecFonSize(val, oldFontSize); if (null !== newFontSize) { range.setFontsize(newFontSize); canChangeColWidth = c_oAscCanChangeColWidth.numbers; } } break; case "style": range.setCellStyle(val); canChangeColWidth = c_oAscCanChangeColWidth.numbers; break; break; case "paste": var pasteExec = function() { if (isLargeRange) { callTrigger = true; t._trigger("slowOperation", true); } var selectData; if(isLocal) selectData = t._pasteFromLS(val); else selectData = t._setInfoAfterPaste(val,onlyActive); if (!selectData) { bIsUpdate = false; History.EndTransaction(); t.model.onEndTriggerAction(); return; } t.expandColsOnScroll(); t.expandRowsOnScroll(); var arrFormula = selectData[1]; lockDraw(t.model.workbook); for (var i = 0; i < arrFormula.length; ++i) {//!!! var rangeF = arrFormula[i].range; var valF = arrFormula[i].val; if(rangeF.isOneCell()) rangeF.setValue(valF); else { var oBBox = rangeF.getBBox0(); t.model._getCell(oBBox.r1, oBBox.c1).setValue(valF); } } helpFunction(t.model.workbook); unLockDraw(t.model.workbook); arn = selectData[0]; selectionRange = arn.clone(true); // Должны обновить больший range, т.к. мы продолжаем строки в ячейках... arn.c1 = 0; arn.c2 = gc_nMaxCol0; if (bIsUpdate) { if (callTrigger) { t._trigger("slowOperation", false); } t.isChanged = true; t._updateCellsRange(arn, canChangeColWidth); t._prepareCellTextMetricsCache(arn); } History.EndTransaction(); History.SetSelection(selectionRange); t.model.onEndTriggerAction(); }; //загрузка шрифтов, в случае удачи на callback вставляем текст var callbackFunc = function(res) { if (res) { //t._drawCollaborativeElements(true); t._loadFonts(val.fontsNew, function () { pasteExec(); if(val.addImages && val.addImages.length != 0) { for(var im = 0; im < val.addImages.length; im++)//вставляем изображения { var src = val.addImages[im].tag.src; if(src && 0 != src.indexOf("file://")) t.objectRender.addImageDrawingObject(src, { cell: val.addImages[im].curCell, width: val.addImages[im].tag.width, height: val.addImages[im].tag.height }); } } }); } else { History.EndTransaction(); t.model.onEndTriggerAction(); } }; var api = window["Asc"]["editor"]; if(isLocal)//вставляем текст из локального буфера pasteExec(); else { if(val.addImages == null || api.isChartEditor)//нет изображений { callbackFunc(true); } else//присутвуют изображения { t.collaborativeEditing.onStartCheckLock(); //на callback грузим шрифты и осуществляем вставку текста if (false === t.collaborativeEditing.getCollaborativeEditing()) { // Пользователь редактирует один: не ждем ответа, а сразу продолжаем редактирование callbackFunc(true); callbackFunc = undefined; return; } t.collaborativeEditing.onEndCheckLock(callbackFunc); } } return; case "hyperlink": if (val) { var type = (c_oAscHyperlinkType.RangeLink !== val.asc_getType()) ? c_oAscHyperlinkType.WebLink : c_oAscHyperlinkType.RangeLink; var location = null; if (c_oAscHyperlinkType.RangeLink === type) { var hyperlinkRangeTmp = t.model.getRange2(val.asc_getRange ()); if (null === hyperlinkRangeTmp) { bIsUpdate = false; break; } var hyperlinkRangeBBox0 = hyperlinkRangeTmp.getBBox0(); var hyperlinkRange = t._getCellTitle(hyperlinkRangeBBox0.c1, hyperlinkRangeBBox0.r1); if (hyperlinkRangeBBox0.c1 !== hyperlinkRangeBBox0.c2 || hyperlinkRangeBBox0.r1 !== hyperlinkRangeBBox0.r2) hyperlinkRange += ":" + t._getCellTitle(hyperlinkRangeBBox0.c2, hyperlinkRangeBBox0.r2); location = val.asc_getSheet() + "!" + hyperlinkRange; } var newHyperlink = new Hyperlink(); newHyperlink.Hyperlink = val.asc_getHyperlinkUrl(); newHyperlink.Location = location; newHyperlink.Ref = range; newHyperlink.Tooltip = val.asc_getTooltip(); range.setHyperlink(newHyperlink); // Вставим текст в активную ячейку (а не так, как MSExcel в первую ячейку диапазона) mc = t._getMergedCellsRange(arn.startCol, arn.startRow); c = mc ? mc.c1 : arn.startCol; r = mc ? mc.r1 : arn.startRow; if (null !== val.asc_getText()) { t.model.getRange3(r, c, r, c) .setValue(val.asc_getText()); // Вызываем функцию пересчета для заголовков форматированной таблицы t.autoFilters._renameTableColumn(t, arn); } break; } else { bIsUpdate = false; break; } default: bIsUpdate = false; break; } if (bIsUpdate) { if (callTrigger) { t._trigger("slowOperation", false); } t.isChanged = true; t._updateCellsRange(arn, canChangeColWidth); } History.EndTransaction(); History.SetSelection(selectionRange); t.model.onEndTriggerAction(); }; if ("paste" === prop) { // Для past свой диапазон if(isLocal) checkRange = t._pasteFromLS(val, true); else checkRange = t._setInfoAfterPaste(val, onlyActive, true); } this._isLockedCells (checkRange, /*subType*/null, onSelectionCallback); }, _setInfoAfterPaste: function (values,clipboard,isCheckSelection) { var t = this; var arn = t.activeRange.clone(true); var arrFormula = []; var numFor = 0; var rMax = values.length + values.rowSpanSpCount; if(values.rowCount && values.rowCount !== 0 && values.isOneTable) rMax = values.rowCount + arn.r1; var cMax = values.cellCount + arn.c1; var isMultiple = false; var firstCell = t.model.getRange3(arn.r1, arn.c1, arn.r1, arn.c1); var isMergedFirstCell = firstCell.hasMerged(); var rangeUnMerge = t.model.getRange3(arn.r1, arn.c1, rMax - 1, cMax - 1); var isOneMerge = false; //если вставляем в мерженную ячейку, диапазон которой больше или равен if (arn.c2 >= cMax -1 && arn.r2 >= rMax - 1 && isMergedFirstCell && isMergedFirstCell.c1 === arn.c1 && isMergedFirstCell.c2 === arn.c2 && isMergedFirstCell.r1 === arn.r1 && isMergedFirstCell.r2 === arn.r2 && cMax - arn.c1 === values[arn.r1][arn.c1][0].colSpan && rMax - arn.r1 === values[arn.r1][arn.c1][0].rowSpan) { if(!isCheckSelection) { values[arn.r1][arn.c1][0].colSpan = isMergedFirstCell.c2 -isMergedFirstCell.c1 + 1; values[arn.r1][arn.c1][0].rowSpan = isMergedFirstCell.r2 -isMergedFirstCell.r1 + 1; } isOneMerge = true; } else if(arn.c2 >= cMax -1 && arn.r2 >= rMax - 1 && values.isOneTable) { //если область кратная куску вставки var widthArea = arn.c2 - arn.c1 + 1; var heightArea = arn.r2 - arn.r1 + 1; var widthPasteFr = cMax - arn.c1; var heightPasteFr = rMax - arn.r1; //если кратны, то обрабатываем if(widthArea%widthPasteFr === 0 && heightArea%heightPasteFr === 0) { isMultiple = true; } else if(firstCell.hasMerged() !== null)//в противном случае ошибка { if(isCheckSelection) { return arn; } else { this._trigger ("onError", c_oAscError.ID.PastInMergeAreaError, c_oAscError.Level.NoCritical); return; } } } else { //проверка на наличие части объединённой ячейки в области куда осуществляем вставку for (var rFirst = arn.r1;rFirst < rMax; ++rFirst) { for (var cFirst = arn.c1; cFirst < cMax; ++cFirst) { range = t.model.getRange3(rFirst, cFirst, rFirst, cFirst); var merged = range.hasMerged(); if(merged) { if(merged.r1 < arn.r1 || merged.r2 > rMax - 1 || merged.c1 < arn.c1 || merged.c2 > cMax-1) { //ошибка в случае если вставка происходит в часть объедененной ячейки if(isCheckSelection) { return arn; } else { this._trigger ("onErrorEvent", c_oAscError.ID.PastInMergeAreaError, c_oAscError.Level.NoCritical); return; } } } } } } var rMax2 = rMax; var cMax2 = cMax; var rMax = values.length; var trueArn = t.activeRange; if(isCheckSelection) { var newArr = arn.clone(true); newArr.r2 = rMax2 - 1; newArr.c2 = cMax2 -1; if(isMultiple || isOneMerge) { newArr.r2 = trueArn.r2; newArr.c2 = trueArn.c2; } return newArr; } //если не возникает конфликт, делаем unmerge rangeUnMerge.unmerge(); if(!isOneMerge) { arn.r2 = rMax2 - 1; arn.c2 = cMax2 -1; } var mergeArr = []; var n = 0; if(isMultiple)//случай автозаполнения сложных форм { t.model.getRange3(trueArn.r1, trueArn.c1, trueArn.r2, trueArn.c2).unmerge(); var maxARow = heightArea/heightPasteFr; var maxACol = widthArea/widthPasteFr; var plRow = (rMax2 - arn.r1); var plCol = (arn.c2 - arn.c1) + 1; } else { var maxARow = 1; var maxACol = 1; var plRow = 0; var plCol = 0; } if(isMultiple) { var currentObj = values[arn.r1][arn.c1][0]; var valFormat = ''; if(currentObj[0] !== undefined) valFormat = currentObj[0].text; if(currentObj.format !== null && currentObj.format !== '' && currentObj.format !== undefined) { var nameFormat = clipboard._decode(currentObj.format.split(';')[0]); valFormat = clipboard._decode(currentObj.format.split(';')[1]); } } for (var autoR = 0;autoR < maxARow; ++autoR) { for (var autoC = 0;autoC < maxACol; ++autoC) { for (var r = arn.r1;r < rMax; ++r) { for (var c = arn.c1; c < values[r].length; ++c) { if(undefined !== values[r][c]) { var range = t.model.getRange3(r + autoR*plRow, c + autoC*plCol, r + autoR*plRow, c + autoC*plCol); var res, flag, v; var currentObj = values[r][c][0]; if( currentObj.length === 1 ){ //if(!isMultiple) //{ var valFormat = currentObj[0].text; var nameFormat = false; if(currentObj.format !== null && currentObj.format !== '' && currentObj.format !== undefined) { nameFormat = clipboard._decode(currentObj.format.split(';')[0]); valFormat = clipboard._decode(currentObj.format.split(';')[1]); } //} if( currentObj[0].cellFrom ){ var offset = range.getCells()[0].getOffset2(currentObj[0].cellFrom), assemb, _p_ = new parserFormula(currentObj[0].text.substring(1),"",range.worksheet); if( _p_.parse() ){ assemb = _p_.changeOffset(offset).assemble(); //range.setValue("="+assemb); arrFormula[numFor] = {}; arrFormula[numFor].range = range; arrFormula[numFor].val = "=" + assemb; numFor++; delete _p_; } else{ delete _p_; } } else range.setValue(valFormat); if(nameFormat) range.setNumFormat(nameFormat); range.setBold(currentObj[0].format.b); range.setItalic(currentObj[0].format.i); range.setStrikeout(currentObj[0].format.s); if(!isOneMerge && currentObj[0].format && currentObj[0].format.c != null && currentObj[0].format.c != undefined && asc_parsecolor(currentObj[0].format.c) != null) range.setFontcolor(new RgbColor(asc_parsecolor(currentObj[0].format.c).binary)); range.setUnderline(currentObj[0].format.u); range.setAlignVertical(currentObj.va); range.setFontname(currentObj[0].format.fn); range.setFontsize(currentObj[0].format.fs); } else{ range.setValue2(currentObj); range.setAlignVertical(currentObj.va); } if(currentObj.length === 1 && currentObj[0].format.fs !== '' && currentObj[0].format.fs !== null && currentObj[0].format.fs !== undefined) range.setFontsize(currentObj[0].format.fs) if(!isOneMerge) range.setAlignHorizontal(currentObj.a); var isMerged = false; for(mergeCheck = 0; mergeCheck < mergeArr.length; ++mergeCheck) { if(r + 1 + autoR*plRow <= mergeArr[mergeCheck].r2 && r + 1 + autoR*plRow >= mergeArr[mergeCheck].r1 && c + autoC*plCol + 1 <= mergeArr[mergeCheck].c2 && c + 1 + autoC*plCol >= mergeArr[mergeCheck].c1) isMerged = true; } //обработка для мерженных ячеек if((currentObj.colSpan > 1 || currentObj.rowSpan > 1) && !isMerged) { range.setOffsetLast({offsetCol: currentObj.colSpan -1, offsetRow: currentObj.rowSpan -1}); mergeArr[n] = { r1: range.first.row, r2: range.last.row, c1: range.first.col, c2: range.last.col }; n++; if(currentObj[0] == undefined) range.setValue(''); range.merge(c_oAscMergeOptions.Merge); } //range.setBorder(null); if(!isOneMerge) range.setBorderSrc(currentObj.borders, false); range.setWrap(currentObj.wrap); if(currentObj.bc && currentObj.bc != 'rgba(0, 0, 0, 0)' && currentObj.bc != 'transparent' && '' != currentObj.bc && !isOneMerge) range.setFill(new RgbColor(asc_parsecolor(currentObj.bc).binary)); var link = values[r][c][0].hyperLink; if(link) { var newHyperlink = new Hyperlink(); if(values[r][c][0].hyperLink.search('#') === 0) newHyperlink.Location = link.replace('#',''); else newHyperlink.Hyperlink = link; newHyperlink.Ref = range; newHyperlink.Tooltip = values[r][c][0].toolTip; range.setHyperlink(newHyperlink); } } } } } } if(isMultiple) { arn.r2 = trueArn.r2; arn.c2 = trueArn.c2; } t.isChanged = true; t.activeRange.c2 = arn.c2; t.activeRange.r2 = arn.r2; var arnFor = []; arnFor[0] = arn; arnFor[1] = arrFormula; return arnFor; }, _pasteFromLS: function(val,isCheckSelection){ var t = this; var arn = t.activeRange.clone(true); var arrFormula = []; var numFor = 0; var rMax = val.lStorage.length + arn.r1; var cMax = val.lStorage[0].length + arn.c1; var values = val.lStorage; var isMultiple = false; var firstCell = t.model.getRange3(arn.r1, arn.c1, arn.r1, arn.c1); var isMergedFirstCell = firstCell.hasMerged(); var rangeUnMerge = t.model.getRange3(arn.r1, arn.c1, rMax - 1, cMax - 1); var isOneMerge = false; var firstValuesCol; var firstValuesRow; if(values[0][0].merge != null) { firstValuesCol = values[0][0].merge.c2-values[0][0].merge.c1; firstValuesRow = values[0][0].merge.r2-values[0][0].merge.r1; } else { firstValuesCol = 0; firstValuesRow = 0; } //если вставляем в мерженную ячейку, диапазон которой больше или равен if (arn.c2 >= cMax -1 && arn.r2 >= rMax - 1 && isMergedFirstCell && isMergedFirstCell.c1 === arn.c1 && isMergedFirstCell.c2 === arn.c2 && isMergedFirstCell.r1 === arn.r1 && isMergedFirstCell.r2 === arn.r2 && cMax - arn.c1 === (firstValuesCol + 1) && rMax - arn.r1 === (firstValuesRow + 1)) { if(!isCheckSelection) { /*values[0][0].merge = { r1: 0, r2: isMergedFirstCell.r2 -isMergedFirstCell.r1, c1: 0, c2: isMergedFirstCell.c2 -isMergedFirstCell.c1 }*/ /*val[0][0].colSpan = isMergedFirstCell.c2 -isMergedFirstCell.c1 + 1; val[0][0].rowSpan = isMergedFirstCell.r2 -isMergedFirstCell.r1 + 1;*/ } isOneMerge = true; } else if(arn.c2 >= cMax -1 && arn.r2 >= rMax - 1) { //если область кратная куску вставки var widthArea = arn.c2 - arn.c1 + 1; var heightArea = arn.r2 - arn.r1 + 1; var widthPasteFr = cMax - arn.c1; var heightPasteFr = rMax - arn.r1; //если кратны, то обрабатываем if(widthArea%widthPasteFr === 0 && heightArea%heightPasteFr === 0) { isMultiple = true; } else if(firstCell.hasMerged() !== null)//в противном случае ошибка { if(isCheckSelection) { return arn; } else { this._trigger ("onError", c_oAscError.ID.PastInMergeAreaError, c_oAscError.Level.NoCritical); return; } } } else { //проверка на наличие части объединённой ячейки в области куда осуществляем вставку for (var rFirst = arn.r1;rFirst < rMax; ++rFirst) { for (var cFirst = arn.c1; cFirst < cMax; ++cFirst) { range = t.model.getRange3(rFirst, cFirst, rFirst, cFirst); var merged = range.hasMerged(); if(merged) { if(merged.r1 < arn.r1 || merged.r2 > rMax - 1 || merged.c1 < arn.c1 || merged.c2 > cMax-1) { //ошибка в случае если вставка происходит в часть объедененной ячейки if(isCheckSelection) { return arn; } else { this._trigger ("onErrorEvent", c_oAscError.ID.PastInMergeAreaError, c_oAscError.Level.NoCritical); return; } } } } } } var rMax2 = rMax; var cMax2 = cMax; //var rMax = values.length; var trueArn = t.activeRange; if(isCheckSelection) { var newArr = arn.clone(true); newArr.r2 = rMax2 - 1; newArr.c2 = cMax2 - 1; if(isMultiple || isOneMerge) { newArr.r2 = trueArn.r2; newArr.c2 = trueArn.c2; } return newArr; } //если не возникает конфликт, делаем unmerge rangeUnMerge.unmerge(); if(!isOneMerge) { arn.r2 = rMax2 - 1; arn.c2 = cMax2 -1; } var mergeArr = []; var n = 0; if(isMultiple)//случай автозаполнения сложных форм { t.model.getRange3(trueArn.r1, trueArn.c1, trueArn.r2, trueArn.c2).unmerge(); var maxARow = heightArea/heightPasteFr; var maxACol = widthArea/widthPasteFr; var plRow = (rMax2 - arn.r1); var plCol = (arn.c2 - arn.c1) + 1; } else { var maxARow = 1; var maxACol = 1; var plRow = 0; var plCol = 0; } /*if(isMultiple) { var currentObj = values[arn.r1][arn.c1][0]; var valFormat = ''; if(currentObj[0] !== undefined) valFormat = currentObj[0].text; if(currentObj.format !== null && currentObj.format !== '' && currentObj.format !== undefined) { var nameFormat = clipboard._decode(currentObj.format.split(';')[0]); valFormat = clipboard._decode(currentObj.format.split(';')[1]); } }*/ for (var autoR = 0;autoR < maxARow; ++autoR) { for (var autoC = 0;autoC < maxACol; ++autoC) { for (var r = arn.r1;r < rMax; ++r) { for (var c = arn.c1; c < cMax; ++c) { var newVal = values[r - arn.r1][c - arn.c1]; if(undefined !== newVal) { var isMerged = false; var range = t.model.getRange3(r + autoR*plRow, c + autoC*plCol, r + autoR*plRow, c + autoC*plCol); if(!isOneMerge) { for(mergeCheck = 0; mergeCheck < mergeArr.length; ++mergeCheck) { if(r + autoR*plRow <= mergeArr[mergeCheck].r2 && r + autoR*plRow >= mergeArr[mergeCheck].r1 && c + autoC*plCol <= mergeArr[mergeCheck].c2 && c + autoC*plCol >= mergeArr[mergeCheck].c1) isMerged = true; } if(newVal.merge != null && !isMerged) { range.setOffsetLast({offsetCol: (newVal.merge.c2 - newVal.merge.c1), offsetRow: (newVal.merge.r2 - newVal.merge.r1)}); range.merge(c_oAscMergeOptions.Merge); mergeArr[n] = { r1: newVal.merge.r1 + arn.r1 - values.fromRow + autoR*plRow, r2: newVal.merge.r2 + arn.r1 - values.fromRow + autoR*plRow, c1: newVal.merge.c1 + arn.c1 - values.fromCol + autoC*plCol, c2: newVal.merge.c2 + arn.c1 - values.fromCol + autoC*plCol }; n++; } } else { for(mergeCheck = 0; mergeCheck < mergeArr.length; ++mergeCheck) { if(r + autoR*plRow <= mergeArr[mergeCheck].r2 && r + autoR*plRow >= mergeArr[mergeCheck].r1 && c + autoC*plCol <= mergeArr[mergeCheck].c2 && c + autoC*plCol >= mergeArr[mergeCheck].c1) isMerged = true; } if(!isMerged) { range.setOffsetLast({offsetCol: (isMergedFirstCell.c2 -isMergedFirstCell.c1), offsetRow: (isMergedFirstCell.r2 -isMergedFirstCell.r1)}); range.merge(c_oAscMergeOptions.Merge); mergeArr[n] = { r1: isMergedFirstCell.r1, r2: isMergedFirstCell.r2, c1: isMergedFirstCell.c1, c2: isMergedFirstCell.c2 }; n++; } } //add formula var numFormula = null; var skipFormat = null; var noSkipVal = null; for(var nF = 0; nF < newVal.value2.length;nF++) { if(newVal.value2[nF] && newVal.value2[nF].sId) { numFormula = nF; break; } else if(newVal.value2[nF] && newVal.value2[nF].format && newVal.value2[nF].format.skip) skipFormat = true; else if(newVal.value2[nF] && newVal.value2[nF].format && !newVal.value2[nF].format.skip) noSkipVal = nF; } if(newVal.value2.length == 1 || numFormula != null || (skipFormat != null && noSkipVal!= null)) { if(numFormula == null) numFormula = 0; var numStyle = 0; if(skipFormat != null && noSkipVal!= null) numStyle = noSkipVal; if( newVal.value2[numFormula].sId){ var offset = range.getCells()[numFormula].getOffset2(newVal.value2[numFormula].sId), assemb, _p_ = new parserFormula(newVal.value2[numFormula].sFormula,"",range.worksheet); if( _p_.parse() ){ assemb = _p_.changeOffset(offset).assemble(); //range.setValue("="+assemb); arrFormula[numFor] = {}; arrFormula[numFor].range = range; arrFormula[numFor].val = "=" + assemb; numFor++; delete _p_; } else{ delete _p_; } } else if(newVal.valWithoutFormat) range.setValue(newVal.valWithoutFormat); else range.setValue(newVal.value2[numStyle].text); range.setBold(newVal.value2[numStyle].format.b); range.setItalic(newVal.value2[numStyle].format.i); range.setStrikeout(newVal.value2[numStyle].format.s); if(!isOneMerge && newVal.value2[numStyle].format && newVal.value2[numStyle].format.c != null && newVal.value2[numStyle].format.c != undefined) range.setFontcolor(new RgbColor(asc_parsecolor(newVal.value2[numStyle].format.c).binary)); range.setUnderline(newVal.value2[numStyle].format.u); //range.setAlignVertical(currentObj.va); range.setFontname(newVal.value2[numStyle].format.fn); range.setFontsize(newVal.value2[numStyle].format.fs); } else range.setValue2(newVal.value2); range.setAlignVertical(newVal.verAlign); if(!isOneMerge) range.setAlignHorizontal(newVal.horAlign); if(!isOneMerge) range.setBorderSrc(newVal.borders, false); var nameFormat; /*if(newVal.format.oPositiveFormat) { var output = new Object(); var bRes = newVal.format.shiftFormat(output, 0); if(true == bRes) nameFormat = output.format; }*/ if(newVal.format && newVal.format.sFormat) nameFormat = newVal.format.sFormat; if(nameFormat) range.setNumFormat(nameFormat); range.setFill(newVal.fill); range.setWrap(newVal.wrap); if(newVal.hyperlink != null) { newVal.hyperlink.Ref = range; range.setHyperlink(newVal.hyperlink); } } } } } } if(isMultiple) { arn.r2 = trueArn.r2; arn.c2 = trueArn.c2; } t.isChanged = true; t.activeRange.c2 = arn.c2; t.activeRange.r2 = arn.r2; var arnFor = []; arnFor[0] = arn; arnFor[1] = arrFormula; return arnFor; }, // Залочен ли весь лист _isLockedAll: function (callback) { if (false === this.collaborativeEditing.isCoAuthoringExcellEnable()) { // Запрещено совместное редактирование if ($.isFunction(callback)) {callback(true);} return; } var sheetId = this.model.getId(); var subType = c_oAscLockTypeElemSubType.ChangeProperties; var ar = this.activeRange; var lockInfo = this.collaborativeEditing.getLockInfo(c_oAscLockTypeElem.Range, /*subType*/subType, sheetId, new asc_CCollaborativeRange(ar.c1, ar.r1, ar.c2, ar.r2)); if (false === this.collaborativeEditing.getCollaborativeEditing()) { // Пользователь редактирует один: не ждем ответа, а сразу продолжаем редактирование if ($.isFunction(callback)) {callback(true);} callback = undefined; } else if (false !== this.collaborativeEditing.getLockIntersection(lockInfo, c_oAscLockTypes.kLockTypeMine, /*bCheckOnlyLockAll*/true)) { // Редактируем сами if ($.isFunction(callback)) {callback(true);} return; } else if (false !== this.collaborativeEditing.getLockIntersection(lockInfo, c_oAscLockTypes.kLockTypeOther, /*bCheckOnlyLockAll*/true)) { // Уже ячейку кто-то редактирует if ($.isFunction(callback)) {callback(false);} return; } this.collaborativeEditing.onStartCheckLock(); this.collaborativeEditing.addCheckLock(lockInfo); this.collaborativeEditing.onEndCheckLock(callback); }, // Пересчет для входящих ячеек в добавленные строки/столбцы _recalcRangeByInsertRowsAndColumns: function (sheetId, ar) { var isIntersection = false, isIntersectionC1 = true, isIntersectionC2 = true, isIntersectionR1 = true, isIntersectionR2 = true; do { if (isIntersectionC1 && this.collaborativeEditing.isIntersectionInCols(sheetId, ar.c1)) ar.c1 += 1; else isIntersectionC1 = false; if (isIntersectionR1 && this.collaborativeEditing.isIntersectionInRows(sheetId, ar.r1)) ar.r1 += 1; else isIntersectionR1 = false; if (isIntersectionC2 && this.collaborativeEditing.isIntersectionInCols(sheetId, ar.c2)) ar.c2 -= 1; else isIntersectionC2 = false; if (isIntersectionR2 && this.collaborativeEditing.isIntersectionInRows(sheetId, ar.r2)) ar.r2 -= 1; else isIntersectionR2 = false; if (ar.c1 > ar.c2 || ar.r1 > ar.r2) { isIntersection = true; break; } } while (isIntersectionC1 || isIntersectionC2 || isIntersectionR1 || isIntersectionR2) ; if (false === isIntersection) { ar.c1 = this.collaborativeEditing.getLockMeColumn(sheetId, ar.c1); ar.c2 = this.collaborativeEditing.getLockMeColumn(sheetId, ar.c2); ar.r1 = this.collaborativeEditing.getLockMeRow(sheetId, ar.r1); ar.r2 = this.collaborativeEditing.getLockMeRow(sheetId, ar.r2); } return isIntersection; }, // Функция проверки lock (возвращаемый результат нельзя использовать в качестве ответа, он нужен только для редактирования ячейки) _isLockedCells: function (range, subType, callback) { if (false === this.collaborativeEditing.isCoAuthoringExcellEnable()) { // Запрещено совместное редактирование if ($.isFunction(callback)) {callback(true);} return true; } var sheetId = this.model.getId(); var isIntersection = false; var newCallback = callback; var t = this; this.collaborativeEditing.onStartCheckLock(); var nLength = ("array" === asc_typeof(range)) ? range.length : 1; var nIndex = 0; var ar = null; for (; nIndex < nLength; ++nIndex) { ar = ("array" === asc_typeof(range)) ? range[nIndex].clone(true) : range.clone(true); if (c_oAscLockTypeElemSubType.InsertColumns !== subType && c_oAscLockTypeElemSubType.InsertRows !== subType) { // Пересчет для входящих ячеек в добавленные строки/столбцы isIntersection = this._recalcRangeByInsertRowsAndColumns(sheetId, ar); } if (false === isIntersection) { var lockInfo = this.collaborativeEditing.getLockInfo(c_oAscLockTypeElem.Range, /*subType*/subType, sheetId, new asc_CCollaborativeRange(ar.c1, ar.r1, ar.c2, ar.r2)); if (false !== this.collaborativeEditing.getLockIntersection(lockInfo, c_oAscLockTypes.kLockTypeOther, /*bCheckOnlyLockAll*/false)) { // Уже ячейку кто-то редактирует if ($.isFunction(callback)) {callback(false);} return false; } else { if (c_oAscLockTypeElemSubType.InsertColumns === subType) { newCallback = function (isSuccess) { if (isSuccess) { t.collaborativeEditing.addColsRange(sheetId, range.clone(true)); t.collaborativeEditing.addCols(sheetId, range.c1, range.c2 - range.c1 + 1); } callback(isSuccess); }; } else if (c_oAscLockTypeElemSubType.InsertRows === subType) { newCallback = function (isSuccess) { if (isSuccess) { t.collaborativeEditing.addRowsRange(sheetId, range.clone(true)); t.collaborativeEditing.addRows(sheetId, range.r1, range.r2 - range.r1 + 1); } callback(isSuccess); }; } else if (c_oAscLockTypeElemSubType.DeleteColumns === subType) { newCallback = function (isSuccess) { if (isSuccess) { t.collaborativeEditing.removeColsRange(sheetId, range.clone(true)); t.collaborativeEditing.removeCols(sheetId, range.c1, range.c2 - range.c1 + 1); } callback(isSuccess); }; } else if (c_oAscLockTypeElemSubType.DeleteRows === subType) { newCallback = function (isSuccess) { if (isSuccess) { t.collaborativeEditing.removeRowsRange(sheetId, range.clone(true)); t.collaborativeEditing.removeRows(sheetId, range.r1, range.r2 - range.r1 + 1); } callback(isSuccess); }; } this.collaborativeEditing.addCheckLock(lockInfo); } } else { if (c_oAscLockTypeElemSubType.InsertColumns === subType) { t.collaborativeEditing.addColsRange(sheetId, range.clone(true)); t.collaborativeEditing.addCols(sheetId, range.c1, range.c2 - range.c1 + 1); } else if (c_oAscLockTypeElemSubType.InsertRows === subType) { t.collaborativeEditing.addRowsRange(sheetId, range.clone(true)); t.collaborativeEditing.addRows(sheetId, range.r1, range.r2 - range.r1 + 1); } else if (c_oAscLockTypeElemSubType.DeleteColumns === subType) { t.collaborativeEditing.removeColsRange(sheetId, range.clone(true)); t.collaborativeEditing.removeCols(sheetId, range.c1, range.c2 - range.c1 + 1); } else if (c_oAscLockTypeElemSubType.DeleteRows === subType) { t.collaborativeEditing.removeRowsRange(sheetId, range.clone(true)); t.collaborativeEditing.removeRows(sheetId, range.r1, range.r2 - range.r1 + 1); } } } if (false === this.collaborativeEditing.getCollaborativeEditing()) { // Пользователь редактирует один: не ждем ответа, а сразу продолжаем редактирование newCallback(true); newCallback = undefined; } this.collaborativeEditing.onEndCheckLock(newCallback); return true; }, changeWorksheet: function (prop, val) { // Проверка глобального лока if (this.collaborativeEditing.getGlobalLock()) return; var t = this; var arn = t.activeRange.clone(true); var range; var fullRecalc = undefined; var pad, cw; var isUpdateCols = false, isUpdateRows = false; var cleanCacheCols = false, cleanCacheRows = false; var _updateRangeIns, _updateRangeDel, bUndoRedo; var functionModelAction = null; var lockDraw = false; // Параметр, при котором не будет отрисовки (т.к. мы просто обновляем информацию на неактивном листе) var onChangeWorksheetCallback = function (isSuccess) { if (false === isSuccess) return; if ($.isFunction(functionModelAction)) {functionModelAction();} t._initCellsArea(fullRecalc); if (fullRecalc) { t.cache.reset(); } else { if (cleanCacheCols) { t._cleanCache(asc_Range(arn.c1, 0, arn.c2, t.rows.length - 1)); } if (cleanCacheRows) { t._cleanCache(asc_Range(0, arn.r1, t.cols.length - 1, arn.r2)); } } t._cleanCellsTextMetricsCache(); t._prepareCellTextMetricsCache(t.visibleRange); t.draw(); t._trigger("reinitializeScroll"); if (isUpdateCols) { t._updateVisibleColsCount(); } if (isUpdateRows) { t._updateVisibleRowsCount(); } t.objectRender.showDrawingObjects(true); }; switch (prop) { case "colWidth": functionModelAction = function () { pad = t.width_padding * 2 + t.width_1px; cw = t._charCountToModelColWidth(val, true); t.model.setColWidth(cw, arn.c1, arn.c2); isUpdateCols = true; fullRecalc = true; }; return this._isLockedAll (onChangeWorksheetCallback); case "insColBefore": functionModelAction = function () { fullRecalc = true; t.autoFilters.insertColumn(t, prop, val, arn); t.model.insertColsBefore(arn.c1, val); }; return this._isLockedCells (new asc_Range(arn.c1, 0, arn.c1 + val - 1, gc_nMaxRow0), c_oAscLockTypeElemSubType.InsertColumns, onChangeWorksheetCallback); case "insColAfter": functionModelAction = function () { fullRecalc = true; t.autoFilters.insertColumn(t, prop, val, arn); t.model.insertColsAfter(arn.c2, val); }; return this._isLockedCells (new asc_Range(arn.c2, 0, arn.c2 + val - 1, gc_nMaxRow0), c_oAscLockTypeElemSubType.InsertColumns, onChangeWorksheetCallback); case "delCol": functionModelAction = function () { fullRecalc = true; t.model.removeCols(arn.c1, arn.c2); }; return this._isLockedCells (new asc_Range(arn.c1, 0, arn.c2, gc_nMaxRow0), c_oAscLockTypeElemSubType.DeleteColumns, onChangeWorksheetCallback); case "showCols": functionModelAction = function () { t.model.setColHidden(/*bHidden*/false, arn.c1, arn.c2); fullRecalc = true; }; return this._isLockedAll (onChangeWorksheetCallback); case "hideCols": functionModelAction = function () { t.model.setColHidden(/*bHidden*/true, arn.c1, arn.c2); fullRecalc = true; }; return this._isLockedAll (onChangeWorksheetCallback); case "rowHeight": functionModelAction = function () { t.model.setRowHeight(Math.min(val + t.height_1px, t.maxRowHeight), arn.r1, arn.r2); isUpdateRows = true; fullRecalc = true; }; return this._isLockedAll (onChangeWorksheetCallback); case "insRowBefore": functionModelAction = function () { fullRecalc = true; t.model.insertRowsBefore(arn.r1, val); }; return this._isLockedCells (new asc_Range(0, arn.r1, gc_nMaxCol0, arn.r1 + val - 1), c_oAscLockTypeElemSubType.InsertRows, onChangeWorksheetCallback); case "insRowAfter": functionModelAction = function () { fullRecalc = true; t.model.insertRowsAfter(arn.r2, val); }; return this._isLockedCells (new asc_Range(0, arn.r2, gc_nMaxCol0, arn.r2 + val - 1), c_oAscLockTypeElemSubType.InsertRows, onChangeWorksheetCallback); case "delRow": functionModelAction = function () { fullRecalc = true; t.model.removeRows(arn.r1, arn.r2); }; return this._isLockedCells (new asc_Range(0, arn.r1, gc_nMaxCol0, arn.r1), c_oAscLockTypeElemSubType.DeleteRows, onChangeWorksheetCallback); case "showRows": functionModelAction = function () { t.model.setRowHidden(/*bHidden*/false, arn.r1, arn.r2); fullRecalc = true; }; return this._isLockedAll (onChangeWorksheetCallback); case "hideRows": functionModelAction = function () { t.model.setRowHidden(/*bHidden*/true, arn.r1, arn.r2); fullRecalc = true; }; return this._isLockedAll (onChangeWorksheetCallback); case "insCell": bUndoRedo = val.range != undefined; if (val && val.range) { _updateRangeIns = val.range; val = val.val; } else { _updateRangeIns = arn; } range = t.model.getRange3(_updateRangeIns.r1, _updateRangeIns.c1, _updateRangeIns.r2, _updateRangeIns.c2); switch (val) { case c_oAscInsertOptions.InsertCellsAndShiftRight: functionModelAction = function () { t.model.onStartTriggerAction(); if (range.addCellsShiftRight()) { fullRecalc = true; t.cellCommentator.updateCommentsDependencies(true, val, _updateRangeIns); } t.model.onEndTriggerAction(); }; if(bUndoRedo) onChangeWorksheetCallback(true); else this._isLockedCells (new asc_Range(_updateRangeIns.c1, _updateRangeIns.r1, gc_nMaxCol0, _updateRangeIns.r2), null, onChangeWorksheetCallback); return; case c_oAscInsertOptions.InsertCellsAndShiftDown: functionModelAction = function () { t.model.onStartTriggerAction(); if (range.addCellsShiftBottom()) { fullRecalc = true; t.cellCommentator.updateCommentsDependencies(true, val, _updateRangeIns); } t.model.onEndTriggerAction(); }; if(bUndoRedo) onChangeWorksheetCallback(true); else this._isLockedCells (new asc_Range(_updateRangeIns.c1, _updateRangeIns.r1, _updateRangeIns.c2, gc_nMaxRow0), null, onChangeWorksheetCallback); return; case c_oAscInsertOptions.InsertColumns: functionModelAction = function () { fullRecalc = true; t.model.onStartTriggerAction(); t.model.insertColsBefore(_updateRangeIns.c1, _updateRangeIns.c2 - _updateRangeIns.c1 + 1); t.model.onEndTriggerAction(); t.autoFilters.insertColumn(t, prop, _updateRangeIns, arn); //if ( !bUndoRedo ) { t.objectRender.updateDrawingObject(true, val, _updateRangeIns); //} t.cellCommentator.updateCommentsDependencies(true, val, _updateRangeIns); }; if(bUndoRedo) onChangeWorksheetCallback(true); else this._isLockedCells (new asc_Range(_updateRangeIns.c1, 0, _updateRangeIns.c2, gc_nMaxRow0), c_oAscLockTypeElemSubType.InsertColumns, onChangeWorksheetCallback); return; case c_oAscInsertOptions.InsertRows: functionModelAction = function () { fullRecalc = true; t.model.onStartTriggerAction(); t.model.insertRowsBefore(_updateRangeIns.r1, _updateRangeIns.r2 - _updateRangeIns.r1 + 1); t.model.onEndTriggerAction(); t.autoFilters.insertRows(t, prop,_updateRangeIns, arn); //if ( !bUndoRedo ) { t.objectRender.updateDrawingObject(true, val, _updateRangeIns); //} t.cellCommentator.updateCommentsDependencies(true, val, _updateRangeIns); }; if(bUndoRedo) onChangeWorksheetCallback(true); else this._isLockedCells (new asc_Range(0, _updateRangeIns.r1, gc_nMaxCol0, _updateRangeIns.r2), c_oAscLockTypeElemSubType.InsertRows, onChangeWorksheetCallback); return; default: return; } break; case "delCell": bUndoRedo = val.range != undefined; if (val && val.range) { _updateRangeDel = val.range; val = val.val; } else { _updateRangeDel = arn; } range = t.model.getRange3(_updateRangeDel.r1, _updateRangeDel.c1, _updateRangeDel.r2, _updateRangeDel.c2); switch (val) { case c_oAscDeleteOptions.DeleteCellsAndShiftLeft: functionModelAction = function () { if (range.deleteCellsShiftLeft()) { fullRecalc = true; t.cellCommentator.updateCommentsDependencies(false, val, _updateRangeDel); } }; if(bUndoRedo) onChangeWorksheetCallback(true); else this._isLockedCells (new asc_Range(_updateRangeDel.c1, _updateRangeDel.r1, gc_nMaxCol0, _updateRangeDel.r2), null, onChangeWorksheetCallback); return; case c_oAscDeleteOptions.DeleteCellsAndShiftTop: functionModelAction = function () { if (range.deleteCellsShiftUp()) { fullRecalc = true; t.cellCommentator.updateCommentsDependencies(false, val, _updateRangeDel); } }; if(bUndoRedo) onChangeWorksheetCallback(true); else this._isLockedCells (new asc_Range(_updateRangeDel.c1, _updateRangeDel.r1, _updateRangeDel.c2, gc_nMaxRow0), null, onChangeWorksheetCallback); return; case c_oAscDeleteOptions.DeleteColumns: functionModelAction = function () { fullRecalc = true; t.model.removeCols(_updateRangeDel.c1, _updateRangeDel.c2); t.autoFilters.insertColumn(t, prop,_updateRangeDel, arn); //if (!bUndoRedo) { t.objectRender.updateDrawingObject(false, val, _updateRangeDel); //} t.cellCommentator.updateCommentsDependencies(false, val, _updateRangeDel); }; if(bUndoRedo) onChangeWorksheetCallback(true); else this._isLockedCells (new asc_Range(_updateRangeDel.c1, 0, _updateRangeDel.c2, gc_nMaxRow0), c_oAscLockTypeElemSubType.DeleteColumns, onChangeWorksheetCallback); return; case c_oAscDeleteOptions.DeleteRows: functionModelAction = function () { fullRecalc = true; t.model.removeRows(_updateRangeDel.r1, _updateRangeDel.r2); t.autoFilters.insertRows(t, prop,_updateRangeDel, arn); //if (!bUndoRedo) { t.objectRender.updateDrawingObject(false, val, _updateRangeDel); //} t.cellCommentator.updateCommentsDependencies(false, val, _updateRangeDel); }; if(bUndoRedo) onChangeWorksheetCallback(true); else this._isLockedCells (new asc_Range(0, _updateRangeDel.r1, gc_nMaxCol0, _updateRangeDel.r2), c_oAscLockTypeElemSubType.DeleteRows, onChangeWorksheetCallback); return; default: return; } break; case "sheetViewSettings": functionModelAction = function () { t.model.setSheetViewSettings(val); isUpdateCols = true; isUpdateRows = true; fullRecalc = true; }; return this._isLockedAll (onChangeWorksheetCallback); case "update": if (val !== undefined) { fullRecalc = !!val.fullRecalc; lockDraw = true === val.lockDraw; } break; case "updateRange": if (val && val.range) { t._updateCellsRange(val.range, val.canChangeColWidth, val.isLockDraw); } return; default: return; } t._initCellsArea(fullRecalc); if (fullRecalc) { t.cache.reset(); } else { if (cleanCacheCols) { t._cleanCache(asc_Range(arn.c1, 0, arn.c2, t.rows.length - 1)); } if (cleanCacheRows) { t._cleanCache(asc_Range(0, arn.r1, t.cols.length - 1, arn.r2)); } } t._cleanCellsTextMetricsCache(); t._prepareCellTextMetricsCache(t.visibleRange); t.draw(lockDraw); t._trigger("reinitializeScroll"); if (isUpdateCols) { t._updateVisibleColsCount(); } if (isUpdateRows) { t._updateVisibleRowsCount(); } if (false === lockDraw) t.objectRender.showDrawingObjects(true); }, expandColsOnScroll: function (isNotActive, updateColsCount, newColsCount) { var t = this; var arn; var bIsMaxCols = false; var obr = this.objectRender ? this.objectRender.getDrawingAreaMetrics() : {maxCol: 0, maxRow: 0}; var maxc = Math.max(this.model.getColsCount(), this.cols.length, obr.maxCol); if (newColsCount) { maxc = Math.max(maxc, newColsCount); } // Сохраняем старое значение var nLastCols = this.nColsCount; if(isNotActive){ this.nColsCount = maxc + 1; } else if (updateColsCount) { this.nColsCount = maxc; if (this.cols.length < this.nColsCount) nLastCols = this.cols.length; } else { arn = t.activeRange.clone(true); if (arn.c2 >= t.cols.length - 1) { this.nColsCount = maxc; if(arn.c2 >= this.nColsCount - 1) this.nColsCount = arn.c2 + 2; } } // Проверяем ограничения по столбцам if (gc_nMaxCol < this.nColsCount) { this.nColsCount = gc_nMaxCol; bIsMaxCols = true; } // Проверяем замерженность всего или какой-либо строки this._updateMergedCellsRange(asc_Range (nLastCols, 0, this.nColsCount - 1, this.nRowsCount)); t._calcColumnWidths(/*fullRecalc*/2); return (nLastCols !== this.nColsCount || bIsMaxCols); }, expandRowsOnScroll: function (isNotActive, updateRowsCount, newRowsCount) { var t = this; var arn; var bIsMaxRows = false; var obr = this.objectRender ? this.objectRender.getDrawingAreaMetrics() : {maxCol: 0, maxRow: 0}; var maxr = Math.max(this.model.getRowsCount() , this.rows.length, obr.maxRow); if (newRowsCount) { maxr = Math.max(maxr, newRowsCount); } // Сохраняем старое значение var nLastRows = this.nRowsCount; if(isNotActive){ this.nRowsCount = maxr + 1; } else if (updateRowsCount) { this.nRowsCount = maxr; if (this.rows.length < this.nRowsCount) nLastRows = this.rows.length; } else { arn = t.activeRange.clone(true); if (arn.r2 >= t.rows.length - 1) { this.nRowsCount = maxr; if(arn.r2 >= this.nRowsCount - 1) this.nRowsCount = arn.r2 + 2; } } // Проверяем ограничения по строкам if (gc_nMaxRow < this.nRowsCount) { this.nRowsCount = gc_nMaxRow; bIsMaxRows = true; } // Проверяем замерженность всего или какого-либо столбца this._updateMergedCellsRange(asc_Range (0, nLastRows, this.nColsCount, this.nRowsCount - 1)); t._calcRowHeights(/*fullRecalc*/2); return (nLastRows !== this.nRowsCount || bIsMaxRows); }, optimizeColWidth: function (col) { var t = this; var onChangeWidthCallback = function (isSuccess) { if (false === isSuccess) return; var width = null; var row, ct, c, fl, str, maxW, tm, range; var filterButton = null; for (row = 0; row < t.rows.length; ++row) { ct = t._getCellTextCache(col, row); if (ct === undefined) {continue;} if (ct.flags.isMerged) { range = t._getMergedCellsRange(col, row); if (range === undefined) { // got uncached merged cells, redirect it range = t._fetchMergedCellsRange(col, row); } // Для замерженных ячеек (с 2-мя или более колонками) оптимизировать не нужно if (range.c1 !== range.c2) continue; } // пересчет метрик текста t.cols[col].isCustomWidth = false; t._addCellTextToCache(col, row, /*canChangeColWidth*/c_oAscCanChangeColWidth.all); ct = t._getCellTextCache(col, row); if (ct.metrics.height > t.maxRowHeight) { // вычисление новой ширины столбца, чтобы высота текста была меньше maxRowHeight c = t._getCell(col, row); fl = t._getCellFlags(c); if (fl.isMerged) {continue;} str = c.getValue2(); maxW = ct.metrics.width + t.maxDigitWidth; while (1) { tm = t._roundTextMetrics( t.stringRender.measureString(str, fl, maxW) ); if (tm.height <= t.maxRowHeight) {break;} maxW += t.maxDigitWidth; } width = Math.max(width, tm.width); } else { filterButton = t.autoFilters.getSizeButton(t, {c1: col, r1: row}); if (null !== filterButton && CellValueType.String === ct.cellType) width = Math.max(width, ct.metrics.width + filterButton.width); else width = Math.max(width, ct.metrics.width); } } if (width > 0) { var pad = t.width_padding * 2 + t.width_1px; var cc = Math.min(t._colWidthToCharCount(width + pad), /*max col width*/255); var cw = t._charCountToModelColWidth(cc, true); } else { cw = gc_dDefaultColWidthCharsAttribute; } History.Create_NewPoint(); History.SetSelection(null, true); History.StartTransaction(); // Выставляем, что это bestFit t.model.setColBestFit(true, cw, col, col); History.EndTransaction(); t.nColsCount = 0; t._calcColumnWidths(/*fullRecalc*/0); t._updateVisibleColsCount(); t._cleanCache(asc_Range(col, 0, col, t.rows.length - 1)); t.changeWorksheet("update"); }; return this._isLockedAll (onChangeWidthCallback); }, optimizeRowHeight: function (row) { var t = this; var onChangeHeightCallback = function (isSuccess) { if (false === isSuccess) return; var height = t.defaultRowHeight; var col, ct; for (col = 0; col < t.rows.length; ++col) { ct = t._getCellTextCache(col, row); if (ct === undefined) {continue;} if (ct.flags.isMerged) { range = t._getMergedCellsRange(col, row); if (range === undefined) { // got uncached merged cells, redirect it range = t._fetchMergedCellsRange(col, row); } // Для замерженных ячеек (с 2-мя или более строками) оптимизировать не нужно if (range.r1 !== range.r2) continue; } height = Math.max(height, ct.metrics.height); } History.Create_NewPoint(); History.SetSelection(null, true); History.StartTransaction(); // Выставляем, что это bestFit t.model.setRowBestFit (true, Math.min(height + t.height_1px, t.maxRowHeight), row, row); History.EndTransaction(); t.nRowsCount = 0; t._calcRowHeights(/*fullRecalc*/0); t._updateVisibleRowsCount(); t._cleanCache(asc_Range(0, row, t.cols.length - 1, row)); t.changeWorksheet("update"); }; return this._isLockedAll (onChangeHeightCallback); }, // ----- Search ----- _setActiveCell: function (col, row) { var ar = this.activeRange, sc = ar.startCol, sr = ar.startRow, offs; this.cleanSelection(); ar.assign(col, row, col, row); ar.type = c_oAscSelectionType.RangeCells; ar.startCol = col; ar.startRow = row; this._fixSelectionOfMergedCells(); this._fixSelectionOfHiddenCells(); this._drawSelection(); offs = this._calcActiveRangeOffset(); if (sc !== ar.startCol || sr !== ar.startRow) { this._trigger("selectionNameChanged", this.getSelectionName(/*bRangeText*/false)); this._trigger("selectionChanged", this.getSelectionInfo()); } return offs; }, findCellText: function (options) { var self = this; if (true !== options.isMatchCase) options.text = options.text.toLowerCase(); var ar = options.activeRange ? options.activeRange : this.activeRange; var c = ar.startCol; var r = ar.startRow; var minC = 0; var minR = 0; var maxC = this.cols.length - 1; var maxR = this.rows.length - 1; var inc = options.scanForward ? +1 : -1; var ct, mc, excluded = []; var _tmpCell, cellText; function isExcluded(col, row) { for (var i = 0; i < excluded.length; ++i) { if (excluded[i].contains(col, row)) {return true;} } return false; } function findNextCell() { var ct = undefined; do { do { mc = self._getMergedCellsRange(c, r); if (mc) {excluded.push(mc);} if (options.scanByRows) { c += mc ? (options.scanForward ? mc.c2 + 1 - c : mc.c1 - 1 - c) : inc; if (c < minC || c > maxC) {c = options.scanForward ? minC : maxC; r += inc;} } else { r += mc ? (options.scanForward ? mc.r2 + 1 - r : mc.r1 - 1 - r) : inc; if (r < minR || r > maxR) {r = options.scanForward ? minR : maxR; c += inc;} } if (c < minC || c > maxC || r < minR || r > maxR) {return undefined;} } while ( isExcluded(c, r) ); ct = self._getCellTextCache(c, r); } while (!ct); return ct; } for (ct = findNextCell(); ct; ct = findNextCell()) { // Не пользуемся RegExp, чтобы не возиться со спец.символами mc = this._getMergedCellsRange(c, r); if (mc) _tmpCell = this.model.getCell (new CellAddress(mc.r1, mc.c1, 0)); else _tmpCell = this.model.getCell (new CellAddress(r, c, 0)); cellText = _tmpCell.getValueForEdit(); if (true !== options.isMatchCase) cellText = cellText.toLowerCase(); if (cellText.indexOf(options.text) >= 0) { if (true !== options.isWholeCell || options.text.length === cellText.length) return (options.isNotSelect) ? new asc_Range(c, r, c, r) : this._setActiveCell(c, r); } } // Сбрасываем замерженные excluded = []; // Продолжаем циклический поиск if (options.scanForward){ // Идем вперед с первой ячейки minC = 0; minR = 0; if (options.scanByRows) { c = -1; r = 0; maxC = this.cols.length - 1; maxR = ar.startRow; } else { c = 0; r = -1; maxC = ar.startCol; maxR = this.rows.length - 1; } } else { // Идем назад с последней c = this.cols.length - 1; r = this.rows.length - 1; if (options.scanByRows) { minC = 0; minR = ar.startRow; } else { minC = ar.startCol; minR = 0; } maxC = this.cols.length - 1; maxR = this.rows.length - 1; } for (ct = findNextCell(); ct; ct = findNextCell()) { // Не пользуемся RegExp, чтобы не возиться со спец.символами mc = this._getMergedCellsRange(c, r); if (mc) _tmpCell = this.model.getCell (new CellAddress(mc.r1, mc.c1, 0)); else _tmpCell = this.model.getCell (new CellAddress(r, c, 0)); cellText = _tmpCell.getValueForEdit(); if (true !== options.isMatchCase) cellText = cellText.toLowerCase(); if (cellText.indexOf(options.text) >= 0) { if (true !== options.isWholeCell || options.text.length === cellText.length) return (options.isNotSelect) ? new asc_Range(c, r, c, r) : this._setActiveCell(c, r); } } return undefined; }, replaceCellText: function (options) { var findFlags = "g"; // Заменяем все вхождения if (true !== options.isMatchCase) findFlags += "i"; // Не чувствителен к регистру var valueForSearching = options.findWhat .replace(/(~)?\*/g, function($0, $1){ return $1 ? $0 : '[\\w\\W]*'; }) .replace(/(~)?\?/g, function($0, $1){ return $1 ? $0 : '[\\w\\W]{1,1}'; }) .replace(/(~\*)/g,"\\*").replace(/(~\?)/g, "\\?"); valueForSearching = new RegExp(valueForSearching, findFlags); var t = this; var ar = this.activeRange.clone(); ar.startCol = this.activeRange.startCol; ar.startRow = this.activeRange.startRow; var aReplaceCells = []; if (options.isReplaceAll) { // На ReplaceAll ставим медленную операцию t._trigger("slowOperation", true); var aReplaceCellsIndex = {}; var optionsFind = {text: options.findWhat, scanByRows: true, scanForward: true, isMatchCase: options.isMatchCase, isWholeCell: options.isWholeCell, isNotSelect: true, activeRange: ar}; var findResult, index; while (true) { findResult = t.findCellText(optionsFind); if (undefined === findResult) break; index = findResult.c1 + findResult.r1; if (aReplaceCellsIndex[index]) break; aReplaceCellsIndex[index] = true; aReplaceCells.push(findResult); ar.startCol = findResult.c1; ar.startRow = findResult.r1; } } else { var mc = t._getMergedCellsRange(ar.startCol, ar.startRow); var c1 = mc ? mc.c1 : ar.startCol; var r1 = mc ? mc.r1 : ar.startRow; var c = t._getVisibleCell(c1, r1); if (c === undefined) { asc_debug("log", "Unknown cell's info: col = " + c1 + ", row = " + r1); t._trigger("onRenameCellTextEnd", 0, 0); return; } var cellValue = c.getValueForEdit(); // Попробуем сначала найти if ((true === options.isWholeCell && cellValue.length !== options.findWhat.length) || 0 > cellValue.search(valueForSearching)) { t._trigger("onRenameCellTextEnd", 0, 0); return; } aReplaceCells.push(new asc_Range(ar.startCol, ar.startRow, ar.startCol, ar.startRow)); } if (0 > aReplaceCells.length) { t._trigger("onRenameCellTextEnd", 0, 0); return; } this._replaceCellsText(aReplaceCells, valueForSearching, options); }, _replaceCellsText: function (aReplaceCells, valueForSearching, options) { var oSelectionHistory = this.activeRange.clone(); this.model.onStartTriggerAction(); History.Create_NewPoint(); History.SetSelection(oSelectionHistory); History.StartTransaction(); options.indexInArray = 0; options.countFind = aReplaceCells.length; options.countReplace = 0; this._replaceCellText(aReplaceCells, valueForSearching, options); }, _replaceCellText: function (aReplaceCells, valueForSearching, options) { var t = this; if (options.indexInArray >= aReplaceCells.length) { History.EndTransaction(); t.model.onEndTriggerAction(); if (options.isReplaceAll) { // Завершаем медленную операцию t._trigger("slowOperation", false); } t._trigger("onRenameCellTextEnd", options.countFind, options.countReplace); return; } var onReplaceCallback = function (isSuccess) { var cell = aReplaceCells[options.indexInArray]; ++options.indexInArray; if (false !== isSuccess) { ++options.countReplace; var mc = t._getMergedCellsRange(cell.c1, cell.r1); var c1 = mc ? mc.c1 : cell.c1; var r1 = mc ? mc.r1 : cell.r1; var c = t._getVisibleCell(c1, r1); if (c === undefined) { asc_debug("log", "Unknown cell's info: col = " + c1 + ", row = " + r1); } else { var cellValue = c.getValueForEdit(); cellValue = cellValue.replace(valueForSearching, options.replaceWith); var oCellEdit = new asc_Range(c1, r1, c1, r1); var v, newValue; // get first fragment and change its text v = c.getValueForEdit2().slice(0, 1); // Создаем новый массив, т.к. getValueForEdit2 возвращает ссылку newValue = []; newValue[0] = {text: cellValue, format: v[0].format.clone()}; t._saveCellValueAfterEdit(oCellEdit, c, newValue, /*flags*/undefined, /*skipNLCheck*/false, /*isNotHistory*/true); } } window.setTimeout(function () {t._replaceCellText(aReplaceCells, valueForSearching, options)}, 1); }; this._isLockedCells (aReplaceCells[options.indexInArray], /*subType*/null, onReplaceCallback); }, findCell: function(reference) { var t = this; var match = (/(?:R(\d+)C(\d+)|([A-Z]+[0-9]+))(?::(?:R(\d+)C(\d+)|([A-Z]+[0-9]+)))?/i).exec(reference); if (!match) {return null;} function _findCell(match1, match2, match3) { var addr = typeof match1 === "string" ? new CellAddress(parseInt(match1), parseInt(match2)) : typeof match3 === "string" ? new CellAddress(match3) : null; if (addr && addr.isValid() && addr.getRow0() >= t.rows.length) { t.nRowsCount = addr.getRow0() + 1; t._calcRowHeights(/*fullRecalc*/2); } if (addr && addr.isValid() && addr.getCol0() >= t.cols.length) { t.nColsCount = addr.getCol0() + 1; t._calcColumnWidths(/*fullRecalc*/2); } return addr && addr.isValid() ? addr : null; } var addr1 = _findCell(match[1], match[2], match[3]); var addr2 = _findCell(match[4], match[5], match[6]); if (!addr1 && !addr2) { return {}; } var delta = t._setActiveCell(addr1.getCol0(), addr1.getRow0()); return !addr2 ? delta : t.changeSelectionEndPoint(addr2.getCol0() - addr1.getCol0(), addr2.getRow0() - addr1.getRow0(), /*isCoord*/false, /*isSelectMode*/false); }, // ----- Cell Editor ----- setCellEditMode: function (isCellEditMode) { this.isCellEditMode = isCellEditMode; }, setFormulaEditMode: function (isFormulaEditMode) { this.isFormulaEditMode = isFormulaEditMode; }, getFormulaEditMode: function () { return this.isFormulaEditMode; }, setSelectionDialogMode: function (isSelectionDialogMode, selectRange) { if (isSelectionDialogMode === this.isSelectionDialogMode) return; this.isSelectionDialogMode = isSelectionDialogMode; this.cleanSelection(); if (false === this.isSelectionDialogMode) { if (null !== this.copyOfActiveRange) { this.activeRange = this.copyOfActiveRange.clone(true); this.activeRange.startCol = this.copyOfActiveRange.startCol; this.activeRange.startRow = this.copyOfActiveRange.startRow; } this.copyOfActiveRange = null; } else { this.copyOfActiveRange = this.activeRange.clone(true); this.copyOfActiveRange.startCol = this.activeRange.startCol; this.copyOfActiveRange.startRow = this.activeRange.startRow; if (selectRange) { selectRange = parserHelp.parse3DRef(selectRange); if (selectRange) { // ToDo стоит менять и лист selectRange = this.model.getRange2(selectRange.range); if (null !== selectRange) { this.activeRange = selectRange.getBBox0(); this.activeRange.startCol = this.activeRange.c1; this.activeRange.startRow = this.activeRange.r1; } } } } this._drawSelection(); }, // Получаем свойство: редактируем мы сейчас или нет getCellEditMode: function () { return this.isCellEditMode; }, _isFormula: function (val) { return val.length > 0 && val[0].text.length > 1 && val[0].text.charAt(0) === "=" ? true : false; }, getActiveCellLock: function (x, y, isCoord) { var t = this; var col, row; if (isCoord) { x *= asc_getcvt( 0/*px*/, 1/*pt*/, t._getPPIX() ); y *= asc_getcvt( 0/*px*/, 1/*pt*/, t._getPPIY() ); col = t._findColUnderCursor(x, true); row = t._findRowUnderCursor(y, true); if (!col || !row) {return false;} col = col.col; row = row.row; } else { //col = this.model._getCol(t.activeRange.startCol).getId(); //row = this.model._getRow(t.activeRange.startRow).getId(); col = t.activeRange.startCol; row = t.activeRange.startRow; } // Проверим замерженность var mergedRange = this._getMergedCellsRange(col, row); return mergedRange ? mergedRange : asc_Range(col, row, col, row); }, _saveCellValueAfterEdit: function (oCellEdit, c, val, flags, skipNLCheck, isNotHistory) { var t = this; var oldMode = t.isFormulaEditMode; t.isFormulaEditMode = false; if (!isNotHistory) { t.model.onStartTriggerAction(); History.Create_NewPoint(); History.SetSelection(oCellEdit); History.StartTransaction(); } var isFormula = t._isFormula(val); if (isFormula) { var ftext = val.reduce(function (pv,cv) {return pv + cv.text;}, ""); var ret = true; // ToDo - при вводе формулы в заголовок автофильтра надо писать "0" c.setValue(ftext, function(r){ ret = r;} ); if(!ret) { t.isFormulaEditMode = oldMode; History.EndTransaction(); t.model.onEndTriggerAction(); return false; } isFormula = c.isFormula(); } else { c.setValue2(val); // Вызываем функцию пересчета для заголовков форматированной таблицы t.autoFilters._renameTableColumn(t, oCellEdit); } if (!isFormula) { // Нужно ли выставлять WrapText (ищем символ новой строки в тексте) var bIsSetWrap = false; if (!skipNLCheck) { for (var key in val) { if (val[key].text.indexOf(kNewLine) >= 0) { bIsSetWrap = true; break; } } } if (bIsSetWrap) c.setWrap(true); // Для формулы обновление будет в коде рассчета формулы t._updateCellsRange(oCellEdit, /*canChangeColWidth*/c_oAscCanChangeColWidth.numbers); } if (!isNotHistory) { History.EndTransaction(); t.model.onEndTriggerAction(); } // если вернуть false, то редактор не закроется return true; }, openCellEditor: function (editor, x, y, isCoord, fragments, cursorPos, isFocus, isClearCell, isHideCursor, activeRange) { var t = this, vr = t.visibleRange, tc = t.cols, tr = t.rows, col, row, c, fl, mc, bg; var ar = t.activeRange; if (activeRange) { t.activeRange.c1 = activeRange.c1; t.activeRange.c2 = activeRange.c2; t.activeRange.r1 = activeRange.r1; t.activeRange.r2 = activeRange.r2; t.activeRange.startCol = activeRange.startCol; t.activeRange.startRow = activeRange.startRow; t.activeRange.type = activeRange.type; } if ( t.objectRender.checkCursorDrawingObject(x, y) ) return false; function getLeftSide(col) { var i, res = [], offs = t.cols[vr.c1].left - t.cols[0].left; for (i = col; i >= vr.c1; --i) { res.push(t.cols[i].left - offs); } return res; } function getRightSide(col) { var i, w, res = [], offs = t.cols[vr.c1].left - t.cols[0].left; // Для замерженных ячеек, можем уйти за границу if (fl.isMerged && col > vr.c2) col = vr.c2; for (i = col; i <= vr.c2; ++i) { res.push(t.cols[i].left + t.cols[i].width - offs); } w = t.drawingCtx.getWidth(); if (res[res.length - 1] > w) { res[res.length - 1] = w; } return res; } function getBottomSide(row) { var i, h, res = [], offs = t.rows[vr.r1].top - t.rows[0].top; // Для замерженных ячеек, можем уйти за границу if (fl.isMerged && row > vr.r2) row = vr.r2; for (i = row; i <= vr.r2; ++i) { res.push(t.rows[i].top + t.rows[i].height - offs); } h = t.drawingCtx.getHeight(); if (res[res.length - 1] > h) { res[res.length - 1] = h; } return res; } if (isCoord) { x *= asc_getcvt( 0/*px*/, 1/*pt*/, t._getPPIX() ); y *= asc_getcvt( 0/*px*/, 1/*pt*/, t._getPPIY() ); col = t._findColUnderCursor(x, true); row = t._findRowUnderCursor(y, true); if (!col || !row) {return false;} col = col.col; row = row.row; } else { col = ar.startCol; row = ar.startRow; } c = t._getVisibleCell(col, row); if (!c) {throw "Can not get cell data (col=" + col + ", row=" + row + ")";} fl = t._getCellFlags(c); if (fl.isMerged) { mc = t._getMergedCellsRange(col, row); c = t._getVisibleCell(mc.c1, mc.r1); if (!c) {throw "Can not get merged cell data (col=" + mc.c1 + ", row=" + mc.r1 + ")";} fl = t._getCellFlags(c); // Первую ячейку нужно сделать видимой var bIsUpdateX = false; var bIsUpdateY = false; if (mc.c1 < vr.c1) { vr.c1 = mc.c1; bIsUpdateX = true; t._calcVisibleColumns(); } if (mc.r1 < vr.r1) { vr.r1 = mc.r1; bIsUpdateY = true; t._calcVisibleRows(); } if (bIsUpdateX && bIsUpdateY) { this._trigger("reinitializeScroll"); } else if (bIsUpdateX) { this._trigger("reinitializeScrollX"); } else if (bIsUpdateY) { this._trigger("reinitializeScrollY"); } if (bIsUpdateX || bIsUpdateY) { t.draw(); } } bg = c.getFill(); if(null != bg) bg = bg.getRgb(); t.isFormulaEditMode = false; // Очищаем массив ячеек для текущей формулы t.arrActiveFormulaRanges = []; var oFontColor = c.getFontcolor(); if(null != oFontColor) oFontColor = oFontColor.getRgb(); editor.open({ cellX: t.cellsLeft + tc[!fl.isMerged ? col : mc.c1].left - tc[vr.c1].left, cellY: t.cellsTop + tr[!fl.isMerged ? row : mc.r1].top - tr[vr.r1].top, leftSide: getLeftSide(!fl.isMerged ? col : mc.c1), rightSide: getRightSide(!fl.isMerged ? col : mc.c2), bottomSide: getBottomSide(!fl.isMerged ? row : mc.r2), fragments: fragments !== undefined ? fragments : c.getValueForEdit2(), flags: fl, font: new asc_FP(c.getFontname(), c.getFontsize()), background: bg !== null ? asc_n2css(bg) : t.settings.cells.defaultState.background, hasBackground: bg !== null, textColor: oFontColor || t.settings.cells.defaultState.color, cursorPos: cursorPos, zoom: t.getZoom(), focus: isFocus, isClearCell: isClearCell, isHideCursor: isHideCursor, saveValueCallback: function (val, flags, skipNLCheck) { var oCellEdit = new asc_Range(col, row, col, row); return t._saveCellValueAfterEdit(oCellEdit, c, val, flags, skipNLCheck, /*isNotHistory*/false); } }); // для отрисовки ранджей формулы t._drawSelection(); return true; }, openCellEditorWithText: function (editor, text, cursorPos, isFocus, activeRange) { var t = this; var ar = (activeRange) ? activeRange : t.activeRange; var c = t._getVisibleCell(ar.startCol, ar.startRow); var v, copyValue; if (!c) {throw "Can not get cell data (col=" + ar.startCol + ", row=" + ar.startCol + ")";} // get first fragment and change its text v = c.getValueForEdit2().slice(0, 1); // Создаем новый массив, т.к. getValueForEdit2 возвращает ссылку copyValue = []; copyValue[0] = {text: text, format: v[0].format.clone()}; var bSuccess = t.openCellEditor(editor, 0, 0, /*isCoord*/false, /*fragments*/undefined, /*cursorPos*/undefined, isFocus, /*isClearCell*/true, /*isHideCursor*/false, activeRange); if (bSuccess) { editor.paste(copyValue, cursorPos); } return bSuccess; }, _updateCellsRange: function (range, canChangeColWidth, lockDraw) { var t = this, r, c, h, d, ct; var mergedRange, bUpdateRowHeight; if (range === undefined) {range = t.activeRange.clone(true);} if(gc_nMaxCol0 === range.c2 || gc_nMaxRow0 === range.r2) { range = range.clone(); if(gc_nMaxCol0 === range.c2) range.c2 = this.cols.length - 1; if(gc_nMaxRow0 === range.r2) range.r2 = this.rows.length - 1; } t._cleanCache(range); // Если размер диапазона превышает размер видимой области больше чем в 3 раза, то очищаем весь кэш if (t._isLargeRange(range)) { t.changeWorksheet("update", {lockDraw: lockDraw}); return; } for (r = range.r1; r <= range.r2; ++r) { for (c = range.c1; c <= range.c2; ++c) { c = t._addCellTextToCache(c, r, canChangeColWidth); // may change member 'this.isChanged' } for (h = t.defaultRowHeight, d = t.defaultRowDescender, c = 0; c < t.cols.length; ++c) { ct = t._getCellTextCache(c, r, true); if (!ct) {continue;} // Замерженная ячейка (с 2-мя или более строками) не влияет на высоту строк! if (!ct.flags.isMerged) { bUpdateRowHeight = true; } else { mergedRange = t._getMergedCellsRange(c, r); if (undefined === mergedRange) { // got uncached merged cells, redirect it mergedRange = t._fetchMergedCellsRange(c, r); } // Для замерженных ячеек (с 2-мя или более строками) оптимизировать не нужно bUpdateRowHeight = mergedRange.r1 === mergedRange.r2; } if (bUpdateRowHeight) h = Math.max(h, ct.metrics.height); if (ct.cellVA !== kvaTop && ct.cellVA !== kvaCenter && !ct.flags.isMerged) { d = Math.max(d, ct.metrics.height - ct.metrics.baseline); } } if (Math.abs(h - t.rows[r].height) > 0.000001 && !t.rows[r].isCustomHeight) { t.rows[r].height = Math.min(h, t.maxRowHeight); if (!t.rows[r].isDefaultHeight) { t.model.setRowHeight(t.rows[r].height + this.height_1px, r, r); } t.isChanged = true; } if (Math.abs(d - t.rows[r].descender) > 0.000001) { t.rows[r].descender = d; t.isChanged = true; } } if (t.isChanged) { t.isChanged = false; t._initCellsArea(true); t.cache.reset(); t._cleanCellsTextMetricsCache(); t._prepareCellTextMetricsCache(t.visibleRange); t._trigger("reinitializeScroll"); t._trigger("selectionNameChanged", this.getSelectionName(/*bRangeText*/false)); t._trigger("selectionChanged", t.getSelectionInfo()); } t.objectRender.rebuildChartGraphicObjects(); t.cellCommentator.updateCommentPosition(); t.draw(lockDraw); }, enterCellRange: function (editor) { var t = this; if (!t.isFormulaEditMode) return; var ar = t.arrActiveFormulaRanges[t.arrActiveFormulaRanges.length - 1]; var s = t.getActiveRange(ar); editor.enterCellRange(s); return true; }, changeCellRange: function(editor,range){ var s = this.getActiveRange(range); if( range.isAbsolute ){ var ra = range.isAbsolute.split(":"), _s; if( ra.length >= 1 ){ var sa = s.split(":"); for( var ind = 0; ind < sa.length; ind++ ){ if( ra[ra.length>1?ind:0].indexOf("$") == 0 ){ sa[ind] = "$"+sa[ind]; } if( ra[ra.length>1?ind:0].lastIndexOf("$") != 0 ){ for(var i = 0; i< sa[ind].length; i++){ if(sa[ind].charAt(i).match(/[0-9]/gi)){ _s = i; break; } } sa[ind] = sa[ind].substr(0,_s) + "$" +sa[ind].substr(_s,sa[ind].length); } } s = ""; sa.forEach(function(e,i){ s += (i!=0?":":"") s += e; }) } } editor.changeCellRange(range,s); return true; }, getActiveRange: function (ar) { if (ar.c1 === ar.c2 && ar.r1 === ar.r2) {return this._getCellTitle(ar.c1, ar.r1);} if (ar.c1 === ar.c2 && ar.r1 === 0 && ar.r2 === this.rows.length -1) {var ct = this._getColumnTitle(ar.c1); return ct + ":" + ct;} if (ar.r1 === ar.r2 && ar.c1 === 0 && ar.c2 === this.cols.length -1) {var rt = this._getRowTitle(ar.r1); return rt + ":" + rt;} if (ar.r1 === 0 && ar.r2 === gc_nMaxRow0 || ar.r1 === 1 && ar.r2 === gc_nMaxRow ){return this._getColumnTitle(ar.c1) + ":" + this._getColumnTitle(ar.c2);} if (ar.c1 === 0 && ar.c2 === gc_nMaxCol0 || ar.c1 === 1 && ar.c2 === gc_nMaxCol ){return this._getRowTitle(ar.r1) + ":" + this._getRowTitle(ar.r2);} return this._getCellTitle(ar.c1, ar.r1) + ":" + this._getCellTitle(ar.c2, ar.r2); }, addFormulaRange: function (range) { var r = range !== undefined ? range : this.activeRange.clone(true); if (r.startCol === undefined || r.startRow === undefined) { r.startCol = r.c1; r.startRow = r.r1; } this.arrActiveFormulaRanges.push(r); }, changeFormulaRange: function (range) { for (var i = 0; i < this.arrActiveFormulaRanges.length; ++i) { if (this.arrActiveFormulaRanges[i].isEqual(range)) { var r = this.arrActiveFormulaRanges[i]; this.arrActiveFormulaRanges.splice(i, 1); this.arrActiveFormulaRanges.push(r); return; } } }, cleanFormulaRanges: function () { // Очищаем массив ячеек для текущей формулы this.arrActiveFormulaRanges = []; }, addAutoFilter: function (lTable, addFormatTableOptionsObj) { var t = this; var ar = t.activeRange.clone(true); var onChangeAutoFilterCallback = function (isSuccess) { if (false === isSuccess) return; return t.autoFilters.addAutoFilter(t, lTable, ar, undefined, false, addFormatTableOptionsObj); }; this._isLockedAll (onChangeAutoFilterCallback); }, applyAutoFilter: function (type, autoFilterObject) { var t = this; var ar = t.activeRange.clone(true); var onChangeAutoFilterCallback = function (isSuccess) { if (false === isSuccess) return; t.autoFilters.applyAutoFilter(type, autoFilterObject, ar, t); }; this._isLockedAll (onChangeAutoFilterCallback); }, sortColFilter: function (type,cellId) { var t = this; var ar = this.activeRange.clone(true); var onChangeAutoFilterCallback = function (isSuccess) { if (false === isSuccess) return; return t.autoFilters.sortColFilter(type, cellId, t, ar); }; this._isLockedAll (onChangeAutoFilterCallback); }, getAddFormatTableOptions: function(nameOption) { var ar = this.activeRange.clone(true); var t = this; var result = t.autoFilters.getAddFormatTableOptions(t, ar); return result; }, _loadFonts: function (fontArr, callback) { var originFonts = []; var i, n, k = 0; for (i = 0; i < fontArr.length ;++i) { for (n = 0; n < fontArr[i].length; ++n) { if(-1 == $.inArray(fontArr[i][n], originFonts)) { originFonts[k] = fontArr[i][n]; k++; } } } var api = window["Asc"]["editor"]; api._loadFonts(originFonts, callback); } }; /* * Export * ----------------------------------------------------------------------------- */ window["Asc"].WorksheetView = WorksheetView; } )(jQuery, window);