| Index: chrome/content/ui/filters-search.js | 
| =================================================================== | 
| --- a/chrome/content/ui/filters-search.js | 
| +++ b/chrome/content/ui/filters-search.js | 
| @@ -16,99 +16,138 @@ | 
| */ | 
| /** | 
| * Implementation of the filter search functionality. | 
| * @class | 
| */ | 
| var FilterSearch = | 
| { | 
| + lastSearchString: null, | 
| + | 
| /** | 
| - * Initializes findbar widget. | 
| + * Handles keypress events on the findbar widget. | 
| */ | 
| - init: function() | 
| + keyPress: function(/**Event*/ event) | 
| { | 
| - let filters = E("filtersTree"); | 
| - for (let prop in FilterSearch.fakeBrowser) | 
| - filters[prop] = FilterSearch.fakeBrowser[prop]; | 
| - Object.defineProperty(filters, "_lastSearchString", { | 
| - get: function() | 
| - { | 
| - return this.finder.searchString; | 
| - }, | 
| - enumerable: true, | 
| - configurable: true | 
| - }); | 
| - | 
| - let findbar = E("findbar"); | 
| - findbar.browser = filters; | 
| - | 
| - findbar.addEventListener("keypress", function(event) | 
| + if (event.keyCode == KeyEvent.DOM_VK_RETURN) | 
| + event.preventDefault(); | 
| + else if (event.keyCode == KeyEvent.DOM_VK_ESCAPE) | 
| { | 
| - // Work-around for bug 490047 | 
| - if (event.keyCode == KeyEvent.DOM_VK_RETURN) | 
| - event.preventDefault(); | 
| - }, false); | 
| - | 
| - // Hack to prevent "highlight all" from getting enabled | 
| - findbar.toggleHighlight = function() {}; | 
| + event.preventDefault(); | 
| + this.close(); | 
| + } | 
| + else if (event.keyCode == KeyEvent.DOM_VK_UP) | 
| + { | 
| + event.preventDefault(); | 
| + this.search(-1); | 
| + } | 
| + else if (event.keyCode == KeyEvent.DOM_VK_DOWN) | 
| + { | 
| + event.preventDefault(); | 
| + this.search(1); | 
| + } | 
| + else if (event.keyCode == KeyEvent.DOM_VK_PAGE_UP) | 
| + { | 
| + event.preventDefault(); | 
| + E("filtersTree").treeBoxObject.scrollByPages(-1); | 
| + } | 
| + else if (event.keyCode == KeyEvent.DOM_VK_PAGE_DOWN) | 
| + { | 
| + event.preventDefault(); | 
| + E("filtersTree").treeBoxObject.scrollByPages(1); | 
| + } | 
| }, | 
| /** | 
| - * Performs a text search. | 
| - * @param {String} text text to be searched | 
| - * @param {Integer} direction search direction: -1 (backwards), 0 (forwards | 
| + * Makes the find bar visible and focuses it. | 
| + */ | 
| + open: function() | 
| + { | 
| + E("findbar").hidden = false; | 
| + E("findbar-textbox").focus(); | 
| + }, | 
| + | 
| + /** | 
| + * Closes the find bar. | 
| + */ | 
| + close: function() | 
| + { | 
| + E("findbar").hidden = true; | 
| + }, | 
| + | 
| + /** | 
| + * Performs a filter search. | 
| + * @param {Integer} [direction] search direction: -1 (backwards), 0 (forwards | 
| * starting with current), 1 (forwards starting with next) | 
| - * @param {Boolean} caseSensitive if true, a case-sensitive search is performed | 
| - * @result {Integer} one of the nsITypeAheadFind constants | 
| */ | 
| - search: function(text, direction, caseSensitive) | 
| + search: function(direction) | 
| { | 
| - function normalizeString(string) caseSensitive ? string : string.toLowerCase(); | 
| + let text = E("findbar-textbox").value.trim(); | 
| + let caseSensitive = E("findbar-case-sensitive").checked; | 
| - function findText(text, direction, startIndex) | 
| + if (typeof direction == "undefined") | 
| + direction = (text == this.lastSearchString ? 1 : 0); | 
| + this.lastSearchString = text; | 
| + | 
| + function normalizeString(string) | 
| + { | 
| + return caseSensitive ? string : string.toLowerCase(); | 
| + } | 
| + | 
| + function findText(startIndex) | 
| { | 
| let list = E("filtersTree"); | 
| let col = list.columns.getNamedColumn("col-filter"); | 
| let count = list.view.rowCount; | 
| for (let i = startIndex + direction; i >= 0 && i < count; i += (direction || 1)) | 
| { | 
| let filter = normalizeString(list.view.getCellText(i, col)); | 
| if (filter.indexOf(text) >= 0) | 
| { | 
| FilterView.selectRow(i); | 
| return true; | 
| } | 
| } | 
| return false; | 
| } | 
| + function setStatus(currentStatus) | 
| + { | 
| + for (let status of ["wrappedStart", "wrappedEnd", "notFound"]) | 
| + E("findbar-status-" + status).hidden = status != currentStatus; | 
| + 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.
 
 | 
| + } | 
| + | 
| + if (!text) | 
| + 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!
 
 | 
| + | 
| text = normalizeString(text); | 
| // First try to find the entry in the current list | 
| - if (findText(text, direction, E("filtersTree").currentIndex)) | 
| - return Ci.nsITypeAheadFind.FIND_FOUND; | 
| + if (findText(E("filtersTree").currentIndex)) | 
| + return setStatus(""); | 
| // Now go through the other subscriptions | 
| - let result = Ci.nsITypeAheadFind.FIND_FOUND; | 
| + let result = ""; | 
| let subscriptions = FilterStorage.subscriptions.slice(); | 
| subscriptions.sort((s1, s2) => (s1 instanceof SpecialSubscription) - (s2 instanceof SpecialSubscription)); | 
| let current = subscriptions.indexOf(FilterView.subscription); | 
| direction = direction || 1; | 
| for (let i = current + direction; ; i+= direction) | 
| { | 
| if (i < 0) | 
| { | 
| i = subscriptions.length - 1; | 
| - result = Ci.nsITypeAheadFind.FIND_WRAPPED; | 
| + result = "wrappedStart"; | 
| } | 
| else if (i >= subscriptions.length) | 
| { | 
| i = 0; | 
| - result = Ci.nsITypeAheadFind.FIND_WRAPPED; | 
| + result = "wrappedEnd"; | 
| } | 
| if (i == current) | 
| break; | 
| let subscription = subscriptions[i]; | 
| for (let j = 0; j < subscription.filters.length; j++) | 
| { | 
| let filter = normalizeString(subscription.filters[j].text); | 
| @@ -125,156 +164,22 @@ var FilterSearch = | 
| list.ensureElementIsVisible(node); | 
| list.selectItem(node); | 
| if (oldFocus) | 
| { | 
| oldFocus.focus(); | 
| Utils.runAsync(() => oldFocus.focus()); | 
| } | 
| - Utils.runAsync(() => findText(text, direction, direction == 1 ? -1 : subscription.filters.length)); | 
| - return result; | 
| + Utils.runAsync(() => findText(direction == 1 ? -1 : subscription.filters.length)); | 
| + return setStatus(result); | 
| } | 
| } | 
| } | 
| - return Ci.nsITypeAheadFind.FIND_NOTFOUND; | 
| + return setStatus("notFound"); | 
| } | 
| }; | 
| -/** | 
| - * Fake browser implementation to make findbar widget happy - searches in | 
| - * the filter list. | 
| - */ | 
| -FilterSearch.fakeBrowser = | 
| +window.addEventListener("load", event => | 
| { | 
| - finder: | 
| - { | 
| - _resultListeners: [], | 
| - searchString: null, | 
| - caseSensitive: false, | 
| - lastResult: null, | 
| - | 
| - _notifyResultListeners: function(result, findBackwards) | 
| - { | 
| - this.lastResult = result; | 
| - for (let listener of this._resultListeners) | 
| - { | 
| - // See https://bugzilla.mozilla.org/show_bug.cgi?id=958101, starting | 
| - // with Gecko 29 only one parameter is expected. | 
| - try | 
| - { | 
| - if (listener.onFindResult.length == 1) | 
| - { | 
| - listener.onFindResult({searchString: this.searchString, | 
| - result: result, findBackwards: findBackwards}); | 
| - } | 
| - else | 
| - listener.onFindResult(result, findBackwards); | 
| - } | 
| - catch (e) | 
| - { | 
| - Cu.reportError(e); | 
| - } | 
| - } | 
| - }, | 
| - | 
| - fastFind: function(searchString, linksOnly, drawOutline) | 
| - { | 
| - this.searchString = searchString; | 
| - let result = FilterSearch.search(this.searchString, 0, | 
| - this.caseSensitive); | 
| - this._notifyResultListeners(result, false); | 
| - }, | 
| - | 
| - findAgain: function(findBackwards, linksOnly, drawOutline) | 
| - { | 
| - let result = FilterSearch.search(this.searchString, | 
| - findBackwards ? -1 : 1, | 
| - this.caseSensitive); | 
| - this._notifyResultListeners(result, findBackwards); | 
| - }, | 
| - | 
| - addResultListener: function(listener) | 
| - { | 
| - if (this._resultListeners.indexOf(listener) === -1) | 
| - this._resultListeners.push(listener); | 
| - }, | 
| - | 
| - removeResultListener: function(listener) | 
| - { | 
| - let index = this._resultListeners.indexOf(listener); | 
| - if (index !== -1) | 
| - this._resultListeners.splice(index, 1); | 
| - }, | 
| - | 
| - getInitialSelection: function() | 
| - { | 
| - for (let listener of this._resultListeners) | 
| - listener.onCurrentSelection(null, true); | 
| - }, | 
| - | 
| - // Irrelevant for us | 
| - requestMatchesCount: function(searchString, matchLimit, linksOnly) {}, | 
| - highlight: function(highlight, word) {}, | 
| - enableSelection: function() {}, | 
| - removeSelection: function() {}, | 
| - focusContent: function() {}, | 
| - keyPress: function() {} | 
| - }, | 
| - | 
| - currentURI: Utils.makeURI("http://example.com/"), | 
| - contentWindow: | 
| - { | 
| - focus: function() | 
| - { | 
| - E("filtersTree").focus(); | 
| - }, | 
| - scrollByLines: function(num) | 
| - { | 
| - E("filtersTree").boxObject.scrollByLines(num); | 
| - }, | 
| - scrollByPages: function(num) | 
| - { | 
| - E("filtersTree").boxObject.scrollByPages(num); | 
| - }, | 
| - }, | 
| - | 
| - messageManager: | 
| - { | 
| - _messageMap: { | 
| - "Findbar:Mouseup": "mouseup", | 
| - "Findbar:Keypress": "keypress" | 
| - }, | 
| - | 
| - _messageFromEvent: function(event) | 
| - { | 
| - for (let message in this._messageMap) | 
| - if (this._messageMap[message] == event.type) | 
| - return {target: event.currentTarget, name: message, data: event}; | 
| - return null; | 
| - }, | 
| - | 
| - addMessageListener: function(message, listener) | 
| - { | 
| - if (!this._messageMap.hasOwnProperty(message)) | 
| - return; | 
| - | 
| - if (!("_ABPHandler" in listener)) | 
| - listener._ABPHandler = (event) => listener.receiveMessage(this._messageFromEvent(event)); | 
| - | 
| - E("filtersTree").addEventListener(this._messageMap[message], listener._ABPHandler, false); | 
| - }, | 
| - | 
| - removeMessageListener: function(message, listener) | 
| - { | 
| - if (this._messageMap.hasOwnProperty(message) && listener._ABPHandler) | 
| - E("filtersTree").removeEventListener(this._messageMap[message], listener._ABPHandler, false); | 
| - }, | 
| - | 
| - sendAsyncMessage: function() {} | 
| - } | 
| -}; | 
| - | 
| -window.addEventListener("load", function() | 
| -{ | 
| - FilterSearch.init(); | 
| -}, false); | 
| + E("findbar").setAttribute("data-os", Services.appinfo.OS.toLowerCase()); | 
| +}); |