diff --git a/lib/ace/keyboard/textinput.js b/lib/ace/keyboard/textinput.js index 4e07d24a..0f918dcf 100644 --- a/lib/ace/keyboard/textinput.js +++ b/lib/ace/keyboard/textinput.js @@ -3,7 +3,7 @@ * * 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 @@ -14,7 +14,7 @@ * * 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 @@ -34,66 +34,62 @@ define(function(require, exports, module) { var event = require("../lib/event"); var useragent = require("../lib/useragent"); var dom = require("../lib/dom"); +var lang = require("../lib/lang"); var TextInput = function(parentNode, host) { var text = dom.createElement("textarea"); text.className = "ace_text-input"; /*/ debug - text.style.opacity = 1 - text.style.background = "rgba(0, 250, 0, 0.3)" - text.style.outline = "rgba(0, 250, 0, 0.8) solid 1px" - text.style.outlineOffset = "3px" + text.style.cssText = "opacity:1;background:rgba(0, 250, 0, 0.3);outline:rgba(0, 250, 0, 0.8) solid 1px;outline-offset:3px;width:5em;z-index:500"; /**/ if (useragent.isTouchPad) text.setAttribute("x-palm-disable-auto-cap", true); text.wrap = "off"; + text.autocorrect = "off"; + text.autocapitalize = "off"; text.spellcheck = false; text.style.top = "-2em"; parentNode.insertBefore(text, parentNode.firstChild); - var PLACEHOLDER = useragent.isIE || useragent.isOpera ? "\x01\x01" : "\x00\x00"; + var PLACEHOLDER = "\x01\x01"; - resetValue(); - - if (isFocused()) - host.onFocus(); - - // Somehow fixes problem with firing onpropertychange on first typed char - if (useragent.isOldIE) { - resetSelection(); - resetValue(); - setTimeout(resetSelection); - } - - var cut = false + var cut = false; var copied = false; var pasted = false; - var inCompostion = false; - - var resetTimeout = null; - var tempStyle = ''; + var isSelectionEmpty = true; - function resetValue() { - if (inCompostion) - return; - text.value = PLACEHOLDER; - //http://code.google.com/p/chromium/issues/detail?id=76516 - if (!resetTimeout) { - resetTimeout = setTimeout(function(){ - resetTimeout = null; - if (inCompostion) - return; - if (useragent.isWebKit) - text.value = PLACEHOLDER; - resetSelection(); - }); - } + // FOCUS + var isFocused = document.activeElement === text; + event.addListener(text, "blur", function() { + host.onBlur(); + isFocused = false; + }); + event.addListener(text, "focus", function() { + isFocused = true; + host.onFocus(); + resetSelection(); + }); + this.focus = function() { text.focus(); }; + this.blur = function() { text.blur(); }; + this.isFocused = function() { + return isFocused; }; + // modifying selection of blured textarea can focus it (chrome mac/linux) + var syncSelection = lang.delayedCall(function() { + isFocused && resetSelection(isSelectionEmpty); + }); + var syncValue = lang.delayedCall(function() { + if (!inCompostion) { + text.value = PLACEHOLDER; + isFocused && resetSelection(); + } + }); + function resetSelection(isEmpty) { if (inCompostion) return; @@ -101,19 +97,82 @@ var TextInput = function(parentNode, host) { var selectionEnd = 2; // on firefox this throws if textarea is hidden try { - if (text.setSelectionRange) { - text.setSelectionRange(selectionStart, selectionEnd); - } - // IE8 does not support setSelectionRange - else if (text.createTextRange) { - var range = text.createTextRange(); - range.collapse(true); - range.moveEnd('character', selectionEnd); - range.moveStart('character', selectionStart); - range.select(); - } + text.setSelectionRange(selectionStart, selectionEnd); } catch(e){} + } + + function resetValue() { + if (inCompostion) + return; + text.value = PLACEHOLDER; + //http://code.google.com/p/chromium/issues/detail?id=76516 + if (useragent.isWebKit) + syncValue.schedule(); + } + + useragent.isWebKit || host.addEventListener('changeSelection', function() { + if (host.selection.isEmpty() != isSelectionEmpty) { + isSelectionEmpty = !isSelectionEmpty; + syncSelection.schedule(); + } + }); + + resetValue(); + if (isFocused) + host.onFocus(); + + + var isAllSelected = function(text) { + return text.selectionStart === 0 && text.selectionEnd === text.value.length; }; + // IE8 does not support setSelectionRange + if (!text.setSelectionRange && text.createTextRange) { + text.setSelectionRange = function(selectionStart, selectionEnd) { + var range = this.createTextRange(); + range.collapse(true); + range.moveStart('character', selectionStart); + range.moveEnd('character', selectionEnd); + range.select(); + }; + isAllSelected = function(text) { + try { + var range = text.ownerDocument.selection.createRange(); + }catch(e) {} + if (!range || range.parentElement() != text) return false; + return range.text == text.value; + } + } + if (useragent.isOldIE) { + var inPropertyChange = false; + var onPropertyChange = function(e){ + if (inPropertyChange) + return; + var data = text.value; + if (inCompostion || !data || data == PLACEHOLDER) + return; + // can happen either after delete or during insert operation + if (e && data == PLACEHOLDER[0]) + return syncProperty.schedule(); + + sendText(data); + // ie8 calls propertychange handlers synchronously! + inPropertyChange = true; + resetValue(); + inPropertyChange = false; + }; + var syncProperty = lang.delayedCall(onPropertyChange); + event.addListener(text, "propertychange", onPropertyChange); + + var keytable = { 13:1, 27:1 }; + event.addListener(text, "keyup", function (e) { + if (inCompostion && (!text.value || keytable[e.keyCode])) + setTimeout(onCompositionEnd, 0); + if ((text.value.charCodeAt(0)||0) < 129) { + return; + } + inCompostion ? onCompositionUpdate() : onCompositionStart(); + }); + } var onSelect = function(e) { if (cut) { @@ -124,18 +183,13 @@ var TextInput = function(parentNode, host) { copied = false; return; } - if (text.selectionStart === 0 && text.selectionEnd === text.value.length) { + if (isAllSelected(text)) { host.selectAll(); resetSelection(); } }; - var onInput = function(e) { - if (inCompostion) - return; - var data = text.value; - resetValue(); - + var sendText = function(data) { if (pasted) { resetSelection(); if (data) @@ -148,31 +202,23 @@ var TextInput = function(parentNode, host) { data = data.substr(2); else if (data[0] == PLACEHOLDER[0]) data = data.substr(1); + else if (data[data.length - 1] == PLACEHOLDER[0]) + data = data.slice(0, -1); + // can happen if undo in textarea isn't stopped + if (data[data.length - 1] == PLACEHOLDER[0]) + data = data.slice(0, -1); - if (data) { - // can happen if undo in textarea isn't stopped - if (data[data.length - 1] == PLACEHOLDER[0]) - data = data.slice(0, -1); - if (data) - host.onTextInput(data); - } + if (data) + host.onTextInput(data); } }; + var onInput = function(e) { + if (inCompostion) + return; + var data = text.value; + resetValue(); - var onCompositionStart = function(e) { - inCompostion = true; - host.onCompositionStart(); - setTimeout(onCompositionUpdate, 0); - }; - - var onCompositionUpdate = function() { - if (!inCompostion) return; - host.onCompositionUpdate(text.value); - }; - - var onCompositionEnd = function(e) { - inCompostion = false; - host.onCompositionEnd(); + sendText(data); }; var onCut = function(e) { @@ -214,7 +260,6 @@ var TextInput = function(parentNode, host) { } var clipboardData = e.clipboardData || window.clipboardData; - if (clipboardData) { // Safari 5 has clipboardData object, but does not handle setData() var supported = clipboardData.setData("Text", data); @@ -234,8 +279,6 @@ var TextInput = function(parentNode, host) { host.onCopy(); }); } - - }; var onPaste = function(e) { @@ -286,54 +329,33 @@ var TextInput = function(parentNode, host) { }); } - if (useragent.isOldIE) { - event.addListener(text, "propertychange", function(e){ - if (text.value != "" && text.value != PLACEHOLDER) - onInput(e); - }); - var keytable = { 13:1, 27:1 }; - event.addListener(text, "keyup", function (e) { - if (inCompostion && (!text.value || keytable[e.keyCode])) - setTimeout(onCompositionEnd, 0); - if ((text.value.charCodeAt(0)|0) < 129) { - return; - } - inCompostion ? onCompositionUpdate() : onCompositionStart(); - }); - } + // COMPOSITION + var onCompositionStart = function(e) { + inCompostion = true; + host.onCompositionStart(); + setTimeout(onCompositionUpdate, 0); + }; + + var onCompositionUpdate = function() { + if (!inCompostion) return; + host.onCompositionUpdate(text.value); + }; + + var onCompositionEnd = function(e) { + inCompostion = false; + host.onCompositionEnd(); + }; + event.addListener(text, "compositionstart", onCompositionStart); - if (useragent.isGecko) { + if (useragent.isGecko) event.addListener(text, "text", onCompositionUpdate); - } - if (useragent.isWebKit) { + else event.addListener(text, "keyup", onCompositionUpdate); - } event.addListener(text, "compositionend", onCompositionEnd); - event.addListener(text, "blur", function() { - host.onBlur(); - }); - - event.addListener(text, "focus", function() { - host.onFocus(); - resetSelection(); - }); - - this.focus = function() { - text.focus(); - }; - - this.blur = function() { - text.blur(); - }; - - function isFocused() { - return document.activeElement === text; - } - this.isFocused = isFocused; - + // CONTEXTMENU this.getElement = function() { return text; }; @@ -342,12 +364,17 @@ var TextInput = function(parentNode, host) { if (!tempStyle) tempStyle = text.style.cssText; - text.style.cssText = - "position:fixed; z-index:100000;" + - (useragent.isIE ? "background:rgba(0, 0, 0, 0.03); opacity:0.1;" : "") + //"background:rgba(250, 0, 0, 0.3); opacity:1;" + - "left:" + (e.clientX - 2) + "px; top:" + (e.clientY - 2) + "px;"; + text.style.cssText = "z-index:100000;" + (useragent.isIE ? "opacity:0.1;" : ""); + // text.style.cssText += "background:rgba(250, 0, 0, 0.3); opacity:1;"; resetSelection(host.selection.isEmpty()); + host._emit("nativecontextmenu", {target: editor}); + var rect = host.container.getBoundingClientRect(); + var move = function(e) { + text.style.left = e.clientX - rect.left - 2 + "px"; + text.style.top = e.clientY - rect.top - 2 + "px"; + }; + move(e); if (e.type != "mousedown") return; @@ -357,12 +384,10 @@ var TextInput = function(parentNode, host) { // on windows context menu is opened after mouseup if (useragent.isWin) - event.capture(host.container, function(e) { - text.style.left = e.clientX - 2 + "px"; - text.style.top = e.clientY - 2 + "px"; - }, onContextMenuClose); + event.capture(host.container, move, onContextMenuClose); }; + this.onContextMenuClose = onContextMenuClose; function onContextMenuClose() { setTimeout(function () { if (tempStyle) { @@ -374,15 +399,15 @@ var TextInput = function(parentNode, host) { host.renderer.$moveTextAreaToCursor(); } }, 0); - }; - this.onContextMenuClose = onContextMenuClose; + } // firefox fires contextmenu event after opening it - if (!useragent.isGecko) + if (!useragent.isGecko) { event.addListener(text, "contextmenu", function(e) { host.textInput.onContextMenu(e); onContextMenuClose(); }); + } }; exports.TextInput = TextInput; diff --git a/lib/ace/lib/browser_focus.js b/lib/ace/lib/browser_focus.js deleted file mode 100644 index 37597015..00000000 --- a/lib/ace/lib/browser_focus.js +++ /dev/null @@ -1,95 +0,0 @@ -/* ***** 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 oop = require("./oop"); -var event = require("./event"); -var EventEmitter = require("./event_emitter").EventEmitter; - -/* - * This class keeps track of the focus state of the given window. - * Focus changes for example when the user switches a browser tab, - * goes to the location bar or switches to another application. - */ -var BrowserFocus = function(win) { - win = win || window; - - this.lastFocus = new Date().getTime(); - this._isFocused = true; - - var _self = this; - - // IE < 9 supports focusin and focusout events - if ("onfocusin" in win.document) { - event.addListener(win.document, "focusin", function(e) { - _self._setFocused(true); - }); - - event.addListener(win.document, "focusout", function(e) { - _self._setFocused(!!e.toElement); - }); - } - else { - event.addListener(win, "blur", function(e) { - _self._setFocused(false); - }); - - event.addListener(win, "focus", function(e) { - _self._setFocused(true); - }); - } -}; - -(function(){ - - oop.implement(this, EventEmitter); - - this.isFocused = function() { - return this._isFocused; - }; - - this._setFocused = function(isFocused) { - if (this._isFocused == isFocused) - return; - - if (isFocused) - this.lastFocus = new Date().getTime(); - - this._isFocused = isFocused; - this._emit("changeFocus"); - }; - -}).call(BrowserFocus.prototype); - - -exports.BrowserFocus = BrowserFocus; -}); diff --git a/lib/ace/lib/lang.js b/lib/ace/lib/lang.js index 6ea59761..4f81c842 100644 --- a/lib/ace/lib/lang.js +++ b/lib/ace/lib/lang.js @@ -179,7 +179,7 @@ exports.delayedCall = function(fcn, defaultTimeout) { timer = setTimeout(callback, timeout || defaultTimeout); }; - _self.delay = delayed; + _self.delay = _self; _self.schedule = function(timeout) { if (timer == null) timer = setTimeout(callback, timeout || 0);