null null 15 true false acct.incomingServer.rootFolder); } else { // If we do have a parent folder, then we just build based on those // subFolders for that parent. folders = this.toArray(this.fixIterator(this._parentFolder.subFolders, Ci.nsIMsgFolder)); } this._build(folders); // Lastly, we add a listener to get notified of changes in the folder // structure. this.MailServices.mailSession.AddFolderListener(this._listener, Ci.nsIFolderListener.all); this._initialized = true; ]]> 0) { folders = folders.filter(function(aFolder) { return !(excludeServers.indexOf(aFolder.server.key) != -1); }); } /* This code block will do the following: Add a menu item that refers back to the parent folder when there is a showFileHereLabel attribute or no mode attribute. However the code won't add such a menu item if one of the following conditions is met: (*) There is no parent folder (*) Folder is server and showAccountsFileHere is explicitly false (*) Current folder has a mode, the parent folder can be selected, no messages can be filed into the parent folder (e.g. when the parent folder is a news group or news server) and the folder mode is not equal to newFolder The menu item will have the value of the fileHereLabel attribute as label or if the attribute does not exist the name of the parent folder instead. */ let parent = this._parentFolder; if (parent && (this.getAttribute("showFileHereLabel") == "true" || !mode)) { let showAccountsFileHere = this.getAttribute("showAccountsFileHere"); if ((!parent.isServer || showAccountsFileHere != "false") && (!mode || mode == "newFolder" || parent.noSelect || parent.canFileMessages || showAccountsFileHere == "true")) { var menuitem = document.createElement("menuitem"); menuitem._folder = this._parentFolder; menuitem.setAttribute("generated", "true"); if (this.hasAttribute("fileHereLabel")) { menuitem.setAttribute("label", this.getAttribute("fileHereLabel")); menuitem.setAttribute("accesskey", this.getAttribute("fileHereAccessKey")); } else { menuitem.setAttribute("label", this._parentFolder.prettyName); menuitem.setAttribute("class", "folderMenuItem menuitem-iconic"); this._setCssSelectors(this._parentFolder, menuitem); } // Eww. have to support some legacy code here... menuitem.setAttribute("id", this._parentFolder.URI); this.appendChild(menuitem); if (this._parentFolder.noSelect) menuitem.setAttribute("disabled", "true"); var sep= document.createElement("menuseparator"); sep.setAttribute("generated", "true"); this.appendChild(sep); } } let globalInboxFolder = null; // See if this is the toplevel menu (usually with accounts). if (!this._parentFolder) { // Some menus want a "Recent" option, but that should only be on our // top-level menu if (this.getAttribute("showRecent") == "true") this._buildRecentMenu(); // If we are showing the accounts for deferring, move Local Folders to the top. if (mode == "deferred") { globalInboxFolder = this.MailServices.accounts.localFoldersServer .rootFolder; let localFoldersIndex = folders.indexOf(globalInboxFolder); if (localFoldersIndex != -1) { folders.splice(localFoldersIndex, 1); folders.unshift(globalInboxFolder); } } // If we're the root of the folder hierarchy, then we actually don't // want to sort the folders, but rather the accounts to which the // folders belong. Since that sorting was already done, we don't need // to do anything for that case here. } else { // Sorts the list of folders. We give first priority to the sortKey // property if it is available, otherwise a case-insensitive // comparison of names. folders = folders.sort(function nameCompare(a, b) { return a.compareSortKeys(b); }); } /* In some cases, the user wants to have a list of subfolders for only * some account types (or maybe all of them). So we use this to * determine what the user wanted. */ var shouldExpand; var labels = null; if (this.getAttribute("expandFolders") == "true" || !this.hasAttribute("expandFolders")) { shouldExpand = function (e) { return true; }; } else if (this.getAttribute("expandFolders") == "false") { shouldExpand = function (e) { return false; }; } else { /* We want a subfolder list for only some servers. We also may need * to create headers to select the servers. If so, then headlabels * is a comma-delimited list of labels corresponding to the server * types specified in expandFolders. */ var types = this.getAttribute("expandFolders").split(/ *, */); // Set the labels. labels[type] = label if (this.hasAttribute("headlabels")) { var labelNames = this.getAttribute("headlabels").split(/ *, */); labels = {}; // If the length isn't equal, don't give them any of the labels, // since any combination will probably be wrong. if (labelNames.length == types.length) { for (var index in types) labels[types[index]] = labelNames[index]; } } shouldExpand = function (e) { return types.indexOf(e) != -1; }; } // We need to call this, or hasSubFolders will always return false. // Remove this workaround when Bug 502900 is fixed. this.MailUtils.discoverFolders(); this._serversOnly = true; for (let folder of folders) { let node; if (!folder.isServer) this._serversOnly = false; // If we're going to add subFolders, we need to make menus, not // menuitems. if (!folder.hasSubFolders || !shouldExpand(folder.server.type)) { node = document.createElement("menuitem"); // Grumble, grumble, legacy code support node.setAttribute("id", folder.URI); node.setAttribute("class", "folderMenuItem menuitem-iconic"); node.setAttribute("generated", "true"); this.appendChild(node); } else { this._serversOnly = false; //xxx this is slightly problematic in that we haven't confirmed // whether any of the subfolders will pass the filter node = document.createElement("menu"); node.setAttribute("class", "folderMenuItem menu-iconic"); node.setAttribute("generated", "true"); this.appendChild(node); // Create the submenu // (We must use cloneNode here because on OS X the native menu // functionality and very sad limitations of XBL1 cause the bindings // to never get created for popup if we create a new element. We // perform a shallow clone to avoid picking up any of our children.) var popup = this.cloneNode(false); popup._parentFolder = folder; popup.setAttribute("class", this.getAttribute("class")); popup.setAttribute("type", this.getAttribute("type")); if (this.hasAttribute("fileHereLabel")) popup.setAttribute("fileHereLabel", this.getAttribute("fileHereLabel")); popup.setAttribute("showFileHereLabel", this.getAttribute("showFileHereLabel")); popup.setAttribute("oncommand", this.getAttribute("oncommand")); popup.setAttribute("mode", this.getAttribute("mode")); if (this.hasAttribute("disableServers")) popup.setAttribute("disableServers", this.getAttribute("disableServers")); if (this.hasAttribute("position")) popup.setAttribute("position", this.getAttribute("position")); // If there are labels, add the labels now if (labels) { var serverNode = document.createElement("menuitem"); serverNode.setAttribute("label", labels[folder.server.type]); serverNode._folder = folder; serverNode.setAttribute("generated", "true"); popup.appendChild(serverNode); var sep = document.createElement("menuseparator"); sep.setAttribute("generated", "true"); popup.appendChild(sep); } popup.setAttribute("generated", "true"); node.appendChild(popup); } if (disableServers.indexOf(folder.server.key) != -1) node.setAttribute("disabled", "true"); node._folder = folder; let label = ""; if (mode == "deferred" && folder.isServer && folder.server.rootFolder == globalInboxFolder) { label = this._stringBundle.get("globalInbox", [folder.prettyName]); } else { label = folder.prettyName; } node.setAttribute("label", label); this._setCssSelectors(folder, node); } ]]> f.canFileMessages); let recentFolders = this.getMostRecentFolders(allFolders, this._MAXRECENT, "MRMTime"); // Cache the pretty names so that they do not need to be fetched // _MAXRECENT^2 times later. recentFolders = recentFolders.map( function (f) { return { folder: f, name: f.prettyName } }); // Because we're scanning across multiple accounts, we can end up with // several folders with the same name. Find those dupes. let dupeNames = new Set(); for (let i = 0; i < recentFolders.length; i++) { for (let j = i + 1; j < recentFolders.length; j++) { if (recentFolders[i].name == recentFolders[j].name) dupeNames.add(recentFolders[i].name); } } for (let folderItem of recentFolders) { // If this folder name appears multiple times in the recent list, // append the server name to disambiguate. // TODO: // - maybe this could use verboseFolderFormat from messenger.properties // instead of hardcoded " - ". // - disambiguate folders with same name in same account // (in different subtrees). let label = folderItem.name; if (dupeNames.has(label)) label += " - " + folderItem.folder.server.prettyName; folderItem.label = label; } // Make sure the entries are sorted alphabetically. recentFolders.sort((a, b) => this.folderNameCompare(a.label, b.label)); // Now create the Recent folder and its children var menu = document.createElement("menu"); menu.setAttribute("label", this.getAttribute("recentLabel")); menu.setAttribute("accesskey", this.getAttribute("recentAccessKey")); var popup = document.createElement("menupopup"); popup.setAttribute("class", this.getAttribute("class")); popup.setAttribute("generated", "true"); menu.appendChild(popup); // Create entries for each of the recent folders. for (let folderItem of recentFolders) { let node = document.createElement("menuitem"); node.setAttribute("label", folderItem.label); node._folder = folderItem.folder; node.setAttribute("class", "folderMenuItem menuitem-iconic"); this._setCssSelectors(folderItem.folder, node); node.setAttribute("generated", "true"); popup.appendChild(node); } menu.setAttribute("generated", "true"); this.appendChild(menu); if (!recentFolders.length) menu.setAttribute("disabled", "true"); var sep = document.createElement("menuseparator"); sep.setAttribute("generated", "true"); this.appendChild(sep); ]]> null = 0; i--) { let child = this.childNodes[i]; if (child.getAttribute("generated") != "true") continue; if ("_teardown" in child) child._teardown(); child.remove(); } this._removeListener(); this._initialized = false; ]]> this._ensureInitialized();