1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/xpfe/components/autocomplete/resources/content/autocomplete.xml Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1649 @@ 1.4 +<?xml version="1.0"?> 1.5 +<!-- This Source Code Form is subject to the terms of the Mozilla Public 1.6 + - License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> 1.8 + 1.9 + 1.10 +<bindings id="autocompleteBindings" 1.11 + xmlns="http://www.mozilla.org/xbl" 1.12 + xmlns:html="http://www.w3.org/1999/xhtml" 1.13 + xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" 1.14 + xmlns:xbl="http://www.mozilla.org/xbl"> 1.15 + 1.16 + <binding id="autocomplete" role="xul:combobox" 1.17 + extends="chrome://global/content/bindings/textbox.xml#textbox"> 1.18 + <resources> 1.19 + <stylesheet src="chrome://global/content/autocomplete.css"/> 1.20 + <stylesheet src="chrome://global/skin/autocomplete.css"/> 1.21 + </resources> 1.22 + 1.23 + <content> 1.24 + <children includes="menupopup"/> 1.25 + 1.26 + <xul:hbox class="autocomplete-textbox-container" flex="1" align="center"> 1.27 + <children includes="image|deck|stack|box"> 1.28 + <xul:image class="autocomplete-icon" allowevents="true"/> 1.29 + </children> 1.30 + 1.31 + <xul:hbox class="textbox-input-box" flex="1" xbl:inherits="context,tooltiptext=inputtooltiptext"> 1.32 + <children/> 1.33 + <html:input anonid="input" class="autocomplete-textbox textbox-input" 1.34 + allowevents="true" 1.35 + xbl:inherits="tooltiptext=inputtooltiptext,value,type,maxlength,disabled,size,readonly,placeholder,tabindex,accesskey,mozactionhint,userAction"/> 1.36 + </xul:hbox> 1.37 + <children includes="hbox"/> 1.38 + </xul:hbox> 1.39 + 1.40 + <xul:dropmarker class="autocomplete-history-dropmarker" allowevents="true" 1.41 + xbl:inherits="open,enablehistory" anonid="historydropmarker"/> 1.42 + 1.43 + <xul:popupset> 1.44 + <xul:panel type="autocomplete" anonid="popup" 1.45 + ignorekeys="true" noautofocus="true" level="top" 1.46 + xbl:inherits="for=id,nomatch"/> 1.47 + </xul:popupset> 1.48 + </content> 1.49 + 1.50 + <implementation implements="nsIDOMXULMenuListElement"> 1.51 + 1.52 + <constructor><![CDATA[ 1.53 + // XXX bug 90337 band-aid until we figure out what's going on here 1.54 + if (this.value != this.mInputElt.value) 1.55 + this.mInputElt.value = this.value; 1.56 + delete this.value; 1.57 + 1.58 + // listen for pastes 1.59 + this.mInputElt.controllers.insertControllerAt(0, this.mPasteController); 1.60 + 1.61 + // listen for menubar activation 1.62 + window.top.addEventListener("DOMMenuBarActive", this.mMenuBarListener, true); 1.63 + 1.64 + // set default property values 1.65 + this.ifSetAttribute("timeout", 50); 1.66 + this.ifSetAttribute("pastetimeout", 1000); 1.67 + this.ifSetAttribute("maxrows", 5); 1.68 + this.ifSetAttribute("showpopup", true); 1.69 + this.ifSetAttribute("disableKeyNavigation", true); 1.70 + 1.71 + // initialize the search sessions 1.72 + if (this.hasAttribute("autocompletesearch")) 1.73 + this.initAutoCompleteSearch(); 1.74 + 1.75 + // hack to work around lack of bottom-up constructor calling 1.76 + if ("initialize" in this.popup) 1.77 + this.popup.initialize(); 1.78 + ]]></constructor> 1.79 + 1.80 + <destructor><![CDATA[ 1.81 + this.clearResults(false); 1.82 + window.top.removeEventListener("DOMMenuBarActive", this.mMenuBarListener, true); 1.83 + this.mInputElt.controllers.removeController(this.mPasteController); 1.84 + ]]></destructor> 1.85 + 1.86 + <!-- =================== nsIAutoCompleteInput =================== --> 1.87 + <!-- XXX: This implementation is currently incomplete. --> 1.88 + 1.89 + <!-- reference to the results popup element --> 1.90 + <field name="popup"><![CDATA[ 1.91 + document.getAnonymousElementByAttribute(this, "anonid", "popup"); 1.92 + ]]></field> 1.93 + 1.94 + <property name="popupOpen" 1.95 + onget="return this.mMenuOpen;" 1.96 + onset="if (val) this.openPopup(); else this.closePopup(); return val;"/> 1.97 + 1.98 + <!-- option to turn off autocomplete --> 1.99 + <property name="disableAutoComplete" 1.100 + onset="this.setAttribute('disableautocomplete', val); return val;" 1.101 + onget="return this.getAttribute('disableautocomplete') == 'true';"/> 1.102 + 1.103 + <!-- if the resulting match string is not at the beginning of the typed string, 1.104 + this will optionally autofill like this "bar |>> foobar|" --> 1.105 + <property name="completeDefaultIndex" 1.106 + onset="this.setAttribute('completedefaultindex', val); return val;" 1.107 + onget="return this.getAttribute('completedefaultindex') == 'true';"/> 1.108 + 1.109 + <!-- option for completing to the default result whenever the user hits 1.110 + enter or the textbox loses focus --> 1.111 + <property name="forceComplete" 1.112 + onset="this.setAttribute('forcecomplete', val); return val;" 1.113 + onget="return this.getAttribute('forcecomplete') == 'true';"/> 1.114 + 1.115 + <property name="minResultsForPopup" 1.116 + onset="this.setAttribute('minresultsforpopup', val); return val;" 1.117 + onget="var t = this.getAttribute('minresultsforpopup'); return t ? parseInt(t) : 1;"/> 1.118 + 1.119 + <!-- maximum number of rows to display --> 1.120 + <property name="maxRows" 1.121 + onset="this.setAttribute('maxrows', val); return val;" 1.122 + onget="return parseInt(this.getAttribute('maxrows')) || 0;"/> 1.123 + 1.124 + <!-- toggles a second column in the results list which contains 1.125 + the string in the comment field of each autocomplete result --> 1.126 + <property name="showCommentColumn" 1.127 + onget="return this.getAttribute('showcommentcolumn') == 'true';"> 1.128 + <setter><![CDATA[ 1.129 + this.popup.showCommentColumn = val; 1.130 + this.setAttribute('showcommentcolumn', val); 1.131 + return val; 1.132 + ]]></setter> 1.133 + </property> 1.134 + 1.135 + <!-- number of milliseconds after a keystroke before a search begins --> 1.136 + <property name="timeout" 1.137 + onset="this.setAttribute('timeout', val); return val;" 1.138 + onget="return parseInt(this.getAttribute('timeout')) || 0;"/> 1.139 + 1.140 + <property name="searchParam" 1.141 + onget="return this.getAttribute('autocompletesearchparam') || '';" 1.142 + onset="this.setAttribute('autocompletesearchparam', val); return val;"/> 1.143 + 1.144 + <property name="searchCount" readonly="true" 1.145 + onget="return this.sessionCount;"/> 1.146 + 1.147 + <method name="getSearchAt"> 1.148 + <parameter name="aIndex"/> 1.149 + <body><![CDATA[ 1.150 + var idx = -1; 1.151 + for (var name in this.mSessions) 1.152 + if (++idx == aIndex) 1.153 + return name; 1.154 + 1.155 + return null; 1.156 + ]]></body> 1.157 + </method> 1.158 + 1.159 + <property name="textValue" 1.160 + onget="return this.value;" 1.161 + onset="this.setTextValue(val); return val;"/> 1.162 + 1.163 + <method name="onSearchBegin"> 1.164 + <body><![CDATA[ 1.165 + this._fireEvent("searchbegin"); 1.166 + ]]></body> 1.167 + </method> 1.168 + 1.169 + <method name="onSearchComplete"> 1.170 + <body><![CDATA[ 1.171 + if (this.noMatch) 1.172 + this.setAttribute("nomatch", "true"); 1.173 + else 1.174 + this.removeAttribute("nomatch"); 1.175 + 1.176 + this._fireEvent("searchcomplete"); 1.177 + ]]></body> 1.178 + </method> 1.179 + 1.180 + <method name="onTextReverted"> 1.181 + <body><![CDATA[ 1.182 + return this._fireEvent("textreverted"); 1.183 + ]]></body> 1.184 + </method> 1.185 + 1.186 + <!-- =================== nsIDOMXULMenuListElement =================== --> 1.187 + 1.188 + <property name="editable" readonly="true" 1.189 + onget="return true;" /> 1.190 + 1.191 + <property name="crop" 1.192 + onset="this.setAttribute('crop', val); return val;" 1.193 + onget="return this.getAttribute('crop');"/> 1.194 + 1.195 + <property name="label" readonly="true" 1.196 + onget="return this.mInputElt.value;"/> 1.197 + 1.198 + <property name="open" 1.199 + onget="return this.getAttribute('open') == 'true';"> 1.200 + <setter> 1.201 + <![CDATA[ 1.202 + var historyPopup = document.getAnonymousElementByAttribute(this, "anonid", "historydropmarker"); 1.203 + if (val) { 1.204 + this.setAttribute('open', true); 1.205 + historyPopup.showPopup(); 1.206 + } else { 1.207 + this.removeAttribute('open'); 1.208 + historyPopup.hidePopup(); 1.209 + } 1.210 + ]]> 1.211 + </setter> 1.212 + </property> 1.213 + 1.214 + <!-- =================== PUBLIC PROPERTIES =================== --> 1.215 + 1.216 + <property name="value" 1.217 + onget="return this.mInputElt.value;"> 1.218 + <setter><![CDATA[ 1.219 + this.ignoreInputEvent = true; 1.220 + this.mInputElt.value = val; 1.221 + this.ignoreInputEvent = false; 1.222 + var event = document.createEvent('Events'); 1.223 + event.initEvent('ValueChange', true, true); 1.224 + this.mInputElt.dispatchEvent(event); 1.225 + return val; 1.226 + ]]></setter> 1.227 + </property> 1.228 + 1.229 + <property name="focused" 1.230 + onget="return this.getAttribute('focused') == 'true';"/> 1.231 + 1.232 + <method name="initAutoCompleteSearch"> 1.233 + <body><![CDATA[ 1.234 + var list = this.getAttribute("autocompletesearch").split(" "); 1.235 + for (var i = 0; i < list.length; i++) { 1.236 + var name = list[i]; 1.237 + var contractid = "@mozilla.org/autocomplete/search;1?name=" + name; 1.238 + if (contractid in Components.classes) { 1.239 + try { 1.240 + this.mSessions[name] = 1.241 + Components.classes[contractid].getService(Components.interfaces.nsIAutoCompleteSearch); 1.242 + this.mLastResults[name] = null; 1.243 + this.mLastRows[name] = 0; 1.244 + ++this.sessionCount; 1.245 + } catch (e) { 1.246 + dump("### ERROR - unable to create search \"" + name + "\".\n"); 1.247 + } 1.248 + } else { 1.249 + dump("search \"" + name + "\" not found - skipping.\n"); 1.250 + } 1.251 + } 1.252 + ]]></body> 1.253 + </method> 1.254 + 1.255 + <!-- the number of sessions currently in use --> 1.256 + <field name="sessionCount">0</field> 1.257 + 1.258 + <!-- number of milliseconds after a paste before a search begins --> 1.259 + <property name="pasteTimeout" 1.260 + onset="this.setAttribute('pastetimeout', val); return val;" 1.261 + onget="var t = parseInt(this.getAttribute('pastetimeout')); return t ? t : 0;"/> 1.262 + 1.263 + <!-- option for filling the textbox with the best match while typing 1.264 + and selecting the difference --> 1.265 + <property name="autoFill" 1.266 + onset="this.setAttribute('autofill', val); return val;" 1.267 + onget="return this.getAttribute('autofill') == 'true';"/> 1.268 + 1.269 + <!-- if this attribute is set, allow different style for 1.270 + non auto-completed lines --> 1.271 + <property name="highlightNonMatches" 1.272 + onset="this.setAttribute('highlightnonmatches', val); return val;" 1.273 + onget="return this.getAttribute('highlightnonmatches') == 'true';"/> 1.274 + 1.275 + <!-- option to show the popup containing the results --> 1.276 + <property name="showPopup" 1.277 + onset="this.setAttribute('showpopup', val); return val;" 1.278 + onget="return this.getAttribute('showpopup') == 'true';"/> 1.279 + 1.280 + <!-- option to allow scrolling through the list via the tab key, rather than 1.281 + tab moving focus out of the textbox --> 1.282 + <property name="tabScrolling" 1.283 + onset="return this.setAttribute('tabscrolling', val); return val;" 1.284 + onget="return this.getAttribute('tabscrolling') == 'true';"/> 1.285 + 1.286 + <!-- option to completely ignore any blur events while 1.287 + searches are still going on. This is useful so that nothing 1.288 + gets autopicked if the window is required to lose focus for 1.289 + some reason (eg in LDAP autocomplete, another window may be 1.290 + brought up so that the user can enter a password to authenticate 1.291 + to an LDAP server). --> 1.292 + <property name="ignoreBlurWhileSearching" 1.293 + onset="this.setAttribute('ignoreblurwhilesearching', val); return val;" 1.294 + onget="return this.getAttribute('ignoreblurwhilesearching') == 'true';"/> 1.295 + 1.296 + <!-- state which indicates the current action being performed by the user. 1.297 + Possible values are : none, typing, scrolling --> 1.298 + <property name="userAction" 1.299 + onset="this.setAttribute('userAction', val); return val;" 1.300 + onget="return this.getAttribute('userAction');"/> 1.301 + 1.302 + <!-- state which indicates if the last search had no matches --> 1.303 + <field name="noMatch">true</field> 1.304 + 1.305 + <!-- state which indicates a search is currently happening --> 1.306 + <field name="isSearching">false</field> 1.307 + 1.308 + <!-- state which indicates a search timeout is current waiting --> 1.309 + <property name="isWaiting" 1.310 + onget="return this.mAutoCompleteTimer != 0;"/> 1.311 + 1.312 + <!-- =================== PRIVATE PROPERTIES =================== --> 1.313 + 1.314 + <field name="mSessions">({})</field> 1.315 + <field name="mLastResults">({})</field> 1.316 + <field name="mLastRows">({})</field> 1.317 + <field name="mLastKeyCode">null</field> 1.318 + <field name="mAutoCompleteTimer">0</field> 1.319 + <field name="mMenuOpen">false</field> 1.320 + <field name="mFireAfterSearch">false</field> 1.321 + <field name="mFinishAfterSearch">false</field> 1.322 + <field name="mNeedToFinish">false</field> 1.323 + <field name="mNeedToComplete">false</field> 1.324 + <field name="mTransientValue">false</field> 1.325 + <field name="mView">null</field> 1.326 + <field name="currentSearchString">""</field> 1.327 + <field name="ignoreInputEvent">false</field> 1.328 + <field name="oninit">null</field> 1.329 + <field name="mDefaultMatchFilled">false</field> 1.330 + <field name="mFirstReturn">true</field> 1.331 + <field name="mIsPasting">false</field> 1.332 + 1.333 + <field name="mPasteController"><![CDATA[ 1.334 + ({ 1.335 + self: this, 1.336 + kGlobalClipboard: Components.interfaces.nsIClipboard.kGlobalClipboard, 1.337 + supportsCommand: function(aCommand) { 1.338 + return aCommand == "cmd_paste"; 1.339 + }, 1.340 + isCommandEnabled: function(aCommand) { 1.341 + return aCommand == "cmd_paste" && 1.342 + this.self.editor.isSelectionEditable && 1.343 + this.self.editor.canPaste(this.kGlobalClipboard); 1.344 + }, 1.345 + doCommand: function(aCommand) { 1.346 + if (aCommand == "cmd_paste") { 1.347 + this.self.mIsPasting = true; 1.348 + this.self.editor.paste(this.kGlobalClipboard); 1.349 + this.self.mIsPasting = false; 1.350 + } 1.351 + }, 1.352 + onEvent: function() {} 1.353 + }) 1.354 + ]]></field> 1.355 + 1.356 + <field name="mMenuBarListener"><![CDATA[ 1.357 + ({ 1.358 + self: this, 1.359 + handleEvent: function(aEvent) { 1.360 + try { 1.361 + this.self.finishAutoComplete(false, false, aEvent); 1.362 + this.self.clearTimer(); 1.363 + this.self.closePopup(); 1.364 + } catch (e) { 1.365 + window.top.removeEventListener("DOMMenuBarActive", this, true); 1.366 + } 1.367 + } 1.368 + }) 1.369 + ]]></field> 1.370 + 1.371 + <field name="mAutoCompleteObserver"><![CDATA[ 1.372 + ({ 1.373 + self: this, 1.374 + onSearchResult: function(aSearch, aResult) { 1.375 + for (var name in this.self.mSessions) 1.376 + if (this.self.mSessions[name] == aSearch) 1.377 + this.self.processResults(name, aResult); 1.378 + } 1.379 + }) 1.380 + ]]></field> 1.381 + 1.382 + <field name="mInputElt"><![CDATA[ 1.383 + document.getAnonymousElementByAttribute(this, "anonid", "input"); 1.384 + ]]></field> 1.385 + 1.386 + <field name="mMenuAccessKey"><![CDATA[ 1.387 + Components.classes["@mozilla.org/preferences-service;1"] 1.388 + .getService(Components.interfaces.nsIPrefBranch) 1.389 + .getIntPref("ui.key.menuAccessKey"); 1.390 + ]]></field> 1.391 + 1.392 + <!-- =================== PUBLIC METHODS =================== --> 1.393 + 1.394 + <method name="getErrorAt"> 1.395 + <parameter name="aIndex"/> 1.396 + <body><![CDATA[ 1.397 + var obj = aIndex < 0 ? null : this.convertIndexToSession(aIndex); 1.398 + return obj && this.mLastResults[obj.session] && 1.399 + this.mLastResults[obj.session].errorDescription; 1.400 + ]]></body> 1.401 + </method> 1.402 + 1.403 + <!-- get a value from the autocomplete results as a string via an absolute index--> 1.404 + <method name="getResultValueAt"> 1.405 + <parameter name="aIndex"/> 1.406 + <body><![CDATA[ 1.407 + var obj = this.convertIndexToSession(aIndex); 1.408 + return obj ? this.getSessionValueAt(obj.session, obj.index) : null; 1.409 + ]]></body> 1.410 + </method> 1.411 + 1.412 + <!-- get a value from the autocomplete results as a string from a specific session --> 1.413 + <method name="getSessionValueAt"> 1.414 + <parameter name="aSession"/> 1.415 + <parameter name="aIndex"/> 1.416 + <body><![CDATA[ 1.417 + var result = this.mLastResults[aSession]; 1.418 + return result.errorDescription || result.getValueAt(aIndex); 1.419 + ]]></body> 1.420 + </method> 1.421 + 1.422 + <!-- get the total number of results overall --> 1.423 + <method name="getResultCount"> 1.424 + <body><![CDATA[ 1.425 + return this.view.rowCount; 1.426 + ]]></body> 1.427 + </method> 1.428 + 1.429 + <!-- get the first session that has results --> 1.430 + <method name="getDefaultSession"> 1.431 + <body><![CDATA[ 1.432 + for (var name in this.mLastResults) { 1.433 + var results = this.mLastResults[name]; 1.434 + if (results && results.matchCount > 0 && !results.errorDescription) 1.435 + return name; 1.436 + } 1.437 + return null; 1.438 + ]]></body> 1.439 + </method> 1.440 + 1.441 + <!-- empty the cached result data and empty the results popup --> 1.442 + <method name="clearResults"> 1.443 + <parameter name="aInvalidate"/> 1.444 + <body><![CDATA[ 1.445 + this.clearResultData(); 1.446 + this.clearResultElements(aInvalidate); 1.447 + ]]></body> 1.448 + </method> 1.449 + 1.450 + <!-- =================== PRIVATE METHODS =================== --> 1.451 + 1.452 + <!-- ::::::::::::: session searching ::::::::::::: --> 1.453 + 1.454 + <!-- --> 1.455 + <method name="callListener"> 1.456 + <parameter name="me"/> 1.457 + <parameter name="aAction"/> 1.458 + <body><![CDATA[ 1.459 + // bail if the binding was detached or the element removed from 1.460 + // document during the timeout 1.461 + if (!("startLookup" in me) || !me.ownerDocument || !me.parentNode) 1.462 + return; 1.463 + 1.464 + me.clearTimer(); 1.465 + 1.466 + if (me.disableAutoComplete) 1.467 + return; 1.468 + 1.469 + switch (aAction) { 1.470 + case "startLookup": 1.471 + me.startLookup(); 1.472 + break; 1.473 + 1.474 + case "stopLookup": 1.475 + me.stopLookup(); 1.476 + break; 1.477 + } 1.478 + ]]></body> 1.479 + </method> 1.480 + 1.481 + <!-- --> 1.482 + <method name="startLookup"> 1.483 + <body><![CDATA[ 1.484 + var str = this.currentSearchString; 1.485 + if (!str) { 1.486 + this.clearResults(false); 1.487 + this.closePopup(); 1.488 + return; 1.489 + } 1.490 + 1.491 + this.isSearching = true; 1.492 + this.mFirstReturn = true; 1.493 + this.mSessionReturns = this.sessionCount; 1.494 + this.mFailureItems = 0; 1.495 + this.mDefaultMatchFilled = false; // clear out our prefill state. 1.496 + 1.497 + // Notify the input that the search is beginning. 1.498 + this.onSearchBegin(); 1.499 + 1.500 + // tell each session to start searching... 1.501 + for (var name in this.mSessions) 1.502 + try { 1.503 + this.mSessions[name].startSearch(str, this.searchParam, this.mLastResults[name], this.mAutoCompleteObserver); 1.504 + } catch (e) { 1.505 + --this.mSessionReturns; 1.506 + this.searchFailed(); 1.507 + } 1.508 + ]]></body> 1.509 + </method> 1.510 + 1.511 + <!-- --> 1.512 + <method name="stopLookup"> 1.513 + <body><![CDATA[ 1.514 + for (var name in this.mSessions) 1.515 + this.mSessions[name].stopSearch(); 1.516 + ]]></body> 1.517 + </method> 1.518 + 1.519 + <!-- --> 1.520 + <method name="processResults"> 1.521 + <parameter name="aSessionName"/> 1.522 + <parameter name="aResults"/> 1.523 + <body><![CDATA[ 1.524 + if (this.disableAutoComplete) 1.525 + return; 1.526 + 1.527 + const ACR = Components.interfaces.nsIAutoCompleteResult; 1.528 + var status = aResults.searchResult; 1.529 + if (status != ACR.RESULT_NOMATCH_ONGOING && 1.530 + status != ACR.RESULT_SUCCESS_ONGOING) 1.531 + --this.mSessionReturns; 1.532 + 1.533 + // check the many criteria for failure 1.534 + if (aResults.errorDescription) 1.535 + ++this.mFailureItems; 1.536 + else if (status == ACR.RESULT_IGNORED || 1.537 + status == ACR.RESULT_FAILURE || 1.538 + status == ACR.RESULT_NOMATCH || 1.539 + status == ACR.RESULT_NOMATCH_ONGOING || 1.540 + aResults.matchCount == 0 || 1.541 + aResults.searchString != this.currentSearchString) 1.542 + { 1.543 + this.mLastResults[aSessionName] = null; 1.544 + if (this.mFirstReturn) 1.545 + this.clearResultElements(false); 1.546 + this.mFirstReturn = false; 1.547 + this.searchFailed(); 1.548 + return; 1.549 + } 1.550 + 1.551 + if (this.mFirstReturn) { 1.552 + if (this.view.mTree) 1.553 + this.view.mTree.beginUpdateBatch(); 1.554 + this.clearResultElements(false); // clear results, but don't repaint yet 1.555 + } 1.556 + 1.557 + // always call openPopup...we may not have opened it 1.558 + // if a previous search session didn't return enough search results. 1.559 + // it's smart and doesn't try to open itself multiple times... 1.560 + // be sure to add our result elements before calling openResultPopuup as we need 1.561 + // to know the total # of results found so far. 1.562 + this.addResultElements(aSessionName, aResults); 1.563 + 1.564 + this.autoFillInput(aSessionName, aResults, false); 1.565 + if (this.mFirstReturn && this.view.mTree) 1.566 + this.view.mTree.endUpdateBatch(); 1.567 + this.openPopup(); 1.568 + this.mFirstReturn = false; 1.569 + 1.570 + // if this is the last session to return... 1.571 + if (this.mSessionReturns == 0) 1.572 + this.postSearchCleanup(); 1.573 + 1.574 + if (this.mFinishAfterSearch) 1.575 + this.finishAutoComplete(false, this.mFireAfterSearch, null); 1.576 + ]]></body> 1.577 + </method> 1.578 + 1.579 + <!-- called each time a search fails, except when failure items need 1.580 + to be displayed. If all searches have failed, clear the list 1.581 + and close the popup --> 1.582 + <method name="searchFailed"> 1.583 + <body><![CDATA[ 1.584 + // if all searches are done and they all failed... 1.585 + if (this.mSessionReturns == 0 && this.getResultCount() == 0) { 1.586 + if (this.minResultsForPopup == 0) { 1.587 + this.clearResults(true); // clear data and repaint empty 1.588 + this.openPopup(); 1.589 + } else { 1.590 + this.closePopup(); 1.591 + } 1.592 + } 1.593 + 1.594 + // if it's the last session to return, time to clean up... 1.595 + if (this.mSessionReturns == 0) 1.596 + this.postSearchCleanup(); 1.597 + ]]></body> 1.598 + </method> 1.599 + 1.600 + <!-- does some stuff after a search is done (success or failure) --> 1.601 + <method name="postSearchCleanup"> 1.602 + <body><![CDATA[ 1.603 + this.isSearching = false; 1.604 + 1.605 + // figure out if there are no matches in all search sessions 1.606 + var failed = true; 1.607 + for (var name in this.mSessions) { 1.608 + if (this.mLastResults[name]) 1.609 + failed = this.mLastResults[name].errorDescription || 1.610 + this.mLastResults[name].matchCount == 0; 1.611 + if (!failed) 1.612 + break; 1.613 + } 1.614 + this.noMatch = failed; 1.615 + 1.616 + // if we have processed all of our searches, and none of them gave us a default index, 1.617 + // then we should try to auto fill the input field with the first match. 1.618 + // note: autoFillInput is smart enough to kick out if we've already prefilled something... 1.619 + if (!this.noMatch) { 1.620 + var defaultSession = this.getDefaultSession(); 1.621 + if (defaultSession) 1.622 + this.autoFillInput(defaultSession, this.mLastResults[defaultSession], true); 1.623 + } 1.624 + 1.625 + // Notify the input that the search is complete. 1.626 + this.onSearchComplete(); 1.627 + ]]></body> 1.628 + </method> 1.629 + 1.630 + <!-- when the focus exits the widget or user hits return, 1.631 + determine what value to leave in the textbox --> 1.632 + <method name="finishAutoComplete"> 1.633 + <parameter name="aForceComplete"/> 1.634 + <parameter name="aFireTextCommand"/> 1.635 + <parameter name="aTriggeringEvent"/> 1.636 + <body><![CDATA[ 1.637 + this.mFinishAfterSearch = false; 1.638 + this.mFireAfterSearch = false; 1.639 + if (this.mNeedToFinish && !this.disableAutoComplete) { 1.640 + // set textbox value to either override value, or default search result 1.641 + var val = this.popup.overrideValue; 1.642 + if (val) { 1.643 + this.setTextValue(val); 1.644 + this.mNeedToFinish = false; 1.645 + } else if (this.mTransientValue || 1.646 + !(this.forceComplete || 1.647 + (aForceComplete && 1.648 + this.mDefaultMatchFilled && 1.649 + this.mNeedToComplete))) { 1.650 + this.mNeedToFinish = false; 1.651 + } else if (this.isWaiting) { 1.652 + // if the user typed, the search results are out of date, so let 1.653 + // the search finish, and tell it to come back here when it's done 1.654 + this.mFinishAfterSearch = true; 1.655 + this.mFireAfterSearch = aFireTextCommand; 1.656 + return; 1.657 + } else { 1.658 + // we want to use the default item index for the first session which gave us a valid 1.659 + // default item index... 1.660 + for (var name in this.mLastResults) { 1.661 + var results = this.mLastResults[name]; 1.662 + if (results && results.matchCount > 0 && 1.663 + !results.errorDescription && results.defaultIndex != -1) 1.664 + { 1.665 + val = results.getValueAt(results.defaultIndex); 1.666 + this.setTextValue(val); 1.667 + this.mDefaultMatchFilled = true; 1.668 + this.mNeedToFinish = false; 1.669 + break; 1.670 + } 1.671 + } 1.672 + 1.673 + if (this.mNeedToFinish) { 1.674 + // if a search is happening at this juncture, bail out of this function 1.675 + // and let the search finish, and tell it to come back here when it's done 1.676 + if (this.isSearching) { 1.677 + this.mFinishAfterSearch = true; 1.678 + this.mFireAfterSearch = aFireTextCommand; 1.679 + return; 1.680 + } 1.681 + 1.682 + this.mNeedToFinish = false; 1.683 + var defaultSession = this.getDefaultSession(); 1.684 + if (defaultSession) 1.685 + { 1.686 + // preselect the first one 1.687 + var first = this.getSessionValueAt(defaultSession, 0); 1.688 + this.setTextValue(first); 1.689 + this.mDefaultMatchFilled = true; 1.690 + } 1.691 + } 1.692 + } 1.693 + 1.694 + this.stopLookup(); 1.695 + 1.696 + this.closePopup(); 1.697 + } 1.698 + 1.699 + this.mNeedToComplete = false; 1.700 + this.clearTimer(); 1.701 + 1.702 + if (aFireTextCommand) 1.703 + this._fireEvent("textentered", this.userAction, aTriggeringEvent); 1.704 + ]]></body> 1.705 + </method> 1.706 + 1.707 + <!-- when the user clicks an entry in the autocomplete popup --> 1.708 + <method name="onResultClick"> 1.709 + <body><![CDATA[ 1.710 + // set textbox value to either override value, or the clicked result 1.711 + var errItem = this.getErrorAt(this.popup.selectedIndex); 1.712 + var val = this.popup.overrideValue; 1.713 + if (val) 1.714 + this.setTextValue(val); 1.715 + else if (this.popup.selectedIndex != -1) { 1.716 + if (errItem) { 1.717 + this.setTextValue(this.currentSearchString); 1.718 + this.mTransientValue = true; 1.719 + } else { 1.720 + this.setTextValue(this.getResultValueAt( 1.721 + this.popup.selectedIndex)); 1.722 + } 1.723 + } 1.724 + 1.725 + this.mNeedToFinish = false; 1.726 + this.mNeedToComplete = false; 1.727 + 1.728 + this.closePopup(); 1.729 + 1.730 + this.currentSearchString = ""; 1.731 + 1.732 + if (errItem) 1.733 + this._fireEvent("errorcommand", errItem); 1.734 + this._fireEvent("textentered", "clicking"); 1.735 + ]]></body> 1.736 + </method> 1.737 + 1.738 + <!-- when the user hits escape, revert the previously typed value in the textbox --> 1.739 + <method name="undoAutoComplete"> 1.740 + <body><![CDATA[ 1.741 + var val = this.currentSearchString; 1.742 + 1.743 + var ok = this.onTextReverted(); 1.744 + if ((ok || ok == undefined) && val) 1.745 + this.setTextValue(val); 1.746 + 1.747 + this.userAction = "typing"; 1.748 + 1.749 + this.currentSearchString = this.value; 1.750 + this.mNeedToComplete = false; 1.751 + ]]></body> 1.752 + </method> 1.753 + 1.754 + <!-- convert an absolute result index into a session name/index pair --> 1.755 + <method name="convertIndexToSession"> 1.756 + <parameter name="aIndex"/> 1.757 + <body><![CDATA[ 1.758 + for (var name in this.mLastRows) { 1.759 + if (aIndex < this.mLastRows[name]) 1.760 + return { session: name, index: aIndex }; 1.761 + aIndex -= this.mLastRows[name]; 1.762 + } 1.763 + return null; 1.764 + ]]></body> 1.765 + </method> 1.766 + 1.767 + <!-- ::::::::::::: user input handling ::::::::::::: --> 1.768 + 1.769 + <!-- --> 1.770 + <method name="processInput"> 1.771 + <body><![CDATA[ 1.772 + // stop current lookup in case it's async. 1.773 + this.stopLookup(); 1.774 + // stop the queued up lookup on a timer 1.775 + this.clearTimer(); 1.776 + 1.777 + if (this.disableAutoComplete) 1.778 + return; 1.779 + 1.780 + this.userAction = "typing"; 1.781 + this.mFinishAfterSearch = false; 1.782 + this.mNeedToFinish = true; 1.783 + this.mTransientValue = false; 1.784 + this.mNeedToComplete = true; 1.785 + var str = this.value; 1.786 + this.currentSearchString = str; 1.787 + this.popup.clearSelection(); 1.788 + 1.789 + var timeout = this.mIsPasting ? this.pasteTimeout : this.timeout; 1.790 + this.mAutoCompleteTimer = setTimeout(this.callListener, timeout, this, "startLookup"); 1.791 + ]]></body> 1.792 + </method> 1.793 + 1.794 + <!-- --> 1.795 + <method name="processKeyPress"> 1.796 + <parameter name="aEvent"/> 1.797 + <body><![CDATA[ 1.798 + this.mLastKeyCode = aEvent.keyCode; 1.799 + 1.800 + var killEvent = false; 1.801 + 1.802 + switch (aEvent.keyCode) { 1.803 + case KeyEvent.DOM_VK_TAB: 1.804 + if (this.tabScrolling) { 1.805 + // don't kill this event if alt-tab or ctrl-tab is hit 1.806 + if (!aEvent.altKey && !aEvent.ctrlKey) { 1.807 + killEvent = this.mMenuOpen; 1.808 + if (killEvent) 1.809 + this.keyNavigation(aEvent); 1.810 + } 1.811 + } 1.812 + break; 1.813 + 1.814 + case KeyEvent.DOM_VK_RETURN: 1.815 + 1.816 + // if this is a failure item, save it for fireErrorCommand 1.817 + var errItem = this.getErrorAt(this.popup.selectedIndex); 1.818 + 1.819 + killEvent = this.mMenuOpen; 1.820 + this.finishAutoComplete(true, true, aEvent); 1.821 + this.closePopup(); 1.822 + if (errItem) { 1.823 + this._fireEvent("errorcommand", errItem); 1.824 + } 1.825 + break; 1.826 + 1.827 + case KeyEvent.DOM_VK_ESCAPE: 1.828 + this.clearTimer(); 1.829 + killEvent = this.mMenuOpen; 1.830 + this.undoAutoComplete(); 1.831 + this.closePopup(); 1.832 + break; 1.833 + 1.834 + case KeyEvent.DOM_VK_LEFT: 1.835 + case KeyEvent.DOM_VK_RIGHT: 1.836 + case KeyEvent.DOM_VK_HOME: 1.837 + case KeyEvent.DOM_VK_END: 1.838 + this.finishAutoComplete(true, false, aEvent); 1.839 + this.clearTimer(); 1.840 + this.closePopup(); 1.841 + break; 1.842 + 1.843 + case KeyEvent.DOM_VK_DOWN: 1.844 + if (!aEvent.altKey) { 1.845 + this.clearTimer(); 1.846 + killEvent = this.keyNavigation(aEvent); 1.847 + break; 1.848 + } 1.849 + // Alt+Down falls through to history popup toggling code 1.850 + 1.851 + case KeyEvent.DOM_VK_F4: 1.852 + if (!aEvent.ctrlKey && !aEvent.shiftKey && this.getAttribute("enablehistory") == "true") { 1.853 + var historyPopup = document.getAnonymousElementByAttribute(this, "anonid", "historydropmarker"); 1.854 + if (historyPopup) 1.855 + historyPopup.showPopup(); 1.856 + else 1.857 + historyPopup.hidePopup(); 1.858 + } 1.859 + break; 1.860 + case KeyEvent.DOM_VK_PAGE_UP: 1.861 + case KeyEvent.DOM_VK_PAGE_DOWN: 1.862 + case KeyEvent.DOM_VK_UP: 1.863 + if (!aEvent.ctrlKey && !aEvent.metaKey) { 1.864 + this.clearTimer(); 1.865 + killEvent = this.keyNavigation(aEvent); 1.866 + } 1.867 + break; 1.868 + 1.869 + case KeyEvent.DOM_VK_BACK_SPACE: 1.870 + if (!aEvent.ctrlKey && !aEvent.altKey && !aEvent.shiftKey && 1.871 + this.selectionStart == this.currentSearchString.length && 1.872 + this.selectionEnd == this.value.length && 1.873 + this.mDefaultMatchFilled) { 1.874 + this.mDefaultMatchFilled = false; 1.875 + this.value = this.currentSearchString; 1.876 + } 1.877 + 1.878 + if (!/Mac/.test(navigator.platform)) 1.879 + break; 1.880 + case KeyEvent.DOM_VK_DELETE: 1.881 + if (/Mac/.test(navigator.platform) && !aEvent.shiftKey) 1.882 + break; 1.883 + 1.884 + if (this.mMenuOpen && this.popup.selectedIndex != -1) { 1.885 + var obj = this.convertIndexToSession(this.popup.selectedIndex); 1.886 + if (obj) { 1.887 + var result = this.mLastResults[obj.session]; 1.888 + if (!result.errorDescription) { 1.889 + var count = result.matchCount; 1.890 + result.removeValueAt(obj.index, true); 1.891 + this.view.updateResults(this.popup.selectedIndex, result.matchCount - count); 1.892 + killEvent = true; 1.893 + } 1.894 + } 1.895 + } 1.896 + break; 1.897 + } 1.898 + 1.899 + if (killEvent) { 1.900 + aEvent.preventDefault(); 1.901 + aEvent.stopPropagation(); 1.902 + } 1.903 + 1.904 + return true; 1.905 + ]]></body> 1.906 + </method> 1.907 + 1.908 + <!-- --> 1.909 + <method name="processStartComposition"> 1.910 + <body><![CDATA[ 1.911 + this.finishAutoComplete(false, false, null); 1.912 + this.clearTimer(); 1.913 + this.closePopup(); 1.914 + ]]></body> 1.915 + </method> 1.916 + 1.917 + <!-- --> 1.918 + <method name="keyNavigation"> 1.919 + <parameter name="aEvent"/> 1.920 + <body><![CDATA[ 1.921 + var k = aEvent.keyCode; 1.922 + if (k == KeyEvent.DOM_VK_TAB || 1.923 + k == KeyEvent.DOM_VK_UP || k == KeyEvent.DOM_VK_DOWN || 1.924 + k == KeyEvent.DOM_VK_PAGE_UP || k == KeyEvent.DOM_VK_PAGE_DOWN) 1.925 + { 1.926 + if (!this.mMenuOpen) { 1.927 + // Original xpfe style was to allow the up and down keys to have 1.928 + // their default Mac action if the popup could not be opened. 1.929 + // For compatibility for toolkit we now have to predict which 1.930 + // keys have a default action that we can always allow to fire. 1.931 + if (/Mac/.test(navigator.platform) && 1.932 + ((k == KeyEvent.DOM_VK_UP && 1.933 + (this.selectionStart != 0 || 1.934 + this.selectionEnd != 0)) || 1.935 + (k == KeyEvent.DOM_VK_DOWN && 1.936 + (this.selectionStart != this.value.length || 1.937 + this.selectionEnd != this.value.length)))) 1.938 + return false; 1.939 + if (this.currentSearchString != this.value) { 1.940 + this.processInput(); 1.941 + return true; 1.942 + } 1.943 + if (this.view.rowCount < this.minResultsForPopup) 1.944 + return true; // used to be false, see above 1.945 + 1.946 + this.mNeedToFinish = true; 1.947 + this.openPopup(); 1.948 + return true; 1.949 + } 1.950 + 1.951 + this.userAction = "scrolling"; 1.952 + this.mNeedToComplete = false; 1.953 + 1.954 + var reverse = k == KeyEvent.DOM_VK_TAB && aEvent.shiftKey || 1.955 + k == KeyEvent.DOM_VK_UP || 1.956 + k == KeyEvent.DOM_VK_PAGE_UP; 1.957 + var page = k == KeyEvent.DOM_VK_PAGE_UP || 1.958 + k == KeyEvent.DOM_VK_PAGE_DOWN; 1.959 + var selected = this.popup.selectBy(reverse, page); 1.960 + 1.961 + // determine which value to place in the textbox 1.962 + this.ignoreInputEvent = true; 1.963 + if (selected != -1) { 1.964 + if (this.getErrorAt(selected)) { 1.965 + if (this.currentSearchString) 1.966 + this.setTextValue(this.currentSearchString); 1.967 + } else { 1.968 + this.setTextValue(this.getResultValueAt(selected)); 1.969 + } 1.970 + this.mTransientValue = true; 1.971 + } else { 1.972 + if (this.currentSearchString) 1.973 + this.setTextValue(this.currentSearchString); 1.974 + this.mTransientValue = false; 1.975 + } 1.976 + 1.977 + // move cursor to the end 1.978 + this.mInputElt.setSelectionRange(this.value.length, this.value.length); 1.979 + this.ignoreInputEvent = false; 1.980 + } 1.981 + return true; 1.982 + ]]></body> 1.983 + </method> 1.984 + 1.985 + <!-- while the user is typing, fill the textbox with the "default" value 1.986 + if one can be assumed, and select the end of the text --> 1.987 + <method name="autoFillInput"> 1.988 + <parameter name="aSessionName"/> 1.989 + <parameter name="aResults"/> 1.990 + <parameter name="aUseFirstMatchIfNoDefault"/> 1.991 + <body><![CDATA[ 1.992 + if (this.mInputElt.selectionEnd < this.currentSearchString.length || 1.993 + this.mDefaultMatchFilled) 1.994 + return; 1.995 + 1.996 + if (!this.mFinishAfterSearch && 1.997 + (this.autoFill || this.completeDefaultIndex) && 1.998 + this.mLastKeyCode != KeyEvent.DOM_VK_BACK_SPACE && 1.999 + this.mLastKeyCode != KeyEvent.DOM_VK_DELETE) { 1.1000 + var indexToUse = aResults.defaultIndex; 1.1001 + if (aUseFirstMatchIfNoDefault && indexToUse == -1) 1.1002 + indexToUse = 0; 1.1003 + 1.1004 + if (indexToUse != -1) { 1.1005 + var resultValue = this.getSessionValueAt(aSessionName, indexToUse); 1.1006 + var match = resultValue.toLowerCase(); 1.1007 + var entry = this.currentSearchString.toLowerCase(); 1.1008 + this.ignoreInputEvent = true; 1.1009 + if (match.indexOf(entry) == 0) { 1.1010 + var endPoint = this.value.length; 1.1011 + this.setTextValue(this.value + resultValue.substr(endPoint)); 1.1012 + this.mInputElt.setSelectionRange(endPoint, this.value.length); 1.1013 + } else { 1.1014 + if (this.completeDefaultIndex) { 1.1015 + this.setTextValue(this.value + " >> " + resultValue); 1.1016 + this.mInputElt.setSelectionRange(entry.length, this.value.length); 1.1017 + } else { 1.1018 + var postIndex = resultValue.indexOf(this.value); 1.1019 + if (postIndex >= 0) { 1.1020 + var startPt = this.value.length; 1.1021 + this.setTextValue(this.value + 1.1022 + resultValue.substr(startPt+postIndex)); 1.1023 + this.mInputElt.setSelectionRange(startPt, this.value.length); 1.1024 + } 1.1025 + } 1.1026 + } 1.1027 + this.mNeedToComplete = true; 1.1028 + this.ignoreInputEvent = false; 1.1029 + this.mDefaultMatchFilled = true; 1.1030 + } 1.1031 + } 1.1032 + ]]></body> 1.1033 + </method> 1.1034 + 1.1035 + <!-- ::::::::::::: popup and tree ::::::::::::: --> 1.1036 + 1.1037 + <!-- --> 1.1038 + <method name="openPopup"> 1.1039 + <body><![CDATA[ 1.1040 + if (!this.mMenuOpen && this.focused && 1.1041 + (this.getResultCount() >= this.minResultsForPopup || 1.1042 + this.mFailureItems)) { 1.1043 + var w = this.boxObject.width; 1.1044 + if (w != this.popup.boxObject.width) 1.1045 + this.popup.setAttribute("width", w); 1.1046 + this.popup.showPopup(this, -1, -1, "popup", "bottomleft", "topleft"); 1.1047 + this.mMenuOpen = true; 1.1048 + } 1.1049 + ]]></body> 1.1050 + </method> 1.1051 + 1.1052 + <!-- --> 1.1053 + <method name="closePopup"> 1.1054 + <body><![CDATA[ 1.1055 + if (this.popup && this.mMenuOpen) { 1.1056 + this.popup.hidePopup(); 1.1057 + this.mMenuOpen = false; 1.1058 + } 1.1059 + ]]></body> 1.1060 + </method> 1.1061 + 1.1062 + <!-- --> 1.1063 + <method name="addResultElements"> 1.1064 + <parameter name="aSession"/> 1.1065 + <parameter name="aResults"/> 1.1066 + <body><![CDATA[ 1.1067 + var count = aResults.errorDescription ? 1 : aResults.matchCount; 1.1068 + if (this.focused && this.showPopup) { 1.1069 + var row = 0; 1.1070 + for (var name in this.mSessions) { 1.1071 + row += this.mLastRows[name]; 1.1072 + if (name == aSession) 1.1073 + break; 1.1074 + } 1.1075 + this.view.updateResults(row, count - this.mLastRows[name]); 1.1076 + this.popup.adjustHeight(); 1.1077 + } 1.1078 + this.mLastResults[aSession] = aResults; 1.1079 + this.mLastRows[aSession] = count; 1.1080 + ]]></body> 1.1081 + </method> 1.1082 + 1.1083 + <!-- --> 1.1084 + <method name="clearResultElements"> 1.1085 + <parameter name="aInvalidate"/> 1.1086 + <body><![CDATA[ 1.1087 + for (var name in this.mSessions) 1.1088 + this.mLastRows[name] = 0; 1.1089 + this.view.clearResults(); 1.1090 + if (aInvalidate) 1.1091 + this.popup.adjustHeight(); 1.1092 + 1.1093 + this.noMatch = true; 1.1094 + ]]></body> 1.1095 + </method> 1.1096 + 1.1097 + <!-- --> 1.1098 + <method name="setTextValue"> 1.1099 + <parameter name="aValue"/> 1.1100 + <body><![CDATA[ 1.1101 + this.value = aValue; 1.1102 + 1.1103 + // Completing a result should simulate the user typing the result, 1.1104 + // so fire an input event. 1.1105 + var evt = document.createEvent("UIEvents"); 1.1106 + evt.initUIEvent("input", true, false, window, 0); 1.1107 + var oldIgnoreInput = this.ignoreInputEvent; 1.1108 + this.ignoreInputEvent = true; 1.1109 + this.dispatchEvent(evt); 1.1110 + this.ignoreInputEvent = oldIgnoreInput; 1.1111 + ]]></body> 1.1112 + </method> 1.1113 + 1.1114 + <!-- --> 1.1115 + <method name="clearResultData"> 1.1116 + <body><![CDATA[ 1.1117 + for (var name in this.mSessions) 1.1118 + this.mLastResults[name] = null; 1.1119 + ]]></body> 1.1120 + </method> 1.1121 + 1.1122 + <!-- ::::::::::::: miscellaneous ::::::::::::: --> 1.1123 + 1.1124 + <!-- --> 1.1125 + <method name="ifSetAttribute"> 1.1126 + <parameter name="aAttr"/> 1.1127 + <parameter name="aVal"/> 1.1128 + <body><![CDATA[ 1.1129 + if (!this.hasAttribute(aAttr)) 1.1130 + this.setAttribute(aAttr, aVal); 1.1131 + ]]></body> 1.1132 + </method> 1.1133 + 1.1134 + <!-- --> 1.1135 + <method name="clearTimer"> 1.1136 + <body><![CDATA[ 1.1137 + if (this.mAutoCompleteTimer) { 1.1138 + clearTimeout(this.mAutoCompleteTimer); 1.1139 + this.mAutoCompleteTimer = 0; 1.1140 + } 1.1141 + ]]></body> 1.1142 + </method> 1.1143 + 1.1144 + <!-- ::::::::::::: event dispatching ::::::::::::: --> 1.1145 + 1.1146 + <method name="_fireEvent"> 1.1147 + <parameter name="aEventType"/> 1.1148 + <parameter name="aEventParam"/> 1.1149 + <parameter name="aTriggeringEvent"/> 1.1150 + <body> 1.1151 + <![CDATA[ 1.1152 + var noCancel = true; 1.1153 + // handle any xml attribute event handlers 1.1154 + var handler = this.getAttribute("on"+aEventType); 1.1155 + if (handler) { 1.1156 + var fn = new Function("eventParam", "domEvent", handler); 1.1157 + var returned = fn.apply(this, [aEventParam, aTriggeringEvent]); 1.1158 + if (returned == false) 1.1159 + noCancel = false; 1.1160 + } 1.1161 + 1.1162 + return noCancel; 1.1163 + ]]> 1.1164 + </body> 1.1165 + </method> 1.1166 + 1.1167 + <!-- =================== TREE VIEW =================== --> 1.1168 + 1.1169 + <field name="view"><![CDATA[ 1.1170 + ({ 1.1171 + mTextbox: this, 1.1172 + mTree: null, 1.1173 + mSelection: null, 1.1174 + mRowCount: 0, 1.1175 + 1.1176 + clearResults: function() 1.1177 + { 1.1178 + var oldCount = this.mRowCount; 1.1179 + this.mRowCount = 0; 1.1180 + 1.1181 + if (this.mTree) { 1.1182 + this.mTree.rowCountChanged(0, -oldCount); 1.1183 + this.mTree.scrollToRow(0); 1.1184 + } 1.1185 + }, 1.1186 + 1.1187 + updateResults: function(aRow, aCount) 1.1188 + { 1.1189 + this.mRowCount += aCount; 1.1190 + 1.1191 + if (this.mTree) 1.1192 + this.mTree.rowCountChanged(aRow, aCount); 1.1193 + }, 1.1194 + 1.1195 + ////////////////////////////////////////////////////////// 1.1196 + // nsIAutoCompleteController interface 1.1197 + 1.1198 + // this is the only method required by the treebody mouseup handler 1.1199 + handleEnter: function(aIsPopupSelection) { 1.1200 + this.mTextbox.onResultClick(); 1.1201 + }, 1.1202 + 1.1203 + ////////////////////////////////////////////////////////// 1.1204 + // nsITreeView interface 1.1205 + 1.1206 + get rowCount() { 1.1207 + return this.mRowCount; 1.1208 + }, 1.1209 + 1.1210 + get selection() { 1.1211 + return this.mSelection; 1.1212 + }, 1.1213 + 1.1214 + set selection(aVal) { 1.1215 + return this.mSelection = aVal; 1.1216 + }, 1.1217 + 1.1218 + setTree: function(aTree) 1.1219 + { 1.1220 + this.mTree = aTree; 1.1221 + }, 1.1222 + 1.1223 + getCellText: function(aRow, aCol) 1.1224 + { 1.1225 + for (var name in this.mTextbox.mSessions) { 1.1226 + if (aRow < this.mTextbox.mLastRows[name]) { 1.1227 + var result = this.mTextbox.mLastResults[name]; 1.1228 + switch (aCol.id) { 1.1229 + case "treecolAutoCompleteValue": 1.1230 + return result.errorDescription || result.getLabelAt(aRow); 1.1231 + case "treecolAutoCompleteComment": 1.1232 + if (!result.errorDescription) 1.1233 + return result.getCommentAt(aRow); 1.1234 + default: 1.1235 + return ""; 1.1236 + } 1.1237 + } 1.1238 + aRow -= this.mTextbox.mLastRows[name]; 1.1239 + } 1.1240 + return ""; 1.1241 + }, 1.1242 + 1.1243 + getRowProperties: function(aIndex) 1.1244 + { 1.1245 + return ""; 1.1246 + }, 1.1247 + 1.1248 + getCellProperties: function(aIndex, aCol) 1.1249 + { 1.1250 + // for the value column, append nsIAutoCompleteItem::className 1.1251 + // to the property list so that we can style this column 1.1252 + // using that property 1.1253 + if (aCol.id == "treecolAutoCompleteValue") { 1.1254 + for (var name in this.mTextbox.mSessions) { 1.1255 + if (aIndex < this.mTextbox.mLastRows[name]) { 1.1256 + var result = this.mTextbox.mLastResults[name]; 1.1257 + if (result.errorDescription) 1.1258 + return ""; 1.1259 + return result.getStyleAt(aIndex); 1.1260 + } 1.1261 + aIndex -= this.mTextbox.mLastRows[name]; 1.1262 + } 1.1263 + } 1.1264 + return ""; 1.1265 + }, 1.1266 + 1.1267 + getColumnProperties: function(aCol) 1.1268 + { 1.1269 + return ""; 1.1270 + }, 1.1271 + 1.1272 + getImageSrc: function(aRow, aCol) 1.1273 + { 1.1274 + if (aCol.id == "treecolAutoCompleteValue") { 1.1275 + for (var name in this.mTextbox.mSessions) { 1.1276 + if (aRow < this.mTextbox.mLastRows[name]) { 1.1277 + var result = this.mTextbox.mLastResults[name]; 1.1278 + if (result.errorDescription) 1.1279 + return ""; 1.1280 + return result.getImageAt(aRow); 1.1281 + } 1.1282 + aRow -= this.mTextbox.mLastRows[name]; 1.1283 + } 1.1284 + } 1.1285 + return ""; 1.1286 + }, 1.1287 + 1.1288 + getParentIndex: function(aRowIndex) { }, 1.1289 + hasNextSibling: function(aRowIndex, aAfterIndex) { }, 1.1290 + getLevel: function(aIndex) {}, 1.1291 + getProgressMode: function(aRow, aCol) {}, 1.1292 + getCellValue: function(aRow, aCol) {}, 1.1293 + isContainer: function(aIndex) {}, 1.1294 + isContainerOpen: function(aIndex) {}, 1.1295 + isContainerEmpty: function(aIndex) {}, 1.1296 + isSeparator: function(aIndex) {}, 1.1297 + isSorted: function() {}, 1.1298 + toggleOpenState: function(aIndex) {}, 1.1299 + selectionChanged: function() {}, 1.1300 + cycleHeader: function(aCol) {}, 1.1301 + cycleCell: function(aRow, aCol) {}, 1.1302 + isEditable: function(aRow, aCol) {}, 1.1303 + isSelectable: function(aRow, aCol) {}, 1.1304 + setCellValue: function(aRow, aCol, aValue) {}, 1.1305 + setCellText: function(aRow, aCol, aValue) {}, 1.1306 + performAction: function(aAction) {}, 1.1307 + performActionOnRow: function(aAction, aRow) {}, 1.1308 + performActionOnCell: function(aAction, aRow, aCol) {} 1.1309 + }); 1.1310 + ]]></field> 1.1311 + 1.1312 + </implementation> 1.1313 + 1.1314 + <handlers> 1.1315 + <handler event="input" 1.1316 + action="if (!this.ignoreInputEvent) this.processInput();"/> 1.1317 + 1.1318 + <handler event="keypress" phase="capturing" 1.1319 + action="return this.processKeyPress(event);"/> 1.1320 + 1.1321 + <handler event="compositionstart" phase="capturing" 1.1322 + action="this.processStartComposition();"/> 1.1323 + 1.1324 + <handler event="focus" phase="capturing" 1.1325 + action="this.userAction = 'typing';"/> 1.1326 + 1.1327 + <handler event="blur" phase="capturing" 1.1328 + action="if ( !(this.ignoreBlurWhileSearching && this.isSearching) ) {this.userAction = 'none'; this.finishAutoComplete(false, false, event);}"/> 1.1329 + 1.1330 + <handler event="mousedown" phase="capturing" 1.1331 + action="if ( !this.mMenuOpen ) this.finishAutoComplete(false, false, event);"/> 1.1332 + </handlers> 1.1333 + </binding> 1.1334 + 1.1335 + <binding id="autocomplete-result-popup" extends="chrome://global/content/bindings/popup.xml#popup"> 1.1336 + <resources> 1.1337 + <stylesheet src="chrome://global/content/autocomplete.css"/> 1.1338 + <stylesheet src="chrome://global/skin/autocomplete.css"/> 1.1339 + </resources> 1.1340 + 1.1341 + <content ignorekeys="true" level="top"> 1.1342 + <xul:tree anonid="tree" class="autocomplete-tree plain" flex="1"> 1.1343 + <xul:treecols anonid="treecols"> 1.1344 + <xul:treecol class="autocomplete-treecol" id="treecolAutoCompleteValue" flex="2"/> 1.1345 + <xul:treecol class="autocomplete-treecol" id="treecolAutoCompleteComment" flex="1" hidden="true"/> 1.1346 + </xul:treecols> 1.1347 + <xul:treechildren anonid="treebody" class="autocomplete-treebody"/> 1.1348 + </xul:tree> 1.1349 + </content> 1.1350 + 1.1351 + <implementation implements="nsIAutoCompletePopup"> 1.1352 + <constructor><![CDATA[ 1.1353 + if (this.textbox && this.textbox.view) 1.1354 + this.initialize(); 1.1355 + ]]></constructor> 1.1356 + 1.1357 + <destructor><![CDATA[ 1.1358 + if (this.view) 1.1359 + this.tree.view = null; 1.1360 + ]]></destructor> 1.1361 + 1.1362 + <field name="textbox"> 1.1363 + document.getBindingParent(this); 1.1364 + </field> 1.1365 + 1.1366 + <field name="tree"> 1.1367 + document.getAnonymousElementByAttribute(this, "anonid", "tree"); 1.1368 + </field> 1.1369 + 1.1370 + <field name="treecols"> 1.1371 + document.getAnonymousElementByAttribute(this, "anonid", "treecols"); 1.1372 + </field> 1.1373 + 1.1374 + <field name="treebody"> 1.1375 + document.getAnonymousElementByAttribute(this, "anonid", "treebody"); 1.1376 + </field> 1.1377 + 1.1378 + <field name="view"> 1.1379 + null 1.1380 + </field> 1.1381 + 1.1382 + <!-- Setting tree.view doesn't always immediately create a selection, 1.1383 + so we ensure the selection by asking the tree for the view. Note: 1.1384 + this.view.selection is quicker if we know the selection exists. --> 1.1385 + <property name="selection" onget="return this.tree.view.selection;"/> 1.1386 + 1.1387 + <property name="pageCount" 1.1388 + onget="return this.tree.treeBoxObject.getPageLength();"/> 1.1389 + 1.1390 + <field name="maxRows">0</field> 1.1391 + <field name="mLastRows">0</field> 1.1392 + 1.1393 + <method name="initialize"> 1.1394 + <body><![CDATA[ 1.1395 + this.showCommentColumn = this.textbox.showCommentColumn; 1.1396 + this.tree.view = this.textbox.view; 1.1397 + this.view = this.textbox.view; 1.1398 + this.maxRows = this.textbox.maxRows; 1.1399 + ]]></body> 1.1400 + </method> 1.1401 + 1.1402 + <property name="showCommentColumn" 1.1403 + onget="return !this.treecols.lastChild.hidden;" 1.1404 + onset="this.treecols.lastChild.hidden = !val; return val;"/> 1.1405 + 1.1406 + <method name="adjustHeight"> 1.1407 + <body><![CDATA[ 1.1408 + // detect the desired height of the tree 1.1409 + var bx = this.tree.treeBoxObject; 1.1410 + var view = this.view; 1.1411 + var rows = this.maxRows || 6; 1.1412 + if (!view.rowCount || (rows && view.rowCount < rows)) 1.1413 + rows = view.rowCount; 1.1414 + 1.1415 + var height = rows * bx.rowHeight; 1.1416 + 1.1417 + if (height == 0) 1.1418 + this.tree.setAttribute("collapsed", "true"); 1.1419 + else { 1.1420 + if (this.tree.hasAttribute("collapsed")) 1.1421 + this.tree.removeAttribute("collapsed"); 1.1422 + this.tree.setAttribute("height", height); 1.1423 + } 1.1424 + ]]></body> 1.1425 + </method> 1.1426 + 1.1427 + <method name="clearSelection"> 1.1428 + <body> 1.1429 + this.selection.clearSelection(); 1.1430 + </body> 1.1431 + </method> 1.1432 + 1.1433 + <method name="getNextIndex"> 1.1434 + <parameter name="aReverse"/> 1.1435 + <parameter name="aPage"/> 1.1436 + <parameter name="aIndex"/> 1.1437 + <parameter name="aMaxRow"/> 1.1438 + <body><![CDATA[ 1.1439 + if (aMaxRow < 0) 1.1440 + return -1; 1.1441 + 1.1442 + if (aIndex == -1) 1.1443 + return aReverse ? aMaxRow : 0; 1.1444 + if (aIndex == (aReverse ? 0 : aMaxRow)) 1.1445 + return -1; 1.1446 + 1.1447 + var amount = aPage ? this.pageCount - 1 : 1; 1.1448 + aIndex = aReverse ? aIndex - amount : aIndex + amount; 1.1449 + if (aIndex > aMaxRow) 1.1450 + return aMaxRow; 1.1451 + if (aIndex < 0) 1.1452 + return 0; 1.1453 + return aIndex; 1.1454 + ]]></body> 1.1455 + </method> 1.1456 + 1.1457 + <!-- =================== nsIAutoCompletePopup =================== --> 1.1458 + 1.1459 + <field name="input"> 1.1460 + null 1.1461 + </field> 1.1462 + 1.1463 + <!-- This property is meant to be overriden by bindings extending 1.1464 + this one. When the user selects an item from the list by 1.1465 + hitting enter or clicking, this method can set the value 1.1466 + of the textbox to a different value if it wants to. --> 1.1467 + <property name="overrideValue" readonly="true" onget="return null;"/> 1.1468 + 1.1469 + <property name="selectedIndex"> 1.1470 + <getter> 1.1471 + if (!this.view || !this.selection.count) 1.1472 + return -1; 1.1473 + var start = {}, end = {}; 1.1474 + this.view.selection.getRangeAt(0, start, end); 1.1475 + return start.value; 1.1476 + </getter> 1.1477 + <setter> 1.1478 + if (this.view) { 1.1479 + this.selection.select(val); 1.1480 + if (val >= 0) { 1.1481 + this.view.selection.currentIndex = -1; 1.1482 + this.tree.treeBoxObject.ensureRowIsVisible(val); 1.1483 + } 1.1484 + } 1.1485 + return val; 1.1486 + </setter> 1.1487 + </property> 1.1488 + 1.1489 + <property name="popupOpen" onget="return !!this.input;" readonly="true"/> 1.1490 + 1.1491 + <method name="openAutocompletePopup"> 1.1492 + <parameter name="aInput"/> 1.1493 + <parameter name="aElement"/> 1.1494 + <body><![CDATA[ 1.1495 + if (!this.input) { 1.1496 + this.tree.view = aInput.controller; 1.1497 + this.view = this.tree.view; 1.1498 + this.showCommentColumn = aInput.showCommentColumn; 1.1499 + this.maxRows = aInput.maxRows; 1.1500 + this.invalidate(); 1.1501 + 1.1502 + var viewer = aElement 1.1503 + .ownerDocument 1.1504 + .defaultView 1.1505 + .QueryInterface(Components.interfaces.nsIInterfaceRequestor) 1.1506 + .getInterface(Components.interfaces.nsIWebNavigation) 1.1507 + .QueryInterface(Components.interfaces.nsIDocShell) 1.1508 + .contentViewer 1.1509 + .QueryInterface(Components.interfaces.nsIMarkupDocumentViewer); 1.1510 + var rect = aElement.getBoundingClientRect(); 1.1511 + var width = Math.round((rect.right - rect.left) * viewer.fullZoom); 1.1512 + this.setAttribute("width", width > 100 ? width : 100); 1.1513 + // Adjust the direction (which is not inherited) of the autocomplete 1.1514 + // popup list, based on the textbox direction. (Bug 707039) 1.1515 + this.style.direction = aElement.ownerDocument.defaultView 1.1516 + .getComputedStyle(aElement) 1.1517 + .direction; 1.1518 + const nsIPopupBoxObject = Components.interfaces.nsIPopupBoxObject; 1.1519 + this.popupBoxObject.setConsumeRollupEvent(aInput.consumeRollupEvent 1.1520 + ? nsIPopupBoxObject.ROLLUP_CONSUME 1.1521 + : nsIPopupBoxObject.ROLLUP_NO_CONSUME); 1.1522 + this.openPopup(aElement, "after_start", 0, 0, false, false); 1.1523 + if (this.state != "closed") 1.1524 + this.input = aInput; 1.1525 + } 1.1526 + ]]></body> 1.1527 + </method> 1.1528 + 1.1529 + <method name="closePopup"> 1.1530 + <body> 1.1531 + this.hidePopup(); 1.1532 + </body> 1.1533 + </method> 1.1534 + 1.1535 + <method name="invalidate"> 1.1536 + <body> 1.1537 + if (this.view) 1.1538 + this.adjustHeight(); 1.1539 + this.tree.treeBoxObject.invalidate(); 1.1540 + </body> 1.1541 + </method> 1.1542 + 1.1543 + <method name="selectBy"> 1.1544 + <parameter name="aReverse"/> 1.1545 + <parameter name="aPage"/> 1.1546 + <body><![CDATA[ 1.1547 + try { 1.1548 + return this.selectedIndex = this.getNextIndex(aReverse, aPage, this.selectedIndex, this.view.rowCount - 1); 1.1549 + } catch (ex) { 1.1550 + // do nothing - occasionally timer-related js errors happen here 1.1551 + // e.g. "this.selectedIndex has no properties", when you type fast and hit a 1.1552 + // navigation key before this popup has opened 1.1553 + return -1; 1.1554 + } 1.1555 + ]]></body> 1.1556 + </method> 1.1557 + </implementation> 1.1558 + 1.1559 + <handlers> 1.1560 + <handler event="popupshowing"> 1.1561 + if (this.textbox) 1.1562 + this.textbox.mMenuOpen = true; 1.1563 + </handler> 1.1564 + 1.1565 + <handler event="popuphiding"> 1.1566 + if (this.textbox) 1.1567 + this.textbox.mMenuOpen = false; 1.1568 + this.clearSelection(); 1.1569 + this.input = null; 1.1570 + </handler> 1.1571 + </handlers> 1.1572 + </binding> 1.1573 + 1.1574 + <binding id="autocomplete-treebody"> 1.1575 + <implementation> 1.1576 + <field name="popup">document.getBindingParent(this);</field> 1.1577 + 1.1578 + <field name="mLastMoveTime">Date.now()</field> 1.1579 + </implementation> 1.1580 + 1.1581 + <handlers> 1.1582 + <handler event="mouseout" action="this.popup.selectedIndex = -1;"/> 1.1583 + 1.1584 + <handler event="mouseup"><![CDATA[ 1.1585 + var rc = this.parentNode.treeBoxObject.getRowAt(event.clientX, event.clientY); 1.1586 + if (rc != -1) { 1.1587 + this.popup.selectedIndex = rc; 1.1588 + this.popup.view.handleEnter(true); 1.1589 + } 1.1590 + ]]></handler> 1.1591 + 1.1592 + <handler event="mousemove"><![CDATA[ 1.1593 + if (Date.now() - this.mLastMoveTime > 30) { 1.1594 + var rc = this.parentNode.treeBoxObject.getRowAt(event.clientX, event.clientY); 1.1595 + if (rc != -1 && rc != this.popup.selectedIndex) 1.1596 + this.popup.selectedIndex = rc; 1.1597 + this.mLastMoveTime = Date.now(); 1.1598 + } 1.1599 + ]]></handler> 1.1600 + </handlers> 1.1601 + </binding> 1.1602 + 1.1603 + <binding id="autocomplete-history-popup" 1.1604 + extends="chrome://global/content/bindings/popup.xml#popup-scrollbars"> 1.1605 + <resources> 1.1606 + <stylesheet src="chrome://global/content/autocomplete.css"/> 1.1607 + <stylesheet src="chrome://global/skin/autocomplete.css"/> 1.1608 + </resources> 1.1609 + 1.1610 + <implementation> 1.1611 + <method name="removeOpenAttribute"> 1.1612 + <parameter name="parentNode"/> 1.1613 + <body><![CDATA[ 1.1614 + parentNode.removeAttribute("open"); 1.1615 + ]]></body> 1.1616 + </method> 1.1617 + </implementation> 1.1618 + 1.1619 + <handlers> 1.1620 + <handler event="popuphiding"><![CDATA[ 1.1621 + setTimeout(this.removeOpenAttribute, 0, this.parentNode); 1.1622 + ]]></handler> 1.1623 + </handlers> 1.1624 + </binding> 1.1625 + 1.1626 + <binding id="history-dropmarker" extends="chrome://global/content/bindings/general.xml#dropmarker"> 1.1627 + 1.1628 + <implementation> 1.1629 + <method name="showPopup"> 1.1630 + <body><![CDATA[ 1.1631 + var textbox = document.getBindingParent(this); 1.1632 + var kids = textbox.getElementsByClassName("autocomplete-history-popup"); 1.1633 + if (kids.item(0) && textbox.getAttribute("open") != "true") { // Open history popup 1.1634 + var w = textbox.boxObject.width; 1.1635 + if (w != kids[0].boxObject.width) 1.1636 + kids[0].width = w; 1.1637 + kids[0].showPopup(textbox, -1, -1, "popup", "bottomleft", "topleft"); 1.1638 + textbox.setAttribute("open", "true"); 1.1639 + } 1.1640 + ]]></body> 1.1641 + </method> 1.1642 + </implementation> 1.1643 + 1.1644 + <handlers> 1.1645 + <handler event="mousedown"><![CDATA[ 1.1646 + this.showPopup(); 1.1647 + ]]></handler> 1.1648 + </handlers> 1.1649 + </binding> 1.1650 + 1.1651 +</bindings> 1.1652 +