summaryrefslogtreecommitdiffstats
path: root/mailnews/db/gloda/content/glodacomplete.xml
diff options
context:
space:
mode:
Diffstat (limited to 'mailnews/db/gloda/content/glodacomplete.xml')
-rw-r--r--mailnews/db/gloda/content/glodacomplete.xml644
1 files changed, 644 insertions, 0 deletions
diff --git a/mailnews/db/gloda/content/glodacomplete.xml b/mailnews/db/gloda/content/glodacomplete.xml
new file mode 100644
index 000000000..6c6b2f59d
--- /dev/null
+++ b/mailnews/db/gloda/content/glodacomplete.xml
@@ -0,0 +1,644 @@
+<?xml version="1.0"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<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="glodacomplete-rich-result-popup" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete-rich-result-popup">
+ <implementation implements="nsIAutoCompletePopup">
+ <method name="_appendCurrentResult">
+ <body>
+ <![CDATA[
+ var controller = this.mInput.controller;
+
+ // Process maxRows per chunk to improve performance and user experience
+ for (let i = 0; i < this.maxRows; i++) {
+ if (this._currentIndex >= this._matchCount)
+ return;
+
+ var existingItemsCount = this.richlistbox.childNodes.length;
+ var item;
+
+ // trim the leading/trailing whitespace
+ var trimmedSearchString = controller.searchString.trim();
+
+ // Unescape the URI spec for showing as an entry in the popup
+ let url = Components.classes["@mozilla.org/intl/texttosuburi;1"].
+ getService(Components.interfaces.nsITextToSubURI).
+ unEscapeURIForUI("UTF-8", controller.getValueAt(this._currentIndex));
+
+ // Unlike our superclass, we create nodes every time because we have
+ // heterogeneous results and we cannot rely on the XBL bindings to
+ // to change fast enough.
+ item = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "richlistitem");
+
+ var glodaCompleter = Components.
+ classes["@mozilla.org/autocomplete/search;1?name=gloda"].
+ getService(). //Components.interfaces.nsIAutoCompleteSearch)
+ wrappedJSObject;
+ var result = glodaCompleter.curResult;
+
+ // set these attributes before we set the class
+ // so that we can use them from the contructor
+ var row = result.getObjectAt(this._currentIndex);
+ var obj = row.item;
+ item.setAttribute("text", trimmedSearchString);
+ item.setAttribute("type", result.getStyleAt(this._currentIndex));
+
+ item.row = row;
+
+ // 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 each batch of items so that typing the url bar is responsive
+ setTimeout(() => this._appendCurrentResult(), 0);
+ ]]>
+ </body>
+ </method>
+ <method name="_invalidate">
+ <body>
+ <![CDATA[
+ setTimeout(() => this.adjustHeight(), 0);
+
+ // remove all child nodes because we never want to reuse them.
+ while (this.richlistbox.hasChildNodes())
+ this.richlistbox.lastChild.remove();
+
+ this._currentIndex = 0;
+ this._appendCurrentResult();
+ ]]>
+ </body>
+ </method>
+ </implementation>
+ </binding>
+
+ <!-- This is autocomplete.xml's autocomplete-richlistitem duplicated and
+ modified to include its useful helper functions, but eliminating anything
+ that assumes specific content sub-items. Namely, url/title/etc. -->
+ <binding id="glodacomplete-base-richlistitem" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
+ <implementation implements="nsIDOMXULSelectControlItemElement">
+ <field name="_boundaryCutoff">null</field>
+
+ <property name="boundaryCutoff" readonly="true">
+ <getter>
+ <![CDATA[
+ if (!this._boundaryCutoff) {
+ this._boundaryCutoff =
+ Services.prefs
+ .getIntPref("toolkit.autocomplete.richBoundaryCutoff");
+ }
+ return this._boundaryCutoff;
+ ]]>
+ </getter>
+ </property>
+
+ <method name="_getBoundaryIndices">
+ <parameter name="aText"/>
+ <parameter name="aSearchTokens"/>
+ <body>
+ <![CDATA[
+ // Short circuit for empty search ([""] == "")
+ if (aSearchTokens == "")
+ return [0, aText.length];
+
+ // Find which regions of text match the search terms
+ let regions = [];
+ for (let search of aSearchTokens) {
+ let matchIndex;
+ let startIndex = 0;
+ let searchLen = search.length;
+
+ // Find all matches of the search terms, but stop early for perf
+ let lowerText = aText.toLowerCase().substr(0, this.boundaryCutoff);
+ while ((matchIndex = lowerText.indexOf(search, startIndex)) >= 0) {
+ // Start the next search from where this one finished
+ startIndex = matchIndex + searchLen;
+ regions.push([matchIndex, startIndex]);
+ }
+ }
+
+ // Sort the regions by start position then end position
+ regions = regions.sort(function(a, b) {
+ let start = a[0] - b[0];
+ return (start == 0) ? a[1] - b[1] : start;
+ });
+
+ // Generate the boundary indices from each region
+ let start = 0;
+ let end = 0;
+ let boundaries = [];
+ let len = regions.length;
+ for (let i = 0; i < len; i++) {
+ // We have a new boundary if the start of the next is past the end
+ let region = regions[i];
+ if (region[0] > end) {
+ // First index is the beginning of match
+ boundaries.push(start);
+ // Second index is the beginning of non-match
+ boundaries.push(end);
+
+ // Track the new region now that we've stored the previous one
+ start = region[0];
+ }
+
+ // Push back the end index for the current or new region
+ end = Math.max(end, region[1]);
+ }
+
+ // Add the last region
+ boundaries.push(start);
+ boundaries.push(end);
+
+ // Put on the end boundary if necessary
+ if (end < aText.length)
+ boundaries.push(aText.length);
+
+ // Skip the first item because it's always 0
+ return boundaries.slice(1);
+ ]]>
+ </body>
+ </method>
+
+ <method name="_getSearchTokens">
+ <parameter name="aSearch"/>
+ <body>
+ <![CDATA[
+ let search = aSearch.toLowerCase();
+ return search.split(/\s+/);
+ ]]>
+ </body>
+ </method>
+
+ <method name="_needsAlternateEmphasis">
+ <parameter name="aText"/>
+ <body>
+ <![CDATA[
+ for (let i = aText.length; --i >= 0; ) {
+ let charCode = aText.charCodeAt(i);
+ // Arabic, Syriac, Indic languages are likely to have ligatures
+ // that are broken when using the main emphasis styling
+ if (0x0600 <= charCode && charCode <= 0x109F)
+ return true;
+ }
+
+ return false;
+ ]]>
+ </body>
+ </method>
+
+ <method name="_setUpDescription">
+ <parameter name="aDescriptionElement"/>
+ <parameter name="aText"/>
+ <body>
+ <![CDATA[
+ // Get rid of all previous text
+ while (aDescriptionElement.hasChildNodes())
+ aDescriptionElement.lastChild.remove();
+
+ // Get the indices that separate match and non-match text
+ let search = this.getAttribute("text");
+ let tokens = this._getSearchTokens(search);
+ let indices = this._getBoundaryIndices(aText, tokens);
+
+ // If we're searching for something that needs alternate emphasis,
+ // we'll need to check the text that we match
+ let checkAlt = this._needsAlternateEmphasis(search);
+
+ let next;
+ let start = 0;
+ let len = indices.length;
+ // Even indexed boundaries are matches, so skip the 0th if it's empty
+ for (let i = indices[0] == 0 ? 1 : 0; i < len; i++) {
+ next = indices[i];
+ let text = aText.substr(start, next - start);
+ start = next;
+
+ if (i % 2 == 0) {
+ // Emphasize the text for even indices
+ let span = aDescriptionElement.appendChild(
+ document.createElementNS("http://www.w3.org/1999/xhtml", "span"));
+ span.className = checkAlt && this._needsAlternateEmphasis(text) ?
+ "ac-emphasize-alt" : "ac-emphasize-text";
+ span.textContent = text;
+ } else {
+ // Otherwise, it's plain text
+ aDescriptionElement.appendChild(document.createTextNode(text));
+ }
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="_setUpOverflow">
+ <parameter name="aParentBox"/>
+ <parameter name="aEllipsis"/>
+ <body>
+ <![CDATA[
+ // Hide the ellipsis incase there's just enough to not underflow
+ aEllipsis.hidden = true;
+
+ // Start with the parent's width and subtract off its children
+ let tooltip = [];
+ let children = aParentBox.childNodes;
+ let widthDiff = aParentBox.boxObject.width;
+
+ for (let i = 0; i < children.length; i++) {
+ // Only consider a child if it actually takes up space
+ let childWidth = children[i].boxObject.width;
+ if (childWidth > 0) {
+ // Subtract a little less to account for subpixel rounding
+ widthDiff -= childWidth - .5;
+
+ // Add to the tooltip if it's not hidden and has text
+ let childText = children[i].textContent;
+ if (childText)
+ tooltip.push(childText);
+ }
+ }
+
+ // If the children take up more space than the parent.. overflow!
+ if (widthDiff < 0) {
+ // Re-show the ellipsis now that we know it's needed
+ aEllipsis.hidden = false;
+
+ // Separate text components with a ndash --
+ aParentBox.tooltipText = tooltip.join(" \u2013 ");
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="_doUnderflow">
+ <parameter name="aName"/>
+ <body>
+ <![CDATA[
+ // Hide the ellipsis right when we know we're underflowing instead of
+ // waiting for the timeout to trigger the _setUpOverflow calculations
+ this[aName + "Box"].tooltipText = "";
+ this[aName + "OverflowEllipsis"].hidden = true;
+ ]]>
+ </body>
+ </method>
+
+ </implementation>
+ </binding>
+
+ <binding id="gloda-single-tag-item" extends="chrome://gloda/content/glodacomplete.xml#glodacomplete-base-richlistitem">
+ <content orient="vertical">
+ <xul:description anonid="explanation" class="explanation gloda-single"/>
+ </content>
+ <implementation implements="nsIDOMXULSelectControlItemElement">
+ <constructor>
+ <![CDATA[
+ this._explanation = document.getAnonymousElementByAttribute(this, "anonid", "explanation");
+
+ this._adjustAcItem();
+ ]]>
+ </constructor>
+
+ <property name="label" readonly="true">
+ <getter>
+ <![CDATA[
+ return "tag " + this.row.item.tag;
+ ]]>
+ </getter>
+ </property>
+
+ <method name="_adjustAcItem">
+ <body>
+ <![CDATA[
+ let label = gGlodaCompleteStrings.get("glodaComplete.messagesTagged.label");
+ this._explanation.value = label.replace("#1", this.row.item.tag);
+ ]]>
+ </body>
+ </method>
+ </implementation>
+ </binding>
+
+
+ <binding id="gloda-fulltext-single-item" extends="chrome://gloda/content/glodacomplete.xml#glodacomplete-base-richlistitem">
+ <content orient="vertical">
+ <xul:description anonid="explanation" class="explanation gloda-fulltext-single"/>
+ <xul:description anonid="parameters"/>
+ </content>
+ <implementation implements="nsIDOMXULSelectControlItemElement">
+ <constructor>
+ <![CDATA[
+ this._explanation = document.getAnonymousElementByAttribute(this, "anonid", "explanation");
+
+ this._adjustAcItem();
+ ]]>
+ </constructor>
+
+ <property name="label" readonly="true">
+ <getter>
+ <![CDATA[
+ return "full text search: " + this.row.item;
+ ]]>
+ </getter>
+ </property>
+
+ <method name="_adjustAcItem">
+ <body>
+ <![CDATA[
+ let label = gGlodaCompleteStrings.get("glodaComplete.messagesMentioning.label");
+ this._explanation.value = label.replace("#1", this.row.item);
+ ]]>
+ </body>
+ </method>
+ </implementation>
+ </binding>
+
+ <binding id="gloda-fulltext-all-item" extends="chrome://gloda/content/glodacomplete.xml#glodacomplete-base-richlistitem">
+ <content orient="vertical">
+ <xul:description anonid="explanation" class="explanation"/>
+ </content>
+ <implementation implements="nsIDOMXULSelectControlItemElement">
+ <constructor>
+ <![CDATA[
+ this._explanation = document.getAnonymousElementByAttribute(this, "anonid", "explanation");
+ this._adjustAcItem();
+ ]]>
+ </constructor>
+
+ <property name="label" readonly="true">
+ <getter>
+ <![CDATA[
+ return "full text search: " + this.row.item; // what is this for? l10n?
+ ]]>
+ </getter>
+ </property>
+
+ <method name="_adjustAcItem">
+ <body>
+ <![CDATA[
+ let label = gGlodaCompleteStrings.get("glodaComplete.messagesMentioningMany.label");
+ this._explanation.value = label.replace("#1", this.row.words.join(", "));
+ ]]>
+ </body>
+ </method>
+ </implementation>
+ </binding>
+
+ <binding id="gloda-single-identity-item" extends="chrome://gloda/content/glodacomplete.xml#glodacomplete-base-richlistitem">
+ <content>
+ <xul:hbox class="gloda-single-identity">
+ <xul:image anonid="picture" class="picture"/>
+ <xul:vbox>
+ <xul:hbox>
+ <xul:hbox anonid="name-box" class="ac-title" flex="1"
+ onunderflow="_doUnderflow('_name');">
+ <xul:description anonid="name" class="ac-normal-text ac-comment"
+ xbl:inherits="selected"/>
+ </xul:hbox>
+ <xul:label anonid="name-overflow-ellipsis" xbl:inherits="selected"
+ class="ac-ellipsis-after ac-comment" hidden="true"/>
+ </xul:hbox>
+ <xul:hbox>
+ <xul:hbox anonid="identity-box" class="ac-url" flex="1"
+ onunderflow="_doUnderflow('_identity');">
+ <xul:description anonid="identity" class="ac-normal-text ac-url-text"
+ xbl:inherits="selected"/>
+ </xul:hbox>
+ <xul:label anonid="identity-overflow-ellipsis" xbl:inherits="selected"
+ class="ac-ellipsis-after ac-url-text" hidden="true"/>
+ <xul:image anonid="type-image" class="ac-type-icon"/>
+ </xul:hbox>
+ </xul:vbox>
+ </xul:hbox>
+ </content>
+ <implementation implements="nsIDOMXULSelectControlItemElement">
+ <constructor>
+ <![CDATA[
+ let ellipsis = "\u2026";
+ try {
+ ellipsis = Services.prefs.getComplexValue("intl.ellipsis",
+ Components.interfaces.nsIPrefLocalizedString).data;
+ } catch (ex) {
+ // Do nothing.. we already have a default
+ }
+
+ this._identityOverflowEllipsis = document.getAnonymousElementByAttribute(this, "anonid", "identity-overflow-ellipsis");
+ this._nameOverflowEllipsis = document.getAnonymousElementByAttribute(this, "anonid", "name-overflow-ellipsis");
+
+ this._identityOverflowEllipsis.value = ellipsis;
+ this._nameOverflowEllipsis.value = ellipsis;
+
+ this._typeImage = document.getAnonymousElementByAttribute(this, "anonid", "type-image");
+
+ this._identityBox = document.getAnonymousElementByAttribute(this, "anonid", "identity-box");
+ this._identity = document.getAnonymousElementByAttribute(this, "anonid", "identity");
+
+ this._nameBox = document.getAnonymousElementByAttribute(this, "anonid", "name-box");
+ this._name = document.getAnonymousElementByAttribute(this, "anonid", "name");
+
+ this._picture = document.getAnonymousElementByAttribute(this, "anonid", "picture");
+
+ this._adjustAcItem();
+ ]]>
+ </constructor>
+
+ <property name="label" readonly="true">
+ <getter>
+ <![CDATA[
+ var identity = this.row.item;
+ return identity.accessibleLabel;
+ ]]>
+ </getter>
+ </property>
+
+ <method name="_adjustAcItem">
+ <body>
+ <![CDATA[
+ var identity = this.row.item;
+
+ if (identity == null)
+ return;
+
+ // I guess we should get the picture size from CSS or something?
+ this._picture.src = identity.pictureURL(32);
+
+ // Emphasize the matching search terms for the description
+ this._setUpDescription(this._name, identity.contact.name);
+ this._setUpDescription(this._identity, identity.value);
+
+ // Set up overflow on a timeout because the contents of the box
+ // might not have a width yet even though we just changed them
+ setTimeout(this._setUpOverflow, 0, this._nameBox, this._nameOverflowEllipsis);
+ setTimeout(this._setUpOverflow, 0, this._identityBox, this._identityOverflowEllipsis);
+ ]]>
+ </body>
+ </method>
+ </implementation>
+ </binding>
+
+ <binding id="gloda-contact-chunk" extends="chrome://gloda/content/glodacomplete.xml#glodacomplete-base-richlistitem">
+ <content orient="horizontal">
+ <xul:image anonid="picture"/>
+ <xul:vbox>
+ <xul:hbox>
+ <xul:hbox anonid="name-box" class="ac-title" flex="1"
+ onunderflow="_doUnderflow('_name');">
+ <xul:description anonid="name" class="ac-normal-text ac-comment"
+ xbl:inherits="selected"/>
+ </xul:hbox>
+ <xul:label anonid="name-overflow-ellipsis" xbl:inherits="selected"
+ class="ac-ellipsis-after ac-comment" hidden="true"/>
+ </xul:hbox>
+ <xul:hbox>
+ <xul:hbox anonid="identity-box" class="ac-url" flex="1"
+ onunderflow="_doUnderflow('_identity');">
+ <xul:description anonid="identity" class="ac-normal-text ac-url-text"
+ xbl:inherits="selected"/>
+ </xul:hbox>
+ <xul:label anonid="identity-overflow-ellipsis" xbl:inherits="selected"
+ class="ac-ellipsis-after ac-url-text" hidden="true"/>
+ <xul:image anonid="type-image" class="ac-type-icon"/>
+ </xul:hbox>
+ </xul:vbox>
+ </content>
+ <implementation>
+ <constructor>
+ <![CDATA[
+ let ellipsis = "\u2026";
+ try {
+ ellipsis = Services.prefs.getComplexValue("intl.ellipsis",
+ Components.interfaces.nsIPrefLocalizedString).data;
+ } catch (ex) {
+ // Do nothing.. we already have a default
+ }
+
+ this._identityOverflowEllipsis = document.getAnonymousElementByAttribute(this, "anonid", "identity-overflow-ellipsis");
+ this._nameOverflowEllipsis = document.getAnonymousElementByAttribute(this, "anonid", "name-overflow-ellipsis");
+
+ this._identityOverflowEllipsis.value = ellipsis;
+ this._nameOverflowEllipsis.value = ellipsis;
+
+ this._typeImage = document.getAnonymousElementByAttribute(this, "anonid", "type-image");
+
+ this._identityBox = document.getAnonymousElementByAttribute(this, "anonid", "identity-box");
+ this._identity = document.getAnonymousElementByAttribute(this, "anonid", "identity");
+
+ this._nameBox = document.getAnonymousElementByAttribute(this, "anonid", "name-box");
+ this._name = document.getAnonymousElementByAttribute(this, "anonid", "name");
+
+ this._picture = document.getAnonymousElementByAttribute(this, "anonid", "picture");
+
+ this._adjustAcItem();
+ ]]>
+ </constructor>
+
+ <property name="label" readonly="true">
+ <getter>
+ <![CDATA[
+ var identity = this.obj;
+ return identity.accessibleLabel;
+ ]]>
+ </getter>
+ </property>
+
+ <method name="_adjustAcItem">
+ <body>
+ <![CDATA[
+ var contact = this.obj;
+
+ if (contact == null)
+ return;
+
+ var identity = contact.identities[0];
+
+ // I guess we should get the picture size from CSS or something?
+ this._picture.src = identity.pictureURL(32);
+
+ // Emphasize the matching search terms for the description
+ this._setUpDescription(this._name, contact.name);
+ this._setUpDescription(this._identity, identity.value);
+
+ // Set up overflow on a timeout because the contents of the box
+ // might not have a width yet even though we just changed them
+ setTimeout(this._setUpOverflow, 0, this._nameBox, this._nameOverflowEllipsis);
+ setTimeout(this._setUpOverflow, 0, this._identityBox, this._identityOverflowEllipsis);
+ ]]>
+ </body>
+ </method>
+ </implementation>
+ </binding>
+
+ <binding id="gloda-multi-item" extends="chrome://gloda/content/glodacomplete.xml#glodacomplete-base-richlistitem">
+ <content orient="vertical">
+ <xul:description anonid="explanation"/>
+ <xul:hbox anonid="identity-holder" flex="1">
+ </xul:hbox>
+ </content>
+ <implementation implements="nsIDOMXULSelectControlItemElement">
+ <constructor>
+ <![CDATA[
+ this._explanation = document.getAnonymousElementByAttribute(this, "anonid", "explanation");
+ this._identityHolder = document.getAnonymousElementByAttribute(this, "anonid", "identity-holder");
+
+ this._adjustAcItem();
+ ]]>
+ </constructor>
+
+ <property name="label" readonly="true">
+ <getter>
+ <![CDATA[
+ return this._explanation.value;
+ ]]>
+ </getter>
+ </property>
+
+ <method name="renderItem">
+ <parameter name="aObj"/>
+ <body>
+ var node = document.createElementNS(
+ "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
+ "richlistitem");
+
+ node.obj = aObj;
+ node.setAttribute("type",
+ "gloda-" + this.row.nounDef.name + "-chunk");
+
+ this._identityHolder.appendChild(node);
+ </body>
+ </method>
+
+ <method name="_adjustAcItem">
+ <body>
+ <![CDATA[
+ // clear out any lingering children
+ while (this._identityHolder.hasChildNodes())
+ this._identityHolder.lastChild.remove();
+
+ var row = this.row;
+ if (row == null)
+ return;
+
+ this._explanation.value = row.nounDef.name + "s " +
+ row.criteriaType + "ed " + row.criteria;
+
+ // render anyone already in there
+ for (let item of row.collection.items) {
+ this.renderItem(item);
+ }
+ // listen up, yo.
+ row.renderer = this;
+ ]]>
+ </body>
+ </method>
+ </implementation>
+ </binding>
+
+
+</bindings>