Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | // Define search commands. Depends on dialog.js or another |
michael@0 | 2 | // implementation of the openDialog method. |
michael@0 | 3 | |
michael@0 | 4 | // Replace works a little oddly -- it will do the replace on the next |
michael@0 | 5 | // Ctrl-G (or whatever is bound to findNext) press. You prevent a |
michael@0 | 6 | // replace by making sure the match is no longer selected when hitting |
michael@0 | 7 | // Ctrl-G. |
michael@0 | 8 | |
michael@0 | 9 | (function(mod) { |
michael@0 | 10 | if (typeof exports == "object" && typeof module == "object") // CommonJS |
michael@0 | 11 | mod(require("../../lib/codemirror"), require("./searchcursor"), require("../dialog/dialog")); |
michael@0 | 12 | else if (typeof define == "function" && define.amd) // AMD |
michael@0 | 13 | define(["../../lib/codemirror", "./searchcursor", "../dialog/dialog"], mod); |
michael@0 | 14 | else // Plain browser env |
michael@0 | 15 | mod(CodeMirror); |
michael@0 | 16 | })(function(CodeMirror) { |
michael@0 | 17 | "use strict"; |
michael@0 | 18 | function searchOverlay(query, caseInsensitive) { |
michael@0 | 19 | var startChar; |
michael@0 | 20 | if (typeof query == "string") { |
michael@0 | 21 | startChar = query.charAt(0); |
michael@0 | 22 | query = new RegExp("^" + query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"), |
michael@0 | 23 | caseInsensitive ? "i" : ""); |
michael@0 | 24 | } else { |
michael@0 | 25 | query = new RegExp("^(?:" + query.source + ")", query.ignoreCase ? "i" : ""); |
michael@0 | 26 | } |
michael@0 | 27 | return {token: function(stream) { |
michael@0 | 28 | if (stream.match(query)) return "searching"; |
michael@0 | 29 | while (!stream.eol()) { |
michael@0 | 30 | stream.next(); |
michael@0 | 31 | if (startChar && !caseInsensitive) |
michael@0 | 32 | stream.skipTo(startChar) || stream.skipToEnd(); |
michael@0 | 33 | if (stream.match(query, false)) break; |
michael@0 | 34 | } |
michael@0 | 35 | }}; |
michael@0 | 36 | } |
michael@0 | 37 | |
michael@0 | 38 | function SearchState() { |
michael@0 | 39 | this.posFrom = this.posTo = this.query = null; |
michael@0 | 40 | this.overlay = null; |
michael@0 | 41 | } |
michael@0 | 42 | function getSearchState(cm) { |
michael@0 | 43 | return cm.state.search || (cm.state.search = new SearchState()); |
michael@0 | 44 | } |
michael@0 | 45 | function queryCaseInsensitive(query) { |
michael@0 | 46 | return typeof query == "string" && query == query.toLowerCase(); |
michael@0 | 47 | } |
michael@0 | 48 | function getSearchCursor(cm, query, pos) { |
michael@0 | 49 | // Heuristic: if the query string is all lowercase, do a case insensitive search. |
michael@0 | 50 | return cm.getSearchCursor(query, pos, queryCaseInsensitive(query)); |
michael@0 | 51 | } |
michael@0 | 52 | function dialog(cm, text, shortText, deflt, f) { |
michael@0 | 53 | if (cm.openDialog) cm.openDialog(text, f, {value: deflt}); |
michael@0 | 54 | else f(prompt(shortText, deflt)); |
michael@0 | 55 | } |
michael@0 | 56 | function confirmDialog(cm, text, shortText, fs) { |
michael@0 | 57 | if (cm.openConfirm) cm.openConfirm(text, fs); |
michael@0 | 58 | else if (confirm(shortText)) fs[0](); |
michael@0 | 59 | } |
michael@0 | 60 | function parseQuery(query) { |
michael@0 | 61 | var isRE = query.match(/^\/(.*)\/([a-z]*)$/); |
michael@0 | 62 | if (isRE) { |
michael@0 | 63 | query = new RegExp(isRE[1], isRE[2].indexOf("i") == -1 ? "" : "i"); |
michael@0 | 64 | if (query.test("")) query = /x^/; |
michael@0 | 65 | } else if (query == "") { |
michael@0 | 66 | query = /x^/; |
michael@0 | 67 | } |
michael@0 | 68 | return query; |
michael@0 | 69 | } |
michael@0 | 70 | var queryDialog; |
michael@0 | 71 | function doSearch(cm, rev) { |
michael@0 | 72 | if (!queryDialog) { |
michael@0 | 73 | let doc = cm.getWrapperElement().ownerDocument; |
michael@0 | 74 | let inp = doc.createElement("input"); |
michael@0 | 75 | let txt = doc.createTextNode(cm.l10n("findCmd.promptMessage")); |
michael@0 | 76 | |
michael@0 | 77 | inp.type = "text"; |
michael@0 | 78 | inp.style.width = "10em"; |
michael@0 | 79 | inp.style.MozMarginStart = "1em"; |
michael@0 | 80 | |
michael@0 | 81 | queryDialog = doc.createElement("div"); |
michael@0 | 82 | queryDialog.appendChild(txt); |
michael@0 | 83 | queryDialog.appendChild(inp); |
michael@0 | 84 | } |
michael@0 | 85 | var state = getSearchState(cm); |
michael@0 | 86 | if (state.query) return findNext(cm, rev); |
michael@0 | 87 | dialog(cm, queryDialog, "Search for:", cm.getSelection(), function(query) { |
michael@0 | 88 | cm.operation(function() { |
michael@0 | 89 | if (!query || state.query) return; |
michael@0 | 90 | state.query = parseQuery(query); |
michael@0 | 91 | cm.removeOverlay(state.overlay, queryCaseInsensitive(state.query)); |
michael@0 | 92 | state.overlay = searchOverlay(state.query, queryCaseInsensitive(state.query)); |
michael@0 | 93 | cm.addOverlay(state.overlay); |
michael@0 | 94 | state.posFrom = state.posTo = cm.getCursor(); |
michael@0 | 95 | findNext(cm, rev); |
michael@0 | 96 | }); |
michael@0 | 97 | }); |
michael@0 | 98 | } |
michael@0 | 99 | function findNext(cm, rev) {cm.operation(function() { |
michael@0 | 100 | var state = getSearchState(cm); |
michael@0 | 101 | var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo); |
michael@0 | 102 | if (!cursor.find(rev)) { |
michael@0 | 103 | cursor = getSearchCursor(cm, state.query, rev ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(cm.firstLine(), 0)); |
michael@0 | 104 | if (!cursor.find(rev)) return; |
michael@0 | 105 | } |
michael@0 | 106 | cm.setSelection(cursor.from(), cursor.to()); |
michael@0 | 107 | cm.scrollIntoView({from: cursor.from(), to: cursor.to()}); |
michael@0 | 108 | state.posFrom = cursor.from(); state.posTo = cursor.to(); |
michael@0 | 109 | });} |
michael@0 | 110 | function clearSearch(cm) {cm.operation(function() { |
michael@0 | 111 | var state = getSearchState(cm); |
michael@0 | 112 | if (!state.query) return; |
michael@0 | 113 | state.query = null; |
michael@0 | 114 | cm.removeOverlay(state.overlay); |
michael@0 | 115 | });} |
michael@0 | 116 | |
michael@0 | 117 | var replaceQueryDialog = |
michael@0 | 118 | 'Replace: <input type="text" style="width: 10em"/> <span style="color: #888">(Use /re/ syntax for regexp search)</span>'; |
michael@0 | 119 | var replacementQueryDialog = 'With: <input type="text" style="width: 10em"/>'; |
michael@0 | 120 | var doReplaceConfirm = "Replace? <button>Yes</button> <button>No</button> <button>Stop</button>"; |
michael@0 | 121 | function replace(cm, all) { |
michael@0 | 122 | dialog(cm, replaceQueryDialog, "Replace:", cm.getSelection(), function(query) { |
michael@0 | 123 | if (!query) return; |
michael@0 | 124 | query = parseQuery(query); |
michael@0 | 125 | dialog(cm, replacementQueryDialog, "Replace with:", "", function(text) { |
michael@0 | 126 | if (all) { |
michael@0 | 127 | cm.operation(function() { |
michael@0 | 128 | for (var cursor = getSearchCursor(cm, query); cursor.findNext();) { |
michael@0 | 129 | if (typeof query != "string") { |
michael@0 | 130 | var match = cm.getRange(cursor.from(), cursor.to()).match(query); |
michael@0 | 131 | cursor.replace(text.replace(/\$(\d)/g, function(_, i) {return match[i];})); |
michael@0 | 132 | } else cursor.replace(text); |
michael@0 | 133 | } |
michael@0 | 134 | }); |
michael@0 | 135 | } else { |
michael@0 | 136 | clearSearch(cm); |
michael@0 | 137 | var cursor = getSearchCursor(cm, query, cm.getCursor()); |
michael@0 | 138 | var advance = function() { |
michael@0 | 139 | var start = cursor.from(), match; |
michael@0 | 140 | if (!(match = cursor.findNext())) { |
michael@0 | 141 | cursor = getSearchCursor(cm, query); |
michael@0 | 142 | if (!(match = cursor.findNext()) || |
michael@0 | 143 | (start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return; |
michael@0 | 144 | } |
michael@0 | 145 | cm.setSelection(cursor.from(), cursor.to()); |
michael@0 | 146 | cm.scrollIntoView({from: cursor.from(), to: cursor.to()}); |
michael@0 | 147 | confirmDialog(cm, doReplaceConfirm, "Replace?", |
michael@0 | 148 | [function() {doReplace(match);}, advance]); |
michael@0 | 149 | }; |
michael@0 | 150 | var doReplace = function(match) { |
michael@0 | 151 | cursor.replace(typeof query == "string" ? text : |
michael@0 | 152 | text.replace(/\$(\d)/g, function(_, i) {return match[i];})); |
michael@0 | 153 | advance(); |
michael@0 | 154 | }; |
michael@0 | 155 | advance(); |
michael@0 | 156 | } |
michael@0 | 157 | }); |
michael@0 | 158 | }); |
michael@0 | 159 | } |
michael@0 | 160 | |
michael@0 | 161 | CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);}; |
michael@0 | 162 | CodeMirror.commands.findNext = doSearch; |
michael@0 | 163 | CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);}; |
michael@0 | 164 | CodeMirror.commands.clearSearch = clearSearch; |
michael@0 | 165 | CodeMirror.commands.replace = replace; |
michael@0 | 166 | CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);}; |
michael@0 | 167 | }); |