/* ***** BEGIN LICENSE BLOCK ***** * Distributed under the BSD license: * * Copyright (c) 2010, Ajax.org B.V. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Ajax.org B.V. nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * ***** END LICENSE BLOCK ***** */ define(function(require, exports, module) { "use strict"; var event = require("../lib/event"); var useragent = require("../lib/useragent"); var DefaultHandlers = require("./default_handlers").DefaultHandlers; var DefaultGutterHandler = require("./default_gutter_handler").GutterHandler; var MouseEvent = require("./mouse_event").MouseEvent; var DragdropHandler = require("./dragdrop_handler").DragdropHandler; var config = require("../config"); var MouseHandler = function(editor) { var _self = this; this.editor = editor; new DefaultHandlers(this); new DefaultGutterHandler(this); new DragdropHandler(this); var focusEditor = function(e) { // because we have to call event.preventDefault() any window on ie and iframes // on other browsers do not get focus, so we have to call window.focus() here var windowBlurred = !document.hasFocus || !document.hasFocus() || !editor.isFocused() && document.activeElement == (editor.textInput && editor.textInput.getElement()); if (windowBlurred) window.focus(); editor.focus(); }; var mouseTarget = editor.renderer.getMouseEventTarget(); event.addListener(mouseTarget, "click", this.onMouseEvent.bind(this, "click")); event.addListener(mouseTarget, "mousemove", this.onMouseMove.bind(this, "mousemove")); event.addMultiMouseDownListener([ mouseTarget, editor.renderer.scrollBarV && editor.renderer.scrollBarV.inner, editor.renderer.scrollBarH && editor.renderer.scrollBarH.inner, editor.textInput && editor.textInput.getElement() ].filter(Boolean), [400, 300, 250], this, "onMouseEvent"); event.addMouseWheelListener(editor.container, this.onMouseWheel.bind(this, "mousewheel")); event.addTouchMoveListener(editor.container, this.onTouchMove.bind(this, "touchmove")); var gutterEl = editor.renderer.$gutter; event.addListener(gutterEl, "mousedown", this.onMouseEvent.bind(this, "guttermousedown")); event.addListener(gutterEl, "click", this.onMouseEvent.bind(this, "gutterclick")); event.addListener(gutterEl, "dblclick", this.onMouseEvent.bind(this, "gutterdblclick")); event.addListener(gutterEl, "mousemove", this.onMouseEvent.bind(this, "guttermousemove")); event.addListener(mouseTarget, "mousedown", focusEditor); event.addListener(gutterEl, "mousedown", focusEditor); if (useragent.isIE && editor.renderer.scrollBarV) { event.addListener(editor.renderer.scrollBarV.element, "mousedown", focusEditor); event.addListener(editor.renderer.scrollBarH.element, "mousedown", focusEditor); } editor.on("mousemove", function(e){ if (_self.state || _self.$dragDelay || !_self.$dragEnabled) return; var character = editor.renderer.screenToTextCoordinates(e.x, e.y); var range = editor.session.selection.getRange(); var renderer = editor.renderer; if (!range.isEmpty() && range.insideStart(character.row, character.column)) { renderer.setCursorStyle("default"); } else { renderer.setCursorStyle(""); } }); }; (function() { this.onMouseEvent = function(name, e) { this.editor._emit(name, new MouseEvent(e, this.editor)); }; this.onMouseMove = function(name, e) { // optimization, because mousemove doesn't have a default handler. var listeners = this.editor._eventRegistry && this.editor._eventRegistry.mousemove; if (!listeners || !listeners.length) return; this.editor._emit(name, new MouseEvent(e, this.editor)); }; this.onMouseWheel = function(name, e) { var mouseEvent = new MouseEvent(e, this.editor); mouseEvent.speed = this.$scrollSpeed * 2; mouseEvent.wheelX = e.wheelX; mouseEvent.wheelY = e.wheelY; this.editor._emit(name, mouseEvent); }; this.onTouchMove = function (name, e) { var mouseEvent = new MouseEvent(e, this.editor); mouseEvent.speed = 1;//this.$scrollSpeed * 2; mouseEvent.wheelX = e.wheelX; mouseEvent.wheelY = e.wheelY; this.editor._emit(name, mouseEvent); }; this.setState = function(state) { this.state = state; }; this.captureMouse = function(ev, mouseMoveHandler) { this.x = ev.x; this.y = ev.y; this.isMousePressed = true; // do not move textarea during selection var editor = this.editor; var renderer = this.editor.renderer; if (renderer.$keepTextAreaAtCursor) renderer.$keepTextAreaAtCursor = null; var self = this; var onMouseMove = function(e) { if (!e) return; // if editor is loaded inside iframe, and mouseup event is outside // we won't recieve it, so we cancel on first mousemove without button if (useragent.isWebKit && !e.which && self.releaseMouse) return self.releaseMouse(); self.x = e.clientX; self.y = e.clientY; mouseMoveHandler && mouseMoveHandler(e); self.mouseEvent = new MouseEvent(e, self.editor); self.$mouseMoved = true; }; var onCaptureEnd = function(e) { editor.off("beforeEndOperation", onOperationEnd); clearInterval(timerId); onCaptureInterval(); self[self.state + "End"] && self[self.state + "End"](e); self.state = ""; if (renderer.$keepTextAreaAtCursor == null) { renderer.$keepTextAreaAtCursor = true; renderer.$moveTextAreaToCursor(); } self.isMousePressed = false; self.$onCaptureMouseMove = self.releaseMouse = null; e && self.onMouseEvent("mouseup", e); editor.endOperation(); }; var onCaptureInterval = function() { self[self.state] && self[self.state](); self.$mouseMoved = false; }; if (useragent.isOldIE && ev.domEvent.type == "dblclick") { return setTimeout(function() {onCaptureEnd(ev);}); } var onOperationEnd = function(e) { if (!self.releaseMouse) return; // some touchpads fire mouseup event after a slight delay, // which can cause problems if user presses a keyboard shortcut quickly if (editor.curOp.command.name && editor.curOp.selectionChanged) { self[self.state + "End"] && self[self.state + "End"](); self.state = ""; self.releaseMouse(); } }; editor.on("beforeEndOperation", onOperationEnd); editor.startOperation({command: {name: "mouse"}}); self.$onCaptureMouseMove = onMouseMove; self.releaseMouse = event.capture(this.editor.container, onMouseMove, onCaptureEnd); var timerId = setInterval(onCaptureInterval, 20); }; this.releaseMouse = null; this.cancelContextMenu = function() { var stop = function(e) { if (e && e.domEvent && e.domEvent.type != "contextmenu") return; this.editor.off("nativecontextmenu", stop); if (e && e.domEvent) event.stopEvent(e.domEvent); }.bind(this); setTimeout(stop, 10); this.editor.on("nativecontextmenu", stop); }; }).call(MouseHandler.prototype); config.defineOptions(MouseHandler.prototype, "mouseHandler", { scrollSpeed: {initialValue: 2}, dragDelay: {initialValue: (useragent.isMac ? 150 : 0)}, dragEnabled: {initialValue: true}, focusTimeout: {initialValue: 0}, tooltipFollowsMouse: {initialValue: true} }); exports.MouseHandler = MouseHandler; });