Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: chrome/content/ui/filters-search.js

Issue 29356477: Issue 4510 - Filter preferences: replace the built-in findbar widget by our own look-alike (Closed) Base URL: https://hg.adblockplus.org/adblockplus
Patch Set: Removed outdated CSS rule Created Oct. 11, 2016, 9:21 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * This file is part of Adblock Plus <https://adblockplus.org/>, 2 * This file is part of Adblock Plus <https://adblockplus.org/>,
3 * Copyright (C) 2006-2016 Eyeo GmbH 3 * Copyright (C) 2006-2016 Eyeo GmbH
4 * 4 *
5 * Adblock Plus is free software: you can redistribute it and/or modify 5 * Adblock Plus is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as 6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation. 7 * published by the Free Software Foundation.
8 * 8 *
9 * Adblock Plus is distributed in the hope that it will be useful, 9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details. 12 * GNU General Public License for more details.
13 * 13 *
14 * You should have received a copy of the GNU General Public License 14 * You should have received a copy of the GNU General Public License
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
16 */ 16 */
17 17
18 /** 18 /**
19 * Implementation of the filter search functionality. 19 * Implementation of the filter search functionality.
20 * @class 20 * @class
21 */ 21 */
22 var FilterSearch = 22 var FilterSearch =
23 { 23 {
24 lastSearchString: null,
25
24 /** 26 /**
25 * Initializes findbar widget. 27 * Handles keypress events on the findbar widget.
26 */ 28 */
27 init: function() 29 keyPress: function(/**Event*/ event)
28 { 30 {
29 let filters = E("filtersTree"); 31 if (event.keyCode == KeyEvent.DOM_VK_RETURN)
30 for (let prop in FilterSearch.fakeBrowser) 32 event.preventDefault();
31 filters[prop] = FilterSearch.fakeBrowser[prop]; 33 else if (event.keyCode == KeyEvent.DOM_VK_ESCAPE)
32 Object.defineProperty(filters, "_lastSearchString", {
33 get: function()
34 {
35 return this.finder.searchString;
36 },
37 enumerable: true,
38 configurable: true
39 });
40
41 let findbar = E("findbar");
42 findbar.browser = filters;
43
44 findbar.addEventListener("keypress", function(event)
45 { 34 {
46 // Work-around for bug 490047 35 event.preventDefault();
47 if (event.keyCode == KeyEvent.DOM_VK_RETURN) 36 this.close();
48 event.preventDefault(); 37 }
49 }, false); 38 else if (event.keyCode == KeyEvent.DOM_VK_UP)
50 39 {
51 // Hack to prevent "highlight all" from getting enabled 40 event.preventDefault();
52 findbar.toggleHighlight = function() {}; 41 this.search(-1);
42 }
43 else if (event.keyCode == KeyEvent.DOM_VK_DOWN)
44 {
45 event.preventDefault();
46 this.search(1);
47 }
48 else if (event.keyCode == KeyEvent.DOM_VK_PAGE_UP)
49 {
50 event.preventDefault();
51 E("filtersTree").treeBoxObject.scrollByPages(-1);
52 }
53 else if (event.keyCode == KeyEvent.DOM_VK_PAGE_DOWN)
54 {
55 event.preventDefault();
56 E("filtersTree").treeBoxObject.scrollByPages(1);
57 }
53 }, 58 },
54 59
55 /** 60 /**
56 * Performs a text search. 61 * Makes the find bar visible and focuses it.
57 * @param {String} text text to be searched 62 */
58 * @param {Integer} direction search direction: -1 (backwards), 0 (forwards 63 open: function()
64 {
65 E("findbar").hidden = false;
66 E("findbar-textbox").focus();
67 },
68
69 /**
70 * Closes the find bar.
71 */
72 close: function()
73 {
74 E("findbar").hidden = true;
75 },
76
77 /**
78 * Performs a filter search.
79 * @param {Integer} [direction] search direction: -1 (backwards), 0 (forwards
59 * starting with current), 1 (forwards starting with next) 80 * starting with current), 1 (forwards starting with next)
60 * @param {Boolean} caseSensitive if true, a case-sensitive search is perform ed
61 * @result {Integer} one of the nsITypeAheadFind constants
62 */ 81 */
63 search: function(text, direction, caseSensitive) 82 search: function(direction)
64 { 83 {
65 function normalizeString(string) caseSensitive ? string : string.toLowerCase (); 84 let text = E("findbar-textbox").value.trim();
85 let caseSensitive = E("findbar-case-sensitive").checked;
66 86
67 function findText(text, direction, startIndex) 87 if (typeof direction == "undefined")
88 direction = (text == this.lastSearchString ? 1 : 0);
89 this.lastSearchString = text;
90
91 function normalizeString(string)
92 {
93 return caseSensitive ? string : string.toLowerCase();
94 }
95
96 function findText(startIndex)
68 { 97 {
69 let list = E("filtersTree"); 98 let list = E("filtersTree");
70 let col = list.columns.getNamedColumn("col-filter"); 99 let col = list.columns.getNamedColumn("col-filter");
71 let count = list.view.rowCount; 100 let count = list.view.rowCount;
72 for (let i = startIndex + direction; i >= 0 && i < count; i += (direction || 1)) 101 for (let i = startIndex + direction; i >= 0 && i < count; i += (direction || 1))
73 { 102 {
74 let filter = normalizeString(list.view.getCellText(i, col)); 103 let filter = normalizeString(list.view.getCellText(i, col));
75 if (filter.indexOf(text) >= 0) 104 if (filter.indexOf(text) >= 0)
76 { 105 {
77 FilterView.selectRow(i); 106 FilterView.selectRow(i);
78 return true; 107 return true;
79 } 108 }
80 } 109 }
81 return false; 110 return false;
82 } 111 }
83 112
113 function setStatus(currentStatus)
114 {
115 for (let status of ["wrappedStart", "wrappedEnd", "notFound"])
116 E("findbar-status-" + status).hidden = status != currentStatus;
117 E("findbar-textbox").setAttribute("status", currentStatus);
Thomas Greiner 2016/10/12 14:03:13 Usually, what we do is set a "data-*" attribute on
Wladimir Palant 2016/10/12 15:22:00 Done.
118 }
119
120 if (!text)
121 return setStatus("");
Thomas Greiner 2016/10/12 14:03:13 Detail: `setStatus()` doesn't have a return value
Wladimir Palant 2016/10/12 15:22:00 Actually, I wanted to make sure that each return s
Thomas Greiner 2016/10/12 17:18:10 Thanks!
122
84 text = normalizeString(text); 123 text = normalizeString(text);
85 124
86 // First try to find the entry in the current list 125 // First try to find the entry in the current list
87 if (findText(text, direction, E("filtersTree").currentIndex)) 126 if (findText(E("filtersTree").currentIndex))
88 return Ci.nsITypeAheadFind.FIND_FOUND; 127 return setStatus("");
89 128
90 // Now go through the other subscriptions 129 // Now go through the other subscriptions
91 let result = Ci.nsITypeAheadFind.FIND_FOUND; 130 let result = "";
92 let subscriptions = FilterStorage.subscriptions.slice(); 131 let subscriptions = FilterStorage.subscriptions.slice();
93 subscriptions.sort((s1, s2) => (s1 instanceof SpecialSubscription) - (s2 ins tanceof SpecialSubscription)); 132 subscriptions.sort((s1, s2) => (s1 instanceof SpecialSubscription) - (s2 ins tanceof SpecialSubscription));
94 let current = subscriptions.indexOf(FilterView.subscription); 133 let current = subscriptions.indexOf(FilterView.subscription);
95 direction = direction || 1; 134 direction = direction || 1;
96 for (let i = current + direction; ; i+= direction) 135 for (let i = current + direction; ; i+= direction)
97 { 136 {
98 if (i < 0) 137 if (i < 0)
99 { 138 {
100 i = subscriptions.length - 1; 139 i = subscriptions.length - 1;
101 result = Ci.nsITypeAheadFind.FIND_WRAPPED; 140 result = "wrappedStart";
102 } 141 }
103 else if (i >= subscriptions.length) 142 else if (i >= subscriptions.length)
104 { 143 {
105 i = 0; 144 i = 0;
106 result = Ci.nsITypeAheadFind.FIND_WRAPPED; 145 result = "wrappedEnd";
107 } 146 }
108 if (i == current) 147 if (i == current)
109 break; 148 break;
110 149
111 let subscription = subscriptions[i]; 150 let subscription = subscriptions[i];
112 for (let j = 0; j < subscription.filters.length; j++) 151 for (let j = 0; j < subscription.filters.length; j++)
113 { 152 {
114 let filter = normalizeString(subscription.filters[j].text); 153 let filter = normalizeString(subscription.filters[j].text);
115 if (filter.indexOf(text) >= 0) 154 if (filter.indexOf(text) >= 0)
116 { 155 {
117 let list = E(subscription instanceof SpecialSubscription ? "groups" : "subscriptions"); 156 let list = E(subscription instanceof SpecialSubscription ? "groups" : "subscriptions");
118 let node = Templater.getNodeForData(list, "subscription", subscription ); 157 let node = Templater.getNodeForData(list, "subscription", subscription );
119 if (!node) 158 if (!node)
120 break; 159 break;
121 160
122 // Select subscription in its list and restore focus after that 161 // Select subscription in its list and restore focus after that
123 let oldFocus = document.commandDispatcher.focusedElement; 162 let oldFocus = document.commandDispatcher.focusedElement;
124 E("tabs").selectedIndex = (subscription instanceof SpecialSubscription ? 1 : 0); 163 E("tabs").selectedIndex = (subscription instanceof SpecialSubscription ? 1 : 0);
125 list.ensureElementIsVisible(node); 164 list.ensureElementIsVisible(node);
126 list.selectItem(node); 165 list.selectItem(node);
127 if (oldFocus) 166 if (oldFocus)
128 { 167 {
129 oldFocus.focus(); 168 oldFocus.focus();
130 Utils.runAsync(() => oldFocus.focus()); 169 Utils.runAsync(() => oldFocus.focus());
131 } 170 }
132 171
133 Utils.runAsync(() => findText(text, direction, direction == 1 ? -1 : subscription.filters.length)); 172 Utils.runAsync(() => findText(direction == 1 ? -1 : subscription.filt ers.length));
134 return result; 173 return setStatus(result);
135 } 174 }
136 } 175 }
137 } 176 }
138 177
139 return Ci.nsITypeAheadFind.FIND_NOTFOUND; 178 return setStatus("notFound");
140 } 179 }
141 }; 180 };
142 181
143 /** 182 window.addEventListener("load", event =>
144 * Fake browser implementation to make findbar widget happy - searches in
145 * the filter list.
146 */
147 FilterSearch.fakeBrowser =
148 { 183 {
149 finder: 184 E("findbar").setAttribute("data-os", Services.appinfo.OS.toLowerCase());
150 { 185 });
151 _resultListeners: [],
152 searchString: null,
153 caseSensitive: false,
154 lastResult: null,
155
156 _notifyResultListeners: function(result, findBackwards)
157 {
158 this.lastResult = result;
159 for (let listener of this._resultListeners)
160 {
161 // See https://bugzilla.mozilla.org/show_bug.cgi?id=958101, starting
162 // with Gecko 29 only one parameter is expected.
163 try
164 {
165 if (listener.onFindResult.length == 1)
166 {
167 listener.onFindResult({searchString: this.searchString,
168 result: result, findBackwards: findBackwards});
169 }
170 else
171 listener.onFindResult(result, findBackwards);
172 }
173 catch (e)
174 {
175 Cu.reportError(e);
176 }
177 }
178 },
179
180 fastFind: function(searchString, linksOnly, drawOutline)
181 {
182 this.searchString = searchString;
183 let result = FilterSearch.search(this.searchString, 0,
184 this.caseSensitive);
185 this._notifyResultListeners(result, false);
186 },
187
188 findAgain: function(findBackwards, linksOnly, drawOutline)
189 {
190 let result = FilterSearch.search(this.searchString,
191 findBackwards ? -1 : 1,
192 this.caseSensitive);
193 this._notifyResultListeners(result, findBackwards);
194 },
195
196 addResultListener: function(listener)
197 {
198 if (this._resultListeners.indexOf(listener) === -1)
199 this._resultListeners.push(listener);
200 },
201
202 removeResultListener: function(listener)
203 {
204 let index = this._resultListeners.indexOf(listener);
205 if (index !== -1)
206 this._resultListeners.splice(index, 1);
207 },
208
209 getInitialSelection: function()
210 {
211 for (let listener of this._resultListeners)
212 listener.onCurrentSelection(null, true);
213 },
214
215 // Irrelevant for us
216 requestMatchesCount: function(searchString, matchLimit, linksOnly) {},
217 highlight: function(highlight, word) {},
218 enableSelection: function() {},
219 removeSelection: function() {},
220 focusContent: function() {},
221 keyPress: function() {}
222 },
223
224 currentURI: Utils.makeURI("http://example.com/"),
225 contentWindow:
226 {
227 focus: function()
228 {
229 E("filtersTree").focus();
230 },
231 scrollByLines: function(num)
232 {
233 E("filtersTree").boxObject.scrollByLines(num);
234 },
235 scrollByPages: function(num)
236 {
237 E("filtersTree").boxObject.scrollByPages(num);
238 },
239 },
240
241 messageManager:
242 {
243 _messageMap: {
244 "Findbar:Mouseup": "mouseup",
245 "Findbar:Keypress": "keypress"
246 },
247
248 _messageFromEvent: function(event)
249 {
250 for (let message in this._messageMap)
251 if (this._messageMap[message] == event.type)
252 return {target: event.currentTarget, name: message, data: event};
253 return null;
254 },
255
256 addMessageListener: function(message, listener)
257 {
258 if (!this._messageMap.hasOwnProperty(message))
259 return;
260
261 if (!("_ABPHandler" in listener))
262 listener._ABPHandler = (event) => listener.receiveMessage(this._messageF romEvent(event));
263
264 E("filtersTree").addEventListener(this._messageMap[message], listener._ABP Handler, false);
265 },
266
267 removeMessageListener: function(message, listener)
268 {
269 if (this._messageMap.hasOwnProperty(message) && listener._ABPHandler)
270 E("filtersTree").removeEventListener(this._messageMap[message], listener ._ABPHandler, false);
271 },
272
273 sendAsyncMessage: function() {}
274 }
275 };
276
277 window.addEventListener("load", function()
278 {
279 FilterSearch.init();
280 }, false);
OLDNEW

Powered by Google App Engine
This is Rietveld