1 /* See license.txt for terms of usage */ 2 3 define([ 4 "firebug/lib/object", 5 "firebug/firebug", 6 "firebug/lib/css", 7 "firebug/lib/search", 8 "firebug/lib/system", 9 "firebug/lib/string", 10 "firebug/lib/locale" 11 ], 12 function(Obj, Firebug, Css, Search, System, Str, Locale) { 13 14 // ********************************************************************************************* // 15 // Constants 16 17 const Cc = Components.classes; 18 const Ci = Components.interfaces; 19 20 const searchDelay = 150; 21 22 // ********************************************************************************************* // 23 24 /** 25 * @module Implements basic search box functionality. The box is displayed on the right side 26 * of the Firebug's toolbar. Specific search capabilities depends on the current panel 27 * and implemented in <code>panel.search</code> method. The search-box is automatically 28 * available for panels that have <code>searchable<code> property set to true (set to 29 * false by default). 30 */ 31 Firebug.Search = Obj.extend(Firebug.Module, 32 { 33 dispatchName: "search", 34 35 onSearchCommand: function(document) 36 { 37 var el = document.activeElement; 38 var id = el.id; 39 40 if (id == "fbPanelBar1-browser" || id == "fbPanelBar2-browser") 41 { 42 var sel = el.contentWindow.getSelection().toString(); 43 if (!sel) 44 { 45 var input = el.contentDocument.activeElement; 46 if (input instanceof Ci.nsIDOMNSEditableElement) 47 { 48 sel = input.QueryInterface(Ci.nsIDOMNSEditableElement). 49 editor.selection.toString(); 50 } 51 } 52 53 this.search(sel, Firebug.currentContext); 54 this.focus(); 55 } 56 }, 57 58 search: function(text, context) 59 { 60 var searchBox = Firebug.chrome.$("fbSearchBox"); 61 searchBox.value = text; 62 this.update(context); 63 }, 64 65 searchNext: function(context) 66 { 67 return this.update(context, true, false); 68 }, 69 70 searchPrev: function(context) 71 { 72 return this.update(context, true, true); 73 }, 74 75 displayOnly: function(text, context) 76 { 77 var searchBox = Firebug.chrome.$("fbSearchBox"); 78 79 if (text && text.length > 0) 80 Css.setClass(searchBox, "fbSearchBox-attention"); 81 else 82 Css.removeClass(searchBox, "fbSearchBox-attention"); 83 84 searchBox.value = text; 85 }, 86 87 focus: function(context) 88 { 89 if (Firebug.isDetached()) 90 Firebug.chrome.focus(); 91 else 92 Firebug.toggleBar(true); 93 94 var searchBox = Firebug.chrome.$("fbSearchBox"); 95 searchBox.focus(); 96 searchBox.select(); 97 }, 98 99 update: function(context, immediate, reverse) 100 { 101 var panel = Firebug.chrome.getSelectedPanel(); 102 if (!panel || !panel.searchable) 103 return; 104 105 var searchBox = Firebug.chrome.$("fbSearchBox"); 106 var panelNode = panel.panelNode; 107 108 var value = searchBox.value; 109 110 this.addToHistory(value); 111 112 // This sucks, but the find service won't match nodes that are invisible, so we 113 // have to make sure to make them all visible unless the user is appending to the 114 // last string, in which case it's ok to just search the set of visible nodes 115 if (!panel.searchText || value == panel.searchText || 116 !Str.hasPrefix(value, panel.searchText)) 117 { 118 Css.removeClass(panelNode, "searching"); 119 } 120 121 if (Firebug.Search.isCaseSensitive(value)) 122 Css.setClass(searchBox, "fbSearchBox-autoSensitive"); 123 else 124 Css.removeClass(searchBox, "fbSearchBox-autoSensitive"); 125 126 if (FBTrace.DBG_SEARCH) 127 { 128 FBTrace.sysout("search Firebug.Search.isAutoSensitive(value):" + 129 Firebug.Search.isAutoSensitive(value) + " for " + value, searchBox) 130 } 131 132 // Cancel the previous search to keep typing smooth 133 clearTimeout(panelNode.searchTimeout); 134 135 if (immediate) 136 { 137 var found = panel.search(value, reverse); 138 if (!found && value) 139 this.onNotFound(); 140 141 if (value) 142 { 143 // Hides all nodes that didn't pass the filter 144 Css.setClass(panelNode, "searching"); 145 } 146 else 147 { 148 // Makes all nodes visible again 149 Css.removeClass(panelNode, "searching"); 150 } 151 152 panel.searchText = value; 153 154 return found; 155 } 156 else 157 { 158 // After a delay, perform the search 159 panelNode.searchTimeout = setTimeout(function() 160 { 161 var found = panel.search(value, reverse); 162 if (!found && value) 163 Firebug.Search.onNotFound(value); 164 165 if (value) 166 { 167 // Hides all nodes that didn't pass the filter 168 Css.setClass(panelNode, "searching"); 169 } 170 else 171 { 172 // Makes all nodes visible again 173 Css.removeClass(panelNode, "searching"); 174 } 175 176 panel.searchText = value; 177 searchBox.status = (found ? "found" : "notfound"); 178 179 if (FBTrace.DBG_SEARCH) 180 FBTrace.sysout("search "+searchBox.status+" "+value); 181 }, searchDelay); 182 } 183 }, 184 185 onNotFound: function() 186 { 187 if (this.status != 'notfound') 188 System.beep(); 189 }, 190 191 isCaseSensitive: function(text) 192 { 193 return !!Firebug.searchCaseSensitive || this.isAutoSensitive(text); 194 }, 195 196 isAutoSensitive: function(text) 197 { 198 return (text.toLowerCase() !== text); 199 }, 200 201 getTestingRegex: function(text) 202 { 203 var caseSensitive = Firebug.Search.isCaseSensitive(text); 204 205 try 206 { 207 if (Firebug.searchUseRegularExpression) 208 return new RegExp(text, caseSensitive ? "g" : "gi"); 209 else 210 return new Search.LiteralRegExp(text, false, caseSensitive); 211 } 212 catch (err) 213 { 214 // The user entered an invalid regex. Duck type the regex object 215 // to support literal searches when an invalid regex is entered 216 return new Search.LiteralRegExp(text, false, caseSensitive); 217 } 218 }, 219 220 searchOptionMenu: function(label, option, tooltiptext) 221 { 222 return { 223 label: label, 224 tooltiptext: tooltiptext, 225 checked: Firebug[option], 226 option: option, 227 command: Obj.bindFixed(this.onToggleSearchOption, this, option) 228 }; 229 }, 230 231 onToggleSearchOption: function(option) 232 { 233 Firebug.Options.set(option, !Firebug[option]); 234 235 // Make sure the "Case Sensitive || Case Insensitive" label is updated. 236 this.update(); 237 }, 238 239 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 240 // History 241 242 history: [""], 243 244 addToHistory: function(val) 245 { 246 var history = this.history; 247 248 if (!history[0] || Str.hasPrefix(val, history[0])) 249 history[0] = val; 250 else if (Str.hasPrefix(history[0], val)) 251 return; 252 else 253 history.unshift(val); 254 }, 255 256 cycleHistory: function(dir) 257 { 258 var history = this.history; 259 if (dir > 0) 260 history.unshift(history.pop()); 261 else 262 history.push(history.shift()); 263 264 return history[0] 265 }, 266 267 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 268 // extends Module 269 270 internationalizeUI: function() 271 { 272 var sensitive = Firebug.chrome.$("fbSearchBoxIsSensitive"); 273 sensitive.value = Locale.$STR("search.Case_Sensitive"); 274 sensitive.setAttribute("tooltiptext", Locale.$STR("search.tip.Case_Sensitive")); 275 276 var notSensitive = Firebug.chrome.$("fbSearchBoxIsNotSensitive"); 277 notSensitive.value = Locale.$STR("search.Case_Insensitive"); 278 notSensitive.setAttribute("tooltiptext", Locale.$STR("search.tip.Case_Insensitive")); 279 }, 280 281 shutdown: function() 282 { 283 }, 284 285 showPanel: function(browser, panel) 286 { 287 // Manage visibility of the search-box according to the searchable flag. 288 var searchBox = Firebug.chrome.$("fbSearchBox"); 289 searchBox.status = "noSearch"; 290 Css.removeClass(searchBox, "fbSearchBox-attention"); 291 Css.removeClass(searchBox, "fbSearchBox-autoSensitive"); 292 293 if (panel) 294 { 295 searchBox.collapsed = !panel.searchable; 296 searchBox.updateOptions(panel.getSearchOptionsMenuItems()); 297 } 298 else 299 { 300 searchBox.collapsed = false; 301 } 302 } 303 }); 304 305 // ********************************************************************************************* // 306 // Registration 307 308 Firebug.registerModule(Firebug.Search); 309 310 return Firebug.Search; 311 312 // ********************************************************************************************* // 313 }); 314