From b1f9b957555679254d31a19ce57e07789bffdc6a Mon Sep 17 00:00:00 2001 From: nightwing Date: Wed, 12 Dec 2012 00:05:59 +0400 Subject: [PATCH] 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