summaryrefslogtreecommitdiffstats
path: root/comm/suite/mailnews/content/mailWidgets.xml
diff options
context:
space:
mode:
Diffstat (limited to 'comm/suite/mailnews/content/mailWidgets.xml')
-rw-r--r--comm/suite/mailnews/content/mailWidgets.xml1946
1 files changed, 1946 insertions, 0 deletions
diff --git a/comm/suite/mailnews/content/mailWidgets.xml b/comm/suite/mailnews/content/mailWidgets.xml
new file mode 100644
index 0000000000..29288b3b70
--- /dev/null
+++ b/comm/suite/mailnews/content/mailWidgets.xml
@@ -0,0 +1,1946 @@
+<?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="mailBindings"
+ xmlns="http://www.mozilla.org/xbl"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:nc="http://home.netscape.com/NC-rdf#"
+ xmlns:xbl="http://www.mozilla.org/xbl">
+
+ <!-- dummy widget to force this file to load -->
+ <binding id="dummy" extends="xul:box"/>
+
+ <!-- temporary holding place for horizontal list -->
+
+ <binding id="extdescription" extends="chrome://global/content/bindings/listbox.xml#listbox-base">
+ <implementation>
+ <constructor><![CDATA[
+ this.children.filter(aChild => aChild.getAttribute("selected") == "true")
+ .forEach(this.selectedItems.append, this.selectedItems);
+ ]]></constructor>
+
+ <!-- ///////////////// public members ///////////////// -->
+
+ <property name="itemCount" readonly="true"
+ onget="return this.children.length;"/>
+
+ <method name="getIndexOfItem">
+ <parameter name="item"/>
+ <body><![CDATA[
+ return this.children.indexOf(item);
+ ]]></body>
+ </method>
+ <method name="getItemAtIndex">
+ <parameter name="index"/>
+ <body><![CDATA[
+ return this.children[index] || null;
+ ]]></body>
+ </method>
+ <method name="getRowCount">
+ <body><![CDATA[
+ return this.children.length;
+ ]]></body>
+ </method>
+ <method name="getNumberOfVisibleRows">
+ <body><![CDATA[
+ var firstItem = this.children[0] || null;
+ if (!firstItem)
+ return 0; // nothing to be visible
+ var itemsPerRow = Math.floor(this.boxObject.width / firstItem.boxObject.width);
+ var itemsPerCol = Math.floor(this.boxObject.height / firstItem.boxObject.height);
+ return Math.max(itemsPerRow, 1) * Math.max(itemsPerCol, 1);
+ ]]></body>
+ </method>
+ <method name="getIndexOfFirstVisibleRow">
+ <body><![CDATA[
+ //XXXzeniko unimplementable without a way to scroll
+ ]]></body>
+ </method>
+
+ <method name="ensureIndexIsVisible">
+ <parameter name="index"/>
+ <body><![CDATA[
+ this.ensureElementIsVisible(this.getItemAtIndex(index));
+ ]]></body>
+ </method>
+ <method name="ensureElementIsVisible">
+ <parameter name="item"/>
+ <body><![CDATA[
+ //XXXzeniko unimplementable without a way to scroll
+ ]]></body>
+ </method>
+ <method name="scrollToIndex">
+ <parameter name="index"/>
+ <body><![CDATA[
+ //XXXzeniko unimplementable without a way to scroll
+ ]]></body>
+ </method>
+
+ <method name="appendItem">
+ <parameter name="label"/>
+ <parameter name="value"/>
+ <body><![CDATA[
+ // -1 appends due to the way getItemAtIndex is implemented
+ return this.insertItemAt(-1, label, value);
+ ]]></body>
+ </method>
+ <method name="insertItemAt">
+ <parameter name="index"/>
+ <parameter name="label"/>
+ <parameter name="value"/>
+ <body><![CDATA[
+ const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+ var item = document.createElementNS(XULNS, "descriptionitem");
+ item.setAttribute("label", label);
+ this.insertBefore(item, this.getItemAtIndex(index));
+ return item;
+ ]]></body>
+ </method>
+
+ <method name="scrollOnePage">
+ <parameter name="direction"/>
+ <body><![CDATA[
+ return direction * this.getNumberOfVisibleRows();
+ ]]></body>
+ </method>
+
+ <!-- ///////////////// private members ///////////////// -->
+
+ <property name="children" readonly="true"
+ onget="return Array.from(this.getElementsByTagName('descriptionitem'));"/>
+
+ <method name="_fireOnSelect">
+ <body><![CDATA[
+ if (!this._suppressOnSelect && !this.suppressOnSelect) {
+ this.dispatchEvent(new Event("select",
+ { bubbles: false, cancelable: true }));
+ }
+ ]]></body>
+ </method>
+ </implementation>
+
+ <handlers>
+ <handler event="keypress" keycode="VK_LEFT" modifiers="control shift any"
+ action="this.moveByOffset(-1, !event.ctrlKey, event.shiftKey);"
+ phase="target" preventdefault="true"/>
+ <handler event="keypress" keycode="VK_RIGHT" modifiers="control shift any"
+ action="this.moveByOffset(1, !event.ctrlKey, event.shiftKey);"
+ phase="target" preventdefault="true"/>
+ <handler event="click" button="0" phase="target"><![CDATA[
+ if (this.selType != "multiple" || (!event.ctrlKey && !event.shiftKey && !event.metaKey))
+ this.clearSelection();
+ ]]></handler>
+ <!-- make sure we keep the focus... -->
+ <handler event="mousedown" button="0"
+ action="if (document.commandDispatcher.focusedElement != this) this.focus();"/>
+ </handlers>
+ </binding>
+
+ <binding id="descriptionitem" extends="chrome://global/content/bindings/listbox.xml#listitem">
+ <content>
+ <xul:hbox class="attachmentBox" xbl:inherits="orient" align="start">
+ <xul:label class="descriptioncell-label" xbl:inherits="value=label,flex=flexlabel,crop,disabled,context" flex="1" dir="ltr" crop="center"/>
+ </xul:hbox>
+ </content>
+ </binding>
+
+ <binding id="descriptionitem-iconic" extends="chrome://global/content/bindings/listbox.xml#listitem">
+ <content>
+ <xul:hbox class="attachmentBox" xbl:inherits="orient" align="center">
+ <xul:image class="descriptioncell-icon" xbl:inherits="src=image"/>
+ <xul:label class="descriptioncell-label" xbl:inherits="value=label,flex=flexlabel,crop,disabled,context" flex="1" dir="ltr" crop="center"/>
+ </xul:hbox>
+ </content>
+ </binding>
+
+ <!-- Message Pane Widgets -->
+
+ <!-- mail-toggle-headerfield: Non-email addrs headers which have a toggle
+ associated with them (i.e. the subject).
+ Use label to set the header name.
+ Use headerValue to set the header value. -->
+ <binding id="mail-toggle-headerfield">
+ <content>
+ <xul:hbox class="headerNameBox" align="start">
+ <xul:image class="expandHeaderViewButton" xbl:inherits="onclick=ontwistyclick"/>
+ <xul:spacer flex="1"/>
+ <xul:label class="headerName" xbl:inherits="value=label" control="headerValue"/>
+ </xul:hbox>
+ <xul:hbox class="headerValueBox" flex="1" align="start">
+ <xul:textbox class="headerValue plain" anonid="headerValue" flex="1" readonly="true"/>
+ </xul:hbox>
+ </content>
+
+ <implementation>
+ <property name="headerValue" onset="return document.getAnonymousElementByAttribute(this, 'anonid', 'headerValue').value = val;"/>
+ </implementation>
+ </binding>
+
+ <!-- mail-headerfield: presents standard text header name & value pairs. Don't use this for email addresses.
+ use label to set the header name.
+ use headerValue to set the header value. -->
+ <binding id="mail-headerfield">
+ <content>
+ <xul:hbox class="headerNameBox" align="start">
+ <xul:label class="headerName" xbl:inherits="value=label" control="headerValue" flex="1"/>
+ </xul:hbox>
+ <xul:hbox class="headerValueBox" flex="1" align="start">
+ <xul:textbox class="headerValue plain" anonid="headerValue" flex="1" readonly="true"/>
+ </xul:hbox>
+ </content>
+
+ <implementation>
+ <property name="headerValue" onset="return document.getAnonymousElementByAttribute(this, 'anonid', 'headerValue').value = val;"/>
+ </implementation>
+ </binding>
+
+ <binding id="mail-urlfield" extends="chrome://messenger/content/mailWidgets.xml#mail-headerfield">
+ <content>
+ <xul:hbox class="headerNameBox" align="start">
+ <xul:label class="headerName" xbl:inherits="value=label" flex="1"/>
+ </xul:hbox>
+ <xul:hbox class="headerValueBox" flex="1" align="start">
+ <xul:label onclick="if (event.button != 2) openAsExternal(event.target.value);"
+ ondragstart="this.parentNode.setDataTransfer(event);"
+ class="headerValue plain text-link headerValueUrl"
+ anonid="headerValue" flex="1" readonly="true" context="copyUrlPopup"/>
+ </xul:hbox>
+ </content>
+
+ <implementation>
+ <method name="setDataTransfer">
+ <parameter name="aEvent"/>
+ <body><![CDATA[
+ var dt = aEvent.dataTransfer;
+ var val = aEvent.target.value;
+ dt.setData('text/x-moz-url', val + "\n" + val);
+ dt.setData('text/uri-list', val);
+ dt.setData('text/plain', val);
+ ]]></body>
+ </method>
+ </implementation>
+ </binding>
+
+ <binding id="mail-emailheaderfield">
+ <content>
+ <xul:hbox class="headerNameBox" align="start">
+ <xul:label class="headerName" xbl:inherits="value=label" flex="1"/>
+ </xul:hbox>
+ <xul:hbox class="headerValueBox" flex="1" align="start">
+ <xul:mail-emailaddress class="headerValue" anonid="emailAddressNode"/>
+ </xul:hbox>
+ </content>
+
+ <implementation>
+ <property name="emailAddressNode" onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'emailAddressNode');"
+ readonly="true"/>
+ </implementation>
+ </binding>
+
+ <!-- multi-emailHeaderField: presents multiple emailheaderfields with a toggle -->
+ <binding id="mail-multi-emailHeaderField">
+ <content>
+ <xul:hbox class="headerNameBox" align="start" pack="end">
+ <xul:image class="addresstwisty" anonid="toggleIcon"
+ collapsed="true" onclick="toggleWrap();"/>
+ <xul:label class="headerName" xbl:inherits="value=label"/>
+ </xul:hbox>
+
+ <xul:hbox class="headerValueBox" anonid="longEmailAddresses" flex="1" align="start"
+ onoverflow="if (event.detail != 1) this.parentNode.toggleIcon.collapsed = false;"
+ onunderflow="if (event.detail != 1) this.parentNode.toggleIcon.collapsed = true;">
+ <xul:description class="headerValue" anonid="emailAddresses" flex="1"/>
+ </xul:hbox>
+ </content>
+
+ <implementation>
+ <constructor>
+ <![CDATA[
+ this.mAddresses = new Array;
+ ]]>
+ </constructor>
+
+ <field name="mAddresses"/>
+ <!-- as a perf optimization we are going to keep a cache of email address nodes which we've
+ created around for the lifetime of the widget. mSizeOfAddressCache controls how many of these
+ elements we keep around -->
+ <field name="mSizeOfAddressCache">3</field>
+
+ <!-- addAddressView: a public method used to add an address to this widget.
+ aAddresses is an object with 3 properties: displayName, emailAddress and fullAddress
+ -->
+ <method name="addAddressView">
+ <parameter name="aAddress"/>
+ <body>
+ <![CDATA[
+ this.mAddresses.push(aAddress);
+ ]]>
+ </body>
+ </method>
+
+ <!-- updateEmailAddressNode: private method used to set properties on an address node -->
+ <method name="updateEmailAddressNode">
+ <parameter name="aEmailNode"/>
+ <parameter name="aAddress"/>
+ <body>
+ <![CDATA[
+ if (aEmailNode.parentNode.useShortView && aAddress.displayName)
+ {
+ aEmailNode.setAttribute("label", aAddress.displayName);
+ aEmailNode.setAttribute("tooltiptext", aAddress.fullAddress);
+ }
+ else
+ {
+ aEmailNode.setAttribute("label", aAddress.fullAddress || aAddress.displayName);
+ aEmailNode.removeAttribute("tooltiptext");
+ }
+ aEmailNode.setAttribute("emailAddress", aAddress.emailAddress);
+ aEmailNode.setAttribute("fullAddress", aAddress.fullAddress);
+ aEmailNode.setAttribute("displayName", aAddress.displayName);
+
+ // Add aria-label with header field type and header field content
+ // for better accessibility.
+ // Note: No extra colon and space needed, since it is
+ // already provided by this object's label attribute.
+ var ariaLabel = this.getAttribute("label") +
+ aEmailNode.getAttribute("label");
+ aEmailNode.setAttribute("aria-label", ariaLabel);
+
+ try
+ {
+ if ("UpdateEmailNodeDetails" in top)
+ UpdateEmailNodeDetails(aAddress.emailAddress, aEmailNode);
+ }
+ catch(ex)
+ {
+ dump("UpdateEmailNodeDetails failed: " + ex + "\n");
+ }
+ ]]>
+ </body>
+ </method>
+
+ <!-- fillCachedAddresses: private method used to fill up any cached pre-existing
+ emailAddress fields without creating new email address fields. Returns a remainder
+ for the # of addresses which require new addresses being created.
+ Invariants: 1) aNumAddressesToShow >= 0 && it is <= mAddresses.length -->
+ <method name="fillCachedAddresses">
+ <parameter name="aAddressesNode"/>
+ <parameter name="aNumAddressesToShow"/>
+ <body>
+ <![CDATA[
+ var numExistingCachedAddresses = aAddressesNode.childNodes.length;
+ if (!numExistingCachedAddresses)
+ return this.mAddresses.length; // we couldn't pre fill anything
+ else if (numExistingCachedAddresses > 1)
+ numExistingCachedAddresses = (numExistingCachedAddresses + 1)/ 2;
+
+ var index = 0;
+ var numAddressesAdded = 0;
+ var emailAddressNode;
+ var commaNode;
+ while (numAddressesAdded < numExistingCachedAddresses && numAddressesAdded < aNumAddressesToShow)
+ {
+ if (index && numExistingCachedAddresses > 1)
+ {
+ commaNode = aAddressesNode.childNodes[index++];
+ if (commaNode)
+ commaNode.hidden = false;
+ }
+
+ // get the node pointed to by index
+ emailAddressNode = aAddressesNode.childNodes[index++];
+ this.updateEmailAddressNode(emailAddressNode, this.mAddresses[numAddressesAdded]);
+ emailAddressNode.hidden = false;
+ numAddressesAdded++;
+ }
+
+ // if we have added all of our elements but we still have more cached items in this address node
+ // then make sure the extra cached copies are hidden...
+ numExistingCachedAddresses = aAddressesNode.childNodes.length; // reset
+ while (index < numExistingCachedAddresses)
+ {
+ aAddressesNode.childNodes[index++].hidden = true;
+ }
+
+ return this.mAddresses.length - numAddressesAdded;
+ ]]>
+ </body>
+ </method>
+
+ <!-- fillAddressesNode: private method used to create email address nodes for either our short
+ or long view. aAddressesNode: the div we want to add addresses too.
+ aNumAddressesToShow: number of addresses to put into the list -->
+ <method name="fillAddressesNode">
+ <parameter name="aAddressesNode"/>
+ <parameter name="aNumAddressesToShow"/>
+ <body>
+ <![CDATA[
+ var numAddresses = this.mAddresses.length;
+ if (aNumAddressesToShow <= 0 || aNumAddressesToShow > numAddresses) // then show all
+ aNumAddressesToShow = numAddresses;
+
+ // before we try to create email address nodes, try to leverage any cached nodes...
+ var remainder = this.fillCachedAddresses(aAddressesNode, aNumAddressesToShow);
+ var index = numAddresses - remainder;
+ while (index < numAddresses && index < aNumAddressesToShow)
+ {
+ var newAddressNode = document.createElement("mail-emailaddress");
+
+ // Stash the headerName somewhere that UpdateEmailNodeDetails
+ // will be able to find it.
+ newAddressNode.setAttribute("headerName", this.headerName);
+
+ if (index)
+ {
+ var textNode = document.createElement("text");
+ textNode.setAttribute("value", ", ");
+ textNode.setAttribute("class", "emailSeparator");
+ aAddressesNode.appendChild(textNode);
+ }
+
+ var itemInDocument = aAddressesNode.appendChild(newAddressNode);
+ this.updateEmailAddressNode(itemInDocument, this.mAddresses[index]);
+ index++;
+ }
+ ]]>
+ </body>
+ </method>
+
+ <property name="emailAddresses" onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'emailAddresses');"
+ readonly="true"/>
+ <property name="longEmailAddresses" onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'longEmailAddresses');"
+ readonly="true"/>
+ <property name="toggleIcon" onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'toggleIcon');"
+ readonly="true"/>
+
+ <!-- buildView: public method used by callers when they are done adding all the email addresses to the widget
+ aNumAddressesToShow: total # of addresses to show in the short view -->
+ <method name="buildViews">
+ <body>
+ <![CDATA[
+ this.fillAddressesNode(this.emailAddresses, -1);
+ ]]>
+ </body>
+ </method>
+
+ <!-- Updates the nodes of this field with a call to
+ UpdateExtraAddressProcessing. The parameters are optional fields
+ that can contain extra information to be passed to
+ UpdateExtraAddressProcessing, the implementation of that function
+ should be checked to determine what it requires -->
+ <method name="updateExtraAddressProcessing">
+ <parameter name="aParam1"/>
+ <parameter name="aParam2"/>
+ <parameter name="aParam3"/>
+ <body>
+ <![CDATA[
+ if (UpdateExtraAddressProcessing) {
+ var childNodes = this.emailAddresses.childNodes;
+ for (let i = 0; i < this.mAddresses.length; i++) {
+ UpdateExtraAddressProcessing(this.mAddresses[i],
+ childNodes[i * 2],
+ aParam1, aParam2, aParam3);
+ }
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="toggleWrap">
+ <body>
+ <![CDATA[
+ if (this.toggleIcon.hasAttribute("open")) {
+ this.toggleIcon.removeAttribute("open");
+ this.longEmailAddresses.setAttribute("singleline", "true");
+ } else {
+ this.toggleIcon.setAttribute("open", "true");
+ this.longEmailAddresses.removeAttribute("singleline");
+ }
+ ]]>
+ </body>
+ </method>
+
+ <!-- internal method used to clear both our divs -->
+ <method name="clearChildNodes">
+ <parameter name="aParentNode"/>
+ <body>
+ <![CDATA[
+ // we want to keep around the first mSizeOfAddressCache email address nodes
+ // don't forget that we have comma text nodes in there too so really we want to keep
+ // around cache size * 2 - 1.
+ var numItemsToPreserve = this.mSizeOfAddressCache * 2 - 1;
+ var numItemsInNode = aParentNode.childNodes.length;
+
+ while (numItemsInNode && (numItemsInNode > numItemsToPreserve))
+ {
+ aParentNode.childNodes[numItemsInNode - 1].remove();
+ numItemsInNode = numItemsInNode - 1;
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="clearHeaderValues">
+ <body>
+ <![CDATA[
+ // clear out our local state
+ this.mAddresses = new Array;
+ if (this.toggleIcon.hasAttribute("open"))
+ // no automatic overflow tracking in this case
+ this.toggleIcon.collapsed = true;
+ this.toggleIcon.removeAttribute("open");
+ this.longEmailAddresses.setAttribute("singleline", "true");
+ // remove anything inside of each of our labels....
+ this.clearChildNodes(this.emailAddresses);
+ ]]>
+ </body>
+ </method>
+ </implementation>
+ </binding>
+
+ <binding id="mail-emailaddress">
+ <content popup="emailAddressPopup" context="emailAddressPopup">
+ <xul:description anonid="emailValue" class="emailDisplayButton plain"
+ xbl:inherits="xbl:text=label,crop,aria-label" flex="1"/>
+ <xul:image class="emailDisplayImage" anonid="emailImage"
+ xbl:inherits="src=image"/>
+ </content>
+
+ <implementation>
+ <property name="label" onset="this.getPart('emailValue').setAttribute('label',val); return val;"
+ onget="return this.getPart('emailValue').getAttribute('label');"/>
+ <property name="crop" onset="this.getPart('emailValue').setAttribute('crop',val); return val;"
+ onget="return this.getPart('emailValue').getAttribute('crop');"/>
+ <property name="disabled" onset="this.getPart('emailValue').setAttribute('disabled',val); return val;"
+ onget="return this.getPart('emailValue').getAttribute('disabled');"/>
+ <property name="src" onset="this.getPart('emailImage').setAttribute('src',val); return val;"
+ onget="return this.getPart('emailImage').getAttribute('src');"/>
+ <property name="imgalign" onset="this.getPart('emailImage').setAttribute('imgalign',val); return val;"
+ onget="return this.getPart('emailImage').getAttribute('imgalign');"/>
+
+ <method name="getPart">
+ <parameter name="aPartId"/>
+ <body><![CDATA[
+ return document.getAnonymousElementByAttribute(this, "anonid", aPartId);
+ ]]></body>
+ </method>
+ </implementation>
+ </binding>
+
+ <binding id="mail-messageids-headerfield">
+ <content>
+ <xul:hbox class="headerNameBox" align="start" pack="end">
+ <xul:image class="addresstwisty" anonid="toggleIcon"
+ onclick="toggleWrap();"/>
+ <xul:label class="headerName" xbl:inherits="value=label"/>
+ </xul:hbox>
+ <xul:hbox class="headerValueBox" flex="1" align="start">
+ <xul:label class="headerValue" anonid="headerValue" flex="1"/>
+ </xul:hbox>
+ </content>
+
+ <implementation>
+ <constructor>
+ <![CDATA[
+ this.mMessageIds = [];
+ this.showFullMessageIds = false;
+ ]]>
+ </constructor>
+
+ <property name="headerValue" readonly="true"
+ onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'headerValue');"/>
+ <property name="toggleIcon" readonly="true"
+ onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'toggleIcon');"/>
+
+ <field name="mMessageIds"/>
+
+ <!-- addMessageIdView: a public method used to add a message-id to this widget. -->
+ <method name="addMessageIdView">
+ <parameter name="aMessageId"/>
+ <body>
+ <![CDATA[
+ this.mMessageIds.push(aMessageId);
+ ]]>
+ </body>
+ </method>
+
+ <!-- updateMessageIdNode: private method used to set properties on an MessageId node -->
+ <method name="updateMessageIdNode">
+ <parameter name="aMessageIdNode"/>
+ <parameter name="aIndex"/>
+ <parameter name="aMessageId"/>
+ <parameter name="aLastId"/>
+ <body>
+ <![CDATA[
+ var showFullMessageIds = this.showFullMessageIds;
+
+ if (showFullMessageIds || aIndex == aLastId)
+ {
+ aMessageIdNode.setAttribute("label", aMessageId);
+ aMessageIdNode.removeAttribute("tooltiptext");
+ }
+ else
+ {
+ aMessageIdNode.setAttribute("label", aIndex);
+ aMessageIdNode.setAttribute("tooltiptext", aMessageId);
+ }
+
+ aMessageIdNode.setAttribute("index", aIndex);
+ aMessageIdNode.setAttribute("messageid", aMessageId);
+ ]]>
+ </body>
+ </method>
+
+ <method name="fillMessageIdNodes">
+ <body>
+ <![CDATA[
+ var headerValue = this.headerValue;
+ var messageIdNodes = headerValue.childNodes;
+ var numMessageIds = this.mMessageIds.length;
+ var index = 0;
+
+ while (messageIdNodes.length > numMessageIds * 2 - 1)
+ headerValue.lastChild.remove();
+
+ this.toggleIcon.hidden = numMessageIds <= 1;
+
+ for (var index = 0; index < numMessageIds; index++)
+ {
+ if (index * 2 <= messageIdNodes.length - 1)
+ {
+ this.updateMessageIdNode(messageIdNodes[index * 2], index + 1,
+ this.mMessageIds[index], numMessageIds);
+ }
+ else
+ {
+ var newMessageIdNode = document.createElement("mail-messageid");
+
+ if (index)
+ {
+ var textNode = document.createElement("text");
+ textNode.setAttribute("value", ", ");
+ textNode.setAttribute("class", "messageIdSeparator");
+ headerValue.appendChild(textNode);
+ }
+ var itemInDocument = headerValue.appendChild(newMessageIdNode);
+ this.updateMessageIdNode(itemInDocument, index + 1,
+ this.mMessageIds[index], numMessageIds);
+ }
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="toggleWrap">
+ <body>
+ <![CDATA[
+ var headerValue = this.headerValue;
+ var messageIdNodes = headerValue.childNodes;
+ var showFullMessageIds = !this.showFullMessageIds;
+ var messageIds = this.mMessageIds
+
+ for (var i = 0; i < messageIdNodes.length; i += 2)
+ {
+ if (showFullMessageIds)
+ {
+ this.toggleIcon.setAttribute("open", "true");
+ messageIdNodes[i].setAttribute("label", messageIds[i / 2]);
+ messageIdNodes[i].removeAttribute("tooltiptext");
+ headerValue.removeAttribute("singleline");
+ } else
+ {
+ this.toggleIcon.removeAttribute("open");
+ messageIdNodes[i].setAttribute("label", i / 2 + 1);
+ messageIdNodes[i].setAttribute("tooltiptext", messageIds[i / 2]);
+ }
+ }
+
+ this.showFullMessageIds = showFullMessageIds;
+ ]]>
+ </body>
+ </method>
+
+ <method name="clearHeaderValues">
+ <body>
+ <![CDATA[
+ // clear out our local state
+ this.mMessageIds = new Array;
+ if (this.showFullMessageIds)
+ {
+ this.showFullMessageIds = false;
+ this.toggleIcon.removeAttribute("open");
+ }
+ ]]>
+ </body>
+ </method>
+ </implementation>
+ </binding>
+
+ <binding id="mail-messageid">
+ <content context="messageIdContext" onclick="MessageIdClick(this, event);">
+ <xul:label anonid="messageIdValue" class="messageIdDisplayButton plain"
+ xbl:inherits="value=label"/>
+ <xul:image class="messageIdDisplayImage" anonid="messageIdImage"/>
+ </content>
+
+ <implementation>
+ <property name="label" onset="this.getPart().setAttribute('label',val); return val;"
+ onget="return this.getPart('messageIdValue').getAttribute('label');"/>
+
+ <method name="getPart">
+ <parameter name="aPartId"/>
+ <body><![CDATA[
+ return document.getAnonymousElementByAttribute(this, "anonid", 'messageIdValue');
+ ]]></body>
+ </method>
+ </implementation>
+ </binding>
+
+ <!-- Header field for showing the tags associated with a message -->
+ <binding id="mail-headerfield-tags">
+ <content>
+ <xul:hbox class="headerNameBox" align="start">
+ <xul:label class="headerName" xbl:inherits="value=label" flex="1"/>
+ </xul:hbox>
+ <xul:hbox class="headerValueBox" flex="1" align="start">
+ <xul:label class="headerValue plain" anonid="headerValue" flex="1"/>
+ </xul:hbox>
+ </content>
+
+ <implementation>
+ <property name="headerValue" onset="return this.buildTags(val);"/>
+ <method name="buildTags">
+ <parameter name="aTags"/>
+ <body>
+ <![CDATA[
+ // aTags contains a list of actual tag names (not the keys), delimited by spaces
+ // each tag name is encoded.
+
+ // remove any existing tag items we've appended to the list
+ var headerValueNode = document.getAnonymousElementByAttribute(this, 'anonid', 'headerValue');
+ for (var i = headerValueNode.childNodes.length - 1; i >= 0; --i)
+ headerValueNode.childNodes[i].remove();
+
+ // tokenize the keywords based on ' '
+ var tagsArray = aTags.split(' ');
+ for (var index = 0; index < tagsArray.length; index++)
+ {
+ // for each tag, create a label, give it the font color that corresponds to the
+ // color of the tag and append it.
+ var tagName;
+ try {
+ // if we got a bad tag name, getTagForKey will throw an exception, skip it
+ // and go to the next one.
+ tagName = MailServices.tags.getTagForKey(tagsArray[index]);
+ } catch (ex) { continue; }
+
+ var color = MailServices.tags.getColorForKey(tagsArray[index]);
+
+ // now create a label for the tag name, and set the color
+ var label = document.createElement("label");
+ label.setAttribute('value', tagName);
+ label.style.color = color;
+ label.className = "tagvalue blc-" + color.substr(1);
+ headerValueNode.appendChild(label);
+ }
+ ]]>
+ </body>
+ </method>
+ <constructor>
+ <![CDATA[
+ var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+ );
+ ]]>
+ </constructor>
+ </implementation>
+ </binding>
+
+ <binding id="search-menulist-abstract" name="searchMenulistAbstract" extends="xul:box">
+ <content>
+ <xul:menulist class="search-menulist" xbl:inherits="flex,disabled" oncommand="this.parentNode.onSelect(event)">
+ <xul:menupopup class="search-menulist-popup"/>
+ </xul:menulist>
+ </content>
+
+ <implementation>
+ <field name="internalScope">null</field>
+ <field name="internalValue">-1</field>
+ <field readonly="true" name="validityManager">
+ <![CDATA[
+ Cc['@mozilla.org/mail/search/validityManager;1'].getService(Ci.nsIMsgSearchValidityManager);
+ ]]>
+ </field>
+ <property name="searchScope" onget="return this.internalScope;">
+ <!-- scope ID - retrieve the table -->
+ <setter>
+ <![CDATA[
+ // if scope isn't changing this is a noop
+ if (this.internalScope == val) return val;
+
+ this.internalScope = val;
+ this.refreshList();
+ var targets = this.targets;
+ if (targets) {
+ for (var i=0; i< targets.length; i++) {
+ targets[i].searchScope = val;
+ }
+ }
+ return val;
+ ]]>
+ </setter>
+ </property>
+
+ <property name="validityTable" readonly="true" onget="return this.validityManager.getTable(this.searchScope)"/>
+
+ <property name="targets" readonly="true">
+ <getter>
+ <![CDATA[
+ var forAttrs = this.getAttribute("for");
+ if (!forAttrs) return null;
+ var targetIds = forAttrs.split(",");
+ if (targetIds.length == 0) return null;
+
+ var targets = new Array;
+ for (let j = 0, i = 0; i < targetIds.length; i++) {
+ var target = document.getElementById(targetIds[i]);
+ if (target) targets[j++] = target;
+ }
+ return targets;
+ ]]>
+ </getter>
+ </property>
+
+ <property name="optargets" readonly="true">
+ <getter>
+ <![CDATA[
+ var forAttrs = this.getAttribute("opfor");
+ if (!forAttrs) return null;
+ var optargetIds = forAttrs.split(",");
+ if (optargetIds.length == 0) return null;
+
+ var optargets = new Array;
+ var j=0;
+ for (var i=0; i<optargetIds.length;i++) {
+ var optarget = document.getElementById(optargetIds[i]);
+ if (optarget) optargets[j++] = optarget;
+ }
+ return optargets;
+ ]]>
+ </getter>
+ </property>
+
+ <property name="value" onget="return this.internalValue;">
+ <setter>
+ <![CDATA[
+ if (this.internalValue == val)
+ return val;
+ this.internalValue = val;
+ var menulist = document.getAnonymousNodes(this)[0];
+ menulist.selectedItem = this.validMenuitem;
+
+ // now notify targets of new parent's value
+ var targets = this.targets;
+ if (targets) {
+ for (var i=0; i < targets.length; i++) {
+ targets[i].parentValue = val;
+ }
+ }
+
+ // now notify optargets of new op parent's value
+ var optargets = this.optargets;
+ if (optargets) {
+ for (i=0; i < optargets.length; i++) {
+ optargets[i].opParentValue = val;
+ }
+ }
+
+ return val;
+ ]]>
+ </setter>
+ </property>
+ <!-- label forwards to the internal menulist's "label" attribute -->
+ <property name="label" onget="return document.getAnonymousNodes(this)[0].selectedItem.getAttribute('label');">
+ </property>
+ <property name="validMenuitem" readonly="true">
+ <!-- Prepare menulist selection, adding a missing hidden menuitem if needed, and
+ updating the disabled state of the menulist label. -->
+ <getter>
+ <![CDATA[
+ if (this.value == -1) // -1 means not initialized
+ return null;
+
+ let menulist = document.getAnonymousNodes(this)[0];
+ let isCustom = isNaN(this.value);
+ let typedValue = isCustom ? this.value : parseInt(this.value);
+
+ // custom attribute to style the unavailable menulist item
+ menulist.setAttribute("unavailable",
+ !this.valueIds.includes(typedValue));
+
+ // add a hidden menulist item if value is missing
+ let menuitem = menulist.getElementsByAttribute("value", this.value).item(0);
+ if (!menuitem)
+ { // need to add a hidden menuitem
+ menuitem = menulist.appendItem(this.valueLabel, this.value);
+ menuitem.hidden = true;
+ }
+ return menuitem;
+ ]]>
+ </getter>
+ </property>
+ <method name="refreshList">
+ <parameter name="dontRestore"/> <!-- should we not restore old selection? -->
+ <body>
+ <![CDATA[
+ var menuItemIds = this.valueIds;
+ var menuItemStrings = this.valueStrings;
+
+ var menulist = document.getAnonymousNodes(this)[0];
+ var popup = menulist.firstChild;
+
+ // save our old "value" so we can restore it later
+ var oldData;
+ if (!dontRestore)
+ oldData = menulist.value;
+
+ // remove the old popup children
+ while (popup.hasChildNodes())
+ popup.lastChild.remove();
+
+ var newSelection;
+ var customizePos=-1;
+ for (var i = 0; i < menuItemIds.length; ++i)
+ {
+ // create the menuitem
+ if (Ci.nsMsgSearchAttrib.OtherHeader == menuItemIds[i].toString())
+ customizePos = i;
+ else
+ {
+ var menuitem = document.createElement("menuitem");
+ menuitem.setAttribute("label", menuItemStrings[i]);
+ menuitem.setAttribute("value", menuItemIds[i]);
+ popup.appendChild(menuitem);
+ // try to restore the selection
+ if (!newSelection || oldData == menuItemIds[i].toString())
+ newSelection = menuitem;
+ }
+ }
+ if (customizePos != -1)
+ {
+ var separator = document.createElement("menuseparator");
+ popup.appendChild(separator);
+ menuitem = document.createElement("menuitem");
+ menuitem.setAttribute("label", menuItemStrings[customizePos]);
+ menuitem.setAttribute("value", menuItemIds[customizePos]);
+ popup.appendChild(menuitem);
+ }
+ //
+ // If we are either uninitialized, or if we are called because
+ // of a change in our parent, update the value to the
+ // default stored in newSelection.
+ //
+ if ((this.value == -1 || dontRestore) && newSelection)
+ this.value = newSelection.getAttribute("value");
+ menulist.selectedItem = this.validMenuitem;
+ ]]>
+ </body>
+ </method>
+ <method name="onSelect">
+ <parameter name="event"/>
+ <body>
+ <![CDATA[
+ var menulist = document.getAnonymousNodes(this)[0];
+ if (menulist.value == Ci.nsMsgSearchAttrib.OtherHeader) {
+ // Customize menuitem selected.
+ let args = {};
+ window.openDialog("chrome://messenger/content/CustomHeaders.xul",
+ "",
+ "modal,centerscreen,resizable,titlebar,chrome",
+ args);
+ // User may have removed the custom header currently selected in
+ // the menulist so temporarily set the selection to a safe value.
+ this.value = Ci.nsMsgSearchAttrib.OtherHeader;
+ // rebuild the menulist
+ UpdateAfterCustomHeaderChange();
+ // Find the created or chosen custom header and select it.
+ if (args.selectedVal) {
+ let menuitem = menulist.querySelector('[label="' +
+ args.selectedVal + '"]');
+ this.value = menuitem.value;
+ } else {
+ // Nothing was picked in the custom headers editor so just pick
+ // something instead of the current "Customize" menuitem.
+ this.value = menulist.getItemAtIndex(0).value;
+ }
+ } else {
+ this.value = menulist.value;
+ }
+ ]]>
+ </body>
+ </method>
+ </implementation>
+ </binding>
+
+ <!-- searchattribute - Subject, Sender, To, CC, etc. -->
+ <binding id="searchattribute" name="searchAttribute"
+ extends="chrome://messenger/content/mailWidgets.xml#search-menulist-abstract">
+ <implementation>
+ <field name="stringBundle">
+ <![CDATA[
+ this.Services.strings.createBundle(
+ "chrome://messenger/locale/search-attributes.properties");
+ ]]>
+ </field>
+ <property name="valueLabel" readonly="true">
+ <getter>
+ <![CDATA[
+ if (isNaN(this.value)) // is this a custom term?
+ {
+ let customTerm = MailServices.filters.getCustomTerm(this.value);
+ if (customTerm)
+ return customTerm.name;
+ // The custom term may be missing after the extension that added
+ // it was disabled or removed. We need to notify the user.
+ let scriptError = Cc["@mozilla.org/scripterror;1"]
+ .createInstance(Ci.nsIScriptError);
+ scriptError.init("Missing custom search term " + this.value,
+ null, null, 0, 0, Ci.nsIScriptError.errorFlag,
+ "component javascript");
+ this.Services.console.logMessage(scriptError);
+ return this.stringBundle.GetStringFromName("MissingCustomTerm");
+ }
+ return this.stringBundle.GetStringFromName(
+ this.validityManager.getAttributeProperty(parseInt(this.value)));
+ ]]>
+ </getter>
+ </property>
+ <property name="valueIds" readonly="true">
+ <getter>
+ <![CDATA[
+ let result = this.validityTable.getAvailableAttributes();
+ // add any available custom search terms
+ for (let customTerm of MailServices.filters.getCustomTerms()) {
+ // for custom terms, the array element is a string with the custom id
+ // instead of the integer attribute
+ if (customTerm.getAvailable(this.searchScope, null))
+ result.push(customTerm.id);
+ }
+ return result;
+ ]]>
+ </getter>
+ </property>
+ <property name="valueStrings" readonly="true">
+ <getter>
+ <![CDATA[
+ let strings = new Array;
+ let ids = this.valueIds;
+ let hdrsArray = null;
+ try
+ {
+ let hdrs =
+ this.Services.prefs.getCharPref("mailnews.customHeaders");
+ hdrs = hdrs.replace(/\s+/g, ""); //remove white spaces before splitting
+ hdrsArray = hdrs.match(/[^:]+/g);
+ }
+ catch(ex)
+ {
+ }
+
+ let j = 0;
+ for (let i = 0; i < ids.length; i++)
+ {
+ if (isNaN(ids[i])) // Is this a custom search term?
+ {
+ let customTerm = MailServices.filters.getCustomTerm(ids[i]);
+ if (customTerm)
+ strings[i] = customTerm.name;
+ else
+ strings[i] = "";
+ }
+ else if(ids[i] > Ci.nsMsgSearchAttrib.OtherHeader && hdrsArray)
+ strings[i] = hdrsArray[j++];
+ else
+ strings[i] = this.stringBundle.GetStringFromName(
+ this.validityManager.getAttributeProperty(ids[i]));
+ }
+ return strings;
+ ]]>
+ </getter>
+ </property>
+ <constructor>
+ <![CDATA[
+ var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+ );
+ ChromeUtils.import("resource://gre/modules/Services.jsm", this);
+ initializeTermFromId(this.id);
+ ]]>
+ </constructor>
+ </implementation>
+ </binding>
+
+ <!-- searchoperator - Contains, Is Less than, etc -->
+ <binding id="searchoperator" name="searchOperator"
+ extends="chrome://messenger/content/mailWidgets.xml#search-menulist-abstract">
+ <implementation>
+ <field name="searchAttribute">Ci.nsMsgSearchAttrib.Default</field>
+ <field name="stringBundle">
+ <![CDATA[
+ this.Services.strings.createBundle("chrome://messenger/locale/search-operators.properties")
+ ]]>
+ </field>
+ <property name="valueLabel" readonly="true">
+ <getter>
+ <![CDATA[
+ return this.stringBundle.GetStringFromName(this.value);
+ ]]>
+ </getter>
+ </property>
+ <property name="valueIds" readonly="true">
+ <getter>
+ <![CDATA[
+ let isCustom = isNaN(this.searchAttribute);
+ if (isCustom)
+ {
+ let customTerm = MailServices.filters.getCustomTerm(this.searchAttribute);
+ if (customTerm)
+ return customTerm.getAvailableOperators(this.searchScope);
+ return [Ci.nsMsgSearchOp.Contains];
+ }
+ return this.validityTable.getAvailableOperators(this.searchAttribute);
+ ]]>
+ </getter>
+ </property>
+ <property name="valueStrings" readonly="true">
+ <getter>
+ <![CDATA[
+ let strings = new Array;
+ let ids = this.valueIds;
+ for (let i = 0; i < ids.length; i++)
+ strings[i] = this.stringBundle.GetStringFromID(ids[i]);
+ return strings;
+ ]]>
+ </getter>
+ </property>
+ <property name="parentValue">
+ <setter>
+ <![CDATA[
+ if (this.searchAttribute == val && val != Ci.nsMsgSearchAttrib.OtherHeader) return val;
+ this.searchAttribute = val;
+ this.refreshList(true); // don't restore the selection, since searchvalue nulls it
+ if (val == Ci.nsMsgSearchAttrib.AgeInDays) {
+ // Bug 187741 We want "Age in Days" to default to "is less than".
+ this.value = Ci.nsMsgSearchOp.IsLessThan;
+ }
+ return val;
+ ]]>
+ </setter>
+ <getter>
+ <![CDATA[
+ return this.searchAttribute;
+ ]]>
+ </getter>
+ </property>
+ <constructor>
+ <![CDATA[
+ var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+ );
+ ChromeUtils.import("resource://gre/modules/Services.jsm", this);
+ ]]>
+ </constructor>
+ </implementation>
+ </binding>
+
+ <!-- searchvalue - a widget which dynamically changes its user interface
+ depending on what type of data it's supposed to be showing
+ currently handles arbitrary text entry, and menulists for
+ priority, status, junk status, tags, hasAttachment status,
+ and addressbook
+ -->
+ <binding id="searchvalue" name="searchValue">
+ <content>
+ <xul:textbox flex="1" class="search-value-textbox" xbl:inherits="disabled"/>
+ <xul:menulist flex="1" class="search-value-menulist" xbl:inherits="disabled">
+ <xul:menupopup class="search-value-popup">
+ <xul:menuitem value="6" stringTag="priorityHighest" class="search-value-menuitem"/>
+ <xul:menuitem value="5" stringTag="priorityHigh" class="search-value-menuitem"/>
+ <xul:menuitem value="4" stringTag="priorityNormal" class="search-value-menuitem"/>
+ <xul:menuitem value="3" stringTag="priorityLow" class="search-value-menuitem"/>
+ <xul:menuitem value="2" stringTag="priorityLowest" class="search-value-menuitem"/>
+ </xul:menupopup>
+ </xul:menulist>
+ <xul:menulist flex="1" class="search-value-menulist" xbl:inherits="disabled">
+ <xul:menupopup class="search-value-popup">
+ <xul:menuitem value="2" stringTag="replied" class="search-value-menuitem"/>
+ <xul:menuitem value="1" stringTag="read" class="search-value-menuitem"/>
+ <xul:menuitem value="65536" stringTag="new" class="search-value-menuitem"/>
+ <xul:menuitem value="4096" stringTag="forwarded" class="search-value-menuitem"/>
+ <xul:menuitem value="4" stringTag="flagged" class="search-value-menuitem"/>
+ </xul:menupopup>
+ </xul:menulist>
+ <xul:textbox flex="1" class="search-value-textbox" xbl:inherits="disabled"/>
+ <xul:menulist flex="1" class="search-value-menulist" xbl:inherits="disabled">
+ <xul:menupopup class="search-value-popup addrbooksPopup" localonly="true"/>
+ </xul:menulist>
+ <xul:menulist flex="1" class="search-value-menulist" xbl:inherits="disabled">
+ <xul:menupopup class="search-value-popup">
+ </xul:menupopup>
+ </xul:menulist>
+ <xul:menulist flex="1" class="search-value-menulist" xbl:inherits="disabled">
+ <xul:menupopup class="search-value-popup">
+ <xul:menuitem value="2" stringTag="junk" class="search-value-menuitem"/>
+ </xul:menupopup>
+ </xul:menulist>
+ <xul:menulist flex="1" class="search-value-menulist" xbl:inherits="disabled">
+ <xul:menupopup class="search-value-popup">
+ <xul:menuitem value="0" stringTag="hasAttachments" class="search-value-menuitem"/>
+ </xul:menupopup>
+ </xul:menulist>
+ <xul:menulist flex="1" class="search-value-menulist" xbl:inherits="disabled">
+ <xul:menupopup class="search-value-popup">
+ <xul:menuitem value="plugin" stringTag="junkScoreOriginPlugin"
+ class="search-value-menuitem"/>
+ <xul:menuitem value="user" stringTag="junkScoreOriginUser"
+ class="search-value-menuitem"/>
+ <xul:menuitem value="filter" stringTag="junkScoreOriginFilter"
+ class="search-value-menuitem"/>
+ <xul:menuitem value="whitelist" stringTag="junkScoreOriginWhitelist"
+ class="search-value-menuitem"/>
+ <xul:menuitem value="imapflag" stringTag="junkScoreOriginImapFlag"
+ class="search-value-menuitem"/>
+ </xul:menupopup>
+ </xul:menulist>
+ <xul:hbox flex="1" class="search-value-custom" xbl:inherits="disabled"/>
+ </content>
+ <implementation>
+ <field name="internalOperator">null</field>
+ <field name="internalAttribute">null</field>
+ <field name="internalValue">null</field>
+ <property name="opParentValue" onget="return this.internalOperator;">
+ <setter>
+ <![CDATA[
+ // noop if we're not changing it
+ if (this.internalOperator == val) return val;
+
+ // Keywords has the null field IsEmpty
+ if (this.searchAttribute == Ci.nsMsgSearchAttrib.Keywords) {
+ if (val == Ci.nsMsgSearchOp.IsEmpty ||
+ val == Ci.nsMsgSearchOp.IsntEmpty)
+ this.setAttribute("selectedIndex", "-1");
+ else
+ this.setAttribute("selectedIndex", "5");
+ }
+
+ // JunkStatus has the null field IsEmpty
+ if (this.searchAttribute == Ci.nsMsgSearchAttrib.JunkStatus) {
+ if (val == Ci.nsMsgSearchOp.IsEmpty ||
+ val == Ci.nsMsgSearchOp.IsntEmpty)
+ this.setAttribute("selectedIndex", "-1");
+ else
+ this.setAttribute("selectedIndex", "6");
+ }
+
+ // if it's not sender, to, cc, alladdresses, or toorcc, we don't care
+ if (this.searchAttribute != Ci.nsMsgSearchAttrib.Sender &&
+ this.searchAttribute != Ci.nsMsgSearchAttrib.To &&
+ this.searchAttribute != Ci.nsMsgSearchAttrib.ToOrCC &&
+ this.searchAttribute != Ci.nsMsgSearchAttrib.AllAddresses &&
+ this.searchAttribute != Ci.nsMsgSearchAttrib.CC ) {
+ this.internalOperator = val;
+ return val;
+ }
+
+ var children = document.getAnonymousNodes(this);
+ if (val == Ci.nsMsgSearchOp.IsntInAB ||
+ val == Ci.nsMsgSearchOp.IsInAB) {
+ // if the old internalOperator was
+ // IsntInAB or IsInAB, and the new internalOperator is
+ // IsntInAB or IsInAB, noop because the search value
+ // was an ab type, and it still is.
+ // otherwise, switch to the ab picker and select the PAB
+ if (this.internalOperator != Ci.nsMsgSearchOp.IsntInAB &&
+ this.internalOperator != Ci.nsMsgSearchOp.IsInAB) {
+ var abs = children[4].getElementsByAttribute("value", "moz-abmdbdirectory://abook.mab");
+ if (abs.item(0))
+ children[4].selectedItem = abs[0];
+ this.setAttribute("selectedIndex", "4");
+ }
+ }
+ else {
+ // if the old internalOperator wasn't
+ // IsntInAB or IsInAB, and the new internalOperator isn't
+ // IsntInAB or IsInAB, noop because the search value
+ // wasn't an ab type, and it still isn't.
+ // otherwise, switch to the textbox and clear it
+ if (this.internalOperator == Ci.nsMsgSearchOp.IsntInAB ||
+ this.internalOperator == Ci.nsMsgSearchOp.IsInAB) {
+ children[0].value = "";
+ this.setAttribute("selectedIndex", "0");
+ }
+ }
+
+ this.internalOperator = val;
+ return val;
+ ]]>
+ </setter>
+ </property>
+ <!-- parentValue forwards to the attribute -->
+ <property name="parentValue" onset="return this.searchAttribute=val;"
+ onget="return this.searchAttribute;"/>
+ <property name="searchAttribute" onget="return this.internalAttribute;">
+ <setter>
+ <![CDATA[
+ // noop if we're not changing it
+ if (this.internalAttribute == val) return val;
+ this.internalAttribute = val;
+
+ // if the searchAttribute changing, null out the internalOperator
+ this.internalOperator = null;
+
+ // we inherit from a deck, so just use it's index attribute
+ // to hide/show widgets
+ if (isNaN(val)) // Is this a custom attribute?
+ {
+ this.setAttribute("selectedIndex", "9");
+ let customHbox = document.getAnonymousNodes(this)[9];
+ if (this.internalValue)
+ customHbox.setAttribute("value", this.internalValue.str);
+ // the searchAttribute attribute is intended as a selector in
+ // CSS for custom search terms to bind a custom value
+ customHbox.setAttribute("searchAttribute", val);
+ }
+ else if (val == Ci.nsMsgSearchAttrib.Priority)
+ this.setAttribute("selectedIndex", "1");
+ else if (val == Ci.nsMsgSearchAttrib.MsgStatus)
+ this.setAttribute("selectedIndex", "2");
+ else if (val == Ci.nsMsgSearchAttrib.Date)
+ this.setAttribute("selectedIndex", "3");
+ else if (val == Ci.nsMsgSearchAttrib.Sender) {
+ // since the internalOperator is null
+ // this is the same as the initial state
+ // the initial state for Sender isn't an ab type search
+ // it's a text search, so show the textbox
+ this.setAttribute("selectedIndex", "0");
+ }
+ else if (val == Ci.nsMsgSearchAttrib.Keywords) {
+ this.setAttribute("selectedIndex", "5");
+ }
+ else if (val == Ci.nsMsgSearchAttrib.JunkStatus) {
+ this.setAttribute("selectedIndex", "6");
+ }
+ else if (val == Ci.nsMsgSearchAttrib.HasAttachmentStatus) {
+ this.setAttribute("selectedIndex", "7");
+ }
+ else if (val == Ci.nsMsgSearchAttrib.JunkScoreOrigin) {
+ this.setAttribute("selectedIndex", "8");
+ }
+ else {
+ // a normal text field
+ this.setAttribute("selectedIndex", "0");
+ }
+ return val;
+ ]]>
+ </setter>
+ </property>
+ <property name="value" onget="return this.internalValue;">
+ <setter>
+ <![CDATA[
+ // val is a nsIMsgSearchValue object
+ this.internalValue = val;
+ var attrib = this.internalAttribute;
+ var nsMsgSearchAttrib = Ci.nsMsgSearchAttrib;
+ var children = document.getAnonymousNodes(this);
+ this.searchAttribute = attrib;
+ if (isNaN(attrib)) // a custom term
+ {
+ let customHbox = document.getAnonymousNodes(this)[9];
+ customHbox.setAttribute("value", val.str);
+ return val;
+ }
+ if (attrib == nsMsgSearchAttrib.Priority) {
+ var matchingPriority =
+ children[1].getElementsByAttribute("value", val.priority);
+ if (matchingPriority.item(0))
+ children[1].selectedItem = matchingPriority[0];
+ }
+ else if (attrib == nsMsgSearchAttrib.MsgStatus) {
+ var matchingStatus =
+ children[2].getElementsByAttribute("value", val.status);
+ if (matchingStatus.item(0))
+ children[2].selectedItem = matchingStatus[0];
+ }
+ else if (attrib == nsMsgSearchAttrib.AgeInDays)
+ children[0].value = val.age;
+ else if (attrib == nsMsgSearchAttrib.Date)
+ children[3].value = convertPRTimeToString(val.date);
+ else if (attrib == nsMsgSearchAttrib.Sender ||
+ attrib == nsMsgSearchAttrib.To ||
+ attrib == nsMsgSearchAttrib.CC ||
+ attrib == nsMsgSearchAttrib.AllAddresses ||
+ attrib == nsMsgSearchAttrib.ToOrCC)
+ {
+ if (this.internalOperator == Ci.nsMsgSearchOp.IsntInAB ||
+ this.internalOperator == Ci.nsMsgSearchOp.IsInAB) {
+ var abs = children[4].getElementsByAttribute("value", val.str);
+ if (abs.item(0))
+ children[4].selectedItem = abs[0];
+ }
+ else
+ children[0].value = val.str;
+ }
+ else if (attrib == nsMsgSearchAttrib.Keywords)
+ {
+ var keywordVal = children[5].getElementsByAttribute("value", val.str);
+ if (keywordVal.item(0))
+ {
+ children[5].value = val.str;
+ children[5].selectedItem = keywordVal[0];
+ }
+ }
+ else if (attrib == nsMsgSearchAttrib.JunkStatus) {
+ var junkStatus =
+ children[6].getElementsByAttribute("value", val.junkStatus);
+ if (junkStatus.item(0))
+ children[6].selectedItem = junkStatus[0];
+ }
+ else if (attrib == nsMsgSearchAttrib.HasAttachmentStatus) {
+ var hasAttachmentStatus =
+ children[7].getElementsByAttribute("value", val.hasAttachmentStatus);
+ if (hasAttachmentStatus.item(0))
+ children[7].selectedItem = hasAttachmentStatus[0];
+ }
+ else if (attrib == nsMsgSearchAttrib.JunkScoreOrigin) {
+ var junkScoreOrigin =
+ children[8].getElementsByAttribute("value", val.str);
+ if (junkScoreOrigin.item(0))
+ children[8].selectedItem = junkScoreOrigin[0];
+ }
+ else if (attrib == nsMsgSearchAttrib.JunkPercent) {
+ children[0].value = val.junkPercent;
+ }
+ else if (attrib == nsMsgSearchAttrib.Size) {
+ children[0].value = val.size;
+ }
+ else
+ children[0].value = val.str;
+ return val;
+ ]]>
+ </setter>
+ </property>
+ <method name="save">
+ <body>
+ <![CDATA[
+ var searchValue = this.value;
+ var searchAttribute = this.searchAttribute;
+ var nsMsgSearchAttrib = Ci.nsMsgSearchAttrib;
+ var children = document.getAnonymousNodes(this);
+
+ searchValue.attrib = searchAttribute;
+ if (searchAttribute == nsMsgSearchAttrib.Priority) {
+ searchValue.priority = children[1].selectedItem.value;
+ }
+ else if (searchAttribute == nsMsgSearchAttrib.MsgStatus)
+ searchValue.status = children[2].value;
+ else if (searchAttribute == nsMsgSearchAttrib.AgeInDays)
+ searchValue.age = children[0].value;
+ else if (searchAttribute == nsMsgSearchAttrib.Date)
+ searchValue.date = convertStringToPRTime(children[3].value);
+ else if (searchAttribute == nsMsgSearchAttrib.Sender ||
+ searchAttribute == nsMsgSearchAttrib.To ||
+ searchAttribute == nsMsgSearchAttrib.CC ||
+ searchAttribute == nsMsgSearchAttrib.AllAddresses ||
+ searchAttribute == nsMsgSearchAttrib.ToOrCC)
+ {
+ if (this.internalOperator == Ci.nsMsgSearchOp.IsntInAB ||
+ this.internalOperator == Ci.nsMsgSearchOp.IsInAB)
+ searchValue.str = children[4].selectedItem.value;
+ else
+ searchValue.str = children[0].value;
+ }
+ else if (searchAttribute == nsMsgSearchAttrib.Keywords)
+ {
+ searchValue.str = children[5].value;
+ }
+ else if (searchAttribute == nsMsgSearchAttrib.JunkStatus)
+ searchValue.junkStatus = children[6].value;
+ else if (searchAttribute == nsMsgSearchAttrib.JunkPercent)
+ searchValue.junkPercent = children[0].value;
+ else if (searchAttribute == nsMsgSearchAttrib.Size)
+ searchValue.size = children[0].value;
+ else if (searchAttribute == nsMsgSearchAttrib.HasAttachmentStatus)
+ searchValue.status = 0x10000000; // 0x10000000 is MSG_FLAG_ATTACHMENT;
+ else if (searchAttribute == nsMsgSearchAttrib.JunkScoreOrigin)
+ searchValue.str = children[8].value;
+ else if (isNaN(searchAttribute)) // a custom term
+ {
+ searchValue.attrib = nsMsgSearchAttrib.Custom;
+ searchValue.str = children[9].getAttribute("value");
+ }
+ else
+ searchValue.str = children[0].value;
+ ]]>
+ </body>
+ </method>
+ <method name="saveTo">
+ <parameter name="searchValue"/>
+ <body>
+ <![CDATA[
+ this.internalValue = searchValue;
+ this.save();
+ ]]>
+ </body>
+ </method>
+ <method name="fillInTags">
+ <body>
+ <![CDATA[
+ var children = document.getAnonymousNodes(this);
+ var popupMenu = children[5].firstChild;
+ var tagArray = MailServices.tags.getAllTags();
+ for (var i = 0; i < tagArray.length; ++i)
+ {
+ var taginfo = tagArray[i];
+ var newMenuItem = document.createElement('menuitem');
+ newMenuItem.setAttribute('label', taginfo.tag);
+ newMenuItem.setAttribute('value', taginfo.key);
+ popupMenu.appendChild(newMenuItem);
+ if (!i)
+ children[5].selectedItem = newMenuItem;
+ }
+ ]]>
+ </body>
+ </method>
+ <method name="fillStringsForChildren">
+ <parameter name="parentNode"/>
+ <parameter name="bundle"/>
+ <body>
+ <![CDATA[
+ var children = parentNode.childNodes;
+ var len=children.length;
+ for (var i=0; i<len; i++) {
+ var node = children[i];
+ var stringTag = node.getAttribute("stringTag");
+ if (stringTag) {
+ var attr = (node.tagName == "label") ? "value" : "label";
+ node.setAttribute(attr, bundle.GetStringFromName(stringTag));
+ }
+ }
+ ]]>
+ </body>
+ </method>
+ <method name="initialize">
+ <parameter name="menulist"/>
+ <parameter name="bundle"/>
+ <body>
+ <![CDATA[
+ this.fillStringsForChildren(menulist.firstChild, bundle);
+ ]]>
+ </body>
+ </method>
+ <constructor>
+ <![CDATA[
+ var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+ );
+ ChromeUtils.import("resource://gre/modules/Services.jsm", this);
+
+ // initialize strings
+ let bundle = Services.strings.createBundle("chrome://messenger/locale/messenger.properties");
+
+ // intialize the priority picker
+ this.initialize(document.getAnonymousNodes(this)[1], bundle);
+
+ // initialize the status picker
+ this.initialize(document.getAnonymousNodes(this)[2], bundle);
+
+ // initialize the date picker
+ var datePicker = document.getAnonymousNodes(this)[3];
+ var searchAttribute = this.searchAttribute;
+ var nsMsgSearchAttrib = Ci.nsMsgSearchAttrib;
+ var time;
+ if (searchAttribute == nsMsgSearchAttrib.Date)
+ time = datePicker.value;
+ else
+ time = new Date();
+ // do .value instead of .setAttribute("value", xxx);
+ // to work around for bug #179412
+ // (caused by bug #157210)
+ //
+ // the searchvalue widget has two textboxes
+ // one for text, one as a placeholder for a date / calendar widget
+ datePicker.value = convertDateToString(time);
+
+ // initialize the address book picker
+ this.initialize(document.getAnonymousNodes(this)[4], bundle);
+
+ // initialize the junk status picker
+ this.initialize(document.getAnonymousNodes(this)[6], bundle);
+
+ // initialize the has attachment status picker
+ this.initialize(document.getAnonymousNodes(this)[7], bundle);
+
+ // initialize the junk score origin picker
+ this.initialize(document.getAnonymousNodes(this)[8], bundle);
+
+ // initialize the tag list
+ fillInTags();
+ ]]>
+ </constructor>
+ </implementation>
+ <handlers>
+ <handler event="keypress" keycode="VK_RETURN" modifiers="accel any"
+ action="onEnterInSearchTerm(event);" preventdefault="true"/>
+ </handlers>
+ </binding>
+
+ <binding id="folderSummary-popup" extends="chrome://global/content/bindings/popup.xml#tooltip">
+ <content>
+ <children>
+ <xul:folderSummary/>
+ </children>
+ </content>
+ <handlers>
+ <handler event="popupshowing">
+ <![CDATA[
+ let msgFolder = gFolderTreeView.getFolderAtCoords(event.clientX,
+ event.clientY);
+ if (!msgFolder)
+ return false;
+
+ let tooltipnode = document.getAnonymousNodes(this)[0];
+ let asyncResults = {};
+ if (tooltipnode.parseFolder(msgFolder, null, asyncResults))
+ return true;
+
+ let row = {}, col = {};
+ gFolderTreeView._tree.getCellAt(event.clientX, event.clientY, row,
+ col, {});
+ if (col.value.id == "folderNameCol") {
+ let cropped = gFolderTreeView._tree.isCellCropped(row.value,
+ col.value);
+ if (tooltipnode.addLocationInfo(msgFolder, cropped))
+ return true;
+ }
+
+ let counts = gFolderTreeView.getSummarizedCounts(row.value,
+ col.value.id);
+ if (counts) {
+ if (tooltipnode.addSummarizeExplain(counts))
+ return true;
+ }
+
+ return false;
+ ]]>
+ </handler>
+
+ <handler event="popuphiding">
+ document.getAnonymousNodes(this)[0].clear();
+ </handler>
+ </handlers>
+ </binding>
+
+ <binding id="folderSummary">
+ <content>
+ <xul:vbox/>
+ </content>
+
+ <implementation>
+ <field name="mMaxMsgHdrsInPopup">8</field>
+ <property name="hasMessages" readonly="true" onget="return document.getAnonymousNodes(this)[0].hasChildNodes();"/>
+ <method name="parseFolder">
+ <parameter name="aFolder"/>
+ <parameter name="aUrlListener"/>
+ <parameter name="aOutAsync"/>
+ <body>
+ <![CDATA[
+ // Skip servers, Trash and Junk folders, and newgroups.
+ if (!aFolder || aFolder.isServer || !aFolder.hasNewMessages ||
+ aFolder.getFlag(Ci.nsMsgFolderFlags.Junk) ||
+ aFolder.getFlag(Ci.nsMsgFolderFlags.Trash) ||
+ (aFolder.server instanceof Ci.nsINntpIncomingServer))
+ return false;
+ let showPreviewText = this.Services.prefs.getBoolPref("mail.biff.alert.show_preview");
+ let folderArray = [];
+ let msgDatabase;
+ try {
+ msgDatabase = aFolder.msgDatabase;
+ } catch(e) {
+ // The database for this folder may be missing
+ // (e.g. outdated/missing .msf), so just skip this folder.
+ return false;
+ }
+
+ if (aFolder.flags & Ci.nsMsgFolderFlags.Virtual)
+ {
+ let dbFolderInfo = msgDatabase.dBFolderInfo;
+ var srchFolderUri = dbFolderInfo.getCharProperty("searchFolderUri");
+ var srchFolderUriArray = srchFolderUri.split('|');
+ var foldersAdded = 0;
+ var RDF = Cc['@mozilla.org/rdf/rdf-service;1']
+ .getService(Ci.nsIRDFService);
+ for (var i in srchFolderUriArray)
+ {
+ var realFolder = RDF.GetResource(srchFolderUriArray[i])
+ .QueryInterface(Ci.nsIMsgFolder);
+ if (!realFolder.isServer)
+ folderArray[foldersAdded++] = realFolder;
+ }
+ }
+ else {
+ folderArray[0] = aFolder;
+ }
+
+ var foundNewMsg = false;
+ for (var folderIndex = 0; folderIndex < folderArray.length; folderIndex++)
+ {
+ aFolder = folderArray[folderIndex];
+ // now get the database
+ try {
+ msgDatabase = aFolder.msgDatabase;
+ } catch(e) {
+ // The database for this folder may be missing
+ // (e.g. outdated/missing .msf), then just skip this folder.
+ continue;
+ }
+
+ aFolder.msgDatabase = null;
+ let msgKeys = msgDatabase.getNewList();
+
+ if (!msgKeys.length)
+ continue;
+
+ if (showPreviewText)
+ {
+ // fetchMsgPreviewText forces the previewText property to get generated
+ // for each of the message keys.
+ try {
+ aOutAsync.value = aFolder.fetchMsgPreviewText(msgKeys, aUrlListener);
+ aFolder.msgDatabase = null;
+ }
+ catch (ex)
+ {
+ // fetchMsgPreviewText throws an error when we call it on a news folder, we should just not show
+ // the tooltip if this method returns an error.
+ aFolder.msgDatabase = null;
+ continue;
+ }
+ }
+ // if fetching the preview text is going to be an asynch operation and the caller
+ // is set up to handle that fact, then don't bother filling in any of the fields since
+ // we'll have to do this all over again when the fetch for the preview text completes.
+ // We don't expect to get called with a urlListener if we're doing a virtual folder.
+ if (aOutAsync.value && aUrlListener)
+ return false;
+ var unicodeConverter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
+ .createInstance(Ci.nsIScriptableUnicodeConverter);
+ unicodeConverter.charset = "UTF-8";
+ foundNewMsg = true;
+
+ var index = 0;
+ while (document.getAnonymousNodes(this)[0].childNodes.length < this.mMaxMsgHdrsInPopup && index < msgKeys.length)
+ {
+ var msgPopup = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "folderSummaryMessage");
+ var msgHdr = msgDatabase.GetMsgHdrForKey(msgKeys[index++]);
+
+ var msgSubject = msgHdr.mime2DecodedSubject;
+ const kMsgFlagHasRe = 0x0010; // MSG_FLAG_HAS_RE
+ if(msgHdr.flags & kMsgFlagHasRe)
+ msgSubject = (msgSubject) ? "Re: " + msgSubject : "Re: ";
+
+ msgPopup.setAttribute('subject', msgSubject);
+
+ var previewText = msgHdr.getStringProperty('preview');
+ // convert the preview text from utf-8 to unicode
+ if (previewText)
+ {
+ try
+ {
+ var text = unicodeConverter.ConvertToUnicode(previewText);
+ if (text)
+ msgPopup.setAttribute('previewText', text);
+ }
+ catch (ex) { }
+ }
+
+ var names = {};
+ var emails = {};
+ var numAddresses = MailServices.headerParser.parseHeadersWithArray(msgHdr.mime2DecodedAuthor, emails, names, {});
+ msgPopup.setAttribute('sender', names.value[0] ? names.value[0] : emails.value[0]);
+ msgPopup.messageUri = aFolder.getUriForMsg(msgHdr);
+ msgPopup.folderUri = aFolder.URI;
+ msgPopup.msgKey = msgHdr.messageKey;
+ document.getAnonymousNodes(this)[0].appendChild(msgPopup);
+ }
+ if (document.getAnonymousNodes(this)[0].childNodes.length >= this.mMaxMsgHdrsInPopup)
+ return true;
+ }
+ return foundNewMsg;
+ ]]>
+ </body>
+ </method>
+
+ <method name="addLocationInfo">
+ <parameter name="aFolder"/>
+ <parameter name="aCropped"/>
+ <body>
+ <![CDATA[
+ let popupValue = null;
+ // Display also server name for items that are on level 0 and are
+ // not server names by themselves and do not have server name
+ // already appended in their label.
+ let folderIndex = gFolderTreeView.getIndexOfFolder(aFolder);
+ if (!aFolder.isServer &&
+ gFolderTreeView.getLevel(folderIndex) == 0 &&
+ !gFolderTreeView.getServerNameAdded(folderIndex)) {
+ let midPath = "";
+ let midFolder = aFolder.parent;
+ while (aFolder.server.rootFolder != midFolder) {
+ midPath = midFolder.name + " - " + midPath;
+ midFolder = midFolder.parent;
+ }
+ popupValue = aFolder.server.prettyName + " - " + midPath +
+ aFolder.name;
+ }
+ // If folder name is cropped or is a newsgroup and abbreviated per
+ // pref, use the full name as a tooltip.
+ else if (aCropped ||
+ ((aFolder.server instanceof Ci.nsINntpIncomingServer) &&
+ !(aFolder.flags & Ci.nsMsgFolderFlags.Virtual) &&
+ aFolder.server.abbreviate) && !aFolder.isServer) {
+ popupValue = aFolder.name;
+ }
+
+ if (popupValue) {
+ let loc = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "folderSummaryLocation");
+ loc.setAttribute("location", popupValue);
+ document.getAnonymousNodes(this)[0].appendChild(loc);
+ return true;
+ }
+
+ return false;
+ ]]>
+ </body>
+ </method>
+
+ <method name="addSummarizeExplain">
+ <parameter name="aCounts"/>
+ <body>
+ <![CDATA[
+ if (!aCounts || !aCounts[1])
+ return false;
+ let expl = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "folderSummarySubfoldersSummary");
+ let sumString = document.getElementById("bundle_messenger")
+ .getFormattedString("subfoldersExplanation", [aCounts[0], aCounts[1]], 2);
+ expl.setAttribute("subfolders", sumString);
+ document.getAnonymousNodes(this)[0].appendChild(expl);
+ return true;
+ ]]>
+ </body>
+ </method>
+
+ <method name="clear">
+ <body>
+ <![CDATA[
+ var containingBox = document.getAnonymousNodes(this)[0];
+ while (containingBox.hasChildNodes())
+ containingBox.lastChild.remove();
+ ]]>
+ </body>
+ </method>
+ <constructor>
+ <![CDATA[
+ var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+ );
+ ChromeUtils.import("resource://gre/modules/Services.jsm", this);
+ ]]>
+ </constructor>
+ </implementation>
+ </binding>
+
+ <binding id="folderSummary-location">
+ <content>
+ <xul:hbox>
+ <xul:label anonid="location" xbl:inherits="value=location"/>
+ </xul:hbox>
+ </content>
+ </binding>
+
+ <binding id="folderSummary-subfoldersSummary">
+ <content>
+ <xul:hbox>
+ <xul:label anonid="subfolders" xbl:inherits="value=subfolders"/>
+ </xul:hbox>
+ </content>
+ </binding>
+
+ <binding id="folderSummary-message">
+ <content>
+ <xul:vbox class="folderSummaryMessage">
+ <xul:hbox class="folderSummary-message-row">
+ <xul:label anonid="subject" flex="1" class="folderSummary-subject" xbl:inherits="value=subject" crop="right"/>
+ <xul:label anonid="sender" class="folderSummary-sender" xbl:inherits="value=sender" crop="right"/>
+ <xul:spring anonid="spring" flex="100%"/>
+ </xul:hbox>
+ <xul:description anonid="preview" class="folderSummary-message-row folderSummary-previewText" xbl:inherits="value=previewText" crop="right"></xul:description>
+ </xul:vbox>
+ </content>
+ <implementation>
+ <constructor>
+ <![CDATA[
+ var { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+ );
+ ChromeUtils.import("resource://gre/modules/Services.jsm", this);
+
+ if (!this.Services.prefs.getBoolPref("mail.biff.alert.show_preview"))
+ document.getAnonymousElementByAttribute(this, "anonid", "preview").hidden = true;
+ var hideSubject = !this.Services.prefs.getBoolPref("mail.biff.alert.show_subject");
+ var hideSender = !this.Services.prefs.getBoolPref("mail.biff.alert.show_sender");
+ if (hideSubject)
+ document.getAnonymousElementByAttribute(this, "anonid", "subject").hidden = true;
+ if (hideSender)
+ document.getAnonymousElementByAttribute(this, "anonid", "sender").hidden = true;
+ if (hideSubject && hideSender)
+ document.getAnonymousElementByAttribute(this, "anonid", "spring").hidden = true;
+ ]]>
+ </constructor>
+ </implementation>
+ <handlers>
+ <handler event="click" button="0">
+ <![CDATA[
+ var topmostMsgWindow;
+ try {
+ topmostMsgWindow = MailServices.mailSession.topmostMsgWindow;
+ } catch (ex) {}
+
+ if (topmostMsgWindow)
+ {
+ // Bring window to the front
+ topmostMsgWindow.domWindow.focus();
+
+ try {
+ // SelectFolder throws an exception if the folder is not in the current folder view
+ MailServices.mailSession.topmostMsgWindow.windowCommands.selectFolder(this.folderUri);
+ MailServices.mailSession.topmostMsgWindow.windowCommands.selectMessage(this.messageUri);
+ } catch (ex) {}
+ }
+ else
+ {
+ // open a new window
+ var mailWindowService = Cc["@mozilla.org/messenger/windowservice;1"].
+ getService(Ci.nsIMessengerWindowService);
+ mailWindowService.openMessengerWindowWithUri("mail:3pane", this.folderUri, this.msgKey);
+ }
+
+ if (gAlertListener)
+ gAlertListener.observe(null, "alertclicksimplecallback", "");
+ ]]>
+ </handler>
+ </handlers>
+ </binding>
+</bindings>