/*
 * (c) Copyright Ascensio System SIA 2010-2016
 *
 * This program is a free software product. You can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License (AGPL)
 * version 3 as published by the Free Software Foundation. In accordance with
 * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
 * that Ascensio System SIA expressly excludes the warranty of non-infringement
 * of any third-party rights.
 *
 * This program is distributed WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR  PURPOSE. For
 * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
 *
 * You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia,
 * EU, LV-1021.
 *
 * The  interactive user interfaces in modified source and object code versions
 * of the Program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU AGPL version 3.
 *
 * Pursuant to Section 7(b) of the License you must retain the original Product
 * logo when distributing the program. Pursuant to Section 7(e) we decline to
 * grant you any rights under trademark law for use of our trademarks.
 *
 * All the Product's GUI elements, including illustrations and icon sets, as
 * well as technical writing content are licensed under the terms of the
 * Creative Commons Attribution-ShareAlike 4.0 International. See the License
 * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
 *
 */

"use strict";

(function(window, undefined){

// Import
var g_memory = AscFonts.g_memory;

var CellValueType = AscCommon.CellValueType;
var c_oAscBorderStyles = AscCommon.c_oAscBorderStyles;
var fSortAscending = AscCommon.fSortAscending;
var fSortDescending = AscCommon.fSortDescending;
var parserHelp = AscCommon.parserHelp;
var oNumFormatCache = AscCommon.oNumFormatCache;
var gc_nMaxRow0 = AscCommon.gc_nMaxRow0;
var gc_nMaxCol0 = AscCommon.gc_nMaxCol0;
var g_oCellAddressUtils = AscCommon.g_oCellAddressUtils;
var CellAddress = AscCommon.CellAddress;
var isRealObject = AscCommon.isRealObject;
  var History = AscCommon.History;

var UndoRedoItemSerializable = AscCommonExcel.UndoRedoItemSerializable;
var UndoRedoData_CellSimpleData = AscCommonExcel.UndoRedoData_CellSimpleData;
var UndoRedoData_CellValueData = AscCommonExcel.UndoRedoData_CellValueData;
var UndoRedoData_FromToRowCol = AscCommonExcel.UndoRedoData_FromToRowCol;
var UndoRedoData_FromTo = AscCommonExcel.UndoRedoData_FromTo;
var UndoRedoData_IndexSimpleProp = AscCommonExcel.UndoRedoData_IndexSimpleProp;
var UndoRedoData_BBox = AscCommonExcel.UndoRedoData_BBox;
var UndoRedoData_SheetAdd = AscCommonExcel.UndoRedoData_SheetAdd;
var UndoRedoData_SheetPositions = AscCommonExcel.UndoRedoData_SheetPositions;
var UndoRedoData_DefinedNames = AscCommonExcel.UndoRedoData_DefinedNames;
var UndoRedoData_DefinedNamesChange = AscCommonExcel.UndoRedoData_DefinedNamesChange;
var g_oDefaultFormat = AscCommonExcel.g_oDefaultFormat;
var Border = AscCommonExcel.Border;
var RangeDataManagerElem = AscCommonExcel.RangeDataManagerElem;
var RangeDataManager = AscCommonExcel.RangeDataManager;

var cElementType = AscCommonExcel.cElementType;
var cArea3D = AscCommonExcel.cArea3D;
var cRef3D = AscCommonExcel.cRef3D;

var parserFormula = AscCommonExcel.parserFormula;

var c_oAscError = Asc.c_oAscError;
var c_oAscInsertOptions = Asc.c_oAscInsertOptions;
var c_oAscDeleteOptions = Asc.c_oAscDeleteOptions;
var c_oAscGetDefinedNamesList = Asc.c_oAscGetDefinedNamesList;
var c_oAscDefinedNameReason = Asc.c_oAscDefinedNameReason;
  
var g_nVerticalTextAngle = 255;
  //определяется в WorksheetView.js
var oDefaultMetrics = {
  ColWidthChars: 0,
  RowHeight: 0
};
var g_sNewSheetNamePattern = "Sheet";
var g_nSheetNameMaxLength = 31;
var g_nAllColIndex = -1;
var g_nAllRowIndex = -1;
var aStandartNumFormats = [];
var aStandartNumFormatsId = {};
var arrRecalc = {}, arrDefNameRecalc = {};
var oFormulaLocaleInfo = {
  Parse: true,
  DigitSep: true
};

  (function(){
    aStandartNumFormats[0] = "General";
    aStandartNumFormats[1] = "0";
    aStandartNumFormats[2] = "0.00";
    aStandartNumFormats[3] = "#,##0";
    aStandartNumFormats[4] = "#,##0.00";
    aStandartNumFormats[9] = "0%";
    aStandartNumFormats[10] = "0.00%";
    aStandartNumFormats[11] = "0.00E+00";
    aStandartNumFormats[12] = "# ?/?";
    aStandartNumFormats[13] = "# ??/??";
    aStandartNumFormats[14] = "m/d/yyyy";
    aStandartNumFormats[15] = "d-mmm-yy";
    aStandartNumFormats[16] = "d-mmm";
    aStandartNumFormats[17] = "mmm-yy";
    aStandartNumFormats[18] = "h:mm AM/PM";
    aStandartNumFormats[19] = "h:mm:ss AM/PM";
    aStandartNumFormats[20] = "h:mm";
    aStandartNumFormats[21] = "h:mm:ss";
    aStandartNumFormats[22] = "m/d/yyyy h:mm";
    aStandartNumFormats[37] = "#,##0_);(#,##0)";
    aStandartNumFormats[38] = "#,##0_);[Red](#,##0)";
    aStandartNumFormats[39] = "#,##0.00_);(#,##0.00)";
    aStandartNumFormats[40] = "#,##0.00_);[Red](#,##0.00)";
    aStandartNumFormats[45] = "mm:ss";
    aStandartNumFormats[46] = "[h]:mm:ss";
    aStandartNumFormats[47] = "mm:ss.0";
    aStandartNumFormats[48] = "##0.0E+0";
    aStandartNumFormats[49] = "@";
    for(var i in aStandartNumFormats)
    {
      aStandartNumFormatsId[aStandartNumFormats[i]] = i - 0;
    }
  })();

var c_oRangeType =
{
    Range:0,
    Col:1,
    Row:2,
	All:3
};
function getRangeType(oBBox){
	if(null == oBBox)
		oBBox = this.bbox;
	if(oBBox.c1 == 0 && gc_nMaxCol0 == oBBox.c2 && oBBox.r1 == 0 && gc_nMaxRow0 == oBBox.r2)
		return c_oRangeType.All;
	if(oBBox.c1 == 0 && gc_nMaxCol0 == oBBox.c2)
		return c_oRangeType.Row;
	else if(oBBox.r1 == 0 && gc_nMaxRow0 == oBBox.r2)
		return c_oRangeType.Col;
	else
		return c_oRangeType.Range;
}

/** @constructor */
function DependencyGraph( wb ) {
    this.wb = wb;
    this.nodesId = null;
    this.nodesCell = null;
    this.nodesArea = null;
    this.defNameList = {};
    this.defNameSheets = {};
    this.nodeslength = null;
    this.bSetRefError = false;
    this.oChangeNodeSlave = null;
    this.sTableNamePattern = "Table";
    this.nTableNameMaxIndex = 0;
    this.clear();
}
DependencyGraph.prototype = {
    clear:function () {
        this.nodesId = {};
        this.nodesCell = {};
        this.nodesArea = {};
        this.nodeslength = 0;
		this.bSetRefError = false;
    },
    nodeExist:function ( node ) {
        return this.nodeExist2( node.sheetId, node.cellId );
    },
    nodeExist2:function ( sheetId, cellId ) {
        return null != this.getNode( sheetId, cellId );
    },
    nodeExistWithArea:function ( sheetId, cellId ) {
        var bRes = this.nodeExist2( sheetId, cellId );
        if ( !bRes ) {
            var nodesSheetArea = this.nodesArea[sheetId];
            if ( null != nodesSheetArea ) {
                var bbox = AscCommonExcel.g_oRangeCache.getAscRange( cellId );
                bRes = nodesSheetArea.get( bbox ).all.length > 0;
            }
        }
        return bRes;
    },
    getNode3:function ( nodeId ) {
        return this.nodesId[nodeId];
    },
    getNode2:function ( node ) {
        return this.nodesId[node.nodeId];
    },
    getNode:function ( sheetId, cellId ) {
        return this.nodesId[getVertexId( sheetId, cellId )];
    },

    addNode2:function ( node ) {
        return this.addNode( node.sheetId, node.cellId );
    },
    addNode:function ( sheetId, cellId ) {
		var _this = this;
        var nodeId = getVertexId( sheetId, cellId );
        var oRes = this.nodesId[nodeId];
        if ( null == oRes ) {
            var node = new Vertex( sheetId, cellId, this.wb );
            var oBBoxNode = node.getBBox();
            if ( node.isArea ) {
                var nodesSheetArea = this.nodesArea[node.sheetId];
                if ( null == nodesSheetArea ) {
					nodesSheetArea = new RangeDataManager(function(data, from, to){_this._changeNode(data, from, to);});
                    this.nodesArea[node.sheetId] = nodesSheetArea;
                }
                nodesSheetArea.add( oBBoxNode, node );
            }
            else {
                var nodesSheetCell = this.nodesCell[node.sheetId];
                if ( null == nodesSheetCell ) {
					nodesSheetCell = new AscCommonExcel.CellArea(function(data, from, to){_this._changeNode(data, from, to);});
                    this.nodesCell[node.sheetId] = nodesSheetCell;
                }
                nodesSheetCell.add( oBBoxNode.r1, oBBoxNode.c1, node );
            }
            oRes = node;
        }
        return oRes;
    },
    addEdge2:function ( nodeFrom, nodeTo ) {
        nodeFrom.addMasterEdge( nodeTo );
        nodeTo.addSlaveEdge( nodeFrom );
    },
    addEdge:function ( sheetIdFrom, cellIdFrom, sheetIdTo, cellIdTo ) {
        var n1 = this.addNode( sheetIdFrom, cellIdFrom ),
            n2 = this.addNode( sheetIdTo, cellIdTo );
        this.addEdge2( n1, n2 );
    },
    getNodeBySheetId:function ( sheetId ) {
        var arr = [];
        var nodesSheetCell = this.nodesCell[sheetId];
        if ( nodesSheetCell ) {
            var aNodes = nodesSheetCell.getAll();
            for ( var i = 0, length = aNodes.length; i < length; i++ ) {
                var node = aNodes[i].data;
                var n = node.getSlaveEdges();
                if ( n ) {
                    arr.push( node );
                }
            }
        }
		var nodesSheetArea = this.nodesArea[sheetId];
        if ( nodesSheetArea ) {
            var aNodes = nodesSheetArea.getAll();
            for ( var i = 0, length = aNodes.length; i < length; i++ ) {
                var node = aNodes[i].data;
                var n = node.getSlaveEdges();
                if ( n ) {
                    arr.push( node );
                }
            }
        }
        return arr;
    },
    getNodeBySheetIdAll:function ( sheetId ) {
        var arr = [];
        var nodesSheetCell = this.nodesCell[sheetId];
        if ( nodesSheetCell ) {
            var aNodes = nodesSheetCell.getAll();
            for ( var i = 0, length = aNodes.length; i < length; i++ ) {
                arr.push( aNodes[i].data );
            }
        }
		var nodesSheetArea = this.nodesArea[sheetId];
        if ( nodesSheetArea ) {
            var aNodes = nodesSheetArea.getAll();
            for ( var i = 0, length = aNodes.length; i < length; i++ ) {
                arr.push( aNodes[i].data );
            }
        }
        return arr;
    },
    deleteNode:function ( node ) {
        if(node.isDefinedName) return;
        if ( node.isArea ) {
            var nodesSheetArea = this.nodesArea[node.sheetId];
            if ( nodesSheetArea )
                nodesSheetArea.removeElement( new RangeDataManagerElem( node.getBBox(), node ) );
        }
        else {
            var nodesSheetCell = this.nodesCell[node.sheetId];
            if ( nodesSheetCell )
                nodesSheetCell.removeElement( new RangeDataManagerElem( node.getBBox(), node ) );
        }
    },
	deleteNodes : function(sheetId, bbox){
		var bSetRefErrorOld = this.bSetRefError;
		this.bSetRefError = true;
		this.oChangeNodeSlave = { toDelete: {}, toMove: {} };
		var nodesSheetArea = this.nodesArea[sheetId];
		var oGetRes;
		if(nodesSheetArea)
		{
			oGetRes = nodesSheetArea.get(bbox);
			for(var i = 0, length = oGetRes.inner.length; i < length; ++i)
				nodesSheetArea.removeElement(oGetRes.inner[i]);
        }
		var nodesSheetCell = this.nodesCell[sheetId];
		if(nodesSheetCell)
		{
			oGetRes = nodesSheetCell.get(bbox);
			for(var i = 0, length = oGetRes.length; i < length; ++i)
				nodesSheetCell.removeElement(oGetRes[i]);
		}
		this.changeNodeEnd();
		this.bSetRefError = bSetRefErrorOld;
    },
    deleteMasterNodes:function ( sheetId, cellId ) {
        var node = this.getNode( sheetId, cellId );
        if ( node ) {
            var arr = node.deleteAllMasterEdges();
            for ( var i in arr ) {
                var nodeMaster = arr[i];
                if ( nodeMaster.refCount <= 0 )
                    this.deleteNode( nodeMaster );
            }
        }
		return node;
    },
	deleteMasterNodes2 : function(sheetId, cellId){
		var node = this.deleteMasterNodes(sheetId, cellId);
		if(node && node.refCount <= 0)
			this.deleteNode(node);
		return node;
	},
	deleteMasterNodes3:function ( node ) {
	    var arr = node.deleteAllMasterEdges();
	    for (var i in arr) {
	        var nodeMaster = arr[i];
	        if (nodeMaster.refCount <= 0)
	            this.deleteNode(nodeMaster);
	    }
	},
    getSlaveNodes:function ( sheetId, cellId ) {
        //todo
        return null;
    },
    getMasterNodes:function ( sheetId, cellId ) {
        //todo
        return null;
    },
    getNodesLength:function () {
        return this.nodeslength;
    },
    checkOffset:function ( BBox, offset, wsId, noDelete ) {
        var _this = this,
            bHor = 0 != offset.offsetCol,
            toDelete = offset.offsetCol < 0 || offset.offsetRow < 0,
            bSetRefErrorOld = this.bSetRefError;
		this.bSetRefError = true;
		var oShiftGetBBox = AscCommonExcel.shiftGetBBox(BBox, bHor);
		var sShiftGetBBoxName = oShiftGetBBox.getName();
		this.wb.needRecalc.nodes[getVertexId(wsId, sShiftGetBBoxName)] = [wsId, sShiftGetBBoxName];
		this.wb.needRecalc.length++;
		this.oChangeNodeSlave = { toDelete: {}, toMove: {}};
        var nodesSheetArea = this.nodesArea[wsId];
		if(nodesSheetArea)
			nodesSheetArea.shift(BBox, !toDelete, bHor);
        var nodesSheetCell = this.nodesCell[wsId];
		if(nodesSheetCell)
            nodesSheetCell.shift( BBox, !toDelete, bHor );
		this.changeNodeEnd();
		this.bSetRefError = bSetRefErrorOld;
    },
    changeNodeProcessDelete: function (node, oFormulas, toDelete) {
        //todo deleteAllSlaveEdges deleteMasterNodes
        var oSlaves = node.deleteAllSlaveEdges(), slave, formula;
        if (this.bSetRefError) {
            //выставляем #REF!
            for (var i in oSlaves) {
                slave = oSlaves[i];
                if( slave instanceof DefNameVertex ){
					if(!slave.isTable)
                    	this.wb.delDefinesNames(slave.getAscCDefName());
                    continue;
                }
                if (null == toDelete || slave != toDelete[slave.nodeId]) {
                    formula = slave.setRefError(node);
                    if (null != formula) {
                        if (oFormulas)
                            oFormulas[slave.nodeId] = { node: slave, formula: formula };
                        else
                            slave.setFormula(formula, true, false);
                    }
                }
            }
        }
        this.deleteMasterNodes3(node);
    },
    changeNodeProcessMove: function (node, from, to, oFormulas, toDelete) {
        if (null == toDelete || node != toDelete[node.nodeId])
            node.moveOuter(from, to, oFormulas);
    },
    changeNodeEnd : function(){
        var oChangeNodeSlave = this.oChangeNodeSlave;
        //обнуляем, потому что в цикле можем опять попасть в _changeNode со старым this.oChangeNodeSlave
        this.oChangeNodeSlave = null;
        //накапливаем формулы, потому что когда мы меняем формулу мы удаляем masterNodes, а эти node могут быть необработаными и влиять на формулу(C2=A2+B2 -> C1=A1+B2)
        var oFormulas = {};
        for (var i in oChangeNodeSlave.toDelete) {
            var elem = oChangeNodeSlave.toDelete[i];
            this.changeNodeProcessDelete(elem.node, oFormulas, oChangeNodeSlave.toDelete);
        }
        for (var i in oChangeNodeSlave.toMove) {
            var elem = oChangeNodeSlave.toMove[i];
            this.changeNodeProcessMove(elem.node, elem.from, elem.to, oFormulas, oChangeNodeSlave.toDelete);
        }
        for (var i in oFormulas) {
            var elem = oFormulas[i];
            if (null == elem.formula) {
                var node = elem.node;
                var cell = node.returnCell();
                if (cell && cell.formulaParsed) {
                    this.wb.dependencyFormulas.deleteMasterNodes2(node.sheetId, node.cellId);
                    addToArrRecalc(node.sheetId, cell);
                }
            }
            else
                elem.node.setFormula(elem.formula, true, false);
        }
    },
	_changeNode : function(node, from, to){
        var toDelete = null == to;
		var toAdd = null == from;
		var wsId = node.sheetId;
		var sOldCellId = node.cellId;
		if(toAdd)
		{
			this.nodesId[node.nodeId] = node;
			this.nodeslength++;
		}
		else if(toDelete)
		{
			if (this.oChangeNodeSlave)
			    this.oChangeNodeSlave.toDelete[node.nodeId] = { node: node, from: from, to: to };
			else {
			    this.changeNodeProcessDelete(node, null);
			}
			delete this.nodesId[node.nodeId];
			this.nodeslength--;
		}
		else {
		    var sOldnodeId = node.nodeId;
		    node.moveInner(to);
		    if (this.oChangeNodeSlave)
		        this.oChangeNodeSlave.toMove[node.nodeId] = { node: node, from: from, to: to };
		    else
		        this.changeNodeProcessMove(node, from, to, null);
            delete this.nodesId[sOldnodeId];
            this.nodesId[node.nodeId] = node;
		}
		//важно что ячейки уже сдвинулись, поэтому до вызова returnCell нужно сделать node.move и сдвинуть ячейки в aGCells
		if(!node.isArea)
		{
            var cwf = this.wb.cwf[wsId];
			if(cwf)
			{
				if(!toAdd)
					delete cwf[sOldCellId];
				if(!toDelete)
				{
					var cell = node.returnCell();
					if ( cell && cell.formulaParsed )
						cwf[node.cellId] = node.cellId;
				}
            }
        }
    },
	getCellInRange : function(sheetId, bbox){
		var res = [], oGetRes, nodesSheetCell = this.nodesCell[sheetId];
        if ( nodesSheetCell ) {
			oGetRes = nodesSheetCell.get(bbox);
            for ( var i = 0, length = oGetRes.length; i < length; i++ ) {
				res.push(oGetRes[i].data);
            }
        }
		return res;
	},
	getAreaInRange : function(sheetId, bbox){
		var res = [], oGetRes, nodesSheetArea = this.nodesArea[sheetId];
		if(nodesSheetArea)
		{
			oGetRes = nodesSheetArea.get(bbox);
			for(var i = 0, length = oGetRes.all.length; i < length; i++)
			{
				res.push(oGetRes.all[i].data);
            }
		}
		return res;
    },
	getInRange : function(sheetId, bbox){
		return this.getCellInRange(sheetId, bbox).concat(this.getAreaInRange(sheetId, bbox));
	},
	helper : function(BBoxFrom, oBBoxTo, wsId){
		var oGetRes, node, nodesSheetCell = this.nodesCell[wsId], nodesSheetArea = this.nodesArea[wsId];
		var offset = { offsetCol: oBBoxTo.c1 - BBoxFrom.c1, offsetRow: oBBoxTo.r1 - BBoxFrom.r1 };
		this.oChangeNodeSlave = { toDelete: {}, toMove: {} };
		var elem, bbox;
		if(nodesSheetCell)
			oGetRes = nodesSheetCell.move(BBoxFrom, oBBoxTo);
		if(nodesSheetArea)
		    oGetRes = nodesSheetArea.move(BBoxFrom, oBBoxTo);
		this.changeNodeEnd();
	},
    drawDep:function ( cellId, se ) {
        // ToDo неиспользуемая функция, реализовать после выпуска
        if ( !cellId )
            return;
        var _wsV = this.wb.oApi.wb.getWorksheet(),
            _cc = _wsV.cellCommentator,
            ctx = _wsV.overlayCtx,
            _wsVM = _wsV.model,
            nodeId = getVertexId( _wsVM.getId(), cellId ),
            node = this.getNode( _wsVM.getId(), cellId ),
            cell;

        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 = _wsVM.getMergedByCell( row, col );

            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;
        }

        if ( !node )
            return;

        cell = node.returnCell();

        if ( !cell )
            return;

        var m = [cell.nRow, cell.nCol],
            rc = [], me = se ? node.getSlaveEdges() : node.getMasterEdges();

        for ( var id in me ) {
            if ( me[id].sheetId != node.sheetId )
                return;

            if ( !me[id].isArea ) {
                var _t1 = gCM( _wsV, me[id].returnCell().nCol, me[id].returnCell().nRow )

                rc.push( { 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].getBBox().c1, me[id].getBBox().r1 ),
                    _t2 = gCM( _wsV, me[id].getBBox().c2, me[id].getBBox().r2 );

                rc.push( { 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  } );
            }
        }

        if ( rc.length == 0 )
            return;

        var color = new AscCommon.CColor( 0, 0, 255 );

        function draw_arrow( context, fromx, fromy, tox, toy ) {
            var headlen = 9;
            var dx = tox - fromx;
            var dy = toy - fromy;
            var angle = Math.atan2( dy, dx ), _a = Math.PI / 18;
            // ToDo посмотреть на четкость moveTo, lineTo
            context.save()
                .setLineWidth( 1 )
                .beginPath()
                .moveTo( _cc.pxToPt( fromx ), _cc.pxToPt( fromy ) )
                .lineTo( _cc.pxToPt( tox ), _cc.pxToPt( toy ) );
            // .dashLine(_cc.pxToPt(fromx-.5), _cc.pxToPt(fromy-.5), _cc.pxToPt(tox-.5), _cc.pxToPt(toy-.5), 15, 5)
            context
                .moveTo(
                _cc.pxToPt( tox - headlen * Math.cos( angle - _a ) ),
                _cc.pxToPt( toy - headlen * Math.sin( angle - _a ) ) )
                .lineTo( _cc.pxToPt( tox ), _cc.pxToPt( toy ) )
                .lineTo(
                _cc.pxToPt( tox - headlen * Math.cos( angle + _a ) ),
                _cc.pxToPt( toy - headlen * Math.sin( angle + _a ) ) )
                .lineTo(
                _cc.pxToPt( tox - headlen * Math.cos( angle - _a ) ),
                _cc.pxToPt( toy - headlen * Math.sin( angle - _a ) ) )
                .setStrokeStyle( color )
                .setFillStyle( color )
                .stroke()
                .fill()
                .closePath()
                .restore();
        }

        function h( m, rc ) {
            var m = gCM( _wsV, m[1], m[0] );
            var arrowPointTop = 10, arrowPointLeft = 10;
            for ( var i = 0; i < rc.length; i++ ) {
                var m2 = rc[i],
                    x1 = Math.floor( m2.apl ),
                    y1 = Math.floor( m2.apt ),
                    x2 = Math.floor( m.left + m.width / 4 ),
                    y2 = Math.floor( m.top + m.height / 2 );

                if ( x1 < 0 && x2 < 0 || y1 < 0 && y2 < 0 )
                    continue;

                // ToDo посмотреть на четкость rect
                if ( m2.apl > 0 && m2.apt > 0 )
                    ctx.save()
                        .setLineWidth( 1 )
                        .setStrokeStyle( color )
                        .rect( _cc.pxToPt( m2.l ), _cc.pxToPt( m2.t ), _cc.pxToPt( m2.w - 1 ), _cc.pxToPt( m2.h - 1 ) )
                        .stroke()
                        .restore();
                if ( y1 < 0 && x1 != x2 )
                    x1 = x1 - Math.floor( Math.sqrt( ((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)) * y1 * y1 / ((y2 - y1) * (y2 - y1)) ) / 2 )
                if ( x1 < 0 && y1 != y2 )
                    y1 = y1 - Math.floor( Math.sqrt( ((y1 - y2) * (y1 - y2) + (x1 - x2) * (x1 - x2)) * x1 * x1 / ((x2 - x1) * (x2 - x1)) ) / 2 )

                draw_arrow( ctx, x1 < 0 ? _wsV.getCellLeft( 0, 0 ) : x1, y1 < 0 ? _wsV.getCellTop( 0, 0 ) : y1, x2, y2 );

                if ( m2.apl > 0 && m2.apt > 0 )
                    ctx.save()
                        .beginPath()
                        .arc( _cc.pxToPt( Math.floor( m2.apl ) ),
                        _cc.pxToPt( Math.floor( m2.apt ) ),
                        3, 0, 2 * Math.PI, false, -0.5, -0.5 )
                        .setFillStyle( color )
                        .fill()
                        .closePath()
                        .restore();
            }
        }

        ctx.clear();
        _wsV._drawSelection();

        if ( se ) {
            for ( var i = 0; i < rc.length; i++ )
                h( rc[i], [m] );
        }
        else
            h( m, rc );
    },
    removeNodeBySheetId: function (sheetId) {
        this.oChangeNodeSlave = { toDelete: {}, toMove: {} };
		var nodesSheetArea = this.nodesArea[sheetId];
		if(nodesSheetArea)
			nodesSheetArea.removeAll();
        var nodesSheetCell = this.nodesCell[sheetId];
		if(nodesSheetCell)
		    nodesSheetCell.removeAll();
		this.changeNodeEnd();
    },
    getNodeDependence:function ( aElems, aDefinedNames ) {
        var oRes = { oMasterNodes: {}, oMasterAreaNodes: {}, oMasterAreaNodesRestricted: {}, oWeightMap: {}, oNodeToArea: {}, nCounter: 0 },
            aWeightMapMasters = [],
            aWeightMapMastersNodes = [],
            node,
            elem,
            oSheetRanges = {},
            oSheetWithArea = {};//все sheet на которых есть area для пересчета
        while ( null != aElems || null != aDefinedNames ) {
            if(null != aElems){
				for ( var i in aElems ) {
					elem = aElems[i];
					var sheetId = elem[0];
					var cellId = elem[1];
					//нужно обавлять в oSheetRanges даже несушествующие node, чтобы поддержать именении ячеек в SUM(A1:B2)
					this._getNodeDependenceNodeToRange(sheetId, AscCommonExcel.g_oRangeCache.getAscRange(cellId), oSheetRanges);
					node = this.getNode(sheetId, cellId);
					if ( node && null == oRes.oWeightMap[node.nodeId] ) {
						//все node из aElems записываем в master
						var oWeightMapElem = { id: oRes.nCounter++, cur: 0, max: 0, gray: false, bad: false, master: true, area: node.isArea };
						if (node.isArea)
							oSheetWithArea[node.sheetId] = 1;
						aWeightMapMasters.push( oWeightMapElem );
						aWeightMapMastersNodes.push( node );
						oRes.oWeightMap[node.nodeId] = oWeightMapElem;
						this._getNodeDependence( oRes, oSheetRanges, node );
					}
				}
			}
			if(null != aDefinedNames){
				for(var i = 0; i < aDefinedNames.length; ++i){
					var node = aDefinedNames[i];
					if ( node && null == oRes.oWeightMap[node.nodeId] ) {
						//все node из aDefinedNames записываем в master
						var oWeightMapElem = { id: oRes.nCounter++, cur: 0, max: 0, gray: false, bad: false, master: true, area: false };
						aWeightMapMasters.push( oWeightMapElem );
						aWeightMapMastersNodes.push( node );
						oRes.oWeightMap[node.nodeId] = oWeightMapElem;
						this._getNodeDependence( oRes, oSheetRanges, node );
					}
				}
			}
            aElems = null;
			aDefinedNames = null;
            //расширяем за счет area nodes
            for ( var i in oSheetRanges ) {
                var oSheetRange = oSheetRanges[i];
                if ( oSheetRange.changed ) {
                    oSheetRange.changed = false;
                    var nodesSheetArea = this.nodesArea[i];
                    if ( null != nodesSheetArea ) {
                        var aAllOuter = null;
                        if ( null == oSheetRange.prevRange ) {
                            var oGetRes = nodesSheetArea.get( oSheetRange.range );
                            if ( oGetRes.all.length > 0 )
                                aAllOuter = oGetRes.all;
                        }
                        else {
                            var aEdgeBBox = [];
							var bLeft = oSheetRange.range.c1 < oSheetRange.prevRange.c1;
							var bRight = oSheetRange.range.c2 > oSheetRange.prevRange.c2;
							var bTop = oSheetRange.range.r1 < oSheetRange.prevRange.r1;
							var bBottom = oSheetRange.range.r2 > oSheetRange.prevRange.r2;
                            if (bLeft)
                                aEdgeBBox.push(new Asc.Range(oSheetRange.range.c1, oSheetRange.range.r1, oSheetRange.prevRange.c1 - 1, oSheetRange.range.r2));
                            if (bRight)
                                aEdgeBBox.push(new Asc.Range(oSheetRange.prevRange.c2 + 1, oSheetRange.range.r1, oSheetRange.range.c2, oSheetRange.range.r2));
                            if (bTop || bBottom){
								var nNewC1, nNewC2;
								if(bLeft)
									nNewC1 = oSheetRange.range.c1 + 1;
								else
									nNewC1 = oSheetRange.range.c1;
								if(bRight)
									nNewC2 = oSheetRange.range.c2 - 1;
								else
									nNewC2 = oSheetRange.range.c2;
								if(bTop)
									aEdgeBBox.push(new Asc.Range(nNewC1, oSheetRange.range.r1, nNewC2, oSheetRange.prevRange.r1 - 1));
								if(bBottom)
									aEdgeBBox.push(new Asc.Range(nNewC1, oSheetRange.prevRange.r2 + 1, nNewC2, oSheetRange.range.r2));
							}
                            aAllOuter = [];
                            for ( var j = 0, length = aEdgeBBox.length; j < length; j++ ) {
                                var bbox = aEdgeBBox[j];
                                var oGetRes = nodesSheetArea.get( bbox );
                                if ( oGetRes.all.length > 0 )
                                    aAllOuter = aAllOuter.concat( oGetRes.all );
                            }
                        }
                        if ( aAllOuter && aAllOuter.length > 0 ) {
                            if ( null == aElems )
                                aElems = [];
                            for ( var j in aAllOuter ) {
                                var node = aAllOuter[j].data;
								aElems[node.nodeId] = [node.sheetId, node.cellId];
                            }
                        }
                    }
                }
            }
        }
        var bMasterAreaNodesExist = false;
        var oAllMasterAreaNodes = {};
        for ( var i = 0, length = aWeightMapMasters.length; i < length; i++ ) {
            var oWeightMapElem = aWeightMapMasters[i];
            //возвращаем только настощие master
            if ( oWeightMapElem.master ) {
                node = aWeightMapMastersNodes[i];
                if (oWeightMapElem.area) {
                    bMasterAreaNodesExist = true;
                    oAllMasterAreaNodes[node.nodeId] = node;
                }
                else
                    oRes.oMasterNodes[node.nodeId] = node;
            }
        }
        if (bMasterAreaNodesExist) {
            //заносим все одинарные ячейки для пересчета в CellArea, чтобы определить какие из них лежат внутри area
            var oCellsForCalculation = {};
            for (var i in oRes.oWeightMap) {
                var elem = oRes.oWeightMap[i];
                if (!elem.area && !elem.isDefinedName) {
                    var node = this.wb.dependencyFormulas.nodesId[i];
                    //если ячейка из oMasterNodes, даже если в ней формула, то она не может иметь master из oWeightMap(иначе она бы перестала быть master) - такие ячейки не добавляем
                    //остальные ячейки имеют master из oWeightMap их надо проверять
                    if (node && oSheetWithArea[node.sheetId] && !oRes.oMasterNodes[node.nodeId]) {
                        var oCellsForCalculationSheet = oCellsForCalculation[node.sheetId];
                        if (null == oCellsForCalculationSheet) {
                            oCellsForCalculationSheet = new AscCommonExcel.CellArea(null);
                            oCellsForCalculation[node.sheetId] = oCellsForCalculationSheet;
                        }
                        var bbox = node.getBBox();
                        oCellsForCalculationSheet.add(bbox.r1, bbox.c1, node);
                    }
                }
            }
            //делим oAllMasterAreaNodes на те что можно посчитать сразу и те что имеют внутри себя непосчитанные ячейки.
            //заполняем oNodeToArea ячейками и ссылками на AreaNode в которых ини лежат
            for (var i in oAllMasterAreaNodes) {
                var nodeMaster = oAllMasterAreaNodes[i];
                //элемент запоминает сколько node надо посечить прежде чем считать nodeMaster
                var nodeMasterElement = { node: nodeMaster, cur: 0, max: 0 };
                var bRestricted = false;
                var oCellsForCalculationSheet = oCellsForCalculation[nodeMaster.sheetId];
                if (oCellsForCalculationSheet) {
                    var oGetRes = oCellsForCalculationSheet.get(nodeMaster.getBBox());
                    if (oGetRes.length > 0) {
                        bRestricted = true;
                        for (var j = 0; j < oGetRes.length; ++j) {
                            var node = oGetRes[j].data;
                            var oNodeToAreaElement = oRes.oNodeToArea[node.nodeId];
                            if (null == oNodeToAreaElement) {
                                oNodeToAreaElement = [];
                                oRes.oNodeToArea[node.nodeId] = oNodeToAreaElement;
                            }
                            nodeMasterElement.max++;
                            oNodeToAreaElement.push(nodeMasterElement);
                        }
                    }
                }
                if (bRestricted)
                    oRes.oMasterAreaNodesRestricted[nodeMaster.nodeId] = nodeMasterElement;
                else
                    oRes.oMasterAreaNodes[nodeMaster.nodeId] = nodeMaster;
            }
        }
        return oRes;
    },
    _getNodeDependence:function ( oRes, oSheetRanges, node ) {
        var oResMapCycle = null;
        var bStop = false;
        var oWeightMapElem = oRes.oWeightMap[node.nodeId];
        if ( null == oWeightMapElem ) {
			if(node.isDefinedName)
				oWeightMapElem = { id: oRes.nCounter++, cur: 0, max: 1, gray: false, bad: false, master: false, area: false };
			else
				oWeightMapElem = { id: oRes.nCounter++, cur: 0, max: 1, gray: false, bad: false, master: false, area: node.isArea };
            oRes.oWeightMap[node.nodeId] = oWeightMapElem;
        }
        else {
            oWeightMapElem.max++;
            //если пришли в gray node, то это цикл
            if (oWeightMapElem.gray) {
                bStop = true;
                oResMapCycle = {};
                oResMapCycle[oWeightMapElem.id] = oWeightMapElem;
                oWeightMapElem.bad = true;
                oWeightMapElem.max--;
            }
            else if (oWeightMapElem.master && oWeightMapElem.max > 1) {
                bStop = true;
                //если повторно пришли в master node, то не считаем ее master
                oWeightMapElem.master = false;
                oWeightMapElem.max--;
            }
		}
        if (!bStop && 1 == oWeightMapElem.max && !node.isDefinedName )
            this._getNodeDependenceNodeToRange( node.sheetId, node.getBBox(), oSheetRanges );
        if (!bStop && oWeightMapElem.max <= 1) {
            oWeightMapElem.gray = true;
            var aNext = node.getSlaveEdges();
            for (var i in aNext) {
                var oCurMapCycle = this._getNodeDependence(oRes, oSheetRanges, aNext[i], oWeightMapElem);
                if (null != oCurMapCycle) {
                    oWeightMapElem.bad = true;
                    for (var i in oCurMapCycle) {
                        var oCurElem = oCurMapCycle[i];
                        if (oWeightMapElem != oCurElem) {
                            if (null == oResMapCycle)
                                oResMapCycle = {};
                            oResMapCycle[oCurElem.id] = oCurElem;
                        }
                    }
                }
            }
            oWeightMapElem.gray = false;
        }
        return oResMapCycle;
    },
    _getNodeDependenceNodeToRange:function ( sheetId, bbox, oSheetRanges ) {
        if (!bbox) {
            return;
        }
        var oSheetRange = oSheetRanges[sheetId];
        if ( null == oSheetRange ) {
            oSheetRange = {range:null, changed:false, prevRange:null};
            oSheetRanges[sheetId] = oSheetRange;
        }
        if ( null == oSheetRange.range || !oSheetRange.range.containsRange( bbox ) ) {
            if ( null == oSheetRange.range )
                oSheetRange.range = bbox.clone();
            else {
                if ( !oSheetRange.changed )
                    oSheetRange.prevRange = oSheetRange.range.clone();
                oSheetRange.range.union2( bbox );
            }
            oSheetRange.changed = true;
        }
    },
    getAll:function () {
        return this.nodesId;
    },

    /*Defined Names section*/
    getDefNameNode:function ( node ) {
        var ret = this.defNameList[node];
        return ret ? ret : null;
    },
    getDefNameNodeByName:function ( name, sheetId ) {

        name = name.toLowerCase();

        var sheetNodeList,
            nodeId,
            oRes = false;

        if ( !AscCommon.rx_defName.test( name ) ) {
            return oRes ;
        }

        if ( null != sheetId ) {
            sheetNodeList = this.defNameSheets[sheetId];
            if ( sheetNodeList ) {
                nodeId = getDefNameVertexId( sheetId, name );
                oRes = sheetNodeList[nodeId];
                if ( oRes && oRes.Ref ) return oRes;
            }
        }

        sheetNodeList = this.defNameSheets["WB"];
        if( sheetNodeList ){
            nodeId = getDefNameVertexId( null, name );
            oRes = sheetNodeList[nodeId];
        }
        if ( oRes && !oRes.Ref ) oRes = false;

        return oRes;
    },
    getDefNameNodeByRef:function ( ref, sheetId ) {
        var sheetNodeList, id;

        if ( null != sheetId ) {
            sheetNodeList = this.defNameSheets[sheetId];
            for ( id in sheetNodeList ) {
                if ( !sheetNodeList[id].Hidden && sheetNodeList[id].Ref === ref) {
                    return sheetNodeList[id].Name;
                }
            }
        }

        sheetNodeList = this.defNameSheets["WB"];
        for ( id in sheetNodeList ) {
            if ( !sheetNodeList[id].Hidden && sheetNodeList[id].Ref === ref ) {
                return sheetNodeList[id].Name;
            }
        }

        return false;
    },
    addDefinedNameNode:function ( defName, sheetId, defRef, defHidden, bUndo ) {

        var ws = this.wb.getWorksheet( sheetId );
        ws ? sheetId = ws.getId() : null;

        var nodeId = getDefNameVertexId( sheetId, defName ),
            oRes = this.defNameList[nodeId], dfv, defNameSheetsList;

        if ( null == oRes || ( null == oRes.Ref && null == defRef ) ) {
            dfv = new DefNameVertex( sheetId, defName, defRef, defHidden, this.wb );
            oRes = (this.defNameList[dfv.nodeId] = dfv);
            defNameSheetsList = this.defNameSheets[dfv.sheetId];
            if ( defNameSheetsList == null ) {
                defNameSheetsList = {};
                this.defNameSheets[dfv.sheetId] = defNameSheetsList;
            }
            defNameSheetsList[dfv.nodeId] = dfv;
        }
        else if( null == oRes.Ref && null != defRef ){
            oRes.Ref = defRef;
            oRes.isTable = undefined;
            addToArrDefNameRecalc(oRes);
        }

        /*поставить зависимость между ячейками и текущим ИД*/

        if ( bUndo ) {
            oRes.Ref = defRef;
        }

        if( !oRes.isTable && oRes.Ref != undefined && oRes.Ref != null ){
            addToArrDefNameRecalc(oRes);
        }

        return oRes;

    },
    removeDefName:function ( sheetId, name ) {

        var ws = this.wb.getWorksheet( sheetId );
        ws ? sheetId = ws.getId() : null;

        var nodeId = getDefNameVertexId( sheetId, name ),
            oRes = this.defNameList[nodeId], ret = null;

        if ( oRes ) {
            this.defNameList[nodeId].Ref = null;
            ret = oRes;
            addToArrDefNameRecalc(oRes);
        }

        return ret;

    },
    removeDefNameBySheet:function ( sheetId ) {

        var nodesList = this.defNameList, retRes = {}, defN, seUndoRedo = [], nSE, wsIndex, ws = this.wb.getWorksheetById(sheetId ), wsName = ws.getName();

        for ( var id in nodesList ) {
            defN = nodesList[id];
            if ( defN.isTable && defN.Ref ){
                var a = defN.Ref.split("!")[0];
                if( a.localeCompare(parserHelp.getEscapeSheetName(wsName)) == 0 ){
                    History.Add( AscCommonExcel.g_oUndoRedoWorkbook, AscCH.historyitem_Workbook_DefinedNamesDelete, null, null, new UndoRedoData_DefinedNames( defN.Name, defN.Ref, undefined, defN.isTable ) );
                    defN.Ref = null;
                }
                continue;
            }
            if ( !defN.isTable && defN.parsedRef && defN.parsedRef.removeSheet( sheetId ) ) {
                seUndoRedo = [];

                nSE = defN.getSlaveEdges();
                for ( var nseID in nSE ) {
                    seUndoRedo.push( nseID );
                }

                wsIndex = this.wb.getWorksheetById( defN.sheetId );

                History.Add( AscCommonExcel.g_oUndoRedoWorkbook, AscCH.historyitem_Workbook_DefinedNamesDelete, null, null,
                    new UndoRedoData_DefinedNames( defN.Name, defN.Ref, wsIndex ? wsIndex.getIndex() : undefined, defN.isTable, seUndoRedo ) );

                if ( defN.sheetId == sheetId ) {
                    defN.Ref = null;
                    defN.parsedRef = null;
                    retRes[id] = defN;
                }
                else if( defN.Ref ){
                    defN.Ref = defN.parsedRef.Formula = defN.parsedRef.assemble( true );
                    retRes[id] = defN;
                }
                addToArrDefNameRecalc(defN);
            }
        }
        return retRes;
    },
    changeDefName:function ( oldDefName, newDefName ) {

        var ws = this.wb.getWorksheet( oldDefName.LocalSheetId );
      var sheetId = ws ? ws.getId() : null;

        var oldN = this.getDefNameNodeByName( oldDefName.Name, sheetId ),
            sheetNodeList, nodeId,
            name = oldDefName.Name;

        sheetNodeList = this.defNameSheets[sheetId || "WB"];
        nodeId = getDefNameVertexId( sheetId || "WB", name );

        sheetNodeList ? delete sheetNodeList[nodeId] : null;

        delete this.defNameList[nodeId];

        if(!oldN){
            return false;
        }

        oldN.changeDefName( newDefName );

        this.defNameList[oldN.nodeId] = oldN;
        sheetNodeList[oldN.nodeId] = oldN;

        if (oldDefName.isTable) {
          var dataRange = parserHelp.parse3DRef(oldDefName.Ref);
          if (dataRange) {
            var sheetDefName = this.wb.getWorksheetByName(dataRange.sheet);
            if (sheetDefName) {
              var tableParts = sheetDefName.TableParts;
              for (var i = 0; i < tableParts.length; i++) {
                if (tableParts[i].DisplayName == name) {
                  tableParts[i].DisplayName = oldN.Name;
                }
              }
            }
          }
        } else {
          oldN.deleteAllMasterEdges();
          addToArrDefNameRecalc(oldN);
        }

        return oldN;
    },
    copyDefNameByWorksheet:function( oldSheetId, newSheetId ){

        var obj = {}, oldS = this.defNameSheets[oldSheetId], defNamNode,
            oldWS = this.wb.getWorksheetById(oldSheetId ),
            newWS = this.wb.getWorksheetById(newSheetId);

        for( var id in oldS ){
            defNamNode = oldS[id].clone();
            defNamNode.changeScope(newSheetId);
            defNamNode.changeRefToNewSheet(oldWS.getName(),newWS.getName());
            obj[defNamNode.nodeId] = defNamNode;
            this.defNameList[defNamNode.nodeId] = defNamNode;
        }

        this.defNameSheets[newSheetId] = obj;

    },
    relinkDefNameByWorksheet:function (oName, nName){

        var oldS = this.defNameList, dN;
        for( var id in oldS ){
            dN = oldS[id];
            if(dN.isTable ){
                if(dN.Ref)
                    dN.Ref = dN.Ref.replace(oName,nName);
            }
            else{
				if( dN.Ref ){
                	dN.relinkRef(oName, nName);
				}
            }
        }

    },
    saveDefName:function () {
        var list = [], defN;
        for ( var id in this.defNameList ) {
            defN = this.defNameList[id];
            if ( defN.isTable ) {
                continue;
            }
            if ( defN.Ref != null ) {
                list.push( defN.getAscCDefName() );
            }
        }
        return list;
    },
    unlockDefName:function(){
        var nodesList = this.defNameList;

        for ( var id in nodesList ) {
            if ( nodesList[id].isLock ) {
                delete nodesList[id].isLock;
            }
        }
    },
    checkDefNameLock:function(){
        var nodesList = this.defNameList, countNodes = 0;

        for ( var id in nodesList ) {
            countNodes++;
            if ( nodesList[id].isLock ) {
                return true;
            }
        }
        return !countNodes;
//        return false;
    },

    getNextTableName:function ( ws, Ref, tableName ) {
        this.nTableNameMaxIndex++;
		
		if(tableName){
			 sNewName = tableName;
		}
		else{
			var collaborativeIndexUser = "";
			if(ws.workbook.oApi.collaborativeEditing.getCollaborativeEditing())
			{
				collaborativeIndexUser = "_" + ws.workbook.oApi.CoAuthoringApi.get_indexUser();
			}
			var sNewName = this.sTableNamePattern + this.nTableNameMaxIndex + collaborativeIndexUser,
				name = getDefNameVertexId( null, sNewName );
			while ( this.defNameList[name] ) {
				this.nTableNameMaxIndex++;
				sNewName = this.sTableNamePattern + this.nTableNameMaxIndex + collaborativeIndexUser;
				name = getDefNameVertexId( null, sNewName );
			}
		}
		
        this.addTableName( sNewName, ws, Ref );
        return sNewName;
    },
    addTableName:function ( sName, ws, Ref, table ) {
        var refClone, defNameSheetsList, dfv, nSE, se;
		if(this.defNameSheets["WB"]){
			dfv = this.defNameSheets["WB"][getDefNameVertexId( null, sName )]
		}
		
		if(table)
		{
			refClone = table.getRangeWithoutHeaderFooter().clone();
		}
		else
		{
			refClone = Ref.clone( true );
			refClone.r1++;
		}
		
		if(!dfv){
			dfv = new DefNameVertex( null, sName, parserHelp.get3DRef( ws.getName(), refClone.getAbsName() ), null, this.wb, true );
			defNameSheetsList = this.defNameSheets[dfv.sheetId];
			this.defNameList[dfv.nodeId] = dfv;
			if ( defNameSheetsList == null ) {
				defNameSheetsList = {};
				this.defNameSheets[dfv.sheetId] = defNameSheetsList;
			}
			defNameSheetsList[dfv.nodeId] = dfv;
		}
		else{
			dfv.Ref = parserHelp.get3DRef( ws.getName(), refClone.getAbsName() );
			nSE = dfv.getSlaveEdges();
			for ( var id in nSE ) {
				se = nSE[id];
				addToArrRecalc(se.sheetId, se.cell);
				this.wb.needRecalc.nodes[se.nodeId] = [se.sheetId, se.cellId ];
				this.wb.needRecalc.length++;
			}
		}

		addToArrDefNameRecalc(dfv);
    },
    changeTableName:function(tableName, ws, newRef){
        var table = this.getDefNameNodeByName( tableName, ws ),
            newRefClone = newRef.clone(true);
        newRefClone.r1++;
        table.Ref = table.Ref.split("!")[0]+"!"+newRefClone.getAbsName();
		table.rebuild();
//        table.Ref = parserHelp.getEscapeSheetName(ws.getName())+"!"+newRef.getAbsName();
    },
	changeTableRef:function(tableName, ws, newName){
        var table = this.getDefNameNodeByName( tableName, ws );
        
		var newTable = table.clone();
		
		if(table)
		{
			//изменяем имя именнованного диапазона
			newTable.Name = newName;
			newTable.cellId = newName.toLowerCase();
			newTable.nodeId = table.sheetId + table.cellId;
			
			this.changeDefName(table, newTable);
			
			//изменяем все ссылки на данную таблицу
			var nameRef = tableName + "[]";
			for(var i in this.defNameList)
			{
				if(this.defNameList[i] && this.defNameList[i].Ref && this.defNameList[i].Ref === nameRef)
				{
					this.defNameList[i].Ref = newName + "[]";
				}
			}
		}
		
		
		table.rebuild();
    },
    delTableName:function(name,ws){
        var table = this.getDefNameNodeByName( name, ws );
        table.Ref = null;

		var nSE, se;
		nSE = table.getSlaveEdges();

		table.deleteAllMasterEdges();

		for ( var id in nSE ) {
			se = nSE[id];
			addToArrRecalc(se.sheetId, se.cell);
			this.wb.needRecalc.nodes[se.nodeId] = [se.sheetId, se.cellId ];
			this.wb.needRecalc.length++;
		}
		addToArrDefNameRecalc(table);
    },
	rebuildTable:function(tableName){
		var table = this.getDefNameNodeByName( tableName, null ), nSE, se, nME, range;

		nME = table.getMasterEdges();
		for ( var id in nME ) {
			range = this.getInRange( nME[id].sheetId, nME[id].bbox );
			for ( var i = 0; i < range.length; i++ ) {
				nSE = range[i].getSlaveEdges();
				for ( var id in nSE ) {
					se = nSE[id];
					se = se.returnCell();
					if ( se ) {
						se.setFormula( se.formulaParsed.assemble() );
					}
				}
			}
		}
	}
};

/** @constructor */
function Vertex(sheetId, cellId, wb){
	
	this.sheetId = sheetId;
	this.cellId = cellId;
	this.bbox = AscCommonExcel.g_oRangeCache.getAscRange(this.cellId);
	this.isArea = !this.bbox.isOneCell();
	this.valid = true;
	this.nodeId = getVertexId(this.sheetId, this.cellId);
    this.wb = wb;
	this.cell = null;
	
	//вершина которую мы прошли и поставили в очередь обхода
	this.isBlack = false;
	
	//вершина которую мы прошли, но не поставили в очередь обхода. нужно для определения петель в графе.
	this.isGray = false;
	
	//если вершина входит в цикличный путь, то она помечается плохой и запоминается в списке плохих вершин.
	this.isBad = false;
	
	//masterEdges содержит ячейки, от которых зависит текущая ячейка
	this.masterEdges = {};
	
	//slaveEdges содержит ячейки, которые зависят от данной ячейки
	this.slaveEdges = {};
	
	this.refCount = 0;
	
	this.isDefinedName = false;
}
Vertex.prototype = {	
	
	constructor: Vertex,
	
	getBBox : function()
	{
		return this.bbox;
	},
	setFormula : function(sFormula, bAddToHistory, bAddNeedRecalc)
	{
	    this.wb.dependencyFormulas.deleteMasterNodes2(this.sheetId, this.cellId);
	    var cell = this.returnCell();
	    if (null != sFormula)
	        cell.setFormula(sFormula, bAddToHistory);
		addToArrRecalc(this.sheetId, cell);
		if(bAddNeedRecalc)
		{
			this.wb.needRecalc.nodes[this.nodeId] = [this.sheetId, this.cellId ];
			this.wb.needRecalc.length++;
		}
	},
	setRefError : function(wsId, cellId)
	{
	    var sRes = null;
		var cell = this.returnCell();
		if( cell && cell.formulaParsed )
		{
		    cell.formulaParsed.setRefError(wsId, cellId);
		    sRes = cell.formulaParsed.assemble(true);
		}
		return sRes;
	},
	moveInner: function (bboxTo) {
	    //удаляем старые ссылки slave и master
        var slave, master;
        for (var i in this.slaveEdges) {
	        slave = this.slaveEdges[i];
	        slave.deleteMasterEdge(this);
	    }
	    for (var i in this.masterEdges) {
	        master = this.masterEdges[i];
	        master.deleteSlaveEdge(this);
	    }
	    this.bbox = bboxTo;
	    this.cellId = bboxTo.getName();
	    this.nodeId = getVertexId(this.sheetId, this.cellId);
	    this.wb.needRecalc.nodes[this.nodeId] = [this.sheetId, this.cellId];
	    this.wb.needRecalc.length++;
	    //добавляем новые slave и master
	    for (var i in this.slaveEdges) {
	        slave = this.slaveEdges[i];
	        slave.addMasterEdge(this);
	    }
	    for (var i in this.masterEdges) {
	        master = this.masterEdges[i];
	        master.addSlaveEdge(this);
	    }
	},
	moveOuter: function (from, to, oFormulas) {
	    if ((from.r1 == to.r1 && from.c1 == to.c1) || (from.r2 == to.r2 && from.c2 == to.c2)) {
	        /*вставляем/удаляем по вертикали/горизонтали внутри диапазона*/
	        var _sn = this.getSlaveEdges(), slave, cell;
	        for (var _id in _sn) {
	            slave = _sn[_id];
                if( slave instanceof DefNameVertex ){
                    if ( false == this.wb.bUndoChanges && (false == this.wb.bRedoChanges || true == this.wb.bCollaborativeChanges )){
                        History.LocalChange = true;
                        var oN = slave.getAscCDefName(), nN;
                        slave.parsedRef.stretchArea(this, from, to);
                        nN = slave.getAscCDefName();
                        nN.Ref = slave.parsedRef.assemble();
                        this.wb.editDefinesNames( oN, nN );
                        History.LocalChange = false;
                    }
                    continue;
                }
	            cell = slave.returnCell();
	            if (cell && cell.formulaParsed) {
	                cell.formulaParsed.stretchArea(this, from, to);
	                var formula = cell.formulaParsed.assemble();
	                if (null != formula) {
	                    if (oFormulas)
	                        oFormulas[slave.nodeId] = { node: slave, formula: formula };
	                    else
	                        slave.setFormula(formula, true, false);
	                }
	            }
	        }
	    }
	    else {
	        /*вставляем.удаляем левее/выше вне диапазона*/
	        if (oFormulas) {
	            if (null == oFormulas[this.nodeId])
	                oFormulas[this.nodeId] = { node: this, formula: null };
	        }
	        else {
	            var cell = this.returnCell();
	            if (cell && cell.formulaParsed) {
	                this.wb.dependencyFormulas.deleteMasterNodes2(this.sheetId, this.cellId);
	                addToArrRecalc(this.sheetId, cell);
	            }
	        }
	        var _sn = this.getSlaveEdges(), slave, cell;
	        for (var _id in _sn) {
	            slave = _sn[_id]
                if( slave instanceof DefNameVertex ){
                    /*slave.parsedRef.shiftCells(this, from, to);
                    slave.relinkRef();*/

                    if ( false == this.wb.bUndoChanges && (false == this.wb.bRedoChanges || true == this.wb.bCollaborativeChanges )){
                        History.LocalChange = true;
                        var oN = slave.getAscCDefName(), nN;
                        slave.parsedRef.shiftCells(this, from, to);
                        nN = slave.getAscCDefName();
                        nN.Ref = slave.parsedRef.assemble();
                        this.wb.editDefinesNames( oN, nN );
                        History.LocalChange = false;
                    }

                    continue;
                }
                cell = slave.returnCell();
	            if (cell && cell.formulaParsed) {
	                cell.formulaParsed.shiftCells(this, from, to);
	                var formula = cell.formulaParsed.assemble();
	                if (null != formula) {
	                    if (oFormulas)
	                        oFormulas[slave.nodeId] = { node: slave, formula: formula };
	                    else
	                        slave.setFormula(formula, true, false);
	                }
	            }
	        }
	    }
	},
	//добавляем ведущую ячейку.
	addMasterEdge : function(node){
		if( !this.masterEdges ){
			this.masterEdges = {};
        }
        if( !this.masterEdges[node.nodeId] ){
		    this.masterEdges[node.nodeId] = node;
    		this.refCount ++;
        }
	},

	//добавляем зависимую(ведомую) ячейку.
	addSlaveEdge : function(node){
		if( !this.slaveEdges ){
			this.slaveEdges = {};
        }
        if( !this.slaveEdges[node.nodeId] ){
            this.slaveEdges[node.nodeId] = node;
            this.refCount ++;
        }
	},
	
	getMasterEdges : function(){
		return this.masterEdges;
	},

	getSlaveEdges : function(){
		return this.slaveEdges;
	},

	getSlaveEdges2 : function(){
		var ret = {}, count = 0;
		for(var id in this.slaveEdges){
			ret[id] = this.slaveEdges[id];
			count++;
		}
		if ( count > 0 ) 
			return ret; 
		else
			return null;
	},
	
	//удаляем ребро между конкретной ведущей ячейки.
	deleteMasterEdge: function (node) {
	    if (this.masterEdges) {
            delete this.masterEdges[node.nodeId];
            this.refCount--;
	    }
	},

	//удаляем ребро между конкретной зависимой(ведомой) ячейки.
	deleteSlaveEdge: function (node) {
	    if (this.slaveEdges) {
            delete this.slaveEdges[node.nodeId];
            this.refCount--;
	    }
	},

	//очищаем все ребра по ведущим ячейкам.
	deleteAllMasterEdges : function(){
		var ret = {};
		for( var id in this.masterEdges ){
			var masterEdge = this.masterEdges[id];
			masterEdge.deleteSlaveEdge(this);
			delete this.masterEdges[id];
			this.refCount--;
			ret[id] = masterEdge;
		}
		this.masterEdges = {};
		return ret;
	},
	
	//очищаем все ребра по ведомым ячейкам.
	deleteAllSlaveEdges : function(){
		var ret = {};
		for( var id in this.slaveEdges ){
			var slaveEdge = this.slaveEdges[id];
			slaveEdge.deleteMasterEdge(this);
			delete this.slaveEdges[id];
			this.refCount--;
			ret[id] = slaveEdge;
		}
		this.slaveEdges = {};
		return ret;
	},

	returnCell : function(){
		//todo
		if(null == this.cell && this.wb && !this.isArea)
		{
			var ws = this.wb.getWorksheetById(this.sheetId);
			if(ws)
				this.cell = ws._getCell(this.bbox.r1, this.bbox.c1);
//				this.cell = ws._getCellNoEmpty(this.bbox.r1, this.bbox.c1);
		}
		return this.cell;
	}
	
};

function DefNameVertex( scope, defName, defRef, defHidden, wb, isTable ) {

    this.sheetId = scope === null || scope === undefined ? "WB" : scope;
    this.cellId = defName.toLowerCase();
    this.Ref = defRef;
    this.Name = defName;
    this.Hidden = defHidden;
    this.isTable = isTable;
    this.nodeId = getDefNameVertexId( this.sheetId, defName );
    this.wb = wb;

    //вершина которую мы прошли и поставили в очередь обхода
    this.isBlack = false;

    //вершина которую мы прошли, но не поставили в очередь обхода. нужно для определения петель в графе.
    this.isGray = false;

    //если вершина входит в цикличный путь, то она помечается плохой и запоминается в списке плохих вершин.
    this.isBad = false;

    //masterEdges содержит ячейки, от которых зависит текущая ячейка
    this.masterEdges = {};

    //slaveEdges содержит ячейки, которые зависят от данной ячейки
    this.slaveEdges = {};

    this.refCount = 0;

    this.isDefinedName = true;
}
DefNameVertex.prototype = {

    constructor:Vertex,

    clone:function(){
        var dN = new DefNameVertex( this.sheetId, this.cellId, this.Ref, this.Hidden, this.wb, this.isTable );
        dN.parsedRef = new parserFormula( dN.Ref, "", this.wb.getWorksheet(0) );
        if( dN.Ref ){
            dN.parsedRef.parse();
//            dN.parsedRef.buildDependencies();
        }
        return dN;
    },

    changeScope:function( newScope ){
        this.sheetId = newScope === null || newScope === undefined ? "WB" : newScope;
        this.nodeId = getDefNameVertexId( this.sheetId, this.Name );
    },

    changeRefToNewSheet:function( lastName, newName ){
        if( !this.isTable && this.parsedRef.isParsed ){
            this.parsedRef = this.parsedRef.changeSheet( lastName, newName );
            this.Ref = this.parsedRef.assemble();
        }
    },

    moveInner:function ( bboxTo ) {
        //удаляем старые ссылки slave и master
        for ( var i in this.slaveEdges ) {
            var slave = this.slaveEdges[i];
            slave.deleteMasterEdge( this );
        }
        for ( var i in this.masterEdges ) {
            var master = this.masterEdges[i];
            master.deleteSlaveEdge( this );
        }
        var sOldNodeId = this.nodeId;
        this.bbox = bboxTo;
        this.cellId = bboxTo.getName();
        this.nodeId = getVertexId( this.sheetId, this.cellId );
        this.wb.needRecalc.nodes[this.nodeId] = [this.sheetId, this.cellId ];
        this.wb.needRecalc.length++;
        //добавляем новые slave и master
        for ( var i in this.slaveEdges ) {
            var slave = this.slaveEdges[i];
            slave.addMasterEdge( this );
        }
        for ( var i in this.masterEdges ) {
            var master = this.masterEdges[i];
            master.addSlaveEdge( this );
        }
    },
    moveOuter:function ( from, to, oFormulas ) {
        if ( (from.r1 == to.r1 && from.c1 == to.c1) || (from.r2 == to.r2 && from.c2 == to.c2) ) {
            var _sn = this.getSlaveEdges();
            for ( var _id in _sn ) {
                var slave = _sn[_id];
                var cell = slave.returnCell();
                if ( cell && cell.formulaParsed ) {
                    cell.formulaParsed.stretchArea( this, from, to );
                    var formula = cell.formulaParsed.assemble();
                    if ( null != formula ) {
                        if ( oFormulas )
                            oFormulas[slave.nodeId] = { node:slave, formula:formula };
                        else
                            slave.setFormula( formula, true, false );
                    }
                }
            }
        }
        else {
            if ( oFormulas ) {
                if ( null == oFormulas[this.nodeId] )
                    oFormulas[this.nodeId] = { node:this, formula:null };
            }
            else {
                var cell = this.returnCell();
                if ( cell && cell.formulaParsed ) {
                    this.wb.dependencyFormulas.deleteMasterNodes2( this.sheetId, this.cellId );
                    addToArrRecalc( this.sheetId, cell );
                }
            }
            var _sn = this.getSlaveEdges();
            for ( var _id in _sn ) {
                var slave = _sn[_id]
                var cell = slave.returnCell();
                if ( cell && cell.formulaParsed ) {
                    cell.formulaParsed.shiftCells( this, from, to );
                    var formula = cell.formulaParsed.assemble();
                    if ( null != formula ) {
                        if ( oFormulas )
                            oFormulas[slave.nodeId] = { node:slave, formula:formula };
                        else
                            slave.setFormula( formula, true, false );
                    }
                }
            }
        }
    },
    //добавляем ведущую ячейку.
    addMasterEdge:function ( node ) {
        if ( !this.masterEdges ) {
            this.masterEdges = {};
        }
        if ( !this.masterEdges[node.nodeId] ) {
            this.masterEdges[node.nodeId] = node;
            this.refCount++;
        }
    },

    //добавляем зависимую(ведомую) ячейку.
    addSlaveEdge:function ( node ) {
        if ( !this.slaveEdges ) {
            this.slaveEdges = {};
        }
        if ( !this.slaveEdges[node.nodeId] ) {
            this.slaveEdges[node.nodeId] = node;
            this.refCount++;
        }
    },

    getMasterEdges:function () {
        return this.masterEdges;
    },

    getSlaveEdges:function () {
        return this.slaveEdges;
    },

    getSlaveEdges2:function () {
        var ret = {}, count = 0;
        for ( var id in this.slaveEdges ) {
            ret[id] = this.slaveEdges[id];
            count++;
        }
        if ( count > 0 )
            return ret;
        else
            return null;
    },

    //удаляем ребро между конкретной ведущей ячейки.
    deleteMasterEdge:function ( node ) {
        if ( this.masterEdges ) {
            delete this.masterEdges[node.nodeId];
            this.refCount--;
        }
    },

    //удаляем ребро между конкретной зависимой(ведомой) ячейки.
    deleteSlaveEdge:function ( node ) {
        if ( this.slaveEdges ) {
            delete this.slaveEdges[node.nodeId];
            this.refCount--;
        }
    },

    //очищаем все ребра по ведущим ячейкам.
    deleteAllMasterEdges:function () {
        var ret = {};
        for ( var id in this.masterEdges ) {
            var masterEdge = this.masterEdges[id];
            masterEdge.deleteSlaveEdge( this );
            this.masterEdges[id] = null;
            delete this.masterEdges[id];
            this.refCount--;
            ret[id] = masterEdge;
        }
        this.masterEdges = {};
        return ret;
    },

    //очищаем все ребра по ведомым ячейкам.
    deleteAllSlaveEdges:function () {
        var ret = {};
        for ( var id in this.slaveEdges ) {
            var slaveEdge = this.slaveEdges[id];
            slaveEdge.deleteMasterEdge( this );
            this.slaveEdges[id] = null;
            delete this.slaveEdges[id];
            this.refCount--;
            ret[id] = slaveEdge;
        }
        this.slaveEdges = {};
        return ret;
    },

    returnCell:function () {
        //todo
        return false;
//        if ( null == this.cell && this.wb && !this.isArea && this.Ref !== null && this.Ref !== undefined ) {
//            var ws = this.wb.getWorksheetById( this.sheetId );
//            if ( ws )
//                this.cell = ws._getCellNoEmpty( this.bbox.r1, this.bbox.c1 );
//        }
//        return this.cell;
    },

    getAscCDefName:function () {
        var a = this.wb.getWorksheetById( this.sheetId );
        return new Asc.asc_CDefName( this.Name,
            this.Ref,
            this.sheetId == "WB" ? null : a ? a.getIndex() : null,
            this.isTable, this.Hidden, this.isLock );
    },

    changeDefName:function ( newName ) {
        this.cellId = newName.Name.toLowerCase();
        this.Ref = newName.Ref;
        this.Name = newName.Name;
        this.nodeId = getDefNameVertexId( this.sheetId, newName.Name );
    },

    relinkRef:function(oName, nName){
        if( this.parsedRef && this.parsedRef.isParsed ){
            this.Ref = this.parsedRef.assemble();
        }
    },

    renameDefNameToCollaborate:function(name){
        var lastname = this.Name;
        //из-за особенностей реализации формул, сначала делаем parse со старым именем, потом преименовываем, потом assemble
        var aFormulas = [];
        //переименование для отправки изменений
        for(var i = 0, length = this.wb.aCollaborativeActions.length; i < length; ++i)
        {
            var aPointActions = this.wb.aCollaborativeActions[i];
            for (var j = 0, length2 = aPointActions.length; j < length2; ++j) {
                var action = aPointActions[j];
                if (AscCommonExcel.g_oUndoRedoWorkbook == action.oClass) {
                    if (AscCH.historyitem_Workbook_DefinedNamesAdd == action.nActionType) {
                        if (lastname == action.oData.newName.Name)
                            action.oData.newName.Name = name;
                    }
                }
                else if (AscCommonExcel.g_oUndoRedoCell == action.oClass) {
                    if (action.oData instanceof UndoRedoData_CellSimpleData) {
                        if (action.oData.oNewVal instanceof UndoRedoData_CellValueData) {
                            var oNewVal = action.oData.oNewVal;
                            if (null != oNewVal.formula && -1 != oNewVal.formula.indexOf(lastname)) {
                                var oParser = new parserFormula(oNewVal.formula, "A1", this.wb.getWorksheet(0));
                                oParser.parse();
                                aFormulas.push({ formula: oParser, value: oNewVal });
                            }
                        }
                    }
                }
            }
        }
        var clone = this.clone();
        clone.Name = name;
        this.wb.editDefinesNames( this.getAscCDefName(), clone.getAscCDefName() );
        for(var i = 0, length = aFormulas.length; i < length; ++i)
        {
            var item = aFormulas[i];
            item.value.formula = item.formula.assemble();
        }
    },

    rebuild:function(){
        if(this.Ref){
			this.deleteAllMasterEdges();
            this.parsedRef = new parserFormula(this.Ref, "", this.wb.getWorksheet(0));
            this.parsedRef.parse();
            this.parsedRef.buildDependencies(null,this);
        }
    }

};

function getVertexId(sheetId, cellId){
	return sheetId + AscCommon.g_cCharDelimiter + cellId;
}
function getDefNameVertexId(scope, name){
	return ( scope === null || scope === undefined ? "WB" : scope ) + AscCommon.g_cCharDelimiter + name.toLowerCase();
}
function addToArrRecalc(sheetId, cell){
    var temp = arrRecalc[sheetId];
    if( !temp )
    {
        temp = [];
        arrRecalc[sheetId] = temp;
    }
    temp.push(cell);
}
function addToArrDefNameRecalc(name){
    arrDefNameRecalc[name.nodeId] = name;
}
function buildDefNameAfterRenameWorksheet(ws) {
  var dN;
  for(var id in arrDefNameRecalc ){
    dN  = arrDefNameRecalc[id];
    if( !dN.parsedRef && dN.Ref ){
      dN.parsedRef = new parserFormula(dN.Ref, "", ws.workbook.getWorksheet(0));
      dN.parsedRef.parse();
    }
  }
}

function angleFormatToInterface(val)
{
	var nRes = 0;
	if(0 <= val && val <= 180)
		nRes = val <= 90 ? val : 90 - val;
	return nRes;
}
function angleFormatToInterface2(val)
{
	if(g_nVerticalTextAngle == val)
		return val;
	else
		return angleFormatToInterface(val);
}
function angleInterfaceToFormat(val)
{
	var nRes = val;
	if(-90 <= val && val <= 90)
	{
		if(val < 0)
			nRes = 90 - val;
	}
	else if(g_nVerticalTextAngle != val)
		nRes = 0;
	return nRes;
}
function getUniqueKeys(array) {
  var i, o = {};
  for (i = 0; i < array.length; ++i) {
    o[array[i].v] = o.hasOwnProperty(array[i].v);
  }
  return o;
}
//-------------------------------------------------------------------------------------------------
/**
 * @constructor
 */
function Workbook(eventsHandlers, oApi){
	this.oApi = oApi;
	this.handlers = eventsHandlers;
	this.needRecalc = {nodes: {}, length:0};
	this.dependencyFormulas = new DependencyGraph(this);
	this.nActive = 0;
	
	this.theme = null;
	this.clrSchemeMap = null;
	
	this.DefinedNames = {};
	this.oRealDefinedNames = {};
//	this.oNameGenerator = new NameGenerator(this);
	this.CellStyles = new AscCommonExcel.CCellStyles();
	this.TableStyles = new Asc.CTableStyles();
	this.oStyleManager = new AscCommonExcel.StyleManager();
	this.calcChain = [];
	this.aComments = [];	// Комментарии к документу
	this.aCommentsCoords = [];
	this.aWorksheets = [];
	this.aWorksheetsById = {};
	this.cwf = {};
	this.aCollaborativeActions = [];
	this.bCollaborativeChanges = false;
	this.bUndoChanges = false;
	this.bRedoChanges = false;
	this.aCollaborativeChangeElements = [];
	
	this.wsHandlers = null;

  this.openErrors = [];

  this.lockCounter = 0;

	this.maxDigitWidth = 0;
	this.paddingPlusBorder = 0;
}
Workbook.prototype.init=function(bNoBuildDep){
	if(this.nActive < 0)
		this.nActive = 0;
	if(this.nActive >= this.aWorksheets.length)
		this.nActive = this.aWorksheets.length - 1;
	
	var self = this;

	this.wsHandlers = new AscCommonExcel.asc_CHandlersList( /*handlers*/{
		"changeRefTablePart"   : function ( displayName, ref ) {
			self.dependencyFormulas.changeTableName( displayName, null, ref );
		},
		"changeColumnTablePart": function ( tableName ) {
			self.dependencyFormulas.rebuildTable( tableName );
		},
		"delTable"             : function ( name, ws ) {
			self.dependencyFormulas.delTableName( name, ws );
		}
	} );
	
    //charts
    for(var i = 0, length = this.aWorksheets.length; i < length; ++i)
    {
        var ws = this.aWorksheets[i];
        ws.initPostOpen(this.wsHandlers);
    }
	//show active if it hidden
	var wsActive = this.getActiveWs();
	if (wsActive && wsActive.getHidden()) {
		wsActive.setHidden(false);
	}

	if(!bNoBuildDep){
		/*
			buildDependency необходимо запускать для построения графа зависимостей между ячейками.
			Сортировка графа производится при необходимости пересчета формул: 
				при открытии документа если есть ячейки помеченные как пересчитываемые или есть ячейки без значения.
		*/
		this.buildDependency();
		this.sortDependency();
	}

};
Workbook.prototype.rebuildColors=function(){
  AscCommonExcel.g_oColorManager.rebuildColors();
	for(var i = 0 , length = this.aWorksheets.length; i < length; ++i)
		this.aWorksheets[i].rebuildColors();
};
Workbook.prototype.getDefaultFont=function(){
	return g_oDefaultFormat.Font.fn;
};
Workbook.prototype.getDefaultSize=function(){
	return g_oDefaultFormat.Font.fs;
};
Workbook.prototype.getActive=function(){
	return this.nActive;
};
Workbook.prototype.getActiveWs = function () {
    return this.getWorksheet(this.nActive);
};
Workbook.prototype.setActive=function(index){
	if(index >= 0 && index < this.aWorksheets.length){
		this.nActive = index;
		return true;
	}
	return false;
};
Workbook.prototype.getWorksheet=function(index){
	//index 0-based
	if(index >= 0 && index < this.aWorksheets.length){
		return this.aWorksheets[index];
	}
	return null;
};
Workbook.prototype.getWorksheetById=function(id){
	return this.aWorksheetsById[id];
};
Workbook.prototype.getWorksheetByName=function(name){
	for(var i = 0; i < this.aWorksheets.length; i++)
		if(this.aWorksheets[i].getName() == name){
			return this.aWorksheets[i];
		}
	return null;
};
Workbook.prototype.getWorksheetIndexByName=function(name){
	for(var i = 0; i < this.aWorksheets.length; i++)
		if(this.aWorksheets[i].getName() == name){
			return i;
		}
	return null;
};
Workbook.prototype.getWorksheetCount=function(){
	return this.aWorksheets.length;
};
Workbook.prototype.createWorksheet=function(indexBefore, sName, sId){
	History.Create_NewPoint();
	History.TurnOff();
	var wsActive = this.getActiveWs();
    var oNewWorksheet = new Woorksheet(this, this.aWorksheets.length, sId);
	if (this.checkValidSheetName(sName))
		oNewWorksheet.sName = sName;
	oNewWorksheet.initPostOpen(this.wsHandlers);
	if(null != indexBefore && indexBefore >= 0 && indexBefore < this.aWorksheets.length)
		this.aWorksheets.splice(indexBefore, 0, oNewWorksheet);
	else
	{
		indexBefore = this.aWorksheets.length;
		this.aWorksheets.push(oNewWorksheet);
	}
	this.aWorksheetsById[oNewWorksheet.getId()] = oNewWorksheet;
	this._updateWorksheetIndexes(wsActive);
	History.TurnOn();
	this._insertWorksheetFormula(oNewWorksheet.index);
	History.Add(AscCommonExcel.g_oUndoRedoWorkbook, AscCH.historyitem_Workbook_SheetAdd, null, null, new UndoRedoData_SheetAdd(indexBefore, oNewWorksheet.getName(), null, oNewWorksheet.getId()));
	History.SetSheetUndo(wsActive.getId());
	History.SetSheetRedo(oNewWorksheet.getId());
	return oNewWorksheet.index;
};
Workbook.prototype.copyWorksheet=function(index, insertBefore, sName, sId, bFromRedo, tableNames){
	//insertBefore - optional
	if(index >= 0 && index < this.aWorksheets.length){
		//buildRecalc вызываем чтобы пересчиталося cwf(может быть пустым если сделать сдвиг формул и скопировать лист)
		this.buildRecalc(true, true);
		History.TurnOff();
		var wsActive = this.getActiveWs();
		var wsFrom = this.aWorksheets[index];
		var newSheet = wsFrom.clone(sId, sName, tableNames);
		newSheet.initPostOpen(this.wsHandlers);
		if(null != insertBefore && insertBefore >= 0 && insertBefore < this.aWorksheets.length){
			//помещаем новый sheet перед insertBefore
			this.aWorksheets.splice(insertBefore, 0, newSheet);
		}
		else{
			//помещаем новый sheet в конец
			this.aWorksheets.push(newSheet);
		}
		this.aWorksheetsById[newSheet.getId()] = newSheet;
		this._updateWorksheetIndexes(wsActive);
		History.TurnOn();
		this._insertWorksheetFormula(insertBefore);

        this.dependencyFormulas.copyDefNameByWorksheet( wsFrom.getId(), newSheet.getId() );

		//для формул. создаем копию this.cwf[this.Id] для нового листа.
		if ( this.cwf[wsFrom.getId()] ){
			var cwf = {};
			var newSheetId = newSheet.getId();
			var cwfFrom = this.cwf[wsFrom.getId()];
			this.cwf[newSheetId] = cwf;
			for( var id in cwfFrom ){
				cwf[id] = cwfFrom[id];
				this.needRecalc.nodes[getVertexId(newSheetId, id)] = [newSheetId, id];
				this.needRecalc.length++;
			}
			newSheet._BuildDependencies(cwf);
		}
		
		if(!tableNames && newSheet.TableParts && newSheet.TableParts.length)
		{
			tableNames = [];
			for(var i = 0; i < newSheet.TableParts.length; i++)
			{
				tableNames.push(newSheet.TableParts[i].DisplayName);
			}
		}
		
		History.Add(AscCommonExcel.g_oUndoRedoWorkbook, AscCH.historyitem_Workbook_SheetAdd, null, null, new UndoRedoData_SheetAdd(insertBefore, newSheet.getName(), wsFrom.getId(), newSheet.getId(), tableNames));
		History.SetSheetUndo(wsActive.getId());
		History.SetSheetRedo(newSheet.getId());
        if(!(bFromRedo === true))
        {
            wsFrom.copyDrawingObjects(newSheet, wsFrom);
        }
        this.sortDependency();
	}
};
Workbook.prototype.insertWorksheet = function (index, sheet, cwf) {
    var wsActive = this.getActiveWs();
	if(null != index && index >= 0 && index < this.aWorksheets.length){
		//помещаем новый sheet перед insertBefore
		this.aWorksheets.splice(index, 0, sheet);
	}
	else{
		//помещаем новый sheet в конец
		this.aWorksheets.push(sheet);
	}
	this.aWorksheetsById[sheet.getId()] = sheet;
	this._updateWorksheetIndexes(wsActive);
	this._insertWorksheetFormula(index);
	this._insertTablePartsName(sheet);
	//восстанавливаем список ячеек с формулами для sheet
	this.cwf[sheet.getId()] = cwf;
	sheet._BuildDependencies(cwf);
  this.sortDependency();
};
Workbook.prototype._insertTablePartsName = function (sheet) {
	if(sheet && sheet.TableParts && sheet.TableParts.length)
	{
		for(var i = 0; i < sheet.TableParts.length; i++)
		{	
			var oNewTable = sheet.TableParts[i];
			this.dependencyFormulas.addTableName(oNewTable.DisplayName, sheet, oNewTable.Ref);
		}
	}
};
Workbook.prototype._insertWorksheetFormula=function(index){
	if( index > 0 && index < this.aWorksheets.length - 1 ){
		var oWsTo = this.aWorksheets[index - 1];
		var nodesSheetTo = this.dependencyFormulas.getNodeBySheetId(oWsTo.getId());
		for( var i = 0; i < nodesSheetTo.length; i++ ){
			var se = nodesSheetTo[i].getSlaveEdges();
			if(se)
			{
				for( var id in se ){
					var slave = se[id];
                    if(slave.isDefinedName){
                        continue;
                    }
					var cell = slave.returnCell();
					if( cell && cell.formulaParsed && cell.formulaParsed.is3D )
					{
						if(cell.formulaParsed.insertSheet(index))
							slave.setFormula(null, false, true);
					}
				}
			}
		}
	}
};
Workbook.prototype.replaceWorksheet=function(indexFrom, indexTo){
	if(indexFrom >= 0 && indexFrom < this.aWorksheets.length &&
		indexTo >= 0 && indexTo < this.aWorksheets.length){
		History.Create_NewPoint();
		History.TurnOff();
		var wsActive = this.getActiveWs();
		var oWsFrom = this.aWorksheets[indexFrom];
		var oWsTo = this.aWorksheets[indexTo];
		var tempW = {
					wFN: oWsFrom.getName(),
					wFI: indexFrom,
					wFId: oWsFrom.getId(),
					wTN: oWsTo.getName(),
					wTI: indexTo,
					wTId: oWsTo.getId()
				};
		//переводим обратно в индекс sheet перед которым надо вставить
		if(tempW.wFI < tempW.wTI)
			tempW.wTI++;
		/*
			Формулы:
				перестройка графа для трехмерных формул вида Sheet1:Sheet3!A1:A3, Sheet1:Sheet3!A1.
				пересчет трехмерных формул, перестройка формул при изменении положения листа: Sheet1, Sheet2, Sheet3, Sheet4 - Sheet1:Sheet4!A1 -> Sheet4, Sheet1, Sheet2, Sheet3 - Sheet1:Sheet3!A1;
		*/
		this.lockDraw();
		var a = this.dependencyFormulas.getNodeBySheetId(tempW.wFId);
		for(var i=0;i<a.length;i++){
			var se = a[i].getSlaveEdges();
			if(se){
				for(var id in se){
					var slave = se[id];
                    if( slave.isDefinedName ) continue;
					var cell = slave.returnCell();
					if( cell && cell.formulaParsed && cell.formulaParsed.is3D )
					{
						var nMoveRes = cell.formulaParsed.moveSheet(tempW, true);
						if(2 == nMoveRes)
							slave.setFormula(cell.formulaParsed.assemble(), true, true);
						else if(1 == nMoveRes)
							slave.setFormula(null, false, true);
					}
				}
			}
		}
		History.TurnOn();
		var movedSheet = this.aWorksheets.splice(indexFrom, 1);
		this.aWorksheets.splice(indexTo, 0, movedSheet[0]);
		this._updateWorksheetIndexes(wsActive);
		
		this._insertWorksheetFormula(tempW.wTI);
		
		History.Add(AscCommonExcel.g_oUndoRedoWorkbook, AscCH.historyitem_Workbook_SheetMove, null, null, new UndoRedoData_FromTo(indexFrom, indexTo), true);
    this.unLockDraw();
    this.buildRecalc();
	}
};
Workbook.prototype.findSheetNoHidden = function (nIndex) {
    var i, ws, oRes = null, bFound = false, countWorksheets = this.getWorksheetCount();
    for (i = nIndex; i < countWorksheets; ++i) {
        ws = this.getWorksheet(i);
        if (false === ws.getHidden()) {
            oRes = ws;
            bFound = true;
            break;
        }
    }
    // Не нашли справа, ищем слева от текущего
    if (!bFound) {
        for (i = nIndex - 1; i >= 0; --i) {
            ws = this.getWorksheet(i);
            if (false === ws.getHidden()) {
                oRes = ws;
                break;
            }
        }
    }
    return oRes;
};
Workbook.prototype.removeWorksheet=function(nIndex, outputParams){
	//проверяем останется ли хоть один нескрытый sheet
	var bEmpty = true;
	for(var i = 0, length = this.aWorksheets.length; i < length; ++i)
	{
		var worksheet = this.aWorksheets[i];
		if(false == worksheet.getHidden() && i != nIndex)
		{
			bEmpty = false;
			break;
		}
	}
	if(bEmpty)
		return -1;

	var removedSheet = this.getWorksheet(nIndex);
	if(removedSheet)
	{
		History.Create_NewPoint();
		var removedSheetId = removedSheet.getId();
    this.lockDraw();
        var retRes = this.dependencyFormulas.removeDefNameBySheet( removedSheetId ), nSE, se, seUndoRedo = [];
		var a = this.dependencyFormulas.getNodeBySheetId(removedSheetId);
		for(var i = 0; i < a.length; i++)
		{
			var node = a[i];
			var se = node.getSlaveEdges();
			if(se){
				for(var id in se){
					var slave = se[id];
					var cell = slave.returnCell();
					if( cell && cell.formulaParsed && cell.formulaParsed.is3D )
					{
						if(cell.formulaParsed.removeSheet(removedSheetId))
							slave.setFormula(cell.formulaParsed.assemble(true), true, true);
					}
				}
			}
		}

	    //по всем удаленным листам пробегаемся и удаляем из workbook.cwf (cwf - cells with forluma) элементы с названием соответствующего листа.
		this.dependencyFormulas.removeNodeBySheetId(removedSheetId);
		var _cwf = this.cwf[removedSheetId];
		delete this.cwf[removedSheetId];

        if ( retRes ) {
            /*
             * #1. поменяли название - перестроили формулу. нужно вызвать пересборку формул в ячейках, в которыйх есть эта именованная ссылка.
             *  для этого пробегаемся по всем slave, и вызываем пересборку. в результате должна получиться новая формула, где используется новый диапазон.
             * #2. поменяли диапазон. нужно перестроить граф зависимосте и пересчитать формулу. находим диапазон; меняем в нем ссылку; разбираем ссылку;
             *  удаляем старые master и добавляем новые, которые получились в результате разбора новой ссылки; пересчитываем формулу.
             * */

            for( var defNameID in retRes ){
                nSE = retRes[defNameID].getSlaveEdges();
                retRes[defNameID].deleteAllMasterEdges();

                for ( var id in nSE ) {
//                    seUndoRedo.push( id );
                    se = nSE[id];
                    se.deleteMasterEdge( retRes[defNameID] );

                    this.needRecalc.nodes[se.nodeId] = [se.sheetId, se.cellId ];
                    this.needRecalc.length++;

                    se = se.returnCell();
                    se ? function () {
                        se.setFormula( se.formulaParsed.assemble() );
                        se.formulaParsed.isParsed = false;
                        se.formulaParsed.parse();
                        se.formulaParsed.buildDependencies();
                    }() : null;

                }
            }
        }

		var wsActive = this.getActiveWs();
		var oVisibleWs = null;
		this.aWorksheets.splice(nIndex, 1);
		delete this.aWorksheetsById[removedSheetId];
		if (nIndex == this.getActive()) {
		    oVisibleWs = this.findSheetNoHidden(nIndex);
		    if (null != oVisibleWs)
		        wsActive = oVisibleWs;
		}

		History.Add(AscCommonExcel.g_oUndoRedoWorkbook, AscCH.historyitem_Workbook_SheetRemove, null, null, new AscCommonExcel.UndoRedoData_SheetRemove(nIndex, removedSheetId, removedSheet, _cwf));
		if (null != oVisibleWs) {
		    History.SetSheetUndo(removedSheetId);
		    History.SetSheetRedo(wsActive.getId());
		}
		if(null != outputParams)
		{
			outputParams.sheet = removedSheet;
			outputParams.cwf = _cwf;
		}
		this._updateWorksheetIndexes(wsActive);
    this.unLockDraw();
    this.buildRecalc();
		return wsActive.getIndex();
	}
	return -1;
};
Workbook.prototype._updateWorksheetIndexes = function (wsActive) {
	for(var i = 0, length = this.aWorksheets.length; i < length; ++i)
		this.aWorksheets[i]._setIndex(i);
	if (null != wsActive)
	    this.setActive(wsActive.getIndex());
};
Workbook.prototype.checkUniqueSheetName=function(name){
	var workbookSheetCount = this.getWorksheetCount();
	for (var i = 0; i < workbookSheetCount; i++){
		if (this.getWorksheet(i).getName() == name)
			return i;
	}
	return -1;
};
Workbook.prototype.checkValidSheetName=function(name){
	return (name && name.length < g_nSheetNameMaxLength);
};
Workbook.prototype.getUniqueSheetNameFrom=function(name, bCopy){
	var nIndex = 1;
	var sNewName = "";
	var fGetPostfix = null;
	if(bCopy)
	{
		
		var result = /^(.*)\((\d)\)$/.exec(name);
		if(result)
		{
			fGetPostfix = function(nIndex){return "(" + nIndex +")";};
			name = result[1];
		}
		else
		{
			fGetPostfix = function(nIndex){return " (" + nIndex +")";};
			name = name;
		}
	}
	else
	{
		fGetPostfix = function(nIndex){return nIndex.toString();};
	}
	var workbookSheetCount = this.getWorksheetCount();
	while(nIndex < 10000)
	{
		var sPosfix = fGetPostfix(nIndex);
		sNewName = name + sPosfix;
		if(sNewName.length > g_nSheetNameMaxLength)
		{
			name = name.substring(0, g_nSheetNameMaxLength - sPosfix.length);
			sNewName = name + sPosfix;
		}
		var bUniqueName = true;
		for (var i = 0; i < workbookSheetCount; i++){
			if (this.getWorksheet(i).getName() == sNewName)
			{
				bUniqueName = false;
				break;
			}
		}
		if(bUniqueName)
			break;
		nIndex++;
	}
	return sNewName;
};
Workbook.prototype._generateFontMap=function(){
	var oFontMap = {
		"Arial"		: 1
	};

	if(null != g_oDefaultFormat.Font.fn)
		oFontMap[g_oDefaultFormat.Font.fn] = 1;
	
	for(var i = 0, length = this.aWorksheets.length; i < length; ++i)
		this.aWorksheets[i].generateFontMap(oFontMap);
	this.CellStyles.generateFontMap(oFontMap);
	
	return oFontMap;
};
Workbook.prototype.generateFontMap=function(){
	var oFontMap = this._generateFontMap();
	
	var aRes = [];
	for(var i in oFontMap)
		aRes.push(i);
	return aRes;
};
Workbook.prototype.generateFontMap2=function(){
	var oFontMap = this._generateFontMap();

	var aRes = [];
	for(var i in oFontMap)
		aRes.push(new AscFonts.CFont(i, 0, "", 0));
	return aRes;
};
Workbook.prototype.getAllImageUrls = function(){
    var aImageUrls = [];
    for(var i = 0; i < this.aWorksheets.length; ++i){
        this.aWorksheets[i].getAllImageUrls(aImageUrls);
    }
    return aImageUrls;
};
Workbook.prototype.reassignImageUrls = function(oImages){
    for(var i = 0; i < this.aWorksheets.length; ++i){
        this.aWorksheets[i].reassignImageUrls(oImages);
    }
};
Workbook.prototype.recalcWB = function(isRecalcWB){
	//todo
    if ( this.dependencyFormulas.getNodesLength() > 0 ) {
        var aNodes = isRecalcWB ? this.dependencyFormulas.getAll() : this.dependencyFormulas.getNodeBySheetIdAll(this.getWorksheet(this.getActive()).getId());
        var nR = this.needRecalc;
        for ( var i in aNodes ) {
            var node = aNodes[i];
            if ( !node.isArea ) {
                nR.nodes[node.nodeId] = [node.sheetId, node.cellId];
                nR.length++;
            }
        }
      this.sortDependency();
    }
};
	Workbook.prototype.checkDefName = function (checkName, scope) {
		var res = new Asc.asc_CCheckDefName();
		var range = AscCommonExcel.g_oRangeCache.getRange3D(checkName) ||
			AscCommonExcel.g_oRangeCache.getAscRange(checkName);
		if (range || !AscCommon.rx_defName.test(checkName.toLowerCase())) {
			res.status = false;
			res.reason = c_oAscDefinedNameReason.WrongName;
			return res;
		}

		if (scope !== null) {
			scope = this.getWorksheet(scope).getId();
		}

		var defName = this.dependencyFormulas.getDefNameNode(getDefNameVertexId(scope, checkName));
		if (defName) {
			defName = defName.getAscCDefName();
			res.status = false;
			if (defName.isLock) {
				res.reason = c_oAscDefinedNameReason.IsLocked;
			} else if (defName.Ref == null) {
				res.reason = c_oAscDefinedNameReason.NameReserved;
			} else {
				res.reason = c_oAscDefinedNameReason.Existed;
			}
		} else {
			res.status = true;
			res.reason = c_oAscDefinedNameReason.OK;
		}

		return res;
	};
Workbook.prototype.isDefinedNamesExists = function ( name, sheetId ) {
    var n = name.toLowerCase();
    return !!this.dependencyFormulas.defNameList[getDefNameVertexId(sheetId, n)];
};
Workbook.prototype.getDefinedNamesWB = function (defNameListId) {
    var names = [], name, thas = this, activeWS;

    /*c_oAscGetDefinedNamesList.
        Worksheet           :   0,
        WorksheetWorkbook   :   1,
        All*/

    function getNames(id,arr){
        var listDN = thas.dependencyFormulas.defNameSheets[id], name;
        for ( var id in listDN ) {
            name = listDN[id];

            if ( name.Ref && !name.Hidden && name.Name.indexOf("_xlnm") < 0 ) {
                if( name.isTable || name.parsedRef && name.parsedRef.isParsed && name.parsedRef.countRef == 1 && name.parsedRef.outStack.length == 1 && name.parsedRef.calculate().errorType !== AscCommonExcel.cErrorType.bad_reference ){
                    arr.push( name.getAscCDefName() );
                }
            }
        }
    }

    function sort(a,b){
        if (a.Name > b.Name) return 1;
        if (a.Name < b.Name) return -1;
    }

    switch(defNameListId){
        case c_oAscGetDefinedNamesList.Worksheet:
        case c_oAscGetDefinedNamesList.WorksheetWorkbook:
            activeWS = this.getActiveWs();

            getNames(activeWS.getId(),names);

            if( c_oAscGetDefinedNamesList.WorksheetWorkbook ){
                getNames("WB",names);
            }
            break;
        case c_oAscGetDefinedNamesList.All:
        default:
            for ( var id in this.dependencyFormulas.defNameList ) {
                name = this.dependencyFormulas.defNameList[id].getAscCDefName()
                if ( name.Ref && !name.Hidden && name.Name.indexOf("_xlnm") < 0 ) {
                    names.push( name );
                }
            }
            break;
    }

    return names.sort(sort);
};
Workbook.prototype.getDefinesNames = function ( name, sheetId ) {
    return this.dependencyFormulas.getDefNameNodeByName( name, sheetId );
};
Workbook.prototype.getDefinedName = function ( name ) {
    var ws = this.getWorksheet( name.LocalSheetId ), sheetId = null;
    ws ? sheetId = ws.getId() : null;
    return this.dependencyFormulas.getDefNameNodeByName( name.Name, sheetId );
};
Workbook.prototype.delDefinesNames = function ( defName, bUndo ) {
    History.Create_NewPoint();
    var retRes = false;

    retRes = this.dependencyFormulas.removeDefName( defName.LocalSheetId, defName.Name );

    if ( retRes ) {
        /*
         * #1. поменяли название - перестроили формулу. нужно вызвать пересборку формул в ячейках, в которыйх есть эта именованная ссылка.
         *  для этого пробегаемся по всем slave, и вызываем пересборку. в результате должна получиться новая формула, где используется новый диапазон.
         * #2. поменяли диапазон. нужно перестроить граф зависимосте и пересчитать формулу. находим диапазон; меняем в нем ссылку; разбираем ссылку;
         *  удаляем старые master и добавляем новые, которые получились в результате разбора новой ссылки; пересчитываем формулу.
         * */

        var nSE, se, seUndoRedo = [];
        nSE = retRes.getSlaveEdges();

        retRes.deleteAllMasterEdges();

        for ( var id in nSE ) {
            seUndoRedo.push( id );
            se = nSE[id];
            addToArrRecalc(se.sheetId, se.cell);
            this.needRecalc.nodes[se.nodeId] = [se.sheetId, se.cellId ];
            this.needRecalc.length++;
        }

        if(!bUndo)
            this.buildRecalc();

        History.Add( AscCommonExcel.g_oUndoRedoWorkbook, AscCH.historyitem_Workbook_DefinedNamesDelete, null, null, new UndoRedoData_DefinedNames( defName.Name, defName.Ref, defName.LocalSheetId, defName.isTable, seUndoRedo ) );
    }

    return retRes;

};
Workbook.prototype.editDefinesNames = function ( oldName, newName, bUndo ) {

    var newN = newName.Name.toLowerCase(), retRes = null, rename = false, nSE, se;

    if ( !AscCommon.rx_defName.test( newN ) || !newName.Ref || newName.Ref.length == 0  ) {
        return retRes;
    }

    if ( oldName ) {
        //при переименовании важно строить зависимости иначе не пересчитаются зависимые формулы
        //пример foo->bar влечет SUM(foo)->SUM(bar), но если нет зависимостей то останется SUM(foo)(повторяется при принятии изменений)
        //при сдвиге ячеек пересчитывать не надо, т.к. пересчет может запуститься когда сдвинется часть ячеек и тогда будут неправильные зависимости
        if(newName.Name != oldName.Name){
            this.buildRecalc(true, true);
        }
        retRes = this.dependencyFormulas.changeDefName( oldName, newName );
        rename = true;
    }
    else {
        retRes = this.dependencyFormulas.addDefinedNameNode( newName.Name, newName.LocalSheetId, newName.Ref, newName.Hidden, bUndo );
    }

    if ( retRes ) {

        History.Create_NewPoint();

        /*  #1. поменяли название - перестроили формулу. нужно вызвать пересборку формул в ячейках, в которыйх есть эта именованная ссылка.
           для этого пробегаемся по всем slave, и вызываем пересборку. в результате должна получиться новая формула, где используется новый диапазон.
          #2. поменяли диапазон. нужно перестроить граф зависимосте и пересчитать формулу. находим диапазон; меняем в нем ссылку; разбираем ссылку;
           удаляем старые master и добавляем новые, которые получились в результате разбора новой ссылки; пересчитываем формулу.*/

        if( !rename ){
            retRes = this.dependencyFormulas.getDefNameNodeByName(newName.Name)
        }

        if(retRes){
            nSE = retRes.getSlaveEdges();
        }

        for ( var id in nSE ) {
            se = nSE[id];
            se.deleteMasterEdge( retRes );
			//todo Master/Slave взаимно обратные свойства, добавлять и удалять надо вместе
			//из Vertex удаляем, в DefName остается, на добавлении новой зависимости в DefName ничего не делаем т.к. уже есть такая зависимость
			//получалось что DefName и Vertex имеют похожи зависимости, но разные обьекты(из-за этого страдали дальнейшие операции)
			//не уверен в этой правке
			retRes.deleteSlaveEdge(se);
            if (!se.isDefinedName) {
                this.needRecalc.nodes[se.nodeId] = [se.sheetId, se.cellId ];
                this.needRecalc.length++;
                addToArrRecalc(se.sheetId, se.cell);
            }
            se = se.returnCell();
            if ( se ) {
                se.setFormula( se.formulaParsed.assemble() );
                if ( !rename ) {
                    se.formulaParsed.setFormula(se.sFormula);
                }
            }
        }

		if ( rename ) {
			History.Add( AscCommonExcel.g_oUndoRedoWorkbook, AscCH.historyitem_Workbook_DefinedNamesChange, null, null, new UndoRedoData_DefinedNamesChange( oldName, newName ) );
		}
		else {
			History.Add( AscCommonExcel.g_oUndoRedoWorkbook, AscCH.historyitem_Workbook_DefinedNamesAdd, null, null, new UndoRedoData_DefinedNamesChange( oldName, newName ) );
		}
        /*if(retRes){
            retRes = retRes.getAscCDefName();
        }*/
    }
    if(!bUndo)
        this.buildRecalc();
    return retRes;

};
Workbook.prototype.findDefinesNames = function ( ref, sheetId ) {
    return this.dependencyFormulas.getDefNameNodeByRef( ref, sheetId );
};
Workbook.prototype.unlockDefName = function(){
    this.dependencyFormulas.unlockDefName();
};
Workbook.prototype.checkDefNameLock = function(){
    return this.dependencyFormulas.checkDefNameLock();
};
Workbook.prototype.getUniqueDefinedNameFrom=function(name, bCopy){
    var nIndex = 1,
        dnNewName = "",
        fGetPostfix = null,
        name = name.Name,
        sheetId = name.sheetId;
    if(bCopy)
    {

        var result = /^(.*)(\d)$/.exec(name);
        if(result)
        {
            fGetPostfix = function(nIndex){return "" + nIndex;};
            name = result[1];
        }
        else
        {
            fGetPostfix = function(nIndex){return "_" + nIndex;};
            name = name;
        }
    }
    else
    {
        fGetPostfix = function(nIndex){return nIndex.toString();};
    }
    while(nIndex < 10000)
    {
        var sPosfix = fGetPostfix(nIndex);
        dnNewName = name + sPosfix;
        var bUniqueName = false;
        if(!this.isDefinedNamesExists( dnNewName, sheetId )){
            bUniqueName = true;
            break;
        }
        if(bUniqueName)
            break;
        nIndex++;
    }
    return dnNewName;
};
Workbook.prototype.buildDependency = function(){
	for(var i in this.cwf){
		this.getWorksheetById(i)._BuildDependencies(this.cwf[i]);
	}
};
Workbook.prototype.recalcDependency = function(f,bad,notRecalc){
    if(f.length > 0){

        var sr = {};

        for(var i = 0; i < f.length; i++){
            var sheetID = f[i].sheetId, cellID = f[i].cellId, selectedSheet;
            if( cellID.indexOf(":") > -1 ) continue;

            var l = new CellAddress(cellID);
            var lRow = l.getRow(), lCol = l.getCol();

            if( !(sheetID in sr) ){
                sr[sheetID] = {max:new CellAddress(cellID),min:new CellAddress(cellID)}
            }

            selectedSheet = sr[sheetID];

            if ( selectedSheet.min.getRow() > lRow )
                selectedSheet.min = new CellAddress( lRow, selectedSheet.min.getCol() );

            if ( selectedSheet.min.getCol() > lCol )
                selectedSheet.min = new CellAddress( selectedSheet.min.getRow(), lCol );

            if ( selectedSheet.max.getRow() < lRow )
                selectedSheet.max = new CellAddress( lRow, selectedSheet.max.getCol() );

            if ( selectedSheet.max.getCol() < lCol )
                selectedSheet.max = new CellAddress( selectedSheet.max.getRow(), lCol );

            if( !notRecalc )
                this.getWorksheetById( sheetID )._RecalculatedFunctions( cellID, bad );
        }

        return sr;
    }
};
Workbook.prototype._SerializeHistoryBase64 = function (oMemory, item, aPointChangesBase64) {
    if (!item.LocalChange) {
        var nPosStart = oMemory.GetCurPosition();
        item.Serialize(oMemory, this.oApi.collaborativeEditing);
        var nPosEnd = oMemory.GetCurPosition();
        var nLen = nPosEnd - nPosStart;
        if (nLen > 0)
            aPointChangesBase64.push(nLen + ";" + oMemory.GetBase64Memory2(nPosStart, nLen));
    }
};
Workbook.prototype.SerializeHistory = function(){
	var aRes = [];
	//соединяем изменения, которые были до приема данных с теми, что получились после.

    var worksheets = this.aWorksheets, t, j, length2;
    for(t = 0; t < worksheets.length; ++t)
    {
        worksheets[t] && worksheets[t].refreshContentChanges();
    }
	var aActions = this.aCollaborativeActions.concat(History.GetSerializeArray());
	if(aActions.length > 0)
	{
		var oMemory = new AscCommon.CMemory();
		var bChangeSheetPlace = false;
		for(var i = 0, length = aActions.length; i < length; ++i)
		{
		    var aPointChanges = aActions[i];
		    bChangeSheetPlace = false;
		    for (j = 0, length2 = aPointChanges.length; j < length2; ++j) {
		        var item = aPointChanges[j];
		        if (AscCommonExcel.g_oUndoRedoWorkbook == item.oClass) {
		            if (AscCH.historyitem_Workbook_SheetAdd == item.nActionType || AscCH.historyitem_Workbook_SheetRemove == item.nActionType || AscCH.historyitem_Workbook_SheetMove == item.nActionType)
		                bChangeSheetPlace = true;
		        }
		        else if (AscCommonExcel.g_oUndoRedoWorksheet === item.oClass && AscCH.historyitem_Worksheet_Hide === item.nActionType) {
		            bChangeSheetPlace = true;
		        }
		        this._SerializeHistoryBase64(oMemory, item, aRes);
		    }
			var oUndoRedoData_SheetPositions;
		    if (bChangeSheetPlace) {
		        //создаем еще один элемент в undo/redo - взаимное расположение Sheet, чтобы не запутываться в add, move событиях
		        //добавляем не после конца aActions, чтобы можно было делать undo/redo и просто удалять хвост изменений.
		        var oSheetPlaceData = [];
		        for (j = 0, length2 = this.aWorksheets.length; j < length2; ++j)
		            oSheetPlaceData.push(this.aWorksheets[j].getId());
		        oUndoRedoData_SheetPositions = new UndoRedoData_SheetPositions(oSheetPlaceData);
		    }
			else
				oUndoRedoData_SheetPositions = new UndoRedoData_SheetPositions();
			this._SerializeHistoryBase64(oMemory, new UndoRedoItemSerializable(AscCommonExcel.g_oUndoRedoWorkbook, AscCH.historyitem_Workbook_SheetPositions, null, null, oUndoRedoData_SheetPositions), aRes);
		}
		this.aCollaborativeActions = [];
	}
	return aRes;
};
Workbook.prototype.DeserializeHistory = function(aChanges, fCallback){
	var oThis = this;
	//сохраняем те изменения, которые были до приема данных, потому что дальше undo/redo будет очищено
	this.aCollaborativeActions = this.aCollaborativeActions.concat(History.GetSerializeArray());
	if(aChanges.length > 0)
	{
		this.bCollaborativeChanges = true;
		//собираем общую длину
		var dstLen = 0;
		var aIndexes = [], i, length = aChanges.length, sChange;
		for(i = 0; i < length; ++i)
		{
		    sChange = aChanges[i];
			var nIndex = sChange.indexOf(";");
			if (-1 != nIndex) {
				dstLen += parseInt(sChange.substring(0, nIndex));
				nIndex++;
			}
			aIndexes.push(nIndex);
		}
		var pointer = g_memory.Alloc(dstLen);
		var stream = new AscCommon.FT_Stream2(pointer.data, dstLen);
		stream.obj = pointer.obj;
		var nCurOffset = 0;
		//пробегаемся первый раз чтобы заполнить oFontMap
		var oFontMap = {};//собираем все шрифтры со всех изменений
		var aUndoRedoElems = [];
		for (i = 0; i < length; ++i) {
		    sChange = aChanges[i];
			var oBinaryFileReader = new AscCommonExcel.BinaryFileReader();
			nCurOffset = oBinaryFileReader.getbase64DecodedData2(sChange, aIndexes[i], stream, nCurOffset);
			var item = new UndoRedoItemSerializable();
			item.Deserialize(stream);
			if (AscCommonExcel.g_oUndoRedoWorkbook == item.oClass && AscCH.historyitem_Workbook_AddFont == item.nActionType) {
				for (var k = 0, length3 = item.oData.elem.length; k < length3; ++k)
					oFontMap[item.oData.elem[k]] = 1;
			}
			aUndoRedoElems.push(item);
		}

		window["Asc"]["editor"]._loadFonts(oFontMap, function(){
                var wsViews = window["Asc"]["editor"].wb.wsViews;
                if(oThis.oApi.collaborativeEditing.getFast()){
                  AscCommon.CollaborativeEditing.Clear_DocumentPositions();
                }
                for(var i in wsViews)
                {
                    if(isRealObject(wsViews[i]) && isRealObject(wsViews[i].objectRender) && isRealObject(wsViews[i].objectRender.controller))
                    {
                        if ( wsViews[i].isChartAreaEditMode ) {
                            wsViews[i].isChartAreaEditMode = false;
                            wsViews[i].arrActiveChartsRanges = [];
                        }
                        if(oThis.oApi.collaborativeEditing.getFast()){
                            var oState = wsViews[i].objectRender.saveStateBeforeLoadChanges();
                            if(oState){
                                if (oState.Pos)
                                  AscCommon.CollaborativeEditing.Add_DocumentPosition(oState.Pos);
                                if (oState.StartPos)
                                  AscCommon.CollaborativeEditing.Add_DocumentPosition(oState.StartPos);
                                if (oState.EndPos)
                                  AscCommon.CollaborativeEditing.Add_DocumentPosition(oState.EndPos);
                            }
                        }
                        wsViews[i].objectRender.controller.resetSelection();
                    }
                }
      oFormulaLocaleInfo.Parse = false;
      oFormulaLocaleInfo.DigitSep = false;
                History.Clear();
				History.Create_NewPoint();

				History.SetSelection(null);
				History.SetSelectionRedo(null);
				var oRedoObjectParam = new AscCommonExcel.RedoObjectParam();
				History.UndoRedoPrepare(oRedoObjectParam, false);
				for (var i = 0, length = aUndoRedoElems.length; i < length; ++i)
				{
				    var item = aUndoRedoElems[i];
				    if ((null != item.oClass || (item.oData && typeof item.oData.sChangedObjectId === "string")) && null != item.nActionType) {
				        if (window["NATIVE_EDITOR_ENJINE"] === true && window["native"]["CheckNextChange"]) {
				            if (!window["native"]["CheckNextChange"]())
				                break;
				        }
				        // TODO if(g_oUndoRedoGraphicObjects == item.oClass && item.oData.drawingData)
				        //     item.oData.drawingData.bCollaborativeChanges = true;
				        History.RedoAdd(oRedoObjectParam, item.oClass, item.nActionType, item.nSheetId, item.oRange, item.oData);
				    }
				}
			    if(oThis.oApi.collaborativeEditing.getFast()){


                    for(var i in wsViews){
                        if(isRealObject(wsViews[i]) && isRealObject(wsViews[i].objectRender) && isRealObject(wsViews[i].objectRender.controller)){
                            var oState = wsViews[i].objectRender.getStateBeforeLoadChanges();
                            if(oState){
                                if (oState.Pos)
                                  AscCommon.CollaborativeEditing.Update_DocumentPosition(oState.Pos);
                                if (oState.StartPos)
                                  AscCommon.CollaborativeEditing.Update_DocumentPosition(oState.StartPos);
                                if (oState.EndPos)
                                  AscCommon.CollaborativeEditing.Update_DocumentPosition(oState.EndPos);
                            }
                            wsViews[i].objectRender.loadStateAfterLoadChanges();
                        }
                    }
                }
      oFormulaLocaleInfo.Parse = true;
      oFormulaLocaleInfo.DigitSep = true;
				History.UndoRedoEnd(null, oRedoObjectParam, false);

				oThis.bCollaborativeChanges = false;
				History.Clear();
				if(null != fCallback)
					fCallback();
			});
	}
};
Workbook.prototype.DeserializeHistoryNative = function(oRedoObjectParam, data, isFull){
	if(null != data)
	{
		this.bCollaborativeChanges = true;
		
		if(null == oRedoObjectParam)
		{
			var wsViews = window["Asc"]["editor"].wb.wsViews;
			for(var i in wsViews)
			{
				if(isRealObject(wsViews[i]) && isRealObject(wsViews[i].objectRender) && isRealObject(wsViews[i].objectRender.controller))
				{
					if ( wsViews[i].isChartAreaEditMode ) {
						wsViews[i].isChartAreaEditMode = false;
						wsViews[i].arrActiveChartsRanges = [];
					}
					wsViews[i].objectRender.controller.resetSelection();
				}
			}
			
			History.Clear();
			History.Create_NewPoint();
			History.SetSelection(null);
			History.SetSelectionRedo(null);
			oRedoObjectParam = new AscCommonExcel.RedoObjectParam();
			History.UndoRedoPrepare(oRedoObjectParam, false);
		}
		
		var stream = new AscCommon.FT_Stream2(data, data.length);
		stream.obj = null;
		// Применяем изменения, пока они есть
		var _count = stream.GetLong();
		var _pos = 4;
		for (var i = 0; i < _count; i++)
		{
			if (window["NATIVE_EDITOR_ENJINE"] === true && window["native"]["CheckNextChange"])
			{
				if (!window["native"]["CheckNextChange"]())
					break;
			}
			
			var _len = stream.GetLong();

			_pos += 4;
			stream.size = _pos + _len;
            stream.Seek(_pos);
			stream.Seek2(_pos);
			
			var item = new UndoRedoItemSerializable();
			item.Deserialize(stream);
			if ((null != item.oClass || (item.oData && typeof item.oData.sChangedObjectId === "string")) && null != item.nActionType)
				History.RedoAdd(oRedoObjectParam, item.oClass, item.nActionType, item.nSheetId, item.oRange, item.oData);

			_pos += _len;
			stream.Seek2(_pos);
			stream.size = data.length;
		}
				
		if(isFull){
			History.UndoRedoEnd(null, oRedoObjectParam, false);
			History.Clear();
			oRedoObjectParam = null;
		}
		this.bCollaborativeChanges = false;
	}
	return oRedoObjectParam;
};
Workbook.prototype.getTableRangeForFormula = function(name, objectParam){
	var res = null;
	for(var i = 0, length = this.aWorksheets.length; i < length; ++i)
    {
        var ws = this.aWorksheets[i];
		res = ws.getTableRangeForFormula(name, objectParam);
		if(res !== null){
            res = {wsID:ws.getId(),range:res};
            break;
        }
    }
	return res;
};
Workbook.prototype.getTableIndexColumnByName = function(tableName, columnName){
	var res = null;
	for(var i = 0, length = this.aWorksheets.length; i < length; ++i)
    {
        var ws = this.aWorksheets[i];
		res = ws.getTableIndexColumnByName(tableName, columnName);
		if(res !== null){
            res = {wsID:ws.getId(), index: res};
            break;
        }
    }
	return res;
};
Workbook.prototype.getTableNameColumnByIndex = function(tableName, columnIndex){
	var res = null;
	for(var i = 0, length = this.aWorksheets.length; i < length; ++i)
    {
        var ws = this.aWorksheets[i];
		res = ws.getTableNameColumnByIndex(tableName, columnIndex);
		if(res !== null){
            res = {wsID:ws.getId(), columnName: res};
            break;
        }
    }
	return res;
};
	Workbook.prototype.updateSparklineCache = function (sheet, ranges) {
		for (var i = 0; i < this.aWorksheets.length; ++i) {
			this.aWorksheets[i].updateSparklineCache(sheet, ranges);
		}
	};
	Workbook.prototype.lockDraw = function () {
		++this.lockCounter;
	};
	Workbook.prototype.unLockDraw = function () {
		if (0 < this.lockCounter) {
			--this.lockCounter;
		}
	};
	Workbook.prototype.buildRecalc = function (notrec, bForce) {
		var ws;
		if (this.lockCounter > 0 && !bForce) {
			return;
		}
		if (!bForce) {
			for (var id in arrDefNameRecalc) {
				arrDefNameRecalc[id].rebuild();
			}
			arrDefNameRecalc = {};
		}
		for (var id in arrRecalc) {
			ws = this.getWorksheetById(id);
			if (ws) {
				var temp = arrRecalc[id];
				var _rec = {};
				for (var i = 0, length = temp.length; i < length; ++i) {
					var cell = temp[i];
					var cellId = g_oCellAddressUtils.getCellId(cell.nRow, cell.nCol);
					_rec[cellId] = cellId;
					this.needRecalc.nodes[getVertexId(id, cellId)] = [id, cellId];
					this.needRecalc.length++;
				}
				ws._BuildDependencies(_rec);
			}
		}
		arrRecalc = {};
		if (!notrec) {
			this.sortDependency();
		}
	};
	Workbook.prototype.sortDependency = function (setCellFormat) {
		this.buildRecalc(true);

		var i;
		var nR = this.needRecalc;
		if (nR && (nR.length > 0)) {
			var oCleanCellCacheArea = {};
			var oNodeDependence = this.dependencyFormulas.getNodeDependence(nR.nodes);
			for (i in oNodeDependence.oMasterNodes) {
				this._sortDependency(oNodeDependence.oMasterNodes[i], oNodeDependence, oNodeDependence.oMasterAreaNodes, false,
					oCleanCellCacheArea, setCellFormat);
			}
			//те AreaNodes
			var oCurMasterAreaNodes = oNodeDependence.oMasterAreaNodes;
			while (true) {
				var bEmpty = true;
				var oNewMasterAreaNodes = {};
				for (i in oCurMasterAreaNodes) {
					bEmpty = false;
					this._sortDependency(oCurMasterAreaNodes[i], oNodeDependence, oNewMasterAreaNodes, false, oCleanCellCacheArea,
						setCellFormat);
				}
				oCurMasterAreaNodes = oNewMasterAreaNodes;
				if (bEmpty) {
					//все оставшиеся считаем как bad
					//todo сделать как в Excel, которой определяет циклические ссылки на момент подсчета(пример A1=VLOOKUP(1,B1:D2,2),B2 = 1, D1=A1 - это не циклическая ссылка)
					for (i in oNodeDependence.oMasterAreaNodesRestricted) {
						this._sortDependency(oNodeDependence.oMasterAreaNodesRestricted[i].node, oNodeDependence, null, true,
							oCleanCellCacheArea, setCellFormat);
					}
					break;
				}
			}
			for (i in oCleanCellCacheArea) {
				this.handlers.trigger("cleanCellCache", i, oCleanCellCacheArea[i], AscCommonExcel.c_oAscCanChangeColWidth.none);
			}

			AscCommonExcel.g_oVLOOKUPCache.clean();
			AscCommonExcel.g_oHLOOKUPCache.clean();
		}
		this.needRecalc = {nodes: {}, length: 0};
	};
	Workbook.prototype._sortDependency =
		function (node, oNodeDependence, oNewMasterAreaNodes, bBad, oCleanCellCacheArea, setCellFormat) {
			if (node) {
				var oWeightMapElem = oNodeDependence.oWeightMap[node.nodeId];
				if (oWeightMapElem) {
					oWeightMapElem.cur++;
					if (oWeightMapElem.cur == oWeightMapElem.max && !oWeightMapElem.gray) {
						if (null != oNewMasterAreaNodes) {
							var oNodeToAreaElement = oNodeDependence.oNodeToArea[node.nodeId];
							if (oNodeToAreaElement) {
								for (var i = 0, length = oNodeToAreaElement.length; i < length; ++i) {
									var elem = oNodeToAreaElement[i];
									elem.cur++;
									if (elem.cur == elem.max) {
										oNewMasterAreaNodes[elem.node.nodeId] = elem.node;
										delete oNodeDependence.oMasterAreaNodesRestricted[elem.node.nodeId];
									}
								}
							}
						}
						var bCurBad = oWeightMapElem.bad || bBad;
						if (node.isDefinedName) {
							//todo
							//Обрабатываем тут все, что было сделано с именованной ссылкой: переименована;
							//перемещен диапазон; сдвиг/удаление ячеек, приведшие к сдвигу ячеек; удаление именованного диапазона.
							//
							//        var ws = this.getWorksheetById( node.sheetId );
							//        ws._ReBuildFormulas
							//        ws._RecalculatedFunctions(node.cellId, bCurBad, setCellFormat);
						} else {
							//пересчитываем функцию
							var ws = this.getWorksheetById(node.sheetId);
							ws._RecalculatedFunctions(node.cellId, bCurBad, setCellFormat);
							//запоминаем области для удаления cache
							var sheetArea = oCleanCellCacheArea[node.sheetId];
							if (null == sheetArea) {
								sheetArea = {};
								oCleanCellCacheArea[node.sheetId] = sheetArea;
							}
							if (!node.isArea) {
								sheetArea[node.cellId] = node.getBBox();
							}
						}
						//обрабатываем child
						oWeightMapElem.gray = true;
						var oSlaveNodes = node.getSlaveEdges();
						if (oSlaveNodes) {
							for (var i in oSlaveNodes) {
								this._sortDependency(oSlaveNodes[i], oNodeDependence, oNewMasterAreaNodes, bBad, oCleanCellCacheArea);
							}
						}
						oWeightMapElem.gray = false;
					}
				}
			}
		};
	/**
	 * Вычисляет ширину столбца для заданного количества символов
	 * @param {Number} count  Количество символов
	 * @returns {Number}      Ширина столбца в символах
	 */
	Workbook.prototype.charCountToModelColWidth = function (count) {
		if (count <= 0) {
			return 0;
		}
		return Asc.floor((count * this.maxDigitWidth + this.paddingPlusBorder) / this.maxDigitWidth * 256) / 256;
	};
//-------------------------------------------------------------------------------------------------
/**
 * @constructor
 */
function Woorksheet(wb, _index, sId){
	this.workbook = wb;
	this.DefinedNames = {};
	this.sName = this.workbook.getUniqueSheetNameFrom(g_sNewSheetNamePattern, false);
	this.bHidden = false;
	this.oSheetFormatPr = new AscCommonExcel.SheetFormatPr();
	this.index = _index;
	this.Id = null != sId ? sId : AscCommon.g_oIdCounter.Get_NewId();
	this.nRowsCount = 0;
	this.nColsCount = 0;
	this.aGCells = {};// 0 based
	this.aCols = [];// 0 based
	this.Drawings = [];
	this.TableParts = [];
	this.AutoFilter = null;
	this.oAllCol = null;
	this.aComments = [];
	this.aCommentsCoords = [];
	var oThis = this;
	this.mergeManager = new RangeDataManager(function(data, from, to){
		if(History.Is_On() && (null != from || null != to))
		{
			if(null != from)
				from = from.clone();
			if(null != to)
				to = to.clone();
			var oHistoryRange = from;
			if(null == oHistoryRange)
				oHistoryRange = to;
			History.Add(AscCommonExcel.g_oUndoRedoWorksheet, AscCH.historyitem_Worksheet_ChangeMerge, oThis.getId(), oHistoryRange, new UndoRedoData_FromTo(new UndoRedoData_BBox(from), new UndoRedoData_BBox(to)));
		}
		//расширяем границы
		if(null != to){
			if(to.r2 >= oThis.nRowsCount)
				oThis.nRowsCount = to.r2 + 1;
			if(to.c2 >= oThis.nColsCount)
				oThis.nColsCount = to.c2 + 1;
		}
	});
	this.hyperlinkManager = new RangeDataManager(function(data, from, to, oChangeParam){
		if(History.Is_On() && (null != from || null != to))
		{
			if(null != from)
				from = from.clone();
			if(null != to)
				to = to.clone();
			var oHistoryRange = from;
			if(null == oHistoryRange)
				oHistoryRange = to;
			var oHistoryData = null;
			if(null == from || null == to)
				oHistoryData = data.clone();
			History.Add(AscCommonExcel.g_oUndoRedoWorksheet, AscCH.historyitem_Worksheet_ChangeHyperlink, oThis.getId(), oHistoryRange, new AscCommonExcel.UndoRedoData_FromToHyperlink(from, to, oHistoryData));
		}
		if (null != to)
		    data.Ref = oThis.getRange3(to.r1, to.c1, to.r2, to.c2);
		else if (oChangeParam && oChangeParam.removeStyle && null != data.Ref)
		    data.Ref.cleanFormat();
		//расширяем границы
		if(null != to){
			if(to.r2 >= oThis.nRowsCount)
				oThis.nRowsCount = to.r2 + 1;
			if(to.c2 >= oThis.nColsCount)
				oThis.nColsCount = to.c2 + 1;
		}
	});
	this.hyperlinkManager.setDependenceManager(this.mergeManager);
    this.DrawingDocument = new AscCommon.CDrawingDocument();
	this.sheetViews = [];
	this.aConditionalFormatting = [];
	this.sheetPr = null;
	this.aFormulaExt = null;
	
	this.autoFilters = AscCommonExcel.AutoFilters !== undefined ? new AscCommonExcel.AutoFilters(this) : null;

    this.oDrawingOjectsManager = new DrawingObjectsManager(this);
    this.contentChanges = new AscCommon.CContentChanges();

    this.aSparklineGroups = [];

	/*handlers*/
	this.handlers = null;
}

Woorksheet.prototype.addContentChanges = function(changes)
{
    this.contentChanges.Add(changes);
};

Woorksheet.prototype.refreshContentChanges = function()
{
    this.contentChanges.Refresh();
    this.contentChanges.Clear();
};

Woorksheet.prototype.rebuildColors=function(){
	this._forEachCell(function(cell){
		cell.cleanCache();
	});
  this.rebuildTabColor();

  for (var i = 0; i < this.aSparklineGroups.length; ++i) {
    this.aSparklineGroups[i].cleanCache();
  }
};
Woorksheet.prototype.generateFontMap=function(oFontMap){
	//пробегаемся по Drawing
	for(var i = 0, length = this.Drawings.length; i < length; ++i)
	{
		var drawing = this.Drawings[i];
		if(drawing)
			drawing.getAllFonts(oFontMap);
	}
	if(null != this.workbook.theme)
    AscFormat.checkThemeFonts(oFontMap, this.workbook.theme.themeElements.fontScheme);
	//пробегаемся по колонкам
	for(var i in this.aCols)
	{
		var col = this.aCols[i];
		if(null != col && null != col.xfs && null != col.xfs.font && null != col.xfs.font.fn)
			oFontMap[col.xfs.font.fn] = 1;
	}
	if(null != this.oAllCol && null != this.oAllCol.xfs && null != this.oAllCol.xfs.font && null != this.oAllCol.xfs.font.fn)
		oFontMap[this.oAllCol.xfs.font.fn] = 1;
	//пробегаемся строкам
	for(var i in this.aGCells)
	{
		var row = this.aGCells[i];
		if(null != row && null != row.xfs && null != row.xfs.font && null != row.xfs.font.fn)
			oFontMap[row.xfs.font.fn] = 1;
		//пробегаемся по ячейкам
		for(var j in row.c)
		{
			var cell = row.c[j];
			if(null != cell)
			{
				if(null != cell.xfs && null != cell.xfs.font && null != cell.xfs.font.fn)
					oFontMap[cell.xfs.font.fn] = 1;
				//смотрим в комплексных строках
				if(null != cell.oValue && null != cell.oValue.multiText)
				{
					for(var k = 0, length3 = cell.oValue.multiText.length; k < length3; ++k)
					{
						var part = cell.oValue.multiText[k];
						if(null != part.format && null != part.format.fn)
							oFontMap[part.format.fn] = 1;
					}
				}
			}
		}
	}
};
Woorksheet.prototype.getAllImageUrls = function(aImages){
    for(var i = 0; i < this.Drawings.length; ++i){
        this.Drawings[i].graphicObject.getAllRasterImages(aImages);
    }
};
Woorksheet.prototype.reassignImageUrls = function(oImages){
    for(var i = 0; i < this.Drawings.length; ++i){
        this.Drawings[i].graphicObject.Reassign_ImageUrls(oImages);
    }
};
Woorksheet.prototype.clone=function(sNewId, sName, tableNames){
	var i, elem, range;
	var oNewWs = new Woorksheet(this.workbook, this.workbook.aWorksheets.length, sNewId);
	oNewWs.sName = this.workbook.checkValidSheetName(sName) ? sName : this.workbook.getUniqueSheetNameFrom(this.sName, true);
	oNewWs.bHidden = this.bHidden;
	oNewWs.oSheetFormatPr = this.oSheetFormatPr.clone();
	oNewWs.index = this.index;
	oNewWs.nRowsCount = this.nRowsCount;
	oNewWs.nColsCount = this.nColsCount;
	for (i = 0; i < this.TableParts.length; ++i)
	{
		var tableName = null;
		if(tableNames && tableNames.length)
		{
			tableName = tableNames[i];
		}
		oNewWs.TableParts.push(this.TableParts[i].clone(oNewWs, tableName));
	}
	if(this.AutoFilter)
		oNewWs.AutoFilter = this.AutoFilter.clone();
	for (i in this.aCols) {
	    var col = this.aCols[i];
	    if(null != col)
	        oNewWs.aCols[i] = col.clone(oNewWs);
	}
	if(null != this.oAllCol)
		oNewWs.oAllCol = this.oAllCol.clone(oNewWs);
	for(i in this.aGCells)
		oNewWs.aGCells[i] = this.aGCells[i].clone(oNewWs);
	var aMerged = this.mergeManager.getAll();
	for(i in aMerged)
	{
		elem = aMerged[i];
		range = oNewWs.getRange3(elem.bbox.r1, elem.bbox.c1, elem.bbox.r2, elem.bbox.c2);
		range.mergeOpen();
	}
	var aHyperlinks = this.hyperlinkManager.getAll();
	for(i in aHyperlinks)
	{
		elem = aHyperlinks[i];
		range = oNewWs.getRange3(elem.bbox.r1, elem.bbox.c1, elem.bbox.r2, elem.bbox.c2);
		range.setHyperlinkOpen(elem.data);
	}
	if(null != this.aComments) {
		for (i = 0; i < this.aComments.length; i++) {
			var comment = new Asc.asc_CCommentData(this.aComments[i]);
			comment.wsId = oNewWs.getId();
      comment.nId = "sheet" + comment.wsId + "_" + (i + 1);
			oNewWs.aComments.push(comment);
		}
	}
	for (i = 0; i < this.sheetViews.length; ++i) {
		oNewWs.sheetViews.push(this.sheetViews[i].clone());
	}
	for (i = 0; i < this.aConditionalFormatting.length; ++i) {
		oNewWs.aConditionalFormatting.push(this.aConditionalFormatting[i].clone());
	}
  for (i = 0; i < this.aSparklineGroups.length; ++i) {
    oNewWs.aSparklineGroups.push(this.aSparklineGroups[i].clone());
  }
	if (this.sheetPr)
		oNewWs.sheetPr = this.sheetPr.clone();

	return oNewWs;
};
Woorksheet.prototype.copyDrawingObjects=function(oNewWs, wsFrom)
{
    if(null != this.Drawings && this.Drawings.length > 0)
    {
        var drawingObjects = new AscFormat.DrawingObjects();
        oNewWs.Drawings = [];
      AscFormat.NEW_WORKSHEET_DRAWING_DOCUMENT = oNewWs.DrawingDocument;
        for(var i = 0; i < this.Drawings.length; ++i)
        {
            var drawingObject = drawingObjects.cloneDrawingObject(this.Drawings[i]);
            drawingObject.graphicObject = this.Drawings[i].graphicObject.copy();
            drawingObject.graphicObject.setWorksheet(oNewWs);
            drawingObject.graphicObject.addToDrawingObjects();
            var drawingBase = this.Drawings[i];
            drawingObject.graphicObject.setDrawingBaseCoords(drawingBase.from.col, drawingBase.from.colOff, drawingBase.from.row, drawingBase.from.rowOff, drawingBase.to.col, drawingBase.to.colOff, drawingBase.to.row, drawingBase.to.rowOff, drawingBase.Pos.X, drawingBase.Pos.Y, drawingBase.ext.cx, drawingBase.ext.cy);
            oNewWs.Drawings[oNewWs.Drawings.length - 1] = drawingObject;
        }
      AscFormat.NEW_WORKSHEET_DRAWING_DOCUMENT = null;
        drawingObjects.pushToAObjects(oNewWs.Drawings);
        drawingObjects.updateChartReferences2(parserHelp.getEscapeSheetName(wsFrom.sName), parserHelp.getEscapeSheetName(oNewWs.sName));
    }
};
	Woorksheet.prototype.initPostOpen = function (handlers) {
		var cwf = this.workbook.cwf[this.Id] = {};
		if (this.aFormulaExt) {
			var formulaShared = {};
			for (var i = 0; i < this.aFormulaExt.length; ++i) {
				var elem = this.aFormulaExt[i];
				var oCell = elem.cell;
				var sCellId = g_oCellAddressUtils.getCellId(oCell.nRow, oCell.nCol);
				var oFormulaExt = elem.ext;
				var isShared = oFormulaExt.t === Asc.ECellFormulaType.cellformulatypeShared && null !== oFormulaExt.si;
				if (isShared) {
					if (null !== oFormulaExt.ref) {
						if (oFormulaExt.v.length <= AscCommon.c_oAscMaxFormulaLength) {
							oCell.formulaParsed = new parserFormula(oFormulaExt.v, sCellId, this);
							oCell.formulaParsed.parse();
							oCell.formulaParsed.buildDependencies();
							formulaShared[oFormulaExt.si] = {
								fVal: oCell.formulaParsed,
								fRef: AscCommonExcel.g_oRangeCache.getAscRange(oFormulaExt.ref)
							};
						}
					} else {
						var fs = formulaShared[oFormulaExt.si];
						if (fs && fs.fRef.contains(oCell.nCol, oCell.nRow)) {
							if (fs.fVal.isParsed) {
								var off = oCell.getOffset3(fs.fRef.c1 + 1, fs.fRef.r1 + 1);

								oCell.formulaParsed = fs.fVal.clone(null, sCellId, this);
								oCell.formulaParsed.changeOffset(off);
								oCell.formulaParsed.buildDependencies();
								oFormulaExt.v = oCell.formulaParsed.assemble();
							}
						}
					}
				}
				if (oFormulaExt.v) {
					if (oFormulaExt.v.length <= AscCommon.c_oAscMaxFormulaLength) {
						oCell.setFormula(oFormulaExt.v);
						if (oFormulaExt.ca) {
							oCell.sFormulaCA = true;
						}
					} else {
						this.workbook.openErrors.push(oCell.getName());
					}
				}

				// Если ячейка содержит в себе формулу, то добавляем ее в список ячеек с формулами. (Shared-формулу мы уже распарсили)
				if (isShared) {
					cwf[sCellId] = null;
				} else {
					if (oCell.sFormula) {
						cwf[sCellId] = sCellId;
					}
				}

				// Строится список ячеек, которые необходимо пересчитать при открытии. Это ячейки имеющие атрибут f.ca или значение в которых неопределено.
				if (oCell.sFormula && (oFormulaExt.ca || !oCell.oValue.getValueWithoutFormat())) {
					this.workbook.needRecalc.nodes[getVertexId(this.Id, sCellId)] = [this.Id, sCellId];
					this.workbook.needRecalc.length++;
				}
			}
			this.aFormulaExt = null;
		}

		if (!this.PagePrintOptions) {
			// Даже если не было, создадим
			this.PagePrintOptions = new Asc.asc_CPageOptions();
		}
		this.PagePrintOptions.init();

		// Sheet Views
		if (0 === this.sheetViews.length) {
			// Даже если не было, создадим
			this.sheetViews.push(new AscCommonExcel.asc_CSheetViewSettings());
		}

		if (window['IS_NATIVE_EDITOR']) {
			for (var j = this.sheetViews.length - 1; j >= 0; --j) {
				this.sheetViews[j].pane = null;
			}
		}

		this._updateConditionalFormatting(null);

		this.handlers = handlers;
		this._setHandlersTablePart();
	};
Woorksheet.prototype._getValuesForConditionalFormatting = function(sqref, withEmpty) {
  var range = this.getRange3(sqref.r1, sqref.c1, sqref.r2, sqref.c2);
	return range._getValues(withEmpty);
};
Woorksheet.prototype._updateConditionalFormatting = function(range) {
  var oGradient1, oGradient2;
  var aCFs = this.aConditionalFormatting;
  var aRules, oRule;
  var oRuleElement = null;
  var o;
  var i, j, l, cell, sqref, values, value, v, tmp, min, mid, max, dxf, compareFunction, nc, sum;
  for (i = 0; i < aCFs.length; ++i) {
    sqref = aCFs[i].sqref;
    // ToDo убрать null === sqref когда научимся мультиселект обрабатывать (\\192.168.5.2\source\DOCUMENTS\XLSX\Matematika Quantum Sedekah.xlsx)
    if (null === sqref) {
      continue;
    }
    if (!range || range.isIntersect(sqref)) {
      aRules = aCFs[i].aRules;
      for (j = 0; j < aRules.length; ++j) {
        oRule = aRules[j];

        // ToDo dataBar, expression, iconSet (page 2679)
        if (AscCommonExcel.ECfType.colorScale === oRule.type) {
          if (1 !== oRule.aRuleElements.length) {
            break;
          }
          oRuleElement = oRule.aRuleElements[0];
          if (!(oRuleElement instanceof AscCommonExcel.CColorScale)) {
            break;
          }
          nc = 0;
          min = Number.MAX_VALUE;
          max = -Number.MAX_VALUE;
          values = this._getValuesForConditionalFormatting(sqref, false);
          for (cell = 0; cell < values.length; ++cell) {
            value = values[cell];
            if (CellValueType.Number === value.c.getType() && !isNaN(tmp = parseFloat(value.v))) {
              value.v = tmp;
              min = Math.min(min, tmp);
              max = Math.max(max, tmp);
              ++nc;
            } else {
              value.v = null;
            }
          }

          // ToDo CFVO Type formula (page 2681)
          l = oRuleElement.aColors.length;
          if (0 < values.length && 2 <= l) {
            oGradient1 = new AscCommonExcel.CGradient(oRuleElement.aColors[0], oRuleElement.aColors[1]);
            min = oRuleElement.getMin(min, max, nc);
            max = oRuleElement.getMax(min, max, nc);
            oGradient2 = null;
            if (2 < l) {
              oGradient2 = new AscCommonExcel.CGradient(oRuleElement.aColors[1], oRuleElement.aColors[2]);
              mid = oRuleElement.getMid(min, max, nc);

              oGradient1.init(min, mid);
              oGradient2.init(mid, max);
            } else {
              oGradient1.init(min, max);
            }

            for (cell = 0; cell < values.length; ++cell) {
              value = values[cell];
              v = value.v;
              dxf = null;
              if (null !== v) {
                dxf = new AscCommonExcel.CellXfs();
                tmp = (oGradient2 && v > oGradient1.max) ? oGradient2 : oGradient1;
                dxf.fill = new AscCommonExcel.Fill({bg: tmp.calculateColor(v)});
              }
              value.c.setConditionalFormattingStyle(dxf);
            }
          }
        } else if (AscCommonExcel.ECfType.top10 === oRule.type) {
          if (oRule.rank > 0 && oRule.dxf) {
            nc = 0;
            values = this._getValuesForConditionalFormatting(sqref, false);
            o = oRule.bottom ? -Number.MAX_VALUE : Number.MAX_VALUE;
            for (cell = 0; cell < values.length; ++cell) {
              value = values[cell];
              if (CellValueType.Number === value.c.getType() && !isNaN(tmp = parseFloat(value.v))) {
                ++nc;
                value.v = tmp;
              } else {
                value.v = o;
              }
            }
            values.sort((function(condition) {
              return function(v1, v2) {
                return condition * (v2.v - v1.v);
              }
            })(oRule.bottom ? -1 : 1));

            tmp = 0;
            nc = oRule.percent ? Math.floor(nc * oRule.rank / 100) : oRule.rank;
            for (cell = 0; cell < values.length; ++cell) {
              value = values[cell];
              value.c.setConditionalFormattingStyle((o !== value.v && tmp < nc) ? (++tmp && oRule.dxf) : null);
            }
          }
        } else if (AscCommonExcel.ECfType.aboveAverage === oRule.type) {
          if (!oRule.dxf) {
            continue;
          }
          values = this._getValuesForConditionalFormatting(sqref, false);
          sum = 0;
          nc = 0;
          for (cell = 0; cell < values.length; ++cell) {
            value = values[cell];
            if (!value.c.isEmptyTextString(value.v)) {
              ++nc;
              if (CellValueType.Number === value.c.getType() && !isNaN(tmp = parseFloat(value.v))) {
                value.v = tmp;
                sum += tmp;
              } else {
                value.v = null;
              }
            }
          }

          tmp = sum / nc;
          /*if (oRule.hasStdDev()) {
            sum = 0;
            for (cell = 0; cell < values.length; ++cell) {
              value = values[cell];
              if (null !== value.v) {
                sum += (value.v - tmp) * (value.v - tmp);
              }
            }
            sum = Math.sqrt(sum / (nc - 1));
          }*/

          for (cell = 0; cell < values.length; ++cell) {
            value = values[cell];
            value.c.setConditionalFormattingStyle((null !== value.v && oRule.getAverage(value.v, tmp, sum)) ? oRule.dxf : null);
          }
        } else {
          if (!oRule.dxf) {
            continue;
          }
          values = this._getValuesForConditionalFormatting(sqref, true);

          switch (oRule.type) {
            case AscCommonExcel.ECfType.duplicateValues:
            case AscCommonExcel.ECfType.uniqueValues:
              o = getUniqueKeys(values);
              compareFunction = (function(obj, condition){
                return function(val) {
                  return condition === obj[val];
                };
              })(o, oRule.type === AscCommonExcel.ECfType.duplicateValues);
              break;
            case AscCommonExcel.ECfType.containsText:
              compareFunction = (function(text){
                return function(val) {
                  return -1 !== val.indexOf(text);
                };
              })(oRule.text);
              break;
            case AscCommonExcel.ECfType.notContainsText:
              compareFunction = (function(text){
                return function(val) {
                  return -1 === val.indexOf(text);
                };
              })(oRule.text);
              break;
            case AscCommonExcel.ECfType.beginsWith:
              compareFunction = (function(text){
                return function(val) {
                  return val.startsWith(text);
                };
              })(oRule.text);

              break;
            case AscCommonExcel.ECfType.endsWith:
              compareFunction = (function(text){
                return function(val) {
                  return val.endsWith(text);
                };
              })(oRule.text);
              break;
            case AscCommonExcel.ECfType.containsErrors:
              compareFunction = function(val, c) {
                return CellValueType.Error === c.getType();
              };
              break;
            case AscCommonExcel.ECfType.notContainsErrors:
              compareFunction = function(val, c) {
                return CellValueType.Error !== c.getType();
              };
              break;
            case AscCommonExcel.ECfType.containsBlanks:
              compareFunction = function(val, c) {
                return c.isEmptyTextString();
              };
              break;
            case AscCommonExcel.ECfType.notContainsBlanks:
              compareFunction = function(val, c) {
                return !c.isEmptyTextString();
              };
              break;
            case AscCommonExcel.ECfType.timePeriod:
              if (oRule.timePeriod) {
                compareFunction = (function(period) {
                  return function(val, c) {
                    var n = parseFloat(val);
                    return period.start <= n && n <= period.end;
                  };
                })(oRule.getTimePeriod());
              } else {
                continue;
              }
              break;
            case AscCommonExcel.ECfType.cellIs:
              compareFunction = (function(rule, v1, v2) {
                return function(val, c) {
                  return rule.cellIs(val, v1, v2);
                };
              })(oRule, oRule.aRuleElements[0] && oRule.aRuleElements[0].getValue(this), oRule.aRuleElements[1] && oRule.aRuleElements[1].getValue(this));
              break;
            default:
              continue;
              break;
          }

          for (cell = 0; cell < values.length; ++cell) {
            value = values[cell];
            value.c.setConditionalFormattingStyle(compareFunction(value.v, value.c) ? oRule.dxf : null);
          }
        }
      }
    }
  }
};
Woorksheet.prototype._forEachCell=function(fAction){
	for(var rowInd in this.aGCells){
		var row = this.aGCells[rowInd];
		if(row){
			for(var cellInd in row.c){
				var cell = row.c[cellInd];
				if(cell){
					fAction(cell);
				}
			}
		}
	}
};
Woorksheet.prototype.getId=function(){
	return this.Id;
};
Woorksheet.prototype.getIndex=function(){
	return this.index;
};
Woorksheet.prototype.getName=function(){
	return this.sName !== undefined && this.sName.length > 0 ? this.sName : "";
};
Woorksheet.prototype.setName=function(name, bFromUndoRedo){
	if(name.length <= g_nSheetNameMaxLength)
	{
		var lastName = this.sName;
		this.sName = name;
		History.Create_NewPoint();
		History.Add(AscCommonExcel.g_oUndoRedoWorksheet, AscCH.historyitem_Worksheet_Rename, this.getId(), null, new UndoRedoData_FromTo(lastName, name));

		//перестраиваем формулы, если у них были ссылки на лист со старым именем.
		for(var id in this.workbook.cwf) {
			this.workbook.getWorksheetById(id)._ReBuildFormulas(this.workbook.cwf[id]);
		}

        this.workbook.dependencyFormulas.relinkDefNameByWorksheet(lastName, name);

        if(!bFromUndoRedo)
        {
            var _lastName = parserHelp.getEscapeSheetName(lastName);
            var _newName = parserHelp.getEscapeSheetName(this.sName);

            for (var key in this.workbook.aWorksheets)
            {
                var wsModel = this.workbook.aWorksheets[key];
                if ( wsModel )
                    wsModel.oDrawingOjectsManager.updateChartReferencesWidthHistory(_lastName, _newName, true);
            }
        }
	}
};
Woorksheet.prototype.getTabColor=function(){
	return this.sheetPr && this.sheetPr.TabColor ? Asc.colorObjToAscColor(this.sheetPr.TabColor) : null;
};
Woorksheet.prototype.setTabColor=function(color){
	if (!this.sheetPr)
		this.sheetPr = new AscCommonExcel.asc_CSheetPr();

	History.Create_NewPoint();
	History.Add(AscCommonExcel.g_oUndoRedoWorksheet, AscCH.historyitem_Worksheet_SetTabColor, this.getId(), null,
		new UndoRedoData_FromTo(this.sheetPr.TabColor ? this.sheetPr.TabColor.clone() : null, color ? color.clone() : null));

	this.sheetPr.TabColor = color;
	if (!this.workbook.bUndoChanges && !this.workbook.bRedoChanges)
	    this.workbook.handlers.trigger("asc_onUpdateTabColor", this.getIndex());
};
Woorksheet.prototype.rebuildTabColor = function() {
  if (this.sheetPr && this.sheetPr.TabColor) {
    this.workbook.handlers.trigger("asc_onUpdateTabColor", this.getIndex());
  }
};
Woorksheet.prototype.renameWsToCollaborate=function(name){
    var lastname = this.getName();
	//из-за особенностей реализации формул, сначала делаем parse со старым именем, потом преименовываем, потом assemble
	var aFormulas = [];
	//переименование для отправки изменений
	for(var i = 0, length = this.workbook.aCollaborativeActions.length; i < length; ++i)
	{
	    var aPointActions = this.workbook.aCollaborativeActions[i];
	    for (var j = 0, length2 = aPointActions.length; j < length2; ++j) {
	        var action = aPointActions[j];
	        if (AscCommonExcel.g_oUndoRedoWorkbook == action.oClass) {
	            if (AscCH.historyitem_Workbook_SheetAdd == action.nActionType) {
	                if (lastname == action.oData.name)
	                    action.oData.name = name;
	            }
	        }
	        else if (AscCommonExcel.g_oUndoRedoWorksheet == action.oClass) {
	            if (AscCH.historyitem_Worksheet_Rename == action.nActionType) {
	                if (lastname == action.oData.to)
	                    action.oData.to = name;
	            }
	        }
	        else if (AscCommonExcel.g_oUndoRedoCell == action.oClass) {
	            if (action.oData instanceof UndoRedoData_CellSimpleData) {
	                if (action.oData.oNewVal instanceof UndoRedoData_CellValueData) {
	                    var oNewVal = action.oData.oNewVal;
	                    if (null != oNewVal.formula && -1 != oNewVal.formula.indexOf(lastname)) {
	                        var oParser = new parserFormula(oNewVal.formula, "A1", this);
	                        oParser.parse();
	                        aFormulas.push({ formula: oParser, value: oNewVal });

	                    }
	                }
	            }
	        }
	    }
	}
	//переименование для локальной версии
	this.setName(name);
	for(var i = 0, length = aFormulas.length; i < length; ++i)
	{
		var item = aFormulas[i];
		item.value.formula = item.formula.assemble();
	}
};
Woorksheet.prototype.getHidden=function(){
	if(null != this.bHidden)
		return false != this.bHidden;
	return false;
};
Woorksheet.prototype.setHidden = function (hidden) {
    var bOldHidden = this.bHidden, wb = this.workbook, wsActive = wb.getActiveWs(), oVisibleWs = null;
    this.bHidden = hidden;
    if (true == this.bHidden && this.getIndex() == wsActive.getIndex())
	{
	    oVisibleWs = wb.findSheetNoHidden(this.getIndex());
	    if (null != oVisibleWs) {
	        var nNewIndex = oVisibleWs.getIndex();
	        wb.setActive(nNewIndex);
	        if (!wb.bUndoChanges && !wb.bRedoChanges)
	            wb.handlers.trigger("undoRedoHideSheet", nNewIndex);
	    }
	}
	if (bOldHidden != hidden) {
	    History.Create_NewPoint();
	    History.Add(AscCommonExcel.g_oUndoRedoWorksheet, AscCH.historyitem_Worksheet_Hide, this.getId(), null, new UndoRedoData_FromTo(bOldHidden, hidden));
	    if (null != oVisibleWs) {
	        History.SetSheetUndo(wsActive.getId());
	        History.SetSheetRedo(oVisibleWs.getId());
	    }
	}
};
Woorksheet.prototype.getSheetViewSettings = function () {
	return this.sheetViews[0].clone();
};
Woorksheet.prototype.setSheetViewSettings = function (options) {
	var current = this.getSheetViewSettings();
	if (current.isEqual(options))
		return;

	History.Create_NewPoint();
	History.Add(AscCommonExcel.g_oUndoRedoWorksheet, AscCH.historyitem_Worksheet_SetViewSettings, this.getId(), null, new UndoRedoData_FromTo(current, options.clone()));

	this.sheetViews[0].setSettings(options);
	if (!this.workbook.bUndoChanges && !this.workbook.bRedoChanges)
	    this.workbook.handlers.trigger("asc_onUpdateSheetViewSettings");
};
Woorksheet.prototype.getRowsCount=function(){
	var result = this.nRowsCount;
	var pane = this.sheetViews[0].pane;
	if (null !== pane && null !== pane.topLeftFrozenCell)
		result = Math.max(result, pane.topLeftFrozenCell.getRow0());
	return result;
};
Woorksheet.prototype.removeRows=function(start, stop){
	var oRange = this.getRange(new CellAddress(start, 0, 0), new CellAddress(stop, gc_nMaxCol0, 0));
	oRange.deleteCellsShiftUp();
};
Woorksheet.prototype._removeRows=function(start, stop){
	//до перемещения ячеек, перед функцией, в которой используются nodesSheetArea/nodesSheetCell move/shift нужно обязательно вызвать force buildRecalc
	//чтобы формулы, которые копим попали в струкруры nodesSheet, иначе формулы не сдвинутся
	//например принимаем изменения: добавление формул, сдвиг с помощью _removeRows. результат формулы указывают на теже ячейки, что и до _removeRows
  this.workbook.buildRecalc(true, true);
  this.workbook.lockDraw();
	History.Create_NewPoint();
	//start, stop 0 based
	var nDif = -(stop - start + 1);
    var oActualRange = {r1: start, c1: 0, r2: stop, c2: gc_nMaxCol0};
    this.renameDependencyNodes({offsetRow:nDif,offsetCol:0}, oActualRange);
	var i, j, length, nIndex, aIndexes = [];
	for(i in this.aGCells)
	{
		nIndex = i - 0;
		if(nIndex >= start)
			aIndexes.push(nIndex);
	}
	//По возрастанию
	aIndexes.sort(fSortAscending);
	var oDefRowPr = new AscCommonExcel.UndoRedoData_RowProp();
	for(i = 0, length = aIndexes.length; i < length; ++i)
	{
		nIndex = aIndexes[i];
		var row = this.aGCells[nIndex];
		if(nIndex > stop)
		{
			if(false == row.isEmpty())
			{
				var oTargetRow = this._getRow(nIndex + nDif);
				oTargetRow.copyProperty(row);
			}
			for(j in row.c)
				this._moveCellVer(nIndex, j - 0, nDif);
		}
		else
		{
			var oOldProps = row.getHeightProp();
			if (false === oOldProps.isEqual(oDefRowPr))
			    History.Add(AscCommonExcel.g_oUndoRedoWorksheet, AscCH.historyitem_Worksheet_RowProp, this.getId(), row._getUpdateRange(), new UndoRedoData_IndexSimpleProp(nIndex, true, oOldProps, oDefRowPr));
			row.setStyle(null);
			for(j in row.c)
			{
				var nColIndex = j - 0;
				//удаляем ячейку
				this._removeCell(nIndex, nColIndex);
			}
		}
		delete this.aGCells[nIndex];
	}


	History.Add(AscCommonExcel.g_oUndoRedoWorksheet, AscCH.historyitem_Worksheet_RemoveRows, this.getId(), new Asc.Range(0, start, gc_nMaxCol0, gc_nMaxRow0), new UndoRedoData_FromToRowCol(true, start, stop));
    this.autoFilters.insertRows( "delCell", new Asc.Range(0, start, gc_nMaxCol0, stop), c_oAscDeleteOptions.DeleteRows );

  this.workbook.unLockDraw();
  this.workbook.buildRecalc();
		
	return true;
};
Woorksheet.prototype.insertRowsBefore=function(index, count){
	var oRange = this.getRange(new CellAddress(index, 0, 0), new CellAddress(index + count - 1, gc_nMaxCol0, 0));
	oRange.addCellsShiftBottom();
};
Woorksheet.prototype._insertRowsBefore=function(index, count){
	//до перемещения ячеек, перед функцией, в которой используются nodesSheetArea/nodesSheetCell move/shift нужно обязательно вызвать force buildRecalc
	//чтобы формулы, которые копим попали в струкруры nodesSheet, иначе формулы не сдвинутся
	//например принимаем изменения: добавление формул, сдвиг с помощью _removeRows. результат формулы указывают на теже ячейки, что и до _removeRows
  this.workbook.buildRecalc(true, true);
  this.workbook.lockDraw();
	var oActualRange = {r1: index, c1: 0, r2: index + count - 1, c2: gc_nMaxCol0};
	History.Create_NewPoint();
    this.renameDependencyNodes({offsetRow:count,offsetCol:0},oActualRange);
	History.Add(AscCommonExcel.g_oUndoRedoWorksheet, AscCH.historyitem_Worksheet_AddRows, this.getId(), new Asc.Range(0, index, gc_nMaxCol0, gc_nMaxRow0), new UndoRedoData_FromToRowCol(true, index, index + count - 1));
	//index 0 based
	var aIndexes = [];
	for(var i in this.aGCells)
	{
		var nIndex = i - 0;
		if(nIndex >= index)
			aIndexes.push(nIndex);
	}
    var oPrevRow = null;
    if(index > 0)
        oPrevRow = this.aGCells[index - 1];
	//По убыванию
	aIndexes.sort(fSortDescending);
	for(var i = 0, length = aIndexes.length; i < length; ++i)
	{
		var nIndex = aIndexes[i];
		var row = this.aGCells[nIndex];
		if(false == row.isEmpty())
		{
			var oTargetRow = this._getRow(nIndex + count);
			oTargetRow.copyProperty(row);
		}
		for(var j in row.c)
			this._moveCellVer(nIndex, j - 0, count);
        delete this.aGCells[nIndex];
	}
	if (null != oPrevRow && false == this.workbook.bUndoChanges && (false == this.workbook.bRedoChanges || true == this.workbook.bCollaborativeChanges))
	{
	    History.LocalChange = true;
        for(var i = 0; i < count; ++i)
        {
            var row = this._getRow(index + i);
            row.copyProperty(oPrevRow);
			row.flags &= ~AscCommonExcel.g_nRowFlag_hd;
        }
        History.LocalChange = false;
    }

    this.autoFilters.insertRows( "insCell", new Asc.Range(0, index, gc_nMaxCol0, index + count - 1), c_oAscInsertOptions.InsertColumns );

  this.workbook.unLockDraw();
  this.workbook.buildRecalc();
	
	this.nRowsCount += count;
	
	return true;
};
Woorksheet.prototype.insertRowsAfter=function(index, count){
	//index 0 based
	return this.insertRowsBefore(index + 1, count);
};
Woorksheet.prototype.getColsCount=function(){
	var result = this.nColsCount;
	var pane = this.sheetViews[0].pane;
	if (null !== pane && null !== pane.topLeftFrozenCell)
		result = Math.max(result, pane.topLeftFrozenCell.getCol0());
	return result;
};
Woorksheet.prototype.removeCols=function(start, stop){
	var oRange = this.getRange(new CellAddress(0, start, 0), new CellAddress(gc_nMaxRow0, stop, 0));
	oRange.deleteCellsShiftLeft();
};
Woorksheet.prototype._removeCols=function(start, stop){
	//до перемещения ячеек, перед функцией, в которой используются nodesSheetArea/nodesSheetCell move/shift нужно обязательно вызвать force buildRecalc
	//чтобы формулы, которые копим попали в струкруры nodesSheet, иначе формулы не сдвинутся
	//например принимаем изменения: добавление формул, сдвиг с помощью _removeRows. результат формулы указывают на теже ячейки, что и до _removeRows
  this.workbook.buildRecalc(true, true);
  this.workbook.lockDraw();
	History.Create_NewPoint();
	//start, stop 0 based
	var nDif = -(stop - start + 1), i, j, length, nIndex;
    var oActualRange = { r1: 0, c1: start, r2: gc_nMaxRow0, c2: stop };
    this.renameDependencyNodes({ offsetRow: 0, offsetCol: nDif }, oActualRange);
	for(i in this.aGCells)
	{
		var nRowIndex = i - 0;
		var row = this.aGCells[i];
		var aIndexes = [];
		for(j in row.c)
		{
			nIndex = j - 0;
			if(nIndex >= start)
				aIndexes.push(nIndex);
		}
		//сортируем по возрастанию
		aIndexes.sort(fSortAscending);
		for(j = 0, length = aIndexes.length; j < length; ++j)
		{
			nIndex = aIndexes[j];
			if(nIndex > stop)
			{
				this._moveCellHor(nRowIndex, nIndex, nDif, {r1: 0, c1: start, r2: gc_nMaxRow0, c2: stop});
			}
			else
			{
				//удаляем ячейку
				this._removeCell(nRowIndex, nIndex);
			}
		}
	}
	
	var oDefColPr = new AscCommonExcel.UndoRedoData_ColProp();
	for(i = start; i <= stop; ++i)
	{
		var col = this.aCols[i];
		if(null != col)
		{
			var oOldProps = col.getWidthProp();
			if(false === oOldProps.isEqual(oDefColPr))
				History.Add(AscCommonExcel.g_oUndoRedoWorksheet, AscCH.historyitem_Worksheet_ColProp, this.getId(), new Asc.Range(i, 0, i, gc_nMaxRow0), new UndoRedoData_IndexSimpleProp(i, false, oOldProps, oDefColPr));
			col.setStyle(null);
		}
	}
	this.aCols.splice(start, stop - start + 1);
	for(i = start, length = this.aCols.length; i < length; ++i)
	{
		var elem = this.aCols[i];
		if(null != elem)
			elem.moveHor(nDif);
	}


	History.Add(AscCommonExcel.g_oUndoRedoWorksheet, AscCH.historyitem_Worksheet_RemoveCols, this.getId(), new Asc.Range(start, 0, gc_nMaxCol0, gc_nMaxRow0), new UndoRedoData_FromToRowCol(false, start, stop));
	
	this.autoFilters.insertColumn( "delCell",  new Asc.Range(start, 0, stop, gc_nMaxRow0), c_oAscInsertOptions.InsertColumns );
  this.workbook.unLockDraw();
  this.workbook.buildRecalc();

	return true;
};
Woorksheet.prototype.insertColsBefore=function(index, count){
	var oRange = this.getRange3(0, index, gc_nMaxRow0, index + count - 1);
	oRange.addCellsShiftRight();
};
Woorksheet.prototype._insertColsBefore=function(index, count){
	//до перемещения ячеек, перед функцией, в которой используются nodesSheetArea/nodesSheetCell move/shift нужно обязательно вызвать force buildRecalc
	//чтобы формулы, которые копим попали в струкруры nodesSheet, иначе формулы не сдвинутся
	//например принимаем изменения: добавление формул, сдвиг с помощью _removeRows. результат формулы указывают на теже ячейки, что и до _removeRows
  this.workbook.buildRecalc(true, true);
  this.workbook.lockDraw();
	var oActualRange = {r1: 0, c1: index, r2: gc_nMaxRow0, c2: index + count - 1};
	History.Create_NewPoint();
    this.renameDependencyNodes({offsetRow:0,offsetCol:count},oActualRange);
    History.Add(AscCommonExcel.g_oUndoRedoWorksheet, AscCH.historyitem_Worksheet_AddCols, this.getId(), new Asc.Range(index, 0, gc_nMaxCol0, gc_nMaxRow0), new UndoRedoData_FromToRowCol(false, index, index + count - 1));
	//index 0 based
	for(var i in this.aGCells)
	{
		var nRowIndex = i - 0;
		var row = this.aGCells[i];
		var aIndexes = [];
		for(var j in row.c)
		{
			var nIndex = j - 0;
			if(nIndex >= index)
				aIndexes.push(nIndex);
		}
		//сортируем по убыванию
		aIndexes.sort(fSortDescending);
		for(var j = 0, length2 = aIndexes.length; j < length2; ++j)
		{
			var nIndex = aIndexes[j];
			this._moveCellHor(nRowIndex, nIndex, count, oActualRange);
		}
	}
	
	this.autoFilters.insertColumn( "insCells",  new Asc.Range(index, 0, index + count - 1, gc_nMaxRow0), c_oAscInsertOptions.InsertColumns );

  this.workbook.unLockDraw();
  this.workbook.buildRecalc();
	
    var oPrevCol = null;
    if(index > 0)
        oPrevCol = this.aCols[index - 1];
	if(null == oPrevCol && null != this.oAllCol)
		oPrevCol = this.oAllCol;
	for(var i = 0; i < count; ++i)
    {
        var oNewCol = null;
        if (null != oPrevCol && false == this.workbook.bUndoChanges && (false == this.workbook.bRedoChanges || true == this.workbook.bCollaborativeChanges))
        {
           History.LocalChange = true;
           oNewCol = oPrevCol.clone();
		   oNewCol.hd = null;
           oNewCol.BestFit = null;
           oNewCol.index = index + i; 
           History.LocalChange = false;
        }
		this.aCols.splice(index, 0, oNewCol);
    }
	for(var i = index + count, length = this.aCols.length; i < length; ++i)
	{
		var elem = this.aCols[i];
		if(null != elem)
			elem.moveHor(count);
	}
	this.nColsCount += count;

	return true;
};
Woorksheet.prototype.insertColsAfter=function(index, count){
	//index 0 based
	return this.insertColsBefore(index + 1, count);
};
Woorksheet.prototype.getDefaultWidth=function(){
	return this.oSheetFormatPr.dDefaultColWidth;
};
Woorksheet.prototype.getDefaultFontName=function(){
	return this.workbook.getDefaultFont();
};
Woorksheet.prototype.getDefaultFontSize=function(){
	return this.workbook.getDefaultSize();
};
Woorksheet.prototype.charCountToModelColWidth = function (count) {
	return this.workbook.charCountToModelColWidth(count);
};
Woorksheet.prototype.getColWidth=function(index){
	//index 0 based
	//Результат в пунктах
	var col = this._getColNoEmptyWithAll(index);
	if(null != col && null != col.width)
		return col.width;
	var dResult = this.oSheetFormatPr.dDefaultColWidth;
	if(dResult === undefined || dResult === null || dResult == 0)
		//dResult = (8) + 5;//(EMCA-376.page 1857.)defaultColWidth = baseColumnWidth + {margin padding (2 pixels on each side, totalling 4 pixels)} + {gridline (1pixel)}
		dResult = -1; // calc default width at presentation level
	return dResult;
};
Woorksheet.prototype.setColWidth=function(width, start, stop){
	width = this.charCountToModelColWidth(width);
	if(0 == width)
		return this.setColHidden(true, start, stop);

	//start, stop 0 based
	if(null == start)
		return;
	if(null == stop)
		stop = start;
	History.Create_NewPoint();
	var oSelection = History.GetSelection();
	if(null != oSelection)
	{
		oSelection = oSelection.clone();
		oSelection.assign(start, 0, stop, gc_nMaxRow0);
		oSelection.type = Asc.c_oAscSelectionType.RangeCol;
		History.SetSelection(oSelection);
		History.SetSelectionRedo(oSelection);
	}
	var oThis = this;
	var fProcessCol = function(col){
		if(col.width != width)
		{
			var oOldProps = col.getWidthProp();
			col.width = width;
			col.CustomWidth = true;
			col.BestFit = null;
			col.hd = null;
			var oNewProps = col.getWidthProp();
			if(false == oOldProps.isEqual(oNewProps))
				History.Add(AscCommonExcel.g_oUndoRedoWorksheet, AscCH.historyitem_Worksheet_ColProp, oThis.getId(),
					col._getUpdateRange(),
					new UndoRedoData_IndexSimpleProp(col.index, false, oOldProps, oNewProps));
		}
	};
	if(0 == start && gc_nMaxCol0 == stop)
	{
		var col = this.getAllCol();
		fProcessCol(col);
		for(var i in this.aCols){
		    var col = this.aCols[i];
		    if (null != col)
			    fProcessCol(col);
		}
	}
	else
	{
		for(var i = start; i <= stop; i++){
			var col = this._getCol(i);
			fProcessCol(col);
		}
	}
};
Woorksheet.prototype.getColHidden=function(index){
	var col = this._getColNoEmptyWithAll(index);
	return col ? col.hd : false;
};
Woorksheet.prototype.setColHidden=function(bHidden, start, stop){
	//start, stop 0 based
	if(null == start)
		return;
	if(null == stop)
		stop = start;
	History.Create_NewPoint();
	var oThis = this;
	var fProcessCol = function(col){
		if(col.hd != bHidden)
		{
			var oOldProps = col.getWidthProp();
			if(bHidden)
			{
				col.hd = bHidden;
				if(null == col.width || true != col.CustomWidth)
					col.width = 0;
				col.CustomWidth = true;
				col.BestFit = null;
			}
			else
			{
				col.hd = null;
				if(0 == col.width)
					col.width = null;
			}
			var oNewProps = col.getWidthProp();
			if(false == oOldProps.isEqual(oNewProps))
				History.Add(AscCommonExcel.g_oUndoRedoWorksheet, AscCH.historyitem_Worksheet_ColProp, oThis.getId(),
					col._getUpdateRange(),
					new UndoRedoData_IndexSimpleProp(col.index, false, oOldProps, oNewProps));
		}
	};
	if(0 != start && gc_nMaxCol0 == stop)
	{
		var col = null;
		if(false == bHidden)
			col = this.oAllCol;
		else
			col = this.getAllCol();
		if(null != col)
			fProcessCol(col);
		for(var i in this.aCols){
		    var col = this.aCols[i];
		    if (null != col)
			    fProcessCol(col);
		}
	}
	else
	{
		for(var i = start; i <= stop; i++){
			var col = null;
			if(false == bHidden)
				col = this._getColNoEmpty(i);
			else
				col = this._getCol(i);
			if(null != col)
				fProcessCol(col);
		}
	}
};
Woorksheet.prototype.setColBestFit=function(bBestFit, width, start, stop){
	//start, stop 0 based
	if(null == start)
		return;
	if(null == stop)
		stop = start;
	History.Create_NewPoint();
	var oThis = this;
	var fProcessCol = function(col){
		var oOldProps = col.getWidthProp();
		if(bBestFit)
		{
			col.BestFit = bBestFit;
			col.hd = null;
		}
		else
			col.BestFit = null;
		col.width = width;
		var oNewProps = col.getWidthProp();
		if(false == oOldProps.isEqual(oNewProps))
			History.Add(AscCommonExcel.g_oUndoRedoWorksheet, AscCH.historyitem_Worksheet_ColProp, oThis.getId(),
				col._getUpdateRange(),
				new UndoRedoData_IndexSimpleProp(col.index, false, oOldProps, oNewProps));
	};
	if(0 != start && gc_nMaxCol0 == stop)
	{
		var col = null;
		if(bBestFit && oDefaultMetrics.ColWidthChars == width)
			col = this.oAllCol;
		else
			col = this.getAllCol();
		if(null != col)
			fProcessCol(col);
		for(var i in this.aCols){
		    var col = this.aCols[i];
		    if (null != col)
			    fProcessCol(col);
		}
	}
	else
	{
		for(var i = start; i <= stop; i++){
			var col = null;
			if(bBestFit && oDefaultMetrics.ColWidthChars == width)
				col = this._getColNoEmpty(i);
			else
				col = this._getCol(i);
			if(null != col)
				fProcessCol(col);
		}
	}
};
Woorksheet.prototype.isDefaultHeightHidden=function(){
	return null != this.oSheetFormatPr.oAllRow && 0 != (AscCommonExcel.g_nRowFlag_hd & this.oSheetFormatPr.oAllRow.flags);
};
Woorksheet.prototype.isDefaultWidthHidden=function(){
	return null != this.oAllCol && this.oAllCol.hd;
};
Woorksheet.prototype.getDefaultHeight=function(){
    // ToDo http://bugzilla.onlyoffice.com/show_bug.cgi?id=19666 (флага CustomHeight нет)
	var dRes = null;
	// Нужно возвращать выставленную, только если флаг CustomHeight = true
	if(null != this.oSheetFormatPr.oAllRow && 0 != (AscCommonExcel.g_nRowFlag_CustomHeight & this.oSheetFormatPr.oAllRow.flags))
		dRes = this.oSheetFormatPr.oAllRow.h;
	return dRes;
};
Woorksheet.prototype.getRowHeight=function(index){
	//index 0 based
	var row = this.aGCells[index];
	if(null != row && null != row.h)
		return row.h;
	else
		return -1;
};
Woorksheet.prototype.setRowHeight=function(height, start, stop, isCustom){
	if(0 == height)
		return this.setRowHidden(true, start, stop);
	//start, stop 0 based
	if(null == start)
		return;
	if(null == stop)
		stop = start;
	History.Create_NewPoint();
	var oThis = this, i;
	var oSelection = History.GetSelection();
	if(null != oSelection)
	{
		oSelection = oSelection.clone();
		oSelection.assign(0, start, gc_nMaxCol0, stop);
		oSelection.type = Asc.c_oAscSelectionType.RangeRow;
		History.SetSelection(oSelection);
		History.SetSelectionRedo(oSelection);
	}
	var fProcessRow = function(row){
		if(row)
		{
			var oOldProps = row.getHeightProp();
			row.h = height;
            if (isCustom) {
              row.flags |= AscCommonExcel.g_nRowFlag_CustomHeight;
            }
			row.flags &= ~AscCommonExcel.g_nRowFlag_hd;
			var oNewProps = row.getHeightProp();
			if(false === oOldProps.isEqual(oNewProps))
			    History.Add(AscCommonExcel.g_oUndoRedoWorksheet, AscCH.historyitem_Worksheet_RowProp, oThis.getId(), row._getUpdateRange(), new UndoRedoData_IndexSimpleProp(row.index, true, oOldProps, oNewProps));
		}
	};
	if(0 == start && gc_nMaxRow0 == stop)
	{
		fProcessRow(this.getAllRow());
		for(i in this.aGCells){
			fProcessRow(this.aGCells[i]);
		}
	}
	else
	{
		for(i = start; i <= stop; ++i){
			fProcessRow(this._getRow(i));
		}
	}
};
Woorksheet.prototype.getRowHidden=function(index){
	var row = this._getRowNoEmptyWithAll(index);
	return row ? 0 != (AscCommonExcel.g_nRowFlag_hd & row.flags) : false;
};
Woorksheet.prototype.setRowHidden=function(bHidden, start, stop){
	//start, stop 0 based
	if(null == start)
		return;
	if(null == stop)
		stop = start;
	History.Create_NewPoint();
	var oThis = this, i;
	var startIndex = null, endIndex = null, updateRange;
	
	var fProcessRow = function(row){
		if(row && bHidden != (0 != (AscCommonExcel.g_nRowFlag_hd & row.flags)))
		{
			if(bHidden)
				row.flags |= AscCommonExcel.g_nRowFlag_hd;
			else
				row.flags &= ~AscCommonExcel.g_nRowFlag_hd;
			
			
			if(row.index === endIndex + 1 && startIndex !== null)
				endIndex++;
			else
			{
				if(startIndex !== null)
				{
					updateRange = new Asc.Range(0, startIndex, gc_nMaxCol0, endIndex);
					History.Add(AscCommonExcel.g_oUndoRedoWorksheet, AscCH.historyitem_Worksheet_RowHide, oThis.getId(), updateRange, new UndoRedoData_FromToRowCol(bHidden, startIndex, endIndex));
				}	
				
				startIndex = row.index;
				endIndex = row.index;
			}
		}
	};
	if(0 == start && gc_nMaxRow0 == stop)
	{
        // ToDo реализовать скрытие всех строк!
	}
	else
	{
		for(i = start; i <= stop; ++i)
			fProcessRow(false == bHidden ? this._getRowNoEmpty(i) : this._getRow(i));
		
		if(startIndex !== null)//заносим последние строки
		{
			updateRange = new Asc.Range(0, startIndex, gc_nMaxCol0, endIndex);
			History.Add(AscCommonExcel.g_oUndoRedoWorksheet, AscCH.historyitem_Worksheet_RowHide, oThis.getId(),updateRange, new UndoRedoData_FromToRowCol(bHidden, startIndex, endIndex));
		}
	}
};
Woorksheet.prototype.setRowBestFit=function(bBestFit, height, start, stop){
	//start, stop 0 based
	if(null == start)
		return;
	if(null == stop)
		stop = start;
	History.Create_NewPoint();
	var oThis = this, i;
    var isDefaultProp = (true == bBestFit && oDefaultMetrics.RowHeight == height);
	var fProcessRow = function(row){
		if(row)
		{
			var oOldProps = row.getHeightProp();
			if(true == bBestFit)
				row.flags &= ~AscCommonExcel.g_nRowFlag_CustomHeight;
			else
				row.flags |= AscCommonExcel.g_nRowFlag_CustomHeight;
			row.h = height;
			var oNewProps = row.getHeightProp();
			if(false == oOldProps.isEqual(oNewProps))
			    History.Add(AscCommonExcel.g_oUndoRedoWorksheet, AscCH.historyitem_Worksheet_RowProp, oThis.getId(), row._getUpdateRange(), new UndoRedoData_IndexSimpleProp(row.index, true, oOldProps, oNewProps));
		}
	};
	if(0 == start && gc_nMaxRow0 == stop)
	{
		fProcessRow(isDefaultProp ? this.oSheetFormatPr.oAllRow : this.getAllRow());
		for(i in this.aGCells)
			fProcessRow(this.aGCells[i]);
	}
	for(i = start; i <= stop; ++i)
		fProcessRow(isDefaultProp ? this._getRowNoEmpty(i) : this._getRow(i));
};
Woorksheet.prototype.getCell=function(oCellAdd){
	return this.getRange(oCellAdd, oCellAdd);
};
Woorksheet.prototype.getCell2=function(sCellAdd){
	if( sCellAdd.indexOf("$") > -1)
		sCellAdd = sCellAdd.replace(/\$/g,"");
	return this.getRange2(sCellAdd);
};
Woorksheet.prototype.getCell3=function(r1, c1){
	return this.getRange3(r1, c1, r1, c1);
};
Woorksheet.prototype.getRange=function(cellAdd1, cellAdd2){
	//Если range находится за границами ячеек расширяем их
	var nRow1 = cellAdd1.getRow0();
	var nCol1 = cellAdd1.getCol0();
	var nRow2 = cellAdd2.getRow0();
	var nCol2 = cellAdd2.getCol0();
	return this.getRange3(nRow1, nCol1, nRow2, nCol2);
};
Woorksheet.prototype.getRange2=function(sRange){
	var bbox = AscCommonExcel.g_oRangeCache.getAscRange(sRange);
	if(null != bbox)
		return Range.prototype.createFromBBox(this, bbox);
	return null;
};
Woorksheet.prototype.getRange3=function(r1, c1, r2, c2){
	var nRowMin = r1;
	var nRowMax = r2;
	var nColMin = c1;
	var nColMax = c2;
	if(r1 > r2){
		nRowMax = r1;
		nRowMin = r2;
	}
	if(c1 > c2){
		nColMax = c1;
		nColMin = c2;
	}
	return new Range(this, nRowMin, nColMin, nRowMax, nColMax);
};
Woorksheet.prototype._getRows=function(){
	return this.aGCells;
};
Woorksheet.prototype._getCols=function(){
	return this.aCols;
};
Woorksheet.prototype._removeCell=function(nRow, nCol, cell){
	if(null != cell)
	{
		nRow = cell.nRow;
		nCol = cell.nCol;
	}
	var row = this.aGCells[nRow];
	if(null != row)
	{
		var cell = row.c[nCol];
		if(null != cell)
		{
		    var sheetId = this.getId();
		    var cellId = cell.getName();
			if(cell.formulaParsed)
			    this.workbook.dependencyFormulas.deleteMasterNodes2(sheetId, cellId);
			this.workbook.needRecalc.nodes[getVertexId(sheetId, cellId)] = [sheetId, cellId];
			this.workbook.needRecalc.length++;
			
			if (null != cell.getConditionalFormattingStyle() || null != cell.getTableStyle()) {
			    cell.setValue("");
			    cell.setStyle(null);
			}
			else {
			    if (false == cell.isEmpty()) {
			        var oUndoRedoData_CellData = new AscCommonExcel.UndoRedoData_CellData(cell.getValueData(), null);
			        if (null != cell.xfs)
			            oUndoRedoData_CellData.style = cell.xfs.clone();
			        History.Add(AscCommonExcel.g_oUndoRedoWorksheet, AscCH.historyitem_Worksheet_RemoveCell, sheetId, new Asc.Range(nCol, nRow, nCol, nRow), new UndoRedoData_CellSimpleData(nRow, nCol, oUndoRedoData_CellData, null));
			    }
			    delete row.c[nCol];
			    if (row.isEmpty())
			        delete this.aGCells[nRow];
			}
		}
	}
};
Woorksheet.prototype._getCell=function(row, col){
	//0-based
	var oCurRow = this._getRow(row);
	var oCurCell = oCurRow.c[col];
	if(null == oCurCell){
		oCurCell = new Cell(this);
		var oRow = this._getRowNoEmpty(row);
		var oCol = this._getColNoEmptyWithAll(col);
		var xfs = null;
		if(null != oRow && null != oRow.xfs)
			xfs = oRow.xfs.clone();
		else if(null != oCol && null != oCol.xfs)
			xfs = oCol.xfs.clone();
		oCurCell.create(xfs, row, col);
		oCurRow.c[col] = oCurCell;
		if(row >= this.nRowsCount)
			this.nRowsCount = row + 1;
		if(col >= this.nColsCount)
			this.nColsCount = col + 1;
		//History.Add(AscCommonExcel.g_oUndoRedoWorksheet, AscCH.historyitem_Worksheet_CreateCell, this.getId(), null, new UndoRedoData_CellSimpleData(row, col, null, null));
	}
	return oCurCell;
};
Woorksheet.prototype._getCellNoEmpty=function(row, col){
	//0-based
	var oCurRow = this.aGCells[row];
	if(oCurRow)
	{
		var cell = oCurRow.c[col];
		return cell ? cell : null;
	}
	return null;
};
Woorksheet.prototype._getRowNoEmpty=function(row){
	//0-based
	var oCurRow = this.aGCells[row];
	if(oCurRow)
		return oCurRow;
	return null;
};
Woorksheet.prototype._getRowNoEmptyWithAll=function(row){
	var oRes = this._getRowNoEmpty(row);
	if(null == oRes)
		oRes = this.oSheetFormatPr.oAllRow;
	return oRes;
};
Woorksheet.prototype._getColNoEmpty=function(col){
	//0-based
	var oCurCol = this.aCols[col];
	if(oCurCol)
		return oCurCol;
	return null;
};
Woorksheet.prototype._getColNoEmptyWithAll=function(col){
	var oRes = this._getColNoEmpty(col);
	if(null == oRes)
		oRes = this.oAllCol;
	return oRes;
};
Woorksheet.prototype._getRow=function(row){
	//0-based
    var oCurRow = null;
    if (g_nAllRowIndex == row)
        oCurRow = this.getAllRow();
    else {
        oCurRow = this.aGCells[row];
        if (!oCurRow) {
            if (null != this.oSheetFormatPr.oAllRow)
                oCurRow = this.oSheetFormatPr.oAllRow.clone(this);
            else
                oCurRow = new AscCommonExcel.Row(this);
            oCurRow.create(row + 1);
            this.aGCells[row] = oCurRow;
            this.nRowsCount = row >= this.nRowsCount ? row + 1 : this.nRowsCount;
            //History.Add(AscCommonExcel.g_oUndoRedoWorksheet, AscCH.historyitem_Worksheet_CreateRow, this.getId(), null, new UndoRedoData_SingleProperty(row));
        }
    }
	return oCurRow;
};
Woorksheet.prototype._removeRow=function(index){
	delete this.aGCells[index];
};
Woorksheet.prototype._getCol=function(index){
	//0-based
	var oCurCol;
	if (g_nAllColIndex == index)
		oCurCol = this.getAllCol();
	else
	{
		oCurCol = this.aCols[index];
		if(null == oCurCol)
		{
			if(null != this.oAllCol)
			{
				oCurCol = this.oAllCol.clone();
				oCurCol.index = index;
			}
			else
				oCurCol = new AscCommonExcel.Col(this, index);
			this.aCols[index] = oCurCol;
			this.nColsCount = index >= this.nColsCount ? index + 1 : this.nColsCount;
			//History.Add(AscCommonExcel.g_oUndoRedoWorksheet, AscCH.historyitem_Worksheet_CreateCol, this.getId(), null, new UndoRedoData_SingleProperty(index));
		}
	}
	return oCurCol;
};
Woorksheet.prototype._removeCol=function(index){
	//0-based
	delete this.aCols[index];
};
Woorksheet.prototype._moveCellHor=function(nRow, nCol, dif){
	var cell = this._getCellNoEmpty(nRow, nCol);
	if(cell)
	{
		cell.moveHor(dif);
		var row = this._getRow(nRow);
		row.c[nCol + dif] = cell;
		delete row.c[nCol];
	}
};
Woorksheet.prototype._moveCellVer=function(nRow, nCol, dif){
	var cell = this._getCellNoEmpty(nRow, nCol);
	if(cell)
	{
		cell.moveVer(dif);
		var oCurRow = this._getRow(nRow);
		var oTargetRow = this._getRow(nRow + dif);
		delete oCurRow.c[nCol];
		oTargetRow.c[nCol] = cell;
		if(oCurRow.isEmpty())
			delete this.aGCells[nRow];
	}
};
Woorksheet.prototype._prepareMoveRangeGetCleanRanges=function(oBBoxFrom, oBBoxTo){
	var intersection = oBBoxFrom.intersectionSimple(oBBoxTo);
	var aRangesToCheck = [];
	if(null != intersection)
	{
		var oThis = this;
		var fAddToRangesToCheck = function(aRangesToCheck, r1, c1, r2, c2)
		{
			if(r1 <= r2 && c1 <= c2)
				aRangesToCheck.push(oThis.getRange3(r1, c1, r2, c2));
		};
		if(intersection.r1 == oBBoxTo.r1 && intersection.c1 == oBBoxTo.c1)
		{
			fAddToRangesToCheck(aRangesToCheck, oBBoxTo.r1, intersection.c2 + 1, intersection.r2, oBBoxTo.c2);
			fAddToRangesToCheck(aRangesToCheck, intersection.r2 + 1, oBBoxTo.c1, oBBoxTo.r2, oBBoxTo.c2);
		}
		else if(intersection.r2 == oBBoxTo.r2 && intersection.c1 == oBBoxTo.c1)
		{
			fAddToRangesToCheck(aRangesToCheck, oBBoxTo.r1, oBBoxTo.c1, intersection.r1 - 1, oBBoxTo.c2);
			fAddToRangesToCheck(aRangesToCheck, intersection.r1, intersection.c2 + 1, oBBoxTo.r2, oBBoxTo.c2);
		}
		else if(intersection.r1 == oBBoxTo.r1 && intersection.c2 == oBBoxTo.c2)
		{
			fAddToRangesToCheck(aRangesToCheck, oBBoxTo.r1, oBBoxTo.c1, intersection.r2, intersection.c1 - 1);
			fAddToRangesToCheck(aRangesToCheck, intersection.r2 + 1, oBBoxTo.c1, oBBoxTo.r2, oBBoxTo.c2);
		}
		else if(intersection.r2 == oBBoxTo.r2 && intersection.c2 == oBBoxTo.c2)
		{
			fAddToRangesToCheck(aRangesToCheck, oBBoxTo.r1, oBBoxTo.c1, intersection.r1 - 1, oBBoxTo.c2);
			fAddToRangesToCheck(aRangesToCheck, intersection.r1, oBBoxTo.c1, oBBoxTo.r2, intersection.c1 - 1);
		}
	}
	else
		aRangesToCheck.push(this.getRange3(oBBoxTo.r1, oBBoxTo.c1, oBBoxTo.r2, oBBoxTo.c2));
	return aRangesToCheck;
};
Woorksheet.prototype._prepareMoveRange=function(oBBoxFrom, oBBoxTo){
	var res = 0;
	if(oBBoxFrom.isEqual(oBBoxTo))
		return res;
	var range = this.getRange3(oBBoxTo.r1, oBBoxTo.c1, oBBoxTo.r2, oBBoxTo.c2);
	var aMerged = this.mergeManager.get(range.getBBox0());
	if(aMerged.outer.length > 0)
		return -2;
	var aRangesToCheck = this._prepareMoveRangeGetCleanRanges(oBBoxFrom, oBBoxTo);
	for(var i = 0, length = aRangesToCheck.length; i < length; i++)
	{
		range = aRangesToCheck[i];
		range._foreachNoEmpty(
			function(cell){
				if(!cell.isEmptyTextString())
				{
					res = -1;
					return res;
				}
			});
		if(0 != res)
			return res;
	}
	return res;
};
Woorksheet.prototype._moveRange=function(oBBoxFrom, oBBoxTo, copyRange){
	if(oBBoxFrom.isEqual(oBBoxTo))
		return;
	var oThis = this;
	History.Create_NewPoint();
	History.StartTransaction();
	
	var offset = { offsetRow : oBBoxTo.r1 - oBBoxFrom.r1, offsetCol : oBBoxTo.c1 - oBBoxFrom.c1 };
	var intersection = oBBoxFrom.intersectionSimple(oBBoxTo);
	var oRangeIntersection = null;
	if(null != intersection)
		oRangeIntersection = this.getRange3(intersection.r1, intersection.c1, intersection.r2, intersection.c2 );
	//запоминаем то что нужно переместить
	var aTempObj = {cells: {}, merged: null, hyperlinks: null};
	for(var i = oBBoxFrom.r1; i <= oBBoxFrom.r2; i++)
	{
		var row = this._getRowNoEmpty(i);
		if(null != row)
		{
			var oTempRow = {};
			aTempObj.cells[i + offset.offsetRow] = oTempRow;
			for(var j = oBBoxFrom.c1; j <= oBBoxFrom.c2; j++)
			{
				var cell = row.c[j];
				if(null != cell){
                    if(copyRange)
					    oTempRow[j + offset.offsetCol] = cell.clone();
                    else
					{
						cell.setTableStyle(null);
						oTempRow[j + offset.offsetCol] = cell;
					}
                }
			}
		}
	}
	if(false == this.workbook.bUndoChanges && (false == this.workbook.bRedoChanges || true == this.workbook.bCollaborativeChanges))
	{
	    History.LocalChange = true;
		var aMerged = this.mergeManager.get(oBBoxFrom);
		if(aMerged.inner.length > 0)
			aTempObj.merged = aMerged.inner;
		var aHyperlinks = this.hyperlinkManager.get(oBBoxFrom);
		if(aHyperlinks.inner.length > 0)
			aTempObj.hyperlinks = aHyperlinks.inner;
		var aMergedToRemove = null;
		if(!copyRange){
			aMergedToRemove = aTempObj.merged;
		}
		else if(null != intersection){
			var aMergedIntersection = this.mergeManager.get(intersection);
			if(aMergedIntersection.all.length > 0)
				aMergedToRemove = aMergedIntersection.all;
		}
		if(null != aMergedToRemove){
			for(var i = 0, length = aMergedToRemove.length; i < length; i++)
			{
				var elem = aMergedToRemove[i];
				this.mergeManager.removeElement(elem);
			}
		}
		if(!copyRange){
			if(null != aTempObj.hyperlinks)
			{
				for(var i = 0, length = aTempObj.hyperlinks.length; i < length; i++)
				{
					var elem = aTempObj.hyperlinks[i];
					this.hyperlinkManager.removeElement(elem);
				}
			}
		}
		History.LocalChange = false;
	}
	//удаляем to через историю, для undo
	var aRangesToCheck = this._prepareMoveRangeGetCleanRanges(oBBoxFrom, oBBoxTo);
	for (var i = 0, length = aRangesToCheck.length; i < length; i++) {
	    var range = aRangesToCheck[i];
	    range.cleanAll();
	    //выставляем для slave refError
	    if (!copyRange)
	        this.workbook.dependencyFormulas.deleteNodes(this.getId(), range.getBBox0());
	}
	//удаляем from без истории, потому что эти данные не терются а перемещаются
    if(!copyRange || (copyRange && this.workbook.bUndoChanges)){
		var oRangeFrom = this.getRange3(oBBoxFrom.r1, oBBoxFrom.c1, oBBoxFrom.r2, oBBoxFrom.c2 );
        oRangeFrom._setPropertyNoEmpty(null, null, function(cell, nRow0, nCol0, nRowStart, nColStart){
            var row = oThis._getRowNoEmpty(nRow0);
            if(null != row)
                delete row.c[nCol0];
        });
    }
	else{
		//в случае копирования удаляем пересечение, чтобы затирались значения, если мы копируем пустые ячейки(все что не входит в пересечение удалилось выше через историю)
		if(null != intersection){
			oRangeIntersection._setPropertyNoEmpty(null, null, function(cell, nRow0, nCol0, nRowStart, nColStart){
				var row = oThis._getRowNoEmpty(nRow0);
				if(null != row)
					delete row.c[nCol0];
			});
		}
	}
    if(!copyRange){
		//до перемещения ячеек, перед функцией, в которой используются nodesSheetArea/nodesSheetCell move/shift нужно обязательно вызвать force buildRecalc
		//чтобы формулы, которые копим попали в струкруры nodesSheet, иначе формулы не сдвинутся
		//например принимаем изменения: добавление формул, сдвиг с помощью _removeRows. результат формулы указывают на теже ячейки, что и до _removeRows
      this.workbook.buildRecalc(true, true);
        this.workbook.dependencyFormulas.helper(oBBoxFrom, oBBoxTo,this.Id);
    }

    for ( var i in aTempObj.cells ) {
        var oTempRow = aTempObj.cells[i];
        var row = this._getRow( i - 0 );
        for ( var j in oTempRow ) {
            var oTempCell = oTempRow[j];
            if ( null != oTempCell ) {
                oTempCell.moveHor( offset.offsetCol );
                oTempCell.moveVer( offset.offsetRow );
                row.c[j] = oTempCell;

                if ( oTempCell.sFormula ) {
                    if(copyRange){
                        oTempCell.formulaParsed = new parserFormula( oTempCell.sFormula, g_oCellAddressUtils.getCellId(oTempCell.nRow, oTempCell.nCol), this );
                        oTempCell.formulaParsed.parse();
                        oTempCell.formulaParsed = oTempCell.formulaParsed.changeOffset(offset);
                        oTempCell.sFormula = oTempCell.formulaParsed.assemble();
						
						addToArrRecalc(this.getId(), oTempCell);
                    }
                }
            }
        }
    }

    if(false == this.workbook.bUndoChanges && false == this.workbook.bRedoChanges)
        this.autoFilters._moveAutoFilters( oBBoxTo, oBBoxFrom, null, copyRange, true, oBBoxFrom );

	if(false == this.workbook.bUndoChanges && (false == this.workbook.bRedoChanges || true == this.workbook.bCollaborativeChanges))
	{
	    History.LocalChange = true;
		if(null != aTempObj.merged)
		{
			for(var i = 0, length = aTempObj.merged.length; i < length; i++)
			{
				var elem = aTempObj.merged[i];
				var oNewBBox;
				var oNewData = elem.data;
				if(copyRange)
					oNewBBox = elem.bbox.clone();
				else
					oNewBBox = elem.bbox;
				oNewBBox.setOffset(offset);
				this.mergeManager.add(oNewBBox, oNewData);
			}
		}
		//todo сделать для пересечения
		if(null != aTempObj.hyperlinks && (!copyRange || null == intersection))
		{
			for(var i = 0, length = aTempObj.hyperlinks.length; i < length; i++)
			{
				var elem = aTempObj.hyperlinks[i];
				var oNewBBox;
				var oNewData;
				if(copyRange){
					oNewBBox = elem.bbox.clone();
					oNewData = elem.data.clone();
				}
				else{
					oNewBBox = elem.bbox;
					oNewData = elem.data;
				}
				oNewBBox.setOffset(offset);
				this.hyperlinkManager.add(oNewBBox, oNewData);
			}
		}
		History.LocalChange = false;
	}
	//расширяем границы
	if(oBBoxFrom.r2 >= this.nRowsCount)
		this.nRowsCount = oBBoxFrom.r2 + 1;
	if(oBBoxFrom.c2 >= this.nColsCount)
		this.nColsCount = oBBoxFrom.c2 + 1;
	if(oBBoxTo.r2 >= this.nRowsCount)
		this.nRowsCount = oBBoxTo.r2 + 1;
	if(oBBoxTo.c2 >= this.nColsCount)
		this.nColsCount = oBBoxTo.c2 + 1;

	if(!copyRange){
		var sBBoxFromName = oBBoxFrom.getName();
		this.workbook.needRecalc.nodes[getVertexId(this.getId(), sBBoxFromName)] = [this.getId(), sBBoxFromName];
		this.workbook.needRecalc.length++;
	}
	var sBBoxToName = oBBoxTo.getName();
	this.workbook.needRecalc.nodes[getVertexId(this.getId(), sBBoxToName)] = [this.getId(), sBBoxToName];
	this.workbook.needRecalc.length++;
	if (!this.workbook.lockCounter){
		/*
			Если необходим пересчет, то по списку пересчитываемых ячеек сортируем граф зависимостей и пересчиываем в получившемся порядке. Плохим ячейкам с цикличискими ссылками выставляем ошибку "#REF!".
		*/
    this.workbook.sortDependency();
	}
	
	if(true == this.workbook.bUndoChanges || true == this.workbook.bRedoChanges)
	{
		this.autoFilters.unmergeTablesAfterMove( oBBoxTo );
	}
	
	// ToDo возможно нужно уменьшить диапазон обновления
    History.Add(AscCommonExcel.g_oUndoRedoWorksheet, AscCH.historyitem_Worksheet_MoveRange,
				this.getId(), new Asc.Range(0, 0, gc_nMaxCol0, gc_nMaxRow0),
				new UndoRedoData_FromTo(new UndoRedoData_BBox(oBBoxFrom), new UndoRedoData_BBox(oBBoxTo), copyRange));
	History.EndTransaction();
	return true;
};
Woorksheet.prototype._shiftCellsLeft=function(oBBox){
	//до перемещения ячеек, перед функцией, в которой используются nodesSheetArea/nodesSheetCell move/shift нужно обязательно вызвать force buildRecalc
	//чтобы формулы, которые копим попали в струкруры nodesSheet, иначе формулы не сдвинутся
	//например принимаем изменения: добавление формул, сдвиг с помощью _removeRows. результат формулы указывают на теже ячейки, что и до _removeRows
  this.workbook.buildRecalc(true, true);
	//todo удаление когда есть замерженые ячейки
	var nLeft = oBBox.c1;
	var nRight = oBBox.c2;
	var dif = nLeft - nRight - 1;
    this.renameDependencyNodes( {offsetRow:0,offsetCol:dif}, oBBox );
	for(var i = oBBox.r1; i <= oBBox.r2; i++){
		var row = this.aGCells[i];
		if(row){
			var aIndexes = [];
			for(var cellInd in row.c)
			{
				var nIndex = cellInd - 0;
				if(nIndex >= nLeft)
					aIndexes.push(nIndex);
			}
			//По возрастанию
			aIndexes.sort(fSortAscending);
			for(var j = 0, length2 = aIndexes.length; j < length2; ++j){
				var nCellInd = aIndexes[j];
				if(nCellInd <= nRight){
					//Удаляем ячейки
					this._removeCell(i, nCellInd);
				}
				else{
					//Сдвигаем ячейки
					this._moveCellHor(i, nCellInd, dif, oBBox);
				}
			}
		}
	}

	History.Add(AscCommonExcel.g_oUndoRedoWorksheet, AscCH.historyitem_Worksheet_ShiftCellsLeft, this.getId(), new Asc.Range(nLeft, oBBox.r1, gc_nMaxCol0, oBBox.r2), new UndoRedoData_BBox(oBBox));
    this.autoFilters.insertColumn( "delCell",  oBBox, c_oAscDeleteOptions.DeleteCellsAndShiftLeft );
	//todo проверить не уменьшились ли границы таблицы
};
Woorksheet.prototype._shiftCellsUp=function(oBBox){
	//до перемещения ячеек, перед функцией, в которой используются nodesSheetArea/nodesSheetCell move/shift нужно обязательно вызвать force buildRecalc
	//чтобы формулы, которые копим попали в струкруры nodesSheet, иначе формулы не сдвинутся
	//например принимаем изменения: добавление формул, сдвиг с помощью _removeRows. результат формулы указывают на теже ячейки, что и до _removeRows
  this.workbook.buildRecalc(true, true);
	var nTop = oBBox.r1;
	var nBottom = oBBox.r2;
	var dif = nTop - nBottom - 1;
    this.renameDependencyNodes({offsetRow:dif,offsetCol:0}, oBBox );
	var aIndexes = [];
	for(var i in this.aGCells)
	{
		var rowInd = i - 0;
		if(rowInd >= nTop)
			aIndexes.push(rowInd);
	}
	//по возрастанию
	aIndexes.sort(fSortAscending);
	for(var i = 0, length = aIndexes.length; i < length; ++i){
		var rowInd = aIndexes[i];
		var row = this.aGCells[rowInd];
		if(row){
			if(rowInd <= nBottom){
				//Удаляем ячейки
				for(var j = oBBox.c1; j <= oBBox.c2; j++){
					this._removeCell(rowInd, j);
				}
			}
			else{
				var nIndex = rowInd + dif;
				var rowTop = this._getRow(nIndex);
				//Сдвигаем ячейки
				for(var j = oBBox.c1; j <= oBBox.c2; j++){
					this._moveCellVer(rowInd, j, dif);
				}
			}
		}
	}

	History.Add(AscCommonExcel.g_oUndoRedoWorksheet, AscCH.historyitem_Worksheet_ShiftCellsTop, this.getId(), new Asc.Range(oBBox.c1, oBBox.r1, oBBox.c2, gc_nMaxRow0), new UndoRedoData_BBox(oBBox));
    this.autoFilters.insertRows( "delCell", oBBox, c_oAscDeleteOptions.DeleteCellsAndShiftTop );
	//todo проверить не уменьшились ли границы таблицы
};
Woorksheet.prototype._shiftCellsRight=function(oBBox, displayNameFormatTable){
	//до перемещения ячеек, перед функцией, в которой используются nodesSheetArea/nodesSheetCell move/shift нужно обязательно вызвать force buildRecalc
	//чтобы формулы, которые копим попали в струкруры nodesSheet, иначе формулы не сдвинутся
	//например принимаем изменения: добавление формул, сдвиг с помощью _removeRows. результат формулы указывают на теже ячейки, что и до _removeRows
  this.workbook.buildRecalc(true, true);
	var nLeft = oBBox.c1;
	var nRight = oBBox.c2;
	var dif = nRight - nLeft + 1;
    this.renameDependencyNodes({offsetRow:0,offsetCol:dif}, oBBox);
	for(var i = oBBox.r1; i <= oBBox.r2; i++){
		var row = this.aGCells[i];
		if(row){
			var aIndexes = [];
			for(var cellInd in row.c)
			{
				var nIndex = cellInd - 0;
				if(nIndex >= nLeft)
					aIndexes.push(nIndex);
			}
			//по убыванию
			aIndexes.sort(fSortDescending);
			for(var j = 0, length2 = aIndexes.length; j < length2; ++j){
				var nCellInd = aIndexes[j];
				//Сдвигаем ячейки
				var cell = row.c[nCellInd];
				if(cell){
					if(nCellInd + dif > this.nColsCount)
						this.nColsCount = nCellInd + dif;
					this._moveCellHor(/*row*/i, /*col*/nCellInd, dif, oBBox);
				}
			}
		}
	}

	History.Add(AscCommonExcel.g_oUndoRedoWorksheet, AscCH.historyitem_Worksheet_ShiftCellsRight, this.getId(), new Asc.Range(oBBox.c1, oBBox.r1, gc_nMaxCol0, oBBox.r2), new UndoRedoData_BBox(oBBox));
    this.autoFilters.insertColumn( "insCells",  oBBox, c_oAscInsertOptions.InsertCellsAndShiftRight, displayNameFormatTable );
};
Woorksheet.prototype._shiftCellsBottom=function(oBBox, displayNameFormatTable){
	//до перемещения ячеек, перед функцией, в которой используются nodesSheetArea/nodesSheetCell move/shift нужно обязательно вызвать force buildRecalc
	//чтобы формулы, которые копим попали в струкруры nodesSheet, иначе формулы не сдвинутся
	//например принимаем изменения: добавление формул, сдвиг с помощью _removeRows. результат формулы указывают на теже ячейки, что и до _removeRows
  this.workbook.buildRecalc(true, true);
	var nTop = oBBox.r1;
	var nBottom = oBBox.r2;
	var dif = nBottom - nTop + 1;
	var aIndexes = [];
    this.renameDependencyNodes({offsetRow:dif,offsetCol:0}, oBBox);
	for(var i in this.aGCells){
		var rowInd = i - 0;
		if(rowInd >= nTop)
			aIndexes.push(rowInd);
	}
	//по убыванию
	aIndexes.sort(fSortDescending);
	for(var i = 0, length = aIndexes.length; i < length; ++i){
		rowInd = aIndexes[i];
		var row = this.aGCells[rowInd];
		if(row){
			var nIndex = rowInd + dif;
			if(nIndex + dif > this.nRowsCount)
				this.nRowsCount = nIndex + dif;
			var rowTop = this._getRow(nIndex);
			//Сдвигаем ячейки
			for(var j = oBBox.c1; j <= oBBox.c2; j++){
				this._moveCellVer(rowInd, j, dif);
			}
		}
	}

	History.Add(AscCommonExcel.g_oUndoRedoWorksheet, AscCH.historyitem_Worksheet_ShiftCellsBottom, this.getId(), new Asc.Range(oBBox.c1, oBBox.r1, oBBox.c2, gc_nMaxRow0), new UndoRedoData_BBox(oBBox));
	
	if(!this.workbook.bUndoChanges)
	{
		this.autoFilters.insertRows( "insCell", oBBox, c_oAscInsertOptions.InsertCellsAndShiftDown, displayNameFormatTable );
	}
    
};
Woorksheet.prototype._setIndex=function(ind){
	this.index = ind;
};
Woorksheet.prototype._BuildDependencies=function(cellRange){
	/*
		Построение графа зависимостей.
	*/
	var c, ca, oLengthCache = {};
	for(var i in cellRange){
		if (null === cellRange[i]) {
			cellRange[i] = i;
			continue;
		}

		ca = g_oCellAddressUtils.getCellAddress(i);
		c = this._getCellNoEmpty(ca.getRow0(),ca.getCol0());

		if( c && c.sFormula ){
			var elem = oLengthCache[c.sFormula.length];
			if(null == elem)
			{
				elem = [];
				oLengthCache[c.sFormula.length] = elem;
			}
			elem.push(c);
		}
	}
	for(var i in oLengthCache)
	{
		var temp = oLengthCache[i];
		var aCache = [];
		for(var j = 0, length2 = temp.length; j < length2; j++)
		{
			var c = temp[j],
			    cellId = g_oCellAddressUtils.getCellId(c.nRow, c.nCol ),
                aRefs = [],
                cache = inCache(aCache, c.sFormula, aRefs ),
                //cache = false,
                bInCache = false;
			if(cache)
			{
				bInCache = true;
				var oNewFormula = cache.formulaParsed.clone(c.sFormula, cellId, this);
				var RefPos = cache.formulaParsed.RefPos;
				for(var k = 0, length3 = RefPos.length; k < length3; k++)
				{
					var pos = RefPos[k];
					var elem = oNewFormula.outStack[pos.index];
					if(elem)
					{
						//todo случай ,если ref число или именованный диапазон
						var ref = aRefs[k];
						var range = AscCommonExcel.g_oRangeCache.getAscRange(ref);
						if(null != range)
						{
							var oNewElem;
							if(range.isOneCell())
							{
								if(elem instanceof cRef3D)
									oNewElem = new cRef3D(ref, elem.ws.getName(), elem._wb);
								else if(elem instanceof cArea3D)
								{
									var wsFrom = elem._wb.getWorksheetById( elem.wsFrom ).getName();
									var wsTo = elem._wb.getWorksheetById( elem.wsTo ).getName();
									oNewElem = new cArea3D(ref, wsFrom, wsTo, elem._wb);
								}
								else if(-1 != ref.indexOf(":"))//случай "A1:A1"
									oNewElem = new AscCommonExcel.cArea(ref, elem.ws);
								else
									oNewElem = new AscCommonExcel.cRef(ref, elem.ws);
							}
							else
							{
								if(elem instanceof cRef3D)
									oNewElem = new cArea3D(ref, elem.ws.getName(), elem.ws.getName(), elem._wb);
								else if(elem instanceof cArea3D)
								{
									var wsFrom = elem._wb.getWorksheetById( elem.wsFrom ).getName();
									var wsTo = elem._wb.getWorksheetById( elem.wsTo ).getName();
									oNewElem = new cArea3D(ref, wsFrom, wsTo, elem._wb);
								}
								else
									oNewElem = new AscCommonExcel.cArea(ref, elem.ws);
							}
							if ( ref.indexOf( "$" ) > -1 )
								oNewElem.isAbsolute = true; // ToDo - пересмотреть этот параметр (есть в Range информация о абсолютной ссылке)
							oNewFormula.outStack[pos.index] = oNewElem;
						}
						else
							bInCache = false;
					}
				}
				if(bInCache)
				{
					c.formulaParsed = oNewFormula;
					c.formulaParsed.buildDependencies();
				}
			}
			if(!bInCache)
			{
				c.formulaParsed = new parserFormula( c.sFormula, cellId, this );
				c.formulaParsed.parse();
				c.formulaParsed.buildDependencies();
				//error не добавляем в кеш у них не распознались RefPos, их бессмысленно сравнивать.Это только добавит торозов
				if(0 == c.formulaParsed.error.length)
					aCache.push(c);
			}
		}
	}
};
Woorksheet.prototype._setHandlersTablePart = function(){
	if(!this.TableParts)
		return;
	
	for(var i = 0; i < this.TableParts.length; i++)
	{
		this.TableParts[i].setHandlers(this.handlers);
	}
};
Woorksheet.prototype.getTableRangeForFormula = function(name, objectParam){
	var res = null;
	if(!this.TableParts)
		return res;
	
	for(var i = 0; i < this.TableParts.length; i++)
	{
		if(this.TableParts[i].DisplayName === name)
		{
			res = this.TableParts[i].getTableRangeForFormula(objectParam);
			break;
		}
	}
	return res;
};
Woorksheet.prototype.getTableIndexColumnByName = function(tableName, columnName){
	var res = null;
	if(!this.TableParts)
		return res;
	
	for(var i = 0; i < this.TableParts.length; i++)
	{
		if(this.TableParts[i].DisplayName === tableName)
		{
			res = this.TableParts[i].getTableIndexColumnByName(columnName);
			break;
		}
	}
	return res;
};
Woorksheet.prototype.getTableNameColumnByIndex = function(tableName, columnIndex){
	var res = null;
	if(!this.TableParts)
		return res;
	
	for(var i = 0; i < this.TableParts.length; i++)
	{
		if(this.TableParts[i].DisplayName === tableName)
		{
			res = this.TableParts[i].getTableNameColumnByIndex(columnIndex);
			break;
		}
	}
	return res;
};

function inCache(aCache, sFormula, aRefs)
{
	var oRes = null;
	for(var i = 0, length = aCache.length; i < length; i++)
	{
		var cache = aCache[i];
		var sCurFormula = cache.sFormula;
		if(null != cache.formulaParsed){
			var RefPos = cache.formulaParsed.RefPos;
			//todo свое сравнение для error
			if(0 == cache.formulaParsed.error.length && inCacheStrcmp(sCurFormula, sFormula, RefPos, aRefs)){
				oRes = cache;
				break;
			}
		}
	}
	return oRes;
}
function inCacheStrcmp(str1, str2, RefPos, aRefs)
{
	var bRes = true;
	var nStartIndex = 0;
	for(var i = 0, length = RefPos.length; i < length; i++)
	{
		var mask = RefPos[i];
		for(var j = nStartIndex; j < mask.start; j++)
		{
			if(str1[j] != str2[j])
			{
				bRes = false;
				break;
			}
		}
		nStartIndex = mask.end;
	}
	if(bRes)
	{
		for(var i = nStartIndex; i < str1.length; i++)
		{
			if(str1[i] != str2[i])
			{
				bRes = false;
				break;
			}
		}
	}
	if(bRes)
	{
		for(var i = 0, length = RefPos.length; i < length; i++)
		{
			var mask = RefPos[i];
			aRefs.push(str2.substring(mask.start, mask.end));
		}
	}
	return bRes;
}
Woorksheet.prototype._RecalculatedFunctions=function(cell,bad,setCellFormat){
    function adjustCellFormat( c ) {
        // ищет в формуле первый рэндж и устанавливает формат ячейки как формат первой ячейки в рэндже
        var elem = null;
        if ( c.formulaParsed && c.formulaParsed.outStack ) {
            for ( var i = 0, length = c.formulaParsed.outStack.length; i < length; i++ ) {
                elem = c.formulaParsed.outStack[i];
                if ( elem instanceof AscCommonExcel.cRef || elem instanceof cRef3D || elem instanceof AscCommonExcel.cArea || elem instanceof cArea3D ) {
                    var r = elem.getRange();
                    if ( elem instanceof cArea3D && r.length > 0 )
                        r = r[0];
                    if ( r && r.getNumFormatStr ) {
                        var sCurFormat = c.getNumFormatStr();
                        if ( g_oDefaultFormat.Num.getFormat() == sCurFormat ) {
                            var sNewFormat = r.getNumFormatStr();
                            if ( sCurFormat != sNewFormat )
                                c.setNumFormat( sNewFormat );
                        }
                    }
                    break;
                }
            }
        }
    }
	
	if( cell.indexOf(":")>-1 ) return;
	
	var celladd = this.getRange2(cell).getFirst(),
		__cell = this._getCellNoEmpty( celladd.getRow0(),celladd.getCol0() ), res;
	
	if( !__cell || !__cell.sFormula )
		return;

	/*
		bad - флаг, показывающий, что ячейка находится в списке плохих ячеек (имеются циклические ссылки на ячейку), после сортировки графа зависимостей.
	*/
	if(!bad){
		res = __cell.formulaParsed.calculate();
	}
	else {
		res = new AscCommonExcel.cError( AscCommonExcel.cErrorType.bad_reference )
	}
	if(res){
		if( res.type == cElementType.cell || res.type == cElementType.cell3D){
			var nF = res.numFormat;
			res = res.getValue();
			res.numFormat = nF;
		}
		else if( res.type == cElementType.array ){
			var nF = res.numFormat;
			res = res.getElement(0);
			res.numFormat = nF;
		}
		else if( res.type == cElementType.cellsRange || res.type == cElementType.cellsRange3D ){
			var nF = res.numFormat;
			res = res.cross( new CellAddress(cell), this.Id );
//			res = res.getElementByCell(new CellAddress(cell));
			res.numFormat = nF;
		}
		__cell.oValue.clean();
		switch (res.type){
			case cElementType.number:
				__cell.oValue.type = CellValueType.Number;
				__cell.oValue.number = res.getValue();
				break;
			case cElementType.bool:
				__cell.oValue.type = CellValueType.Bool;
				__cell.oValue.number = res.value ? 1 : 0;
				break;
			case cElementType.error:
				__cell.oValue.type = CellValueType.Error;
				__cell.oValue.text = res.getValue().toString();
				break;
            case cElementType.name:
				__cell.oValue.type = CellValueType.Error;
				__cell.oValue.text = res.getValue().toString();
				break;
			default:
				__cell.oValue.type = CellValueType.String;
				__cell.oValue.text = res.getValue().toString();
		}
    AscCommonExcel.g_oVLOOKUPCache.remove(__cell);
    AscCommonExcel.g_oHLOOKUPCache.remove(__cell);
		__cell.setFormulaCA(res.ca);
        if(setCellFormat){
            if( res.numFormat !== undefined && res.numFormat >= 0){

                if( aStandartNumFormatsId[__cell.getNumFormatStr()] == 0 )
                    __cell.setNumFormat(aStandartNumFormats[res.numFormat])

            }
            else if( res.numFormat !== undefined && res.numFormat == -1 ){
                adjustCellFormat(__cell,__cell.sFormula);
            }
        }
	}
};
Woorksheet.prototype._ReBuildFormulas=function(cellRange){
	/*
		Если существуют трехмерные ссылки на ячейки, то у них необходимо поменять имя листа на новое после переименования листа.
	*/
	var c, ca;
	for(var i in cellRange){
		ca = new CellAddress(i);
		c = this._getCellNoEmpty(ca.getRow0(),ca.getCol0());
		
		if( c && c.formulaParsed && c.formulaParsed.is3D ){
			c.setFormula(c.formulaParsed.assemble());
		}
	}
};
Woorksheet.prototype.renameDependencyNodes = function(offset, oBBox, rec, noDelete){
	this.workbook.dependencyFormulas.checkOffset(oBBox, offset, this.Id, noDelete);
};
Woorksheet.prototype.getAllCol = function(){
	if(null == this.oAllCol)
		this.oAllCol = new AscCommonExcel.Col(this, g_nAllColIndex);
	return this.oAllCol;
};
Woorksheet.prototype.getAllRow = function(){
    if (null == this.oSheetFormatPr.oAllRow) {
        this.oSheetFormatPr.oAllRow = new AscCommonExcel.Row(this);
        this.oSheetFormatPr.oAllRow.create(g_nAllRowIndex + 1);
    }
	return this.oSheetFormatPr.oAllRow;
};
Woorksheet.prototype.getHyperlinkByCell = function(row, col){
	var oHyperlink = this.hyperlinkManager.getByCell(row, col);
	return oHyperlink ? oHyperlink.data : null;
};
Woorksheet.prototype.getMergedByCell = function(row, col){
	var oMergeInfo = this.mergeManager.getByCell(row, col);
	return oMergeInfo ? oMergeInfo.bbox : null;
};
Woorksheet.prototype.getMergedByRange = function(bbox){
	return this.mergeManager.get(bbox);
};
Woorksheet.prototype._expandRangeByMergedAddToOuter = function(aOuter, range, aMerged){
	for(var i = 0, length = aMerged.all.length; i < length; i++)
	{
		var elem = aMerged.all[i];
		if(!range.containsRange(elem.bbox))
			aOuter.push(elem);
	}
};
Woorksheet.prototype._expandRangeByMergedGetOuter = function(range){
	var aOuter = [];
	//смотрим только границы
	this._expandRangeByMergedAddToOuter(aOuter, range, this.mergeManager.get({r1: range.r1, c1: range.c1, r2: range.r2, c2: range.c1}));
	if(range.c1 != range.c2)
	{
		this._expandRangeByMergedAddToOuter(aOuter, range, this.mergeManager.get({r1: range.r1, c1: range.c2, r2: range.r2, c2: range.c2}));
		if(range.c2 - range.c1 > 1)
		{
			this._expandRangeByMergedAddToOuter(aOuter, range, this.mergeManager.get({r1: range.r1, c1: range.c1 + 1, r2: range.r1, c2: range.c2 - 1}));
			if(range.r1 != range.r2)
				this._expandRangeByMergedAddToOuter(aOuter, range, this.mergeManager.get({r1: range.r2, c1: range.c1 + 1, r2: range.r2, c2: range.c2 - 1}));
		}
	}
	return aOuter;
};
Woorksheet.prototype.expandRangeByMerged = function(range){
	if(null != range)
	{
		var aOuter = this._expandRangeByMergedGetOuter(range);
		if(aOuter.length > 0)
		{
			range = range.clone();
			while(aOuter.length > 0)
			{
				for(var i = 0, length = aOuter.length; i < length; i++)
					range.union2(aOuter[i].bbox);
				aOuter = this._expandRangeByMergedGetOuter(range);
			}
		}
	}
	return range;
};
Woorksheet.prototype.createTablePart = function(){
	
	return new AscCommonExcel.TablePart(this.handlers);
};
Woorksheet.prototype.onUpdateRanges = function(ranges) {
  this.workbook.updateSparklineCache(this.sName, ranges);
  this._updateConditionalFormatting(new AscCommonExcel.MultiplyRange(ranges));
};
Woorksheet.prototype.updateSparklineCache = function(sheet, ranges) {
  for (var i = 0; i < this.aSparklineGroups.length; ++i) {
    this.aSparklineGroups[i].updateCache(sheet, ranges);
  }
};
//-------------------------------------------------------------------------------------------------
/**
 * @constructor
 */
function Cell(worksheet){
	this.ws = worksheet;
	this.oValue = new AscCommonExcel.CCellValue();
	this.xfs = null;
	this.tableXfs = null;
	this.conditionalFormattingXfs = null;
	this.compiledXfs = null;
	this.nRow = -1;
	this.nCol = -1;
	this.sFormula = null;
	this.sFormulaCA = null;
	this.formulaParsed = null;
}
Cell.prototype.getStyle=function(){
	return this.xfs;
};
Cell.prototype.getCompiledStyle = function () {
    if (null == this.compiledXfs && (null != this.xfs || null != this.tableXfs || null != this.conditionalFormattingXfs)) {
        this.compileXfs();
    }
    return this.compiledXfs;
};
Cell.prototype.compileXfs=function(){
	this.compiledXfs = null;
	if(null != this.xfs || null != this.tableXfs || null != this.conditionalFormattingXfs)
	{
		if(null != this.tableXfs)
			this.compiledXfs = this.tableXfs;
		if(null != this.xfs)
		{
			if(null != this.compiledXfs)
				this.compiledXfs = this.xfs.merge(this.compiledXfs);
			else
				this.compiledXfs = this.xfs;
		}
		if(null != this.conditionalFormattingXfs)
		{
			if(null != this.compiledXfs)
				this.compiledXfs = this.conditionalFormattingXfs.merge(this.compiledXfs);
			else
				this.compiledXfs = this.conditionalFormattingXfs;
		}
	}
};
Cell.prototype.clone=function(oNewWs){
    if(!oNewWs)
        oNewWs = this.ws;
	var oNewCell = new Cell(oNewWs);
	oNewCell.nRow = this.nRow;
	oNewCell.nCol = this.nCol;
	if(null != this.xfs)
		oNewCell.xfs = this.xfs.clone();
	oNewCell.oValue = this.oValue.clone();
	if(null != this.sFormula)
		oNewCell.sFormula = this.sFormula;
	return oNewCell;
};
Cell.prototype.create=function(xfs, nRow, nCol){
	this.xfs = xfs;
	this.nRow = nRow;
	this.nCol = nCol;
};
Cell.prototype.isEmptyText=function(){
	if(false == this.oValue.isEmpty())
		return false;
	if(null != this.sFormula)
		return false;
	return true;
};
Cell.prototype.isEmptyTextString=function(){
	return this.oValue.isEmpty();
};
Cell.prototype.isEmpty=function(){
	if(false == this.isEmptyText())
		return false;
	if(null != this.xfs)
		return false;
	return true;
};
Cell.prototype.isFormula=function(){
	return this.sFormula ? true : false;
};
Cell.prototype.Remove=function(){
	this.ws._removeCell(null, null, this);
};
Cell.prototype.getName=function(){
	return g_oCellAddressUtils.getCellId(this.nRow, this.nCol);
};
Cell.prototype.cleanCache=function(){
	this.oValue.cleanCache();
};
Cell.prototype.setFormula=function(val, bAddToHistory){
	if(bAddToHistory)
	{
		History.Add( AscCommonExcel.g_oUndoRedoWorksheet, AscCH.historyitem_Worksheet_RemoveCellFormula, this.ws.getId(), new Asc.Range( this.nCol, this.nRow, this.nCol, this.nRow ), new UndoRedoData_CellSimpleData( this.nRow, this.nCol, null, null, this.sFormula ));
	}
	this.sFormula = val;
	this.oValue.cleanCache();
};
Cell.prototype.setValue=function(val,callback, isCopyPaste){
	var ret = true;
	var DataOld = null;
	if(History.Is_On())
		DataOld = this.getValueData();
	var bIsTextFormat = false;
	if(!isCopyPaste){
		var sNumFormat;
		if(null != this.xfs && null != this.xfs.num)
			sNumFormat = this.xfs.num.getFormat();
		else
			sNumFormat = g_oDefaultFormat.Num.getFormat();
		var numFormat = oNumFormatCache.get(sNumFormat);
		bIsTextFormat = numFormat.isTextFormat();
	}
	var wb = this.ws.workbook;
	var ws = this.ws;
	if(false == bIsTextFormat)
	{
		/*
			Устанавливаем значение в Range ячеек. При этом происходит проверка значения на формулу.
			Если значение является формулой, то проверяем содержиться ли в ячейке формула или нет, если "да" - то очищаем в графе зависимостей список, от которых зависит формула(masterNodes), позже будет построен новый. Затем выставляем флаг о необходимости дальнейшего пересчета, и заносим ячейку в список пересчитываемых ячеек.
		*/
		if( null != val && val[0] == "=" && val.length > 1){

            var oldFP = undefined;

            if( this.formulaParsed  )
                oldFP = this.formulaParsed;
			var cellId = g_oCellAddressUtils.getCellId(this.nRow, this.nCol);
            this.formulaParsed = new parserFormula(val.substring(1),cellId,this.ws);
			
			var formulaLocaleParse = isCopyPaste === true ? false : oFormulaLocaleInfo.Parse;
			var formulaLocaleDigetSep = isCopyPaste === true ? false : oFormulaLocaleInfo.DigitSep;
			if( !this.formulaParsed.parse( formulaLocaleParse, formulaLocaleDigetSep ) ){
				switch( this.formulaParsed.error[this.formulaParsed.error.length-1] ){
					case c_oAscError.ID.FrmlWrongFunctionName:
						break;
                    case c_oAscError.ID.FrmlParenthesesCorrectCount:
                        this.setValue("="+this.formulaParsed.Formula, callback, isCopyPaste);
                        return;
					default :{
						wb.handlers.trigger("asc_onError",this.formulaParsed.error[this.formulaParsed.error.length-1], c_oAscError.Level.NoCritical);
						if( callback )
							callback(false);
                        if( oldFP !== undefined ){
                            this.formulaParsed = oldFP;
                        }
						return;
					}
				}
			}
			else{
				val = "="+this.formulaParsed.assemble();
			}

		}
        else{
            this.formulaParsed = null;
        }
	}
	//удаляем старые значения
	this.oValue.clean();
	var sheetId = this.ws.getId();
	var cellId = g_oCellAddressUtils.getCellId(this.nRow, this.nCol);
	if (this.sFormula)
		wb.dependencyFormulas.deleteMasterNodes2( ws.Id, cellId );
	if( !(null != val && val[0] != "=" || true == bIsTextFormat))
		addToArrRecalc(this.ws.getId(), this);
	wb.needRecalc.nodes[getVertexId(sheetId,cellId)] = [sheetId, cellId];
    wb.needRecalc.length++;
	
	this.sFormula = null;
	this.setFormulaCA(false);
	if(val){
		if(false == bIsTextFormat && val[0] == "=" && val.length > 1){
			this.setFormula( val.substring(1) );
		}
		else {
			this.oValue.setValue(this, val);
            this.formulaParsed = null;
		}
	}
	var DataNew = null;
	if(History.Is_On())
		DataNew = this.getValueData();
	if(History.Is_On() && false == DataOld.isEqual(DataNew))
	    History.Add(AscCommonExcel.g_oUndoRedoCell, AscCH.historyitem_Cell_ChangeValue, this.ws.getId(), new Asc.Range(this.nCol, this.nRow, this.nCol, this.nRow), new UndoRedoData_CellSimpleData(this.nRow, this.nCol, DataOld, DataNew));
    if (!this.ws.workbook.lockCounter) {
		/*
			Если необходим пересчет, то по списку пересчитываемых ячеек сортируем граф зависимостей и пересчиываем в получившемся порядке. Плохим ячейкам с цикличискими ссылками выставляем ошибку "#REF!".
		*/
        //sortDependency вызывается ниже History.Add(AscCH.historyitem_Cell_ChangeValue, потому что в ней может быть выставлен формат ячейки(если это текстовый, то принимая изменения формула станет текстом)
      this.ws.workbook.sortDependency(true);
	}
	//todo не должны удаляться ссылки, если сделать merge ее части.
	if(this.isEmptyTextString())
	{
		var cell = this.ws.getCell3(this.nRow, this.nCol);
		cell.removeHyperlink();
	}
	return ret;
};
Cell.prototype.setValue2=function(array){
	var DataOld = null;
	if(History.Is_On())
		DataOld = this.getValueData();
	//[{text:"",format:TextFormat},{}...]
	this.setValueCleanFormula();
    this.oValue.clean();
    this.oValue.setValue2(this, array);
  this.ws.workbook.sortDependency();
	var DataNew = null;
	if(History.Is_On())
		DataNew = this.getValueData();
	if(History.Is_On() && false == DataOld.isEqual(DataNew))
		History.Add(AscCommonExcel.g_oUndoRedoCell, AscCH.historyitem_Cell_ChangeValue, this.ws.getId(), new Asc.Range(this.nCol, this.nRow, this.nCol, this.nRow), new UndoRedoData_CellSimpleData(this.nRow, this.nCol, DataOld, DataNew));
	//todo не должны удаляться ссылки, если сделать merge ее части.
	if(this.isEmptyTextString())
	{
		var cell = this.ws.getCell3(this.nRow, this.nCol);
		cell.removeHyperlink();
	}
};
Cell.prototype.setValueCleanFormula = function (array) {
    //удаляем сторое значение
    var ws = this.ws;
    var wb = this.ws.workbook;
    var sheetId = this.ws.getId();
    var cellId = g_oCellAddressUtils.getCellId(this.nRow, this.nCol);
    if (this.sFormula)
        wb.dependencyFormulas.deleteMasterNodes2(ws.Id, cellId);

    this.sFormula = null;
    this.formulaParsed = null;
    this.setFormulaCA(false);

    wb.needRecalc.nodes[getVertexId(sheetId, cellId)] = [sheetId, cellId];
    wb.needRecalc.length++;
};
Cell.prototype.setType=function(type){
	if(type != this.oValue.type){
		var DataOld = this.getValueData();
		this.oValue.setValueType(type);
		var DataNew = this.getValueData();
		History.Add(AscCommonExcel.g_oUndoRedoCell, AscCH.historyitem_Cell_ChangeValue, this.ws.getId(), new Asc.Range(this.nCol, this.nRow, this.nCol, this.nRow), new UndoRedoData_CellSimpleData(this.nRow, this.nCol, DataOld, DataNew));
	}
	return this.oValue.type;
};
Cell.prototype.getType=function(){
	return this.oValue.type;
};
Cell.prototype.setCellStyle=function(val){
	var newVal = this.ws.workbook.CellStyles._prepareCellStyle(val);
	var oRes = this.ws.workbook.oStyleManager.setCellStyle(this, newVal);
	if(History.Is_On()) {
		var oldStyleName = this.ws.workbook.CellStyles.getStyleNameByXfId(oRes.oldVal);
		History.Add(AscCommonExcel.g_oUndoRedoCell, AscCH.historyitem_Cell_Style, this.ws.getId(), new Asc.Range(this.nCol, this.nRow, this.nCol, this.nRow), new UndoRedoData_CellSimpleData(this.nRow, this.nCol, oldStyleName, val));

		// Выставляем стиль
		var oStyle = this.ws.workbook.CellStyles.getStyleByXfId(oRes.newVal);
		if (oStyle.ApplyFont)
			this.setFont(oStyle.getFont());
		if (oStyle.ApplyFill)
			this.setFill(oStyle.getFill());
		if (oStyle.ApplyBorder)
			this.setBorder(oStyle.getBorder());
		if (oStyle.ApplyNumberFormat)
			this.setNumFormat(oStyle.getNumFormatStr());
	}
	this.compiledXfs = null;
	this.oValue.cleanCache();
};
Cell.prototype.setNumFormat=function(val){
	var oRes;
    /*if( val == aStandartNumFormats[0] &&
        this.formulaParsed && this.formulaParsed.value && this.formulaParsed.value.numFormat !== null &&
        this.formulaParsed.value.numFormat !== undefined && aStandartNumFormats[this.formulaParsed.value.numFormat] )
        oRes = this.ws.workbook.oStyleManager.setNumFormat(this, aStandartNumFormats[this.formulaParsed.value.numFormat]);
    else*/
        oRes = this.ws.workbook.oStyleManager.setNumFormat(this, val);
    if(History.Is_On() && oRes.oldVal != oRes.newVal)
        History.Add(AscCommonExcel.g_oUndoRedoCell, AscCH.historyitem_Cell_Numformat, this.ws.getId(), new Asc.Range(this.nCol, this.nRow, this.nCol, this.nRow), new UndoRedoData_CellSimpleData(this.nRow, this.nCol, oRes.oldVal, oRes.newVal));
	this.compiledXfs = null;
	this.oValue.cleanCache();
};
Cell.prototype.shiftNumFormat=function(nShift, dDigitsCount){
	var bRes = false;
	var bGeneral = true;
    var sNumFormat;
    if(null != this.xfs && null != this.xfs.num)
        sNumFormat = this.xfs.num.getFormat();
    else
        sNumFormat = g_oDefaultFormat.Num.getFormat();
	if("General" != sNumFormat)
	{
		var oCurNumFormat = oNumFormatCache.get(sNumFormat);
		if(null != oCurNumFormat && false == oCurNumFormat.isGeneralFormat())
		{
			bGeneral = false;
			var output = {};
			bRes = oCurNumFormat.shiftFormat(output, nShift);
			if(true == bRes)
				this.setNumFormat(output.format);
		}
	}
	if(bGeneral)
	{
		if(CellValueType.Number == this.oValue.type)
		{
			var sGeneral = AscCommon.DecodeGeneralFormat(this.oValue.number, this.oValue.type, dDigitsCount);
			var oGeneral = oNumFormatCache.get(sGeneral);
			if(null != oGeneral && false == oGeneral.isGeneralFormat())
			{
				var output = {};
				bRes = oGeneral.shiftFormat(output, nShift);
				if(true == bRes)
					this.setNumFormat(output.format);
			}
		}
	}
	this.oValue.cleanCache();
	return bRes;
};
Cell.prototype.setFont=function(val, bModifyValue){
	if(false != bModifyValue)
	{
		//убираем комплексные строки
		if(null != this.oValue.multiText)
		{
			var oldVal = null;
			if(History.Is_On())
				oldVal = this.getValueData();
			this.oValue.makeSimpleText();
			if(History.Is_On())
			{
				var newVal = this.getValueData();
				History.Add(AscCommonExcel.g_oUndoRedoCell, AscCH.historyitem_Cell_ChangeValue, this.ws.getId(), new Asc.Range(this.nCol, this.nRow, this.nCol, this.nRow), new UndoRedoData_CellSimpleData(this.nRow, this.nCol, oldVal, newVal));
			}
		}
	}
	var oRes = this.ws.workbook.oStyleManager.setFont(this, val);
    if(History.Is_On() && oRes.oldVal != oRes.newVal)
	{
		var oldVal = null;
		if(null != oRes.oldVal)
			oldVal = oRes.oldVal.clone();
		var newVal = null;
		if(null != oRes.newVal)
			newVal = oRes.newVal.clone();
        History.Add(AscCommonExcel.g_oUndoRedoCell, AscCH.historyitem_Cell_SetFont, this.ws.getId(), new Asc.Range(this.nCol, this.nRow, this.nCol, this.nRow), new UndoRedoData_CellSimpleData(this.nRow, this.nCol, oldVal, newVal));
	}
	this.compiledXfs = null;
	this.oValue.cleanCache();
};
Cell.prototype.setFontname=function(val){
    var oRes = this.ws.workbook.oStyleManager.setFontname(this, val);
	this.oValue.setFontname(this, val);
	if(History.Is_On() && oRes.oldVal != oRes.newVal)
		History.Add(AscCommonExcel.g_oUndoRedoCell, AscCH.historyitem_Cell_Fontname, this.ws.getId(), new Asc.Range(this.nCol, this.nRow, this.nCol, this.nRow), new UndoRedoData_CellSimpleData(this.nRow, this.nCol, oRes.oldVal, oRes.newVal));
	this.compiledXfs = null;
	this.oValue.cleanCache();
};
Cell.prototype.setFontsize=function(val){
    var oRes = this.ws.workbook.oStyleManager.setFontsize(this, val);
	this.oValue.setFontsize(this, val);
    if(History.Is_On() && oRes.oldVal != oRes.newVal)
        History.Add(AscCommonExcel.g_oUndoRedoCell, AscCH.historyitem_Cell_Fontsize, this.ws.getId(), new Asc.Range(this.nCol, this.nRow, this.nCol, this.nRow), new UndoRedoData_CellSimpleData(this.nRow, this.nCol, oRes.oldVal, oRes.newVal));
	this.compiledXfs = null;
	this.oValue.cleanCache();
};
Cell.prototype.setFontcolor=function(val){
    var oRes = this.ws.workbook.oStyleManager.setFontcolor(this, val);
	this.oValue.setFontcolor(this, val);
    if(History.Is_On() && oRes.oldVal != oRes.newVal)
        History.Add(AscCommonExcel.g_oUndoRedoCell, AscCH.historyitem_Cell_Fontcolor, this.ws.getId(), new Asc.Range(this.nCol, this.nRow, this.nCol, this.nRow), new UndoRedoData_CellSimpleData(this.nRow, this.nCol, oRes.oldVal, oRes.newVal));
	this.compiledXfs = null;
	this.oValue.cleanCache();
};
Cell.prototype.setBold=function(val){
    var oRes = this.ws.workbook.oStyleManager.setBold(this, val);
	this.oValue.setBold(this, val);
    if(History.Is_On() && oRes.oldVal != oRes.newVal)
        History.Add(AscCommonExcel.g_oUndoRedoCell, AscCH.historyitem_Cell_Bold, this.ws.getId(), new Asc.Range(this.nCol, this.nRow, this.nCol, this.nRow), new UndoRedoData_CellSimpleData(this.nRow, this.nCol, oRes.oldVal, oRes.newVal));
	this.compiledXfs = null;
	this.oValue.cleanCache();
};
Cell.prototype.setItalic=function(val){
    var oRes = this.ws.workbook.oStyleManager.setItalic(this, val);
	this.oValue.setItalic(this, val);
    if(History.Is_On() && oRes.oldVal != oRes.newVal)
        History.Add(AscCommonExcel.g_oUndoRedoCell, AscCH.historyitem_Cell_Italic, this.ws.getId(), new Asc.Range(this.nCol, this.nRow, this.nCol, this.nRow), new UndoRedoData_CellSimpleData(this.nRow, this.nCol, oRes.oldVal, oRes.newVal));
	this.compiledXfs = null;
	this.oValue.cleanCache();
};
Cell.prototype.setUnderline=function(val){
    var oRes = this.ws.workbook.oStyleManager.setUnderline(this, val);
	this.oValue.setUnderline(this, val);
    if(History.Is_On() && oRes.oldVal != oRes.newVal)
        History.Add(AscCommonExcel.g_oUndoRedoCell, AscCH.historyitem_Cell_Underline, this.ws.getId(), new Asc.Range(this.nCol, this.nRow, this.nCol, this.nRow), new UndoRedoData_CellSimpleData(this.nRow, this.nCol, oRes.oldVal, oRes.newVal));
	this.compiledXfs = null;
	this.oValue.cleanCache();
};
Cell.prototype.setStrikeout=function(val){
    var oRes = this.ws.workbook.oStyleManager.setStrikeout(this, val);
	this.oValue.setStrikeout(this, val);
    if(History.Is_On() && oRes.oldVal != oRes.newVal)
        History.Add(AscCommonExcel.g_oUndoRedoCell, AscCH.historyitem_Cell_Strikeout, this.ws.getId(), new Asc.Range(this.nCol, this.nRow, this.nCol, this.nRow), new UndoRedoData_CellSimpleData(this.nRow, this.nCol, oRes.oldVal, oRes.newVal));
	this.compiledXfs = null;
	this.oValue.cleanCache();
};
Cell.prototype.setFontAlign=function(val){
    var oRes = this.ws.workbook.oStyleManager.setFontAlign(this, val);
	this.oValue.setFontAlign(this, val);
    if(History.Is_On() && oRes.oldVal != oRes.newVal)
        History.Add(AscCommonExcel.g_oUndoRedoCell, AscCH.historyitem_Cell_FontAlign, this.ws.getId(), new Asc.Range(this.nCol, this.nRow, this.nCol, this.nRow), new UndoRedoData_CellSimpleData(this.nRow, this.nCol, oRes.oldVal, oRes.newVal));
	this.compiledXfs = null;
	this.oValue.cleanCache();
};
Cell.prototype.setAlignVertical=function(val){
	var oRes = this.ws.workbook.oStyleManager.setAlignVertical(this, val);
    if(History.Is_On() && oRes.oldVal != oRes.newVal)
        History.Add(AscCommonExcel.g_oUndoRedoCell, AscCH.historyitem_Cell_AlignVertical, this.ws.getId(), new Asc.Range(this.nCol, this.nRow, this.nCol, this.nRow), new UndoRedoData_CellSimpleData(this.nRow, this.nCol, oRes.oldVal, oRes.newVal));
	this.compiledXfs = null;
};
Cell.prototype.setAlignHorizontal=function(val){
	var oRes = this.ws.workbook.oStyleManager.setAlignHorizontal(this, val);
    if(History.Is_On() && oRes.oldVal != oRes.newVal)
        History.Add(AscCommonExcel.g_oUndoRedoCell, AscCH.historyitem_Cell_AlignHorizontal, this.ws.getId(), new Asc.Range(this.nCol, this.nRow, this.nCol, this.nRow), new UndoRedoData_CellSimpleData(this.nRow, this.nCol, oRes.oldVal, oRes.newVal));
	this.compiledXfs = null;
};
Cell.prototype.setFill=function(val){
	var oRes = this.ws.workbook.oStyleManager.setFill(this, val);
    if(History.Is_On() && oRes.oldVal != oRes.newVal)
        History.Add(AscCommonExcel.g_oUndoRedoCell, AscCH.historyitem_Cell_Fill, this.ws.getId(), new Asc.Range(this.nCol, this.nRow, this.nCol, this.nRow), new UndoRedoData_CellSimpleData(this.nRow, this.nCol, oRes.oldVal, oRes.newVal));
	this.compiledXfs = null;
};
Cell.prototype.setBorder=function(val){
	var oRes = this.ws.workbook.oStyleManager.setBorder(this, val);
    if(History.Is_On() && oRes.oldVal != oRes.newVal){
		var oldVal = null;
		if(null != oRes.oldVal)
			oldVal = oRes.oldVal.clone();
		var newVal = null;
		if(null != oRes.newVal)
			newVal = oRes.newVal.clone();
        History.Add(AscCommonExcel.g_oUndoRedoCell, AscCH.historyitem_Cell_Border, this.ws.getId(), new Asc.Range(this.nCol, this.nRow, this.nCol, this.nRow), new UndoRedoData_CellSimpleData(this.nRow, this.nCol, oldVal, newVal));
	}
	this.compiledXfs = null;
};
Cell.prototype.setShrinkToFit=function(val){
	var oRes = this.ws.workbook.oStyleManager.setShrinkToFit(this, val);
    if(History.Is_On() && oRes.oldVal != oRes.newVal)
        History.Add(AscCommonExcel.g_oUndoRedoCell, AscCH.historyitem_Cell_ShrinkToFit, this.ws.getId(), new Asc.Range(this.nCol, this.nRow, this.nCol, this.nRow), new UndoRedoData_CellSimpleData(this.nRow, this.nCol, oRes.oldVal, oRes.newVal));
	this.compiledXfs = null;
};
Cell.prototype.setWrap=function(val){
	var oRes = this.ws.workbook.oStyleManager.setWrap(this, val);
    if(History.Is_On() && oRes.oldVal != oRes.newVal)
        History.Add(AscCommonExcel.g_oUndoRedoCell, AscCH.historyitem_Cell_Wrap, this.ws.getId(), new Asc.Range(this.nCol, this.nRow, this.nCol, this.nRow), new UndoRedoData_CellSimpleData(this.nRow, this.nCol, oRes.oldVal, oRes.newVal));
	this.compiledXfs = null;
};
Cell.prototype.setAngle=function(val){
    var oRes = this.ws.workbook.oStyleManager.setAngle(this, val);
    if(History.Is_On() && oRes.oldVal != oRes.newVal)
        History.Add(AscCommonExcel.g_oUndoRedoCell, AscCH.historyitem_Cell_Angle, this.ws.getId(), new Asc.Range(this.nCol, this.nRow, this.nCol, this.nRow), new UndoRedoData_CellSimpleData(this.nRow, this.nCol, oRes.oldVal, oRes.newVal));
    this.compiledXfs = null;
};
Cell.prototype.setVerticalText=function(val){
    var oRes = this.ws.workbook.oStyleManager.setVerticalText(this, val);
    if(History.Is_On() && oRes.oldVal != oRes.newVal)
        History.Add(AscCommonExcel.g_oUndoRedoCell, AscCH.historyitem_Cell_Angle, this.ws.getId(), new Asc.Range(this.nCol, this.nRow, this.nCol, this.nRow), new UndoRedoData_CellSimpleData(this.nRow, this.nCol, oRes.oldVal, oRes.newVal));
    this.compiledXfs = null;
};
Cell.prototype.setQuotePrefix=function(val){
	var oRes = this.ws.workbook.oStyleManager.setQuotePrefix(this, val);
    if(History.Is_On() && oRes.oldVal != oRes.newVal)
        History.Add(AscCommonExcel.g_oUndoRedoCell, AscCH.historyitem_Cell_SetQuotePrefix, this.ws.getId(), new Asc.Range(this.nCol, this.nRow, this.nCol, this.nRow), new UndoRedoData_CellSimpleData(this.nRow, this.nCol, oRes.oldVal, oRes.newVal));
	this.oValue.cleanCache();
};
Cell.prototype.setConditionalFormattingStyle=function(xfs){
	this.conditionalFormattingXfs = xfs;
	this.compiledXfs = null;
	this.oValue.cleanCache();
};
Cell.prototype.getConditionalFormattingStyle = function (xfs) {
    return this.conditionalFormattingXfs;
};
Cell.prototype.setTableStyle=function(xfs){
	this.tableXfs = xfs;
	this.compiledXfs = null;
	this.oValue.cleanCache();
};
Cell.prototype.getTableStyle=function(){
    return this.tableXfs;
};
Cell.prototype.setStyle=function(xfs){
	var oldVal = this.xfs;
	var newVal = null;
    this.xfs = null;
	if(null != xfs)
	{
        this.xfs = xfs.clone();
        newVal = this.xfs;
	}
	this.compiledXfs = null;
	this.oValue.cleanCache();
	if(History.Is_On() && false == ((null == oldVal && null == newVal) || (null != oldVal && null != newVal && true == oldVal.isEqual(newVal))))
	{
		if(null != oldVal)
			oldVal = oldVal.clone();
		if(null != newVal)
			newVal = newVal.clone();
		History.Add(AscCommonExcel.g_oUndoRedoCell, AscCH.historyitem_Cell_SetStyle, this.ws.getId(), new Asc.Range(this.nCol, this.nRow, this.nCol, this.nRow), new UndoRedoData_CellSimpleData(this.nRow, this.nCol, oldVal, newVal));
	}
	// if(this.isEmpty())
		// this.Remove();
};
Cell.prototype.getFormula=function(){
	if(null != this.sFormula)
		return this.sFormula;
	else
		return "";
};
Cell.prototype.getValueForEdit=function(numFormat){
	return this.oValue.getValueForEdit(this);
};
Cell.prototype.getValueForEdit2=function(numFormat){
	return this.oValue.getValueForEdit2(this);
};
Cell.prototype.getValueWithoutFormat=function(){
	return this.oValue.getValueWithoutFormat();
};
Cell.prototype.getValue=function(numFormat, dDigitsCount){
	return this.oValue.getValue(this);
};
Cell.prototype.getValue2=function(dDigitsCount, fIsFitMeasurer){
	if(null == fIsFitMeasurer)
		fIsFitMeasurer = function(aText){return true;};
	if(null == dDigitsCount)
		dDigitsCount = AscCommon.gc_nMaxDigCountView;
	return this.oValue.getValue2(this, dDigitsCount, fIsFitMeasurer);
};
Cell.prototype.getNumFormatStr=function(){
	if(null != this.xfs && null != this.xfs.num)
            return this.xfs.num.getFormat();
	return g_oDefaultFormat.Num.getFormat();
};
Cell.prototype.getNumFormat=function(){
    return oNumFormatCache.get(this.getNumFormatStr());
};
Cell.prototype.getNumFormatType=function(){
    return this.getNumFormat().getType();
};
Cell.prototype.moveHor=function(val){
	this.nCol += val;
};
Cell.prototype.moveVer=function(val){
	this.nRow += val;
};
Cell.prototype.getOffset=function(cell){
	return this.getOffset3(cell.nCol + 1, cell.nRow + 1);
};
Cell.prototype.getOffset2=function(cellId){
	var cAddr2 = new CellAddress(cellId);
	return this.getOffset3(cAddr2.col, cAddr2.row);
};
Cell.prototype.getOffset3=function(col, row){
	return new AscCommonExcel.CRangeOffset((this.nCol - col + 1), (this.nRow - row + 1));
};
Cell.prototype.getValueData = function(){
	return new UndoRedoData_CellValueData(this.sFormula, this.oValue.clone());
};
Cell.prototype.setValueData = function(Val){
	//значения устанавляваются через setValue, чтобы пересчитались формулы
	if(null != Val.formula)
		this.setValue("=" + Val.formula);
	else if(null != Val.value)
	{
	    var DataOld = null;
	    var DataNew = null;
	    if (History.Is_On())
	        DataOld = this.getValueData();
	    this.setValueCleanFormula();
	    this.oValue = Val.value.clone(this);
    this.ws.workbook.sortDependency();
	    if (History.Is_On()) {
	        DataNew = this.getValueData();
	        if (false == DataOld.isEqual(DataNew))
	            History.Add(AscCommonExcel.g_oUndoRedoCell, AscCH.historyitem_Cell_ChangeValue, this.ws.getId(), new Asc.Range(this.nCol, this.nRow, this.nCol, this.nRow), new UndoRedoData_CellSimpleData(this.nRow, this.nCol, DataOld, DataNew));
	    }
    }
	else
		this.setValue("");
};
Cell.prototype.setFormulaCA = function(ca){
	if(ca) this.sFormulaCA = true;
	else if( this.sFormulaCA ) this.sFormulaCA = null;
};
//-------------------------------------------------------------------------------------------------

/**
 * @constructor
 */
function Range(worksheet, r1, c1, r2, c2){
	this.worksheet = worksheet;
	this.bbox = new Asc.Range(c1, r1, c2, r2);
	//first last устарели, не убраны только для совместимости
	this.first = new CellAddress(this.bbox.r1, this.bbox.c1, 0);
	this.last = new CellAddress(this.bbox.r2, this.bbox.c2, 0);
}
Range.prototype.createFromBBox=function(worksheet, bbox){
	var oRes = new Range(worksheet, bbox.r1, bbox.c1, bbox.r2, bbox.c2);
	oRes.bbox = bbox.clone();
	return oRes;
};
Range.prototype.clone=function(oNewWs){
	if(!oNewWs)
		oNewWs = this.worksheet;
	return new Range(oNewWs, this.bbox.r1, this.bbox.c1, this.bbox.r2, this.bbox.c2);
};
Range.prototype.getFirst=function(){
	return this.first;
};
Range.prototype.getLast=function(){
	return this.last;
};
Range.prototype._foreach=function(action){
	if(null != action)
	{
		var oBBox = this.bbox;
		for(var i = oBBox.r1; i <= oBBox.r2; i++){
			for(var j = oBBox.c1; j <= oBBox.c2; j++){
				var oCurCell = this.worksheet._getCell(i, j);
				action(oCurCell, i, j, oBBox.r1, oBBox.c1);
			}
		}
	}
};
Range.prototype._foreach2=function(action){
	if(null != action)
	{
		var oBBox = this.bbox, minC = Math.min( this.worksheet.getColsCount(), oBBox.c2 ), minR = Math.min( this.worksheet.getRowsCount(), oBBox.r2 );
		for(var i = oBBox.r1; i <= minR; i++){
			for(var j = oBBox.c1; j <= minC; j++){
				var oCurCell = this.worksheet._getCellNoEmpty(i, j);
				var oRes = action(oCurCell, i, j, oBBox.r1, oBBox.c1);
				if(null != oRes)
					return oRes;
			}
		}
	}
};
Range.prototype._foreachNoEmpty=function(action){
	if(null != action)
	{
		var oBBox = this.bbox, minC = Math.min( this.worksheet.getColsCount(), oBBox.c2 ), minR = Math.min( this.worksheet.getRowsCount(), oBBox.r2 );
		for(var i = oBBox.r1; i <= minR; i++){
			for(var j = oBBox.c1; j <= minC; j++){
				var oCurCell = this.worksheet._getCellNoEmpty(i, j);
				if(null != oCurCell)
				{
					var oRes = action(oCurCell, i, j, oBBox.r1, oBBox.c1);
					if(null != oRes)
						return oRes;
				}
			}
		}
	}
};
Range.prototype._foreachRow=function(actionRow, actionCell){
	var oBBox = this.bbox;
	for(var i = oBBox.r1; i <= oBBox.r2; i++){
		var row = this.worksheet._getRow(i);
		if(row)
		{
			if(null != actionRow)
				actionRow(row);
			if(null != actionCell)
			{
				for(var j in row.c){
					var oCurCell = row.c[j];
					if(null != oCurCell)
						actionCell(oCurCell, i, j - 0, oBBox.r1, oBBox.c1);
				}
			}
		}
	}
};
Range.prototype._foreachRowNoEmpty=function(actionRow, actionCell){
	var oBBox = this.bbox;
	if(0 == oBBox.r1 && gc_nMaxRow0 == oBBox.r2)
	{
		var aRows = this.worksheet._getRows();
		for(var i in aRows)
		{
			var row = aRows[i];
			if( null != actionRow )
			{
				var oRes = actionRow(row);
				if(null != oRes)
					return oRes;
			}
			if( null != actionCell )
				for(var j in row.c){
					var oCurCell = row.c[j];
					if(null != oCurCell)
					{
						var oRes = actionCell(oCurCell, i - 0, j - 0, oBBox.r1, oBBox.c1);
						if(null != oRes)
							return oRes;
					}
				}
		}
	}
	else
	{
		var minR = Math.min(oBBox.r2,this.worksheet.getRowsCount());
		for(var i = oBBox.r1; i <= minR; i++){
			var row = this.worksheet._getRowNoEmpty(i);
			if(row)
			{
				if( null != actionRow )
				{
					var oRes = actionRow(row);
					if(null != oRes)
						return oRes;
				}
				if( null != actionCell )
					for(var j in row.c){
						var oCurCell = row.c[j];
						if(null != oCurCell)
						{
							var oRes = actionCell(oCurCell, i, j - 0, oBBox.r1, oBBox.c1);
							if(null != oRes)
								return oRes;
						}
					}
			}
		}
	}
};
Range.prototype._foreachCol=function(actionCol, actionCell){
	var oBBox = this.bbox;
	if(null != actionCol)
	{
		for(var i = oBBox.c1; i <= oBBox.c2; ++i)
		{
			var col = this.worksheet._getCol(i);
			if(null != col)
				actionCol(col);
		}
	}
	if(null != actionCell)
	{
		var aRows = this.worksheet._getRows();
		for(var i in aRows)
		{
			var row = aRows[i];
			if(row)
			{
				if(0 == oBBox.c1 && gc_nMaxCol0 == oBBox.c2)
				{
					for(var j in row.c)
					{
						var oCurCell = row.c[j];
						if(null != oCurCell)
							actionCell(oCurCell, i - 0, j - 0, oBBox.r1, oBBox.c1);
					}
				}
				else
				{
					for(var j = oBBox.c1; j <= oBBox.c2; ++j)
					{
						var oCurCell = row.c[j];
						if(null != oCurCell)
							actionCell(oCurCell, i - 0, j, oBBox.r1, oBBox.c1);
					}
				}
			}
		}
	}
};
Range.prototype._foreachColNoEmpty=function(actionCol, actionCell){
	var oBBox = this.bbox;
	var minC = Math.min( oBBox.c2,this.worksheet.getColsCount() );
	if(0 == oBBox.c1 && gc_nMaxCol0 == oBBox.c2)
	{
		if(null != actionCol)
		{
			var aCols = this.worksheet._getCols();
			for(var i in aCols)
			{
				var nIndex = i - 0;
				if(nIndex >= oBBox.c1 && nIndex <= minC )
				{
					var col = this.worksheet._getColNoEmpty(nIndex);
					if(null != col)
					{
						var oRes = actionCol(col);
						if(null != oRes)
							return oRes;
					}
				}
			}
		}
		if(null != actionCell)
		{
			var aRows = this.worksheet._getRows();
			for(var i in aRows)
			{
				var row = aRows[i];
				if(row)
				{
					for(var j in row.c)
					{
						var nIndex = j - 0;
						if(nIndex >= oBBox.c1 && nIndex <= minC)
						{
							var oCurCell = row.c[j];
							if(null != oCurCell)
							{
								var oRes = actionCell(oCurCell, i - 0, nIndex, oBBox.r1, oBBox.c1);
								if(null != oRes)
									return oRes;
							}
						}
					}
				}
			}
		}
	}
	else
	{
		if(null != actionCol)
		{
			for(var i = oBBox.c1; i <= minC; ++i)
			{
				var col = this.worksheet._getColNoEmpty(i);
				if(null != col)
				{
					var oRes = actionCol(col);
					if(null != oRes)
						return oRes;
				}
			}
		}
		if(null != actionCell)
		{
			var aRows = this.worksheet._getRows();
			for(var i in aRows)
			{
				var row = aRows[i];
				if(row)
				{
					for(var j = oBBox.c1; j <= minC; ++j)
					{
						var oCurCell = row.c[j];
						if(null != oCurCell)
						{
							var oRes = actionCell(oCurCell, i - 0, j, oBBox.r1, oBBox.c1);
							if(null != oRes)
								return oRes;
						}
					}
				}
			}
		}
	}
};
Range.prototype._foreachIndex=function(action){
	var oBBox = this.bbox;
	for(var i = oBBox.r1; i <= oBBox.r2; i++){
		for(var j = oBBox.c1; j <= oBBox.c2; j++){
			var res = action(i, j);
			if(null != res)
				return res;
		}
	}
	return null;
};
Range.prototype._getRangeType=function(oBBox){
	if(null == oBBox)
		oBBox = this.bbox;
	return getRangeType(oBBox);
};
Range.prototype._getValues = function (withEmpty) {
	var res = [];
	var fAction = function(c) {
		res.push({c: c, v: c.getValueWithoutFormat()});
	};
	if (withEmpty) {
		this._setProperty(null, null, fAction);
	} else {
		this._setPropertyNoEmpty(null, null, fAction);
	}
	return res;
};
Range.prototype._getValuesAndMap = function (withEmpty) {
	var v, arrRes = [], mapRes = {};
	var fAction = function(c) {
		v = c.getValueWithoutFormat();
		arrRes.push({c: c, v: v});
		mapRes[v.toLowerCase()] = true;
	};
	if (withEmpty) {
		this._setProperty(null, null, fAction);
	} else {
		this._setPropertyNoEmpty(null, null, fAction);
	}
	return {values: arrRes, map: mapRes};
};
Range.prototype._setProperty=function(actionRow, actionCol, actionCell){
	var nRangeType = this._getRangeType();
	if(c_oRangeType.Range == nRangeType)
		this._foreach(actionCell);
	else if(c_oRangeType.Row == nRangeType)
		this._foreachRow(actionRow, actionCell);
	else if(c_oRangeType.Col == nRangeType)
		this._foreachCol(actionCol, actionCell);
	else
	{
		//сюда не должны заходить вообще
		// this._foreachRow(actionRow, actionCell);
		// if(null != actionCol)
			// this._foreachCol(actionCol, null);
	}
};
Range.prototype._setPropertyNoEmpty=function(actionRow, actionCol, actionCell){
	var nRangeType = this._getRangeType();
	if(c_oRangeType.Range == nRangeType)
		return this._foreachNoEmpty(actionCell);
	else if(c_oRangeType.Row == nRangeType)
		return this._foreachRowNoEmpty(actionRow, actionCell);
	else if(c_oRangeType.Col == nRangeType)
		return this._foreachColNoEmpty(actionCol, actionCell);
	else
	{
		var oRes = this._foreachRowNoEmpty(actionRow, actionCell);
		if(null != oRes)
			return oRes;
		if(null != actionCol)
			oRes = this._foreachColNoEmpty(actionCol, null);
		return oRes;
	}
};
Range.prototype.containCell=function(cellId){
	var cellAddress = cellId;
	return 	cellAddress.getRow0() >= this.bbox.r1 && cellAddress.getCol0() >= this.bbox.c1 &&
			cellAddress.getRow0() <= this.bbox.r2 && cellAddress.getCol0() <= this.bbox.c2;
};
Range.prototype.containCell2=function(cell){
	return 	cell.nRow >= this.bbox.r1 && cell.nCol >= this.bbox.c1 &&
			cell.nRow <= this.bbox.r2 && cell.nCol <= this.bbox.c2;
};
Range.prototype.cross = function(cellAddress){

	if( cellAddress.getRow0() >= this.bbox.r1 && cellAddress.getRow0() <= this.bbox.r2 && this.bbox.c1 == this.bbox.c2)
		return {r:cellAddress.getRow()};
	if( cellAddress.getCol0() >= this.bbox.c1 && cellAddress.getCol0() <= this.bbox.c2 && this.bbox.r1 == this.bbox.r2)
		return {c:cellAddress.getCol()};

	return undefined;
};
Range.prototype.getWorksheet=function(){
	return this.worksheet;
};
Range.prototype.isFormula = function(){
	var nRow = this.bbox.r1;
	var nCol = this.bbox.c1;
	var cell = this.worksheet._getCellNoEmpty(nRow, nCol);
	return cell.isFormula();
};
Range.prototype.isOneCell=function(){
	var oBBox = this.bbox;
	return oBBox.r1 == oBBox.r2 && oBBox.c1 == oBBox.c2;
};
Range.prototype.isColumn = function(){
	if(this.first.getRow() == 1 && this.last.getRow() == AscCommon.gc_nMaxRow)
		return true;
	else
		return false;
};
Range.prototype.isRow = function(){
	if(this.first.getCol() == 1 && this.last.getCol() == AscCommon.gc_nMaxCol)
		return true;
	else
		return false;
};
Range.prototype.getBBox=function(){
	//1 - based
	return {r1: this.bbox.r1 + 1, r2: this.bbox.r2 + 1, c1: this.bbox.c1 + 1, c2: this.bbox.c2 + 1};
};
Range.prototype.getBBox0=function(){
	//0 - based
	return this.bbox;
};
Range.prototype.getName=function(){
    return this.bbox.getName();
};
Range.prototype.getCells=function(){
	var aResult = [];
	var oBBox = this.bbox;
	if(!((0 == oBBox.c1 && gc_nMaxCol0 == oBBox.c2) || (0 == oBBox.r1 && gc_nMaxRow0 == oBBox.r2)))
	{
		for(var i = oBBox.r1; i <= oBBox.r2; i++){
			for(var j = oBBox.c1; j <= oBBox.c2; j++){
				aResult.push(this.worksheet._getCell(i, j));
			}
		}
	}
	return aResult;
};
Range.prototype.setValue=function(val,callback, isCopyPaste){
	History.Create_NewPoint();
	History.StartTransaction();
	this._foreach(function(cell){
		cell.setValue(val,callback, isCopyPaste);
		// if(cell.isEmpty())
			// cell.Remove();
	});
	History.EndTransaction();
};
Range.prototype.setValue2=function(array){
	History.Create_NewPoint();
	History.StartTransaction();
    //[{"text":"qwe","format":{"b":true, "i":false, "u":Asc.EUnderline.underlineNone, "s":false, "fn":"Arial", "fs": 12, "c": 0xff00ff, "va": "subscript"  }},{}...]
	/*
		Устанавливаем значение в Range ячеек. В отличае от setValue, сюда мы попадаем только в случае ввода значения отличного от формулы. Таким образом, если в ячейке была формула, то для нее в графе очищается список ячеек от которых зависела. После чего выставляем флаг о необходимости пересчета.
	*/
	this._foreach(function(cell){
		cell.setValue2(array);
		// if(cell.isEmpty())
			// cell.Remove();
	});
	History.EndTransaction();
};
Range.prototype.setCellStyle=function(val){
	History.Create_NewPoint();
	this.createCellOnRowColCross();
	var fSetProperty = this._setProperty;
	var nRangeType = this._getRangeType();
	if(c_oRangeType.All == nRangeType)
	{
		this.worksheet.getAllCol().setCellStyle(val);
		fSetProperty = this._setPropertyNoEmpty;
	}
	fSetProperty.call(this, function(row){
			if(c_oRangeType.All == nRangeType && null == row.xfs)
				return;
			row.setCellStyle(val);
		},
		function(col){
			col.setCellStyle(val);
		},
		function(cell){
			cell.setCellStyle(val);
		});
};
Range.prototype.setTableStyle=function(val){
	this.createCellOnRowColCross();
	var fSetProperty = this._setProperty;
	var nRangeType = this._getRangeType();
	if(c_oRangeType.All == nRangeType || null === val)
	{
		//this.worksheet.getAllCol().setCellStyle(val);
		fSetProperty = this._setPropertyNoEmpty;
	}
	fSetProperty.call(this, function(row){
			if(c_oRangeType.All == nRangeType && null == row.xfs)
				return;
			//row.setCellStyle(val);
		},
		function(col){
			//col.setCellStyle(val);
		},
		function(cell){
			cell.setTableStyle(val);
		});
};
Range.prototype.setNumFormat=function(val){
	History.Create_NewPoint();
	this.createCellOnRowColCross();
	var fSetProperty = this._setProperty;
	var nRangeType = this._getRangeType();
	if(c_oRangeType.All == nRangeType)
	{
		this.worksheet.getAllCol().setNumFormat(val);
		fSetProperty = this._setPropertyNoEmpty;
	}
	fSetProperty.call(this, function(row){
		if(c_oRangeType.All == nRangeType && null == row.xfs)
			return;
		row.setNumFormat(val);
	},
	function(col){
		col.setNumFormat(val);
	},
	function(cell){
		cell.setNumFormat(val);
	});
};
Range.prototype.shiftNumFormat=function(nShift, aDigitsCount){
	History.Create_NewPoint();
	var bRes = false;
	this._setPropertyNoEmpty(null, null, function(cell, nRow0, nCol0, nRowStart, nColStart){
		bRes |= cell.shiftNumFormat(nShift, aDigitsCount[nCol0 - nColStart] || 8);
	});
	return bRes;
};
Range.prototype.setFont=function(val){
	History.Create_NewPoint();
	this.createCellOnRowColCross();
	var fSetProperty = this._setProperty;
	var nRangeType = this._getRangeType();
	if(c_oRangeType.All == nRangeType)
	{
		this.worksheet.getAllCol().setFont(val);
		fSetProperty = this._setPropertyNoEmpty;
	}
	fSetProperty.call(this, function(row){
		if(c_oRangeType.All == nRangeType && null == row.xfs)
			return;
		row.setFont(val);
	},
	function(col){
		col.setFont(val);
	},
	function(cell){
		cell.setFont(val);
	});
};
Range.prototype.setFontname=function(val){
	History.Create_NewPoint();
	this.createCellOnRowColCross();
	var fSetProperty = this._setProperty;
	var nRangeType = this._getRangeType();
	if(c_oRangeType.All == nRangeType)
	{
		this.worksheet.getAllCol().setFontname(val);
		fSetProperty = this._setPropertyNoEmpty;
	}
	fSetProperty.call(this, function(row){
		if(c_oRangeType.All == nRangeType && null == row.xfs)
			return;
		row.setFontname(val);
	},
	function(col){
		col.setFontname(val);
	},
	function(cell){
		cell.setFontname(val);
	});
};
Range.prototype.setFontsize=function(val){
	History.Create_NewPoint();
	this.createCellOnRowColCross();
	var fSetProperty = this._setProperty;
	var nRangeType = this._getRangeType();
	if(c_oRangeType.All == nRangeType)
	{
		this.worksheet.getAllCol().setFontsize(val);
		fSetProperty = this._setPropertyNoEmpty;
	}
	fSetProperty.call(this, function(row){
		if(c_oRangeType.All == nRangeType && null == row.xfs)
			return;
		row.setFontsize(val);
	},
	function(col){
		col.setFontsize(val);
	},
	function(cell){
		cell.setFontsize(val);
	});
};
Range.prototype.setFontcolor=function(val){
	History.Create_NewPoint();
	this.createCellOnRowColCross();
	var fSetProperty = this._setProperty;
	var nRangeType = this._getRangeType();
	if(c_oRangeType.All == nRangeType)
	{
		this.worksheet.getAllCol().setFontcolor(val);
		fSetProperty = this._setPropertyNoEmpty;
	}
	fSetProperty.call(this, function(row){
		if(c_oRangeType.All == nRangeType && null == row.xfs)
			return;
		row.setFontcolor(val);
	},
	function(col){
		col.setFontcolor(val);
	},
	function(cell){
		cell.setFontcolor(val);
	});
};
Range.prototype.setBold=function(val){
	History.Create_NewPoint();
	this.createCellOnRowColCross();
	var fSetProperty = this._setProperty;
	var nRangeType = this._getRangeType();
	if(c_oRangeType.All == nRangeType)
	{
		this.worksheet.getAllCol().setBold(val);
		fSetProperty = this._setPropertyNoEmpty;
	}
	fSetProperty.call(this, function(row){
		if(c_oRangeType.All == nRangeType && null == row.xfs)
			return;
		row.setBold(val);
	},
	function(col){
		col.setBold(val);
	},
	function(cell){
		cell.setBold(val);
	});
};
Range.prototype.setItalic=function(val){
	History.Create_NewPoint();
	this.createCellOnRowColCross();
	var fSetProperty = this._setProperty;
	var nRangeType = this._getRangeType();
	if(c_oRangeType.All == nRangeType)
	{
		this.worksheet.getAllCol().setItalic(val);
		fSetProperty = this._setPropertyNoEmpty;
	}
	fSetProperty.call(this, function(row){
		if(c_oRangeType.All == nRangeType && null == row.xfs)
			return;
		row.setItalic(val);
	},
	function(col){
		col.setItalic(val);
	},
	function(cell){
		cell.setItalic(val);
	});
};
Range.prototype.setUnderline=function(val){
	History.Create_NewPoint();
	this.createCellOnRowColCross();
	var fSetProperty = this._setProperty;
	var nRangeType = this._getRangeType();
	if(c_oRangeType.All == nRangeType)
	{
		this.worksheet.getAllCol().setUnderline(val);
		fSetProperty = this._setPropertyNoEmpty;
	}
	fSetProperty.call(this, function(row){
		if(c_oRangeType.All == nRangeType && null == row.xfs)
			return;
		row.setUnderline(val);
        },
        function(col){
            col.setUnderline(val);
        },
        function(cell){
            cell.setUnderline(val);
        });
};
Range.prototype.setStrikeout=function(val){
	History.Create_NewPoint();
	this.createCellOnRowColCross();
	var fSetProperty = this._setProperty;
	var nRangeType = this._getRangeType();
	if(c_oRangeType.All == nRangeType)
	{
		this.worksheet.getAllCol().setStrikeout(val);
		fSetProperty = this._setPropertyNoEmpty;
	}
	fSetProperty.call(this, function(row){
		if(c_oRangeType.All == nRangeType && null == row.xfs)
			return;
		row.setStrikeout(val);
	},
	function(col){
		col.setStrikeout(val);
	},
	function(cell){
		cell.setStrikeout(val);
	});
};
Range.prototype.setFontAlign=function(val){
	History.Create_NewPoint();
	this.createCellOnRowColCross();
	var fSetProperty = this._setProperty;
	var nRangeType = this._getRangeType();
	if(c_oRangeType.All == nRangeType)
	{
		this.worksheet.getAllCol().setFontAlign(val);
		fSetProperty = this._setPropertyNoEmpty;
	}
	fSetProperty.call(this, function(row){
		if(c_oRangeType.All == nRangeType && null == row.xfs)
			return;
		row.setFontAlign(val);
	},
	function(col){
		col.setFontAlign(val);
	},
	function(cell){
		cell.setFontAlign(val);
	});
};
Range.prototype.setAlignVertical=function(val){
	History.Create_NewPoint();
	if("none" == val)
		val = null;
	this.createCellOnRowColCross();
	var fSetProperty = this._setProperty;
	var nRangeType = this._getRangeType();
	if(c_oRangeType.All == nRangeType)
	{
		this.worksheet.getAllCol().setAlignVertical(val);
		fSetProperty = this._setPropertyNoEmpty;
	}
	fSetProperty.call(this, function(row){
		if(c_oRangeType.All == nRangeType && null == row.xfs)
			return;
		row.setAlignVertical(val);
	},
	function(col){
		col.setAlignVertical(val);
	},
	function(cell){
		cell.setAlignVertical(val);
	});
};
Range.prototype.setAlignHorizontal=function(val){
	History.Create_NewPoint();
	this.createCellOnRowColCross();
	var fSetProperty = this._setProperty;
	var nRangeType = this._getRangeType();
	if(c_oRangeType.All == nRangeType)
	{
		this.worksheet.getAllCol().setAlignHorizontal(val);
		fSetProperty = this._setPropertyNoEmpty;
	}
	fSetProperty.call(this, function(row){
		if(c_oRangeType.All == nRangeType && null == row.xfs)
			return;
		row.setAlignHorizontal(val);
	},
	function(col){
		col.setAlignHorizontal(val);
	},
	function(cell){
		cell.setAlignHorizontal(val);
	});
};
Range.prototype.setFill=function(val){
	History.Create_NewPoint();
	this.createCellOnRowColCross();
	var fSetProperty = this._setProperty;
	var nRangeType = this._getRangeType();
	if(c_oRangeType.All == nRangeType)
	{
		this.worksheet.getAllCol().setFill(val);
		fSetProperty = this._setPropertyNoEmpty;
	}
	fSetProperty.call(this, function(row){
		if(c_oRangeType.All == nRangeType && null == row.xfs)
			return;
		row.setFill(val);
	},
	function(col){
		col.setFill(val);
	},
	function(cell){
		cell.setFill(val);
	});
};
Range.prototype.setBorderSrc=function(border){
	History.Create_NewPoint();
	History.StartTransaction();
	if (null == border)
		border = new Border();
	this.createCellOnRowColCross();
	var fSetProperty = this._setProperty;
	var nRangeType = this._getRangeType();
	if(c_oRangeType.All == nRangeType)
	{
		this.worksheet.getAllCol().setBorder(border.clone());
		fSetProperty = this._setPropertyNoEmpty;
	}
	fSetProperty.call(this, function(row){
		if(c_oRangeType.All == nRangeType && null == row.xfs)
			return;
		row.setBorder(border.clone());
	},
	function(col){
		col.setBorder(border.clone());
	},
	function(cell){
		cell.setBorder(border.clone());
	});
	History.EndTransaction();
};
Range.prototype._setBorderMerge=function(bLeft, bTop, bRight, bBottom, oNewBorder, oCurBorder){
	var oTargetBorder = new Border();
	//не делаем clone для свойств потому у нас нельзя поменять свойство отдельное свойство border можно только применить border целиком
	if(bLeft)
		oTargetBorder.l = oNewBorder.l;
	else
		oTargetBorder.l = oNewBorder.iv;
	if(bTop)
		oTargetBorder.t = oNewBorder.t;
	else
		oTargetBorder.t = oNewBorder.ih;
	if(bRight)
		oTargetBorder.r = oNewBorder.r;
	else
		oTargetBorder.r = oNewBorder.iv;
	if(bBottom)
		oTargetBorder.b = oNewBorder.b;
	else
		oTargetBorder.b = oNewBorder.ih;
	oTargetBorder.d = oNewBorder.d;
	oTargetBorder.dd = oNewBorder.dd;
	oTargetBorder.du = oNewBorder.du;
	var oRes = null;
	if(null != oCurBorder)
	{
		oCurBorder.mergeInner(oTargetBorder);
		oRes = oCurBorder;
	}
	else
		oRes = oTargetBorder;
	return oRes;
};
Range.prototype._setCellBorder=function(bbox, cell, oNewBorder){
	if(null == oNewBorder)
		cell.setBorder(oNewBorder);
	else
	{
		var oCurBorder = null;
        if(null != cell.xfs && null != cell.xfs.border)
			oCurBorder = cell.xfs.border.clone();
		else
			oCurBorder = g_oDefaultFormat.Border.clone();
		var nRow = cell.nRow;
		var nCol = cell.nCol;
		cell.setBorder(this._setBorderMerge(nCol == bbox.c1, nRow == bbox.r1, nCol == bbox.c2, nRow == bbox.r2, oNewBorder, oCurBorder));
	}
};
Range.prototype._setRowColBorder=function(bbox, rowcol, bRow, oNewBorder){
	if(null == oNewBorder)
		rowcol.setBorder(oNewBorder);
	else
	{
		var oCurBorder = null;
		if(null != rowcol.xfs && null != rowcol.xfs.border)
			oCurBorder = rowcol.xfs.border.clone();
		var bLeft, bTop, bRight, bBottom = false;
		if(bRow)
		{
			bTop = rowcol.index == bbox.r1;
			bBottom = rowcol.index == bbox.r2;
		}
		else
		{
			bLeft = rowcol.index == bbox.c1;
			bRight = rowcol.index == bbox.c2;
		}
		rowcol.setBorder(this._setBorderMerge(bLeft, bTop, bRight, bBottom, oNewBorder, oCurBorder));
	}
};
Range.prototype._setBorderEdge=function(bbox, oItemWithXfs, nRow, nCol, oNewBorder){
	var oCurBorder = null;
    if(null != oItemWithXfs.xfs && null != oItemWithXfs.xfs.border)
		oCurBorder = oItemWithXfs.xfs.border;
	if(null != oCurBorder)
	{
		var oCurBorderProp = null;
		if(nCol == bbox.c1 - 1)
			oCurBorderProp = oCurBorder.r;
		else if(nRow == bbox.r1 - 1)
			oCurBorderProp = oCurBorder.b;
		else if(nCol == bbox.c2 + 1)
			oCurBorderProp = oCurBorder.l;
		else if(nRow == bbox.r2 + 1)
			oCurBorderProp = oCurBorder.t;
		var oNewBorderProp = null;
		if(null == oNewBorder)
			oNewBorderProp = new AscCommonExcel.BorderProp();
		else
		{
			if(nCol == bbox.c1 - 1)
				oNewBorderProp = oNewBorder.l;
			else if(nRow == bbox.r1 - 1)
				oNewBorderProp = oNewBorder.t;
			else if(nCol == bbox.c2 + 1)
				oNewBorderProp = oNewBorder.r;
			else if(nRow == bbox.r2 + 1)
				oNewBorderProp = oNewBorder.b;
		}
		
		if(null != oNewBorderProp && null != oCurBorderProp && c_oAscBorderStyles.None != oCurBorderProp.s && (null == oNewBorder || c_oAscBorderStyles.None != oNewBorderProp.s) &&
			(oNewBorderProp.s != oCurBorderProp.s || oNewBorderProp.getRgbOrNull() != oCurBorderProp.getRgbOrNull())){
				var oTargetBorder = oCurBorder.clone();
				if(nCol == bbox.c1 - 1)
					oTargetBorder.r = new AscCommonExcel.BorderProp();
				else if(nRow == bbox.r1 - 1)
					oTargetBorder.b = new AscCommonExcel.BorderProp();
				else if(nCol == bbox.c2 + 1)
					oTargetBorder.l = new AscCommonExcel.BorderProp();
				else if(nRow == bbox.r2 + 1)
					oTargetBorder.t = new AscCommonExcel.BorderProp();
				oItemWithXfs.setBorder(oTargetBorder);
			}
	}
};
Range.prototype.setBorder=function(border){
	//border = null очисть border
	//"ih" - внутренние горизонтальные, "iv" - внутренние вертикальные
	History.Create_NewPoint();
	var _this = this;
	var oBBox = this.bbox;
	this.createCellOnRowColCross();
	var fSetProperty = this._setProperty;
	var nRangeType = this._getRangeType();
	if(c_oRangeType.All == nRangeType)
	{
		var oAllCol = this.worksheet.getAllCol();
		_this._setRowColBorder(oBBox, oAllCol, false, border);
		fSetProperty = this._setPropertyNoEmpty;
	}
	fSetProperty.call(this, function(row){
		if(c_oRangeType.All == nRangeType && null == row.xfs)
			return;
		_this._setRowColBorder(oBBox, row, true, border);
	},
	function(col){
		_this._setRowColBorder(oBBox, col, false, border);
	},
	function(cell){
		_this._setCellBorder(oBBox, cell, border);
	});
	//убираем граничные border
	var aEdgeBorders = [];
	if(oBBox.c1 > 0 && (null == border || !border.l.isEmpty()))
		aEdgeBorders.push(this.worksheet.getRange3(oBBox.r1, oBBox.c1 - 1, oBBox.r2, oBBox.c1 - 1));
	if(oBBox.r1 > 0 && (null == border || !border.t.isEmpty()))
		aEdgeBorders.push(this.worksheet.getRange3(oBBox.r1 - 1, oBBox.c1, oBBox.r1 - 1, oBBox.c2));
	if(oBBox.c2 < gc_nMaxCol0 && (null == border || !border.r.isEmpty()))
		aEdgeBorders.push(this.worksheet.getRange3(oBBox.r1, oBBox.c2 + 1, oBBox.r2, oBBox.c2 + 1));
	if(oBBox.r2 < gc_nMaxRow0 && (null == border || !border.b.isEmpty()))
		aEdgeBorders.push(this.worksheet.getRange3(oBBox.r2 + 1, oBBox.c1, oBBox.r2 + 1, oBBox.c2));
	for(var i = 0, length = aEdgeBorders.length; i < length; i++)
	{
		var range = aEdgeBorders[i];
		range._setPropertyNoEmpty(function(row){
			if(c_oRangeType.All == nRangeType && null == row.xfs)
				return;
			_this._setBorderEdge(oBBox, row, row.index, 0, border);
		},
		function(col){
			_this._setBorderEdge(oBBox, col, 0, col.index, border);
		},
		function(cell){
			_this._setBorderEdge(oBBox, cell, cell.nRow, cell.nCol, border);
		});
	}
};
Range.prototype.setShrinkToFit=function(val){
	History.Create_NewPoint();
	this.createCellOnRowColCross();
	var fSetProperty = this._setProperty;
	var nRangeType = this._getRangeType();
	if(c_oRangeType.All == nRangeType)
	{
		this.worksheet.getAllCol().setShrinkToFit(val);
		fSetProperty = this._setPropertyNoEmpty;
	}
	fSetProperty.call(this, function(row){
		if(c_oRangeType.All == nRangeType && null == row.xfs)
			return;
		row.setShrinkToFit(val);
	},
	function(col){
		col.setShrinkToFit(val);
	},
	function(cell){
		cell.setShrinkToFit(val);
	});
};
Range.prototype.setWrap=function(val){
	History.Create_NewPoint();
	this.createCellOnRowColCross();
	var fSetProperty = this._setProperty;
	var nRangeType = this._getRangeType();
	if(c_oRangeType.All == nRangeType)
	{
		this.worksheet.getAllCol().setWrap(val);
		fSetProperty = this._setPropertyNoEmpty;
	}
	fSetProperty.call(this, function(row){
		if(c_oRangeType.All == nRangeType && null == row.xfs)
			return;
		row.setWrap(val);
	},
	function(col){
		col.setWrap(val);
	},
	function(cell){
		cell.setWrap(val);
	});
};
Range.prototype.setAngle=function(val){
    History.Create_NewPoint();
	this.createCellOnRowColCross();
	var fSetProperty = this._setProperty;
	var nRangeType = this._getRangeType();
	if(c_oRangeType.All == nRangeType)
	{
		this.worksheet.getAllCol().setAngle(val);
		fSetProperty = this._setPropertyNoEmpty;
	}
	fSetProperty.call(this, function(row){
		if(c_oRangeType.All == nRangeType && null == row.xfs)
			return;
		row.setAngle(val);
	},
	function(col){
		col.setAngle(val);
	},
	function(cell){
		cell.setAngle(val);
	});
};
Range.prototype.setVerticalText=function(val){
    History.Create_NewPoint();
	this.createCellOnRowColCross();
	var fSetProperty = this._setProperty;
	var nRangeType = this._getRangeType();
	if(c_oRangeType.All == nRangeType)
	{
		this.worksheet.getAllCol().setVerticalText(val);
		fSetProperty = this._setPropertyNoEmpty;
	}
	fSetProperty.call(this, function(row){
		if(c_oRangeType.All == nRangeType && null == row.xfs)
			return;
		row.setVerticalText(val);
	},
	function(col){
		col.setVerticalText(val);
	},
	function(cell){
		cell.setVerticalText(val);
	});
};
Range.prototype.setType=function(type){
    History.Create_NewPoint();
	this.createCellOnRowColCross();
	var fSetProperty = this._setProperty;
	var nRangeType = this._getRangeType();
	if(c_oRangeType.All == nRangeType)
		fSetProperty = this._setPropertyNoEmpty;
	fSetProperty.call(this, null, null,
	function(cell){
		cell.setType(type);
	});
};
Range.prototype.getType=function(){
	var cell = this.worksheet._getCellNoEmpty(this.bbox.r1,this.bbox.c1);
	if(null != cell)
		return cell.getType();
	else
		return null;
};
Range.prototype.isEmptyText=function(){
	var cell = this.worksheet._getCellNoEmpty(this.bbox.r1,this.bbox.c1);
	return (null != cell) ? cell.isEmptyText() : true;
};
Range.prototype.isEmptyTextString=function(){
	var cell = this.worksheet._getCellNoEmpty(this.bbox.r1,this.bbox.c1);
	return (null != cell) ? cell.isEmptyTextString() : true;
};
Range.prototype.isFormula=function(){
	var cell = this.worksheet._getCellNoEmpty(this.bbox.r1,this.bbox.c1);
	return (null != cell) ? cell.isFormula() : false;
};
Range.prototype.getFormula=function(){
	var cell = this.worksheet._getCellNoEmpty(this.bbox.r1,this.bbox.c1);
	if(null != cell)
		return cell.getFormula();
	else
		return "";
};
Range.prototype.getValueForEdit=function(){
	var cell = this.worksheet._getCellNoEmpty(this.bbox.r1,this.bbox.c1);
	if(null != cell)
	{
		var numFormat = this.getNumFormat();
		return cell.getValueForEdit(numFormat);
	}
	else
		return "";
};
Range.prototype.getValueForEdit2=function(){
	var cell = this.worksheet._getCellNoEmpty(this.bbox.r1,this.bbox.c1);
	if(null != cell)
	{
		var numFormat = this.getNumFormat();
		return cell.getValueForEdit2(numFormat);
	}
	else
	{
		var oRow = this.worksheet._getRowNoEmpty(this.bbox.r1);
		var oCol = this.worksheet._getColNoEmptyWithAll(this.bbox.c1);
		var xfs = null;
		if(null != oRow && null != oRow.xfs)
			xfs = oRow.xfs.clone();
		else if(null != oCol && null != oCol.xfs)
			xfs = oCol.xfs.clone();
		var oTempCell = new Cell(this.worksheet);
		oTempCell.create(xfs, this.bbox.r1, this.bbox.c1);
		return oTempCell.getValueForEdit2();
	}
};
Range.prototype.getValueWithoutFormat=function(){
	var cell = this.worksheet._getCellNoEmpty(this.bbox.r1, this.bbox.c1);
	if(null != cell)
		return cell.getValueWithoutFormat();
	else
		return "";
};
Range.prototype.getValue=function(){
	return this.getValueWithoutFormat();
};
Range.prototype.getValueWithFormat=function(){
	var cell = this.worksheet._getCellNoEmpty(this.bbox.r1, this.bbox.c1);
	if(null != cell)
		return cell.getValue();
	else
		return "";
};
Range.prototype.getValue2=function(dDigitsCount, fIsFitMeasurer){
    //[{"text":"qwe","format":{"b":true, "i":false, "u":Asc.EUnderline.underlineNone, "s":false, "fn":"Arial", "fs": 12, "c": 0xff00ff, "va": "subscript"  }},{}...]
	var cell = this.worksheet._getCellNoEmpty(this.bbox.r1, this.bbox.c1);
	if(null != cell)
		return cell.getValue2(dDigitsCount, fIsFitMeasurer);
	else
	{
		var oRow = this.worksheet._getRowNoEmpty(this.bbox.r1);
		var oCol = this.worksheet._getColNoEmptyWithAll(this.bbox.c1);
		var xfs = null;
		if(null != oRow && null != oRow.xfs)
			xfs = oRow.xfs.clone();
		else if(null != oCol && null != oCol.xfs)
			xfs = oCol.xfs.clone();
		var oTempCell = new Cell(this.worksheet);
		oTempCell.create(xfs, this.bbox.r1, this.bbox.c1);
		return oTempCell.getValue2(dDigitsCount, fIsFitMeasurer);
	}
};
Range.prototype.getXfId=function(){
	var nRow = this.bbox.r1;
	var nCol = this.bbox.c1;
	var cell = this.worksheet._getCellNoEmpty(nRow, nCol);
	if(null != cell) {
	    var xfs = cell.getCompiledStyle();
		if(null != xfs && null != xfs.XfId)
			return xfs.XfId;
	} else {
		//стили столбов и колонок
		var row = this.worksheet._getRowNoEmpty(nRow);
		if(null != row && null != row.xfs && null != row.xfs.XfId)
			return row.xfs.XfId;
		var col = this.worksheet._getColNoEmptyWithAll(nCol);
		if(null != col && null != col.xfs && null != col.xfs.XfId)
			return col.xfs.XfId;
	}
	return g_oDefaultFormat.XfId;
};
Range.prototype.getStyleName=function(){
	var res = this.worksheet.workbook.CellStyles.getStyleNameByXfId(this.getXfId());

	// ToDo убрать эту заглушку (нужно делать на открытии) в InitStyleManager
	return res || this.worksheet.workbook.CellStyles.getStyleNameByXfId(g_oDefaultFormat.XfId);
};
Range.prototype.getTableStyle=function(){
    var cell = this.worksheet._getCellNoEmpty(this.bbox.r1,this.bbox.c1);
    return cell ? cell.getTableStyle() : null;
};
Range.prototype.getNumFormat=function(){
	return oNumFormatCache.get(this.getNumFormatStr());
};
Range.prototype.getNumFormatStr=function(){
	var nRow = this.bbox.r1;
	var nCol = this.bbox.c1;
	var cell = this.worksheet._getCellNoEmpty(nRow, nCol);
	if(null != cell)
    {
	    var xfs = cell.getCompiledStyle();
        if(null != xfs && null != xfs.num)
            return xfs.num.getFormat();
    }
	else
	{
		//стили столбов и колонок
		var row = this.worksheet._getRowNoEmpty(nRow);
		if(null != row && null != row.xfs && null != row.xfs.num)
			return row.xfs.num.getFormat();
		var col = this.worksheet._getColNoEmptyWithAll(nCol);
		if(null != col && null != col.xfs && null != col.xfs.num)
			return col.xfs.num.getFormat();
	}
    return g_oDefaultFormat.Num.getFormat();
};
Range.prototype.getNumFormatType=function(){
	return this.getNumFormat().getType();
};
// Узнаем отличается ли шрифт (размер и гарнитура) в ячейке от шрифта в строке
Range.prototype.isNotDefaultFont = function () {
	// Получаем фонт ячейки
	var cellFont = this.getFont();
	var rowFont = g_oDefaultFormat.Font;
	var row = this.worksheet._getRowNoEmpty(this.bbox.r1);
	if (null != row && null != row.xfs && null != row.xfs.font)
		rowFont = row.xfs.font;
	else if (null != this.oAllCol)
		rowFont = this.oAllCol;

	return (cellFont.fn !== rowFont.fn || cellFont.fs !== rowFont.fs);
};
Range.prototype.getFont = function(){
	var nRow = this.bbox.r1;
	var nCol = this.bbox.c1;
	var cell = this.worksheet._getCellNoEmpty(nRow, nCol);
	if(null != cell)
    {
		var xfs = cell.getCompiledStyle();
        if(null != xfs && null != xfs.font)
            return xfs.font;
    }
	else
	{
		//стили столбов и колонок
		var row = this.worksheet._getRowNoEmpty(nRow);
		if(null != row && null != row.xfs && null != row.xfs.font)
			return row.xfs.font;
		var col = this.worksheet._getColNoEmptyWithAll(nCol);
		if(null != col && null != col.xfs && null != col.xfs.font)
			return col.xfs.font;
	}
    return g_oDefaultFormat.Font;
};
Range.prototype.getFontname=function(){
	return this.getFont().fn;
};
Range.prototype.getFontsize=function(){
	return this.getFont().fs;
};
Range.prototype.getFontcolor=function(){
	return this.getFont().c;
};
Range.prototype.getBold=function(){
	return this.getFont().b;
};
Range.prototype.getItalic=function(){
	return this.getFont().i;
};
Range.prototype.getUnderline=function(){
	return this.getFont().u;
};
Range.prototype.getStrikeout=function(){
	return this.getFont().s;
};
Range.prototype.getFontAlign=function(){
	return this.getFont().va;
};
Range.prototype.getQuotePrefix=function(){
	var nRow = this.bbox.r1;
	var nCol = this.bbox.c1;
	var cell = this.worksheet._getCellNoEmpty(nRow, nCol);
	if(null != cell)
	{
		var xfs = cell.getCompiledStyle();
		if(null != xfs && null != xfs.QuotePrefix)
			return xfs.QuotePrefix;
	}
	return false;
};
Range.prototype.getAlignVertical=function(){
	var nRow = this.bbox.r1;
	var nCol = this.bbox.c1;
	var cell = this.worksheet._getCellNoEmpty(nRow, nCol);
	if(null != cell)
    {
		var xfs = cell.getCompiledStyle();
        if(null != xfs)
		{
			if(null != xfs.align)
				return xfs.align.ver;
			else
				return g_oDefaultFormat.AlignAbs.ver;
		}
    }
	else
	{
		//стили столбов и колонок
		var row = this.worksheet._getRowNoEmpty(nRow);
		if(null != row && null != row.xfs && null != row.xfs.align)
			return row.xfs.align.ver;
		var col = this.worksheet._getColNoEmptyWithAll(nCol);
		if(null != col && null != col.xfs && null != col.xfs.align)
			return col.xfs.align.ver;
	}
    return g_oDefaultFormat.Align.ver;
};
Range.prototype.getAlignHorizontal=function(){
	var nRow = this.bbox.r1;
	var nCol = this.bbox.c1;
	var cell = this.worksheet._getCellNoEmpty(nRow, nCol);
	if(null != cell)
    {
		var xfs = cell.getCompiledStyle();
        if(null != xfs)
		{
			if(null != xfs.align)
				return xfs.align.hor;
			else
				return g_oDefaultFormat.AlignAbs.hor;
		}
    }
	else
	{
		//стили столбов и колонок
		var row = this.worksheet._getRowNoEmpty(nRow);
		if(null != row && null != row.xfs && null != row.xfs.align)
			return row.xfs.align.hor;
		var col = this.worksheet._getColNoEmptyWithAll(nCol);
		if(null != col && null != col.xfs && null != col.xfs.align)
			return col.xfs.align.hor;
	}
    return g_oDefaultFormat.Align.hor;
};
Range.prototype.getAlignHorizontalByValue=function(){
	//возвращает Align в зависимости от значния в ячейке
	//values:  none, center, justify, left , right, null
	var align = this.getAlignHorizontal();
	if("none" == align){
		//пытаемся определить по значению
		var nRow = this.bbox.r1;
		var nCol = this.bbox.c1;
		var cell = this.worksheet._getCellNoEmpty(nRow, nCol);
		if(cell){
			switch(cell.getType()){
				case CellValueType.String:align = "left";break;
				case CellValueType.Bool:
				case CellValueType.Error:align = "center";break;
				default:
				//Если есть value и не проставлен тип значит это число, у всех остальных типов значение не null
				if(this.getValueWithoutFormat())
				{
					//смотрим 
					var oNumFmt = this.getNumFormat();
					if(true == oNumFmt.isTextFormat())
						align = "left";
					else
						align = "right";
				}
				else
					align = "left";
				break;
			}
		}
		if("none" == align)
			align = "left";
	}
	return align;
};
Range.prototype.getFill=function(){
	var nRow = this.bbox.r1;
	var nCol = this.bbox.c1;
	var cell = this.worksheet._getCellNoEmpty(nRow, nCol);
	if(null != cell)
    {
		var xfs = cell.getCompiledStyle();
        if(null != xfs && null != xfs.fill)
            return xfs.fill.bg;
    }
	else
	{
		//стили столбов и колонок
		var row = this.worksheet._getRowNoEmpty(nRow);
		if(null != row && null != row.xfs && null != row.xfs.fill)
			return row.xfs.fill.bg;
		var col = this.worksheet._getColNoEmptyWithAll(nCol);
		if(null != col && null != col.xfs && null != col.xfs.fill)
			return col.xfs.fill.bg;
	}
    return g_oDefaultFormat.Fill.bg;
};
Range.prototype.getBorderSrc=function(_cell){
	//Возвращает как записано в файле, не проверяя бордеры соседних ячеек
	//формат
	//\{"l": {"s": "solid", "c": 0xff0000}, "t": {} ,"r": {} ,"b": {} ,"d": {},"dd": false ,"du": false }
	//"s" values: none, thick, thin, medium, dashDot, dashDotDot, dashed, dotted, double, hair, mediumDashDot, mediumDashDotDot, mediumDashed, slantDashDot
	//"dd" diagonal line, starting at the top left corner of the cell and moving down to the bottom right corner of the cell
	//"du" diagonal line, starting at the bottom left corner of the cell and moving up to the top right corner of the cell
    if(null == _cell)
        _cell = this.getFirst();
 	var nRow = _cell.getRow0();
	var nCol = _cell.getCol0();
	var cell = this.worksheet._getCellNoEmpty(nRow, nCol);
	if(null != cell)
    {
		var xfs = cell.getCompiledStyle();
        if(null != xfs && null != xfs.border)
            return xfs.border;
    }
	else
	{
		//стили столбов и колонок
		var row = this.worksheet._getRowNoEmpty(nRow);
		if(null != row && null != row.xfs && null != row.xfs.border)
			return row.xfs.border;
		var col = this.worksheet._getColNoEmptyWithAll(nCol);
		if(null != col && null != col.xfs && null != col.xfs.border)
			return col.xfs.border;
	}
    return g_oDefaultFormat.Border;
};
Range.prototype.getBorder=function(_cell){
	//_cell - optional
	//Возвращает как записано в файле, не проверяя бордеры соседних ячеек
	//формат
	//\{"l": {"s": "solid", "c": 0xff0000}, "t": {} ,"r": {} ,"b": {} ,"d": {},"dd": false ,"du": false }
	//"s" values: none, thick, thin, medium, dashDot, dashDotDot, dashed, dotted, double, hair, mediumDashDot, mediumDashDotDot, mediumDashed, slantDashDot
	//"dd" diagonal line, starting at the top left corner of the cell and moving down to the bottom right corner of the cell
	//"du" diagonal line, starting at the bottom left corner of the cell and moving up to the top right corner of the cell
    var oRes = this.getBorderSrc(_cell);
    if(null != oRes)
        return oRes;
    else
        return g_oDefaultFormat.Border;
};
Range.prototype.getBorderFull=function(){
	//Возвращает как excel, т.е. проверяет бордеры соседних ячеек
	//
	//\{"l": {"s": "solid", "c": 0xff0000}, "t": {} ,"r": {} ,"b": {} ,"d": {},"dd": false ,"du": false }
	//"s" values: none, thick, thin, medium, dashDot, dashDotDot, dashed, dotted, double, hair, mediumDashDot, mediumDashDotDot, mediumDashed, slantDashDot
	//
	//"dd" diagonal line, starting at the top left corner of the cell and moving down to the bottom right corner of the cell
	//"du" diagonal line, starting at the bottom left corner of the cell and moving up to the top right corner of the cell
	var borders = this.getBorder(this.getFirst()).clone();
	var nRow = this.bbox.r1;
	var nCol = this.bbox.c1;    
	if(c_oAscBorderStyles.None === borders.l.s){
		if(nCol > 1){
			var left = this.getBorder(new CellAddress(nRow, nCol - 1, 0));
			if(c_oAscBorderStyles.None !== left.r.s)
				borders.l = left.r;
		}
	}
	if(c_oAscBorderStyles.None === borders.t.s){
		if(nRow > 1){
			var top = this.getBorder(new CellAddress(nRow - 1, nCol, 0));
			if(c_oAscBorderStyles.None !== top.b.s)
				borders.t = top.b;
		}
	}
	if(c_oAscBorderStyles.None === borders.r.s){
		var right = this.getBorder(new CellAddress(nRow, nCol + 1, 0));
		if(c_oAscBorderStyles.None !== right.l.s)
			borders.r = right.l;
	}
	if(c_oAscBorderStyles.None === borders.b.s){
		var bottom = this.getBorder(new CellAddress(nRow + 1, nCol, 0));
		if(c_oAscBorderStyles.None !== bottom.t.s)
			borders.b = bottom.t;
	}
	return borders;
};
Range.prototype.getShrinkToFit=function(){
	var nRow = this.bbox.r1;
	var nCol = this.bbox.c1;
	var cell = this.worksheet._getCellNoEmpty(nRow, nCol);
	if(null != cell)
    {
		var xfs = cell.getCompiledStyle();
        if(null != xfs)
		{
			if(null != xfs.align)
				return xfs.align.shrink;
			else
				return g_oDefaultFormat.AlignAbs.shrink;
		}
    }
	else
	{
		//стили столбов и колонок
		var row = this.worksheet._getRowNoEmpty(nRow);
		if(null != row && null != row.xfs && null != row.xfs.align)
			return row.xfs.align.shrink;
		var col = this.worksheet._getColNoEmptyWithAll(nCol);
		if(null != col && null != col.xfs && null != col.xfs.align)
			return col.xfs.align.shrink;
	}
    return g_oDefaultFormat.Align.shrink;
};
Range.prototype.getWrapByAlign = function (align) {
	// Для justify wrap всегда true
	return "justify" === align.hor ? true : align.wrap;
};
Range.prototype.getWrap=function(){
	var nRow = this.bbox.r1;
	var nCol = this.bbox.c1;
	var cell = this.worksheet._getCellNoEmpty(nRow, nCol);
	if(null != cell) {
		var xfs = cell.getCompiledStyle();
        if(null != xfs) {
			if(null != xfs.align)
				return this.getWrapByAlign(xfs.align);
			else
				return this.getWrapByAlign(g_oDefaultFormat.AlignAbs);
		}
    } else {
		//стили столбов и колонок
		var row = this.worksheet._getRowNoEmpty(nRow);
		if(null != row && null != row.xfs && null != row.xfs.align)
			return this.getWrapByAlign(row.xfs.align);
		var col = this.worksheet._getColNoEmptyWithAll(nCol);
		if(null != col && null != col.xfs && null != col.xfs.align)
			return this.getWrapByAlign(col.xfs.align);
	}
    return this.getWrapByAlign(g_oDefaultFormat.Align);
};
Range.prototype.getAngle=function(){
	//угол от -90 до 90 против часовой стрелки от оси OX
	var nRow = this.bbox.r1;
	var nCol = this.bbox.c1;
	var cell = this.worksheet._getCellNoEmpty(nRow, nCol);
	if(null != cell)
    {
		var xfs = cell.getCompiledStyle();
        if(null != xfs)
		{
			if(null != xfs.align)
				return angleFormatToInterface(xfs.align.angle);
			else
				return angleFormatToInterface(g_oDefaultFormat.AlignAbs.angle);
		}
    }
	else
	{
		//стили столбов и колонок
		var row = this.worksheet._getRowNoEmpty(nRow);
		if(null != row && null != row.xfs && null != row.xfs.align)
			return angleFormatToInterface(row.xfs.align.angle);
		var col = this.worksheet._getColNoEmptyWithAll(nCol);
		if(null != col && null != col.xfs && null != col.xfs.align)
			return angleFormatToInterface(col.xfs.align.angle);
	}
    return angleFormatToInterface(g_oDefaultFormat.Align.angle);
};
Range.prototype.getVerticalText=function(){
	var nRow = this.bbox.r1;
	var nCol = this.bbox.c1;
	var cell = this.worksheet._getCellNoEmpty(nRow, nCol);
	if(null != cell)
    {
		var xfs = cell.getCompiledStyle();
        if(null != xfs)
		{
			if(null != xfs.align)
				return g_nVerticalTextAngle == xfs.align.angle;
			else
				return g_nVerticalTextAngle == g_oDefaultFormat.AlignAbs.angle;
		}
    }
	else
	{
		//стили столбов и колонок
		var row = this.worksheet._getRowNoEmpty(nRow);
		if(null != row && null != row.xfs && null != row.xfs.align)
			return g_nVerticalTextAngle == row.xfs.align.angle;
		var col = this.worksheet._getColNoEmptyWithAll(nCol);
		if(null != col && null != col.xfs && null != col.xfs.align)
			return g_nVerticalTextAngle == col.xfs.align.angle;
	}
    return g_nVerticalTextAngle == g_oDefaultFormat.Align.angle;
};
Range.prototype.hasMerged=function(){
	var aMerged = this.worksheet.mergeManager.get(this.bbox);
	if(aMerged.all.length > 0)
		return aMerged.all[0].bbox;
	return null;
};
Range.prototype.mergeOpen=function(){
	this.worksheet.mergeManager.add(this.bbox, 1);
};
Range.prototype.merge=function(type){
	if(null == type)
		type = Asc.c_oAscMergeOptions.Merge;
	var oBBox = this.bbox;
	History.Create_NewPoint();
	History.StartTransaction();	
	if(oBBox.r1 == oBBox.r2 && oBBox.c1 == oBBox.c2){
		if(type == Asc.c_oAscMergeOptions.MergeCenter)
			this.setAlignHorizontal("center");
		History.EndTransaction();
		return;
	}
	if(this.hasMerged())
	{
		this.unmerge();
		if(type == Asc.c_oAscMergeOptions.MergeCenter)
		{
			//сбрасываем AlignHorizontal
			this.setAlignHorizontal("none");
			History.EndTransaction();
			return;
		}
	}
	//пробегаемся по границе диапазона, чтобы посмотреть какие границы нужно оставлять
	var oLeftBorder = null;
	var oTopBorder = null;
	var oRightBorder = null;
	var oBottomBorder = null;
	var nRangeType = this._getRangeType(oBBox);
	if(c_oRangeType.Range == nRangeType)
	{
		var oThis = this;
		var fGetBorder = function(bRow, v1, v2, v3, type)
		{
			var oRes = null;
			for(var i = v1; i <= v2; ++i)
			{
				var bNeedDelete = true;
				var oCurCell;
				if(bRow)
					oCurCell = oThis.worksheet._getCellNoEmpty(v3, i);
				else
					oCurCell = oThis.worksheet._getCellNoEmpty(i, v3);
				if(null != oCurCell && null != oCurCell.xfs && null != oCurCell.xfs.border)
				{
					var border = oCurCell.xfs.border;
					var oBorderProp;
					switch(type)
					{
						case 1: oBorderProp = border.l;break;
						case 2: oBorderProp = border.t;break;
						case 3: oBorderProp = border.r;break;
						case 4: oBorderProp = border.b;break;
					}
					if(false == oBorderProp.isEmpty())
					{
						if(null == oRes)
						{
							oRes = oBorderProp;
							bNeedDelete = false;
						}
						else if(true == oRes.isEqual(oBorderProp))
							bNeedDelete = false;
					}
				}
				if(bNeedDelete)
				{
					oRes = null;
					break;
				}
			}
			return oRes;
		};
		oLeftBorder = fGetBorder(false, oBBox.r1, oBBox.r2, oBBox.c1, 1);
		oTopBorder = fGetBorder(true, oBBox.c1, oBBox.c2, oBBox.r1, 2);
		oRightBorder = fGetBorder(false, oBBox.r1, oBBox.r2, oBBox.c2, 3);
		oBottomBorder = fGetBorder(true, oBBox.c1, oBBox.c2, oBBox.r2, 4);
	}
	else if(c_oRangeType.Row == nRangeType)
	{
		var oTopRow = this.worksheet._getRowNoEmpty(oBBox.r1);
		if(null != oTopRow && null != oTopRow.xfs && null != oTopRow.xfs.border && false == oTopRow.xfs.border.t.isEmpty())
			oTopBorder = oTopRow.xfs.border.t;
		if(oBBox.r1 != oBBox.r2)
		{
			var oBottomRow = this.worksheet._getRowNoEmpty(oBBox.r2);
			if(null != oBottomRow && null != oBottomRow.xfs && null != oBottomRow.xfs.border && false == oBottomRow.xfs.border.b.isEmpty())
				oBottomBorder = oBottomRow.xfs.border.b;
		}
	}
	else
	{
		var oLeftCol = this.worksheet._getColNoEmptyWithAll(oBBox.c1);
		if(null != oLeftCol && null != oLeftCol.xfs && null != oLeftCol.xfs.border && false == oLeftCol.xfs.border.l.isEmpty())
			oLeftBorder = oLeftCol.xfs.border.l;
		if(oBBox.c1 != oBBox.c2)
		{
			var oRightCol = this.worksheet._getColNoEmptyWithAll(oBBox.c2);
			if(null != oRightCol && null != oRightCol.xfs && null != oRightCol.xfs.border && false == oRightCol.xfs.border.r.isEmpty())
				oRightBorder = oRightCol.xfs.border.r;
		}
	}
	
	var bFirst = true;
	var oLeftTopCellStyle = null;
	var oFirstCellStyle = null;
	var oFirstCellValue = null;
	var oFirstCellRow = null;
	var oFirstCellCol = null;
	var oFirstCellHyperlink = null;
	this._setPropertyNoEmpty(null,null,
	function(cell, nRow0, nCol0, nRowStart, nColStart){
		if(bFirst && false == cell.isEmptyText())
		{
			bFirst = false;
			oFirstCellStyle = cell.getStyle();
			oFirstCellValue = cell.getValueData();
			oFirstCellRow = cell.nRow;
			oFirstCellCol = cell.nCol;

		}
		if(nRow0 == nRowStart && nCol0 == nColStart)
			oLeftTopCellStyle = cell.getStyle();
	});
	//правила работы с гиперссылками во время merge(отличются от Excel в случаем областей, например hyperlink: C3:D3 мержим C2:C3)
	// 1)оставляем все ссылки, которые не полностью лежат в merge области
	// 2)оставляем многоклеточные ссылки, top граница которых совпадает с top границей merge области, а высота merge > 1 или совпадает с высотой области merge
	// 3)оставляем и переносим в первую ячейку одну одноклеточную ссылку, если она находится в первой ячейке с данными
	var aHyperlinks = this.worksheet.hyperlinkManager.get(oBBox);
	var aHyperlinksToRestore = [];
	for(var i = 0, length = aHyperlinks.inner.length; i < length; i++)
	{
		var elem = aHyperlinks.inner[i];
		if(oFirstCellRow == elem.bbox.r1 && oFirstCellCol == elem.bbox.c1 && elem.bbox.r1 == elem.bbox.r2 && elem.bbox.c1 == elem.bbox.c2)
		{
			var oNewHyperlink = elem.data.clone();
			oNewHyperlink.Ref.setOffset({offsetCol:oBBox.c1 - oFirstCellCol, offsetRow:oBBox.r1 - oFirstCellRow});
			aHyperlinksToRestore.push(oNewHyperlink);
		}
		else if( oBBox.r1 == elem.bbox.r1 && (elem.bbox.r1 != elem.bbox.r2 || (elem.bbox.c1 != elem.bbox.c2 && oBBox.r1 == oBBox.r2)))
			aHyperlinksToRestore.push(elem.data);
	}
	this.cleanAll();
	//восстанавливаем hyperlink
	for(var i = 0, length = aHyperlinksToRestore.length; i < length; i++)
	{
		var elem = aHyperlinksToRestore[i];
		this.worksheet.hyperlinkManager.add(elem.Ref.getBBox0(), elem);
	}
	var oTargetStyle = null;
	if(null != oFirstCellValue && null != oFirstCellRow && null != oFirstCellCol)
	{
		if(null != oFirstCellStyle)
			oTargetStyle = oFirstCellStyle.clone();
		var oLeftTopCell = this.worksheet._getCell(oBBox.r1, oBBox.c1);
		oLeftTopCell.setValueData(oFirstCellValue);
		if(null != oFirstCellHyperlink)
		{
			var oLeftTopRange = this.worksheet.getCell3(oBBox.r1, oBBox.c1);
			oLeftTopRange.setHyperlink(oFirstCellHyperlink, true);
		}
	}
	else if(null != oLeftTopCellStyle)
		oTargetStyle = oLeftTopCellStyle.clone();

	//убираем бордеры
	if(null != oTargetStyle)
	{
		if(null != oTargetStyle.border)
			oTargetStyle.border = null;
	}
	else if(null != oLeftBorder || null != oTopBorder || null != oRightBorder || null != oBottomBorder)
		oTargetStyle = new AscCommonExcel.CellXfs();
	var fSetProperty = this._setProperty;
	var nRangeType = this._getRangeType();
	if(c_oRangeType.All == nRangeType)
	{
		fSetProperty = this._setPropertyNoEmpty;
		oTargetStyle = null
	}
	fSetProperty.call(this, function(row){
		if(null == oTargetStyle)
			row.setStyle(null);
		else
		{
			var oNewStyle = oTargetStyle.clone();
			if(row.index == oBBox.r1 && null != oTopBorder)
			{
				oNewStyle.border = new Border();
				oNewStyle.border.t = oTopBorder.clone();
			}
			else if(row.index == oBBox.r2 && null != oBottomBorder)
			{
				oNewStyle.border = new Border();
				oNewStyle.border.b = oBottomBorder.clone();
			}
			row.setStyle(oNewStyle);
		}
	},function(col){
		if(null == oTargetStyle)
			col.setStyle(null);
		else
		{
			var oNewStyle = oTargetStyle.clone();
			if(col.index == oBBox.c1 && null != oLeftBorder)
			{
				oNewStyle.border = new Border();
				oNewStyle.border.l = oLeftBorder.clone();
			}
			else if(col.index == oBBox.c2 && null != oRightBorder)
			{
				oNewStyle.border = new Border();
				oNewStyle.border.r = oRightBorder.clone();
			}
			col.setStyle(oNewStyle);
		}
	},
	function(cell, nRow, nCol, nRowStart, nColStart){
		//важно установить именно здесь, чтобы ячейка не удалилась после применения стилей.
		if(null == oTargetStyle)
			cell.setStyle(null);
		else
		{
			var oNewStyle = oTargetStyle.clone();
			if(oBBox.r1 == nRow && oBBox.c1 == nCol)
			{
				if(null != oLeftBorder || null != oTopBorder || (oBBox.r1 == oBBox.r2 && null != oBottomBorder) || (oBBox.c1 == oBBox.c2 && null != oRightBorder))
				{
					oNewStyle.border = new Border();
					if(null != oLeftBorder)
						oNewStyle.border.l = oLeftBorder.clone();
					if(null != oTopBorder)
						oNewStyle.border.t = oTopBorder.clone();
					if(oBBox.r1 == oBBox.r2 && null != oBottomBorder)
						oNewStyle.border.b = oBottomBorder.clone();
					if(oBBox.c1 == oBBox.c2 && null != oRightBorder)
						oNewStyle.border.r = oRightBorder.clone();
				}
			}
			else if(oBBox.r1 == nRow && oBBox.c2 == nCol)
			{
				if(null != oRightBorder || null != oTopBorder || (oBBox.r1 == oBBox.r2 && null != oBottomBorder))
				{
					oNewStyle.border = new Border();
					if(null != oRightBorder)
						oNewStyle.border.r = oRightBorder.clone();
					if(null != oTopBorder)
						oNewStyle.border.t = oTopBorder.clone();
					if(oBBox.r1 == oBBox.r2 && null != oBottomBorder)
						oNewStyle.border.b = oBottomBorder.clone();
				}
			}
			else if(oBBox.r2 == nRow && oBBox.c1 == nCol)
			{
				if(null != oLeftBorder || null != oBottomBorder || (oBBox.c1 == oBBox.c2 && null != oRightBorder))
				{
					oNewStyle.border = new Border();
					if(null != oLeftBorder)
						oNewStyle.border.l = oLeftBorder.clone();
					if(null != oBottomBorder)
						oNewStyle.border.b = oBottomBorder.clone();
					if(oBBox.c1 == oBBox.c2 && null != oRightBorder)
						oNewStyle.border.r = oRightBorder.clone();
				}
			}
			else if(oBBox.r2 == nRow && oBBox.c2 == nCol)
			{
				if(null != oRightBorder || null != oBottomBorder)
				{
					oNewStyle.border = new Border();
					if(null != oRightBorder)
						oNewStyle.border.r = oRightBorder.clone();
					if(null != oBottomBorder)
						oNewStyle.border.b = oBottomBorder.clone();
				}
			}
			else if(oBBox.r1 == nRow)
			{
				if(null != oTopBorder || (oBBox.r1 == oBBox.r2 && null != oBottomBorder))
				{
					oNewStyle.border = new Border();
					if(null != oTopBorder)
						oNewStyle.border.t = oTopBorder.clone();
					if(oBBox.r1 == oBBox.r2 && null != oBottomBorder)
						oNewStyle.border.b = oBottomBorder.clone();
				}
			}
			else if(oBBox.r2 == nRow)
			{
				if(null != oBottomBorder)
				{
					oNewStyle.border = new Border();
					oNewStyle.border.b = oBottomBorder.clone();
				}
			}
			else if(oBBox.c1 == nCol)
			{
				if(null != oLeftBorder || (oBBox.c1 == oBBox.c2 && null != oRightBorder))
				{
					oNewStyle.border = new Border();
					if(null != oLeftBorder)
						oNewStyle.border.l = oLeftBorder.clone();
					if(oBBox.c1 == oBBox.c2 && null != oRightBorder)
						oNewStyle.border.r = oRightBorder.clone();
				}
			}
			else if(oBBox.c2 == nCol)
			{
				if(null != oRightBorder)
				{
					oNewStyle.border = new Border();
					oNewStyle.border.r = oRightBorder.clone();
				}
			}
			cell.setStyle(oNewStyle);
		}
	});
	if(type == Asc.c_oAscMergeOptions.MergeCenter)
		this.setAlignHorizontal("center");
	if(false == this.worksheet.workbook.bUndoChanges && false == this.worksheet.workbook.bRedoChanges)
		this.worksheet.mergeManager.add(this.bbox, 1);
	History.EndTransaction();
};
Range.prototype.unmerge=function(bOnlyInRange){
	History.Create_NewPoint();
	History.StartTransaction();
	if(false == this.worksheet.workbook.bUndoChanges && false == this.worksheet.workbook.bRedoChanges)
		this.worksheet.mergeManager.remove(this.bbox);
	History.EndTransaction();
};
Range.prototype._getHyperlinks=function(){
	var nRangeType = this._getRangeType();
	var result = [];
	var oThis = this;
	if(c_oRangeType.Range == nRangeType)
	{
		var oTempRows = {};
		var fAddToTempRows = function(oTempRows, bbox, data){
			if(null != bbox)
			{
				for(var i = bbox.r1; i <= bbox.r2; i++)
				{
					var row = oTempRows[i];
					if(null == row)
					{
						row = {};
						oTempRows[i] = row;
					}
					for(var j = bbox.c1; j <= bbox.c2; j++)
					{
						var cell = row[j];
						if(null == cell)
							row[j] = data;
					}
				}
			}
		};
		//todo возможно надо сделать оптимизацию для скрытых строк
		var aHyperlinks = this.worksheet.hyperlinkManager.get(this.bbox);
		for(var i = 0, length = aHyperlinks.all.length; i < length; i++)
		{
			var hyp = aHyperlinks.all[i];
			var hypBBox = hyp.bbox.intersectionSimple(this.bbox);
			fAddToTempRows(oTempRows, hypBBox, hyp.data);
			//расширяем гиперссылки на merge ячейках
			var aMerged = this.worksheet.mergeManager.get(hyp.bbox);
			for(var j = 0, length2 = aMerged.all.length; j < length2; j++)
			{
				var merge = aMerged.all[j];
				var mergeBBox = merge.bbox.intersectionSimple(this.bbox);
				fAddToTempRows(oTempRows, mergeBBox, hyp.data);
			}
		}
		//формируем результат
		for(var i in oTempRows)
		{
			var nRowIndex = i - 0;
			var row = oTempRows[i];
			for(var j in row)
			{
				var nColIndex = j - 0;
				var oCurHyp = row[j];
				result.push({hyperlink: oCurHyp, col: nColIndex, row: nRowIndex});
			}
		}
	}
	return result;
};
Range.prototype.getHyperlink=function(){
	var aHyperlinks = this._getHyperlinks();
	if(null != aHyperlinks && aHyperlinks.length > 0)
		return aHyperlinks[0].hyperlink;
	return null;
};
Range.prototype.getHyperlinks=function(){
	return this._getHyperlinks();
};
Range.prototype.setHyperlinkOpen=function(val){
	if(null != val && false == val.isValid())
		return;
	this.worksheet.hyperlinkManager.add(val.Ref.getBBox0(), val);
};
Range.prototype.setHyperlink=function(val, bWithoutStyle){
	if(null != val && false == val.isValid())
		return;
	//проверяем, может эта ссылка уже существует
    var i, length, hyp;
	var bExist = false;
	var aHyperlinks = this.worksheet.hyperlinkManager.get(this.bbox);
	for(i = 0, length = aHyperlinks.all.length; i < length; i++)
	{
		hyp = aHyperlinks.all[i];
		if(hyp.data.isEqual(val))
		{
			bExist = true;
			break;
		}
	}
	if(false == bExist)
	{
		History.Create_NewPoint();
		History.StartTransaction();
		if(false == this.worksheet.workbook.bUndoChanges && false == this.worksheet.workbook.bRedoChanges)
		{
			//удаляем ссылки с тем же адресом
			for(i = 0, length = aHyperlinks.all.length; i < length; i++)
			{
				hyp = aHyperlinks.all[i];
				if(hyp.bbox.isEqual(this.bbox))
					this.worksheet.hyperlinkManager.removeElement(hyp);
			}
		}
		//todo перейти на CellStyle
		if(true != bWithoutStyle)
		{
			var oHyperlinkFont = new AscCommonExcel.Font();
			oHyperlinkFont.fn = this.worksheet.workbook.getDefaultFont();
			oHyperlinkFont.fs = this.worksheet.workbook.getDefaultSize();
			oHyperlinkFont.u = Asc.EUnderline.underlineSingle;
			oHyperlinkFont.c = AscCommonExcel.g_oColorManager.getThemeColor(AscCommonExcel.g_nColorHyperlink);
			this.setFont(oHyperlinkFont);
		}
		if(false == this.worksheet.workbook.bUndoChanges && false == this.worksheet.workbook.bRedoChanges)
			this.worksheet.hyperlinkManager.add(val.Ref.getBBox0(), val);
		History.EndTransaction();
	}
};
Range.prototype.removeHyperlink = function (val, removeStyle) {
	var bbox = this.bbox;
	var elem = null;
	if(null != val)
	{
		bbox = val.Ref.getBBox0();
		elem = new RangeDataManagerElem(bbox, val);
	}
	if(false == this.worksheet.workbook.bUndoChanges && false == this.worksheet.workbook.bRedoChanges)
	{
	    History.Create_NewPoint();
	    History.StartTransaction();
	    var oChangeParam = { removeStyle: removeStyle };
		if(null != elem)
		    this.worksheet.hyperlinkManager.removeElement(elem, oChangeParam);
		else
		    this.worksheet.hyperlinkManager.remove(bbox, !bbox.isOneCell(), oChangeParam);
		History.EndTransaction();
	}
};
Range.prototype.deleteCellsShiftUp=function(preDeleteAction){
	return this._shiftUpDown(true, preDeleteAction);
};
Range.prototype.addCellsShiftBottom=function(displayNameFormatTable){
	return this._shiftUpDown(false, null, displayNameFormatTable);
};
Range.prototype.addCellsShiftRight=function(displayNameFormatTable){
	return this._shiftLeftRight(false, null,displayNameFormatTable);
};
Range.prototype.deleteCellsShiftLeft=function(preDeleteAction){
	return this._shiftLeftRight(true, preDeleteAction);
};
Range.prototype._canShiftLeftRight=function(bLeft){
	var aColsToDelete = [], aCellsToDelete = [];
	var oBBox = this.bbox;
	var nRangeType = this._getRangeType(oBBox);
	if(c_oRangeType.Range != nRangeType && c_oRangeType.Col != nRangeType)
		return null;

	var nWidth = oBBox.c2 - oBBox.c1 + 1;
	if(!bLeft && !this.worksheet.workbook.bUndoChanges && !this.worksheet.workbook.bRedoChanges){
		var rangeEdge = this.worksheet.getRange3(oBBox.r1, gc_nMaxCol0 - nWidth + 1, oBBox.r2, gc_nMaxCol0);
		var aMerged = this.worksheet.mergeManager.get(rangeEdge.bbox);
		if(aMerged.all.length > 0)
			return null;
		var aHyperlink = this.worksheet.hyperlinkManager.get(rangeEdge.bbox);
		if(aHyperlink.all.length > 0)
			return null;

		var bError = rangeEdge._setPropertyNoEmpty(null, function(col){
			if(null != col){
				if(null != col && null != col.xfs && null != col.xfs.fill && null != col.xfs.fill.getRgbOrNull())
					return true;
				aColsToDelete.push(col);
			}
		}, function(cell){
			if(null != cell){
				if(null != cell.xfs && null != cell.xfs.fill && null != cell.xfs.fill.getRgbOrNull())
					return true;
				if(!cell.isEmptyText())
					return true;
				aCellsToDelete.push(cell);
			}
		});
		if(bError)
			return null;
	}
	return {aColsToDelete: aColsToDelete, aCellsToDelete: aCellsToDelete};
};
Range.prototype._shiftLeftRight=function(bLeft, preDeleteAction, displayNameFormatTable){
	var canShiftRes = this._canShiftLeftRight(bLeft);
	if(null === canShiftRes)
		return false;

	if (preDeleteAction)
		preDeleteAction();

	//удаляем крайние колонки и ячейки
	var i, length, colIndex;
	for(i = 0, length = canShiftRes.aColsToDelete.length; i < length; ++i){
		colIndex = canShiftRes.aColsToDelete[i].index;
		this.worksheet._removeCols(colIndex, colIndex);
	}
	for(i = 0, length = canShiftRes.aCellsToDelete.length; i < length; ++i)
		this.worksheet._removeCell(null, null, canShiftRes.aCellsToDelete[i]);

	var oBBox = this.bbox;
	var nWidth = oBBox.c2 - oBBox.c1 + 1;
	var nRangeType = this._getRangeType(oBBox);
	var mergeManager = this.worksheet.mergeManager;
  this.worksheet.workbook.lockDraw();
	//todo вставить предупреждение, что будет unmerge
	History.Create_NewPoint();
	History.StartTransaction();
	var oShiftGet = null;
	if (false == this.worksheet.workbook.bUndoChanges && (false == this.worksheet.workbook.bRedoChanges || true == this.worksheet.workbook.bCollaborativeChanges))
	{
	    History.LocalChange = true;
		oShiftGet = mergeManager.shiftGet(this.bbox, true);
		var aMerged = oShiftGet.elems;
		if(null != aMerged.outer && aMerged.outer.length > 0)
		{
			var bChanged = false;
			for(i = 0, length = aMerged.outer.length; i < length; i++)
			{
				var elem = aMerged.outer[i];
				if(!(elem.bbox.c1 < oShiftGet.bbox.c1 && oShiftGet.bbox.r1 <= elem.bbox.r1 && elem.bbox.r2 <= oShiftGet.bbox.r2))
				{
					mergeManager.removeElement(elem);
					bChanged = true;
				}
			}
			if(bChanged)
				oShiftGet = null;
		}
		History.LocalChange = false;
	}
	//сдвигаем ячейки
	if(bLeft)
	{
		if(c_oRangeType.Range == nRangeType)
			this.worksheet._shiftCellsLeft(oBBox);
		else
			this.worksheet._removeCols(oBBox.c1, oBBox.c2);
	}
	else
	{
		if(c_oRangeType.Range == nRangeType)
			this.worksheet._shiftCellsRight(oBBox, displayNameFormatTable);
		else
			this.worksheet._insertColsBefore(oBBox.c1, nWidth);
	}
	if (false == this.worksheet.workbook.bUndoChanges && (false == this.worksheet.workbook.bRedoChanges || true == this.worksheet.workbook.bCollaborativeChanges))
	{
	    History.LocalChange = true;
		mergeManager.shift(this.bbox, !bLeft, true, oShiftGet);
		this.worksheet.hyperlinkManager.shift(this.bbox, !bLeft, true);
		History.LocalChange = false;
	}
	History.EndTransaction();
  this.worksheet.workbook.unLockDraw();
  this.worksheet.workbook.buildRecalc();
	return true;
};
Range.prototype._canShiftUpDown=function(bUp){
	var aRowsToDelete = [], aCellsToDelete = [];
	var oBBox = this.bbox;
	var nRangeType = this._getRangeType(oBBox);
	if(c_oRangeType.Range != nRangeType && c_oRangeType.Row != nRangeType)
		return null;

	var nHeight = oBBox.r2 - oBBox.r1 + 1;
	if(!bUp && !this.worksheet.workbook.bUndoChanges && !this.worksheet.workbook.bRedoChanges){
		var rangeEdge = this.worksheet.getRange3(gc_nMaxRow0 - nHeight + 1, oBBox.c1, gc_nMaxRow0, oBBox.c2);
		var aMerged = this.worksheet.mergeManager.get(rangeEdge.bbox);
		if(aMerged.all.length > 0)
			return null;
		var aHyperlink = this.worksheet.hyperlinkManager.get(rangeEdge.bbox);
		if(aHyperlink.all.length > 0)
			return null;

		var bError = rangeEdge._setPropertyNoEmpty(function(row){
			if(null != row){
				if(null != row.xfs && null != row.xfs.fill && null != row.xfs.fill.getRgbOrNull())
					return true;
				aRowsToDelete.push(row);
			}
		}, null,  function(cell){
			if(null != cell){
				if(null != cell.xfs && null != cell.xfs.fill && null != cell.xfs.fill.getRgbOrNull())
					return true;
				if(!cell.isEmptyText())
					return true;
				aCellsToDelete.push(cell);
			}
		});
		if(bError)
			return null;
	}
	return {aRowsToDelete: aRowsToDelete, aCellsToDelete: aCellsToDelete};
};
Range.prototype._shiftUpDown = function (bUp, preDeleteAction, displayNameFormatTable) {
	var canShiftRes = this._canShiftUpDown(bUp);
	if(null === canShiftRes)
		return false;

	if (preDeleteAction)
		preDeleteAction();

	//удаляем крайние колонки и ячейки
	var i, length, rowIndex;
	for(i = 0, length = canShiftRes.aRowsToDelete.length; i < length; ++i){
		rowIndex = canShiftRes.aRowsToDelete[i].index;
		this.worksheet._removeRows(rowIndex, rowIndex);
	}
	for(i = 0, length = canShiftRes.aCellsToDelete.length; i < length; ++i)
		this.worksheet._removeCell(null, null, canShiftRes.aCellsToDelete[i]);

	var oBBox = this.bbox;
	var nHeight = oBBox.r2 - oBBox.r1 + 1;
	var nRangeType = this._getRangeType(oBBox);
	var mergeManager = this.worksheet.mergeManager;
  this.worksheet.workbook.lockDraw();
	//todo вставить предупреждение, что будет unmerge
	History.Create_NewPoint();
	History.StartTransaction();
	var oShiftGet = null;
	if (false == this.worksheet.workbook.bUndoChanges && (false == this.worksheet.workbook.bRedoChanges || true == this.worksheet.workbook.bCollaborativeChanges))
	{
	    History.LocalChange = true;
		oShiftGet = mergeManager.shiftGet(this.bbox, false);
		var aMerged = oShiftGet.elems;
		if(null != aMerged.outer && aMerged.outer.length > 0)
		{	
			var bChanged = false;
			for(i = 0, length = aMerged.outer.length; i < length; i++)
			{
				var elem = aMerged.outer[i];
				if(!(elem.bbox.r1 < oShiftGet.bbox.r1 && oShiftGet.bbox.c1 <= elem.bbox.c1 && elem.bbox.c2 <= oShiftGet.bbox.c2))
				{
					mergeManager.removeElement(elem);
					bChanged = true;
				}
			}
			if(bChanged)
				oShiftGet = null;
		}
		History.LocalChange = false;
	}
	//сдвигаем ячейки
	if(bUp)
	{
		if(c_oRangeType.Range == nRangeType)
			this.worksheet._shiftCellsUp(oBBox);
		else
			this.worksheet._removeRows(oBBox.r1, oBBox.r2);
	}
	else
	{
		if(c_oRangeType.Range == nRangeType)
			this.worksheet._shiftCellsBottom(oBBox, displayNameFormatTable);
		else
			this.worksheet._insertRowsBefore(oBBox.r1, nHeight);
	}
	if (false == this.worksheet.workbook.bUndoChanges && (false == this.worksheet.workbook.bRedoChanges || true == this.worksheet.workbook.bCollaborativeChanges))
	{
	    History.LocalChange = true;
		mergeManager.shift(this.bbox, !bUp, false, oShiftGet);
		this.worksheet.hyperlinkManager.shift(this.bbox, !bUp, false);
		History.LocalChange = false;
	}
	History.EndTransaction();
  this.worksheet.workbook.unLockDraw();
  this.worksheet.workbook.buildRecalc();
	return true;
};
Range.prototype.setOffset=function(offset){//offset = AscCommonExcel.CRangeOffset
	this.bbox.c1 += offset.offsetCol;
	if( this.bbox.c1 < 0 )
		this.bbox.c1 = 0;
	this.bbox.r1 += offset.offsetRow;
	if( this.bbox.r1 < 0 )
		this.bbox.r1 = 0;
	this.bbox.c2 += offset.offsetCol;
	if( this.bbox.c2 < 0 )
		this.bbox.c2 = 0;
	this.bbox.r2 += offset.offsetRow;
	if( this.bbox.r2 < 0 )
		this.bbox.r2 = 0;
	this.first = new CellAddress(this.bbox.r1, this.bbox.c1, 0);
	this.last = new CellAddress(this.bbox.r2, this.bbox.c2, 0);
};
Range.prototype.setOffsetFirst=function(offset){//offset = {offsetCol:intNumber, offsetRow:intNumber}
	this.bbox.c1 += offset.offsetCol;
	if( this.bbox.c1 < 0 )
		this.bbox.c1 = 0;
	this.bbox.r1 += offset.offsetRow;
	if( this.bbox.r1 < 0 )
		this.bbox.r1 = 0;
	this.first = new CellAddress(this.bbox.r1, this.bbox.c1, 0);
};
Range.prototype.setOffsetLast=function(offset){//offset = {offsetCol:intNumber, offsetRow:intNumber}
	this.bbox.c2 += offset.offsetCol;
	if( this.bbox.c2 < 0 )
		this.bbox.c2 = 0;
	this.bbox.r2 += offset.offsetRow;
	if( this.bbox.r2 < 0 )
		this.bbox.r2 = 0;
	this.last = new CellAddress(this.bbox.r2, this.bbox.c2, 0);
};
Range.prototype.intersect=function(range){
	var oBBox1 = this.bbox;
	var oBBox2 = range.bbox;
	var r1 = Math.max(oBBox1.r1, oBBox2.r1);
	var c1 = Math.max(oBBox1.c1, oBBox2.c1);
	var r2 = Math.min(oBBox1.r2, oBBox2.r2);
	var c2 = Math.min(oBBox1.c2, oBBox2.c2);
	if(r1 <= r2 && c1 <= c2)
		return this.worksheet.getRange3(r1, c1, r2, c2);
	return null;
};
Range.prototype.cleanCache=function(){
	this._setPropertyNoEmpty(null,null,function(cell, nRow0, nCol0, nRowStart, nColStart){
		cell.cleanCache();
	});
};
Range.prototype.cleanFormat=function(){
	History.Create_NewPoint();
	History.StartTransaction();
	this.unmerge();
	this._setPropertyNoEmpty(function(row){
		row.setStyle(null);
		// if(row.isEmpty())
			// row.Remove();
	},function(col){
		col.setStyle(null);
		// if(col.isEmpty())
			// col.Remove();
	},function(cell, nRow0, nCol0, nRowStart, nColStart){
		cell.setStyle(null);
		// if(cell.isEmpty())
			// cell.Remove();
	});
	History.EndTransaction();
};
Range.prototype.cleanText=function(){
	History.Create_NewPoint();
	History.StartTransaction();
	this._setPropertyNoEmpty(null, null,
		function(cell, nRow0, nCol0, nRowStart, nColStart){
			cell.setValue("");
			// if(cell.isEmpty())
				// cell.Remove();
	});
	History.EndTransaction();
};
Range.prototype.cleanAll=function(){
	History.Create_NewPoint();
	History.StartTransaction();
	this.unmerge();
	//удаляем только гиперссылки, которые полностью лежат в области
	var aHyperlinks = this.worksheet.hyperlinkManager.get(this.bbox);
	for(var i = 0, length = aHyperlinks.inner.length; i < length; ++i)
		this.removeHyperlink(aHyperlinks.inner[i].data);
	var oThis = this;
	this._setPropertyNoEmpty(function(row){
		row.setStyle(null);
		// if(row.isEmpty())
			// row.Remove();
	},function(col){
		col.setStyle(null);
		// if(col.isEmpty())
			// col.Remove();
	},function(cell, nRow0, nCol0, nRowStart, nColStart){
		oThis.worksheet._removeCell(nRow0, nCol0);
	});
  this.worksheet.workbook.buildRecalc();
	History.EndTransaction();
};
Range.prototype.cleanHyperlinks=function(){
	History.Create_NewPoint();
	History.StartTransaction();
	//удаляем только гиперссылки, которые полностью лежат в области
	var aHyperlinks = this.worksheet.hyperlinkManager.get(this.bbox);
	for(var i = 0, length = aHyperlinks.inner.length; i < length; ++i)
		this.removeHyperlink(aHyperlinks.inner[i].data);
	History.EndTransaction();
};
Range.prototype.sort=function(nOption, nStartCol, sortColor, opt_guessHeader){
	var bbox = this.bbox;
	if (opt_guessHeader && bbox.r1 < bbox.r2) {
		//если тип ячеек первого и второго row попарно совпадает, то считаем первую строку заголовком
		//todo рассмотреть замерженые ячейки. стили тоже влияют, но непонятно как сравнивать border
		var rowFirst = this.worksheet.getRange3(bbox.r1, bbox.c1, bbox.r1, bbox.c2);
		var rowSecond = this.worksheet.getRange3(bbox.r1 + 1, bbox.c1, bbox.r1 + 1, bbox.c2);
		var typesFirst = [];
		var typesSecond = [];
		rowFirst._setPropertyNoEmpty(null, null, function(cell, row, col) {
			if (cell && !cell.isEmptyTextString()) {
				typesFirst.push({col: col, type: cell.getType()});
			}
		});
		rowSecond._setPropertyNoEmpty(null, null, function(cell, row, col) {
			if (cell && !cell.isEmptyTextString()) {
				typesSecond.push({col: col, type: cell.getType()});
			}
		});
		var indexFirst = 0;
		var indexSecond = 0;
		while (indexFirst < typesFirst.length && indexSecond < typesSecond.length) {
			var curFirst = typesFirst[indexFirst];
			var curSecond = typesSecond[indexSecond];
			if (curFirst.col < curSecond.col) {
				indexFirst++;
			} else if (curFirst.col > curSecond.col) {
				indexSecond++;
			} else {
				if (curFirst.type != curSecond.type) {
					//has head
					bbox = bbox.clone();
					bbox.r1++;
					break;
				}
				indexFirst++;
				indexSecond++;
			}
		}
	}
	//todo горизонтальная сортировка
	var aMerged = this.worksheet.mergeManager.get(bbox);
	if(aMerged.outer.length > 0 || (aMerged.inner.length > 0 && null == _isSameSizeMerged(bbox, aMerged.inner)))
		return null;
	var nMergedHeight = 1;
	if(aMerged.inner.length > 0)
	{
		var merged = aMerged.inner[0];
		nMergedHeight = merged.bbox.r2 - merged.bbox.r1 + 1;
		//меняем nStartCol, потому что приходит колонка той ячейки, на которой начали выделение
		nStartCol = merged.bbox.c1;
	}
  this.worksheet.workbook.lockDraw();
	var colorFill = nOption === Asc.c_oAscSortOptions.ByColorFill;
	var colorText = nOption === Asc.c_oAscSortOptions.ByColorFont;
	var isSortColor = !!(colorFill || colorText);
	
	var oRes = null;
	var oThis = this;
	var bAscent = false;
	if(nOption == Asc.c_oAscSortOptions.Ascending)
		bAscent = true;
	var nRowFirst0 = bbox.r1;
	var nRowLast0 = bbox.r2;
	var nColFirst0 = bbox.c1;
	var nColLast0 = bbox.c2;
	var bWholeCol = false;
	var bWholeRow = false;
	if(0 == nRowFirst0 && gc_nMaxRow0 == nRowLast0)
		bWholeCol = true;
 	if(0 == nColFirst0 && gc_nMaxCol0 == nColLast0)
		bWholeRow = true;
	var oRangeCol = this.worksheet.getRange(new CellAddress(nRowFirst0, nStartCol, 0), new CellAddress(nRowLast0, nStartCol, 0));
	var nLastRow0 = 0;
	var nLastCol0 = nColLast0;
	if(true == bWholeRow)
	{
		nLastCol0 = 0;
		this._foreachRowNoEmpty(function(){}, function(cell){
			var nCurCol0 = cell.nCol;
			if(nCurCol0 > nLastCol0)
				nLastCol0 = nCurCol0;
		});
	}
	//собираем массив обьектов для сортировки
	var aSortElems = [];
	var aHiddenRow = {};
	var fAddSortElems = function(oCell, nRow0, nCol0,nRowStart0, nColStart0){
		//не сортируем сткрытие строки
		var row = oThis.worksheet._getRowNoEmpty(nRow0);
		if(null != row)
		{
			if(0 != (AscCommonExcel.g_nRowFlag_hd & row.flags))
				aHiddenRow[nRow0] = 1;
			else
			{
				if(nLastRow0 < nRow0)
					nLastRow0 = nRow0;
				var val = oCell.getValueWithoutFormat();
				
				//for sort color
				var colorFillCell, colorsTextCell = null;
				if(colorFill)
				{
					var styleCell = oCell.getStyle();
					colorFillCell = styleCell !== null && styleCell.fill ? styleCell.fill.bg : null;
				}
				else if(colorText)
				{
					var value2 = oCell.getValue2();
					for(var n = 0; n < value2.length; n++)
					{
						if(null === colorsTextCell)
						{
							colorsTextCell = [];
						}
						
						colorsTextCell.push(value2[n].format.c);
					}
				}
				
				var nNumber = null;
				var sText = null;
				if("" != val)
				{
					var nVal = val - 0;
					if(nVal == val)
						nNumber = nVal;
					else
						sText = val;
					aSortElems.push({row: nRow0, num: nNumber, text: sText, colorFill: colorFillCell, colorsText: colorsTextCell});
				}
				else if(isSortColor)
				{
					aSortElems.push({row: nRow0, num: nNumber, text: sText, colorFill: colorFillCell, colorsText: colorsTextCell});
				}
			}
		}
	};
	if(nColFirst0 == nStartCol)
	{
		while(0 == aSortElems.length && nStartCol <= nLastCol0)
		{
			if(false == bWholeCol)
				oRangeCol._foreachNoEmpty(fAddSortElems);
			else
				oRangeCol._foreachColNoEmpty(null, fAddSortElems);
			if(0 == aSortElems.length)
			{
				nStartCol++;
				oRangeCol = this.worksheet.getRange(new CellAddress(nRowFirst0, nStartCol, 0), new CellAddress(nRowLast0, nStartCol, 0));
			}
		}
	}
	else
	{
		if(false == bWholeCol)
			oRangeCol._foreachNoEmpty(fAddSortElems);
		else
			oRangeCol._foreachColNoEmpty(null, fAddSortElems);
	}
	function strcmp ( str1, str2 ) {
			return ( ( str1 == str2 ) ? 0 : ( ( str1 > str2 ) ? 1 : -1 ) );
		}
	
	
	//color sort
	var colorFillCmp = function(color1, color2)
	{
		var res = false;
		//TODO возможно так сравнивать не правильно, позже пересмотреть
		if(colorFill)
		{
			res = (color1 !== null && color2 !== null && color1.rgb === color2.rgb) || (color1 === color2) ? true : false;
		}
		else if(colorText && color1 && color1.length)
		{
			for(var n = 0; n < color1.length; n++)
			{
				if(color1[n] && color2 !== null && color1[n].rgb === color2.rgb)
				{
					res = true;
					break;
				}
			}
		}
		
		return res;
	};
	
	if(isSortColor)
	{
		var newArrayNeedColor = [];
		var newArrayAnotherColor = [];
		
		for(var i = 0; i < aSortElems.length; i++)
		{
			var color = colorFill ? aSortElems[i].colorFill : aSortElems[i].colorsText;
			if(colorFillCmp(color, sortColor))
			{
				newArrayNeedColor.push(aSortElems[i]);
			}
			else
			{
				newArrayAnotherColor.push(aSortElems[i]);
			}
		}
		
		aSortElems = newArrayNeedColor.concat(newArrayAnotherColor);
	}
	else
	{
		aSortElems.sort(function(a, b){
			var res = 0;
			if(null != a.text)
			{
				if(null != b.text)
					res = strcmp(a.text.toUpperCase(), b.text.toUpperCase());
				else
					res = 1;
			}
			else if(null != a.num)
			{
				if(null != b.num)
					res = a.num - b.num;
				else
					res = -1;
			}
			if(0 == res)
				res = a.row - b.row;
			else if(!bAscent)
				res = -res;
			return res;
		});
	}
	
	//проверяем что это не пустая операция
	var aSortData = [];
	var nHiddenCount = 0;
	var oFromArray = {};
	var nRowMax = 0;
	var nRowMin = gc_nMaxRow0;
	var nToMax = 0;
	for(var i = 0, length = aSortElems.length; i < length; ++i)
	{
		var item = aSortElems[i];
		var nNewIndex = i * nMergedHeight + nRowFirst0 + nHiddenCount;
		while(null != aHiddenRow[nNewIndex])
		{
			nHiddenCount++;
			nNewIndex = i * nMergedHeight + nRowFirst0 + nHiddenCount;
		}
		var oNewElem = new UndoRedoData_FromToRowCol(true, item.row, nNewIndex);
		oFromArray[item.row] = 1;
		if(nRowMax < item.row)
			nRowMax = item.row;
		if(nRowMax < nNewIndex)
			nRowMax = nNewIndex;
		if(nRowMin > item.row)
			nRowMin = item.row;
		if(nRowMin > nNewIndex)
			nRowMin = nNewIndex;
		if(nToMax < nNewIndex)
			nToMax = nNewIndex;
		if(oNewElem.from != oNewElem.to)
			aSortData.push(oNewElem);
	}
	if(aSortData.length > 0)
	{
		//добавляем индексы перехода пустых ячеек(нужно для сортировки комментариев)
		for(var i = nRowMin; i <= nRowMax; ++i)
		{
			if(null == oFromArray[i] && null == aHiddenRow[i])
			{
				var nFrom = i;
				var nTo = ++nToMax;
				while(null != aHiddenRow[nTo])
					nTo = ++nToMax;
				if(nFrom != nTo)
				{
					var oNewElem = new UndoRedoData_FromToRowCol(true, nFrom, nTo);
					aSortData.push(oNewElem);
				}
			}
		}
		History.Create_NewPoint();
		var oSelection = History.GetSelection();
		if(null != oSelection)
		{
			oSelection = oSelection.clone();
			oSelection.assign(nColFirst0, nRowFirst0, nLastCol0, nLastRow0);
			History.SetSelection(oSelection);
			History.SetSelectionRedo(oSelection);
		}
		var oUndoRedoBBox = new UndoRedoData_BBox({r1:nRowFirst0, c1:nColFirst0, r2:nLastRow0, c2:nLastCol0});
		oRes = new AscCommonExcel.UndoRedoData_SortData(oUndoRedoBBox, aSortData);
		History.Add(AscCommonExcel.g_oUndoRedoWorksheet, AscCH.historyitem_Worksheet_Sort, this.worksheet.getId(), new Asc.Range(0, nRowFirst0, gc_nMaxCol0, nLastRow0), oRes);
		this._sortByArray(oUndoRedoBBox, aSortData);
	}
  this.worksheet.workbook.unLockDraw();
  this.worksheet.workbook.buildRecalc();
	return oRes;
};
Range.prototype._sortByArray=function(oBBox, aSortData, bUndo){
    var rec = {length:0};
	var oSortedIndexes = {};
	for(var i = 0, length = aSortData.length; i < length; ++i)
	{
		var item = aSortData[i];
		var nFrom = item.from;
		var nTo = item.to;
		if(true == this.worksheet.workbook.bUndoChanges)
		{
			nFrom = item.to;
			nTo = item.from;
		}
		oSortedIndexes[nFrom] = nTo;
	}
	//сортируются только одинарные гиперссылки, все неодинарные оставляем
	var aSortedHyperlinks = [];
	if(false == this.worksheet.workbook.bUndoChanges && (false == this.worksheet.workbook.bRedoChanges || true == this.worksheet.workbook.bCollaborativeChanges))
	{
	    History.LocalChange = true;
		var aHyperlinks = this.worksheet.hyperlinkManager.get(this.bbox);
		for(var i = 0, length = aHyperlinks.inner.length; i < length; i++)
		{
			var elem = aHyperlinks.inner[i];
			var hyp = elem.data;
			if(hyp.Ref.isOneCell())
			{
				var nFrom = elem.bbox.r1;
				var nTo = oSortedIndexes[nFrom];
				if(null != nTo)
				{
					//удаляем ссылки, а не перемеаем, чтобы не было конфликтов(например в случае если все ячейки имеют ссылки и их надо передвинуть)
					var oTempBBox = hyp.Ref.getBBox0();
					this.worksheet.hyperlinkManager.removeElement(new RangeDataManagerElem(oTempBBox, hyp));
					var oNewHyp = hyp.clone();
					oNewHyp.Ref.setOffset({offsetCol: 0, offsetRow: nTo - nFrom});
					aSortedHyperlinks.push(oNewHyp);
				}
			}
		}
		History.LocalChange = false;
	}
	//окончательно устанавливаем ячейки
	var nColFirst0 = oBBox.c1;
	var nLastCol0 = oBBox.c2;
	for(var i = nColFirst0; i <= nLastCol0; ++i)
	{
		//запоминаем ячейки в которые уже что-то передвинули, чтобы не потерять их
		var oTempCellsTo = {};
		for(var j in oSortedIndexes)
		{
			var nIndexFrom = j - 0;
			var nIndexTo = oSortedIndexes[j];
			var shift = nIndexTo - nIndexFrom;
			var rowFrom = this.worksheet._getRow(nIndexFrom);
			var rowTo = this.worksheet._getRow(nIndexTo);
			
			var oCurCell;
			if(oTempCellsTo.hasOwnProperty(nIndexFrom))
				oCurCell = oTempCellsTo[nIndexFrom];
			else{
				oCurCell = rowFrom.c[i];
				delete rowFrom.c[i];
			}
			oTempCellsTo[nIndexTo] = rowTo.c[i];
			if(null != oCurCell)
			{
				var lastName = oCurCell.getName();
				oCurCell.moveVer(shift);
				rowTo.c[i] = oCurCell;
				if(oCurCell.sFormula)
				{
					var sNewName = oCurCell.getName();
					oCurCell.setFormula(oCurCell.formulaParsed.changeOffset({offsetCol:0, offsetRow:shift}).assemble());//получаем новую формулу, путем сдвига входящих в нее ссылок на ячейки на offsetCol и offsetRow. не путать с shiftCells - меняет одну конкретную ячейку в формуле, changeOffset - меняет оффсет для всех входящих в формулу ячеек.
					this.worksheet.workbook.dependencyFormulas.deleteMasterNodes2( this.worksheet.Id, lastName );//разрываем ссылки между старой ячейкой и ведущими ячейками для нее.
					addToArrRecalc(this.worksheet.getId(), oCurCell);
				}
				}
			else
			{
				if(null != rowTo.c[i])
				{
					//здесь достаточно простого delete, потому что на самом деле в функции ячейки только меняются местами, удаления не происходит
					delete rowTo.c[i];
				}
			}
		}
	}
	var aNodes = this.worksheet.workbook.dependencyFormulas.getInRange( this.worksheet.Id, new Asc.Range(oBBox.c1, oBBox.r1, oBBox.c2, oBBox.r2) );
	if(aNodes && aNodes.length > 0)
	{
		for(var i = 0, length = aNodes.length; i < length; ++i)
		{
			var node = aNodes[i];
			this.worksheet.workbook.needRecalc.nodes[ node.nodeId ] = [ node.sheetId, node.cellId ];
			this.worksheet.workbook.needRecalc.length++;
		}
	}

	if (!this.worksheet.workbook.lockCounter)
    this.worksheet.workbook.sortDependency();
	if(false == this.worksheet.workbook.bUndoChanges && (false == this.worksheet.workbook.bRedoChanges || true == this.worksheet.workbook.bCollaborativeChanges))
	{
	    History.LocalChange = true;
		//восстанавливаем удаленые гиперссылки
		if(aSortedHyperlinks.length > 0)
		{
			for(var i = 0, length = aSortedHyperlinks.length; i < length; i++)
			{
				var hyp = aSortedHyperlinks[i];
				this.worksheet.hyperlinkManager.add(hyp.Ref.getBBox0(), hyp);
			}
		}
		History.LocalChange = false;
	}
};
function _isSameSizeMerged(bbox, aMerged) {
	var oRes = null;
	var nWidth = null;
	var nHeight = null;
	for(var i = 0, length = aMerged.length; i < length; i++)
	{
		var mergedBBox = aMerged[i].bbox;
		var nCurWidth = mergedBBox.c2 - mergedBBox.c1 + 1;
		var nCurHeight = mergedBBox.r2 - mergedBBox.r1 + 1;
		if(null == nWidth || null == nHeight)
		{
			nWidth = nCurWidth;
			nHeight = nCurHeight;
		}
		else if(nCurWidth != nWidth || nCurHeight != nHeight)
		{
			nWidth = null;
			nHeight = null;
			break;
		}
	}
	if(null != nWidth && null != nHeight)
	{
		//проверяем что merge ячеки полностью заполняют область
		var nBBoxWidth = bbox.c2 - bbox.c1 + 1;
		var nBBoxHeight = bbox.r2 - bbox.r1 + 1;
		if(nBBoxWidth == nWidth || nBBoxHeight == nHeight)
		{
			var bRes = false;
			var aRowColTest = null;
			if(nBBoxWidth == nWidth && nBBoxHeight == nHeight)
				bRes = true;
			else if(nBBoxWidth == nWidth)
			{
				aRowColTest = new Array(nBBoxHeight);
				for(var i = 0, length = aMerged.length; i < length; i++)
				{
					var merged = aMerged[i];
					for(var j = merged.bbox.r1; j <= merged.bbox.r2; j++)
						aRowColTest[j - bbox.r1] = 1;
				}
			}
			else if(nBBoxHeight == nHeight)
			{
				aRowColTest = new Array(nBBoxWidth);
				for(var i = 0, length = aMerged.length; i < length; i++)
				{
					var merged = aMerged[i];
					for(var j = merged.bbox.c1; j <= merged.bbox.c2; j++)
						aRowColTest[j - bbox.c1] = 1;
				}
			}
			if(null != aRowColTest)
			{
				var bExistNull = false;
				for(var i = 0, length = aRowColTest.length; i < length; i++)
				{
					if(null == aRowColTest[i])
					{
						bExistNull = true;
						break;
					}
				}
				if(!bExistNull)
					bRes = true;
			}
			if(bRes)
				oRes = {width: nWidth, height: nHeight};
		}
	}
	return oRes;
}
function _canPromote(from, wsFrom, to, wsTo, bIsPromote, nWidth, nHeight, bVertical, nIndex) {
	var oRes = {oMergedFrom: null, oMergedTo: null};
	//если надо только удалить внутреннее содержимое не смотрим на замерженость
	if(!bIsPromote || !((true == bVertical && nIndex >= 0 && nIndex < nHeight) || (false == bVertical && nIndex >= 0 && nIndex < nWidth)))
	{
		if(null != to){
			var oMergedTo = wsTo.mergeManager.get(to);
			if(oMergedTo.outer.length > 0)
				oRes = null;
			else
			{
				var oMergedFrom = wsFrom.mergeManager.get(from);
				oRes.oMergedFrom = oMergedFrom;				
				if(oMergedTo.inner.length > 0)
				{
				    oRes.oMergedTo = oMergedTo;
				    if (bIsPromote) {
				        if (oMergedFrom.inner.length > 0) {
				            //merge области должны иметь одинаковый размер
				            var oSizeFrom = _isSameSizeMerged(from, oMergedFrom.inner);
				            var oSizeTo = _isSameSizeMerged(to, oMergedTo.inner);
				            if (!(null != oSizeFrom && null != oSizeTo && oSizeTo.width == oSizeFrom.width && oSizeTo.height == oSizeFrom.height))
				                oRes = null;
				        }
				        else
				            oRes = null;
				    }
				}
			}
		}
	}
	return oRes;
}
// Подготовка Copy Style
function preparePromoteFromTo(from, to) {
	var bSuccess = true;
	if (to.isOneCell())
		to.setOffsetLast({offsetCol: (from.c2 - from.c1) - (to.c2 - to.c1), offsetRow: (from.r2 - from.r1) - (to.r2 - to.r1)});

	if(!from.isIntersect(to)) {
		var bFromWholeCol = (0 == from.c1 && gc_nMaxCol0 == from.c2);
		var bFromWholeRow = (0 == from.r1 && gc_nMaxRow0 == from.r2);
		var bToWholeCol = (0 == to.c1 && gc_nMaxCol0 == to.c2);
		var bToWholeRow = (0 == to.r1 && gc_nMaxRow0 == to.r2);
		bSuccess = (bFromWholeCol === bToWholeCol && bFromWholeRow === bToWholeRow);
	} else
		bSuccess = false;
	return bSuccess;
}
// Перед promoteFromTo обязательно должна быть вызывана функция preparePromoteFromTo
function promoteFromTo(from, wsFrom, to, wsTo) {
	var bVertical = true;
	var nIndex = 1;
	//проверяем можно ли осуществить promote
	var oCanPromote = _canPromote(from, wsFrom, to, wsTo, false, 1, 1, bVertical, nIndex);
	if(null != oCanPromote)
	{
		History.Create_NewPoint();
		var oSelection = History.GetSelection();
		if(null != oSelection)
		{
			oSelection = oSelection.clone();
			oSelection.assign(from.c1, from.r1, from.c2, from.r2);
			History.SetSelection(oSelection);
		}
		var oSelectionRedo = History.GetSelectionRedo();
		if(null != oSelectionRedo)
		{
			oSelectionRedo = oSelectionRedo.clone();
			oSelectionRedo.assign(to.c1, to.r1, to.c2, to.r2);
			History.SetSelectionRedo(oSelectionRedo);
		}
		//удаляем merge ячейки в to(после _canPromote должны остаться только inner)
		wsTo.mergeManager.remove(to, true);
		_promoteFromTo(from, wsFrom, to, wsTo, false, oCanPromote, false, bVertical, nIndex);
	}
}
Range.prototype.promote=function(bCtrl, bVertical, nIndex){
	//todo отдельный метод для promote в таблицах и merge в таблицах
	var oBBox = this.bbox;
	var nWidth = oBBox.c2 - oBBox.c1 + 1;
    var nHeight = oBBox.r2 - oBBox.r1 + 1;
	var bWholeCol = false;	var bWholeRow = false;
	if(0 == oBBox.r1 && gc_nMaxRow0 == oBBox.r2)
		bWholeCol = true;
 	if(0 == oBBox.c1 && gc_nMaxCol0 == oBBox.c2)
		bWholeRow = true;
	if((bWholeCol && bWholeRow) || (true == bVertical && bWholeCol) || (false == bVertical && bWholeRow))
		return false;
	var oPromoteAscRange = null;
	if(0 == nIndex)
		oPromoteAscRange = Asc.Range(oBBox.c1, oBBox.r1, oBBox.c2, oBBox.r2);
	else
	{
		if(bVertical)
		{
			if(nIndex > 0)
			{
				if(nIndex >= nHeight)
					oPromoteAscRange = Asc.Range(oBBox.c1, oBBox.r2 + 1, oBBox.c2, oBBox.r1 + nIndex);
				else
					oPromoteAscRange = Asc.Range(oBBox.c1, oBBox.r1 + nIndex, oBBox.c2, oBBox.r2);
			}
			else
				oPromoteAscRange = Asc.Range(oBBox.c1, oBBox.r1 + nIndex, oBBox.c2, oBBox.r1 - 1);
		}
		else
		{
			if(nIndex > 0)
			{
				if(nIndex >= nWidth)
					oPromoteAscRange = Asc.Range(oBBox.c2 + 1, oBBox.r1, oBBox.c1 + nIndex, oBBox.r2);
				else
					oPromoteAscRange = Asc.Range(oBBox.c1 + nIndex, oBBox.r1, oBBox.c2, oBBox.r2);
			}
			else
				oPromoteAscRange = Asc.Range(oBBox.c1 + nIndex, oBBox.r1, oBBox.c1 - 1, oBBox.r2);
		}
	}
	//проверяем можно ли осуществить promote
	var oCanPromote = _canPromote(oBBox, this.worksheet, oPromoteAscRange, this.worksheet, true, nWidth, nHeight, bVertical, nIndex);
	if(null == oCanPromote)
		return false;

	History.Create_NewPoint();
	var oSelection = History.GetSelection();
	if(null != oSelection)
	{
		oSelection = oSelection.clone();
		oSelection.assign(oBBox.c1, oBBox.r1, oBBox.c2, oBBox.r2);
		History.SetSelection(oSelection);
	}
	var oSelectionRedo = History.GetSelectionRedo();
	if(null != oSelectionRedo)
	{
		oSelectionRedo = oSelectionRedo.clone();
		if(0 == nIndex)
			oSelectionRedo.assign(oBBox.c1, oBBox.r1, oBBox.c2, oBBox.r2);
		else
		{
			if(bVertical)
			{
				if(nIndex > 0)
				{
					if(nIndex >= nHeight)
						oSelectionRedo.assign(oBBox.c1, oBBox.r1, oBBox.c2, oBBox.r1 + nIndex);
					else
						oSelectionRedo.assign(oBBox.c1, oBBox.r1, oBBox.c2, oBBox.r1 + nIndex - 1);
				}
				else
					oSelectionRedo.assign(oBBox.c1, oBBox.r1 + nIndex, oBBox.c2, oBBox.r2);
			}
			else
			{
				if(nIndex > 0)
				{
					if(nIndex >= nWidth)
						oSelectionRedo.assign(oBBox.c1, oBBox.r1, oBBox.c1 + nIndex, oBBox.r2);
					else
						oSelectionRedo.assign(oBBox.c1, oBBox.r1, oBBox.c1 + nIndex - 1, oBBox.r2);
				}
				else
					oSelectionRedo.assign(oBBox.c1 + nIndex, oBBox.r1, oBBox.c2, oBBox.r2);
			}
		}
		History.SetSelectionRedo(oSelectionRedo);
	}
  oFormulaLocaleInfo.Parse = false;
  oFormulaLocaleInfo.DigitSep = false;
	_promoteFromTo(oBBox, this.worksheet, oPromoteAscRange, this.worksheet, true, oCanPromote, bCtrl, bVertical, nIndex);
  oFormulaLocaleInfo.Parse = true;
  oFormulaLocaleInfo.DigitSep = true;
	return true;
};
function _promoteFromTo(from, wsFrom, to, wsTo, bIsPromote, oCanPromote, bCtrl, bVertical, nIndex) {
	var wb = wsFrom.workbook;
	wb.lockDraw();
    History.StartTransaction();
	
	var toRange = wsTo.getRange3(to.r1, to.c1, to.r2, to.c2);
	var fromRange = wsFrom.getRange3(from.r1, from.c1, from.r2, from.c2);
	var bChangeRowColProp = false;
	var nLastCol = from.c2;
	if (0 == from.c1 && gc_nMaxCol0 == from.c2)
	{
		var aRowProperties = [];
		nLastCol = 0;
		fromRange._foreachRowNoEmpty(function(row){
			if(!row.isEmptyProp())
				aRowProperties.push({index: row.index - from.r1, prop: row.getHeightProp(), style: row.getStyle()});
		}, function(cell){
			var nCurCol0 = cell.nCol;
			if(nCurCol0 > nLastCol)
				nLastCol = nCurCol0;
		});
		if(aRowProperties.length > 0)
		{
		    bChangeRowColProp = true;
		    var nCurCount = 0;
		    var nCurIndex = 0;
		    while (true) {
		        for (var i = 0, length = aRowProperties.length; i < length; ++i) {
		            var propElem = aRowProperties[i];
		            nCurIndex = to.r1 + nCurCount * (from.r2 - from.r1 + 1) + propElem.index;
		            if (nCurIndex > to.r2)
		                break;
                    else{
		                var row = wsTo._getRow(nCurIndex);
		                if (null != propElem.style)
		                    row.setStyle(propElem.style);
		                if (null != propElem.prop) {
		                    var oNewProps = propElem.prop;
		                    var oOldProps = row.getHeightProp();
		                    if (false === oOldProps.isEqual(oNewProps)) {
		                        row.setHeightProp(oNewProps);
		                        History.Add(AscCommonExcel.g_oUndoRedoWorksheet, AscCH.historyitem_Worksheet_RowProp, wsTo.getId(), row._getUpdateRange(), new UndoRedoData_IndexSimpleProp(nCurIndex, true, oOldProps, oNewProps));
		                    }
		                }
		            }
		        }
		        nCurCount++;
		        if (nCurIndex > to.r2)
		            break;
		    }
		}
	}
	var nLastRow = from.r2;
	if (0 == from.r1 && gc_nMaxRow0 == from.r2)
	{
		var aColProperties = [];
		nLastRow = 0;
		fromRange._foreachColNoEmpty(function(col){
			if(!col.isEmpty())
			    aColProperties.push({ index: col.index - from.c1, prop: col.getWidthProp(), style: col.getStyle() });
		}, function(cell){
			var nCurRow0 = cell.nRow;
			if(nCurRow0 > nLastRow)
				nLastRow = nCurRow0;
		});
		if (aColProperties.length > 0)
		{
		    bChangeRowColProp = true;
		    var nCurCount = 0;
		    var nCurIndex = 0;
		    while (true) {
		        for (var i = 0, length = aColProperties.length; i < length; ++i) {
		            var propElem = aColProperties[i];
		            nCurIndex = to.c1 + nCurCount * (from.c2 - from.c1 + 1) + propElem.index;
		            if (nCurIndex > to.c2)
		                break;
                    else{
		                var col = wsTo._getCol(nCurIndex);
		                if (null != propElem.style)
		                    col.setStyle(propElem.style);
		                if (null != propElem.prop) {
		                    var oNewProps = propElem.prop;
		                    var oOldProps = col.getWidthProp();
		                    if (false == oOldProps.isEqual(oNewProps)) {
		                        col.setWidthProp(oNewProps);
		                        History.Add(AscCommonExcel.g_oUndoRedoWorksheet, AscCH.historyitem_Worksheet_ColProp, wsTo.getId(), new Asc.Range(nCurIndex, 0, nCurIndex, gc_nMaxRow0), new UndoRedoData_IndexSimpleProp(nCurIndex, false, oOldProps, oNewProps));
		                    }
		                }
		            }
		        }
		        nCurCount++;
		        if (nCurIndex > to.c2)
		            break;
		    }
		}
	}
	if (bChangeRowColProp)
	    wb.handlers.trigger("changeWorksheetUpdate", wsTo.getId());
	if(nLastCol != from.c2 || nLastRow != from.r2)
	{
		var offset = {offsetCol:nLastCol - from.c2, offsetRow:nLastRow - from.r2};
		toRange.setOffsetLast(offset);
		to = toRange.getBBox0();
		fromRange.setOffsetLast(offset);
		from = fromRange.getBBox0();
	}
	var nWidth = from.c2 - from.c1 + 1;
	var nHeight = from.r2 - from.r1 + 1;
	//удаляем текст или все в области для заполнения
	if(bIsPromote && nIndex >= 0 && ((true == bVertical && nHeight > nIndex) || (false == bVertical && nWidth > nIndex)))
	{
		//удаляем только текст в области для заполнения
		toRange.cleanText();
	}
	else
	{
		//удаляем все в области для заполнения
		if(bIsPromote)
			toRange.cleanAll();
		else
			toRange.cleanFormat();
		//собираем все данные
		var bReverse = false;
		if(nIndex < 0)
			bReverse = true;
		var oPromoteHelper = new PromoteHelper(bVertical, bReverse, from);
		fromRange._foreachNoEmpty(function(oCell, nRow0, nCol0, nRowStart0, nColStart0){
			 if(null != oCell)
			 {
				var nVal = null;
				var bDelimiter = false;
				var sPrefix = null;
				var bDate = false;
				if(bIsPromote)
				{
					if (!oCell.sFormula)
					{
						var sValue = oCell.getValueWithoutFormat();
						if("" != sValue)
						{
							bDelimiter = true;
							var nType = oCell.getType();
							if(CellValueType.Number == nType || CellValueType.String == nType)
							{
								if(CellValueType.Number == nType)
									nVal = sValue - 0;
								else
								{
									//если текст заканчивается на цифру тоже используем ее
									var nEndIndex = sValue.length;
									for(var k = sValue.length - 1; k >= 0; --k)
									{
										var sCurChart = sValue[k];
										if('0' <= sCurChart && sCurChart <= '9')
											nEndIndex--;
										else
											break;
									}
									if(sValue.length != nEndIndex)
									{
										sPrefix = sValue.substring(0, nEndIndex);
										nVal = sValue.substring(nEndIndex) - 0;
									}
								}
							}
							if(null != oCell.xfs && null != oCell.xfs.num && null != oCell.xfs.num.getFormat()){
								var numFormat = oNumFormatCache.get(oCell.xfs.num.getFormat());
								if(numFormat.isDateTimeFormat())
									bDate = true;
							}
							if(null != nVal)
								bDelimiter = false;
						}
					}
					else
						bDelimiter = true;
				}
				oPromoteHelper.add(nRow0 - nRowStart0, nCol0 - nColStart0, nVal, bDelimiter, sPrefix, bDate, oCell);
             }
		});
		var bCopy = false;
		if(bCtrl)
			bCopy = true;
		//в случае одной ячейки с числом меняется смысл bCtrl
		if(1 == nWidth && 1 == nHeight && oPromoteHelper.isOnlyIntegerSequence())
			bCopy = !bCopy;
		oPromoteHelper.finishAdd(bCopy);
		//заполняем ячейки данными
		var nStartRow, nEndRow, nStartCol, nEndCol, nColDx, bRowFirst;
		if(bVertical)
		{
			nStartRow = to.c1;
			nEndRow = to.c2;
			bRowFirst = false;
			if(bReverse)
			{
				nStartCol = to.r2;
				nEndCol = to.r1;
				nColDx = -1;
			}
			else
			{
				nStartCol = to.r1;
				nEndCol = to.r2;
				nColDx = 1;
			}
		}
		else
		{
			nStartRow = to.r1;
			nEndRow = to.r2;
			bRowFirst = true;
			if(bReverse)
			{
				nStartCol = to.c2;
				nEndCol = to.c1;
				nColDx = -1;
			}
			else
			{
				nStartCol = to.c1;
				nEndCol = to.c2;
				nColDx = 1;
			}
		}
		for(var i = nStartRow; i <= nEndRow; i ++)
        {
			oPromoteHelper.setIndex(i - nStartRow);
            for(var j = nStartCol; (nStartCol - j) * (nEndCol - j) <= 0; j += nColDx)
            {
                var data = oPromoteHelper.getNext();
				if(null != data && (data.oAdditional || (false == bCopy && null != data.nCurValue)))
				{
					var oFromCell = data.oAdditional;
					var oCopyCell = null;
					if(bRowFirst)
						oCopyCell = wsTo._getCell(i, j);
					else
						oCopyCell = wsTo._getCell(j, i);
					if(bIsPromote)
					{
						if(false == bCopy && null != data.nCurValue)
						{
							var sVal = "";
							if(null != data.sPrefix)
								sVal += data.sPrefix;
							//change javascript NumberDecimalSeparator '.' , to cultural NumberDecimalSeparator
							sVal += data.nCurValue.toString().replace(/\./g, AscCommon.g_oDefaultCultureInfo.NumberDecimalSeparator);
							oCopyCell.setValue(sVal);
						}
						else if(null != oFromCell)
						{
							//копируем полностью
							if(!oFromCell.formulaParsed){
								var DataOld = oCopyCell.getValueData();
								oCopyCell.oValue = oFromCell.oValue.clone();
								var DataNew = oCopyCell.getValueData();
								if(false == DataOld.isEqual(DataNew))
									History.Add(AscCommonExcel.g_oUndoRedoCell, AscCH.historyitem_Cell_ChangeValue, wsTo.getId(), new Asc.Range(oCopyCell.nCol, oCopyCell.nRow, oCopyCell.nCol, oCopyCell.nRow), new UndoRedoData_CellSimpleData(oCopyCell.nRow, oCopyCell.nCol, DataOld, DataNew));
								//todo
								// if(oCopyCell.isEmptyTextString())
									// wsTo._getHyperlink().remove({r1: oCopyCell.nRow, c1: oCopyCell.nCol, r2: oCopyCell.nRow, c2: oCopyCell.nCol});
							}
							else{
								var assemb;
								var _p_ = new parserFormula(oFromCell.sFormula,oCopyCell.getName(),wsTo);
								if( _p_.parse() ){
										assemb = _p_.changeOffset(oCopyCell.getOffset2(oFromCell.getName())).assemble();
										oCopyCell.setValue("="+assemb);
								}
							}
						}
					}
                    //выставляем стиль после текста, потому что если выставить числовой стиль ячейки 'text', то после этого не применится формула
					if (null != oFromCell) {
					    oCopyCell.setStyle(oFromCell.getStyle());
					    if (bIsPromote)
					        oCopyCell.setType(oFromCell.getType());
					}
				}
			}
		}
		if(bIsPromote)
		{
			var aNodes = wb.dependencyFormulas.getInRange( wsTo.Id, to );
			if(aNodes && aNodes.length > 0)
			{
				for(var i = 0, length = aNodes.length; i < length; ++i)
				{
					var node = aNodes[i];
					wb.needRecalc.nodes[ node.nodeId ] = [ node.sheetId, node.cellId ];
					wb.needRecalc.length++;
				}
			}
		}
		//добавляем замерженые области
		var nDx = from.c2 - from.c1 + 1;
		var nDy = from.r2 - from.r1 + 1;
		var oMergedFrom = oCanPromote.oMergedFrom;
		if(null != oMergedFrom && oMergedFrom.all.length > 0)
		{
		    for (var i = to.c1; i <= to.c2; i += nDx) {
		        for (var j = to.r1; j <= to.r2; j += nDy) {
		            for (var k = 0, length3 = oMergedFrom.all.length; k < length3; k++) {
		                var oMergedBBox = oMergedFrom.all[k].bbox;
		                var oNewMerged = Asc.Range(i + oMergedBBox.c1 - from.c1, j + oMergedBBox.r1 - from.r1, i + oMergedBBox.c2 - from.c1, j + oMergedBBox.r2 - from.r1);
						if(to.contains(oNewMerged.c1, oNewMerged.r1)) {
							if(to.c2 < oNewMerged.c2)
								oNewMerged.c2 = to.c2;
							if(to.r2 < oNewMerged.r2)
								oNewMerged.r2 = to.r2;
							if(!oNewMerged.isOneCell())
								wsTo.mergeManager.add(oNewMerged, 1);	
						}
		            }
		        }
		    }
		}
		if(bIsPromote)
		{
			//добавляем ссылки
			//не как в Excel поддерживаются ссылки на диапазоны
			var oHyperlinks = wsFrom.hyperlinkManager.get(from);
			if(oHyperlinks.inner.length > 0)
			{
			    for (var i = to.c1; i <= to.c2; i += nDx) {
			        for (var j = to.r1; j <= to.r2; j += nDy) {
			            for(var k = 0, length3 = oHyperlinks.inner.length; k < length3; k++){
			                var oHyperlink = oHyperlinks.inner[k];
			                var oHyperlinkBBox = oHyperlink.bbox;
			                var oNewHyperlink = Asc.Range(i + oHyperlinkBBox.c1 - from.c1, j + oHyperlinkBBox.r1 - from.r1, i + oHyperlinkBBox.c2 - from.c1, j + oHyperlinkBBox.r2 - from.r1);
			                if (to.containsRange(oNewHyperlink))
			                    wsTo.hyperlinkManager.add(oNewHyperlink, oHyperlink.data.clone());
			            }
			        }
			    }
			}
		}
	}
	History.EndTransaction();
  wb.unLockDraw();
  wb.buildRecalc();
}
Range.prototype.createCellOnRowColCross=function(){
	var oThis = this;
	var bbox = this.bbox;
	var nRangeType = this._getRangeType(bbox);
	if(c_oRangeType.Row == nRangeType)
	{
		this._foreachColNoEmpty(function(col){
			if(null != col.xfs)
			{
				for(var i = bbox.r1; i <= bbox.r2; ++i)
					oThis.worksheet._getCell(i, col.index);
			}
		}, null);
	}
	else if(c_oRangeType.Col == nRangeType)
	{
		this._foreachRowNoEmpty(function(row){
			if(null != row.xfs)
			{
				for(var i = bbox.c1; i <= bbox.c2; ++i)
					oThis.worksheet._getCell(row.index, i);
			}
		}, null);
	}
};
//-------------------------------------------------------------------------------------------------
/**
 * @constructor
 */
function PromoteHelper(bVerical, bReverse, bbox){
	//автозаполнение происходит всегда в правую сторону, поэтому менются индексы в методе add, и это надо учитывать при вызове getNext
	this.bVerical = bVerical;
	this.bReverse = bReverse;
	this.bbox = bbox;
	this.oDataRow = {};
	//для get
	this.oCurRow = null;
	this.nCurColIndex = null;
	this.nRowLength = 0;
	this.nColLength = 0;
	if(this.bVerical)
	{
		this.nRowLength = this.bbox.c2 - this.bbox.c1 + 1;
		this.nColLength = this.bbox.r2 - this.bbox.r1 + 1;
	}
	else
	{
		this.nRowLength = this.bbox.r2 - this.bbox.r1 + 1;
		this.nColLength = this.bbox.c2 - this.bbox.c1 + 1;
	}
}
PromoteHelper.prototype = {
	add: function(nRow, nCol, nVal, bDelimiter, sPrefix, bDate, oAdditional){
		if(this.bVerical)
		{
			//транспонируем для удобства
			var temp = nRow;
			nRow = nCol;
			nCol = temp;
		}
		if(this.bReverse)
			nCol = this.nColLength - nCol - 1;
		var row = this.oDataRow[nRow];
		if(null == row)
		{
			row = {};
			this.oDataRow[nRow] = row;
		}
		row[nCol] = {nCol: nCol, nVal: nVal, bDelimiter: bDelimiter, sPrefix: sPrefix, bDate: bDate, oAdditional: oAdditional, oSequence: null, nCurValue: null};
	},
	isOnlyIntegerSequence: function(){
		var bRes = true;
		var bEmpty = true;
		for(var i in this.oDataRow)
		{
			var row = this.oDataRow[i];
			for(var j in row)
			{
				var data = row[j];
				bEmpty = false;
				if(!(null != data.nVal && true != data.bDate && null == data.sPrefix))
				{
					bRes = false;
					break;
				}
			}
			if(!bRes)
				break;
		}
		if(bEmpty)
			bRes = false;
		return bRes;
	},
	_promoteSequence: function(aDigits){
		// Это коэффициенты линейного приближения (http://office.microsoft.com/ru-ru/excel-help/HP010072685.aspx)
		// y=a1*x+a0 (где: x=0,1....; y=значения в ячейках; a0 и a1 - это решения приближения функции методом наименьших квадратов
		// (n+1)*a0        + (x0+x1+....)      *a1=(y0+y1+...)
		// (x0+x1+....)*a0 + (x0*x0+x1*x1+....)*a1=(y0*x0+y1*x1+...)
		// http://www.exponenta.ru/educat/class/courses/vvm/theme_7/theory.asp
		var a0 = 0.0;
		var a1 = 0.0;
		// Индекс X
		var nX = 0;
		if(1 == aDigits.length)
		{
			nX = 1;
			a1 = 1;
			a0 = aDigits[0].y;
		}
		else
		{
			// (n+1)
			var nN = aDigits.length;
			// (x0+x1+....)
			var nXi = 0;
			// (x0*x0+x1*x1+....)
			var nXiXi = 0;
			// (y0+y1+...)
			var dYi = 0.0;
			// (y0*x0+y1*x1+...)
			var dYiXi = 0.0;

			// Цикл по всем строкам
			for (var i = 0, length = aDigits.length; i < length; ++i)
			{
				var data = aDigits[i];
				nX = data.x;
				var dValue = data.y;

				// Вычисляем значения
				nXi += nX;
				nXiXi += nX * nX;
				dYi += dValue;
				dYiXi += dValue * nX;
			}
			nX++;

			// Теперь решаем систему уравнений
			// Общий детерминант
			var dD = nN * nXiXi - nXi * nXi;
			// Детерминант первого корня
			var dD1 = dYi * nXiXi - nXi * dYiXi;
			// Детерминант второго корня
			var dD2 = nN * dYiXi - dYi * nXi;

			a0 = dD1 / dD;
			a1 = dD2 / dD;
		}
		return {a0: a0, a1: a1, nX: nX};
	},
	_addSequenceToRow : function(nRowIndex, aSortRowIndex, row, aCurSequence){
		if(aCurSequence.length > 0)
		{
			var oFirstData = aCurSequence[0];
			var bCanPromote = true;
			//если последовательность состоит из одного числа и той же колонке есть еще последовательности, то надо копировать, а не автозаполнять
			if(1 == aCurSequence.length)
			{
				var bVisitRowIndex = false;
				var oVisitData = null;
				for(var i = 0, length = aSortRowIndex.length; i < length; i++)
				{
					var nCurRowIndex = aSortRowIndex[i];
					if(nRowIndex == nCurRowIndex)
					{
						bVisitRowIndex = true;
						if(oVisitData && oFirstData.sPrefix == oVisitData.sPrefix && oFirstData.bDate == oVisitData.bDate)
						{
							bCanPromote = false;
							break;
						}
					}
					else
					{
						var oCurRow = this.oDataRow[nCurRowIndex];
						if(oCurRow)
						{
							var data = oCurRow[oFirstData.nCol];
							if(null != data)
							{
								if(null != data.nVal)
								{
									oVisitData = data;
									if(bVisitRowIndex)
									{
										if(oFirstData.sPrefix == oVisitData.sPrefix && oFirstData.bDate == oVisitData.bDate)
											bCanPromote = false;
										break;
									}
								}
								else if(data.bDelimiter)
								{
									oVisitData = null;
									if(bVisitRowIndex)
										break;
								}
							}
						}
					}
				}
			}
			if(bCanPromote)
			{
				var nMinIndex = null;
				var nMaxIndex = null;
				var bValidIndexDif = true;
				var nPrevX = null;
				var nPrevVal = null;
				var nIndexDif = null;
				var nValueDif = null;
				//анализируем последовательность, если числа расположены не на одинаковом расстоянии, то считаем их сплошной последовательностью
				//последовательность с промежутками может быть только целочисленной
				for(var i = 0, length = aCurSequence.length; i < length; i++)
				{
					var data = aCurSequence[i];
					var nCurX = data.nCol;
					if(null == nMinIndex || null == nMaxIndex)
						nMinIndex = nMaxIndex = nCurX;
					else
					{
						if(nCurX < nMinIndex)
							nMinIndex = nCurX;
						if(nCurX > nMaxIndex)
							nMaxIndex = nCurX;
					}
					if(bValidIndexDif)
					{
						if(null != nPrevX && null != nPrevVal)
						{
							var nCurDif = nCurX - nPrevX;
							var nCurValDif = data.nVal - nPrevVal;
							if(null == nIndexDif || null == nCurValDif)
							{
								nIndexDif = nCurDif;
								nValueDif = nCurValDif;
							}
							else if(nIndexDif != nCurDif || nValueDif != nCurValDif)
							{
								nIndexDif = null;
								bValidIndexDif = false;
							}
						}
					}
					nPrevX = nCurX;
					nPrevVal = data.nVal;
				}
				var bWithSpace = false;
				if(null != nIndexDif)
				{
					nIndexDif = Math.abs(nIndexDif);
					if(nIndexDif > 1)
						bWithSpace = true;
				}
				//заполняем массив с координатами
				var bExistSpace = false;
				nPrevX = null;
				var aDigits = [];
				for(var i = 0, length = aCurSequence.length; i < length; i++)
				{
					var data = aCurSequence[i];
					var nCurX = data.nCol;
					var x = nCurX - nMinIndex;
					if(null != nIndexDif && nIndexDif > 0)
						x /= nIndexDif;
					if(null != nPrevX && nCurX - nPrevX > 1)
						bExistSpace = true;
					var y = data.nVal;
					//даты автозаполняем только по целой части
					if(data.bDate)
						y = parseInt(y);
					aDigits.push({x: x, y: y});
					nPrevX = nCurX;
				}
				if(aDigits.length > 0)
				{
					var oSequence = this._promoteSequence(aDigits);
					if(1 == aDigits.length && this.bReverse)
					{
						//меняем коэффициенты для случая одного числа в последовательности, иначе она в любую сторону будет возрастающей
						oSequence.a1 *= -1;
					}
					var bIsIntegerSequence = oSequence.a1 != parseInt(oSequence.a1);
					//для дат и чисел с префиксом автозаполняются только целочисленные последовательности
					if(!((null != oFirstData.sPrefix || true == oFirstData.bDate) && bIsIntegerSequence))
					{
						if(false == bWithSpace && bExistSpace)
						{
							for(var i = nMinIndex; i <= nMaxIndex; i++)
							{
								var data = row[i];
								if(null == data)
								{
									data = {nCol: i, nVal: null, bDelimiter: oFirstData.bDelimiter, sPrefix: oFirstData.sPrefix, bDate: oFirstData.bDate, oAdditional: null, oSequence: null, nCurValue: null};
									row[i] = data;
								}
								data.oSequence = oSequence;
							}
						}
						else
						{
							for(var i = 0, length = aCurSequence.length; i < length; i++)
							{
								var nCurX = aCurSequence[i].nCol;
								if(null != nCurX)
									row[nCurX].oSequence = oSequence;
							}
						}
					}
				}
			}
		}
	},
	finishAdd : function(bCopy){
		if(true != bCopy)
		{
			var aSortRowIndex = [];
			for(var i in this.oDataRow)
				aSortRowIndex.push(i - 0);
			aSortRowIndex.sort(fSortAscending);
			for(var i = 0, length = aSortRowIndex.length; i < length; i++)
			{
				var nRowIndex = aSortRowIndex[i];
				var row = this.oDataRow[nRowIndex];
				//собираем информация о последовательностях в row
				var aSortIndex = [];
				for(var j in row)
					aSortIndex.push(j - 0);
				aSortIndex.sort(fSortAscending);
				var aCurSequence = [];
				var oPrevData = null;
				for(var j = 0, length2 = aSortIndex.length; j < length2; j++)
				{
					var nColIndex = aSortIndex[j];
					var data = row[nColIndex];
					var bAddToSequence = false;
					if(null != data.nVal)
					{
						bAddToSequence = true;
						if(null != oPrevData && (oPrevData.bDelimiter != data.bDelimiter || oPrevData.sPrefix != data.sPrefix || oPrevData.bDate != data.bDate))
						{
							this._addSequenceToRow(nRowIndex, aSortRowIndex, row, aCurSequence);
							aCurSequence = [];
							oPrevData = null;
						}
						oPrevData = data;
					}
					else if(data.bDelimiter)
					{
						this._addSequenceToRow(nRowIndex, aSortRowIndex, row, aCurSequence);
						aCurSequence = [];
						oPrevData = null;
					}
					if(bAddToSequence)
						aCurSequence.push(data);
				}
				this._addSequenceToRow(nRowIndex, aSortRowIndex, row, aCurSequence);
			}
		}
	},
	setIndex: function(index){
		if(0 != this.nRowLength && index >= this.nRowLength)
			index = index % (this.nRowLength);
		this.oCurRow = this.oDataRow[index];
		this.nCurColIndex = 0;
	},
	getNext: function(){
		var oRes = null;
		if(this.oCurRow)
		{
			var oRes = this.oCurRow[this.nCurColIndex];
			if(null != oRes)
			{
				oRes.nCurValue = null;
				if(null != oRes.oSequence)
				{
					var sequence = oRes.oSequence;
					if(oRes.bDate || null != oRes.sPrefix)
						oRes.nCurValue = Math.abs(sequence.a1 * sequence.nX + sequence.a0);
					else
						oRes.nCurValue = sequence.a1 * sequence.nX + sequence.a0;
					sequence.nX ++;
				}
			}
			this.nCurColIndex++;
			if(this.nCurColIndex >= this.nColLength)
				this.nCurColIndex = 0;
		}
		return oRes;
	}
};
function DefinedName(){
	this.Name = null;
	this.Ref = null;
	this.LocalSheetId = null;
    this.Hidden = null;
	this.bTable = false;
}

//-------------------------------------------------------------------------------------------------

/**
 * @constructor
 */

function DrawingObjectsManager(worksheet)
{
    this.worksheet = worksheet;
}

DrawingObjectsManager.prototype.updateChartReferences = function(oldWorksheet, newWorksheet)
{
    AscFormat.ExecuteNoHistory(function(){
        this.updateChartReferencesWidthHistory(oldWorksheet, newWorksheet);
    }, this, []);
};

DrawingObjectsManager.prototype.updateChartReferencesWidthHistory = function(oldWorksheet, newWorksheet, bNoRebuildCache)
{
    var aObjects = this.worksheet.Drawings;
    for (var i = 0; i < aObjects.length; i++) {
        var graphicObject = aObjects[i].graphicObject;
        if ( graphicObject.updateChartReferences )
        {
            graphicObject.updateChartReferences(oldWorksheet, newWorksheet, bNoRebuildCache);
        }
    }
};


DrawingObjectsManager.prototype.rebuildCharts = function(data)
{
    var aObjects = this.worksheet.Drawings;
    for(var i = 0; i < aObjects.length; ++i)
    {
        if(aObjects[i].graphicObject.rebuildSeries)
        {
            aObjects[i].graphicObject.rebuildSeries(data);
        }
    }
};

  window['AscCommonExcel'] = window['AscCommonExcel'] || {};
  window['AscCommonExcel'].g_nVerticalTextAngle = g_nVerticalTextAngle;
  window['AscCommonExcel'].oDefaultMetrics = oDefaultMetrics;
  window['AscCommonExcel'].g_nAllColIndex = g_nAllColIndex;
  window['AscCommonExcel'].g_nAllRowIndex = g_nAllRowIndex;
  window['AscCommonExcel'].aStandartNumFormats = aStandartNumFormats;
  window['AscCommonExcel'].aStandartNumFormatsId = aStandartNumFormatsId;
  window['AscCommonExcel'].oFormulaLocaleInfo = oFormulaLocaleInfo;
  window['AscCommonExcel'].DependencyGraph = DependencyGraph;
  window['AscCommonExcel'].getVertexId = getVertexId;
  window['AscCommonExcel'].buildDefNameAfterRenameWorksheet = buildDefNameAfterRenameWorksheet;
  window['AscCommonExcel'].angleFormatToInterface2 = angleFormatToInterface2;
  window['AscCommonExcel'].angleInterfaceToFormat = angleInterfaceToFormat;
  window['AscCommonExcel'].Workbook = Workbook;
  window['AscCommonExcel'].Woorksheet = Woorksheet;
  window['AscCommonExcel'].Cell = Cell;
  window['AscCommonExcel'].Range = Range;
  window['AscCommonExcel'].preparePromoteFromTo = preparePromoteFromTo;
  window['AscCommonExcel'].promoteFromTo = promoteFromTo;
  window['AscCommonExcel'].DefinedName = DefinedName;
})(window);
