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

		var asc = window["Asc"] ? window["Asc"] : (window["Asc"] = {});
		var asc_applyFunction = asc.applyFunction;

		/**
		 * Desktop event controller for WorkbookView
		 * -----------------------------------------------------------------------------
		 * @constructor
		 * @memberOf Asc
		 */
		function asc_CEventsController() {
			if ( !(this instanceof asc_CEventsController) ) {
				return new asc_CEventsController();
			}

			//----- declaration -----
			this.defaults = {
				vscrollStep: 10,
				hscrollStep: 10,
				scrollTimeout: 20,
				showArrows: true,//показывать или нет стрелки у скролла
				scrollBackgroundColor:"#DDDDDD",//цвет фона скролла
				scrollerColor:"#EDEDED",//цвет ползунка скрола
				isViewerMode: false,
				wheelScrollLines: 3
			};

			this.view     = undefined;
			this.widget   = undefined;
			this.element  = undefined;
			this.handlers = undefined;
			this.settings = $.extend(true, {}, this.defaults);
			this.vsb	= undefined;
			this.vsbHSt	= undefined;
			this.vsbApi	= undefined;
			this.hsb	= undefined;
			this.hsbHSt	= undefined;
			this.hsbApi = undefined;
			this.resizeTimerId = undefined;
			this.scrollTimerId = undefined;
			this.moveRangeTimerId = undefined;
			this.moveResizeRangeTimerId = undefined;
			this.fillHandleModeTimerId = undefined;
			this.enableKeyEvents = true;
			this.isSelectMode = false;
			this.hasCursor = false;
			this.hasFocus = false;
			this.isCellEditMode = undefined;
			this.skipKeyPress = undefined;
			this.strictClose = false;
			this.lastKeyCode = undefined;
			this.targetInfo = undefined;
			this.isResizeMode = false;
			this.isResizeModeMove = false;
			
			// Режим автозаполнения
			this.isFillHandleMode = false;
			this.isMoveRangeMode = false;
			this.isMoveResizeRange = false;
			this.isMoveResizeChartsRange = false;
			// Режим select-а для диалогов
			this.isSelectionDialogMode = false;
			// Режим формулы
			this.isFormulaEditMode = false;
			
			// Обработчик кликов для граф.объектов
			this.clickCounter = new ClickCounter();
			this.isLocked = false;

			// Был ли DblClick обработан в onMouseDown эвенте
			this.isDblClickInMouseDown = false;
			// Нужно ли обрабатывать эвент браузера dblClick
			this.isDoBrowserDblClick = false;
			// Последние координаты, при MouseDown (для IE)
			this.mouseDownLastCord = null;
			//-----------------------

            this.vsbApiLockMouse = false;
            this.hsbApiLockMouse = false;

            return this;
		}


		/**
		 * @param {WorkbookView} view
		 * @param {Element} widgetElem
		 * @param {Element} canvasElem
		 * @param {Object} handlers  Event handlers (resize, reinitializeScroll, scrollY, scrollX, changeSelection, updateWorksheet, editCell, stopCellEditing)
		 * @param {Object} settings
		 */
		asc_CEventsController.prototype.init = function (view, widgetElem, canvasElem, handlers, settings) {
			var self = this;

			this.view     = view;
			this.widget   = widgetElem;
			this.element  = canvasElem;
			this.handlers = new asc.asc_CHandlersList(handlers);
			this.settings = $.extend(true, {}, this.settings, settings);

			this._createScrollBars();

			// initialize events
			if (window.addEventListener) {
				window.addEventListener("resize"	, function () {self._onWindowResize.apply(self, arguments);}				, false);
				window.addEventListener("keydown"	, function () {return self._onWindowKeyDown.apply(self, arguments);}		, false);
				window.addEventListener("keypress"	, function () {return self._onWindowKeyPress.apply(self, arguments);}		, false);
				window.addEventListener("keyup"		, function () {return self._onWindowKeyUp.apply(self, arguments);}			, false);
				window.addEventListener("mousemove"	, function () {return self._onWindowMouseMove.apply(self, arguments);}		, false);
				window.addEventListener("mouseup"	, function () {return self._onWindowMouseUp.apply(self, arguments);}		, false);
				window.addEventListener("mouseleave", function () {return self._onWindowMouseLeaveOut.apply(self, arguments);}	, false);
				window.addEventListener("mouseout"	, function () {return self._onWindowMouseLeaveOut.apply(self, arguments);}	, false);
			}

			// prevent changing mouse cursor when 'mousedown' is occurred
			if (this.element.onselectstart) {
				this.element.onselectstart = function () {return false;};
			}

			if (this.element.addEventListener) {
				this.element.addEventListener("mousedown"	, function () {return self._onMouseDown.apply(self, arguments);}		, false);
				this.element.addEventListener("mouseup"		, function () {return self._onMouseUp.apply(self, arguments);}			, false);
				this.element.addEventListener("mousemove"	, function () {return self._onMouseMove.apply(self, arguments);}		, false);
				this.element.addEventListener("mouseleave"	, function () {return self._onMouseLeave.apply(self, arguments);}		, false);
				this.element.addEventListener("dblclick"	, function () {return self._onMouseDblClick.apply(self, arguments);}	, false);

				this.element.addEventListener("mousewheel"	, function () {return self._onMouseWheel.apply(self, arguments);}		, false);
				// for Mozilla Firefox (можно делать проверку на window.MouseScrollEvent || window.WheelEvent для FF)
				this.element.addEventListener("DOMMouseScroll", function () {return self._onMouseWheel.apply(self, arguments);}		, false);

				this.element.addEventListener("touchstart"	, function (e) {self._onMouseDown(e.touches[0]); return false;}			, false);
				this.element.addEventListener("touchmove"	, function (e) {self._onMouseMove(e.touches[0]); return false;}			, false);
				this.element.addEventListener("touchend"	, function (e) {self._onMouseUp(e.changedTouches[0]); return false;}	, false);
			}

			// Курсор для графических объектов. Определяем mousedown и mouseup для выделения текста.
			var oShapeCursor = document.getElementById("id_target_cursor");
			if (null != oShapeCursor && oShapeCursor.addEventListener) {
				oShapeCursor.addEventListener("mousedown"	, function () {return self._onMouseDown.apply(self, arguments);}, false);
				oShapeCursor.addEventListener("mouseup"		, function () {return self._onMouseUp.apply(self, arguments);}	, false);
				oShapeCursor.addEventListener("mousemove"	, function () {return self._onMouseMove.apply(self, arguments);}, false);
			}

			return this;
		};

		asc_CEventsController.prototype.destroy = function () {
			return this;
		};

		/** @param flag {Boolean} */
		asc_CEventsController.prototype.enableKeyEventsHandler = function (flag) {
			this.enableKeyEvents = !!flag;
		};

		/** @param flag {Boolean} */
		asc_CEventsController.prototype.setCellEditMode = function (flag) {
			this.isCellEditMode = !!flag;
		};

		/** @param isViewerMode {Boolean} */
		asc_CEventsController.prototype.setViewerMode = function (isViewerMode) {
			this.settings.isViewerMode = !!isViewerMode;
		};

		/** @return isViewerMode {Boolean} */
		asc_CEventsController.prototype.getViewerMode = function () {
			return this.settings.isViewerMode;
		};

		asc_CEventsController.prototype.setFocus = function (hasFocus) {
			this.hasFocus = !!hasFocus;
		};

		asc_CEventsController.prototype.setStrictClose = function (enabled) {
			this.strictClose = !!enabled;
		};

		/** @param isFormulaEditMode {Boolean} */
		asc_CEventsController.prototype.setFormulaEditMode = function (isFormulaEditMode) {
			this.isFormulaEditMode = !!isFormulaEditMode;
		};

		/**
		 *
		 * @param {Boolean} isSelectionDialogMode
		 */
		asc_CEventsController.prototype.setSelectionDialogMode = function (isSelectionDialogMode) {
			this.isSelectionDialogMode = isSelectionDialogMode;
		};

		/** @param whichSB {Number}  Scroll bar to reinit (1=vertical, 2=horizontal) */
		asc_CEventsController.prototype.reinitializeScroll = function (whichSB) {
		    if (window["NATIVE_EDITOR_ENJINE"])
		        return;

			var self = this,
			    opt = this.settings,
			    ws = self.view.getWorksheet(),
			    isVert = !whichSB || whichSB === 1,
			    isHoriz = !whichSB || whichSB === 2;

			if (isVert || isHoriz) {
				this.handlers.trigger("reinitializeScroll", whichSB, function (vSize, hSize) {
					if (isVert) {
						vSize = self.vsb.offsetHeight + Math.max(vSize * opt.vscrollStep, 1);
						self.vsbHSt.height = vSize + "px";
						self.vsbApi.Reinit(opt, opt.vscrollStep * ws.getFirstVisibleRow(/*allowPane*/true));
					}
					if (isHoriz) {
						hSize = self.hsb.offsetWidth + Math.max(hSize * opt.hscrollStep, 1);
						self.hsbHSt.width = hSize + "px";
						self.hsbApi.Reinit(opt, opt.vscrollStep * ws.getFirstVisibleCol(/*allowPane*/true));
					}
				});
			}

			return this;
		};

		/** @param delta {Number} */
		asc_CEventsController.prototype.scrollVertical = function (delta, event) {
			if( event && event.preventDefault )
				event.preventDefault();
			this.vsbApi.scrollByY(this.settings.vscrollStep * delta);
			return true;
		};

		/** @param delta {Number} */
		asc_CEventsController.prototype.scrollHorizontal = function (delta, event) {
			if( event && event.preventDefault )
				event.preventDefault();
			this.hsbApi.scrollByX(this.settings.hscrollStep * delta);
			return true;
		};

		// Будем делать dblClick как в Excel
		asc_CEventsController.prototype.doMouseDblClick = function (event, isHideCursor) {
			var t = this;

			// Для формулы не нужно выходить из редактирования ячейки
			if (t.settings.isViewerMode || t.isFormulaEditMode || t.isSelectionDialogMode) {return true;}

			if( this.targetInfo && ( this.targetInfo.target == "moveResizeRange" || this.targetInfo.target == "moveRange" || this.targetInfo.target == "fillhandle" ) ){
				return true;
			}

			if (t.isCellEditMode) {if (!t.handlers.trigger("stopCellEditing")) {return true;}}

			var coord = t._getCoordinates(event);
			var graphicsInfo = t.handlers.trigger("getGraphicsInfo", coord.x, coord.y);
			if ( asc["editor"].isStartAddShape || (graphicsInfo && graphicsInfo.isGraphicObject) )
				return;

			setTimeout(function () {
				var coord = t._getCoordinates(event);
				t.handlers.trigger("mouseDblClick", coord.x, coord.y, isHideCursor, function () {
					// Мы изменяли размеры колонки/строки, не редактируем ячейку. Обновим состояние курсора
					t.handlers.trigger("updateWorksheet", t.element, coord.x, coord.y, event.ctrlKey,
						function (info) {t.targetInfo = info;});
				});
			}, 100);

			return true;
		};

		// Будем показывать курсор у редактора ячейки (только для dblClick)
		asc_CEventsController.prototype.showCellEditorCursor = function () {
			if (this.isCellEditMode) {
				if (this.isDoBrowserDblClick) {
					this.isDoBrowserDblClick = false;
					this.handlers.trigger("showCellEditorCursor");
				}
			}
		};

		asc_CEventsController.prototype._createScrollBars = function () {
			var self = this, opt = this.settings;

			// vertical scroll bar
			this.vsb = document.createElement('div');
			this.vsb.id = "ws-v-scrollbar";
			this.vsb.innerHTML = '<div id="ws-v-scroll-helper"></div>';
			this.widget.appendChild(this.vsb);
			this.vsbHSt = document.getElementById("ws-v-scroll-helper").style;

			if (!this.vsbApi) {
				this.vsbApi = new ScrollObject(this.vsb.id, opt);
				this.vsbApi.bind("scrollvertical", function(evt) {
					self.handlers.trigger("scrollY", evt.scrollPositionY / opt.vscrollStep);
				});
				this.vsbApi.bind("scrollVEnd", function(evt) {
					self.handlers.trigger("addRow",true);
				});
				this.vsbApi.onLockMouse = function(evt){
                    self.vsbApiLockMouse = true;
				};
				this.vsbApi.offLockMouse = function(){
                    self.vsbApiLockMouse = false;
				};
			}

			// horizontal scroll bar
			this.hsb = document.createElement('div');
			this.hsb.id = "ws-h-scrollbar";
			this.hsb.innerHTML = '<div id="ws-h-scroll-helper"></div>';
			this.widget.appendChild(this.hsb);
			this.hsbHSt = document.getElementById("ws-h-scroll-helper").style;

			if (!this.hsbApi) {
				this.hsbApi = new ScrollObject(this.hsb.id, $.extend(true, {}, opt, {wheelScrollLines: 1}));
				this.hsbApi.bind("scrollhorizontal",function(evt) {
					self.handlers.trigger("scrollX", evt.scrollPositionX / opt.hscrollStep);
				});
				this.hsbApi.bind("scrollHEnd",function(evt) {
						self.handlers.trigger("addColumn",true);
					});
				this.hsbApi.onLockMouse = function(){
                    self.hsbApiLockMouse = true;
				};
				this.hsbApi.offLockMouse = function(){
                    self.hsbApiLockMouse = false;
				};
			}

			// right bottom corner
			var corner = document.createElement('div');
			corner.id = "ws-scrollbar-corner";
			this.widget.appendChild(corner);
		};

		/**
		 * @param event {jQuery.Event}
		 * @param callback {Function}
		 */
		asc_CEventsController.prototype._changeSelection = function (event, isSelectMode, callback) {
			var t = this;
			var coord = this._getCoordinates(event);

			if (t.isFormulaEditMode) {
				// для определения рэнджа под курсором и активизации его для WorksheetView
				if (false === t.handlers.trigger("canEnterCellRange")) {
					if (!t.handlers.trigger("stopCellEditing")) {return;}
				}
			}

			this.handlers.trigger("changeSelection", /*isStartPoint*/false, coord.x, coord.y,
					/*isCoord*/true, /*isSelectMode*/isSelectMode,
					function (d) {
						if (d.deltaX) {t.scrollHorizontal(d.deltaX);}
						if (d.deltaY) {t.scrollVertical(d.deltaY);}

						if (t.isFormulaEditMode) {
							t.handlers.trigger("enterCellRange");
						} else if (t.isCellEditMode) {
							if (!t.handlers.trigger("stopCellEditing")) {return;}
						}

						asc_applyFunction(callback);
					});
		};

		/** @param event {jQuery.Event} */
		asc_CEventsController.prototype._changeSelection2 = function (event) {
			var t = this;

			var fn = function () {
				t._changeSelection2(event);
			};

			var callback = function () {
				if (t.isSelectMode && !t.hasCursor) {
					t.scrollTimerId = window.setTimeout(fn, t.settings.scrollTimeout);
				}
			};

			window.clearTimeout(t.scrollTimerId);
			t.scrollTimerId = window.setTimeout(function () {
				if (t.isSelectMode && !t.hasCursor) {
					t._changeSelection(event, /*isSelectMode*/true, callback);
				}
			}, 0);
		};

		/** @param event {jQuery.Event} */
		asc_CEventsController.prototype._moveRangeHandle2 = function (event) {
			var t = this;

			var fn = function () {
				t._moveRangeHandle2(event);
			};

			var callback = function () {
				if (t.isMoveRangeMode && !t.hasCursor) {
					t.moveRangeTimerId  = window.setTimeout(fn, t.settings.scrollTimeout);
				}
			};

			window.clearTimeout(t.moveRangeTimerId);
			t.moveRangeTimerId = window.setTimeout(function () {
				if (t.isMoveRangeMode && !t.hasCursor) {
					t._moveRangeHandle(event, callback);
				}
			}, 0);
		};

		/** @param event {jQuery.Event} */
		asc_CEventsController.prototype._moveResizeRangeHandle2 = function (event) {
			var t = this;

			var fn = function () {
				t._moveResizeRangeHandle2(event);
			};

			var callback = function () {
				if (t.isMoveResizeRange && !t.hasCursor) {
					t.moveResizeRangeTimerId  = window.setTimeout(fn, t.settings.scrollTimeout);
				}
			};

			window.clearTimeout(t.moveResizeRangeTimerId);
			t.moveResizeRangeTimerId = window.setTimeout(function () {
				if (t.isMoveResizeRange && !t.hasCursor) {
					t._moveResizeRangeHandle(event, t.targetInfo, callback);
				}
			}, 0);
		};

		// Окончание выделения
		asc_CEventsController.prototype._changeSelectionDone = function (event) {
			var coord = this._getCoordinates(event);
			if (false === event.ctrlKey) {
				coord.x = -1;
				coord.y = -1;
			}
			this.handlers.trigger("changeSelectionDone", coord.x, coord.y);
		};

		/** @param event {jQuery.Event} */
		asc_CEventsController.prototype._resizeElement = function (event) {
			var coord = this._getCoordinates(event);
			this.handlers.trigger("resizeElement", this.targetInfo, coord.x, coord.y);
		};

		/** @param event {jQuery.Event} */
		asc_CEventsController.prototype._resizeElementDone = function (event) {
			var coord = this._getCoordinates(event);
			this.handlers.trigger("resizeElementDone", this.targetInfo, coord.x, coord.y, this.isResizeModeMove);
			this.isResizeModeMove = false;
		};

		/** @param event {jQuery.Event} */
		asc_CEventsController.prototype._changeFillHandle = function (event, callback) {
			var t = this;
			// Обновляемся в режиме автозаполнения
			var coord = this._getCoordinates(event);
			this.handlers.trigger("changeFillHandle", coord.x, coord.y,
				function (d) {
					if (!d) return;
					if (d.deltaX) {
						t.scrollHorizontal(d.deltaX);
					}
					if (d.deltaY) {
						t.scrollVertical(d.deltaY);
					}
					asc_applyFunction(callback);
				});
		};

		asc_CEventsController.prototype._changeFillHandle2 = function (event){
			var t = this;

			var fn = function () {
				t._changeFillHandle2(event);
			};

			var callback = function () {
				if (t.isFillHandleMode && !t.hasCursor) {
					t.fillHandleModeTimerId  = window.setTimeout(fn, t.settings.scrollTimeout);
				}
			};

			window.clearTimeout(t.fillHandleModeTimerId);
			t.fillHandleModeTimerId = window.setTimeout(function () {
				if (t.isFillHandleMode && !t.hasCursor) {
					t._changeFillHandle(event, callback);
				}
			}, 0);
		};

		/** @param event {jQuery.Event} */
		asc_CEventsController.prototype._changeFillHandleDone = function (event) {
			// Закончили автозаполнение, пересчитаем
			var coord = this._getCoordinates(event);
			var ctrlPress = event.ctrlKey;
			this.handlers.trigger("changeFillHandleDone", coord.x, coord.y, ctrlPress);
		};

		/** @param event {jQuery.Event} */
		asc_CEventsController.prototype._moveRangeHandle = function (event, callback) {
			var t = this;
			// Обновляемся в режиме перемещения диапазона
			var coord = this._getCoordinates(event);
			this.handlers.trigger("moveRangeHandle", coord.x, coord.y,
				function (d) {
					if (!d) return;
					if (d.deltaX) {
						t.scrollHorizontal(d.deltaX);
					}
					if (d.deltaY) {
						t.scrollVertical(d.deltaY);
					}
					asc_applyFunction(callback);
				},
				event.ctrlKey);
		};

		/** @param event {jQuery.Event} */
		asc_CEventsController.prototype._moveResizeRangeHandle = function (event, target, callback) {
			var t = this;
			// Обновляемся в режиме перемещения диапазона
			var coord = this._getCoordinates(event);
			this.handlers.trigger("moveResizeRangeHandle", coord.x, coord.y, target,
				function (d) {
					if (!d) return;
					if (d.deltaX) {
						t.scrollHorizontal(d.deltaX);
					}
					if (d.deltaY) {
						t.scrollVertical(d.deltaY);
					}
					asc_applyFunction(callback);
				});
		};

		asc_CEventsController.prototype._autoFiltersClick = function (event) {
			var t = this;
			var coord = t._getCoordinates(event);
			this.handlers.trigger("autoFiltersClick", coord.x, coord.y);
		};

		asc_CEventsController.prototype._commentCellClick = function (event) {
			var t = this;
			var coord = t._getCoordinates(event);
			this.handlers.trigger("commentCellClick", coord.x, coord.y);
		};

		/** @param event {jQuery.Event} */
		asc_CEventsController.prototype._moveRangeHandleDone = function (event) {
			// Закончили перемещение диапазона, пересчитаем
			this.handlers.trigger("moveRangeHandleDone",event.ctrlKey);
		};

		asc_CEventsController.prototype._moveResizeRangeHandleDone = function (event, target) {
			// Закончили перемещение диапазона, пересчитаем
			this.handlers.trigger("moveResizeRangeHandleDone", target);
		};

		/** @param event {jQuery.Event} */
		asc_CEventsController.prototype._onWindowResize = function (event) {
			var self = this;
			window.clearTimeout(this.resizeTimerId);
			this.resizeTimerId = window.setTimeout(function () {self.handlers.trigger("resize", event);}, 150);
		};

		/** @param event {jQuery.Event} */
		asc_CEventsController.prototype._onWindowKeyDown = function (event) {
			var t = this, dc = 0, dr = 0, isViewerMode = t.settings.isViewerMode;

			t.__retval = true;

			//for Mac OS
			if ( event.metaKey )
				event.ctrlKey = true;

			function stop(immediate) {
				event.stopPropagation();
				immediate ? event.stopImmediatePropagation() : true;
				event.preventDefault();
				t.__retval = false;
			}

			// для исправления Bug 15902 - Alt забирает фокус из приложения
			// этот код должен выполняться самым первым
			if (event.which === 18) {
				t.lastKeyCode = event.which;
			}

			var graphicObjects = t.handlers.trigger("getSelectedGraphicObjects");
			if ( graphicObjects.length && t.enableKeyEvents ) {
				if (t.handlers.trigger("graphicObjectWindowKeyDown", event))
					return true;
			}

			// Двигаемся ли мы в выделенной области
			var selectionActivePointChanged = false;

			// Для таких браузеров, которые не присылают отжатие левой кнопки мыши для двойного клика, при выходе из
			// окна редактора и отпускания кнопки, будем отрабатывать выход из окна (только Chrome присылает эвент MouseUp даже при выходе из браузера)
			this.showCellEditorCursor();

			while (t.isCellEditMode && !t.hasFocus || !t.enableKeyEvents || t.isSelectMode || t.isFillHandleMode || t.isMoveRangeMode || t.isMoveResizeRange) {

				if (t.isCellEditMode && !t.strictClose && event.which >= 37 && event.which <= 40) {
					// обрабатываем нажатие клавиш со стрелками, если редактор открыт не по F2
					break;
				}

				// Почему-то очень хочется обрабатывать лишние условия в нашем коде, вместо обработки наверху...
				if (!t.enableKeyEvents && event.ctrlKey && (80 === event.which/* || 83 === event.which*/)) {
					// Только если отключены эвенты и нажаты Ctrl+S или Ctrl+P мы их обработаем
					break;
				}

				return true;
			}

			t.skipKeyPress = true;

			switch (event.which) {

				case 113: // F2
					if (isViewerMode || t.isCellEditMode || t.isSelectionDialogMode) {return true;}
					if (AscBrowser.isOpera) {stop();}
					// Выставляем блокировку на выход из редактора по клавишам-стрелкам
					t.strictClose = true;
					// При F2 выставляем фокус в редакторе
					t.handlers.trigger("editCell", 0, 0, /*isCoord*/false, /*isFocus*/true, /*isClearCell*/false, /*isHideCursor*/undefined);
					return t.__retval;

				case 8: // backspace
					if (isViewerMode || t.isCellEditMode || t.isSelectionDialogMode) {return true;}

					// При backspace фокус не в редакторе (стираем содержимое)
					t.handlers.trigger("editCell", 0, 0, /*isCoord*/false, /*isFocus*/false, /*isClearCell*/true,
						/*isHideCursor*/undefined, /*callback*/undefined, event);
					return true;

				case 46: // Del
					if (isViewerMode || t.isCellEditMode || t.isSelectionDialogMode) {return true;}
					// Удаляем содержимое
					t.handlers.trigger("emptyCell");
					return true;

				case 9: // tab
					if (t.isCellEditMode) {return true;}
					// Отключим стандартную обработку браузера нажатия tab
					stop();

					// Особый случай (возможно движение в выделенной области)
					selectionActivePointChanged = true;
					if (event.shiftKey){
						dc = -1;                 // (shift + tab) - движение по ячейкам влево на 1 столбец
						event.shiftKey = false;  // Сбросим shift, потому что мы не выделяем
					} else {
						dc = +1;                 // (tab) - движение по ячейкам вправо на 1 столбец
					}
					break;

				case 13:  // "enter"
					if (t.isCellEditMode) {return true;}
					// Особый случай (возможно движение в выделенной области)
					selectionActivePointChanged = true;
					if (event.shiftKey) {
						dr = -1;                 // (shift + enter) - движение по ячейкам наверх на 1 строку
						event.shiftKey = false;  // Сбросим shift, потому что мы не выделяем
					} else {
						dr = +1;                 // (enter) - движение по ячейкам вниз на 1 строку
					}
					break;

				case 27: // Esc
					// (https://bugzilla.mozilla.org/show_bug.cgi?id=614304) - баг в Mozilla: Esc abort XMLHttpRequest and WebSocket
					// http://bugzserver/show_bug.cgi?id=14631 - наш баг на редактор
					// Перехватим Esc и отключим дефалтовую обработку
					stop();
					return t.__retval;

				case 144: //Num Lock
				case 145: //Scroll Lock
					if (AscBrowser.isOpera) {stop();}
					return t.__retval;

				case 32: // Spacebar
					if (t.isCellEditMode) {return true;}
					// Обработать как обычный текст
					if (!event.ctrlKey && !event.shiftKey) {
						t.skipKeyPress = false;
						return true;
					}
					// Отключим стандартную обработку браузера нажатия
					// Ctrl+Shift+Spacebar, Ctrl+Spacebar, Shift+Spacebar
					stop();
					// Обработать как спец селект
					if (event.ctrlKey && event.shiftKey) {
						t.handlers.trigger("changeSelection", /*isStartPoint*/true, 0,
							0, /*isCoord*/true, /*isSelectMode*/false);
					} else if (event.ctrlKey) {
						t.handlers.trigger("selectColumnsByRange");
					} else {
						t.handlers.trigger("selectRowsByRange");
					}
					return t.__retval;

				case 33: // PageUp
					// Отключим стандартную обработку браузера нажатия PageUp
					stop();
					if (event.ctrlKey) {
						// Перемещение по листам справа налево
						// В chrome не работает (т.к. там своя обработка на некоторые нажатия вместе с Ctrl
						t.handlers.trigger("showNextPrevWorksheet", -1);
						return true;
					} else {
						event.altKey ? dc = -0.5 : dr = -0.5;
					}
					break;

				case 34: // PageDown
					// Отключим стандартную обработку браузера нажатия PageDown
					stop();
					if (event.ctrlKey) {
						// Перемещение по листам слева направо
						// В chrome не работает (т.к. там своя обработка на некоторые нажатия вместе с Ctrl
						t.handlers.trigger("showNextPrevWorksheet", +1);
						return true;
					} else {
						event.altKey ? dc = +0.5 : dr = +0.5;
					}
					break;

				case 37: // left
					stop();                          // Отключим стандартную обработку браузера нажатия left
					dc = event.ctrlKey ? -1.5 : -1;  // Движение стрелками (влево-вправо, вверх-вниз)
					break;

				case 38: // up
					stop();                          // Отключим стандартную обработку браузера нажатия up
					dr = event.ctrlKey ? -1.5 : -1;  // Движение стрелками (влево-вправо, вверх-вниз)
					break;

				case 39: // right
					stop();                          // Отключим стандартную обработку браузера нажатия right
					dc = event.ctrlKey ? +1.5 : +1;  // Движение стрелками (влево-вправо, вверх-вниз)
					break;

				case 40: // down
					stop();                          // Отключим стандартную обработку браузера нажатия down
					dr = event.ctrlKey ? +1.5 : +1;  // Движение стрелками (влево-вправо, вверх-вниз)
					break;

				case 36: // home
					stop();                          // Отключим стандартную обработку браузера нажатия home
					dc = -2.5;
					if (event.ctrlKey) {dr = -2.5;}
					break;

				case 35: // end
					stop();                          // Отключим стандартную обработку браузера нажатия end
					dc = 2.5;
					if (event.ctrlKey) {
						dr = 2.5;
					}
					break;

				case 53: // make strikethrough	Ctrl + 5
				case 66: // make bold			Ctrl + b
				case 73: // make italic			Ctrl + i
				//case 83: // save				Ctrl + s
				case 85: // make underline		Ctrl + u
				case 86: // paste				Ctrl + v
				case 88: // cut					Ctrl + x
				case 89: // redo				Ctrl + y
				case 90: // undo				Ctrl + z
					if (isViewerMode || t.isSelectionDialogMode) {stop(); return false;}

				case 65: // select all      Ctrl + a
				case 67: // copy            Ctrl + c
					if (t.isCellEditMode) { return true; }

				case 80: // print           Ctrl + p
					if (t.isCellEditMode) { stop(); return false; }

					if (!event.ctrlKey) { t.skipKeyPress = false; return true; }

					// Вызовем обработчик
					if (!t.__handlers) {
						t.__handlers = {
							53: function () {stop(); t.handlers.trigger("setFontAttributes", "s");},
							65: function () {stop(); t.handlers.trigger("changeSelection", /*isStartPoint*/true,
								0, 0, /*isCoord*/true, /*isSelectMode*/false);},
							66: function () {stop(); t.handlers.trigger("setFontAttributes", "b");},
							73: function () {stop(); t.handlers.trigger("setFontAttributes", "i");},
							85: function () {stop(); t.handlers.trigger("setFontAttributes", "u");},
							80: function () {stop(); t.handlers.trigger("print");},
							//83: function () {stop(); t.handlers.trigger("save");},
							67: function () {t.handlers.trigger("copy");},
							86: function () {
								if (!window.GlobalPasteFlag)
								{
									if (!window.USER_AGENT_SAFARI_MACOS)
									{
										window.GlobalPasteFlag = true;
										t.handlers.trigger("paste");
									}
									else
									{
										if (0 === window.GlobalPasteFlagCounter)
										{
											SafariIntervalFocus();
											window.GlobalPasteFlag = true;
											t.handlers.trigger("paste");
										}
									}
								}
								else
								{
									if (!window.USER_AGENT_SAFARI_MACOS)
										stop();
								}
							},
							88: function () {t.handlers.trigger("cut");},
							89: function () {stop(); t.handlers.trigger("redo");},
							90: function () {stop(); t.handlers.trigger("undo");}
						};
					}
					t.__handlers[event.which]();
					return t.__retval;

				case 61:  // Firefox, Opera (+/=)
				case 187: // +/=
					if (isViewerMode || t.isCellEditMode || t.isSelectionDialogMode) {return true;}

					if (event.altKey) {
						t.handlers.trigger("addFunction", /*functionName*/"SUM", /*autoComplet*/true);
					} else {
						t.skipKeyPress = false;
					}
					return true;

				default:
					// При зажатом Ctrl не вводим символ
					if (!event.ctrlKey) {t.skipKeyPress = false;}
					return true;

			} // end of switch

			if ((dc !== 0 || dr !== 0) && false === t.handlers.trigger("isGlobalLockEditCell")) {

				// Проверка на движение в выделенной области
				if (selectionActivePointChanged) {
					t.handlers.trigger("selectionActivePointChanged", dc, dr,
						function (d) {
							if (d.deltaX) {t.scrollHorizontal(d.deltaX);}
							if (d.deltaY) {t.scrollVertical(d.deltaY);}
						});
				} else {
					if (this.isCellEditMode && !this.isFormulaEditMode) {
						if (!t.handlers.trigger("stopCellEditing")) {return true;}
					}

					if (t.isFormulaEditMode) {
						// для определения рэнджа под курсором и активизации его для WorksheetView
						if (false === t.handlers.trigger("canEnterCellRange")) {
							if (!t.handlers.trigger("stopCellEditing")) {return true;}
						}
					}

					t.handlers.trigger("changeSelection", /*isStartPoint*/!event.shiftKey, dc, dr,
							/*isCoord*/false, /*isSelectMode*/false,
							function (d) {
								if (d.deltaX) {t.scrollHorizontal(d.deltaX);}
								if (d.deltaY) {t.scrollVertical(d.deltaY);}

								if (t.isFormulaEditMode) {
									t.handlers.trigger("enterCellRange");
								} else if (t.isCellEditMode) {
									if (!t.handlers.trigger("stopCellEditing")) {return true;}
								}
							});
				}
			}

			return t.__retval;
		};

		/** @param event {jQuery.Event} */
		asc_CEventsController.prototype._onWindowKeyPress = function (event) {
			var t = this;

			// Нельзя при отключенных эвентах возвращать false (это касается и ViewerMode)
			if (!t.enableKeyEvents) {return true;}

			// не вводим текст в режиме просмотра
			// если в FF возвращать false, то отменяется дальнейшая обработка серии keydown -> keypress -> keyup
			// и тогда у нас не будут обрабатываться ctrl+c и т.п. события
			if (t.settings.isViewerMode || t.isSelectionDialogMode) {return true;}

			var graphicObjects = t.handlers.trigger("getSelectedGraphicObjects");
			if ( graphicObjects.length && t.enableKeyEvents ) {
				if (!( (event.ctrlKey || event.metaKey) && (event.which == 99 || event.which == 118) )) {		// Mozilla Firefox Fix #20080
					if (t.handlers.trigger("graphicObjectWindowKeyPress", event))
						return true;
				}
			}

			// Для таких браузеров, которые не присылают отжатие левой кнопки мыши для двойного клика, при выходе из
			// окна редактора и отпускания кнопки, будем отрабатывать выход из окна (только Chrome присылает эвент MouseUp даже при выходе из браузера)
			this.showCellEditorCursor();

			if (t.isCellEditMode && !t.hasFocus || !t.enableKeyEvents || t.isSelectMode) {
				return true;
			}

			if (t.skipKeyPress || event.which < 32) {
				t.skipKeyPress = true;
				return true;
			}

			if (!t.isCellEditMode) {
				// При нажатии символа, фокус не ставим
				// Очищаем содержимое ячейки
				t.handlers.trigger("editCell", 0, 0, /*isCoord*/false, /*isFocus*/false, /*isClearCell*/true,
					/*isHideCursor*/undefined, /*callback*/undefined, event);
			}
			return true;
		};

		/** @param event {jQuery.Event} */
		asc_CEventsController.prototype._onWindowKeyUp = function (event) {
			var t = this;

			// для исправления Bug 15902 - Alt забирает фокус из приложения
			if (t.lastKeyCode === 18 && event.which === 18) {
				return false;
			}
			// При отпускании shift нужно переслать информацию о выделении
			if (16 === event.which) {
				this.handlers.trigger("updateSelectionName");
			}

			return true;
		};

		/** @param event {jQuery.Event} */
		asc_CEventsController.prototype._onWindowMouseMove = function (event) {
			if (this.isSelectMode && !this.hasCursor) {this._changeSelection2(event);}
			if (this.isResizeMode && !this.hasCursor) {
				this.isResizeModeMove = true;
				this._resizeElement(event);
			}
            if( this.hsbApiLockMouse )
                this.hsbApi.mouseDown ? this.hsbApi.evt_mousemove.call(this.hsbApi,event) : false;

            else if( this.vsbApiLockMouse )
                this.vsbApi.mouseDown ? this.vsbApi.evt_mousemove.call(this.vsbApi,event) : false;

			return true;
		};

		/** @param event {jQuery.Event} */
		asc_CEventsController.prototype._onWindowMouseUp = function (event) {

//            this.vsbApi.evt_mouseup(event);
//            this.hsbApi.evt_mouseup(event);
			// Shapes
			var coord = this._getCoordinates(event);
			if ( asc["editor"].isStartAddShape ) {
				event.fromWindow = true;
				this.handlers.trigger("graphicObjectMouseUp", event, coord.x, coord.y);
				this._changeSelectionDone(event);
				return true;
			}

			if (this.isSelectMode) {
				this.isSelectMode = false;
				this._changeSelectionDone(event);
			}

			if (this.isResizeMode) {
				this.isResizeMode = false;
				this._resizeElementDone(event);
			}

			// Режим автозаполнения
			if (this.isFillHandleMode) {
				// Закончили автозаполнение
				this.isFillHandleMode = false;
				this._changeFillHandleDone(event);
			}

			// Режим перемещения диапазона
			if (this.isMoveRangeMode) {
				// Закончили перемещение диапазона
				this.isMoveRangeMode = false;
				this._moveRangeHandleDone(event);
			}

			if (this.isMoveResizeRange) {
				this.isMoveResizeRange = false;
				this.isMoveResizeChartsRange = false;
				this.handlers.trigger("moveResizeRangeHandleDone", this.targetInfo);
			}

			// Мы можем dblClick и не отработать, если вышли из области и отпустили кнопку мыши, нужно отработать
			this.showCellEditorCursor();

            if( this.hsbApiLockMouse )
                this.hsbApi.mouseDown ? this.hsbApi.evt_mouseup.call(this.hsbApi,event) : false;

            else if( this.vsbApiLockMouse )
                this.vsbApi.mouseDown ? this.vsbApi.evt_mouseup.call(this.vsbApi,event) : false;

			return true;
		};

		/** @param event {jQuery.Event} */
		asc_CEventsController.prototype._onWindowMouseUpExternal = function (x, y) {
			if (this.isSelectMode) {
				this.isSelectMode = false;
				this.handlers.trigger("changeSelectionDone", x, y);
			}

			if (this.isResizeMode) {
				this.isResizeMode = false;
				this.handlers.trigger("resizeElementDone", this.targetInfo, x, y, this.isResizeModeMove);
				this.isResizeModeMove = false;
			}

			// Режим автозаполнения
			if (this.isFillHandleMode) {
				// Закончили автозаполнение
				this.isFillHandleMode = false;
				this.handlers.trigger("changeFillHandleDone", x, y, /*ctrlPress*/false);
			}

			// Режим перемещения диапазона
			if (this.isMoveRangeMode) {
				// Закончили перемещение диапазона
				this.isMoveRangeMode = false;
				this.handlers.trigger("moveRangeHandleDone");
			}

			if (this.isMoveResizeRange) {
				this.isMoveResizeRange = false;
				this.isMoveResizeChartsRange = false;
				this.handlers.trigger("moveResizeRangeHandleDone", this.targetInfo);
			}

			// Мы можем dblClick и не отработать, если вышли из области и отпустили кнопку мыши, нужно отработать
			this.showCellEditorCursor();

			return true;
		};

		/** @param event {jQuery.Event} */
		asc_CEventsController.prototype._onWindowMouseLeaveOut = function (event) {
			// Когда обрабатывать нечего - выходим
			if (!this.isDoBrowserDblClick)
				return true;

			var relatedTarget = event.relatedTarget || event.fromElement;
			// Если мы двигаемся по редактору ячейки, то ничего не снимаем
			if (relatedTarget && ("ce-canvas-outer" === relatedTarget.id ||
				"ce-canvas" === relatedTarget.id || "ce-canvas-overlay" === relatedTarget.id ||
				"ce-cursor" === relatedTarget.id || "ws-canvas-overlay" === relatedTarget.id))
				return true;

			// Для таких браузеров, которые не присылают отжатие левой кнопки мыши для двойного клика, при выходе из
			// окна редактора и отпускания кнопки, будем отрабатывать выход из окна (только Chrome присылает эвент MouseUp даже при выходе из браузера)
			this.showCellEditorCursor();
			return true;
		};

		/** @param event {jQuery.Event} */
		asc_CEventsController.prototype._onMouseDown = function (event) {
			var t = this;
			var coord = t._getCoordinates(event);
			event.isLocked = true;
			t.isLocked = true;

			if (t.handlers.trigger("isGlobalLockEditCell"))
				return;

			if (!this.enableKeyEvents) {
				t.handlers.trigger("canvasClick");
			}

			// Shapes
			var graphicsInfo = t.handlers.trigger("getGraphicsInfo", coord.x, coord.y);
			if ( asc["editor"].isStartAddShape || (graphicsInfo && graphicsInfo.isGraphicObject) ) {
				// При выборе диапазона не нужно выделять автофигуру
				if (t.isSelectionDialogMode)
					return;
				//for Mac OS
				if (event.metaKey)
					event.ctrlKey = true;

				this.clickCounter.mouseDownEvent(coord.x, coord.y, event.button);
				event.ClickCount = this.clickCounter.clickCount;

				if ( (event.ClickCount == 2) && asc["editor"].isStartAddShape )
					this.isDblClickInMouseDown = true;

				asc["editor"].isStartAddShape = true;
				t.handlers.trigger("graphicObjectMouseDown", event, coord.x, coord.y);

				if ( t.isCellEditMode )
					t.handlers.trigger("stopCellEditing");

				if (asc["editor"].isStartAddShape) {
					// SelectionChanged
					t.handlers.trigger("updateSelectionShape", /*isSelectOnShape*/true);
					return;
				}
			}

			if (2 === event.detail) {
				// Это означает, что это MouseDown для dblClick эвента (его обрабатывать не нужно)
				// Порядок эвентов для dblClick - http://javascript.ru/tutorial/events/mouse#dvoynoy-levyy-klik

				// Проверка для IE, т.к. он присылает DblClick при сдвиге мыши...
				if (this.mouseDownLastCord && coord.x === this.mouseDownLastCord.x && coord.y === this.mouseDownLastCord.y && 0 === event.button) {
					// Выставляем, что мы уже сделали dblClick (иначе вдруг браузер не поддерживает свойство detail)
					this.isDblClickInMouseDown = true;
					// Нам нужно обработать эвент браузера о dblClick (если мы редактируем ячейку, то покажем курсор, если нет - то просто ничего не произойдет)
					this.isDoBrowserDblClick = true;
					this.doMouseDblClick(event, /*isHideCursor*/false);
					// Обнуляем координаты
					this.mouseDownLastCord = null;
					return;
				}
			}
			if (event.preventDefault)
				event.preventDefault();
			else
				event.returnValue = false;

			// Запоминаем координаты нажатия
			this.mouseDownLastCord = coord;

			t.hasFocus = true;
			if (!t.isCellEditMode) {
				if (event.shiftKey) {
					t.isSelectMode = true;
					t._changeSelection(event, /*isSelectMode*/true);
					return;
				}
				if (t.targetInfo){
					if (t.targetInfo.target === "colresize" || t.targetInfo.target === "rowresize") {
						t.isResizeMode = true;
						t._resizeElement(event);
						return;
					} else if (t.targetInfo && t.targetInfo.target === "fillhandle" && false === this.settings.isViewerMode){
						// В режиме автозаполнения
						this.isFillHandleMode = true;
						t._changeFillHandle(event);
						return;
					} else if ( t.targetInfo && t.targetInfo.target === "moveRange" && false === this.settings.isViewerMode ) {
						// В режиме перемещения диапазона
						this.isMoveRangeMode = true;
						t._moveRangeHandle(event);
						return;
					}
					else if (t.targetInfo && (t.targetInfo.target === "aFilterObject")) {
						  t._autoFiltersClick(event);
						  return;
					}
					else if (t.targetInfo && (undefined !== t.targetInfo.commentIndexes) && (false === this.settings.isViewerMode)) {
						t._commentCellClick(event);
					}
					else if ( t.targetInfo && t.targetInfo.target === "moveResizeRange" && false === this.settings.isViewerMode ){
						this.isMoveResizeRange = true;
						this.isMoveResizeChartsRange = true;
						t._moveResizeRangeHandle(event, t.targetInfo);
						return;
					}
				}
			} else {
				if (!t.isFormulaEditMode) {
					if (!t.handlers.trigger("stopCellEditing")) {return;}
				} else {
					if (event.shiftKey) {
						t.isSelectMode = true;
						t._changeSelection(event, /*isSelectMode*/true);
						return;
					} else {
						if (t.isFormulaEditMode) {
							// !!! в зависимости от цели делаем разные действия - либо селектим область либо мувим существующий диапазон
							if ( t.targetInfo && t.targetInfo.target === "moveResizeRange" && false === this.settings.isViewerMode ){
								this.isMoveResizeRange = true;
								t._moveResizeRangeHandle(event, t.targetInfo);
								return;
							}
							// для определения рэнджа под курсором и активизации его для WorksheetView
							else if (false === t.handlers.trigger("canEnterCellRange")) {
								if (!t.handlers.trigger("stopCellEditing")) {return;}
							}
						}
						t.isSelectMode = true;
						t.handlers.trigger("changeSelection", /*isStartPoint*/true, coord.x, coord.y,
								/*isCoord*/true, /*isSelectMode*/true,
								function (d) {
									if (d.deltaX) {t.scrollHorizontal(d.deltaX);}
									if (d.deltaY) {t.scrollVertical(d.deltaY);}

									if (t.isFormulaEditMode) {
										t.handlers.trigger("enterCellRange");
									} else if (t.isCellEditMode) {
										if (!t.handlers.trigger("stopCellEditing")) {return;}
									}
								});
						return;
					}
				}
			}

			// Если нажали правую кнопку мыши, то сменим выделение только если мы не в выделенной области
			if (2 === event.button) {
				t.handlers.trigger("changeSelectionRightClick", coord.x, coord.y);
			} else {
				if (t.targetInfo && t.targetInfo.target === "fillhandle"  && false === this.settings.isViewerMode){
					// В режиме автозаполнения
					t.isFillHandleMode = true;
					t._changeFillHandle(event);
					return;
				} else {
					t.isSelectMode = true;
					t.handlers.trigger("changeSelection", /*isStartPoint*/true, coord.x,
						coord.y, /*isCoord*/true, /*isSelectMode*/true);
					return;
				}
			}
		};

		/** @param event {jQuery.Event} */
		asc_CEventsController.prototype._onMouseUp = function (event) {

			// Shapes
			var coord = this._getCoordinates(event);
			event.isLocked = false;
			this.isLocked = false;

			this.handlers.trigger("graphicObjectMouseUpEx", event, coord.x, coord.y);
			if ( asc["editor"].isStartAddShape ) {

				//for Mac OS
				if ( event.metaKey )
					event.ctrlKey = true;

				//console.log("_onMouseUp: " + this.clickCounter.clickCount);

				event.ClickCount = this.clickCounter.clickCount;
				this.handlers.trigger("graphicObjectMouseUp", event, coord.x, coord.y);
				this._changeSelectionDone(event);
				return true;
			}

			if (this.isSelectMode) {
				this.isSelectMode = false;
				this._changeSelectionDone(event);
			}

			if (this.isResizeMode) {
				this.isResizeMode = false;
				this._resizeElementDone(event);
			}
			// Режим автозаполнения
			if (this.isFillHandleMode) {
				// Закончили автозаполнение
				this.isFillHandleMode = false;
				this._changeFillHandleDone(event);
			}
			// Режим перемещения диапазона
			if (this.isMoveRangeMode) {
				this.isMoveRangeMode = false;
				this._moveRangeHandleDone(event);
			}

			if (this.isMoveResizeRange) {
				this.isMoveResizeRange = false;
				this.isMoveResizeChartsRange = false;
				this._moveResizeRangeHandleDone(event, this.targetInfo);
				return true;
			}

			// Мы можем dblClick и не отработать, если вышли из области и отпустили кнопку мыши, нужно отработать
			this.showCellEditorCursor();
		};

		/** @param event {jQuery.Event} */
		asc_CEventsController.prototype._onMouseMove = function (event) {
			var t = this;
			var coord = t._getCoordinates(event);
			event.isLocked = t.isLocked;

			t.hasCursor = true;

			// Shapes
			var graphicsInfo = t.handlers.trigger("getGraphicsInfo", coord.x, coord.y);
			if ( graphicsInfo && graphicsInfo.isGraphicObject )
				this.clickCounter.mouseMoveEvent(coord.x, coord.y);

			if (t.isSelectMode) {
				t._changeSelection(event, /*isSelectMode*/true);
				return true;
			}

			if (t.isResizeMode) {
				t._resizeElement(event);
				this.isResizeModeMove = true;
				return true;
			}

			// Режим автозаполнения
			if (t.isFillHandleMode) {
				t._changeFillHandle(event);
				return true;
			}

			// Режим перемещения диапазона
			if (t.isMoveRangeMode) {
                if(event.ctrlKey){
                    event.currentTarget.style.cursor = "copy";
                }
                else{
                    event.currentTarget.style.cursor = "move";
                }
				t._moveRangeHandle(event);
				return true;
			}

			if (t.isMoveResizeRange) {
				t._moveResizeRangeHandle(event, t.targetInfo);
				return true;
			}

			if (asc["editor"].isStartAddShape || graphicsInfo) {
				t.handlers.trigger("graphicObjectMouseMove", event, coord.x, coord.y);
				t.handlers.trigger("updateWorksheet", t.element, coord.x, coord.y, event.ctrlKey, function(info){t.targetInfo = info;});
				return true;
			}

			t.handlers.trigger("updateWorksheet", t.element, coord.x, coord.y, event.ctrlKey, function(info){t.targetInfo = info;});
			return true;
		};

		/** @param event {jQuery.Event} */
		asc_CEventsController.prototype._onMouseLeave = function (event) {
			var t = this;
			this.hasCursor = false;
			if (!this.isSelectMode && !this.isResizeMode && !this.isMoveResizeRange) {
				this.targetInfo = undefined;
				this.handlers.trigger("updateWorksheet", this.element);
			}
			if (this.isMoveRangeMode) {
				t.moveRangeTimerId = window.setTimeout(function(){t._moveRangeHandle2(event)},0);
			}
			if (this.isMoveResizeRange) {
				t.moveResizeRangeTimerId = window.setTimeout(function(){t._moveResizeRangeHandle2(event)},0);
			}
			if (this.isFillHandleMode) {
				t.fillHandleModeTimerId = window.setTimeout(function(){t._changeFillHandle2(event)},0);
			}
			return true;
		};

		/**
		 * @param event
		 */
		asc_CEventsController.prototype._onMouseWheel = function (event) {
			if (this.isFillHandleMode || this.isMoveRangeMode || this.isMoveResizeChartsRange || this.isMoveResizeRange) {
				return true;
			}
			var delta = 0;
			if (undefined !== event.wheelDelta && 0 !== event.wheelDelta) {
				delta = -1 * event.wheelDelta / 120;
			} else if (undefined != event.detail && 0 !== event.detail) {
				// FF
				delta = event.detail / 3;
			}


			var self = this;
			delta *= event.shiftKey ? 1 : this.settings.wheelScrollLines;
			this.handlers.trigger("updateWorksheet", this.element, /*x*/undefined, /*y*/undefined, /*ctrlKey*/undefined,
					function () {
						event.shiftKey ? self.scrollHorizontal(delta, event) : self.scrollVertical(delta, event);
						self._onMouseMove(event);
					});
			return true;
		};

		/** @param event {jQuery.Event} */
		asc_CEventsController.prototype._onMouseDblClick = function (event) {
			if (this.handlers.trigger("isGlobalLockEditCell"))
				return false;

			// Браузер не поддерживает свойство detail (будем делать по координатам)
			if (false === this.isDblClickInMouseDown)
				return this.doMouseDblClick(event, /*isHideCursor*/false);

			this.isDblClickInMouseDown = false;

			// Нужно отработать показ курсора, если dblClick был обработан в MouseDown
			this.showCellEditorCursor();
			return true;
		};

		/** @param event {jQuery.Event} */
		asc_CEventsController.prototype._getCoordinates = function (event) {
			var offs = $(this.element).offset();
			var x = event.pageX - offs.left;
			var y = event.pageY - offs.top;
			return {x: x, y: y};
		};


		/*
		 * Export
		 * -----------------------------------------------------------------------------
		 */
		window["Asc"]["asc_CEventsController"] = window["Asc"].asc_CEventsController = asc_CEventsController;


	}
)(jQuery, window);