diff --git a/lib/ace/keyboard/textinput.js b/lib/ace/keyboard/textinput.js index 44632ef3..dfad806b 100644 --- a/lib/ace/keyboard/textinput.js +++ b/lib/ace/keyboard/textinput.js @@ -53,70 +53,103 @@ var TextInput = function(parentNode, host) { 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 resetTimeout = null; + 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 && !resetTimeout) + resetTimeout = 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(); + resetTimeout = null; + }); }; - var onPropertyChange = function(e) { - setTimeout(function() { - if (!inCompostion) - if(text.value != "") { - sendText(); - } - }, 0); + function resetSelection(isEmpty) { + var selectionStart = isEmpty ? 2 : 1; + 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(); + } + } catch(e){} + }; + + 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; + if (data.substring(0, 2) == PLACEHOLDER) + data = data.substr(2); + else + data = data.substr(1); + + resetValue(); + 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); + } else + host.onDelete(); }; var onCompositionStart = function(e) { @@ -135,37 +168,121 @@ 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(){ + cut = false; + resetValue(); + resetSelection(); + 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(){ + copied = false; + resetValue(); + resetSelection(); + 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 +292,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 +309,10 @@ var TextInput = function(parentNode, host) { event.addListener(text, "focus", function() { host.onFocus(); - reset(); + resetSelection(); }); this.focus = function() { - reset(); text.focus(); }; @@ -286,10 +338,7 @@ 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); + resetSelection(host.selection.isEmpty()); if (e.type != "mousedown") return; @@ -311,7 +360,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,7 +372,7 @@ var TextInput = function(parentNode, host) { if (!useragent.isGecko) event.addListener(text, "contextmenu", function(e) { host.textInput.onContextMenu(e); - onContextMenuClose() + onContextMenuClose(); }); };