"use strict";

/* WorkbookView.js
 *
 * Author: Dmitry.Sokolov@avsmedia.net
 * Date:   Jan 27, 2012
 */
(
	/**
	 * @param {jQuery} $
	 * @param {Window} window
	 * @param {undefined} undefined
	 */
	function ($, window, undefined) {


		/*
		 * Import
		 * -----------------------------------------------------------------------------
		 */
		var asc					= window["Asc"];
		var asc_applyFunction	= asc.applyFunction;
		var asc_round			= asc.round;
		var asc_typeof			= asc.typeOf;
		var asc_CMM				= asc.asc_CMouseMoveData;
		var asc_CPrintPagesData	= asc.CPrintPagesData;
		var asc_getcvt			= asc.getCvtRatio;
		var asc_CSP				= asc.asc_CStylesPainter;

		function WorkbookCommentsModel (handlers) {
			this.workbook = {handlers: handlers};
		}
		WorkbookCommentsModel.prototype.getId = function () {
			return "workbook";
		};
		WorkbookCommentsModel.prototype.getMergedByCell = function () {
			return null;
		};

		function WorksheetViewSettings() {
			this.header = {
				style: [
					// Header colors
					{ // kHeaderDefault
						background: new CColor(244, 244, 244),
						border: new CColor(213, 213, 213),
						color: new CColor(54, 54, 54)
					},
					{ // kHeaderActive
						background: new CColor(193, 193, 193),
						border: new CColor(146, 146, 146),
						color: new CColor(54, 54, 54)
					},
					{ // kHeaderHighlighted
						background: new CColor(223, 223, 223),
						border: new CColor(175, 175, 175),
						color: new CColor(101, 106, 112)
					},
					{ // kHeaderSelected
						background: new CColor(170, 170, 170),
						border: new CColor(117, 119, 122),
						color: new CColor(54, 54, 54)
					}
				],
				cornerColor: new CColor(193, 193, 193)
			};
			this.cells = {
				defaultState: {
					background: new CColor(255, 255, 255),
					border: new CColor(212, 212, 212),
					color: new CColor(0, 0, 0)
				},
				padding: -1, /*px horizontal padding*/
				paddingPlusBorder: -1
			};
			this.activeCellBorderColor = new CColor(126, 152, 63);
			this.activeCellBorderColor2 = new CColor(255, 255, 255, 0.75);
			this.activeCellBackground = new CColor(157, 185, 85, 0.2);

			// Цвет заливки границы выделения области автозаполнения
			this.fillHandleBorderColorSelect = new CColor(255, 255, 255);

			// Цвет закрепленных областей
			this.frozenColor = new CColor(105, 119, 62, 1);

			// Число знаков для математической информации
			this.mathMaxDigCount = 9;

			var cnv = document.createElement("canvas");
			cnv.width = 2;
			cnv.height = 2;
			var ctx = cnv.getContext("2d");
			ctx.clearRect(0, 0, 2, 2);
			ctx.fillStyle = "#000";
			ctx.fillRect(0, 0, 1, 1);
			ctx.fillRect(1, 1, 1, 1);
			this.ptrnLineDotted1 = ctx.createPattern(cnv, "repeat");

			return this;
		}


		/**
		 * Widget for displaying and editing Workbook object
		 * -----------------------------------------------------------------------------
		 * @param {Workbook} model               					Workbook
		 * @param {asc_CEventsController} controller  				Events controller
		 * @param {HandlersList} handlers        					Events handlers for WorkbookView events
		 * @param {Element} elem                 					Container element
		 * @param {Element} inputElem            					Input element for top line editor
		 * @param {Object} Api
		 * @param {CCollaborativeEditing} collaborativeEditing
		 * @param {c_oAscFontRenderingModeType} fontRenderingMode
		 *
		 * @constructor
		 * @memberOf Asc
		 */
		function WorkbookView(model, controller, handlers, elem, inputElem, Api, collaborativeEditing, fontRenderingMode) {
			this.defaults = {
				scroll: {
					widthPx	: 14,
					heightPx: 14
				},
				worksheetView: new WorksheetViewSettings()
			};

			this.model					= model;
			this.controller				= controller;
			this.handlers				= handlers;
			this.wsViewHandlers			= null;
			this.element				= elem;
			this.input					= inputElem;
			this.clipboard				= new asc.Clipboard();
			this.Api					= Api;
			this.collaborativeEditing	= collaborativeEditing;
			this.lastSendInfoRange		= null;
			this.canUpdateAfterShiftUp	= false;	// Нужно ли обновлять информацию после отпускания Shift

			//----- declaration -----
			this.canvas = undefined;
			this.canvasOverlay = undefined;
			this.canvasGraphic = undefined;
			this.canvasGraphicOverlay = undefined;
			this.wsActive = -1;
			this.wsMustDraw = false; // Означает, что мы выставили активный, но не отрисовали его
			this.wsViews = [];
			this.cellEditor = undefined;
			this.fontRenderingMode = null;

			this.popUpSelector = null;
			this.formulasList = null;	// Список всех формул
			this.lastFormulaPos = -1; 	// Последняя позиция формулы
			this.lastFormulaName = "";	// Последний кусок формулы

			this.lastFindOptions = null;	// Последний поиск (параметры)
			this.lastFindResults = {};		// Результаты поиска (для поиска по всей книге, чтобы перейти на другой лист)
			this.fReplaceCallback = null;	// Callback для замены текста

			// Фонт, который выставлен в DrawingContext, он должен быть один на все DrawingContext-ы
			this.m_oFont = new asc.FontProperties(this.model.getDefaultFont(), this.model.getDefaultSize());

			// Теперь у нас 2 FontManager-а на весь документ + 1 для автофигур (а не на каждом листе свой)
			this.fmgrGraphics = [];						// FontManager for draw (1 для обычного + 1 для поворотного текста)
			this.fmgrGraphics.push(new CFontManager());	// Для обычного
			this.fmgrGraphics.push(new CFontManager());	// Для поворотного
			this.fmgrGraphics.push(new CFontManager());	// Для автофигур
			this.fmgrGraphics.push(new CFontManager());	// Для измерений

			this.fmgrGraphics[0].Initialize(true); // IE memory enable
			this.fmgrGraphics[1].Initialize(true); // IE memory enable
			this.fmgrGraphics[2].Initialize(true); // IE memory enable
			this.fmgrGraphics[3].Initialize(true); // IE memory enable

			this.buffers = {};
			this.drawingCtx = undefined;
			this.overlayCtx = undefined;
			this.drawingGraphicCtx = undefined;
			this.overlayGraphicCtx = undefined;
			this.stringRender = undefined;

			this.selectionDialogType = c_oAscSelectionDialogType.None;
			this.copyActiveSheet = -1;

			// Комментарии для всего документа
			this.cellCommentator = null;

			// Флаг о подписке на эвенты о смене позиции документа (скролл) для меню
			this.isDocumentPlaceChangedEnabled = false;
			// Константы для подстановке формулы (что не нужно добавлять скобки)
			this.arrExcludeFormulas = ['TRUE', 'FALSE'];

			// Максимальная ширина числа из 0,1,2...,9, померенная в нормальном шрифте(дефалтовый для книги) в px(целое)
			// Ecma-376 Office Open XML Part 1, пункт 18.3.1.13
			this.maxDigitWidth = 0;
			this.defaultFont = new asc.FontProperties(this.model.getDefaultFont(), this.model.getDefaultSize());
			//-----------------------

            this.m_dScrollY         = 0;
            this.m_dScrollX         = 0;
            this.m_dScrollY_max     = 1;
            this.m_dScrollX_max     = 1;

            this.MobileTouchManager = null;

			this._init(fontRenderingMode);

			return this;
		}

		WorkbookView.prototype._init = function (fontRenderingMode) {
			var self = this;

			// Init font managers rendering
			// Изначально мы инициализируем c_oAscFontRenderingModeType.hintingAndSubpixeling
			this.setFontRenderingMode(fontRenderingMode, /*isInit*/true);

			// add style
            var _head = document.getElementsByTagName('head')[0];
            var style0 = document.createElement('style');
            style0.type = 'text/css';
            style0.innerHTML = ".block_elem { position:absolute;padding:0;margin:0; }";
            _head.appendChild(style0);

            // create canvas
			if (null != this.element) {
				this.element.innerHTML = '<div id="ws-canvas-outer">\
											<canvas id="ws-canvas"></canvas>\
											<canvas id="ws-canvas-overlay"></canvas>\
											<canvas id="ws-canvas-graphic"></canvas>\
											<canvas id="ws-canvas-graphic-overlay"></canvas>\
											<canvas id="id_target_cursor" class="block_elem" width="1" height="1"\
												style="width:2px;height:13px;display:none;z-index:1004;"></canvas>\
										</div>';

				this.canvas = document.getElementById("ws-canvas");
				this.canvasOverlay = document.getElementById("ws-canvas-overlay");
				this.canvasGraphic = document.getElementById("ws-canvas-graphic");
				this.canvasGraphicOverlay = document.getElementById("ws-canvas-graphic-overlay");
			}

			// Для мобильных не поддерживаем ретину
			if (this.Api.isMobileVersion) {
				AscBrowser.isRetina = false;
			}

			this.buffers.main = new asc.DrawingContext({canvas: this.canvas, units: 1/*pt*/, fmgrGraphics: this.fmgrGraphics, font: this.m_oFont});
			this.buffers.overlay = new asc.DrawingContext({canvas: this.canvasOverlay, units: 1/*pt*/, fmgrGraphics: this.fmgrGraphics, font: this.m_oFont});
			
			this.buffers.mainGraphic = new asc.DrawingContext({canvas: this.canvasGraphic, units: 1/*pt*/, fmgrGraphics: this.fmgrGraphics, font: this.m_oFont});
			this.buffers.overlayGraphic = new asc.DrawingContext({canvas: this.canvasGraphicOverlay, units: 1/*pt*/, fmgrGraphics: this.fmgrGraphics, font: this.m_oFont});
			
			this.drawingCtx = this.buffers.main;
			this.overlayCtx = this.buffers.overlay;
			this.drawingGraphicCtx = this.buffers.mainGraphic;
			this.overlayGraphicCtx = this.buffers.overlayGraphic;
			
			// Обновляем размеры (чуть ниже, потому что должны быть проинициализированы ctx)
			this._canResize();

			// Shapes
			this.buffers.shapeCtx = new CGraphics();
			this.buffers.shapeCtx.init(this.drawingGraphicCtx.ctx, this.drawingGraphicCtx.getWidth(0), this.drawingGraphicCtx.getHeight(0), this.drawingGraphicCtx.getWidth(3), this.drawingGraphicCtx.getHeight(3));
			this.buffers.shapeCtx.m_oFontManager = this.fmgrGraphics[2];

			this.buffers.shapeOverlayCtx = new CGraphics();
			this.buffers.shapeOverlayCtx.init(this.overlayGraphicCtx.ctx, this.overlayGraphicCtx.getWidth(0), this.overlayGraphicCtx.getHeight(0), this.overlayGraphicCtx.getWidth(3), this.overlayGraphicCtx.getHeight(3));
			this.buffers.shapeOverlayCtx.m_oFontManager = this.fmgrGraphics[2];

			this.stringRender = new asc.StringRender(this.buffers.main);
			this.stringRender.setDefaultFont(this.defaultFont);

			// Мерить нужно только со 100% и один раз для всего документа
			this._calcMaxDigitWidth();

			if (!window["NATIVE_EDITOR_ENJINE"]) {
			    // initialize events controller
			    this.controller.init(this, this.element, /*this.canvasOverlay*/ this.canvasGraphicOverlay, /*handlers*/{
				    "resize":					function () {self.resize.apply(self, arguments);},
				    "reinitializeScroll":		function () {self._onScrollReinitialize.apply(self, arguments);},
				    "scrollY":					function () {self._onScrollY.apply(self, arguments);},
				    "scrollX":					function () {self._onScrollX.apply(self, arguments);},
				    "changeSelection":			function () {self._onChangeSelection.apply(self, arguments);},
				    "changeSelectionDone":		function () {self._onChangeSelectionDone.apply(self, arguments);},
				    "changeSelectionRightClick":function () {self._onChangeSelectionRightClick.apply(self, arguments);},
				    "selectionActivePointChanged":	function () {self._onSelectionActivePointChanged.apply(self, arguments);},
				    "updateWorksheet":			function () {self._onUpdateWorksheet.apply(self, arguments);},
				    "resizeElement":			function () {self._onResizeElement.apply(self, arguments);},
				    "resizeElementDone":		function () {self._onResizeElementDone.apply(self, arguments);},
				    "changeFillHandle":			function () {self._onChangeFillHandle.apply(self, arguments);},
				    "changeFillHandleDone":		function () {self._onChangeFillHandleDone.apply(self, arguments);},
				    "moveRangeHandle":			function () {self._onMoveRangeHandle.apply(self, arguments);},
				    "moveRangeHandleDone":		function () {self._onMoveRangeHandleDone.apply(self, arguments);},
				    "moveResizeRangeHandle":	function () {self._onMoveResizeRangeHandle.apply(self, arguments);},
				    "moveResizeRangeHandleDone":function () {self._onMoveResizeRangeHandleDone.apply(self, arguments);},
				    "editCell":          		function () {self._onEditCell.apply(self, arguments);},
				    "stopCellEditing":   		function () {return self._onStopCellEditing.apply(self, arguments);},
				    "empty":					function () {self._onEmpty.apply(self, arguments);},
				    "canEnterCellRange":		function () {
															    self.cellEditor.setFocus(false);
															    var ret = self.cellEditor.canEnterCellRange();
															    ret ? self.cellEditor.activateCellRange() : true;
															    return ret;
														    },
				    "enterCellRange":			function () {self.cellEditor.setFocus(false); self.getWorksheet().enterCellRange(self.cellEditor);},
				    "copy":						function () {self.copyToClipboard.apply(self, arguments);},
				    "paste":					function () {self.pasteFromClipboard.apply(self, arguments);},
				    "cut":						function () {self.cutToClipboard.apply(self, arguments);},
				    "undo":						function () {self.undo.apply(self, arguments);},
				    "redo":						function () {self.redo.apply(self, arguments);},
				    "addColumn":				function () {self._onAddColumn.apply(self, arguments);},
				    "addRow":					function () {self._onAddRow.apply(self, arguments);},
				    "mouseDblClick":			function () {self._onMouseDblClick.apply(self, arguments);},
				    "showNextPrevWorksheet":	function () {self._onShowNextPrevWorksheet.apply(self, arguments);},
				    "setFontAttributes":		function () {self._onSetFontAttributes.apply(self, arguments);},
				    "selectColumnsByRange":		function () {self._onSelectColumnsByRange.apply(self, arguments);},
				    "selectRowsByRange":		function () {self._onSelectRowsByRange.apply(self, arguments);},
				    "save":						function () {self.Api.asc_Save();},
				    "showCellEditorCursor":		function () {self._onShowCellEditorCursor.apply(self, arguments);},
				    "print":					function () {self.Api.asc_Print();},
				    "addFunction":				function () {self.insertFormulaInEditor.apply(self, arguments);},
				    "canvasClick":				function () {self.enableKeyEventsHandler(true);},
				    "autoFiltersClick":			function () {self._onAutoFiltersClick.apply(self, arguments);},
				    "commentCellClick":			function () {self._onCommentCellClick.apply(self, arguments);},
				    "isGlobalLockEditCell":		function () {return self.collaborativeEditing.getGlobalLockEditCell();},
				    "updateSelectionName":		function () {self._onUpdateSelectionName.apply(self, arguments);},
					"stopFormatPainter":		function () {self._onStopFormatPainter.apply(self, arguments);},

				    // Shapes
				    "graphicObjectMouseDown":		function () {self._onGraphicObjectMouseDown.apply(self, arguments);},
				    "graphicObjectMouseMove":		function () {self._onGraphicObjectMouseMove.apply(self, arguments);},
				    "graphicObjectMouseUp":			function () {self._onGraphicObjectMouseUp.apply(self, arguments);},
				    "graphicObjectMouseUpEx":		function () {self._onGraphicObjectMouseUpEx.apply(self, arguments);},
				    "graphicObjectWindowKeyDown":	function () {return self._onGraphicObjectWindowKeyDown.apply(self, arguments);},
				    "graphicObjectWindowKeyPress":	function () {return self._onGraphicObjectWindowKeyPress.apply(self, arguments);},
				    "getGraphicsInfo":				function () {return self._onGetGraphicsInfo.apply(self, arguments);},
				    "getSelectedGraphicObjects":	function () {return self._onGetSelectedGraphicObjects.apply(self, arguments);},
				    "updateSelectionShape":			function () {return self._onUpdateSelectionShape.apply(self, arguments);},
					"canReceiveKeyPress":			function () {return self.getWorksheet().objectRender.controller.canReceiveKeyPress();},
					"stopAddShape":			        function () {
                        self.getWorksheet().objectRender.controller.checkEndAddShape();
                    },

					// Frozen anchor
					"moveFrozenAnchorHandle":		function () {self._onMoveFrozenAnchorHandle.apply(self, arguments);},
					"moveFrozenAnchorHandleDone":	function () {self._onMoveFrozenAnchorHandleDone.apply(self, arguments);},

					// AutoComplete
					"showAutoComplete":				function () {self._onShowAutoComplete.apply(self, arguments);},
					"popUpSelectorKeyDown":			function (event) {return self._onPopUpSelectorKeyDown(event);},
					"isPopUpSelectorOpen":			function () {return self.popUpSelector.getVisible();},

					"onContextMenu":				function (event) {self.handlers.trigger("asc_onContextMenu", event);}
			    });

				if (this.input && this.input.addEventListener) {
					this.input.addEventListener("focus", function () {
						self.input.isFocused = true;
						if (self.controller.settings.isViewerMode) {
							return;
						}
						self.controller.setStrictClose(true);
						self.cellEditor.callTopLineMouseup = true;
						if (!self.controller.isCellEditMode && !self.controller.isFillHandleMode) {
							self._onEditCell(0, 0, /*isCoord*/false, /*isFocus*/true);
						}
					}, false);
				}

				this.cellEditor = new asc.CellEditor(this.element, this.input, this.fmgrGraphics, this.m_oFont,
					/*handlers*/{
						"closed"   : function () {self._onCloseCellEditor.apply(self, arguments);},
						"updated"  : function () {self._onUpdateCellEditor.apply(self, arguments);},
						"gotFocus" : function (hasFocus) {self.controller.setFocus(!hasFocus);},
						"copy"     : function () {self.copyToClipboard.apply(self, arguments);},
						"paste"    : function () {self.pasteFromClipboard.apply(self, arguments);},
						"cut"      : function () {self.cutToClipboard.apply(self, arguments);},
						"updateFormulaEditMod": function () {
							self.controller.setFormulaEditMode.apply(self.controller, arguments);
							var ws = self.getWorksheet();
							if (ws) {
								ws.cleanSelection();
								ws.cleanFormulaRanges();
								ws.setFormulaEditMode.apply(ws, arguments);
							}
						},
						"updateEditorState"			: function (state) {self.handlers.trigger("asc_onEditCell", state);},
						"isGlobalLockEditCell"		: function () {return self.collaborativeEditing.getGlobalLockEditCell();},
						"updateFormulaEditModEnd"	: function (rangeUpdated) {self.getWorksheet().updateSelection();},
						"newRange"     				: function (range) { self.getWorksheet().addFormulaRange(range); },
						"existedRange" 				: function (range) { self.getWorksheet().activeFormulaRange(range); },
						"updateUndoRedoChanged"		: function (bCanUndo, bCanRedo) {
							self.handlers.trigger("asc_onCanUndoChanged", bCanUndo);
							self.handlers.trigger("asc_onCanRedoChanged", bCanRedo);
							self.Api.setUserAlive();
						},
						"applyCloseEvent"			: function () {self.controller._onWindowKeyDown.apply(self.controller, arguments);},
						"isViewerMode"				: function () {return self.controller.settings.isViewerMode;},
						"popUpSelectorKeyDown"		: function (event) {return self._onPopUpSelectorKeyDown(event);},
						"getFormulaRanges"			: function () {return self.getWorksheet().getFormulaRanges();},
						"setStrictClose"			: function (val) {self.controller.setStrictClose(val);},
						"updateEditorSelectionInfo"	: function (info) {self.handlers.trigger("asc_onEditorSelectionChanged", info);},
						"onContextMenu"				: function (event) {self.handlers.trigger("asc_onContextMenu", event);}
					},
					/*settings*/{
						font: this.defaultFont,
						padding: this.defaults.worksheetView.cells.padding
					});

				this.popUpSelector = new asc.PopUpSelector(this.element, /*handlers*/{
					"insert"	: function () {self._onPopUpSelectorInsert.apply(self, arguments);}
				});
			}

			this.wsViewHandlers = new asc.asc_CHandlersList(/*handlers*/{
				"getViewerMode"				: function () { return self.controller.getViewerMode ? self.controller.getViewerMode() : true; },
				"reinitializeScroll"		: function () {self.controller.reinitializeScroll(/*All*/);},
				"reinitializeScrollY"		: function () {self.controller.reinitializeScroll(/*vertical*/1);},
				"reinitializeScrollX"		: function () {self.controller.reinitializeScroll(/*horizontal*/2);},
				"selectionChanged"			: function () {self._onWSSelectionChanged.apply(self, arguments);},
				"selectionNameChanged"		: function () {self._onSelectionNameChanged.apply(self, arguments);},
				"selectionMathInfoChanged"	: function () {self._onSelectionMathInfoChanged.apply(self, arguments);},
				"onErrorEvent"				: function (errorId, level) {self.handlers.trigger("asc_onError", errorId, level);},
				"slowOperation"				: function (isStart) {self.handlers.trigger((isStart ? "asc_onStartAction" : "asc_onEndAction"), c_oAscAsyncActionType.BlockInteraction, c_oAscAsyncAction.SlowOperation);},
				"setAutoFiltersDialog"  	: function (arrVal) {self.handlers.trigger("asc_onSetAFDialog", arrVal);},
				"selectionRangeChanged"		: function (val) {self.handlers.trigger("asc_onSelectionRangeChanged", val);},
				"onRenameCellTextEnd"		: function (countFind, countReplace) {self.handlers.trigger("asc_onRenameCellTextEnd", countFind, countReplace);},
				"onStopFormatPainter"		: function () {self.handlers.trigger("asc_onStopFormatPainter");},
				"onDocumentPlaceChanged"	: function () {self._onDocumentPlaceChanged();},
				"updateSheetViewSettings"	: function () {self.handlers.trigger("asc_onUpdateSheetViewSettings");},
				"onScroll"					: function (d) {self.controller.scroll(d);}
			});

			this.model.handlers.add("cleanCellCache", function (wsId, oRanges, canChangeColWidth, bLockDraw, updateHeight) {
				var ws = self.getWorksheetById(wsId);
				if (ws)
					ws.updateRanges(oRanges, canChangeColWidth,
						bLockDraw || wsId != self.getWorksheet(self.wsActive).model.getId(), updateHeight);
			});
			this.model.handlers.add("changeWorksheetUpdate", function (wsId, val) {
				var ws = self.getWorksheetById(wsId);
				if (ws)
					ws.changeWorksheet("update", val);
			});
			this.model.handlers.add("showWorksheet", function (wsId) {
				var wsModel = self.model.getWorksheetById(wsId), index;
				if (wsModel) {
					index = wsModel.getIndex();
					self.showWorksheet(index, false, true);
					self.handlers.trigger("asc_onActiveSheetChanged", index);
				}
			});
			this.model.handlers.add("setSelection", function () {
				self._onSetSelection.apply(self, arguments);
			});
			this.model.handlers.add("getSelection", function () {
				return self._onGetSelection.apply(self);
			});
			this.model.handlers.add("getSelectionState", function () {
				return self._onGetSelectionState.apply(self);
			});
			this.model.handlers.add("setSelectionState", function () {
				self._onSetSelectionState.apply(self, arguments);
			});
			this.model.handlers.add("reInit", function () {
				self.reInit.apply(self, arguments);
			});
			this.model.handlers.add("drawWS", function () {
				self.drawWS.apply(self, arguments);
			});
			this.model.handlers.add("showDrawingObjects", function () {
				self.onShowDrawingObjects.apply(self, arguments);
			});
			this.model.handlers.add("setCanUndo", function (bCanUndo) {
				self.handlers.trigger("asc_onCanUndoChanged", bCanUndo);
				self.Api.setUserAlive();
			});
			this.model.handlers.add("setCanRedo", function (bCanRedo) {
				self.handlers.trigger("asc_onCanRedoChanged", bCanRedo);
				self.Api.setUserAlive();
			});
			this.model.handlers.add("setDocumentModified", function (bIsModified) {
				self.Api.onUpdateDocumentModified(bIsModified);
			});
			this.model.handlers.add("initCommentsToSave", function () {
				self._initCommentsToSave();
			});
			this.model.handlers.add("replaceWorksheet", function (from, to) {
				self.replaceWorksheet(from, to);
			});
			this.model.handlers.add("removeWorksheet", function (nIndex) {
				self.removeWorksheet(nIndex);
			});
			this.model.handlers.add("spliceWorksheet", function () {
				self.spliceWorksheet.apply(self, arguments);
			});
			this.model.handlers.add("updateWorksheetByModel", function () {
				self.updateWorksheetByModel.apply(self, arguments);
			});
			this.model.handlers.add("undoRedoAddRemoveRowCols", function (sheetId, type, range, bUndo) {
				if (true === bUndo) {
					if (historyitem_Worksheet_AddRows === type) {
						self.collaborativeEditing.removeRowsRange(sheetId, range.clone(true));
						self.collaborativeEditing.undoRows(sheetId, range.r2 - range.r1 + 1);
					} else if (historyitem_Worksheet_RemoveRows === type) {
						self.collaborativeEditing.addRowsRange(sheetId, range.clone(true));
						self.collaborativeEditing.undoRows(sheetId, range.r2 - range.r1 + 1);
					} else if (historyitem_Worksheet_AddCols === type) {
						self.collaborativeEditing.removeColsRange(sheetId, range.clone(true));
						self.collaborativeEditing.undoCols(sheetId, range.c2 - range.c1 + 1);
					} else if (historyitem_Worksheet_RemoveCols === type) {
						self.collaborativeEditing.addColsRange(sheetId, range.clone(true));
						self.collaborativeEditing.undoCols(sheetId, range.c2 - range.c1 + 1);
					}
				} else {
					if (historyitem_Worksheet_AddRows === type) {
						self.collaborativeEditing.addRowsRange(sheetId, range.clone(true));
						self.collaborativeEditing.addRows(sheetId, range.r1, range.r2 - range.r1 + 1);
					} else if (historyitem_Worksheet_RemoveRows === type) {
						self.collaborativeEditing.removeRowsRange(sheetId, range.clone(true));
						self.collaborativeEditing.removeRows(sheetId, range.r1, range.r2 - range.r1 + 1);
					} else if (historyitem_Worksheet_AddCols === type) {
						self.collaborativeEditing.addColsRange(sheetId, range.clone(true));
						self.collaborativeEditing.addCols(sheetId, range.c1, range.c2 - range.c1 + 1);
					} else if (historyitem_Worksheet_RemoveCols === type) {
						self.collaborativeEditing.removeColsRange(sheetId, range.clone(true));
						self.collaborativeEditing.removeCols(sheetId, range.c1, range.c2 - range.c1 + 1);
					}
				}
			});
			this.model.handlers.add("undoRedoHideSheet", function (sheetId) {
				self.showWorksheet(sheetId);
				// Посылаем callback об изменении списка листов
				self.handlers.trigger("asc_onSheetsChanged");
			});

			this.cellCommentator = new CCellCommentator({
				model: new WorkbookCommentsModel(this.handlers),
				collaborativeEditing: this.collaborativeEditing,
				draw: function() {},
				handlers: {trigger: function (){return true;}}
			});
			var commentList = this.cellCommentator.prepareComments(this.model.aComments);
			if (0 < commentList.length)
				this.handlers.trigger("asc_onAddComments", commentList);

			this.clipboard.Api = this.Api;
			this.clipboard.init();

			this.initFormulasList();

			this.fReplaceCallback = function () {self._replaceCellTextCallback.apply(self, arguments);};

            if (this.Api.isMobileVersion) {
                this.MobileTouchManager = new CMobileTouchManager();
                this.MobileTouchManager.Init(this);
            }
			return this;
		};

		WorkbookView.prototype.destroy = function () {
			this.controller.destroy();
			this.cellEditor.destroy();
			return this;
		};

		WorkbookView.prototype._createWorksheetView = function (wsModel) {
			return new asc.WorksheetView(wsModel, this.wsViewHandlers, this.buffers, this.stringRender,
				this.maxDigitWidth, this.collaborativeEditing, this.defaults.worksheetView);
		};

		WorkbookView.prototype._onSelectionNameChanged = function (name) {
			this.handlers.trigger("asc_onSelectionNameChanged", name);
		};

		WorkbookView.prototype._onSelectionMathInfoChanged = function (info) {
			this.handlers.trigger("asc_onSelectionMathChanged", info);
		};

		// Проверяет, сменили ли мы диапазон (для того, чтобы не отправлять одинаковую информацию о диапазоне)
		WorkbookView.prototype._isEqualRange = function (range, isSelectOnShape) {
			if (null === this.lastSendInfoRange)
				return false;

			return this.lastSendInfoRange.isEqual(range) && this.lastSendInfoRangeIsSelectOnShape === isSelectOnShape;
		};

		WorkbookView.prototype._onWSSelectionChanged = function (info) {
			var ws = this.getWorksheet();
			var ar = ws.activeRange;
			this.lastSendInfoRange = ar.clone(true);
			this.lastSendInfoRangeIsSelectOnShape = ws.getSelectionShape();

			if (null === info) {
				info = ws.getSelectionInfo();
			}
			// При редактировании ячейки не нужно пересылать изменения
			if (this.input && false === ws.getCellEditMode() &&
				c_oAscSelectionDialogType.None === this.selectionDialogType) {
				// Сами запретим заходить в строку формул, когда выделен shape
				if (this.lastSendInfoRangeIsSelectOnShape) {
					this.input.disabled = true;
					this.input.value = '';
				} else {
					this.input.disabled = false;
					this.input.value = info.text;
				}
			}
			this.handlers.trigger("asc_onSelectionChanged", info);
		};


		WorkbookView.prototype._onScrollReinitialize = function (whichSB, callback) {
			var ws = this.getWorksheet(),
			    vsize = !whichSB || whichSB === 1 ? ws.getVerticalScrollRange() : undefined,
			    hsize = !whichSB || whichSB === 2 ? ws.getHorizontalScrollRange() : undefined;

            if( vsize != undefined )
                this.m_dScrollY_max = Math.max(this.controller.settings.vscrollStep * (vsize + 1), 1);
            if( hsize != undefined )
                this.m_dScrollX_max = Math.max(this.controller.settings.hscrollStep * (hsize + 1), 1);

            asc_applyFunction(callback, vsize, hsize);
		};

		WorkbookView.prototype._onScrollY = function (pos) {
			var ws = this.getWorksheet();
			var delta = asc_round(pos - ws.getFirstVisibleRow(/*allowPane*/true));
			if (delta !== 0) {
				ws.scrollVertical(delta, this.cellEditor);
			}
		};

		WorkbookView.prototype._onScrollX = function (pos) {
			var ws = this.getWorksheet();
			var delta = asc_round(pos - ws.getFirstVisibleCol(/*allowPane*/true));
			if (delta !== 0) {
				ws.scrollHorizontal(delta, this.cellEditor);
			}
		};

		WorkbookView.prototype._onSetSelection = function (range, validRange) {
			var ws = this.getWorksheet();
			ws._checkSelectionShape();
			var d = ws.setSelectionUndoRedo(range, validRange);
			this.controller.scroll(d);
		};

		WorkbookView.prototype._onGetSelection = function () {
			var ws = this.getWorksheet();
			return ws.getActiveRangeObj();
		};

		WorkbookView.prototype._onGetSelectionState = function () {
			var ws = this.getWorksheet();
			var res = null;
			if (isRealObject(ws.objectRender) && isRealObject(ws.objectRender.controller))
				res = ws.objectRender.controller.getSelectionState();
			// ToDo лучше на getSelectionState возвращать null
			return (res && res[0] && res[0].focus) ? res : null;
		};

		WorkbookView.prototype._onSetSelectionState = function (state) {
            if (null !== state) {
                var ws = this.getWorksheetById(state[0].worksheetId);
                if (ws && ws.objectRender && ws.objectRender.controller) {
                    ws.objectRender.controller.setSelectionState(state);
                    ws.setSelectionShape(true);
                    var d = ws._calcActiveCellOffset(ws.objectRender.getSelectedDrawingsRange());
					this.controller.scroll(d);
                    ws.objectRender.showDrawingObjectsEx(true);
                    ws.objectRender.controller.updateOverlay();
                    ws.objectRender.controller.updateSelectionState();
                }
                // Селектим после выставления состояния
            }
		};

		WorkbookView.prototype._onChangeSelection = function (isStartPoint, dc, dr, isCoord, isSelectMode, callback) {
			var ws = this.getWorksheet();
			var d = isStartPoint ?
					ws.changeSelectionStartPoint(dc, dr, isCoord, isSelectMode):
					ws.changeSelectionEndPoint(dc, dr, isCoord, isSelectMode);
			if (!isCoord && !isStartPoint && !isSelectMode) {
				// Выделение с зажатым shift
				this.canUpdateAfterShiftUp = true;
			}
			asc_applyFunction(callback, d);
		};

		// Окончание выделения
		WorkbookView.prototype._onChangeSelectionDone = function (x, y) {
			var ws = this.getWorksheet();
			ws.changeSelectionDone();
			this._onSelectionNameChanged(ws.getSelectionName(/*bRangeText*/false));
			// Проверим, нужно ли отсылать информацию о ячейке
			var ar = ws.activeRange;
			var isSelectOnShape = ws.getSelectionShape();
			if (!this._isEqualRange(ar, isSelectOnShape)) {
				this._onWSSelectionChanged(ws.getSelectionInfo());
				this._onSelectionMathInfoChanged(ws.getSelectionMathInfo());
			}

			// Нужно очистить поиск
			this._cleanFindResults();

			var ct = ws.getCursorTypeFromXY(x, y, this.controller.settings.isViewerMode);

			if (c_oTargetType.Hyperlink === ct.target) {
				// Проверим замерженность
				var isHyperlinkClick = false;
				if ((ar.c1 === ar.c2 && ar.r1 === ar.r2) || isSelectOnShape)
					isHyperlinkClick = true;
				else {
					var mergedRange = ws.model.getMergedByCell(ar.r1, ar.c1);
					if (mergedRange && ar.isEqual(mergedRange))
						isHyperlinkClick = true;
				}
				if (isHyperlinkClick) {
					if (false === ct.hyperlink.hyperlinkModel.getVisited() && !isSelectOnShape) {
						ct.hyperlink.hyperlinkModel.setVisited(true);
						if (ct.hyperlink.hyperlinkModel.Ref)
							ws.updateRange(ct.hyperlink.hyperlinkModel.Ref.getBBox0(), false, false);
					}
					switch (ct.hyperlink.asc_getType()) {
						case c_oAscHyperlinkType.WebLink:
							this.handlers.trigger("asc_onHyperlinkClick", ct.hyperlink.asc_getHyperlinkUrl());
							break;
						case c_oAscHyperlinkType.RangeLink:
							// ToDo надо поправить отрисовку комментария для данной ячейки (с которой уходим)
							this.handlers.trigger("asc_onHideComment");
							this.Api._asc_setWorksheetRange(ct.hyperlink);
							break;
					}
				}
			}
		};

		// Обработка нажатия правой кнопки мыши
		WorkbookView.prototype._onChangeSelectionRightClick = function (dc, dr) {
			var ws = this.getWorksheet();
			ws.changeSelectionStartPointRightClick(dc, dr);
		};

		// Обработка движения в выделенной области
		WorkbookView.prototype._onSelectionActivePointChanged = function (dc, dr, callback) {
			var ws = this.getWorksheet();
			var d = ws.changeSelectionActivePoint(dc, dr);
			asc_applyFunction(callback, d);
		};

		WorkbookView.prototype._onUpdateWorksheet = function (canvasElem, x, y, ctrlKey, callback) {
			var ws = this.getWorksheet(), ct = undefined;
			var arrMouseMoveObjects = [];					// Теперь это массив из объектов, над которыми курсор

			//ToDo: включить определение target, если находимся в режиме редактирования ячейки.
			if (this.controller.isCellEditMode && !this.controller.isFormulaEditMode) {
				canvasElem.style.cursor = "";
			} else if (x === undefined && y === undefined) {
				ws.cleanHighlightedHeaders();
			} else {
				ct = ws.getCursorTypeFromXY(x, y, this.controller.settings.isViewerMode);

				// Отправление эвента об удалении всего листа (именно удалении, т.к. если просто залочен, то не рисуем рамку вокруг)
				if (undefined !== ct.userIdAllSheet) {
					arrMouseMoveObjects.push(new asc_CMM({type: c_oAscMouseMoveType.LockedObject, x: ct.lockAllPosLeft,
						y: ct.lockAllPosTop, userId: ct.userIdAllSheet,
						lockedObjectType: c_oAscMouseMoveLockedObjectType.Sheet}));
				} else {
					// Отправление эвента о залоченности свойств всего листа (только если не удален весь лист)
					if (undefined !== ct.userIdAllProps) {
						arrMouseMoveObjects.push(new asc_CMM({type: c_oAscMouseMoveType.LockedObject, x: ct.lockAllPosLeft,
							y: ct.lockAllPosTop, userId: ct.userIdAllProps,
							lockedObjectType: c_oAscMouseMoveLockedObjectType.TableProperties}));
					}
				}
				// Отправление эвента о наведении на залоченный объект
				if (undefined !== ct.userId) {
					arrMouseMoveObjects.push(new asc_CMM({type: c_oAscMouseMoveType.LockedObject, x: ct.lockRangePosLeft,
						y: ct.lockRangePosTop, userId: ct.userId,
						lockedObjectType: c_oAscMouseMoveLockedObjectType.Range}));
				}

				// Проверяем комментарии ячейки
				if (undefined !== ct.commentIndexes) {
					arrMouseMoveObjects.push(new asc_CMM({type: c_oAscMouseMoveType.Comment,
						x: ct.commentCoords.asc_getLeftPX(), reverseX: ct.commentCoords.asc_getReverseLeftPX(), y: ct.commentCoords.asc_getTopPX(),
						aCommentIndexes: ct.commentIndexes}));
				}
				// Проверяем гиперссылку
				if (ct.target === c_oTargetType.Hyperlink) {
					if (true === ctrlKey) {
						// Мы без нажатия на гиперлинк
					} else {
						ct.cursor = ct.cellCursor.cursor;
					}
					arrMouseMoveObjects.push(new asc_CMM({type: c_oAscMouseMoveType.Hyperlink, x: x, y: y,
						hyperlink: ct.hyperlink}));
				}

				/* Проверяем, может мы на никаком объекте (такая схема оказалась приемлимой
				 * для отдела разработки приложений)
				 */
				if (0 === arrMouseMoveObjects.length) {
					// Отправляем эвент, что мы ни на какой области
					arrMouseMoveObjects.push(new asc_CMM({type: c_oAscMouseMoveType.None}));
				}
				// Отсылаем эвент с объектами
				this.handlers.trigger("asc_onMouseMove", arrMouseMoveObjects);

                if(ct.target === c_oTargetType.MoveRange && ctrlKey && ct.cursor == "move"){
                    ct.cursor = "copy";
                }

				if (canvasElem.style.cursor !== ct.cursor) {
					canvasElem.style.cursor = ct.cursor;
				}
				if (ct.target === c_oTargetType.ColumnHeader || ct.target === c_oTargetType.RowHeader) {
					ws.drawHighlightedHeaders(ct.col, ct.row);
				} else {
					ws.cleanHighlightedHeaders();
				}
			}
			asc_applyFunction(callback, ct);
		};

		WorkbookView.prototype._onResizeElement = function (target, x, y) {
			var arrMouseMoveObjects = [];
			if (target.target === c_oTargetType.ColumnResize) {
				arrMouseMoveObjects.push(this.getWorksheet().drawColumnGuides(target.col, x, y, target.mouseX));
			} else if (target.target === c_oTargetType.RowResize) {
				arrMouseMoveObjects.push(this.getWorksheet().drawRowGuides(target.row, x, y, target.mouseY));
			}

			/* Проверяем, может мы на никаком объекте (такая схема оказалась приемлимой
			 * для отдела разработки приложений)
			 */
			if (0 === arrMouseMoveObjects.length) {
				// Отправляем эвент, что мы ни на какой области
				arrMouseMoveObjects.push(new asc_CMM({type: c_oAscMouseMoveType.None}));
			}
			// Отсылаем эвент с объектами
			this.handlers.trigger("asc_onMouseMove", arrMouseMoveObjects);
		};

		WorkbookView.prototype._onResizeElementDone = function (target, x, y, isResizeModeMove) {
			var ws = this.getWorksheet();
			if (isResizeModeMove) {
                if(ws.objectRender)
                {
                    ws.objectRender.saveSizeDrawingObjects();
                }
				if (target.target === c_oTargetType.ColumnResize) {
					ws.changeColumnWidth(target.col, x, target.mouseX);
				} else if (target.target === c_oTargetType.RowResize) {
					ws.changeRowHeight(target.row, y, target.mouseY);
				}
				// ToDo Нужна ли тут updateSizeDrawingObjects
                if(ws.objectRender)
                {
				    ws.objectRender.updateSizeDrawingObjects(target);
                }
				ws.cellCommentator.updateCommentPosition();
				this._onDocumentPlaceChanged();
			}
			ws.draw();

			// Отсылаем окончание смены размеров (в FF не срабатывало обычное движение)
			this.handlers.trigger("asc_onMouseMove", [new asc_CMM({type: c_oAscMouseMoveType.None})]);
		};

		// Обработка автозаполнения
		WorkbookView.prototype._onChangeFillHandle = function (x, y, callback) {
			var ws = this.getWorksheet();
			var d = ws.changeSelectionFillHandle(x, y);
			asc_applyFunction(callback, d);
		};

		// Обработка окончания автозаполнения
		WorkbookView.prototype._onChangeFillHandleDone = function (x, y, ctrlPress) {
			var ws = this.getWorksheet();
			ws.applyFillHandle(x, y, ctrlPress);
		};

		// Обработка перемещения диапазона
		WorkbookView.prototype._onMoveRangeHandle = function (x, y, callback,ctrlKey) {
			var ws = this.getWorksheet();
			var d = ws.changeSelectionMoveRangeHandle(x, y, ctrlKey);
			asc_applyFunction(callback, d);
		};

		// Обработка окончания перемещения диапазона
		WorkbookView.prototype._onMoveRangeHandleDone = function (ctrlKey) {
			var ws = this.getWorksheet();
			ws.applyMoveRangeHandle(ctrlKey);
		};

		WorkbookView.prototype._onMoveResizeRangeHandle = function (x, y, target, callback) {
			var ws = this.getWorksheet();
			var d = ws.changeSelectionMoveResizeRangeHandle(x, y, target, this.cellEditor);
			asc_applyFunction(callback, d);
		};

		WorkbookView.prototype._onMoveResizeRangeHandleDone = function (target) {
			var ws = this.getWorksheet();
			ws.applyMoveResizeRangeHandle(target);
		};
		
		// Frozen anchor
		WorkbookView.prototype._onMoveFrozenAnchorHandle = function (x, y, target) {
			var ws = this.getWorksheet();
			ws.drawFrozenGuides(x, y, target);
		};
		
		WorkbookView.prototype._onMoveFrozenAnchorHandleDone = function (x, y, target) {
			// Закрепляем область
			var ws = this.getWorksheet();
			ws.applyFrozenAnchor(x, y, target);
		};

		WorkbookView.prototype._onShowAutoComplete = function () {
			var ws = this.getWorksheet();
			var arrValues = ws.getCellAutoCompleteValues(ws.activeRange.startCol, ws.activeRange.startRow);
			this.popUpSelector.show(false, arrValues, this.getWorksheet().getActiveCellCoord());
		};

		WorkbookView.prototype._onAutoFiltersClick = function (idFilter) {
			this.getWorksheet().autoFilters.onAutoFilterClick(idFilter);
		};

		WorkbookView.prototype._onCommentCellClick = function (x, y) {
			var ws = this.getWorksheet();
			var comments = ws.cellCommentator.getCommentsXY(x, y);
			if (comments.length)
				ws.cellCommentator.asc_showComment(comments[0].asc_getId());
		};

		WorkbookView.prototype._onUpdateSelectionName = function () {
			if (this.canUpdateAfterShiftUp) {
				this.canUpdateAfterShiftUp = false;
				var ws = this.getWorksheet();
				this._onSelectionNameChanged(ws.getSelectionName(/*bRangeText*/false));
			}
		};

		WorkbookView.prototype._onStopFormatPainter = function () {
			var ws = this.getWorksheet();
			if (ws.stateFormatPainter)
				ws.formatPainter();
		};

		// Shapes
		WorkbookView.prototype._onGraphicObjectMouseDown = function (e, x, y) {
			var ws = this.getWorksheet();
			ws.objectRender.graphicObjectMouseDown(e, x, y);
		};

		WorkbookView.prototype._onGraphicObjectMouseMove = function (e, x, y) {
			var ws = this.getWorksheet();
			ws.objectRender.graphicObjectMouseMove(e, x, y);
		};

		WorkbookView.prototype._onGraphicObjectMouseUp = function (e, x, y) {
			var ws = this.getWorksheet();
			ws.objectRender.graphicObjectMouseUp(e, x, y);
		};

		WorkbookView.prototype._onGraphicObjectMouseUpEx = function (e, x, y) {
			//var ws = this.getWorksheet();
			//ws.objectRender.coordsManager.calculateCell(x, y);
		};

		WorkbookView.prototype._onGraphicObjectWindowKeyDown = function (e) {
			var ws = this.getWorksheet();
			return ws.objectRender.graphicObjectKeyDown(e);
		};

		WorkbookView.prototype._onGraphicObjectWindowKeyPress = function (e) {
			var ws = this.getWorksheet();
			return ws.objectRender.graphicObjectKeyPress(e);
		};

		WorkbookView.prototype._onGetGraphicsInfo = function (x, y) {
			var ws = this.getWorksheet();
			return ws.objectRender.checkCursorDrawingObject(x, y);
		};

		WorkbookView.prototype._onGetSelectedGraphicObjects = function () {
			var ws = this.getWorksheet();
			return ws.objectRender.getSelectedGraphicObjects();
		};

		WorkbookView.prototype._onUpdateSelectionShape = function (isSelectOnShape) {
			var ws = this.getWorksheet();
			return ws.setSelectionShape(isSelectOnShape);
		};

		// Double click
		WorkbookView.prototype._onMouseDblClick = function (x, y, isHideCursor, callback) {
			var ws = this.getWorksheet();
			var ct = ws.getCursorTypeFromXY(x, y, this.controller.settings.isViewerMode);

			if (ct.target === c_oTargetType.ColumnResize || ct.target === c_oTargetType.RowResize) {
				ct.target === c_oTargetType.ColumnResize ? ws.optimizeColWidth(ct.col) : ws.optimizeRowHeight(ct.row);
				asc_applyFunction(callback);
			} else {
				if (ct.col >=0 && ct.row >= 0)
					this.controller.setStrictClose(!ws._isCellEmptyText(ct.col, ct.row));

				// Для нажатия на колонку/строку/all/frozenMove обрабатывать dblClick не нужно
				if (c_oTargetType.ColumnHeader === ct.target || c_oTargetType.RowHeader === ct.target ||
					c_oTargetType.Corner === ct.target || c_oTargetType.FrozenAnchorH === ct.target ||
					c_oTargetType.FrozenAnchorV === ct.target) {
					asc_applyFunction(callback);
					return;
				}

				if (ws.objectRender.checkCursorDrawingObject(x, y)) {
					asc_applyFunction(callback);
					return;
				}

				// При dbl клике фокус выставляем в зависимости от наличия текста в ячейке
				this._onEditCell (x, y, /*isCoord*/true, /*isFocus*/undefined, /*isClearCell*/undefined,
					/*isHideCursor*/isHideCursor, /*isQuickInput*/false);
			}
		};

		WorkbookView.prototype._onEditCell = function (x, y, isCoord, isFocus, isClearCell, isHideCursor,
													   isQuickInput, callback, event) {
			var t = this;

			// Проверка глобального лока
			if (this.collaborativeEditing.getGlobalLock())
				return;

			var ws = t.getWorksheet();
			var activeCellRange = ws.getActiveCell(x, y, isCoord);
			var arn = ws.activeRange.clone(true);

			var editFunction = function() {
				t.controller.setCellEditMode(true);
				ws.setCellEditMode(true);
				if (!ws.openCellEditor(t.cellEditor, x, y, isCoord, /*fragments*/undefined,
					/*cursorPos*/undefined, isFocus, isClearCell,
					/*isHideCursor*/isHideCursor, /*isQuickInput*/isQuickInput, /*activeRange*/arn)) {
					t.controller.setCellEditMode(false);
					t.controller.setStrictClose(false);
					t.controller.setFormulaEditMode(false);
					ws.setCellEditMode(false);
					ws.setFormulaEditMode(false);
					t.input.disabled = true;
					asc_applyFunction(callback, false);
					return;
				}

				t.input.disabled = false;
				t.handlers.trigger("asc_onEditCell", c_oAscCellEditorState.editStart);
				// Эвент от предыдущего нажатия на символ или на backspace
				if (event) {
					if ("keydown" === event.type)
						t.cellEditor._onWindowKeyDown(event);
					else if ("keypress" === event.type)
						t.cellEditor._onWindowKeyPress(event);
				}

				// Эвент на обновление состояния редактора
				t.cellEditor._updateEditorState();
				asc_applyFunction(callback, true);
			};

			var editLockCallback = function (res) {
				if (!res) {
					t.controller.setCellEditMode(false);
					t.controller.setStrictClose(false);
					t.controller.setFormulaEditMode(false);
					ws.setCellEditMode(false);
					ws.setFormulaEditMode(false);
					t.input.disabled = true;

					// Выключаем lock для редактирования ячейки
					t.collaborativeEditing.onStopEditCell();
					t.cellEditor.close(false);
					t._onWSSelectionChanged(null);
				}
			};

			if (false === this.collaborativeEditing.isCoAuthoringExcellEnable()) {
				// Запрещено совместное редактирование
				editFunction();
				editLockCallback(true);
			} else {
				// Стартуем редактировать ячейку
				this.collaborativeEditing.onStartEditCell();
				if (ws._isLockedCells(activeCellRange, /*subType*/null, editLockCallback))
					editFunction();
			}
		};

		WorkbookView.prototype._onStopCellEditing = function () {
			return this.cellEditor.close(true);
		};

		WorkbookView.prototype._onCloseCellEditor = function () {
			this.controller.setCellEditMode(false);
			this.controller.setStrictClose(false);
			this.controller.setFormulaEditMode(false);
			var ws = this.getWorksheet();
			var isCellEditMode = ws.getCellEditMode();
			ws.setCellEditMode(false);
			ws.setFormulaEditMode(false);
			ws.updateSelection();
			if (isCellEditMode)
				this.handlers.trigger("asc_onEditCell", c_oAscCellEditorState.editEnd);
			// Обновляем состояние Undo/Redo
			History._sendCanUndoRedo();
			// Обновляем состояние информации
			this._onWSSelectionChanged(null);

			// Закрываем подбор формулы
			if (this.popUpSelector) {
				this.popUpSelector.hide();
				this.lastFormulaPos = -1;
				this.lastFormulaName = "";
			}
		};

		WorkbookView.prototype._onEmpty = function () {
			this.getWorksheet().emptySelection(c_oAscCleanOptions.Text);
		};

		WorkbookView.prototype._onAddColumn = function (isNotActive) {
			var res = this.getWorksheet().expandColsOnScroll(isNotActive);
			if (res) {
				this.controller.reinitializeScroll(/*horizontal*/2);
			}
		};

		WorkbookView.prototype._onAddRow = function (isNotActive) {
			var res = this.getWorksheet().expandRowsOnScroll(isNotActive);
			if (res) {  // Добавлены строки
				// после добавления controller.settings.wheelScrollLines
				// ws.scrollVertical() здесь не нужен, scroll.js сам все разрулит
				this.controller.reinitializeScroll(/*vertical*/1);
			}
		};

		WorkbookView.prototype._onShowNextPrevWorksheet = function (direction) {
			// Проверка на неправильное направление
			if (0 === direction)
				return false;
			// Колличество листов
			var countWorksheets = this.model.getWorksheetCount();
			// Покажем следующий лист или предыдущий (если больше нет)
			var i, ws;
			for (i = this.wsActive + direction; (0 > direction) ? (i >= 0) : (i < countWorksheets); i += direction) {
				ws = this.model.getWorksheet(i);
				if (false === ws.getHidden()) {
					this.showWorksheet(i);
					this.handlers.trigger("asc_onActiveSheetChanged", i);
					return true;
				}
			}

			return false;
		};

		WorkbookView.prototype._onSetFontAttributes = function (prop) {
			var val;
			var selectionInfo = this.getWorksheet().getSelectionInfo().asc_getFont();
			switch (prop) {
				case "b":
					val = !(selectionInfo.asc_getBold());
					break;
				case "i":
					val = !(selectionInfo.asc_getItalic());
					break;
				case "u":
					// ToDo для двойного подчеркивания нужно будет немного переделать схему
					val = !(selectionInfo.asc_getUnderline());
					val = val ? Asc.EUnderline.underlineSingle : Asc.EUnderline.underlineNone;
					break;
				case "s":
					val = !(selectionInfo.asc_getStrikeout());
					break;
			}
			return this.setFontAttributes(prop, val);
		};

		WorkbookView.prototype._onSelectColumnsByRange = function () {
			this.getWorksheet()._selectColumnsByRange();
		};

		WorkbookView.prototype._onSelectRowsByRange = function () {
			this.getWorksheet()._selectRowsByRange();
		};

		WorkbookView.prototype._onShowCellEditorCursor = function () {
			var ws = this.getWorksheet();
			// Показываем курсор
			if (ws.getCellEditMode())
				this.cellEditor.showCursor();
		};

		WorkbookView.prototype._onDocumentPlaceChanged = function () {
			if (this.isDocumentPlaceChangedEnabled)
				this.handlers.trigger("asc_onDocumentPlaceChanged");
		};

		WorkbookView.prototype.getTablePictures = function () {
			var autoFilters = new asc.AutoFilters();
			return autoFilters.getTablePictures(this.model, this.fmgrGraphics, this.m_oFont);
		};

		WorkbookView.prototype.getCellStyles = function () {
			var oStylesPainter = new asc_CSP();
			oStylesPainter.generateStylesAll(this.model.CellStyles, this.fmgrGraphics, this.m_oFont, this.stringRender);
			return oStylesPainter;
		};

		WorkbookView.prototype.getWorksheetById = function (id) {
			var wsModel = this.model.getWorksheetById(id);
			if (wsModel)
				return this.getWorksheet(wsModel.getIndex());
			return null;
		};

		/**
		 * @param {Number} [index]
		 * @return {WorksheetView}
		 */
		WorkbookView.prototype.getWorksheet = function (index) {
			var wb = this.model;
			var i  = asc_typeof(index) === "number" && index >= 0 ? index : wb.getActive();
			var ws = this.wsViews[i];
			if (null == ws) {
				ws = this.wsViews[i] = this._createWorksheetView(wb.getWorksheet(i));
				ws._prepareComments();
				ws._prepareDrawingObjects();
			}
			return ws;
		};

		/**
		 *
	 	 * @param index
	 	 * @param [isResized]
		 * @param [bLockDraw]
	 	 * @returns {WorkbookView}
	 	 */
		WorkbookView.prototype.showWorksheet = function (index, isResized, bLockDraw) {
			if (index === this.wsActive) {return this;}

			var isSendInfo = (-1 === this.wsActive) || !isResized;
			// Только если есть активный
			if (-1 !== this.wsActive) {
				var ws = this.getWorksheet();
				// Останавливаем ввод данных в редакторе ввода
				if (ws.getCellEditMode() && !isResized)
					this._onStopCellEditing();
				// Делаем очистку селекта
				ws.cleanSelection();

				this.stopTarget(ws);
			}

			var tmpWorksheet, selectionRange = null;
			if (c_oAscSelectionDialogType.Chart === this.selectionDialogType) {
				// Когда идет выбор диапазона, то должны на закрываемом листе отменить выбор диапазона
				tmpWorksheet = this.getWorksheet();
				selectionRange = tmpWorksheet.activeRange.clone(true);
				tmpWorksheet.setSelectionDialogMode(c_oAscSelectionDialogType.None);
			}

			var wb = this.model;
			if (asc_typeof(index) === "number" && index >= 0) {
				if (index !== wb.getActive()) {wb.setActive(index);}
			} else {
				index = wb.getActive();
			}
			this.wsActive = index;
			this.wsMustDraw = bLockDraw;

			ws = this.getWorksheet(index);
			// Мы делали resize или меняли zoom, но не перерисовывали данный лист (он был не активный)
			if (ws.updateResize && ws.updateZoom)
				ws.changeZoomResize();
			else if (ws.updateResize)
				ws.resize(true);
			else if (ws.updateZoom)
				ws.changeZoom(true);

			if (!bLockDraw)
				ws.draw();

			if (c_oAscSelectionDialogType.Chart === this.selectionDialogType) {
				// Когда идет выбор диапазона, то на позываемом листе должны выставить нужный режим
				ws.setSelectionDialogMode(this.selectionDialogType, selectionRange);
				this.handlers.trigger("asc_onSelectionRangeChanged", ws.getSelectionRangeValue());
			}
            if (!bLockDraw)
            {
                ws.objectRender.controller.updateSelectionState();
                ws.objectRender.controller.updateOverlay();
            }
			if (isSendInfo) {
				this._onSelectionNameChanged(ws.getSelectionName(/*bRangeText*/false));
				this._onWSSelectionChanged(ws.getSelectionInfo());
				this._onSelectionMathInfoChanged(ws.getSelectionMathInfo());
			}
			this.controller.reinitializeScroll();
            if(this.Api.isMobileVersion)
                this.MobileTouchManager.Resize();
			// Zoom теперь на каждом листе одинаковый, не отправляем смену

			// Нужно очистить поиск
			this._cleanFindResults();
			return this;
		};

		/** @param nIndex {Number} массив индексов */
		WorkbookView.prototype.removeWorksheet = function (nIndex) {
			this.stopTarget(null);
			this.wsViews.splice(nIndex, 1);
			// Сбрасываем активный (чтобы не досчитывать после смены)
			this.wsActive = -1;
		};

		// Меняет местами 2 элемента просмотра
		WorkbookView.prototype.replaceWorksheet = function (indexFrom, indexTo) {
			// Только если есть активный
			if (-1 !== this.wsActive) {
				var ws = this.getWorksheet(this.wsActive);
				// Останавливаем ввод данных в редакторе ввода
				if (ws.getCellEditMode())
					this._onStopCellEditing();
				// Делаем очистку селекта
				ws.cleanSelection();

				this.stopTarget(ws);
				this.wsActive = -1;
				// Чтобы поменять, нужно его добавить
				this.getWorksheet(indexTo);
			}
			var movedSheet = this.wsViews.splice(indexFrom,1);
			this.wsViews.splice(indexTo,0,movedSheet[0])
		};

		WorkbookView.prototype.stopTarget = function (ws) {
			if (null === ws && -1 !== this.wsActive)
				ws = this.getWorksheet(this.wsActive);
			if (null !== ws && ws.objectRender && ws.objectRender.drawingDocument)
				ws.objectRender.drawingDocument.TargetEnd();
		};

		// Копирует элемент перед другим элементом
		WorkbookView.prototype.copyWorksheet = function (index, insertBefore) {
			// Только если есть активный
			if (-1 !== this.wsActive) {
				var ws = this.getWorksheet();
				// Останавливаем ввод данных в редакторе ввода
				if (ws.getCellEditMode())
					this._onStopCellEditing();
				// Делаем очистку селекта
				ws.cleanSelection();

				this.stopTarget(ws);
				this.wsActive = -1;
			}

			if (null != insertBefore && insertBefore >= 0 && insertBefore < this.wsViews.length){
				// Помещаем нулевой элемент перед insertBefore
				this.wsViews.splice(insertBefore, 0, null);
			}
		};

		WorkbookView.prototype.updateWorksheetByModel = function () {
			// ToDo Сделал небольшую заглушку с показом листа. Нужно как мне кажется перейти от wsViews на wsViewsId (хранить по id)
			var oldActiveWs;
			if (-1 !== this.wsActive)
				oldActiveWs = this.wsViews[this.wsActive];

			//расставляем ws так как они идут в модели.
			var oNewWsViews = [];
			for (var i in this.wsViews)
			{
				var item = this.wsViews[i];
				if (null != item && null != this.model.getWorksheetById(item.model.getId()))
					oNewWsViews[item.model.getIndex()] = item;
			}
			this.wsViews = oNewWsViews;
			var wsActive = this.model.getActive();

			var newActiveWs = this.wsViews[wsActive];
			if (undefined === newActiveWs || oldActiveWs !== newActiveWs) {
				// Если сменили, то покажем
				this.wsActive = -1;
				this.showWorksheet(undefined, false, true);
			} else
				this.wsActive = wsActive;
		};

		WorkbookView.prototype.spliceWorksheet = function () {
			this.stopTarget(null);
			this.wsViews.splice.apply(this.wsViews, arguments);
			this.wsActive = -1;
		};

		WorkbookView.prototype._canResize = function () {
			var oldWidth = this.canvas.width;
			var oldHeight = this.canvas.height;
			var width = this.element.offsetWidth - (this.Api.isMobileVersion ? 0 : this.defaults.scroll.widthPx);
			var height = this.element.offsetHeight - (this.Api.isMobileVersion ? 0 : this.defaults.scroll.heightPx);
			var styleWidth, styleHeight, isRetina = AscBrowser.isRetina;

			if (isRetina) {
				styleWidth = width;
				styleHeight = height;
				width <<= 1;
				height <<= 1;
			}

			if (oldWidth === width && oldHeight === height)
				return false;

			this.canvas.width = this.canvasOverlay.width = this.canvasGraphic.width =
				this.canvasGraphicOverlay.width = width;
			this.canvas.height = this.canvasOverlay.height = this.canvasGraphic.height =
				this.canvasGraphicOverlay.height = height;
			if (isRetina) {
				this.canvas.style.width = this.canvasOverlay.style.width = this.canvasGraphic.style.width
					= this.canvasGraphicOverlay.style.width = styleWidth + 'px';
				this.canvas.style.height = this.canvasOverlay.style.height = this.canvasGraphic.style.height
					= this.canvasGraphicOverlay.style.height = styleHeight + 'px';
			}

			// При смене ориентации у планшета, сбрасываются флаги у canvas!
			// ToDo перепроверить на новых исходниках, должно поправиться, был баг в отрисовке!!!!!!!!!!!!!
			//this.drawingCtx.initContextSmoothing();
			//this.overlayCtx.initContextSmoothing();
			return true;
		};

		/** @param event {jQuery.Event} */
		WorkbookView.prototype.resize = function (event) {
			if (this._canResize()) {
				var item;
				var activeIndex = this.model.getActive();
				for(var i in this.wsViews) {
					item = this.wsViews[i];
					// Делаем resize (для не активных сменим как только сделаем его активным)
					item.resize(/*isDraw*/i == activeIndex);
				}
				this.showWorksheet(undefined, true);
			} else {
				// ToDo не должно происходить ничего, но нам приходит resize сверху, поэтому проверим отрисовывали ли мы
				if (-1 === this.wsActive || this.wsMustDraw)
					this.showWorksheet(undefined, true);
			}
			this.wsMustDraw = false;
		};

		// Получаем свойство: редактируем мы сейчас или нет
		WorkbookView.prototype.getCellEditMode = function () {
			return this.controller.isCellEditMode;
		};

		WorkbookView.prototype.getZoom = function () {
			return this.drawingCtx.getZoom();
		};

		WorkbookView.prototype.changeZoom = function (factor) {
			if (factor === this.getZoom())
				return;

			this.buffers.main.changeZoom(factor);
			this.buffers.overlay.changeZoom(factor);
			this.buffers.mainGraphic.changeZoom(factor);
			this.buffers.overlayGraphic.changeZoom(factor);
			// Нужно сбросить кэш букв
			var i, length;
			for (i = 0, length = this.fmgrGraphics.length; i < length; ++i)
				this.fmgrGraphics[i].ClearFontsRasterCache();

			var item;
			var activeIndex = this.model.getActive();
			for(i in this.wsViews) {
				item = this.wsViews[i];
				// Меняем zoom (для не активных сменим как только сделаем его активным)
				item.changeZoom(/*isDraw*/i == activeIndex);
				item.objectRender.changeZoom(this.drawingCtx.scaleFactor);
				if (i == activeIndex) {
					item.draw();
					//ToDo item.drawDepCells();
				}
			}

			this.controller.reinitializeScroll();
			this.handlers.trigger("asc_onZoomChanged",this.getZoom());
		};

		WorkbookView.prototype.enableKeyEventsHandler = function (f) {
			this.controller.enableKeyEventsHandler(f);
			if (this.cellEditor)
				this.cellEditor.enableKeyEventsHandler(f);
		};

		// Останавливаем ввод данных в редакторе ввода
		WorkbookView.prototype.closeCellEditor = function () {
			var ws = this.getWorksheet();
			// Останавливаем ввод данных в редакторе ввода
			if (ws.getCellEditMode())
				this._onStopCellEditing();
		};

		WorkbookView.prototype.restoreFocus = function () {
			if (this.cellEditor.hasFocus) {
				this.cellEditor.restoreFocus();
			}
		};

		WorkbookView.prototype._onUpdateCellEditor = function (text, cursorPosition, isFormula, formulaPos, formulaName) {
			// ToDo для ускорения можно завести объект, куда класть результаты поиска по формулам и второй раз не искать.
			var arrResult = [];
			if (isFormula && formulaName) {
				formulaName = formulaName.toUpperCase();
				for (var i = 0; i < this.formulasList.length; ++i) {
					if (0 === this.formulasList[i].indexOf(formulaName))
						arrResult.push(this.formulasList[i]);
				}
			}
			if (0 < arrResult.length) {
				this.popUpSelector.show(true, arrResult, this.getWorksheet().getActiveCellCoord());
				this.lastFormulaPos = formulaPos;
				this.lastFormulaName = formulaName;
			} else {
				this.popUpSelector.hide();
				this.lastFormulaPos = -1;
				this.lastFormulaName = "";
			}
		};
		WorkbookView.prototype._onPopUpSelectorKeyDown = function (event) {
			if (!this.popUpSelector.getVisible())
				return true;
			return this.popUpSelector.onKeyDown(event);
		};
		WorkbookView.prototype._onPopUpSelectorInsert = function (value) {
			if (this.controller.isCellEditMode) {
				if (-1 === this.arrExcludeFormulas.indexOf(value))
					value += '('; // ToDo сделать проверки при добавлении, чтобы не вызывать постоянно окно
				this.cellEditor.replaceText(this.lastFormulaPos, this.lastFormulaName.length, value);
			} else
				this.getWorksheet().setSelectionInfo("value", value, /*onlyActive*/true);
		};

		// Вставка формулы в редактор
		WorkbookView.prototype.insertFormulaInEditor = function (functionName, autoComplete, isDefName) {
			var t = this, ws = this.getWorksheet(), cursorPos

			// Проверяем, открыт ли редактор
			if (ws.getCellEditMode()) {
				// При autoComplete заканчиваем ввод
				if (autoComplete)
					this.cellEditor.close(true);
				else if (false === this.cellEditor.insertFormula (functionName,isDefName)) {
					// Не смогли вставить формулу, закроем редактор, с сохранением текста
					this.cellEditor.close(true);
				}
			} else {
				// Он закрыт
				var cellRange = null;
				// Если нужно сделать автозаполнение формулы, то ищем ячейки)
				if (autoComplete) {
					cellRange = ws.autoCompleteFormula(functionName);
				}
                if(isDefName){
                    functionName = "=" + functionName
                }
                else{
                    if (cellRange) {
                        if (cellRange.notEditCell) {
                            // Мы уже ввели все что нужно, редактор открывать не нужно
                            return;
                        }
                        // Меняем значение ячейки
                        functionName = "=" + functionName + "(" + cellRange.text + ")";
                    } else {
                        // Меняем значение ячейки
                        functionName = "=" + functionName + "()";
                    }
                    // Вычисляем позицию курсора (он должен быть в функции)
                    cursorPos = functionName.length - 1;
                }

				// Проверка глобального лока
				if (this.collaborativeEditing.getGlobalLock())
					return false;

				var arn = ws.activeRange.clone(true);

				var openEditor = function (res) {
					if (res) {
						// Выставляем переменные, что мы редактируем
						t.controller.setCellEditMode(true);
						ws.setCellEditMode(true);

						t.handlers.trigger("asc_onEditCell", c_oAscCellEditorState.editStart);
						// Открываем, с выставлением позиции курсора
						if (!ws.openCellEditorWithText(t.cellEditor, functionName, cursorPos, /*isFocus*/false,
							/*activeRange*/arn)) {
							t.handlers.trigger("asc_onEditCell", c_oAscCellEditorState.editEnd);
							t.controller.setCellEditMode(false);
							t.controller.setStrictClose(false);
							t.controller.setFormulaEditMode(false);
							ws.setCellEditMode(false);
							ws.setFormulaEditMode(false);
						}
					} else {
						t.controller.setCellEditMode(false);
						t.controller.setStrictClose(false);
						t.controller.setFormulaEditMode(false);
						ws.setCellEditMode(false);
						ws.setFormulaEditMode(false);
					}
				};

				var activeCellRange = ws.getActiveCell(0, 0, /*isCoord*/false);

				if (false === this.collaborativeEditing.isCoAuthoringExcellEnable()) {
					// Запрещено совместное редактирование
					openEditor(true);
				} else {
					ws._isLockedCells(activeCellRange, /*subType*/null, openEditor);
				}
			}
		};

		WorkbookView.prototype.copyToClipboard = function () {
			var t = this, ws, v;
			if (!t.controller.isCellEditMode) {
				ws = t.getWorksheet();
				t.clipboard.copyRange(ws.getSelectedRange(), ws);
			} else {
				v = t.cellEditor.copySelection();
				if (v) {t.clipboard.copyCellValue(v);}
			}
		};

		WorkbookView.prototype.copyToClipboardButton = function () {
			var t = this, ws, v;
			if (!t.controller.isCellEditMode) {
				ws = t.getWorksheet();
				return t.clipboard.copyRangeButton(ws.getSelectedRange(), ws);
			} else {
				v = t.cellEditor.copySelection();
				if (v) {return t.clipboard.copyCellValueButton(v);}
				else {return true;}
			}
		};

		WorkbookView.prototype.pasteFromClipboard = function () {
			var t = this;
			if (!t.controller.isCellEditMode) {
				var ws = t.getWorksheet();
				t.clipboard.pasteRange(ws);
			} else {
				// t.clipboard.pasteCellValue(function (text, fonts) {
					// if (fonts.length > 0) {
						// t.handlers.trigger("loadFonts", fonts, function(){t.cellEditor.paste(fragments)});
					// } else {
						// t.cellEditor.pasteText(text);
					// }
				// });
				t.clipboard.pasteAsText(function (text) {
					t.cellEditor.pasteText(text);
				});
			}
		};

		WorkbookView.prototype.pasteFromClipboardButton = function () {
			var t = this;
			if (!t.controller.isCellEditMode) {
				var ws = t.getWorksheet();
				return t.clipboard.pasteRangeButton(ws);
			} else {
				return t.clipboard.pasteAsTextButton(function (text) {
					t.cellEditor.pasteText(text);
				});
			}
		};

		WorkbookView.prototype.cutToClipboard = function () {
			var t = this, ws, v;
			if (!t.controller.isCellEditMode && !window.USER_AGENT_SAFARI_MACOS) {
				ws = t.getWorksheet();

				// Запрещаем копирование диаграмм в iframe
				//if ( t.Api.isChartEditor && ws.objectRender.selectedGraphicObjectsExists() )
				//	return;

				t.clipboard.copyRange(ws.getSelectedRange(), ws, true);
				ws.emptySelection(c_oAscCleanOptions.All);
			} else if(!window.USER_AGENT_SAFARI_MACOS){
				v = t.cellEditor.cutSelection();
				if (v) {t.clipboard.copyCellValue(v);}
			}
		};

		WorkbookView.prototype.bIsEmptyClipboard = function () {
			var t = this, ws, v;
			var result = t.clipboard.bIsEmptyClipboard(t.controller.isCellEditMode);
			
			return result;
		};
		
		WorkbookView.prototype.cutToClipboardButton = function () {
			var t = this, ws, v;
			if (!t.controller.isCellEditMode) {
				ws = t.getWorksheet();
				var result = t.clipboard.copyRangeButton(ws.getSelectedRange(), ws, true);
				if(result)
					ws.emptySelection(c_oAscCleanOptions.All);
				return result;
			} else {
				v = t.cellEditor.cutSelection();
				if (v) {return t.clipboard.copyCellValueButton(v);}
				else {return true;}
			}
		};

		WorkbookView.prototype.undo = function () {
			if (!this.controller.isCellEditMode) {
				History.Undo();
			} else {
				this.cellEditor.undo();
			}
		};

		WorkbookView.prototype.redo = function () {
			if (!this.controller.isCellEditMode) {
				History.Redo();
			} else {
				this.cellEditor.redo();
			}
		};

		WorkbookView.prototype.setFontAttributes = function (prop, val) {
			if (!this.controller.isCellEditMode) {
				this.getWorksheet().setSelectionInfo(prop, val);
			} else {
				this.cellEditor.setTextStyle(prop, val);
			}
		};

		WorkbookView.prototype.changeFontSize = function (prop, val) {
			if (!this.controller.isCellEditMode) {
				this.getWorksheet().setSelectionInfo(prop, val);
			} else {
				this.cellEditor.setTextStyle(prop, val);
			}
		};

		WorkbookView.prototype.emptyCells = function (options) {
			if (!this.controller.isCellEditMode) {
				this.getWorksheet().emptySelection(options);
				this.restoreFocus();
			} else {
				this.cellEditor.empty(options);
			}
		};

		WorkbookView.prototype.setSelectionDialogMode = function (selectionDialogType, selectRange) {
			if (selectionDialogType === this.selectionDialogType)
				return;

			if (c_oAscSelectionDialogType.None === selectionDialogType) {
				this.selectionDialogType = selectionDialogType;
				this.getWorksheet().setSelectionDialogMode(selectionDialogType, selectRange);
				if (this.copyActiveSheet !== this.wsActive) {
					this.showWorksheet(this.copyActiveSheet);
					// Посылаем эвент о смене активного листа
					this.handlers.trigger("asc_onActiveSheetChanged", this.copyActiveSheet);
				}
				this.copyActiveSheet = -1;
				this.input.disabled = false;
			} else {
				this.copyActiveSheet = this.wsActive;

				var index, tmpSelectRange = parserHelp.parse3DRef(selectRange);
				if (tmpSelectRange) {
					if (c_oAscSelectionDialogType.Chart === selectionDialogType) {
						// Получаем sheet по имени
						var ws = this.model.getWorksheetByName(tmpSelectRange.sheet);
						if (!ws || ws.getHidden())
							tmpSelectRange = null;
						else {
							index = ws.getIndex();
							this.showWorksheet(index);
							// Посылаем эвент о смене активного листа
							this.handlers.trigger("asc_onActiveSheetChanged", index);

							tmpSelectRange = tmpSelectRange.range;
						}
					}
					else
						tmpSelectRange = tmpSelectRange.range;
				} else {
					// Это не 3D ссылка
					tmpSelectRange = selectRange;
				}

				this.getWorksheet().setSelectionDialogMode(selectionDialogType, tmpSelectRange);
				// Нужно выставить после, т.к. при смене листа не должны проставлять режим
				this.selectionDialogType = selectionDialogType;
				this.input.disabled = true;
			}
		};

		WorkbookView.prototype._cleanFindResults = function () {
			this.lastFindOptions = null;
			this.lastFindResults = {};
		};

		// Поиск текста в листе
		WorkbookView.prototype.findCellText = function (options) {
			// Для поиска эта переменная не нужна (но она может остаться от replace)
			options.activeRange = null;

			var ws = this.getWorksheet();
			// Останавливаем ввод данных в редакторе ввода
			if (ws.getCellEditMode())
				this._onStopCellEditing();
			var result = ws.findCellText(options);
			if (false === options.scanOnOnlySheet) {
				// Поиск по всей книге
				var key = result ? (result.c1 + "-" + result.r1) : null;
				if (null === key || options.isEqual(this.lastFindOptions)) {
					if (null === key || this.lastFindResults[key]) {
						// Мы уже находили данную ячейку, попробуем на другом листе
						var i, active = this.model.getActive(), start = 0, end = this.model.getWorksheetCount();
						var inc = options.scanForward ? +1 : -1;
						var tmpWs, tmpResult = null;
						for (i = active + inc; i < end && i >= start; i += inc) {
							tmpWs = this.getWorksheet(i);
							tmpResult = tmpWs.findCellText(options);
							if (tmpResult)
								break;
						}
						if (!tmpResult) {
							// Мы дошли до конца или начала (в зависимости от направления, теперь пойдем до активного)
							if (options.scanForward) {
								i = 0; end = active;
							} else {
								i = end - 1;
								start = active + 1;
							}
							inc *= -1;
							for (; i < end && i >= start; i += inc) {
								tmpWs = this.getWorksheet(i);
								tmpResult = tmpWs.findCellText(options);
								if (tmpResult)
									break;
							}
						}

						if (tmpResult) {
							ws = tmpWs;
							result = tmpResult;
							this.showWorksheet(i);
							// Посылаем эвент о смене активного листа
							this.handlers.trigger ("asc_onActiveSheetChanged", i);
							key = result.c1 + "-" + result.r1;
						}

						this.lastFindResults = {};
					}
				}
				if (null !== key) {
					this.lastFindOptions = options.clone();
					this.lastFindResults[key] = true;
				}
			}

			if (result)
				return ws._setActiveCell(result.c1, result.r1);
			this._cleanFindResults();
			return null;
		};

		// Замена текста в листе
		WorkbookView.prototype.replaceCellText = function (options) {
			var ws = this.getWorksheet();
			// Останавливаем ввод данных в редакторе ввода
			if (ws.getCellEditMode())
				this._onStopCellEditing();

			History.Create_NewPoint();
			History.StartTransaction();

			options.clearFindAll();
			if (options.isReplaceAll) {
				// На ReplaceAll ставим медленную операцию
				this.handlers.trigger("asc_onStartAction", c_oAscAsyncActionType.BlockInteraction,
					c_oAscAsyncAction.SlowOperation);
			}

			ws.replaceCellText(options, false, this.fReplaceCallback);
		};
		WorkbookView.prototype._replaceCellTextCallback = function (options) {
			options.updateFindAll();
			if (!options.scanOnOnlySheet && options.isReplaceAll) {
				// Замена на всей книге
				var i = ++options.sheetIndex;
				if (this.model.getActive() === i)
					i = ++options.sheetIndex;

				if (i < this.model.getWorksheetCount()) {
					var ws = this.getWorksheet(i);
					ws.replaceCellText(options, true, this.fReplaceCallback);
					return;
				}
			}

			this.handlers.trigger("asc_onRenameCellTextEnd", options.countFindAll, options.countReplaceAll);

			History.EndTransaction();
			if (options.isReplaceAll) {
				// Заканчиваем медленную операцию
				this.handlers.trigger("asc_onEndAction", c_oAscAsyncActionType.BlockInteraction,
					c_oAscAsyncAction.SlowOperation);
			}
		};

		// Поиск ячейки по ссылке
		WorkbookView.prototype.findCell = function (reference) {
			var ws = this.getWorksheet(), retRange;
			// Останавливаем ввод данных в редакторе ввода
			if (ws.getCellEditMode())
				this._onStopCellEditing();


            return ws.findCell(reference);
        };

        WorkbookView.prototype.getDefinedNames = function (defNameListId) {
            return this.model.getDefinesNamesWB(defNameListId);
        };

        WorkbookView.prototype.setDefinedNames = function (defName) {
            //ToDo проверка defName.ref на знак "=" в начале ссылки. знака нет тогда это либо число либо строка, так делает Excel.

            this.handlers.trigger("asc_onDefName", this.model.setDefinesNames(defName.Name,defName.Ref,defName.Scope ));

        };

        WorkbookView.prototype.checkDefName = function (checkName,scope) {

            return this.model.checkDefName(checkName,scope);

        };

        WorkbookView.prototype.editDefinedNames = function (oldName, newName) {
            //ToDo проверка defName.ref на знак "=" в начале ссылки. знака нет тогда это либо число либо строка, так делает Excel.
            var res = this.model.editDefinesNames(oldName, newName);
            if( oldName ){
                this.handlers.trigger("asc_onEditDefName", oldName, newName);
            }
            else{
                this.handlers.trigger("asc_onDefName", res);
            }

        };

        WorkbookView.prototype.delDefinedNames = function (oldName) {
            //ToDo проверка defName.ref на знак "=" в начале ссылки. знака нет тогда это либо число либо строка, так делает Excel.

            this.handlers.trigger("asc_onDelDefName", this.model.delDefinesNames(oldName));

        };

        WorkbookView.prototype.getDefaultDefinedName = function () {
            //ToDo проверка defName.ref на знак "=" в начале ссылки. знака нет тогда это либо число либо строка, так делает Excel.

            var ws = this.getWorksheet();

            return new Asc.asc_CDefName( "", ws.getSelectionRangeValue(), null );

        };

		// Печать
		WorkbookView.prototype.printSheet = function (pdf_writer, printPagesData) {
			var ws;
			// Закончили печать или нет
			var isEndPrint = false;
			if (null === printPagesData.arrPages || 0 === printPagesData.arrPages.length) {
				// Печать пустой страницы
				ws = this.getWorksheet();
				ws.drawForPrint(pdf_writer, null);
				isEndPrint = true;
			} else {
				var indexWorksheet = -1;
				var indexWorksheetTmp = -1;
				for (var i = 0; i < printPagesData.arrPages.length; ++i) {
					indexWorksheetTmp = printPagesData.arrPages[i].indexWorksheet;
					if (indexWorksheetTmp !== indexWorksheet) {
						ws = this.getWorksheet(indexWorksheetTmp);
						indexWorksheet = indexWorksheetTmp;
					}
					ws.drawForPrint(pdf_writer, printPagesData.arrPages[i]);
				}
				isEndPrint = (i === printPagesData.arrPages.length);
			}

			return isEndPrint;
		};

		WorkbookView.prototype.calcPagesPrint = function (adjustPrint) {
			var ws = null;
			var wb = this.model;
			var activeWs;
			var printPagesData = new asc_CPrintPagesData();
			var printType = adjustPrint.asc_getPrintType();
			var layoutPageType = adjustPrint.asc_getLayoutPageType();
			if (printType == c_oAscPrintType.ActiveSheets) {
				activeWs = wb.getActive();
				ws = this.getWorksheet();
				printPagesData.arrPages = ws.calcPagesPrint (wb.getWorksheet(activeWs).PagePrintOptions, /*printOnlySelection*/false, /*indexWorksheet*/activeWs, layoutPageType);
			} else if (printType == c_oAscPrintType.EntireWorkbook) {
				// Колличество листов
				var countWorksheets = this.model.getWorksheetCount();
				for (var i = 0; i < countWorksheets; ++i) {
					ws = this.getWorksheet(i);
					var arrPages = ws.calcPagesPrint (wb.getWorksheet(i).PagePrintOptions, /*printOnlySelection*/false, /*indexWorksheet*/i, layoutPageType);
					if (null !== arrPages) {
						if (null === printPagesData.arrPages)
							printPagesData.arrPages = [];
						printPagesData.arrPages = printPagesData.arrPages.concat(arrPages);
					}
				}
			} else if (printType == c_oAscPrintType.Selection) {
				activeWs = wb.getActive();
				ws = this.getWorksheet();
				printPagesData.arrPages = ws.calcPagesPrint (wb.getWorksheet(activeWs).PagePrintOptions, /*printOnlySelection*/true, /*indexWorksheet*/activeWs, layoutPageType);
			}

			return printPagesData;
		};

		WorkbookView.prototype._initCommentsToSave  = function () {
			var isFirst = true;
			for (var wsKey in this.wsViews) {
				var wsView = this.wsViews[wsKey];
				var wsModel = wsView.model;
				wsView.cellCommentator.prepareCommentsToSave();
				wsModel.aComments = wsView.cellCommentator.aComments;
				wsModel.aCommentsCoords = wsView.cellCommentator.aCommentCoords;

				if (isFirst) {
					isFirst = false;
					this.cellCommentator.worksheet = wsView;
					this.cellCommentator.overlayCtx = wsView.overlayCtx;
					this.cellCommentator.drawingCtx = wsView.drawingCtx;
					this.cellCommentator.prepareCommentsToSave();
					wsModel.aComments = wsModel.aComments.concat(this.cellCommentator.aComments);
					wsModel.aCommentsCoords = wsModel.aCommentsCoords.concat(this.cellCommentator.aCommentCoords);
				}
			}
		};

		WorkbookView.prototype.reInit = function () {
			var ws = this.getWorksheet();
			ws._initCellsArea(/*fullRecalc*/true);
			ws._updateVisibleColsCount();
			ws._updateVisibleRowsCount();
		};
		WorkbookView.prototype.drawWS = function (){
			this.getWorksheet().draw();
		};
		WorkbookView.prototype.onShowDrawingObjects = function (clearCanvas) {
			var ws = this.getWorksheet();
			ws.objectRender.showDrawingObjects(clearCanvas);
		};

		WorkbookView.prototype.insertHyperlink = function (options) {
			var ws = this.getWorksheet();
			if ( ws.objectRender.selectedGraphicObjectsExists() ) {
				if ( ws.objectRender.controller.canAddHyperlink() )
					ws.objectRender.controller.insertHyperlink(options);
			} else {
				// На всякий случай проверка (вдруг кто собирается вызвать...)
				this.closeCellEditor();
				ws.setSelectionInfo("hyperlink", options);
				this.restoreFocus();
			}
		};
		WorkbookView.prototype.removeHyperlink = function () {
			var ws = this.getWorksheet();
			if (ws.objectRender.selectedGraphicObjectsExists())
				ws.objectRender.controller.removeHyperlink();
			else {
				ws.setSelectionInfo("rh");
			}
		};

		WorkbookView.prototype.setDocumentPlaceChangedEnabled = function (val) {
			this.isDocumentPlaceChangedEnabled = val;
		};

		/*
		 * @param {c_oAscRenderingModeType} mode Режим отрисовки
		 * @param {Boolean} isInit инициализация или нет
		 */
		WorkbookView.prototype.setFontRenderingMode = function (mode, isInit) {
			var ws;
			if (mode !== this.fontRenderingMode) {
				this.fontRenderingMode = mode;
				if (c_oAscFontRenderingModeType.noHinting === mode)
					this._setHintsProps(false, false);
				else if (c_oAscFontRenderingModeType.hinting === mode)
					this._setHintsProps(true, false);
				else if (c_oAscFontRenderingModeType.hintingAndSubpixeling === mode)
					this._setHintsProps(true, true);

				if (!isInit) {
					ws = this.getWorksheet();
					ws.draw();
					this.cellEditor.setFontRenderingMode(mode);
				}
			}
		};

		WorkbookView.prototype.initFormulasList = function () {
			this.formulasList = [];
			var oFormulaList = cFormulaFunctionLocalized ? cFormulaFunctionLocalized : cFormulaFunction;
			for (var f in oFormulaList)
				this.formulasList.push(f);
		};

		WorkbookView.prototype._setHintsProps = function (bIsHinting, bIsSubpixHinting) {
			var manager, hintProps;
			for (var i = 0, length = this.fmgrGraphics.length; i < length; ++i) {
				manager = this.fmgrGraphics[i];
				hintProps = manager.m_oLibrary.tt_hint_props;
				if (!hintProps)
					continue;

				// Последний без хинтования (только для измерения)
				if (i === length - 1) {
					bIsHinting = bIsSubpixHinting = false;
				}

				if (bIsHinting && bIsSubpixHinting) {
					hintProps.TT_USE_BYTECODE_INTERPRETER = true;
					hintProps.TT_CONFIG_OPTION_SUBPIXEL_HINTING = true;

					manager.LOAD_MODE = 40968;
				} else if (bIsHinting) {
					hintProps.TT_USE_BYTECODE_INTERPRETER = true;
					hintProps.TT_CONFIG_OPTION_SUBPIXEL_HINTING = false;

					manager.LOAD_MODE = 40968;
				} else {
					hintProps.TT_USE_BYTECODE_INTERPRETER = true;
					hintProps.TT_CONFIG_OPTION_SUBPIXEL_HINTING = false;

					manager.LOAD_MODE = 40970;
				}

				manager.ClearFontsRasterCache();
			}
		};

		WorkbookView.prototype._calcMaxDigitWidth = function () {
			// set default worksheet header font for calculations
			this.buffers.main.setFont(this.defaultFont);
			// Измеряем в pt
			this.stringRender.measureString(
				"0123456789", {wrapText: false, shrinkToFit: false, isMerged: false, textAlign: /*khaLeft*/"left"});

			var ppiX = 96; // Мерить только с 96
			var ptConvToPx = asc_getcvt(1/*pt*/, 0/*px*/, ppiX);

			// Максимальная ширина в Pt
			var maxWidthInPt = this.stringRender.getWidestCharWidth();
			// Переводим в px и приводим к целому (int)
			this.maxDigitWidth = asc_round(maxWidthInPt * ptConvToPx);
			// Проверка для Calibri 11 должно быть this.maxDigitWidth = 7

			if (!this.maxDigitWidth) {throw "Error: can't measure text string";}

			// Padding рассчитывается исходя из maxDigitWidth (http://social.msdn.microsoft.com/Forums/en-US/9a6a9785-66ad-4b6b-bb9f-74429381bd72/margin-padding-in-cell-excel?forum=os_binaryfile)
			this.defaults.worksheetView.cells.padding = Math.max(asc.ceil(this.maxDigitWidth / 4), 2);
			this.defaults.worksheetView.cells.paddingPlusBorder = 2 * this.defaults.worksheetView.cells.padding + 1;
		};

		/*
		 * Export
		 * -----------------------------------------------------------------------------
		 */
		window["Asc"].WorkbookView = WorkbookView;
	}
)(jQuery, window);
