| OLD | NEW | 
|---|
| 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 |  | 
| 58    * @param {Integer} direction  search direction: -1 (backwards), 0 (forwards |  | 
| 59    *                  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    */ | 62    */ | 
| 63   search: function(text, direction, caseSensitive) | 63   open: function() | 
| 64   { | 64   { | 
| 65     function normalizeString(string) caseSensitive ? string : string.toLowerCase
     (); | 65     E("findbar").hidden = false; | 
|  | 66     E("findbar-textbox").focus(); | 
|  | 67   }, | 
| 66 | 68 | 
| 67     function findText(text, direction, startIndex) | 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] | 
|  | 80    *   See @link{FilterSearch#search} | 
|  | 81    * @return {String} | 
|  | 82    *   result status, one of "" (success), "notFound", "wrappedEnd", | 
|  | 83    *   "wrappedStart" | 
|  | 84    */ | 
|  | 85   _search: function(direction) | 
|  | 86   { | 
|  | 87     let text = E("findbar-textbox").value.trim(); | 
|  | 88     if (!text) | 
|  | 89       return ""; | 
|  | 90 | 
|  | 91     let caseSensitive = E("findbar-case-sensitive").checked; | 
|  | 92 | 
|  | 93     if (typeof direction == "undefined") | 
|  | 94       direction = (text == this.lastSearchString ? 1 : 0); | 
|  | 95     this.lastSearchString = text; | 
|  | 96 | 
|  | 97     function normalizeString(string) | 
|  | 98     { | 
|  | 99       return caseSensitive ? string : string.toLowerCase(); | 
|  | 100     } | 
|  | 101 | 
|  | 102     function findText(startIndex) | 
| 68     { | 103     { | 
| 69       let list = E("filtersTree"); | 104       let list = E("filtersTree"); | 
| 70       let col = list.columns.getNamedColumn("col-filter"); | 105       let col = list.columns.getNamedColumn("col-filter"); | 
| 71       let count = list.view.rowCount; | 106       let count = list.view.rowCount; | 
| 72       for (let i = startIndex + direction; i >= 0 && i < count; i += (direction 
     || 1)) | 107       for (let i = startIndex + direction; i >= 0 && i < count; i += (direction 
     || 1)) | 
| 73       { | 108       { | 
| 74         let filter = normalizeString(list.view.getCellText(i, col)); | 109         let filter = normalizeString(list.view.getCellText(i, col)); | 
| 75         if (filter.indexOf(text) >= 0) | 110         if (filter.indexOf(text) >= 0) | 
| 76         { | 111         { | 
| 77           FilterView.selectRow(i); | 112           FilterView.selectRow(i); | 
| 78           return true; | 113           return true; | 
| 79         } | 114         } | 
| 80       } | 115       } | 
| 81       return false; | 116       return false; | 
| 82     } | 117     } | 
| 83 | 118 | 
| 84     text = normalizeString(text); | 119     text = normalizeString(text); | 
| 85 | 120 | 
| 86     // First try to find the entry in the current list | 121     // First try to find the entry in the current list | 
| 87     if (findText(text, direction, E("filtersTree").currentIndex)) | 122     if (findText(E("filtersTree").currentIndex)) | 
| 88       return Ci.nsITypeAheadFind.FIND_FOUND; | 123       return ""; | 
| 89 | 124 | 
| 90     // Now go through the other subscriptions | 125     // Now go through the other subscriptions | 
| 91     let result = Ci.nsITypeAheadFind.FIND_FOUND; | 126     let result = ""; | 
| 92     let subscriptions = FilterStorage.subscriptions.slice(); | 127     let subscriptions = FilterStorage.subscriptions.slice(); | 
| 93     subscriptions.sort((s1, s2) => (s1 instanceof SpecialSubscription) - (s2 ins
     tanceof SpecialSubscription)); | 128     subscriptions.sort((s1, s2) => (s1 instanceof SpecialSubscription) - (s2 ins
     tanceof SpecialSubscription)); | 
| 94     let current = subscriptions.indexOf(FilterView.subscription); | 129     let current = subscriptions.indexOf(FilterView.subscription); | 
| 95     direction = direction || 1; | 130     direction = direction || 1; | 
| 96     for (let i = current + direction; ; i+= direction) | 131     for (let i = current + direction; ; i+= direction) | 
| 97     { | 132     { | 
| 98       if (i < 0) | 133       if (i < 0) | 
| 99       { | 134       { | 
| 100         i = subscriptions.length - 1; | 135         i = subscriptions.length - 1; | 
| 101         result = Ci.nsITypeAheadFind.FIND_WRAPPED; | 136         result = "wrappedStart"; | 
| 102       } | 137       } | 
| 103       else if (i >= subscriptions.length) | 138       else if (i >= subscriptions.length) | 
| 104       { | 139       { | 
| 105         i = 0; | 140         i = 0; | 
| 106         result = Ci.nsITypeAheadFind.FIND_WRAPPED; | 141         result = "wrappedEnd"; | 
| 107       } | 142       } | 
| 108       if (i == current) | 143       if (i == current) | 
| 109         break; | 144         break; | 
| 110 | 145 | 
| 111       let subscription = subscriptions[i]; | 146       let subscription = subscriptions[i]; | 
| 112       for (let j = 0; j < subscription.filters.length; j++) | 147       for (let j = 0; j < subscription.filters.length; j++) | 
| 113       { | 148       { | 
| 114         let filter = normalizeString(subscription.filters[j].text); | 149         let filter = normalizeString(subscription.filters[j].text); | 
| 115         if (filter.indexOf(text) >= 0) | 150         if (filter.indexOf(text) >= 0) | 
| 116         { | 151         { | 
| 117           let list = E(subscription instanceof SpecialSubscription ? "groups" : 
     "subscriptions"); | 152           let list = E(subscription instanceof SpecialSubscription ? "groups" : 
     "subscriptions"); | 
| 118           let node = Templater.getNodeForData(list, "subscription", subscription
     ); | 153           let node = Templater.getNodeForData(list, "subscription", subscription
     ); | 
| 119           if (!node) | 154           if (!node) | 
| 120             break; | 155             break; | 
| 121 | 156 | 
| 122           // Select subscription in its list and restore focus after that | 157           // Select subscription in its list and restore focus after that | 
| 123           let oldFocus = document.commandDispatcher.focusedElement; | 158           let oldFocus = document.commandDispatcher.focusedElement; | 
| 124           E("tabs").selectedIndex = (subscription instanceof SpecialSubscription
      ? 1 : 0); | 159           E("tabs").selectedIndex = (subscription instanceof SpecialSubscription
      ? 1 : 0); | 
| 125           list.ensureElementIsVisible(node); | 160           list.ensureElementIsVisible(node); | 
| 126           list.selectItem(node); | 161           list.selectItem(node); | 
| 127           if (oldFocus) | 162           if (oldFocus) | 
| 128           { | 163           { | 
| 129             oldFocus.focus(); | 164             oldFocus.focus(); | 
| 130             Utils.runAsync(() => oldFocus.focus()); | 165             Utils.runAsync(() => oldFocus.focus()); | 
| 131           } | 166           } | 
| 132 | 167 | 
| 133           Utils.runAsync(() => findText(text, direction, direction == 1 ? -1 :  
     subscription.filters.length)); | 168           Utils.runAsync(() => findText(direction == 1 ? -1 :  subscription.filt
     ers.length)); | 
| 134           return result; | 169           return result; | 
| 135         } | 170         } | 
| 136       } | 171       } | 
| 137     } | 172     } | 
| 138 | 173 | 
| 139     return Ci.nsITypeAheadFind.FIND_NOTFOUND; | 174     return "notFound"; | 
|  | 175   }, | 
|  | 176 | 
|  | 177   /** | 
|  | 178    * Performs a filter search and displays the resulting search status. | 
|  | 179    * @param {Integer} [direction] | 
|  | 180    *   search direction: -1 (backwards), 0 (forwards starting with current), | 
|  | 181    *   1 (forwards starting with next) | 
|  | 182    */ | 
|  | 183   search: function(direction) | 
|  | 184   { | 
|  | 185     E("findbar").setAttribute("data-status", this._search(direction)); | 
| 140   } | 186   } | 
| 141 }; | 187 }; | 
| 142 | 188 | 
| 143 /** | 189 window.addEventListener("load", event => | 
| 144  * Fake browser implementation to make findbar widget happy - searches in |  | 
| 145  * the filter list. |  | 
| 146  */ |  | 
| 147 FilterSearch.fakeBrowser = |  | 
| 148 { | 190 { | 
| 149   finder: | 191   E("findbar").setAttribute("data-os", Services.appinfo.OS.toLowerCase()); | 
| 150   { | 192 }); | 
| 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); |  | 
| OLD | NEW | 
|---|