diff options
author | Matt A. Tobin <email@mattatobin.com> | 2019-11-03 00:17:46 -0400 |
---|---|---|
committer | Matt A. Tobin <email@mattatobin.com> | 2019-11-03 00:17:46 -0400 |
commit | 302bf1b523012e11b60425d6eee1221ebc2724eb (patch) | |
tree | b191a895f8716efcbe42f454f37597a545a6f421 /mailnews/db/gloda/content/glodacomplete.xml | |
parent | 21b3f6247403c06f85e1f45d219f87549862198f (diff) | |
download | UXP-302bf1b523012e11b60425d6eee1221ebc2724eb.tar UXP-302bf1b523012e11b60425d6eee1221ebc2724eb.tar.gz UXP-302bf1b523012e11b60425d6eee1221ebc2724eb.tar.lz UXP-302bf1b523012e11b60425d6eee1221ebc2724eb.tar.xz UXP-302bf1b523012e11b60425d6eee1221ebc2724eb.zip |
Issue #1258 - Part 1: Import mailnews, ldap, and mork from comm-esr52.9.1
Diffstat (limited to 'mailnews/db/gloda/content/glodacomplete.xml')
-rw-r--r-- | mailnews/db/gloda/content/glodacomplete.xml | 644 |
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> |