UserChrome.js/Mail
This page contains sample userChrome.js scripts for mail.
Unless otherwise stated, they are self-contained scripts that work in both Thunderbird and SeaMonkey.
Note: Version 0.8 of the userChrome.js extension may create a default userChrome.js file that is only appropriate for browsers. Add scripts for mail above the default content of the file. For Thunderbird, delete the default content.
Account settings list
This script collapses the list of accounts in account settings, leaving only one account expanded. It also removes the hard-coded width from the list, so that you can set your preferred width in userChrome.css.
Note: The Mail Tweak extension also provides this feature, and it does not require any script.
When you copy the script from this web page, make sure that you scroll to get all of it:
AccountSelect = { load: function (win) { const tgt = "/content/AccountManager.xul" if (win.location.pathname != tgt) return with (win) { document.getElementById("accounttree") .parentNode .removeAttribute("style") selectServer = function (id, pg) { var svr = null, elem = null if (id) svr = document.getElementById(id) if (!svr) svr = getFirstAccount() if (id && svr && pg) elem = findSelectPage(svr, pg) if (!elem) elem = svr var view = accounttree.view var box = accounttree.treeBoxObject for (var i = 0; i < view.rowCount; ++i) { if (!view.isContainer(i)) continue if (view.isContainerOpen(i)) view.toggleOpenState(i) } var n = view.getIndexOfItem(elem) view.selection.select(n) view.toggleOpenState(n) box.ensureRowIsVisible(n) elem = svr.lastChild.lastChild if (elem.localName == "treeitem") n = view.getIndexOfItem(elem) box.ensureRowIsVisible(n) } } }, observe: function (win, act) { if (act == "domwindowopened") win.addEventListener( "load", function () {AccountSelect.load(win)}, false ) } } Components .classes["@mozilla.org/embedcomp/window-watcher;1"] .getService(Components.interfaces.nsIWindowWatcher) .registerNotification(AccountSelect)
To set the width, add a rule like this to your userChrome.css file, and adjust the number depending on your font. For example, you could adjust it so that Composition & Addressing is completely visible:
#accounttree {min-width: 18em !important;}
See also: Dialog too small or too large
Auto-hide folders
This script provides a simple way to make the folders sidebar auto-hide. To reveal the sidebar, move your mouse over the splitter at the left edge of the mail window. To hide it, move your mouse over the main part of the mail window.
Note: The Mail Tweak extension also provides this feature, and it does not require any script.
AutoHideFolders = document.getElementById("folderpane_splitter") || document.getElementById("gray_vertical_splitter") AutoHideFolders.addEventListener( "mouseover", function () {AutoHideFolders.setAttribute("state", "open")}, false) AutoHideFolders.nextSibling.addEventListener( "mouseover", function () {AutoHideFolders.setAttribute("state", "collapsed")}, false)
Display name from address book
This Thunderbird-only script uses your personal address book to look up addresses in message headers, and displays the name from your address book instead of the name in the header. It places the name and address from the header in a tooltip, which you can see by positioning your mouse pointer over the header.
To activate this feature, choose Options (Preferences) – Advanced – General and check the box: "Show only display name for people in my address book" This is a standard Thunderbird feature, but it only displays names found in messages. The script modifies it to display names found in your address book. The modification is the subject of bug 243631.
You can customize the script by specifying a different address book field as the cardColumn to display—for example: nickName or aimScreenName For the full list, see: nsIAbCard
To enable the display name feature for From or Reply-To addresses, change abFrom or abReplyTo to true.
When you copy the script from this web page, make sure that you scroll to get all of it:
ABDisplayName = { // customize: cardColumn: "displayName", abFrom: false, abReplyTo: false, DB: Components .classes["@mozilla.org/addressbook;1"] .createInstance(Components.interfaces.nsIAddressBook) .getAbDatabaseFromURI("moz-abmdbdirectory://abook.mab"), card: function (addr, col) { return this.DB.getCardFromAttribute(null, col, addr, true) }, load: function (win) { if (!win.AddExtraAddressProcessing) return win.AddExtraAddressProcessing = function (addr, node) { var label = "", tip = "", ok = gShowCondensedEmailAddresses var parent = node.parentNode, id = parent.id while (!id) parent = parent.parentNode, id = parent.id if (ok && id == "expandedfromBox") ok = ABDisplayName.abFrom else if (ok && id == "expandedreply-toBox") ok = ABDisplayName.abReplyTo if (ok) { var card = ABDisplayName.card(addr, "LowercasePrimaryEmail") if (!card) card = ABDisplayName.card(addr, "SecondEmail") var cardName = card? card[ABDisplayName.cardColumn] : "" var mailName = node.getAttribute("displayName") label = cardName? cardName : mailName? mailName : "" tip = cardName? mailName : mailName? cardName : "" } if (label) { node.setAttribute("label", label) node.setAttribute("tooltiptext", tip? tip + " <" + addr + ">" : addr) node.setAttribute("tooltip", "emailAddressTooltip") } else node.removeAttribute("tooltiptext") } }, observe: function (win, act) { if (act == "domwindowopened") win.addEventListener( "load", function () {ABDisplayName.load(win)}, false ) } } ABDisplayName.load(self) Components .classes["@mozilla.org/embedcomp/window-watcher;1"] .getService(Components.interfaces.nsIWindowWatcher) .registerNotification(ABDisplayName)
Folder icons (1)
This script provides a simple way to specify icons, colors or other styles for particular accounts or folders in the folder pane. The script is only an enabler—you must specify style rules in a userChrome.css file in the usual way.
This script identifies accounts and folders by name, so all accounts and folders with the same name get the same style. For a method that identifies accounts and folders individually, see the next section.
Notes:
- Spaces or other special characters in account or folder names might cause problems with this script.
- The Mail Tweak extension also provides a similar feature, and it does not require any script.
// make account and folder names available to userChrome.css with (document.getElementById("folderNameCell")) setAttribute("properties", getAttribute("properties") + " name-?folderTreeName")
For example, if you want a special icon for an account or folder named Haddock, you can use code like this in userChrome.css:
treechildren::-moz-tree-image(folderNameCol, name-Haddock) {list-style-image: url("fish.png") !important;}
In this example, the image file fish.png should be 16×16 pixels with a transparent background, stored in the same place as your userChrome files.
For another example, if you want all folders named ToDo to appear in bold red, you can use code like this in userChrome.css:
treechildren::-moz-tree-cell-text(folderNameCol, name-ToDo) {color: red !important; font-weight: bold !important;}
Folder icons (2)
This script provides another way to specify icons, colors or other styles for particular accounts or folders in the folder pane. The script is only an enabler—you must specify style rules in a userChrome.css file in the usual way.
Note: The Mail Tweak extension also provides a similar feature.
This script identifies accounts and folders individually by using a mailbox URI. In a mailbox URI, spaces and other special characters are encoded so that they do not cause any problems.
To use this script, customize the list of mailbox URIs near the top so that it lists the accounts and folders that you want to style. Ensure that every mailbox URI in the list is enclosed in double-quote characters. Ensure that every mailbox URI except the last is followed by a comma.
In this example, the first mailbox URI specifies an account, and the second mailbox URI specifies that account's Inbox:
FolderStyle = { // customize... folder: [ "mailbox://someone@pop3.example.net", "mailbox://someone@pop3.example.net/Inbox" ], load: function () { var ds = Components .classes["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"] .createInstance(Components.interfaces.nsIRDFDataSource) var styleURI = "http://home.netscape.com/NC-rdf#Style" var p = RDF.GetResource(styleURI) for (var i = 0; i < this.folder.length; ++i) ds.Assert(RDF.GetResource(this.folder[i]), p, RDF.GetLiteral(i), true) document.getElementById("folderTree").database.AddDataSource(ds) var bindings = document.getElementById("folderTree") .firstChild.firstChild.firstChild.nextSibling with (bindings.appendChild(document.createElement("binding"))) setAttribute("subject", "?member"), setAttribute("predicate", styleURI), setAttribute("object", "?style") with (document.getElementById("folderNameCell")) setAttribute("properties", getAttribute("properties") + " style-?style") } } FolderStyle.load()
In your |userChrome.css file, add rules to specify the style for each account or folder. The property style-0 identifies the first mailbox URI in your list, the property style-1 identifies the next, and so on. This example gives the account name a green background, and makes the name of its Inbox folder green:
treechildren::-moz-tree-cell-text(folderNameCol, style-0) {background-color: palegreen !important;} treechildren::-moz-tree-cell-text(folderNameCol, style-1) {color: limegreen !important;}
This Thunderbird-only script modifies the View – Messages menu and the Mail Views control on the toolbar so that certain tag views and custom views that you use most often are easier to select.
Note: The View – Messages menu is only available when you customize the toolbar so that it includes the Mail Views control. This is bug 332802. To work around it, customize the toolbar then hide the Mail Views control with userChrome.css: #mailviews-container {visibility: collapse;}
Customize this script by naming the views that you want to promote. You can name tag views or custom views. Ensure that you preserve the punctuation in the script. Each view name must be quoted, and every view name except the last must be followed by a comma.
Promoted views appear after All and Unread in the menu, in the order that you name them in the script.
A limitation of this script is that it does not monitor changes that you make to tags or custom views. To refresh the menus, restart Thunderbird.
When you copy the script from this web page, make sure that you scroll to get all of it:
MessageViewMenu = { // customize... promote: [ "Important", "People I Know", "Personal", "Last 5 Days" ], // translate... title: "Message View Menu", error: "No view named: %", MVL: Components .classes["@mozilla.org/messenger/mailviewlist;1"] .getService(Components.interfaces.nsIMsgMailViewList), MTS: Components .classes["@mozilla.org/messenger/tagservice;1"] .getService(Components.interfaces.nsIMsgTagService), PS: Components .classes["@mozilla.org/embedcomp/prompt-service;1"] .getService(Components.interfaces.nsIPromptService), customize: function (id) { var tgt = document.getElementById(id) if (!tgt || tgt.parentNode.hasAttribute("messageviewmenu")) return var radio = tgt.parentNode.parentNode.localName == "menu" for each (var name in this.promote) { var v = this.tag(name) || this.view(name) if (!v && radio) this.PS.alert(self, this.title, this.error.replace(/%/, name)) else { with (tgt.parentNode.insertBefore(document.createElement("menuitem"), tgt.nextSibling)) { className = "messageviewmenu" + (v.color? " lc-" + v.color.substr(1) : "") setAttribute("value", v.key? ":" + v.key : v) setAttribute("label", name) if (radio) setAttribute("accesskey", name.charAt(0)), setAttribute("type", "radio") setAttribute("oncommand", "ViewChangeByMenuitem(this)") } tgt = tgt.nextSibling } } tgt.parentNode.setAttribute("messageviewmenu", "true") if (radio) tgt.parentNode.addEventListener("popupshowing", this.refresh, false) }, load: function (win) { this.customize("viewMessageUnread") this.customize("viewPickerUnread") }, observe: function (win, act) { if (act == "domwindowopened") win.addEventListener( "load", function () {MessageViewMenu.load(win)}, false ) }, refresh: function (event) { var item = event.target.firstChild while (item) with (item) { if (className.indexOf("messageviewmenu") >= 0) setAttribute("checked", getAttribute("value") == gCurrentViewValue) item = nextSibling } }, tag: function (name) { if (!this._tags) this._tags = this.MTS.getAllTags({}) for each (var t in this._tags) if (t.tag == name) return t return null }, view: function (name) { for (var i = 0; i < this.MVL.mailViewCount; ++i) { var v = this.MVL.getMailViewAt(i) if (v.prettyName == name) return i + 9 } return "" } } MessageViewMenu.load(self) Components .classes["@mozilla.org/embedcomp/window-watcher;1"] .getService(Components.interfaces.nsIWindowWatcher) .registerNotification(MessageViewMenu)
Permanent pen
This script adds a "permanent pen" for adding notes to HTML replies and drafts.
Note: The Mail Tweak extension also provides this feature, and it does not require any script.
Normally, when you type in existing text, the characters that you type inherit the style of the surrounding text. When the permanent pen is active, the characters that you type have a style that is permanently set. For example, you reply to a message quoting the original text. You press F9 to activate the permanent pen, and type some comments in various places in the original text. All your comments appear in red.
An indicator in the formatting toolbar shows when permanent pen is active. To see the formatting toolbar, you must be using the HTML editor. If you do not see the formatting toolbar, choose: View – Toolbars – Formatting Toolbar
As supplied, this script sets the permanent pen color to red. To use some other color, change the value of color near the top of the script. To set other text style attributes, change styleSet to list the attributes. To clear text style attributes, change styleNot to list the attributes. The available attributes are listed in the HTML section here, and they match the choices in the Format – Text Style menu. In this script, enclose them in quotes and separate them with commas. For example, to specify a green, bold, italic permanent pen that clears underlining:
color: "green", styleSet: ["bold", "italic"], styleNot: ["underline"],
As supplied, this script uses the F9 key to toggle the permanent pen. To use some other key, change the value of keyCode near the top of the script. To specify a key combination, set one or more of altKey, ctrlKey, shiftKey to true.
When you copy the script from this web page, make sure that you scroll to get all of it:
PermanentPen = { // customize... color: "red", styleSet: [], styleNot: [], keyCode: "F9", altKey: false, ctrlKey: false, shiftKey: false, key: function (win, event) { if (!win.IsHTMLEditor()) return var my = PermanentPen if (event.keyCode == event["DOM_VK_" + my.keyCode] && event.altKey === my.altKey && event.ctrlKey === my.ctrlKey && event.shiftKey === my.shiftKey ) { my._active = !my._active win.document.getElementById("ColorButtons").style .borderLeftColor = my._active? my.color : "transparent" return } if (!my._active) return with (win) { gColorObj.TextColor = my.color GetCurrentEditor().setInlineProperty(my._font, "color", my.color) for each (var cmd in my.styleSet) { cmd = "cmd_" + cmd var on = top.document.getElementById(cmd).getAttribute("state") == "true" if (!on) doStyleUICommand(cmd) } for each (var cmd in my.styleNot) { cmd = "cmd_" + cmd var on = top.document.getElementById(cmd).getAttribute("state") == "true" if (on) doStyleUICommand(cmd) } } }, load: function (win) { const tgt = "/content/messengercompose/messengercompose.xul" if (win.location.pathname != tgt) return PermanentPen._active = false with (win.document.getElementById("ColorButtons").style) borderStyle = "solid", borderWidth = "0 0 0 1px", borderLeftColor = "transparent" if (!win.gAtomService) win.GetAtomService() PermanentPen._font = win.gAtomService.getAtom("font") win.addEventListener("keypress", function (event) {PermanentPen.key(win, event)}, true) }, observe: function (win, act) { if (act == "domwindowopened") win.addEventListener( "load", function () {PermanentPen.load(win)}, false ) } } Components .classes["@mozilla.org/embedcomp/window-watcher;1"] .getService(Components.interfaces.nsIWindowWatcher) .registerNotification(PermanentPen)
Print preview focus
This script works around a bug that leaves the print preview window without focus, making it difficult to access with the keyboard.
PrintPreviewFocus = { load: function (win) { if (win.document.documentElement.id == "printEngineWin") this.preview = win }, unload: function (win) { if (win.location.pathname == "/content/printPreviewProgress.xul") this.preview.setTimeout( function () {try {PrintPreviewFocus.preview.focus()} catch (ex) {}}, 0 ) }, observe: function (win, act) { if (act == "domwindowopened") { win.addEventListener( "load", function () {PrintPreviewFocus.load(win)}, false ) win.addEventListener( "unload", function () {PrintPreviewFocus.unload(win)}, false ) } } } Components .classes["@mozilla.org/embedcomp/window-watcher;1"] .getService(Components.interfaces.nsIWindowWatcher) .registerNotification(PrintPreviewFocus)
Priority column icons
This script for Thunderbird changes the Priority column in the thread pane so that it displays icons instead of text. To make this work, there are three steps.
1. Add this script to your userChrome.js file to modify the Priority column:
PriorityIcon = { init: function () { const p = ["priorityNotSet", "priorityNone", "priorityLowest", "priorityLow", "priorityNormal", "priorityHigh", "priorityHighest"] this._atom = [] with (Components .classes["@mozilla.org/atom-service;1"] .getService(Components.interfaces.nsIAtomService)) for (var i = 0; i <= 6; ++i) this._atom[i] = getAtom(p[i]) Components .classes["@mozilla.org/observer-service;1"] .getService(Components.interfaces.nsIObserverService) .addObserver(this, "MsgCreateDBView", false) }, observe: function (subject, topic, data) { gDBView.addColumnHandler("priorityCol", this) }, set: function (row, props) { with (gDBView) var p = db .GetMsgHdrForKey(getKeyAt(row)) .getUint32Property("priority") props.AppendElement(this._atom[p]) }, // nsIMsgCustomColumnHandler... getCellProperties: function (row, col, props) {this.set(row, props)}, getRowProperties: function (row, props) {}, getImageSrc: function (row, col) {}, getCellText: function (row, col) {}, getSortStringForRow: function (hdr) {}, getSortLongForRow: function (hdr) {return hdr.getUint32Property("priority")}, isString: function () {return false} } PriorityIcon.init()
2. Add this code to your userChrome.css file to specify details of the column display:
#priorityCol, treechildren::-moz-tree-image(priorityCol) {list-style-image: url("Priority-icons-tb.png");} treechildren::-moz-tree-image(priorityCol) {-moz-image-region: rect(0px 16px 16px 0px);} treechildren::-moz-tree-image(priorityCol, priorityNone) {-moz-image-region: rect(0px 32px 16px 16px);} treechildren::-moz-tree-image(priorityCol, priorityLowest) {-moz-image-region: rect(0px 48px 16px 32px);} treechildren::-moz-tree-image(priorityCol, priorityLow) {-moz-image-region: rect(0px 64px 16px 48px);} treechildren::-moz-tree-image(priorityCol, priorityNormal) {-moz-image-region: rect(0px 80px 16px 64px);} treechildren::-moz-tree-image(priorityCol, priorityHigh) {-moz-image-region: rect(0px 96px 16px 80px);} treechildren::-moz-tree-image(priorityCol, priorityHighest) {-moz-image-region: rect(0px 112px 16px 96px);} #priorityCol {-moz-image-region: rect(0px 128px 16px 112px); -moz-binding: url("chrome://global/content/bindings/tree.xml#treecol-image"); padding-left: 0 !important; width: 23px; max-width: 23px;}
3. Download one of these images containing the actual icons. (To download an image, get a context menu for it in your web browser—for example, by right-clicking it. From the context menu choose: Save Image As... ) Save it in the same place as your userChrome.js and userChrome.css files. If necessary, rename it Priority-icons-tb.png to match the name in your userChrome.css file:
The image contains eight icons, for the seven priority levels and the column header. The icons for Not Set, None and Normal are blank.
Word count
This script provides a word count in the status bar when you select text while composing a message.
Notes:
- Like the spelling checker, this script sees hidden text that may be in an HTML message, and includes it in the count. Thunderbird and SeaMonkey do not normally insert hidden text, but HTML from other sources might contain hidden text.
- The Mail Tweak extension also provides this feature, and it does not require any script.
WordCount = { // translatable text... oneWord: "1 word", manyWords: "% words", load: function (win) { const tgt = "/content/messengercompose/messengercompose.xul" if (win.location.pathname != tgt) return this._status = win.document.getElementById("statusText") win.countWords = function () { var sel = null with (win.document.getElementById("content-frame")) try {sel = getEditor(contentWindow).selection} catch (ex) {} if (sel) sel .QueryInterface(Components.interfaces.nsISelectionPrivate) .addSelectionListener(WordCount) } with (win.document.getElementById("selectEditMenuItems")) setAttribute("oncommandupdate", getAttribute("oncommandupdate") + ";countWords()") }, notifySelectionChanged: function (doc, sel, why) { var wds = sel.toString().match(/(^|\s+)\S/g) var n = wds? wds.length : 0 this._status.label = n == 0 ? "" : n == 1 ? this.oneWord : this.manyWords.replace("%", n) }, observe: function (win, act) { if (act == "domwindowopened") win.addEventListener( "load", function () {WordCount.load(win)}, false ) } } Components .classes["@mozilla.org/embedcomp/window-watcher;1"] .getService(Components.interfaces.nsIWindowWatcher) .registerNotification(WordCount)
Image defaults
This script changes the defaults in the Image Properties dialog—the dialog that you see when when you are writing a message and you choose Insert – Image... The changes mean that by default Thunderbird does not attach the image file, and does not require alternate text. If you only need one of these features, change the appropriate false to true in the settings near the start of the script.
See also: Creating complex mails with inline images
Note: The Mail Tweak extension also provides this feature, and it does not require any script.
ImageDefaults = { // settings... attachLabel: "Attach", defaultAttach: false, requireAltText: false, load: function (win) { const tgt = "/content/EdImageProps.xul" if (win.location.pathname != tgt) return var doc = win.document var box = doc.getElementById("AdvancedEdit") with (box.insertBefore(doc.createElement("checkbox"), box.firstChild)) { setAttribute("id", "ImageDefaults-attach") setAttribute("label", this.attachLabel) setAttribute("disabled", "true") } win.ImageDefaults_enable = win.doOverallEnabling win.doOverallEnabling = function () { with (win) { var ok = (TrimString(gDialog.srcInput.value) != "") var dnsa = document.getElementById("ImageDefaults-attach") dnsa.checked = !ok? false : gInsertNewImage? ImageDefaults.defaultAttach : !globalElement.hasAttribute("moz-do-not-send") dnsa.disabled = !ok if (!ImageDefaults.requireAltText) with (gDialog) { if (altTextRadioGroup.selectedIndex == 0 && TrimString(altTextInput.value) == "") altTextRadioGroup.selectedIndex = 1 } ImageDefaults_enable() } } win.ImageDefaults_validate = win.ValidateData win.ValidateData = function () { with (win) { var src = TrimString(gDialog.srcInput.value) var dnsa = document.getElementById("ImageDefaults-attach") var mdns = (src && !dnsa.checked) globalElement[mdns? "setAttribute" : "removeAttribute"]("moz-do-not-send", "true") return ImageDefaults_validate() } } }, observe: function (win, act) { if (act == "domwindowopened") win.addEventListener( "load", function () {ImageDefaults.load(win)}, false ) } } Components .classes["@mozilla.org/embedcomp/window-watcher;1"] .getService(Components.interfaces.nsIWindowWatcher) .registerNotification(ImageDefaults)