mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-01 22:55:23 +00:00
1368 lines
49 KiB
XML
1368 lines
49 KiB
XML
<?xml version="1.0"?>
|
|
|
|
# -*- Mode: HTML -*-
|
|
# ***** BEGIN LICENSE BLOCK *****
|
|
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
#
|
|
# The contents of this file are subject to the Mozilla Public License Version
|
|
# 1.1 (the "License"); you may not use this file except in compliance with
|
|
# the License. You may obtain a copy of the License at
|
|
# http://www.mozilla.org/MPL/
|
|
#
|
|
# Software distributed under the License is distributed on an "AS IS" basis,
|
|
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
# for the specific language governing rights and limitations under the
|
|
# License.
|
|
#
|
|
# The Original Code is mozilla.org browser.
|
|
#
|
|
# The Initial Developer of the Original Code is
|
|
# Joe Hewitt.
|
|
# Portions created by the Initial Developer are Copyright (C) 2003
|
|
# the Initial Developer. All Rights Reserved.
|
|
#
|
|
# Contributor(s):
|
|
# Pierre Chanial (p_ch@verizon.net)
|
|
# Dean Tessman (dean_tessman@hotmail.com)
|
|
# Masayuki Nakano (masayuki@d-toybox.com)
|
|
# Pamela Greene (pamg.bugs@gmail.com)
|
|
#
|
|
# Alternatively, the contents of this file may be used under the terms of
|
|
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
# in which case the provisions of the GPL or the LGPL are applicable instead
|
|
# of those above. If you wish to allow use of your version of this file only
|
|
# under the terms of either the GPL or the LGPL, and not to allow others to
|
|
# use your version of this file under the terms of the MPL, indicate your
|
|
# decision by deleting the provisions above and replace them with the notice
|
|
# and other provisions required by the GPL or the LGPL. If you do not delete
|
|
# the provisions above, a recipient may use your version of this file under
|
|
# the terms of any one of the MPL, the GPL or the LGPL.
|
|
#
|
|
# ***** END LICENSE BLOCK *****
|
|
|
|
<bindings id="autocompleteBindings"
|
|
xmlns="http://www.mozilla.org/xbl"
|
|
xmlns:html="http://www.w3.org/1999/xhtml"
|
|
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
|
xmlns:xbl="http://www.mozilla.org/xbl">
|
|
|
|
<binding id="autocomplete"
|
|
extends="chrome://global/content/bindings/textbox.xml#textbox">
|
|
<resources>
|
|
<stylesheet src="chrome://global/skin/autocomplete.css"/>
|
|
</resources>
|
|
|
|
<content sizetopopup="pref">
|
|
<xul:hbox class="autocomplete-textbox-container" flex="1">
|
|
<children includes="image|deck|stack|box">
|
|
<xul:image class="autocomplete-icon" allowevents="true"/>
|
|
</children>
|
|
|
|
<xul:hbox anonid="textbox-input-box" class="textbox-input-box" flex="1" xbl:inherits="tooltiptext=inputtooltiptext">
|
|
<children/>
|
|
<html:input anonid="input" class="autocomplete-textbox textbox-input"
|
|
flex="1" allowevents="true"
|
|
xbl:inherits="tooltiptext=inputtooltiptext,onfocus,onblur,value,type,maxlength,disabled,size,readonly,userAction"/>
|
|
</xul:hbox>
|
|
<children includes="hbox"/>
|
|
</xul:hbox>
|
|
|
|
<xul:dropmarker anonid="historydropmarker" class="autocomplete-history-dropmarker"
|
|
allowevents="true"
|
|
xbl:inherits="open,enablehistory"/>
|
|
|
|
<xul:popupset anonid="popupset" class="autocomplete-result-popupset"/>
|
|
</content>
|
|
|
|
<implementation implements="nsIAccessibleProvider, nsIAutoCompleteInput, nsIDOMXULMenuListElement">
|
|
<field name="mController">null</field>
|
|
<field name="mSearchNames">null</field>
|
|
<field name="mIgnoreInput">false</field>
|
|
<field name="mEnterEvent">null</field>
|
|
<field name="mConsumeRollupEvent">false</field>
|
|
|
|
<field name="mInputElt">
|
|
document.getAnonymousElementByAttribute(this, "anonid", "input");
|
|
</field>
|
|
|
|
<constructor><![CDATA[
|
|
mController = Components.classes["@mozilla.org/autocomplete/controller;1"].
|
|
getService(Components.interfaces.nsIAutoCompleteController);
|
|
]]></constructor>
|
|
|
|
<!-- =================== nsIAccessibleProvider =================== -->
|
|
|
|
<property name="accessibleType" readonly="true">
|
|
<getter>
|
|
<![CDATA[
|
|
// Will be exposed as nsIAccessible::ROLE_AUTOCOMPLETE
|
|
return Components.interfaces.nsIAccessibleProvider.XULCombobox;
|
|
]]>
|
|
</getter>
|
|
</property>
|
|
|
|
<!-- =================== nsIAutoCompleteInput =================== -->
|
|
|
|
<field name="popup"><![CDATA[
|
|
var popup = null;
|
|
var popupId = this.getAttribute("autocompletepopup");
|
|
if (popupId)
|
|
popup = document.getElementById(popupId);
|
|
if (!popup) {
|
|
popup = document.createElement("panel");
|
|
popup.setAttribute("type", "autocomplete");
|
|
popup.setAttribute("noautofocus", "true");
|
|
|
|
var popupset = document.getAnonymousElementByAttribute(this, "anonid", "popupset");
|
|
popupset.appendChild(popup);
|
|
}
|
|
popup.mInput = this;
|
|
popup;
|
|
]]></field>
|
|
|
|
<property name="controller" onget="return this.mController;" readonly="true"/>
|
|
|
|
<property name="popupOpen"
|
|
onget="return this.popup.popupOpen;"
|
|
onset="if (val) this.openPopup(); else this.closePopup();"/>
|
|
|
|
<property name="disableAutoComplete"
|
|
onset="this.setAttribute('disableautocomplete', val); return val;"
|
|
onget="return this.getAttribute('disableautocomplete') == 'true';"/>
|
|
|
|
<property name="completeDefaultIndex"
|
|
onset="this.setAttribute('completedefaultindex', val); return val;"
|
|
onget="return this.getAttribute('completedefaultindex') == 'true';"/>
|
|
|
|
<property name="completeSelectedIndex"
|
|
onset="this.setAttribute('completeselectedindex', val); return val;"
|
|
onget="return this.getAttribute('completeselectedindex') == 'true';"/>
|
|
|
|
<property name="forceComplete"
|
|
onset="this.setAttribute('forcecomplete', val); return val;"
|
|
onget="return this.getAttribute('forcecomplete') == 'true';"/>
|
|
|
|
<property name="minResultsForPopup"
|
|
onset="this.setAttribute('minresultsforpopup', val); return val;"
|
|
onget="return parseInt(this.getAttribute('minresultsforpopup')) || 0;"/>
|
|
|
|
<property name="showCommentColumn"
|
|
onset="this.setAttribute('showcommentcolumn', val); return val;"
|
|
onget="return this.getAttribute('showcommentcolumn') == 'true';"/>
|
|
|
|
<property name="showImageColumn"
|
|
onset="this.setAttribute('showimagecolumn', val); return val;"
|
|
onget="return this.getAttribute('showimagecolumn') == 'true';"/>
|
|
|
|
<property name="timeout"
|
|
onset="this.setAttribute('timeout', val); return val;"
|
|
onget="return parseInt(this.getAttribute('timeout')) || 50;"/>
|
|
|
|
<property name="searchParam"
|
|
onget="return this.getAttribute('autocompletesearchparam');"
|
|
onset="this.setAttribute('autocompletesearchparam', val); return val;"/>
|
|
|
|
<property name="searchCount" readonly="true"
|
|
onget="this.initSearchNames(); return this.mSearchNames.length;"/>
|
|
|
|
<property name="consumeRollupEvent" readonly="true"
|
|
onget="return this.mConsumeRollupEvent;"/>
|
|
|
|
<!-- This is the maximum number of drop-down rows we get when we
|
|
hit the drop marker beside fields that have it (like the URLbar).-->
|
|
<field name="maxDropMarkerRows" readonly="true">14</field>
|
|
|
|
<method name="getSearchAt">
|
|
<parameter name="aIndex"/>
|
|
<body><![CDATA[
|
|
this.initSearchNames();
|
|
return this.mSearchNames[aIndex];
|
|
]]></body>
|
|
</method>
|
|
|
|
<property name="textValue"
|
|
onget="return this.value;">
|
|
<setter><![CDATA[
|
|
// Completing a result should simulate the user typing the result,
|
|
// so fire an input event.
|
|
this.value = val;
|
|
var evt = document.createEvent("UIEvents");
|
|
evt.initUIEvent("input", true, false, window, 0);
|
|
this.mIgnoreInput = true;
|
|
this.dispatchEvent(evt);
|
|
this.mIgnoreInput = false;
|
|
return this.value;
|
|
]]></setter>
|
|
</property>
|
|
|
|
<method name="selectTextRange">
|
|
<parameter name="aStartIndex"/>
|
|
<parameter name="aEndIndex"/>
|
|
<body><![CDATA[
|
|
this.mInputElt.setSelectionRange(aStartIndex, aEndIndex);
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="onSearchComplete">
|
|
<body><![CDATA[
|
|
if (this.mController.matchCount == 0)
|
|
this.popup.setAttribute("nomatch", "true");
|
|
else
|
|
this.popup.removeAttribute("nomatch");
|
|
|
|
this.fireEvent("searchcomplete");
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="onTextEntered">
|
|
<body><![CDATA[
|
|
var rv = this.fireEvent("textentered", this.mEnterEvent);
|
|
this.mEnterEvent = null;
|
|
return rv;
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="onTextReverted">
|
|
<body><![CDATA[
|
|
return this.fireEvent("textreverted");
|
|
]]></body>
|
|
</method>
|
|
|
|
<!-- =================== nsIDOMXULMenuListElement =================== -->
|
|
|
|
<property name="editable" readonly="true"
|
|
onget="return true;" />
|
|
|
|
<property name="crop"
|
|
onset="this.setAttribute('crop',val); return val;"
|
|
onget="return this.getAttribute('crop');"/>
|
|
|
|
<property name="label" readonly="true" onget="return this.mInputElt.value;"/>
|
|
|
|
<property name="open"
|
|
onget="return this.getAttribute('open') == 'true';">
|
|
<setter><![CDATA[
|
|
if (val) {
|
|
this.setAttribute("open", "true");
|
|
this.showHistoryPopup();
|
|
}
|
|
else {
|
|
this.removeAttribute("open");
|
|
this.closePopup();
|
|
}
|
|
]]></setter>
|
|
</property>
|
|
|
|
<!-- =================== PUBLIC MEMBERS =================== -->
|
|
|
|
<property name="value"
|
|
onget="return this.mInputElt.value;">
|
|
<setter><![CDATA[
|
|
this.mIgnoreInput = true;
|
|
this.mInputElt.value = val;
|
|
this.mIgnoreInput = false;
|
|
var event = document.createEvent('Events');
|
|
event.initEvent('ValueChange', true, true);
|
|
this.mInputElt.dispatchEvent(event);
|
|
return val;
|
|
]]></setter>
|
|
</property>
|
|
|
|
<property name="focused" readonly="true"
|
|
onget="return this.getAttribute('focused') == 'true';"/>
|
|
|
|
<!-- maximum number of rows to display at a time -->
|
|
<property name="maxRows"
|
|
onset="this.setAttribute('maxrows', val); return val;"
|
|
onget="return parseInt(this.getAttribute('maxrows')) || 0;"/>
|
|
|
|
<!-- option to allow scrolling through the list via the tab key, rather than
|
|
tab moving focus out of the textbox -->
|
|
<property name="tabScrolling"
|
|
onset="return this.setAttribute('tabscrolling', val); return val;"
|
|
onget="return this.getAttribute('tabscrolling') == 'true';"/>
|
|
|
|
<!-- disable key navigation handling in the popup results -->
|
|
<property name="disableKeyNavigation"
|
|
onset="this.setAttribute('disablekeynavigation', val); return val;"
|
|
onget="return this.getAttribute('disablekeynavigation') == 'true';"/>
|
|
|
|
<!-- option to completely ignore any blur events while
|
|
searches are still going on. This is useful so that nothing
|
|
gets autopicked if the window is required to lose focus for
|
|
some reason (eg in LDAP autocomplete, another window may be
|
|
brought up so that the user can enter a password to authenticate
|
|
to an LDAP server). -->
|
|
<property name="ignoreBlurWhileSearching"
|
|
onset="this.setAttribute('ignoreblurwhilesearching', val); return val;"
|
|
onget="return this.getAttribute('ignoreblurwhilesearching') == 'true';"/>
|
|
|
|
<!-- =================== PRIVATE MEMBERS =================== -->
|
|
|
|
<!-- ::::::::::::: autocomplete controller ::::::::::::: -->
|
|
|
|
<method name="attachController">
|
|
<body><![CDATA[
|
|
if (this.mController.input != this)
|
|
this.mController.input = this;
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="detachController">
|
|
<body><![CDATA[
|
|
try {
|
|
if (this.mController.input == this)
|
|
this.mController.input = null;
|
|
} catch (ex) {
|
|
// nothing really to do.
|
|
}
|
|
]]></body>
|
|
</method>
|
|
|
|
<!-- ::::::::::::: popup opening ::::::::::::: -->
|
|
|
|
<method name="openPopup">
|
|
<body><![CDATA[
|
|
this.popup.openAutocompletePopup(this, this);
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="closePopup">
|
|
<body><![CDATA[
|
|
this.mConsumeRollupEvent = false;
|
|
this.popup.closePopup();
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="showHistoryPopup">
|
|
<body><![CDATA[
|
|
// Store our "normal" maxRows on the popup, so that it can reset the
|
|
// value when the popup is hidden.
|
|
this.popup._normalMaxRows = this.maxRows;
|
|
|
|
// Increase our maxRows temporarily, since we want the dropdown to
|
|
// be bigger in this case. The popup's popupshowing/popuphiding
|
|
// handlers will take care of resetting this.
|
|
this.maxRows = this.maxDropMarkerRows;
|
|
|
|
// Ensure that we have focus.
|
|
if (!this.focused)
|
|
this.focus();
|
|
this.mConsumeRollupEvent = true;
|
|
this.attachController();
|
|
this.mController.startSearch("");
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="toggleHistoryPopup">
|
|
<body><![CDATA[
|
|
if (!this.popup.mPopupOpen)
|
|
this.showHistoryPopup();
|
|
else
|
|
this.closePopup();
|
|
]]></body>
|
|
</method>
|
|
|
|
<!-- ::::::::::::: event dispatching ::::::::::::: -->
|
|
|
|
<method name="fireEvent">
|
|
<parameter name="aEventType"/>
|
|
<body><![CDATA[
|
|
var cancel = false;
|
|
// handle any xml attribute event handlers
|
|
var handler = this.getAttribute("on"+aEventType);
|
|
if (handler) {
|
|
var fn = new Function("eventType", "param", handler);
|
|
cancel = fn.apply(this, arguments);
|
|
}
|
|
|
|
return cancel;
|
|
]]></body>
|
|
</method>
|
|
|
|
<!-- ::::::::::::: key handling ::::::::::::: -->
|
|
|
|
<method name="onKeyPress">
|
|
<parameter name="aEvent"/>
|
|
<body><![CDATA[
|
|
//XXXpch this is so bogus...
|
|
if (aEvent.getPreventDefault())
|
|
return false;
|
|
|
|
var cancel = false;
|
|
|
|
// Catch any keys that could potentially move the caret. Ctrl can be
|
|
// used in combination with these keys, so only make sure that Alt
|
|
// isn't used.
|
|
if (!this.disableKeyNavigation && !aEvent.altKey) {
|
|
switch (aEvent.keyCode) {
|
|
case KeyEvent.DOM_VK_LEFT:
|
|
case KeyEvent.DOM_VK_RIGHT:
|
|
case KeyEvent.DOM_VK_HOME:
|
|
cancel = this.mController.handleKeyNavigation(aEvent.keyCode);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Handle keys that are not part of a keyboard shortcut (no Ctrl or Alt)
|
|
if (!this.disableKeyNavigation && !aEvent.ctrlKey && !aEvent.altKey) {
|
|
switch (aEvent.keyCode) {
|
|
case KeyEvent.DOM_VK_TAB:
|
|
if (this.tabScrolling && this.popup.mPopupOpen)
|
|
cancel = this.mController.handleKeyNavigation(aEvent.shiftKey ?
|
|
KeyEvent.DOM_VK_UP :
|
|
KeyEvent.DOM_VK_DOWN);
|
|
break;
|
|
case KeyEvent.DOM_VK_UP:
|
|
case KeyEvent.DOM_VK_DOWN:
|
|
case KeyEvent.DOM_VK_PAGE_UP:
|
|
case KeyEvent.DOM_VK_PAGE_DOWN:
|
|
cancel = this.mController.handleKeyNavigation(aEvent.keyCode);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Handle keys we know aren't part of a shortcut, even with Alt or
|
|
// Ctrl.
|
|
switch (aEvent.keyCode) {
|
|
case KeyEvent.DOM_VK_ESCAPE:
|
|
cancel = this.mController.handleEscape();
|
|
break;
|
|
case KeyEvent.DOM_VK_RETURN:
|
|
this.mEnterEvent = aEvent;
|
|
cancel = this.mController.handleEnter();
|
|
break;
|
|
case KeyEvent.DOM_VK_DELETE:
|
|
#ifdef XP_MACOSX
|
|
case KeyEvent.DOM_VK_BACK_SPACE:
|
|
if (aEvent.shiftKey)
|
|
#endif
|
|
cancel = this.mController.handleDelete();
|
|
break;
|
|
case KeyEvent.DOM_VK_DOWN:
|
|
case KeyEvent.DOM_VK_UP:
|
|
if (aEvent.altKey)
|
|
this.toggleHistoryPopup();
|
|
break;
|
|
#ifndef XP_MACOSX
|
|
case KeyEvent.DOM_VK_F4:
|
|
this.toggleHistoryPopup();
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
if (cancel) {
|
|
aEvent.stopPropagation();
|
|
aEvent.preventDefault();
|
|
}
|
|
|
|
return true;
|
|
]]></body>
|
|
</method>
|
|
|
|
<!-- ::::::::::::: miscellaneous ::::::::::::: -->
|
|
|
|
<method name="initSearchNames">
|
|
<body><![CDATA[
|
|
if (!this.mSearchNames) {
|
|
var names = this.getAttribute("autocompletesearch");
|
|
if (!names)
|
|
this.mSearchNames = [];
|
|
else
|
|
this.mSearchNames = names.split(" ");
|
|
}
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="ifSetAttribute">
|
|
<parameter name="aAttr"/>
|
|
<parameter name="aVal"/>
|
|
<body><![CDATA[
|
|
if (!this.hasAttribute(aAttr))
|
|
this.setAttribute(aAttr, aVal);
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="_focus">
|
|
<!-- doesn't reset this.mController -->
|
|
<body><![CDATA[
|
|
this._dontBlur = true;
|
|
this.focus();
|
|
this._dontBlur = false;
|
|
]]></body>
|
|
</method>
|
|
|
|
</implementation>
|
|
|
|
<handlers>
|
|
<handler event="input"
|
|
action="if (!this.mIgnoreInput && this.mController.input == this) this.mController.handleText(false);"/>
|
|
|
|
<handler event="keypress" phase="capturing"
|
|
action="return this.onKeyPress(event);"/>
|
|
|
|
<handler event="compositionstart" phase="capturing"
|
|
action="if (this.mController.input == this) this.mController.handleStartComposition();"/>
|
|
|
|
<handler event="compositionend" phase="capturing"
|
|
action="if (this.mController.input == this) this.mController.handleEndComposition();"/>
|
|
|
|
<handler event="focus" phase="capturing"
|
|
action="this.attachController();"/>
|
|
|
|
<handler event="blur" phase="capturing"
|
|
action="if (!this._dontBlur) this.detachController();"/>
|
|
|
|
<handler event="contextmenu" phase="capturing"
|
|
action="this.closePopup();"/>
|
|
</handlers>
|
|
</binding>
|
|
|
|
<binding id="autocomplete-result-popup" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete-base-popup">
|
|
<resources>
|
|
<stylesheet src="chrome://global/skin/tree.css"/>
|
|
<stylesheet src="chrome://global/skin/autocomplete.css"/>
|
|
</resources>
|
|
|
|
<content ignorekeys="true">
|
|
<xul:tree anonid="tree" class="autocomplete-tree plain" hidecolumnpicker="true" flex="1">
|
|
<xul:treecols anonid="treecols">
|
|
<xul:treecol id="treecolAutoCompleteValue" class="autocomplete-treecol" flex="1" overflow="true"/>
|
|
</xul:treecols>
|
|
<xul:treechildren class="autocomplete-treebody"/>
|
|
</xul:tree>
|
|
</content>
|
|
|
|
<implementation>
|
|
<field name="mShowCommentColumn">false</field>
|
|
<field name="mShowImageColumn">false</field>
|
|
|
|
<property name="showCommentColumn"
|
|
onget="return this.mShowCommentColumn;">
|
|
<setter>
|
|
<![CDATA[
|
|
if (!val && this.mShowCommentColumn) {
|
|
// reset the flex on the value column and remove the comment column
|
|
document.getElementById("treecolAutoCompleteValue").setAttribute("flex", 1);
|
|
this.removeColumn("treecolAutoCompleteComment");
|
|
} else if (val && !this.mShowCommentColumn) {
|
|
// reset the flex on the value column and add the comment column
|
|
document.getElementById("treecolAutoCompleteValue").setAttribute("flex", 2);
|
|
this.addColumn({id: "treecolAutoCompleteComment", flex: 1});
|
|
}
|
|
this.mShowCommentColumn = val;
|
|
return val;
|
|
]]>
|
|
</setter>
|
|
</property>
|
|
|
|
<property name="showImageColumn"
|
|
onget="return this.mShowImageColumn;">
|
|
<setter>
|
|
<![CDATA[
|
|
if (!val && this.mShowImageColumn) {
|
|
// remove the image column
|
|
this.removeColumn("treecolAutoCompleteImage");
|
|
} else if (val && !this.mShowImageColumn) {
|
|
// add the image column
|
|
this.addColumn({id: "treecolAutoCompleteImage", flex: 1});
|
|
}
|
|
this.mShowImageColumn = val;
|
|
return val;
|
|
]]>
|
|
</setter>
|
|
</property>
|
|
|
|
|
|
<method name="addColumn">
|
|
<parameter name="aAttrs"/>
|
|
<body>
|
|
<![CDATA[
|
|
var col = document.createElement("treecol");
|
|
col.setAttribute("class", "autocomplete-treecol");
|
|
for (var name in aAttrs)
|
|
col.setAttribute(name, aAttrs[name]);
|
|
this.treecols.appendChild(col);
|
|
return col;
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="removeColumn">
|
|
<parameter name="aColId"/>
|
|
<body>
|
|
<![CDATA[
|
|
return this.treecols.removeChild(document.getElementById(aColId));
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<property name="selectedIndex"
|
|
onget="return this.tree.currentIndex;">
|
|
<setter>
|
|
<![CDATA[
|
|
this.tree.view.selection.select(val);
|
|
if (this.tree.treeBoxObject.height > 0)
|
|
this.tree.treeBoxObject.ensureRowIsVisible(val < 0 ? 0 : val);
|
|
// Fire select event on xul:tree so that accessibility API
|
|
// support layer can fire appropriate accessibility events.
|
|
var event = document.createEvent('Events');
|
|
event.initEvent("select", true, true);
|
|
this.tree.dispatchEvent(event);
|
|
return val;
|
|
]]></setter>
|
|
</property>
|
|
|
|
<method name="adjustHeight">
|
|
<body>
|
|
<![CDATA[
|
|
// detect the desired height of the tree
|
|
var bx = this.tree.treeBoxObject;
|
|
var view = this.tree.view;
|
|
if (!view)
|
|
return;
|
|
var rows = this.maxRows;
|
|
if (!view.rowCount || (rows && view.rowCount < rows))
|
|
rows = view.rowCount;
|
|
|
|
var height = rows * bx.rowHeight;
|
|
|
|
if (height == 0)
|
|
this.tree.setAttribute("collapsed", "true");
|
|
else {
|
|
if (this.tree.hasAttribute("collapsed"))
|
|
this.tree.removeAttribute("collapsed");
|
|
|
|
this.tree.setAttribute("height", height);
|
|
}
|
|
this.tree.setAttribute("hidescrollbar", view.rowCount <= rows);
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="openAutocompletePopup">
|
|
<parameter name="aInput"/>
|
|
<parameter name="aElement"/>
|
|
<body><![CDATA[
|
|
// until we have "baseBinding", (see bug #373652) this allows
|
|
// us to override openAutocompletePopup(), but still call
|
|
// the method on the base class
|
|
this._openAutocompletePopup(aInput, aElement);
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="_openAutocompletePopup">
|
|
<parameter name="aInput"/>
|
|
<parameter name="aElement"/>
|
|
<body><![CDATA[
|
|
if (!this.mPopupOpen) {
|
|
this.mInput = aInput;
|
|
this.view = aInput.controller.QueryInterface(Components.interfaces.nsITreeView);
|
|
this.invalidate();
|
|
|
|
this.showCommentColumn = this.mInput.showCommentColumn;
|
|
this.showImageColumn = this.mInput.showImageColumn;
|
|
|
|
document.popupNode = null;
|
|
|
|
var rect = aElement.getBoundingClientRect();
|
|
var width = rect.right - rect.left;
|
|
this.setAttribute("width", width > 100 ? width : 100);
|
|
this.openPopup(aElement, "after_start", 0, 0, false, false);
|
|
|
|
this.popupBoxObject.setConsumeRollupEvent(this.mInput.consumeRollupEvent);
|
|
}
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="invalidate">
|
|
<body><![CDATA[
|
|
this.adjustHeight();
|
|
this.tree.treeBoxObject.invalidate();
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="selectBy">
|
|
<parameter name="aReverse"/>
|
|
<parameter name="aPage"/>
|
|
<body><![CDATA[
|
|
try {
|
|
var amount = aPage ? 5 : 1;
|
|
this.selectedIndex = this.getNextIndex(aReverse, amount, this.selectedIndex, this.tree.view.rowCount-1);
|
|
if (this.selectedIndex == -1) {
|
|
this.input._focus();
|
|
}
|
|
} catch (ex) {
|
|
// do nothing - occasionally timer-related js errors happen here
|
|
// e.g. "this.selectedIndex has no properties", when you type fast and hit a
|
|
// navigation key before this popup has opened
|
|
}
|
|
]]></body>
|
|
</method>
|
|
|
|
<!-- =================== PUBLIC MEMBERS =================== -->
|
|
|
|
<field name="tree">
|
|
document.getAnonymousElementByAttribute(this, "anonid", "tree");
|
|
</field>
|
|
|
|
<field name="treecols">
|
|
document.getAnonymousElementByAttribute(this, "anonid", "treecols");
|
|
</field>
|
|
|
|
<property name="view"
|
|
onget="return this.mView;">
|
|
<setter><![CDATA[
|
|
// We must do this by hand because the tree binding may not be ready yet
|
|
this.mView = val;
|
|
var bx = this.tree.boxObject;
|
|
bx = bx.QueryInterface(Components.interfaces.nsITreeBoxObject);
|
|
bx.view = val;
|
|
]]></setter>
|
|
</property>
|
|
|
|
</implementation>
|
|
</binding>
|
|
|
|
<binding id="autocomplete-base-popup" extends="chrome://global/content/bindings/popup.xml#popup">
|
|
<implementation implements="nsIAutoCompletePopup">
|
|
<field name="mInput">null</field>
|
|
<field name="mPopupOpen">false</field>
|
|
|
|
<!-- =================== nsIAutoCompletePopup =================== -->
|
|
|
|
<property name="input" readonly="true"
|
|
onget="return this.mInput"/>
|
|
|
|
<property name="overrideValue" readonly="true"
|
|
onget="return null;"/>
|
|
|
|
<property name="popupOpen" readonly="true"
|
|
onget="return this.mPopupOpen;"/>
|
|
|
|
<method name="closePopup">
|
|
<body>
|
|
<![CDATA[
|
|
if (this.mPopupOpen) {
|
|
this.hidePopup();
|
|
document.popupNode = null;
|
|
this.removeAttribute("width");
|
|
}
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<!-- This is the default number of rows that we give the autocomplete
|
|
popup when the textbox doesn't have a "maxrows" attribute
|
|
for us to use. -->
|
|
<field name="defaultMaxRows" readonly="true">6</field>
|
|
|
|
<!-- In some cases (e.g. when the input's dropmarker button is clicked),
|
|
the input wants to display a popup with more rows. In that case, it
|
|
should increase its maxRows property and store the "normal" maxRows
|
|
in this field. When the popup is hidden, we restore the input's
|
|
maxRows to the value stored in this field.
|
|
|
|
This field is set to -1 between uses so that we can tell when it's
|
|
been set by the input and when we need to set it in the popupshowing
|
|
handler. -->
|
|
<field name="_normalMaxRows">-1</field>
|
|
|
|
<property name="maxRows" readonly="true">
|
|
<getter>
|
|
<![CDATA[
|
|
return (this.mInput && this.mInput.maxRows) || this.defaultMaxRows;
|
|
]]>
|
|
</getter>
|
|
</property>
|
|
|
|
<method name="getNextIndex">
|
|
<parameter name="aReverse"/>
|
|
<parameter name="aAmount"/>
|
|
<parameter name="aIndex"/>
|
|
<parameter name="aMaxRow"/>
|
|
<body><![CDATA[
|
|
if (aMaxRow < 0)
|
|
return -1;
|
|
|
|
var newIdx = aIndex + (aReverse?-1:1)*aAmount;
|
|
if (aReverse && aIndex == -1 || newIdx > aMaxRow && aIndex != aMaxRow)
|
|
newIdx = aMaxRow;
|
|
else if (!aReverse && aIndex == -1 || newIdx < 0 && aIndex != 0)
|
|
newIdx = 0;
|
|
|
|
if (newIdx < 0 && aIndex == 0 || newIdx > aMaxRow && aIndex == aMaxRow)
|
|
aIndex = -1;
|
|
else
|
|
aIndex = newIdx;
|
|
|
|
return aIndex;
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="onPopupClick">
|
|
<parameter name="aEvent"/>
|
|
<body><![CDATA[
|
|
var controller = this.view.QueryInterface(Components.interfaces.nsIAutoCompleteController);
|
|
controller.handleEnter();
|
|
]]></body>
|
|
</method>
|
|
</implementation>
|
|
|
|
<handlers>
|
|
<handler event="popupshowing"><![CDATA[
|
|
// If normalMaxRows wasn't already set by the input, then set it here
|
|
// so that we restore the correct number when the popup is hidden.
|
|
if (this._normalMaxRows < 0)
|
|
this._normalMaxRows = this.mInput.maxRows;
|
|
|
|
this.mPopupOpen = true;
|
|
]]></handler>
|
|
|
|
<handler event="popuphiding"><![CDATA[
|
|
var controller = this.view.QueryInterface(Components.interfaces.nsIAutoCompleteController);
|
|
controller.stopSearch();
|
|
|
|
this.mPopupOpen = false;
|
|
|
|
// Reset the maxRows property to the cached "normal" value, and reset
|
|
// _normalMaxRows so that we can detect whether it was set by the input
|
|
// when the popupshowing handler runs.
|
|
this.mInput.maxRows = this._normalMaxRows;
|
|
this._normalMaxRows = -1;
|
|
]]></handler>
|
|
</handlers>
|
|
</binding>
|
|
|
|
<binding id="autocomplete-rich-result-popup" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete-base-popup">
|
|
<resources>
|
|
<stylesheet src="chrome://global/skin/autocomplete.css"/>
|
|
</resources>
|
|
|
|
<content ignorekeys="true">
|
|
<xul:richlistbox anonid="richlistbox" class="autocomplete-richlistbox" flex="1"/>
|
|
</content>
|
|
|
|
<implementation implements="nsIAutoCompletePopup">
|
|
<field name="_currentIndex">0</field>
|
|
<field name="_rowHeight">0</field>
|
|
|
|
<!-- =================== nsIAutoCompletePopup =================== -->
|
|
|
|
<property name="selectedIndex"
|
|
onget="return this.richlistbox.selectedIndex;">
|
|
<setter>
|
|
<![CDATA[
|
|
this.richlistbox.selectedIndex = val;
|
|
this.richlistbox.ensureSelectedElementIsVisible();
|
|
return val;
|
|
]]>
|
|
</setter>
|
|
</property>
|
|
|
|
<method name="openAutocompletePopup">
|
|
<parameter name="aInput"/>
|
|
<parameter name="aElement"/>
|
|
<body>
|
|
<![CDATA[
|
|
// until we have "baseBinding", (see bug #373652) this allows
|
|
// us to override openAutocompletePopup(), but still call
|
|
// the method on the base class
|
|
this._openAutocompletePopup(aInput, aElement);
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="_openAutocompletePopup">
|
|
<parameter name="aInput"/>
|
|
<parameter name="aElement"/>
|
|
<body>
|
|
<![CDATA[
|
|
if (!this.mPopupOpen) {
|
|
this.mInput = aInput;
|
|
document.popupNode = null;
|
|
|
|
var rect = aElement.getBoundingClientRect();
|
|
var width = rect.right - rect.left;
|
|
this.setAttribute("width", width > 100 ? width : 100);
|
|
// invalidate() depends on the width attribute
|
|
this.invalidate();
|
|
|
|
this.openPopup(aElement, "after_start", 0, 0, false, false);
|
|
this.popupBoxObject.setConsumeRollupEvent(this.mInput.consumeRollupEvent);
|
|
}
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="invalidate">
|
|
<body>
|
|
<![CDATA[
|
|
// collapsed if no matches
|
|
this.richlistbox.collapsed = (this._matchCount == 0);
|
|
|
|
// make sure to collapse any existing richlistitems
|
|
// that aren't going to be used
|
|
var existingItemsCount = this.richlistbox.childNodes.length;
|
|
for (var i = this._matchCount; i < existingItemsCount; i++)
|
|
this.richlistbox.childNodes[i].collapsed = true;
|
|
|
|
var clearSelection = true;
|
|
var currentSelectedIndex = this.selectedIndex;
|
|
if (currentSelectedIndex != -1 && currentSelectedIndex < this._matchCount) {
|
|
var controller = this.mInput.controller;
|
|
var item = this.richlistbox.childNodes[currentSelectedIndex];
|
|
if (item.getAttribute("url") == controller.getValueAt(currentSelectedIndex) &&
|
|
item.getAttribute("title") == controller.getCommentAt(currentSelectedIndex)) {
|
|
clearSelection = false;
|
|
}
|
|
}
|
|
if (currentSelectedIndex != -1 && clearSelection)
|
|
this.selectedIndex = -1;
|
|
|
|
this._currentIndex = 0;
|
|
this._appendCurrentResult();
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<property name="maxResults" readonly="true">
|
|
<getter>
|
|
<![CDATA[
|
|
// this is how many richlistitems will be kept around
|
|
// (note, this getter may be overridden)
|
|
return 20;
|
|
]]>
|
|
</getter>
|
|
</property>
|
|
|
|
<property name="_matchCount" readonly="true">
|
|
<getter>
|
|
<![CDATA[
|
|
return Math.min(this.mInput.controller.matchCount, this.maxResults);
|
|
]]>
|
|
</getter>
|
|
</property>
|
|
|
|
<method name="_appendCurrentResult">
|
|
<body>
|
|
<![CDATA[
|
|
var controller = this.mInput.controller;
|
|
|
|
// detect the desired height of the tree
|
|
var rows = this.maxRows;
|
|
if (!this._matchCount || (rows && this._matchCount < rows))
|
|
rows = this._matchCount;
|
|
|
|
// until we have support for "rows" on richlistbox,
|
|
// determine the height dynamically. (see bug #401939)
|
|
if (!this._rowHeight && this.richlistbox.childNodes.length)
|
|
this._rowHeight = this.richlistbox.childNodes[0].boxObject.height;
|
|
|
|
var height = this._rowHeight * rows;
|
|
if (this._rowHeight && this.richlistbox.height != height)
|
|
this.richlistbox.height = height;
|
|
|
|
if (this._currentIndex < this._matchCount) {
|
|
var existingItemsCount = this.richlistbox.childNodes.length;
|
|
var item;
|
|
|
|
// trim the leading/trailing whitespace
|
|
var trimmedSearchString = controller.searchString.replace(/^\s+/, "").replace(/\s+$/, "");
|
|
|
|
if (this._currentIndex < existingItemsCount) {
|
|
// re-use the existing item
|
|
item = this.richlistbox.childNodes[this._currentIndex];
|
|
}
|
|
else {
|
|
// need to create a new item
|
|
item = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "richlistitem");
|
|
}
|
|
|
|
// set these attributes before we set the class
|
|
// so that we can use them from the contructor
|
|
item.setAttribute("image", controller.getImageAt(this._currentIndex));
|
|
item.setAttribute("url", controller.getValueAt(this._currentIndex));
|
|
item.setAttribute("title", controller.getCommentAt(this._currentIndex));
|
|
item.setAttribute("type", controller.getStyleAt(this._currentIndex));
|
|
item.setAttribute("text", trimmedSearchString);
|
|
|
|
var width = this.getAttribute("width");
|
|
|
|
if (this._currentIndex < existingItemsCount) {
|
|
// re-use the existing item
|
|
item._adjustAcItem();
|
|
item.collapsed = false;
|
|
|
|
var current_width = item.getAttribute("current_width");
|
|
|
|
if (width && current_width != width) {
|
|
item.setAttribute("current_width", width);
|
|
item._adjustWidth();
|
|
}
|
|
}
|
|
else {
|
|
// created a new item
|
|
if (width)
|
|
item.setAttribute("current_width", width);
|
|
|
|
// set the class at the end so we can use the attributes
|
|
// in the xbl constructor
|
|
item.className = "autocomplete-richlistitem";
|
|
this.richlistbox.appendChild(item);
|
|
}
|
|
|
|
this._currentIndex++;
|
|
|
|
// yield after creating each item so that the UI is responsive
|
|
// note, when browser.urlbar.maxRichResults is small, this setTimeout()
|
|
// is not needed, and we could call this._appendCurrentResult() directly.
|
|
// but the bigger browser.urlbar.maxRichResults gets, this is necessary
|
|
// so that typing the url bar is responsive
|
|
setTimeout(function (self) { self._appendCurrentResult(); }, 0, this);
|
|
}
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="selectBy">
|
|
<parameter name="aReverse"/>
|
|
<parameter name="aPage"/>
|
|
<body>
|
|
<![CDATA[
|
|
try {
|
|
var amount = aPage ? 5 : 1;
|
|
|
|
// because we collapsed unused items, we can't use this.richlistbox.getRowCount(), we need to use the matchCount
|
|
this.selectedIndex = this.getNextIndex(aReverse, amount, this.selectedIndex, this._matchCount - 1);
|
|
if (this.selectedIndex == -1) {
|
|
this.input._focus();
|
|
}
|
|
} catch (ex) {
|
|
// do nothing - occasionally timer-related js errors happen here
|
|
// e.g. "this.selectedIndex has no properties", when you type fast and hit a
|
|
// navigation key before this popup has opened
|
|
}
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<field name="richlistbox">
|
|
document.getAnonymousElementByAttribute(this, "anonid", "richlistbox");
|
|
</field>
|
|
|
|
<property name="view"
|
|
onget="return this.mInput.controller;"
|
|
onset="return val;"/>
|
|
|
|
</implementation>
|
|
</binding>
|
|
|
|
<binding id="autocomplete-richlistitem" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
|
|
<content>
|
|
<xul:vbox>
|
|
<xul:hbox align="center">
|
|
<xul:image xbl:inherits="src=image" class="ac-site-icon"/>
|
|
<xul:scrollbox anonid="title-scrollbox" class="ac-title">
|
|
# note, we rely on the newlines here so that we have
|
|
# a textNode before and after the span. see _setUpDescription()
|
|
# for more details
|
|
<xul:description anonid="title" class="ac-normal-text ac-comment" xbl:inherits="selected">
|
|
<xul:label class="ac-emphasize-text"/>
|
|
</xul:description>
|
|
</xul:scrollbox>
|
|
<xul:label anonid="title-overflow-ellipsis" xbl:inherits="selected"
|
|
class="ac-ellipsis-after ac-comment"/>
|
|
<xul:image anonid="type-image" class="ac-type-icon"/>
|
|
</xul:hbox>
|
|
<xul:hbox align="center">
|
|
<xul:spacer class="ac-site-icon"/>
|
|
<xul:scrollbox anonid="url-scrollbox" class="ac-url">
|
|
# note, we rely on the newlines here so that we have
|
|
# a textNode before and after the span. see _setUpDescription()
|
|
# for more details
|
|
<xul:description anonid="url" class="ac-normal-text ac-url-text" xbl:inherits="selected">
|
|
<xul:label class="ac-emphasize-text"/>
|
|
</xul:description>
|
|
</xul:scrollbox>
|
|
<xul:label anonid="url-overflow-ellipsis" xbl:inherits="selected"
|
|
class="ac-ellipsis-after ac-url-text"/>
|
|
<xul:spacer class="ac-type-icon"/>
|
|
</xul:hbox>
|
|
</xul:vbox>
|
|
</content>
|
|
<implementation>
|
|
<constructor>
|
|
<![CDATA[
|
|
this._urlOverflowEllipsis = document.getAnonymousElementByAttribute(this, "anonid", "url-overflow-ellipsis");
|
|
this._titleOverflowEllipsis = document.getAnonymousElementByAttribute(this, "anonid", "title-overflow-ellipsis");
|
|
|
|
this._typeImage = document.getAnonymousElementByAttribute(this, "anonid", "type-image");
|
|
|
|
this._urlScrollbox = document.getAnonymousElementByAttribute(this, "anonid", "url-scrollbox");
|
|
this._url = document.getAnonymousElementByAttribute(this, "anonid", "url");
|
|
|
|
this._titleScrollbox = document.getAnonymousElementByAttribute(this, "anonid", "title-scrollbox");
|
|
this._title = document.getAnonymousElementByAttribute(this, "anonid", "title");
|
|
|
|
// XXX hack
|
|
// for the first richlistitem, when we call _adjustWidth()
|
|
// from the xbl constructor, these elements don't have widths
|
|
// but we rely on those widths to properly set the widths
|
|
// of the scrollboxes. if we don't have the widths
|
|
// try again on a timeout.
|
|
if (!this._urlOverflowEllipsis.boxObject.width ||
|
|
!this._titleOverflowEllipsis.boxObject.width)
|
|
setTimeout(function(self) { self._adjustWidth(); }, 0, this);
|
|
else
|
|
this._adjustWidth();
|
|
|
|
this._adjustAcItem();
|
|
]]>
|
|
</constructor>
|
|
|
|
<field name="_ellipsis">null</field>
|
|
|
|
<property name="ellipsis" readonly="true">
|
|
<getter>
|
|
<![CDATA[
|
|
if (!this._ellipsis) {
|
|
try {
|
|
var pref = Components.classes["@mozilla.org/preferences-service;1"]
|
|
.getService(Components.interfaces.nsIPrefBranch);
|
|
this._ellipsis = pref.getComplexValue("intl.ellipsis",
|
|
Components.interfaces.nsIPrefLocalizedString).data;
|
|
} catch (ex) {
|
|
this._ellipsis = "\u2026";
|
|
}
|
|
}
|
|
return this._ellipsis;
|
|
]]>
|
|
</getter>
|
|
</property>
|
|
|
|
<method name="_setUpDescription">
|
|
<parameter name="aDescriptionElement"/>
|
|
<parameter name="aText"/>
|
|
<parameter name="aScrollBoxObject"/>
|
|
<parameter name="aEllipsis"/>
|
|
<parameter name="aMatchIndex"/>
|
|
<parameter name="aMatchLength"/>
|
|
<body>
|
|
<![CDATA[
|
|
if (aMatchIndex == -1) {
|
|
aDescriptionElement.childNodes[0].textContent = aText;
|
|
aDescriptionElement.childNodes[1].value = "";
|
|
aDescriptionElement.childNodes[2].textContent = "";
|
|
}
|
|
else {
|
|
aDescriptionElement.childNodes[0].textContent =
|
|
aText.substring(0, aMatchIndex);
|
|
|
|
aDescriptionElement.childNodes[1].value =
|
|
aText.substring(aMatchIndex, aMatchIndex + aMatchLength);
|
|
|
|
aDescriptionElement.childNodes[2].textContent =
|
|
aText.substring(aMatchIndex + aMatchLength);
|
|
}
|
|
|
|
// need to set up the ellipsis on a time out,
|
|
// because the width of the description element may still be 0
|
|
// even though the aText length is non-zero
|
|
setTimeout(function(self) { self._setUpEllipsis(aScrollBoxObject, aDescriptionElement, aEllipsis); }, 0, this);
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="_adjustAcItem">
|
|
<body>
|
|
<![CDATA[
|
|
var text = this.getAttribute("text");
|
|
var url = this.getAttribute("url");
|
|
var title = this.getAttribute("title");
|
|
var type = this.getAttribute("type");
|
|
|
|
var url_sbo = this._urlScrollbox.boxObject.QueryInterface(Components.interfaces.nsIScrollBoxObject);
|
|
var title_sbo = this._titleScrollbox.boxObject.QueryInterface(Components.interfaces.nsIScrollBoxObject);
|
|
|
|
this._typeImage.className = type ? ("ac-result-type-" + type) : "";
|
|
|
|
// if we aren't matching any text
|
|
// (the user has clicked on the history drop down
|
|
// or this result is a "tag" match, don't bold any matching text
|
|
if (text == "" || type == "tag") {
|
|
this._setUpDescription(this._url, url, url_sbo, this._urlOverflowEllipsis, -1, -1);
|
|
this._setUpDescription(this._title, title, title_sbo, this._titleOverflowEllipsis, -1, -1);
|
|
return;
|
|
}
|
|
|
|
// emphasize the matching text, first try title, and then try url
|
|
var needle = text.toLowerCase();
|
|
var hay = title.toLowerCase();
|
|
var index = hay.indexOf(needle);
|
|
|
|
this._setUpDescription(this._title, title, title_sbo, this._titleOverflowEllipsis, index, text.length);
|
|
|
|
// if we didn't find it in the url, check the title
|
|
if (index == -1) {
|
|
hay = url.toLowerCase();
|
|
index = hay.indexOf(needle);
|
|
}
|
|
else
|
|
index = -1;
|
|
|
|
this._setUpDescription(this._url, url, url_sbo, this._urlOverflowEllipsis, index, text.length);
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="_adjustWidth">
|
|
<body>
|
|
<![CDATA[
|
|
// XXX hack
|
|
// see bug #399644 comment #39 for some details
|
|
// add 16px for the site image, 8 for the margin right of the site icon,
|
|
// 16px for the type image, and 24px for scroll bar
|
|
var pixelsUsedForNonText = 16 + 8 + 16 + 24;
|
|
|
|
var current_url_width = this.getAttribute("current_width") -
|
|
pixelsUsedForNonText -
|
|
this._urlOverflowEllipsis.boxObject.width;
|
|
|
|
var current_title_width = this.getAttribute("current_width") -
|
|
pixelsUsedForNonText -
|
|
this._titleOverflowEllipsis.boxObject.width;
|
|
|
|
this._urlScrollbox.minWidth = current_url_width;
|
|
this._urlScrollbox.maxWidth = current_url_width;
|
|
|
|
this._titleScrollbox.minWidth = current_title_width;
|
|
this._titleScrollbox.maxWidth = current_title_width;
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="_setUpEllipsis">
|
|
<parameter name="aScrollBoxObject"/>
|
|
<parameter name="aDescriptionElement"/>
|
|
<parameter name="aEllipsis"/>
|
|
<body>
|
|
<![CDATA[
|
|
// determine if we need to show the ellipsis
|
|
if ((aScrollBoxObject.x + aScrollBoxObject.width) >= (aDescriptionElement.boxObject.x + aDescriptionElement.boxObject.width))
|
|
aEllipsis.value = "";
|
|
else
|
|
aEllipsis.value = this.ellipsis;
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
</implementation>
|
|
</binding>
|
|
|
|
<binding id="autocomplete-tree" extends="chrome://global/content/bindings/tree.xml#tree">
|
|
<content>
|
|
<children includes="treecols"/>
|
|
<xul:treerows class="autocomplete-treerows tree-rows" xbl:inherits="hidescrollbar" flex="1">
|
|
<children/>
|
|
</xul:treerows>
|
|
</content>
|
|
</binding>
|
|
|
|
<binding id="autocomplete-richlistbox" extends="chrome://global/content/bindings/richlistbox.xml#richlistbox">
|
|
<implementation>
|
|
<field name="mLastMoveTime">Date.now()</field>
|
|
</implementation>
|
|
<handlers>
|
|
<handler event="mouseup">
|
|
<![CDATA[
|
|
// don't call onPopupClick for the scrollbar buttons, thumb, slider, etc.
|
|
var item = event.originalTarget;
|
|
|
|
while (item && item.localName != "richlistitem")
|
|
item = item.parentNode;
|
|
|
|
if (!item)
|
|
return;
|
|
|
|
this.parentNode.onPopupClick(event);
|
|
]]>
|
|
</handler>
|
|
|
|
<handler event="mousemove">
|
|
<![CDATA[
|
|
if (Date.now() - this.mLastMoveTime > 30) {
|
|
var item = event.target;
|
|
|
|
while (item && item.localName != "richlistitem")
|
|
item = item.parentNode;
|
|
|
|
if (!item)
|
|
return;
|
|
|
|
var rc = this.getIndexOfItem(item);
|
|
if (rc != this.selectedIndex)
|
|
this.selectedIndex = rc;
|
|
|
|
this.mLastMoveTime = Date.now();
|
|
}
|
|
]]>
|
|
</handler>
|
|
</handlers>
|
|
</binding>
|
|
|
|
<binding id="autocomplete-treebody">
|
|
<implementation>
|
|
<field name="mLastMoveTime">Date.now()</field>
|
|
</implementation>
|
|
|
|
<handlers>
|
|
<handler event="mouseup" action="this.parentNode.parentNode.onPopupClick(event);"/>
|
|
|
|
<handler event="mousedown"><![CDATA[
|
|
var rc = this.parentNode.treeBoxObject.getRowAt(event.clientX, event.clientY);
|
|
if (rc != this.parentNode.currentIndex)
|
|
this.parentNode.view.selection.select(rc);
|
|
]]></handler>
|
|
|
|
<handler event="mousemove"><![CDATA[
|
|
if (Date.now() - this.mLastMoveTime > 30) {
|
|
var rc = this.parentNode.treeBoxObject.getRowAt(event.clientX, event.clientY);
|
|
if (rc != this.parentNode.currentIndex)
|
|
this.parentNode.view.selection.select(rc);
|
|
this.mLastMoveTime = Date.now();
|
|
}
|
|
]]></handler>
|
|
</handlers>
|
|
</binding>
|
|
|
|
<binding id="autocomplete-treerows">
|
|
<content>
|
|
<xul:hbox flex="1" class="tree-bodybox">
|
|
<children/>
|
|
</xul:hbox>
|
|
<xul:scrollbar xbl:inherits="collapsed=hidescrollbar" orient="vertical" class="tree-scrollbar"/>
|
|
</content>
|
|
</binding>
|
|
|
|
<binding id="history-dropmarker" extends="chrome://global/content/bindings/general.xml#dropmarker">
|
|
<implementation>
|
|
<method name="showPopup">
|
|
<body><![CDATA[
|
|
var textbox = document.getBindingParent(this);
|
|
textbox.showHistoryPopup();
|
|
]]></body>
|
|
</method>
|
|
</implementation>
|
|
|
|
<handlers>
|
|
<handler event="mousedown" button="0"><![CDATA[
|
|
this.showPopup();
|
|
]]></handler>
|
|
</handlers>
|
|
</binding>
|
|
|
|
</bindings>
|
|
|