diff --git a/lib/ace/editor.js b/lib/ace/editor.js index 937e0e73..67a296f8 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -656,6 +656,11 @@ var Editor = function(renderer, session) { this.insert(text); }; + + this.execCommand = function(command, args) { + this.commands.exec(command, this, args); + }; + /** * Editor.insert(text) * - text (String): The new text to add @@ -2126,4 +2131,4 @@ var Editor = function(renderer, session) { exports.Editor = Editor; -}); \ No newline at end of file +}); diff --git a/lib/ace/keyboard/textinput.js b/lib/ace/keyboard/textinput.js index 7204c022..1e215071 100644 --- a/lib/ace/keyboard/textinput.js +++ b/lib/ace/keyboard/textinput.js @@ -48,75 +48,98 @@ var TextInput = function(parentNode, host) { 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 ? "\x01" : "\x00"; - reset(true); + var PLACEHOLDER = useragent.isIE || useragent.isOpera ? "\x01\x01" : "\x00\x00"; + + resetValue(); + if (isFocused()) host.onFocus(); - var inCompostion = false; + // Somehow fixes problem with firing onpropertychange on first typed char + if (useragent.isOldIE) { + resetSelection(); + resetValue(); + setTimeout(resetSelection); + } + + var cut = false var copied = false; var pasted = false; + + var inCompostion = false; + + var isSelectionEmpty = true; + var tempStyle = ''; - function reset(full) { - try { - if (full) { + function resetValue() { + text.value = PLACEHOLDER; + //http://code.google.com/p/chromium/issues/detail?id=76516 + if (useragent.isWebKit) + setTimeout(function(){ text.value = PLACEHOLDER; - text.selectionStart = 0; - text.selectionEnd = 1; - } else - text.select(); - } catch (e) {} - } - - function sendText(valueToSend) { - if (!copied) { - var value = valueToSend || text.value; - if (value) { - if (value.length > 1) { - if (value.charAt(0) == PLACEHOLDER) - value = value.substr(1); - else if (value.charAt(value.length - 1) == PLACEHOLDER) - value = value.slice(0, -1); - } - - if (value && value != PLACEHOLDER) { - if (pasted) - host.onPaste(value); - else - host.onTextInput(value); - } - } - } - - copied = false; - pasted = false; - - // Safari doesn't fire copy events if no text is selected - reset(true); - } - - var onTextInput = function(e) { - if (!inCompostion) - sendText(e.data); - setTimeout(function () { - if (!inCompostion) - reset(true); - }, 0); + resetSelection(); + }); }; - var onPropertyChange = function(e) { - setTimeout(function() { - if (!inCompostion) - if(text.value != "") { - sendText(); - } - }, 0); + function resetSelection() { + var selectionStart = isSelectionEmpty ? 2 : 1; + var selectionEnd = 2; + + 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(); + } + }; + + var onSelect = function(e) { + if (cut) { + cut = false; + return; + } + if (copied) { + copied = false; + return; + } + if (text.selectionStart === 0 && text.selectionEnd === text.value.length) { + host.selectAll(); + resetSelection(); + } + }; + + var onInput = function(e) { + if (inCompostion) + return; + + if (pasted) { + var data = text.value; + resetValue(); + if (data) + host.onPaste(data); + pasted = false; + return; + } + + var data = text.value.substring(isSelectionEmpty ? 2 : 1); + resetValue(); + if (data) + host.onTextInput(data); + else + host.execCommand("del", {source: "ace"}); }; var onCompositionStart = function(e) { @@ -135,37 +158,111 @@ var TextInput = function(parentNode, host) { host.onCompositionEnd(); }; - var onCopy = function(e) { - copied = true; - var copyText = host.getCopyText(); - if(copyText) - text.value = copyText; - else - e.preventDefault(); - reset(); - setTimeout(function () { - sendText(); - }, 0); + var onCut = function(e) { + var data = host.getCopyText(); + if (!data) { + event.preventDefault(e); + return; + } + + var clipboardData = e.clipboardData || window.clipboardData; + + if (clipboardData) { + // Safari 5 has clipboardData object, but does not handle setData() + var supported = clipboardData.setData("Text", data); + if (supported) { + host.onCut(); + event.preventDefault(e); + } + } + + if (!supported) { + cut = true; + text.value = data; + text.select(); + setTimeout(function(){ host.onCut() }); + } }; - var onCut = function(e) { - copied = true; - var copyText = host.getCopyText(); - if(copyText) { - text.value = copyText; - host.onCut(); - } else - e.preventDefault(); - reset(); - setTimeout(function () { - sendText(); - }, 0); + var onCopy = function(e) { + var data = host.getCopyText(); + if (!data) { + event.preventDefault(e); + return; + } + + var clipboardData = e.clipboardData || window.clipboardData; + + if (clipboardData) { + // Safari 5 has clipboardData object, but does not handle setData() + var supported = clipboardData.setData("Text", data); + if (supported) { + host.onCopy(); + event.preventDefault(e); + } + } + if (!supported) { + copied = true; + text.value = data; + text.select(); + setTimeout(function(){ host.onCopy() }); + } + + + }; + + var onPaste = function(e) { + var clipboardData = e.clipboardData || window.clipboardData; + + if (clipboardData) { + var data = clipboardData.getData("Text"); + if (data) + host.onPaste(data); + event.preventDefault(e); + } + else { + text.value = ""; + pasted = true; + } }; event.addCommandKeyListener(text, host.onCommandKey.bind(host)); - event.addListener(text, "input", onTextInput); - + + event.addListener(text, "select", onSelect); + + event.addListener(text, "input", onInput); + + event.addListener(text, "cut", onCut); + event.addListener(text, "copy", onCopy); + event.addListener(text, "paste", onPaste); + + + // Opera has no clipboard events + if (!('oncut' in text) || !('oncopy' in text) || !('onpaste' in text)){ + event.addListener(parentNode, "keydown", function(e) { + if ((useragent.isMac && !e.metaKey) || !e.ctrlKey) + return; + + switch (e.keyCode) { + case 67: + onCopy(e); + break; + case 86: + onPaste(e); + break; + case 88: + onCut(e); + break; + } + }); + } + 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])) @@ -175,70 +272,6 @@ var TextInput = function(parentNode, host) { } inCompostion ? onCompositionUpdate() : onCompositionStart(); }); - - event.addListener(text, "propertychange", function() { - if (text.value != PLACEHOLDER) - setTimeout(sendText, 0); - }); - } - - event.addListener(text, "paste", function(e) { - // Mark that the next input text comes from past. - pasted = true; - // Some browsers support the event.clipboardData API. Use this to get - // the pasted content which increases speed if pasting a lot of lines. - if (e.clipboardData && e.clipboardData.getData) { - sendText(e.clipboardData.getData("text/plain")); - e.preventDefault(); - } - else { - // If a browser doesn't support any of the things above, use the regular - // method to detect the pasted input. - onPropertyChange(); - } - }); - - if ("onbeforecopy" in text && typeof clipboardData !== "undefined") { - event.addListener(text, "beforecopy", function(e) { - if (tempStyle) - return; // without this text is copied when contextmenu is shown - var copyText = host.getCopyText(); - if (copyText) - clipboardData.setData("Text", copyText); - else - e.preventDefault(); - }); - event.addListener(parentNode, "keydown", function(e) { - if (e.ctrlKey && e.keyCode == 88) { - var copyText = host.getCopyText(); - if (copyText) { - clipboardData.setData("Text", copyText); - host.onCut(); - } - event.preventDefault(e); - } - }); - event.addListener(text, "cut", onCut); // for ie9 context menu - } - else if (useragent.isOpera && !("KeyboardEvent" in window)) { - event.addListener(parentNode, "keydown", function(e) { - if ((useragent.isMac && !e.metaKey) || !e.ctrlKey) - return; - - if ((e.keyCode == 88 || e.keyCode == 67)) { - var copyText = host.getCopyText(); - if (copyText) { - text.value = copyText; - text.select(); - if (e.keyCode == 88) - host.onCut(); - } - } - }); - } - else { - event.addListener(text, "copy", onCopy); - event.addListener(text, "cut", onCut); } event.addListener(text, "compositionstart", onCompositionStart); @@ -256,11 +289,10 @@ var TextInput = function(parentNode, host) { event.addListener(text, "focus", function() { host.onFocus(); - reset(); + resetSelection(); }); this.focus = function() { - reset(); text.focus(); }; @@ -286,11 +318,6 @@ var TextInput = function(parentNode, host) { (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;"; - if (host.selection.isEmpty()) - text.value = ""; - else - reset(true); - if (e.type != "mousedown") return; @@ -311,7 +338,6 @@ var TextInput = function(parentNode, host) { text.style.cssText = tempStyle; tempStyle = ''; } - sendText(); if (host.renderer.$keepTextAreaAtCursor == null) { host.renderer.$keepTextAreaAtCursor = true; host.renderer.$moveTextAreaToCursor(); @@ -324,8 +350,16 @@ var TextInput = function(parentNode, host) { if (!useragent.isGecko) event.addListener(text, "contextmenu", function(e) { host.textInput.onContextMenu(e); - onContextMenuClose() + onContextMenuClose(); }); + + + host.addEventListener('changeSelection', function(){ + if (host.selection.isEmpty() != isSelectionEmpty) { + isSelectionEmpty = !isSelectionEmpty; + resetSelection(); + } + }); }; exports.TextInput = TextInput;