From 8d3c4feeb5c798d4bbbb14f930c865f1543c7bab Mon Sep 17 00:00:00 2001 From: Vlad Zinculescu Date: Wed, 28 Nov 2012 16:01:31 +0100 Subject: [PATCH 1/3] searchbox wip --- demo/kitchen-sink/demo.js | 4 +- lib/ace/commands/default_commands.js | 12 +- lib/ace/css/editor.css | 130 ++++++++++++++ lib/ace/ext/searchbox.js | 259 +++++++++++++++++++++++++++ 4 files changed, 394 insertions(+), 11 deletions(-) create mode 100644 lib/ace/ext/searchbox.js diff --git a/demo/kitchen-sink/demo.js b/demo/kitchen-sink/demo.js index 93859a61..183a872a 100644 --- a/demo/kitchen-sink/demo.js +++ b/demo/kitchen-sink/demo.js @@ -108,7 +108,7 @@ env.editor.commands.addCommands([{ editor.gotoLine(line); }, readOnly: true -}, { +}/*, { name: "find", bindKey: {win: "Ctrl-F", mac: "Command-F"}, exec: function(editor, needle) { @@ -121,7 +121,7 @@ env.editor.commands.addCommands([{ editor.find(needle); }, readOnly: true -}, { +}*/, { name: "focusCommandLine", bindKey: "shift-esc", exec: function(editor, needle) { editor.cmdLine.focus(); }, diff --git a/lib/ace/commands/default_commands.js b/lib/ace/commands/default_commands.js index 8c33fc42..7c112300 100644 --- a/lib/ace/commands/default_commands.js +++ b/lib/ace/commands/default_commands.js @@ -33,6 +33,7 @@ define(function(require, exports, module) { var lang = require("../lib/lang"); +var SearchBox = require("ace/ext/searchbox"); function bindKey(win, mac) { return { win: win, @@ -94,8 +95,7 @@ exports.commands = [{ name: "find", bindKey: bindKey("Ctrl-F", "Command-F"), exec: function(editor) { - var needle = prompt("Find:", editor.getCopyText()); - editor.find(needle); + SearchBox.SearchBox(editor, editor.getSelectionRange()); }, readOnly: true }, { @@ -345,13 +345,7 @@ exports.commands = [{ name: "replace", bindKey: bindKey("Ctrl-R", "Command-Option-F"), exec: function(editor) { - var needle = prompt("Find:", editor.getCopyText()); - if (!needle) - return; - var replacement = prompt("Replacement:"); - if (!replacement) - return; - editor.replace(replacement, {needle: needle}); + SearchBox.SearchBox(editor, editor.getSelectionRange(), true); } }, { name: "replaceall", diff --git a/lib/ace/css/editor.css b/lib/ace/css/editor.css index 73702bdf..5581e8df 100644 --- a/lib/ace/css/editor.css +++ b/lib/ace/css/editor.css @@ -366,3 +366,133 @@ .ace_italic { font-style: italic; } + + +/* ------------------------------------------------------------------------------------------ + * Editor Search Form + * --------------------------------------------------------------------------------------- */ + .ace_search { + background-color: #ddd; + border: 1px solid #cbcbcb; + border-top: 0 none; + max-width: 297px; + overflow: hidden; + padding: 4px; + padding-right: 6px; + padding-bottom: 0; + position: absolute; + top: 0px; + z-index: 99; +} +.ace_search.left { + border-left: 0 none; + border-radius: 0px 0px 5px 0px; + left: 0; +} +.ace_search.right { + border-radius: 0px 0px 0px 5px; + border-right: 0 none; + right: 0; +} + +.ace_search_form { + border-radius: 3px; + border: 1px solid #cbcbcb; + float: left; + margin-bottom: 4px; + overflow: hidden; +} +.ace_search label { + float: left; + color: #656565; +} +.ace_search input { + background: white; + border-right: 1px solid #cbcbcb; + border: 0 none; + box-sizing: border-box; + display: block; + float: left; + height: 22px; + outline: 0; + padding: 0 7px; + width: 214px; +} +.ace_searchbtn, +.ace_replacebtn { + background: #fff; + border: 0 none; + border-left: 1px solid #dcdcdc; + cursor: pointer; + display: block; + float: left; + height: 22px; + margin: 0; + padding: 0; + position: relative; +} +.ace_searchbtn:last-child, +.ace_replacebtn:last-child { + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; +} +.ace_searchbtn { + width: 27px; +} +.ace_searchbtn:after { + border: 2px solid #656565; + content: ""; + display: block; + height: 4px; + position: absolute; + width: 4px; + -webkit-transform: rotateZ(45deg); +} +.ace_searchbtn.prev:after { + border-right: 0 none; + border-bottom: 0 none; + left: 10px; + top: 9px; +} +.ace_searchbtn.next:after { + border-left: 0 none; + border-top: 0 none; + left: 10px; + top: 7px; +} +.ace_searchbtn:disabled { + background: none; + cursor: default; +} +.ace_searchbtn:disabled:after { + border-color: #ccc; +} +.ace_searchbtn_close { + background: none; + border-radius: 50%; + border: 0 none; + color: #656565; + cursor: pointer; + display: block; + float: right; + font-family: Arial; + font-size: 16px; + height: 14px; + line-height: 16px; + margin: 5px 1px 9px 5px; + padding: 0; + text-align: center; + width: 14px; +} +.ace_searchbtn_close:hover { + background: #656565; + color: white; +} +.ace_replacebtn { +} +.ace_replacebtn.prev { + width: 54px +} +.ace_replacebtn.next { + width: 27px +} \ No newline at end of file diff --git a/lib/ace/ext/searchbox.js b/lib/ace/ext/searchbox.js new file mode 100644 index 00000000..cd27e808 --- /dev/null +++ b/lib/ace/ext/searchbox.js @@ -0,0 +1,259 @@ +/* ***** 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 dom = require("ace/lib/dom"); +var event = require("ace/lib/event"); +var Range = require("ace/range").Range; + +exports.SearchBox = function (editor, range, showReplaceForm) { + var _self = this; + var sb = null;//search box + var n = null;//needle + var r = null;//replace + var p = null;//parent DOM node + + var needle = editor.session.getTextRange(range); + + this.$init = function() { + + p = editor.container.parentNode; + sb = p.querySelector(".ace_search"); //the complete form + + if(!sb) { + + //Create the elements + sb = document.createElement("div"); + sb.setAttribute("class", "ace_search right"); + p.appendChild( sb ); + + var c = document.createElement("button"); //cancel btn + c.setAttribute("type","button"); + c.setAttribute("class","ace_searchbtn_close"); + c.innerHTML = "×"; + sb.appendChild( c ); + c.onclick = _self.hide; + + var sf = document.createElement("div"); //search form + sf.setAttribute("id", "ace_search_form"); + sf.setAttribute("class", "ace_search_form"); + sb.appendChild( sf ); + + n = document.createElement("input"); //needle + n.setAttribute("class", "ace_search_field"); + n.setAttribute("id", "ace_search_field"); + n.setAttribute("placeholder", "Search for"); + n.setAttribute("type", "text"); + sf.appendChild( n ); + + var sp = document.createElement("button"); + sp.setAttribute("type","button"); + sp.setAttribute("class","ace_searchbtn prev"); + sf.appendChild( sp ); + sp.onclick = _self.findPrev; + + var sn = document.createElement("button"); + sn.setAttribute("type","button"); + sn.setAttribute("class","ace_searchbtn next"); + sf.appendChild( sn ); + sn.onclick = _self.findNext; + + var rf = document.createElement("div"); //replace form + rf.setAttribute("id", "ace_replace_form"); + rf.setAttribute("class", "ace_search_form"); + rf.setAttribute("style", "display: none"); + sb.appendChild( rf); + + r = document.createElement("input"); //needle + r.setAttribute("class", "ace_search_field"); + r.setAttribute("id", "ace_replace_field"); + r.setAttribute("placeholder", "Replace with"); + r.setAttribute("type", "text"); + rf.appendChild( r ); + + var rr = document.createElement("button"); + rr.setAttribute("type","button"); + rr.setAttribute("class","ace_replacebtn prev"); + rr.innerHTML = "Replace"; + rf.appendChild( rr ); + rr.onclick = _self.replace; + + var ra = document.createElement("button"); + ra.setAttribute("type","button"); + ra.setAttribute("class","ace_replacebtn next"); + ra.innerHTML = "All"; + rf.appendChild( ra ); + ra.onclick = _self.replaceAll; + + + } else { + sb.removeAttribute("style"); + n = sb.querySelector("#ace_search_field"); + r = sb.querySelector("#ace_replace_field"); + } + + console.log( "showReplaceForm", showReplaceForm ); + if( showReplaceForm ) { + p.querySelector("#ace_replace_form").removeAttribute("style"); + } + + + if( needle ) + n.value = needle; + + //set initial focus and select text in input + n.focus(); + n.select(); + + //keybinging outsite of the searchbox + _self.$searchKeybingin = { + handleKeyboard: function(data, hashId, keyString, keyCode) { + console.log("MERGE"); + if (keyString == "esc") + return {command: this.command}; + }, + command: { + exec: function(editor) { + _self.hide(); + } + } + }; + editor.keyBinding.addKeyboardHandler(this.$searchKeybingin); + + n.onkeydown = searchonkeydown; + r.onkeydown = searchonkeydown; + function searchonkeydown(e) { + + var cmdKey = e.metaKey || e.ctrlKey; + + console.log(cmdKey); + + if( cmdKey ) { + if( e.which == 70 ) { //f key + var rf = p.querySelector("#ace_replace_form"); + if( e.altKey && rf.hasAttribute("style") ) { + p.querySelector("#ace_replace_form").removeAttribute("style"); + } else { + _self.hide(); + } + return false; + } + } + + if( e.which === 9 ) { //tab + if( e.target == n ) { + r.focus(); + r.select(); + } else { + n.focus(); + n.select(); + } + return false; + } + } + + n.onpaste = function(e) { + //I do this because the onpaste event is fired before the value of the input actually changes + setTimeout(function() { + e.target.onkeyup(); + }, 100); + } + n.onkeyup = function(e){ + console.log("KEYUP", e); + if(!e) { + e = {}; + e.which = 0; + } + + if( e.which === 27 ) { //esc key + _self.hide(); + } else if( e.which === 13 ) { //enter key + _self.findNext( ); + } else { + editor.moveCursorTo( range.start.row, range.start.column ); + _self.findNext(); + } + }; + r.onkeyup = function(e){ + if( e.which === 27 ) { //esc key + _self.hide(); + } else if( e.which === 13 ) { //enter key + editor.moveCursorTo( range.start.row, range.start.column ); + _self.replace(); + } + }; + }; + + this.findPrev = function() { + editor.find(n.value, { + backwards: true, + wrap: true + }); + }; + this.findNext = function() { + editor.find(n.value, { + backwards: false, + wrap: true + }); + }; + this.replace = function() { + editor.replace( r.value ); + _self.findNext(); + }; + this.replaceAll = function() { + editor.replaceAll( r.value ); + }; + + this.hide = function () { + n.value = ""; + r.value = ""; + sb.setAttribute("style", "display: none"); + sb.querySelector("#ace_replace_form").setAttribute("style", "display: none"); + editor.keyBinding.removeKeyboardHandler(this.$searchKeybingin); + editor.focus(); + }; + + _self.$init(); +}; + +}); + + +/* ------------------------------------------------------------------------------------------ + * TODO + * --------------------------------------------------------------------------------------- */ +/* +- move search form to the left if it masks current word +- includ all options that search has. ex: regex +- searchbox.searchbox is not that pretty. we should have just searchbox +- disable prev button if it makes sence +*/ \ No newline at end of file From 7049caa3bfafeed8a122285cb619b41eab55cbd0 Mon Sep 17 00:00:00 2001 From: Vlad Zinculescu Date: Wed, 28 Nov 2012 16:53:49 +0100 Subject: [PATCH 2/3] fixed styles on all browsers - ie --- lib/ace/css/editor.css | 49 ++++++++++++++-------------------------- lib/ace/ext/searchbox.js | 1 - 2 files changed, 17 insertions(+), 33 deletions(-) diff --git a/lib/ace/css/editor.css b/lib/ace/css/editor.css index 5581e8df..5eea817f 100644 --- a/lib/ace/css/editor.css +++ b/lib/ace/css/editor.css @@ -407,10 +407,12 @@ color: #656565; } .ace_search input { - background: white; + background-color: white; border-right: 1px solid #cbcbcb; border: 0 none; - box-sizing: border-box; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; display: block; float: left; height: 22px; @@ -436,39 +438,23 @@ border-top-right-radius: 3px; border-bottom-right-radius: 3px; } -.ace_searchbtn { - width: 27px; -} -.ace_searchbtn:after { - border: 2px solid #656565; - content: ""; - display: block; - height: 4px; - position: absolute; - width: 4px; - -webkit-transform: rotateZ(45deg); -} -.ace_searchbtn.prev:after { - border-right: 0 none; - border-bottom: 0 none; - left: 10px; - top: 9px; -} -.ace_searchbtn.next:after { - border-left: 0 none; - border-top: 0 none; - left: 10px; - top: 7px; -} .ace_searchbtn:disabled { background: none; cursor: default; } -.ace_searchbtn:disabled:after { - border-color: #ccc; +.ace_searchbtn { + background-position: 50% 50%; + background-repeat: no-repeat; + width: 27px; +} +.ace_searchbtn.prev { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAFCAYAAAB4ka1VAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADFJREFUeNpiSU1NZUAC/6E0I0yACYskCpsJiySKIiY0SUZk40FyTEgCjGgKwTRAgAEAQJUIPCE+qfkAAAAASUVORK5CYII=); +} +.ace_searchbtn.next { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAFCAYAAAB4ka1VAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADRJREFUeNpiTE1NZQCC/0DMyIAKwGJMUAYDEo3M/s+EpvM/mkKwCQxYjIeLMaELoLMBAgwAU7UJObTKsvAAAAAASUVORK5CYII=); } .ace_searchbtn_close { - background: none; + background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAcCAYAAABRVo5BAAAAZ0lEQVR42u2SUQrAMAhDvazn8OjZBilCkYVVxiis8H4CT0VrAJb4WHT3C5xU2a2IQZXJjiQIRMdkEoJ5Q2yMqpfDIo+XY4k6h+YXOyKqTIj5REaxloNAd0xiKmAtsTHqW8sR2W5f7gCu5nWFUpVjZwAAAABJRU5ErkJggg==) no-repeat 50% 0; border-radius: 50%; border: 0 none; color: #656565; @@ -485,11 +471,10 @@ width: 14px; } .ace_searchbtn_close:hover { - background: #656565; + background-color: #656565; + background-position: 50% 100%; color: white; } -.ace_replacebtn { -} .ace_replacebtn.prev { width: 54px } diff --git a/lib/ace/ext/searchbox.js b/lib/ace/ext/searchbox.js index cd27e808..1df7cd6d 100644 --- a/lib/ace/ext/searchbox.js +++ b/lib/ace/ext/searchbox.js @@ -59,7 +59,6 @@ exports.SearchBox = function (editor, range, showReplaceForm) { var c = document.createElement("button"); //cancel btn c.setAttribute("type","button"); c.setAttribute("class","ace_searchbtn_close"); - c.innerHTML = "×"; sb.appendChild( c ); c.onclick = _self.hide; From b1f9b957555679254d31a19ce57e07789bffdc6a Mon Sep 17 00:00:00 2001 From: nightwing Date: Wed, 12 Dec 2012 00:05:59 +0400 Subject: [PATCH 3/3] tweak searchbox --- lib/ace/commands/default_commands.js | 20 +- lib/ace/css/editor.css | 115 -------- lib/ace/ext/searchbox.css | 115 ++++++++ lib/ace/ext/searchbox.js | 376 +++++++++++++-------------- 4 files changed, 307 insertions(+), 319 deletions(-) create mode 100644 lib/ace/ext/searchbox.css diff --git a/lib/ace/commands/default_commands.js b/lib/ace/commands/default_commands.js index 7c112300..b8583c06 100644 --- a/lib/ace/commands/default_commands.js +++ b/lib/ace/commands/default_commands.js @@ -32,8 +32,8 @@ define(function(require, exports, module) { "use strict"; var lang = require("../lib/lang"); +var config = require("../config"); -var SearchBox = require("ace/ext/searchbox"); function bindKey(win, mac) { return { win: win, @@ -95,7 +95,7 @@ exports.commands = [{ name: "find", bindKey: bindKey("Ctrl-F", "Command-F"), exec: function(editor) { - SearchBox.SearchBox(editor, editor.getSelectionRange()); + config.loadModule("ace/ext/searchbox", function(e) {e.Search(editor)}); }, readOnly: true }, { @@ -343,21 +343,9 @@ exports.commands = [{ multiSelectAction: "forEach" }, { name: "replace", - bindKey: bindKey("Ctrl-R", "Command-Option-F"), + bindKey: bindKey("Ctrl-H", "Command-Option-F"), exec: function(editor) { - SearchBox.SearchBox(editor, editor.getSelectionRange(), true); - } -}, { - name: "replaceall", - bindKey: bindKey("Ctrl-Shift-R", "Command-Shift-Option-F"), - exec: function(editor) { - var needle = prompt("Find:"); - if (!needle) - return; - var replacement = prompt("Replacement:"); - if (!replacement) - return; - editor.replaceAll(replacement, {needle: needle}); + config.loadModule("ace/ext/searchbox", function(e) {e.Search(editor, true)}); } }, { name: "undo", diff --git a/lib/ace/css/editor.css b/lib/ace/css/editor.css index 5eea817f..73702bdf 100644 --- a/lib/ace/css/editor.css +++ b/lib/ace/css/editor.css @@ -366,118 +366,3 @@ .ace_italic { font-style: italic; } - - -/* ------------------------------------------------------------------------------------------ - * Editor Search Form - * --------------------------------------------------------------------------------------- */ - .ace_search { - background-color: #ddd; - border: 1px solid #cbcbcb; - border-top: 0 none; - max-width: 297px; - overflow: hidden; - padding: 4px; - padding-right: 6px; - padding-bottom: 0; - position: absolute; - top: 0px; - z-index: 99; -} -.ace_search.left { - border-left: 0 none; - border-radius: 0px 0px 5px 0px; - left: 0; -} -.ace_search.right { - border-radius: 0px 0px 0px 5px; - border-right: 0 none; - right: 0; -} - -.ace_search_form { - border-radius: 3px; - border: 1px solid #cbcbcb; - float: left; - margin-bottom: 4px; - overflow: hidden; -} -.ace_search label { - float: left; - color: #656565; -} -.ace_search input { - background-color: white; - border-right: 1px solid #cbcbcb; - border: 0 none; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - display: block; - float: left; - height: 22px; - outline: 0; - padding: 0 7px; - width: 214px; -} -.ace_searchbtn, -.ace_replacebtn { - background: #fff; - border: 0 none; - border-left: 1px solid #dcdcdc; - cursor: pointer; - display: block; - float: left; - height: 22px; - margin: 0; - padding: 0; - position: relative; -} -.ace_searchbtn:last-child, -.ace_replacebtn:last-child { - border-top-right-radius: 3px; - border-bottom-right-radius: 3px; -} -.ace_searchbtn:disabled { - background: none; - cursor: default; -} -.ace_searchbtn { - background-position: 50% 50%; - background-repeat: no-repeat; - width: 27px; -} -.ace_searchbtn.prev { - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAFCAYAAAB4ka1VAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADFJREFUeNpiSU1NZUAC/6E0I0yACYskCpsJiySKIiY0SUZk40FyTEgCjGgKwTRAgAEAQJUIPCE+qfkAAAAASUVORK5CYII=); -} -.ace_searchbtn.next { - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAFCAYAAAB4ka1VAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADRJREFUeNpiTE1NZQCC/0DMyIAKwGJMUAYDEo3M/s+EpvM/mkKwCQxYjIeLMaELoLMBAgwAU7UJObTKsvAAAAAASUVORK5CYII=); -} -.ace_searchbtn_close { - background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAcCAYAAABRVo5BAAAAZ0lEQVR42u2SUQrAMAhDvazn8OjZBilCkYVVxiis8H4CT0VrAJb4WHT3C5xU2a2IQZXJjiQIRMdkEoJ5Q2yMqpfDIo+XY4k6h+YXOyKqTIj5REaxloNAd0xiKmAtsTHqW8sR2W5f7gCu5nWFUpVjZwAAAABJRU5ErkJggg==) no-repeat 50% 0; - border-radius: 50%; - border: 0 none; - color: #656565; - cursor: pointer; - display: block; - float: right; - font-family: Arial; - font-size: 16px; - height: 14px; - line-height: 16px; - margin: 5px 1px 9px 5px; - padding: 0; - text-align: center; - width: 14px; -} -.ace_searchbtn_close:hover { - background-color: #656565; - background-position: 50% 100%; - color: white; -} -.ace_replacebtn.prev { - width: 54px -} -.ace_replacebtn.next { - width: 27px -} \ No newline at end of file diff --git a/lib/ace/ext/searchbox.css b/lib/ace/ext/searchbox.css new file mode 100644 index 00000000..2b828c8d --- /dev/null +++ b/lib/ace/ext/searchbox.css @@ -0,0 +1,115 @@ + + +/* ------------------------------------------------------------------------------------------ + * Editor Search Form + * --------------------------------------------------------------------------------------- */ + .ace_search { + background-color: #ddd; + border: 1px solid #cbcbcb; + border-top: 0 none; + max-width: 297px; + overflow: hidden; + padding: 4px; + padding-right: 6px; + padding-bottom: 0; + position: absolute; + top: 0px; + z-index: 99; +} +.ace_search.left { + border-left: 0 none; + border-radius: 0px 0px 5px 0px; + left: 0; +} +.ace_search.right { + border-radius: 0px 0px 0px 5px; + border-right: 0 none; + right: 0; +} + +.ace_search_form, .ace_replace_form { + border-radius: 3px; + border: 1px solid #cbcbcb; + float: left; + margin-bottom: 4px; + overflow: hidden; +} +.ace_search label { + float: left; + color: #656565; +} +.ace_search input { + background-color: white; + border-right: 1px solid #cbcbcb; + border: 0 none; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + display: block; + float: left; + height: 22px; + outline: 0; + padding: 0 7px; + width: 214px; +} +.ace_searchbtn, +.ace_replacebtn { + background: #fff; + border: 0 none; + border-left: 1px solid #dcdcdc; + cursor: pointer; + display: block; + float: left; + height: 22px; + margin: 0; + padding: 0; + position: relative; +} +.ace_searchbtn:last-child, +.ace_replacebtn:last-child { + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; +} +.ace_searchbtn:disabled { + background: none; + cursor: default; +} +.ace_searchbtn { + background-position: 50% 50%; + background-repeat: no-repeat; + width: 27px; +} +.ace_searchbtn.prev { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAFCAYAAAB4ka1VAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADFJREFUeNpiSU1NZUAC/6E0I0yACYskCpsJiySKIiY0SUZk40FyTEgCjGgKwTRAgAEAQJUIPCE+qfkAAAAASUVORK5CYII=); +} +.ace_searchbtn.next { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAFCAYAAAB4ka1VAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADRJREFUeNpiTE1NZQCC/0DMyIAKwGJMUAYDEo3M/s+EpvM/mkKwCQxYjIeLMaELoLMBAgwAU7UJObTKsvAAAAAASUVORK5CYII=); +} +.ace_searchbtn_close { + background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAcCAYAAABRVo5BAAAAZ0lEQVR42u2SUQrAMAhDvazn8OjZBilCkYVVxiis8H4CT0VrAJb4WHT3C5xU2a2IQZXJjiQIRMdkEoJ5Q2yMqpfDIo+XY4k6h+YXOyKqTIj5REaxloNAd0xiKmAtsTHqW8sR2W5f7gCu5nWFUpVjZwAAAABJRU5ErkJggg==) no-repeat 50% 0; + border-radius: 50%; + border: 0 none; + color: #656565; + cursor: pointer; + display: block; + float: right; + font-family: Arial; + font-size: 16px; + height: 14px; + line-height: 16px; + margin: 5px 1px 9px 5px; + padding: 0; + text-align: center; + width: 14px; +} +.ace_searchbtn_close:hover { + background-color: #656565; + background-position: 50% 100%; + color: white; +} +.ace_replacebtn.prev { + width: 54px +} +.ace_replacebtn.next { + width: 27px +} \ No newline at end of file diff --git a/lib/ace/ext/searchbox.js b/lib/ace/ext/searchbox.js index 1df7cd6d..9f62411b 100644 --- a/lib/ace/ext/searchbox.js +++ b/lib/ace/ext/searchbox.js @@ -31,219 +31,219 @@ define(function(require, exports, module) { "use strict"; -var dom = require("ace/lib/dom"); -var event = require("ace/lib/event"); -var Range = require("ace/range").Range; - -exports.SearchBox = function (editor, range, showReplaceForm) { - var _self = this; - var sb = null;//search box - var n = null;//needle - var r = null;//replace - var p = null;//parent DOM node +var dom = require("../lib/dom"); +var lang = require("../lib/lang"); +var event = require("../lib/event"); +var searchboxCss = require("../requirejs/text!./searchbox.css"); +var HashHandler = require("../keyboard/hash_handler").HashHandler; +var keyUtil = require("../lib/keys"); - var needle = editor.session.getTextRange(range); +dom.importCssString(searchboxCss, "ace_searchbox"); + +var html = ''.replace(/>\s+/g, ">"); + +var SearchBox = function(editor, range, showReplaceForm) { + var div = dom.createElement("div"); + div.innerHTML = html; + this.element = div.firstChild; + + this.$init(); + this.setEditor(editor); +}; + +(function() { + this.setEditor = function(editor) { + editor.searchBox = this; + editor.container.appendChild(this.element); + this.editor = editor; + }; this.$init = function() { + var sb = this.element; - p = editor.container.parentNode; - sb = p.querySelector(".ace_search"); //the complete form + this.searchBox = sb.querySelector(".ace_search_form"); + this.replaceBox = sb.querySelector(".ace_replace_form"); + this.searchInput = this.searchBox.querySelector(".ace_search_field"); + this.replaceInput = this.replaceBox.querySelector(".ace_search_field"); - if(!sb) { - - //Create the elements - sb = document.createElement("div"); - sb.setAttribute("class", "ace_search right"); - p.appendChild( sb ); + var _this = this; + event.addListener(sb, "mousedown", function(e) { + setTimeout(function(){ + _this.activeInput.focus(); + }, 0); + event.stopPropagation(e); + }); + event.addListener(sb, "click", function(e) { + var t = e.target; + var action = t.getAttribute("action"); + if (action && _this[action]) + _this[action](); + event.stopPropagation(e); + }); - var c = document.createElement("button"); //cancel btn - c.setAttribute("type","button"); - c.setAttribute("class","ace_searchbtn_close"); - sb.appendChild( c ); - c.onclick = _self.hide; - - var sf = document.createElement("div"); //search form - sf.setAttribute("id", "ace_search_form"); - sf.setAttribute("class", "ace_search_form"); - sb.appendChild( sf ); - - n = document.createElement("input"); //needle - n.setAttribute("class", "ace_search_field"); - n.setAttribute("id", "ace_search_field"); - n.setAttribute("placeholder", "Search for"); - n.setAttribute("type", "text"); - sf.appendChild( n ); - - var sp = document.createElement("button"); - sp.setAttribute("type","button"); - sp.setAttribute("class","ace_searchbtn prev"); - sf.appendChild( sp ); - sp.onclick = _self.findPrev; - - var sn = document.createElement("button"); - sn.setAttribute("type","button"); - sn.setAttribute("class","ace_searchbtn next"); - sf.appendChild( sn ); - sn.onclick = _self.findNext; - - var rf = document.createElement("div"); //replace form - rf.setAttribute("id", "ace_replace_form"); - rf.setAttribute("class", "ace_search_form"); - rf.setAttribute("style", "display: none"); - sb.appendChild( rf); - - r = document.createElement("input"); //needle - r.setAttribute("class", "ace_search_field"); - r.setAttribute("id", "ace_replace_field"); - r.setAttribute("placeholder", "Replace with"); - r.setAttribute("type", "text"); - rf.appendChild( r ); - - var rr = document.createElement("button"); - rr.setAttribute("type","button"); - rr.setAttribute("class","ace_replacebtn prev"); - rr.innerHTML = "Replace"; - rf.appendChild( rr ); - rr.onclick = _self.replace; - - var ra = document.createElement("button"); - ra.setAttribute("type","button"); - ra.setAttribute("class","ace_replacebtn next"); - ra.innerHTML = "All"; - rf.appendChild( ra ); - ra.onclick = _self.replaceAll; - - - } else { - sb.removeAttribute("style"); - n = sb.querySelector("#ace_search_field"); - r = sb.querySelector("#ace_replace_field"); - } - - console.log( "showReplaceForm", showReplaceForm ); - if( showReplaceForm ) { - p.querySelector("#ace_replace_form").removeAttribute("style"); - } - - - if( needle ) - n.value = needle; - - //set initial focus and select text in input - n.focus(); - n.select(); - - //keybinging outsite of the searchbox - _self.$searchKeybingin = { - handleKeyboard: function(data, hashId, keyString, keyCode) { - console.log("MERGE"); - if (keyString == "esc") - return {command: this.command}; - }, - command: { - exec: function(editor) { - _self.hide(); - } + event.addCommandKeyListener(sb, function(e, hashId, keyCode) { + var keyString = keyUtil.keyCodeToString(keyCode); + var command = _this.$searchBarKb.findKeyCommand(hashId, keyString); + if (command && command.exec) { + command.exec(_this); + event.stopEvent(e); } - }; - editor.keyBinding.addKeyboardHandler(this.$searchKeybingin); + }); - n.onkeydown = searchonkeydown; - r.onkeydown = searchonkeydown; - function searchonkeydown(e) { + this.$onChange = lang.delayedCall(function() { + _this.find(false, false); + }); - var cmdKey = e.metaKey || e.ctrlKey; - - console.log(cmdKey); - - if( cmdKey ) { - if( e.which == 70 ) { //f key - var rf = p.querySelector("#ace_replace_form"); - if( e.altKey && rf.hasAttribute("style") ) { - p.querySelector("#ace_replace_form").removeAttribute("style"); - } else { - _self.hide(); - } - return false; - } - } - - if( e.which === 9 ) { //tab - if( e.target == n ) { - r.focus(); - r.select(); - } else { - n.focus(); - n.select(); - } - return false; - } - } - - n.onpaste = function(e) { - //I do this because the onpaste event is fired before the value of the input actually changes - setTimeout(function() { - e.target.onkeyup(); - }, 100); - } - n.onkeyup = function(e){ - console.log("KEYUP", e); - if(!e) { - e = {}; - e.which = 0; - } - - if( e.which === 27 ) { //esc key - _self.hide(); - } else if( e.which === 13 ) { //enter key - _self.findNext( ); - } else { - editor.moveCursorTo( range.start.row, range.start.column ); - _self.findNext(); - } - }; - r.onkeyup = function(e){ - if( e.which === 27 ) { //esc key - _self.hide(); - } else if( e.which === 13 ) { //enter key - editor.moveCursorTo( range.start.row, range.start.column ); - _self.replace(); - } - }; + event.addListener(this.searchInput, "input", function() { + _this.$onChange.schedule(20); + }); + event.addListener(this.searchInput, "focus", function() { + _this.activeInput = _this.searchInput; + }); + event.addListener(this.replaceInput, "focus", function() { + _this.activeInput = _this.replaceInput; + }); }; - this.findPrev = function() { - editor.find(n.value, { - backwards: true, + //keybinging outsite of the searchbox + this.$closeSearchBarKb = new HashHandler([{ + bindKey: "Esc", + name: "closeSearchBar", + exec: function(editor) { + editor.searchBox.hide(); + } + }]); + + //keybinging outsite of the searchbox + this.$searchBarKb = new HashHandler(); + this.$searchBarKb.bindKeys({ + "Ctrl-f|Command-f|Ctrl-H|Command-Option-F": function(sb) { + var isReplace = sb.isReplace = !sb.isReplace; + sb.replaceBox.style.display = isReplace ? "" : "none"; + sb[isReplace ? "replaceInput" : "searchInput"].focus(); + }, + "esc": function(sb) { + setTimeout(function() { sb.hide();}); + }, + "Return": function(sb) { + if (sb.activeInput == sb.replaceInput) + sb.replace(); + sb.findNext(); + }, + "Shift-Return": function(sb) { + if (sb.activeInput == sb.replaceInput) + sb.replace(); + sb.findPrev(); + }, + "Tab": function(sb) { + (sb.activeInput == sb.replaceInput ? sb.searchInput : sb.replaceInput).focus(); + } + }); + + + this.find = function(skipCurrent, backwards) { + this.editor.find(this.searchInput.value, { + skipCurrent: skipCurrent, + backwards: backwards, wrap: true }); + this.editor.session.highlight(this.editor.$search.$options.re); }; this.findNext = function() { - editor.find(n.value, { - backwards: false, - wrap: true - }); + this.find(true, false); + }; + this.findPrev = function() { + this.find(true, true); }; this.replace = function() { - editor.replace( r.value ); - _self.findNext(); + this.editor.replace(this.replaceInput.value); + this.findNext(); }; this.replaceAll = function() { - editor.replaceAll( r.value ); + this.editor.replaceAll(this.replaceInput.value); }; this.hide = function () { - n.value = ""; - r.value = ""; - sb.setAttribute("style", "display: none"); - sb.querySelector("#ace_replace_form").setAttribute("style", "display: none"); - editor.keyBinding.removeKeyboardHandler(this.$searchKeybingin); - editor.focus(); + this.element.style.display = "none"; + this.editor.keyBinding.removeKeyboardHandler(this.$searchKeybingin); + this.editor.focus(); + }; + this.show = function(value, isReplace) { + this.element.style.display = ""; + this.replaceBox.style.display = isReplace ? "" : "none"; + + this.isReplace = isReplace; + + if (value) + this.searchInput.value = value; + this.searchInput.focus(); + this.searchInput.select(); + + this.editor.keyBinding.addKeyboardHandler(this.$closeSearchBarKb); }; - _self.$init(); +}).call(SearchBox.prototype); + +exports.SearchBox = SearchBox; + +exports.Search = function(editor, isReplace) { + var sb = editor.searchBox || new SearchBox(editor); + sb.show(editor.session.getTextRange(), isReplace); }; + +exports.ISearch = function(session, options) { + this.$changeListener = this.$changeListener.bind(this); + this.startRange = session.selection.toOrientedRange(); + this.options = options || {}; +}; + +(function(){ + this.setSession = function(session) { + if (this.session) { + this.session.removeListener(this.$changeListener); + } + this.session = session; + this.session.addListener(this.$changeListener); + }; + this.setSearchString = function() { + + }; + this.getValue = function() { + if (this.value == null) + this.value = this.session.getValue(); + return this.value; + }; + this.$changeListener = function() { + this.value = null; + }; + this.find = function() { + + }; + this.$edgeBefore = function() { + this.cursor = this.startRange[this.options.backwards ? "start" : "end"]; + }; + this.$edgeAfter = function() { + + }; + this.next = function(dir) { + + }; +}).call(exports.ISearch.prototype); + + }); @@ -251,7 +251,7 @@ exports.SearchBox = function (editor, range, showReplaceForm) { * TODO * --------------------------------------------------------------------------------------- */ /* -- move search form to the left if it masks current word +- move search form to the left if it masks current word - includ all options that search has. ex: regex - searchbox.searchbox is not that pretty. we should have just searchbox - disable prev button if it makes sence