1 /* ***** BEGIN LICENSE BLOCK *****
  2  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3  *
  4  * The contents of this file are subject to the Mozilla Public License Version
  5  * 1.1 (the "License"); you may not use this file except in compliance with
  6  * the License. You may obtain a copy of the License at
  7  * http://www.mozilla.org/MPL/
  8  *
  9  * Software distributed under the License is distributed on an "AS IS" basis,
 10  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 11  * for the specific language governing rights and limitations under the
 12  * License.
 13  *
 14  * The Original Code is Mail utility functions for GMail Conversation View
 15  *
 16  * The Initial Developer of the Original Code is
 17  * Jonathan Protzenko
 18  * Portions created by the Initial Developer are Copyright (C) 2010
 19  * the Initial Developer. All Rights Reserved.
 20  *
 21  * Contributor(s):
 22  *
 23  * Alternatively, the contents of this file may be used under the terms of
 24  * either the GNU General Public License Version 2 or later (the "GPL"), or
 25  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 26  * in which case the provisions of the GPL or the LGPL are applicable instead
 27  * of those above. If you wish to allow use of your version of this file only
 28  * under the terms of either the GPL or the LGPL, and not to allow others to
 29  * use your version of this file under the terms of the MPL, indicate your
 30  * decision by deleting the provisions above and replace them with the notice
 31  * and other provisions required by the GPL or the LGPL. If you do not delete
 32  * the provisions above, a recipient may use your version of this file under
 33  * the terms of any one of the MPL, the GPL or the LGPL.
 34  *
 35  * ***** END LICENSE BLOCK ***** */
 36 
 37 /**
 38  * @fileoverview This file provides various utilities: some helpers to deal with
 39  * identity management, some helpers for JS programming, some helpers for
 40  * low-level XPCOM stuff...
 41  * @author Jonathan Protzenko
 42  */
 43 
 44 var EXPORTED_SYMBOLS = [
 45   // Identity management helpers
 46   'gIdentities', 'fillIdentities',
 47   // JS programming helpers
 48   'range', 'MixIn',
 49   // XPCOM helpers
 50   'NS_FAILED', 'NS_SUCCEEDED',
 51   // Various formatting helpers
 52   'dateAsInMessageList', 'escapeHtml', 'parseMimeLine',
 53 ]
 54 
 55 const Ci = Components.interfaces;
 56 const Cc = Components.classes;
 57 const Cu = Components.utils;
 58 Cu.import("resource:///modules/iteratorUtils.jsm"); // for fixIterator
 59 
 60 const i18nDateFormatter = Cc["@mozilla.org/intl/scriptabledateformat;1"]
 61                             .createInstance(Ci.nsIScriptableDateFormat);
 62 const headerParser = Cc["@mozilla.org/messenger/headerparser;1"]
 63                        .getService(Ci.nsIMsgHeaderParser);
 64 const msgAccountManager = Cc["@mozilla.org/messenger/account-manager;1"]
 65                              .getService(Ci.nsIMsgAccountManager);
 66 
 67 /**
 68  * Low-level XPCOM-style macro. You might need this for the composition and
 69  *  sending listeners which will pass you some status codes.
 70  * @param {Int} v The status code
 71  * @return {Bool}
 72  */
 73 function NS_FAILED(v) {
 74   return (v & 0x80000000);
 75 }
 76 
 77 /**
 78  * Low-level XPCOM-style macro. You might need this for the composition and
 79  *  sending listeners which will pass you some status codes.
 80  * @param {Int} v The status code
 81  * @return {Bool}
 82  */
 83 function NS_SUCCEEDED(v) {
 84   return !NS_FAILED(v);
 85 }
 86 
 87 /**
 88  * Python-style range function to use in list comprehensions.
 89  *  @param {Number} begin 
 90  *  @param {Number} end 
 91  *  @return {Iterator} An iterator that yields from begin to end - 1.
 92  */
 93 function range(begin, end) {
 94   for (let i = begin; i < end; ++i) {
 95     yield i;
 96   }
 97 }
 98 
 99 /**
100  * MixIn-style helper. Adds aMixIn properties, getters and setters to
101  *  aConstructor.
102  * @param {Object} aConstructor
103  * @param {Object} aMixIn
104  */
105 function MixIn(aConstructor, aMixIn) {
106   let proto = aConstructor.prototype;
107   for (let [name, func] in Iterator(aMixIn)) {
108     if (name.substring(0, 4) == "get_")
109       proto.__defineGetter__(name.substring(4), func);
110     else
111       proto[name] = func;
112   }
113 }
114 
115 /**
116  * A global pointer to all the identities known for the user. Feel free to call
117  *  fillIdentities again if you feel that the user has updated them!
118  * The keys are email addresses, the values are <tt>nsIMsgIdentity</tt> objects.
119  *
120  * @const
121  */
122 let gIdentities = {};
123 
124 /**
125  * This function you should call to populate the gIdentities global object. The
126  *  recommended time to call this is after the mail-startup-done event, although
127  *  doing this at overlay load-time seems to be fine as well.
128  * Beware, although gIdentities has a "default" key, it is not guaranteed to be
129  *  non-null.
130  */
131 function fillIdentities() {
132   for each (let id in fixIterator(msgAccountManager.allIdentities, Ci.nsIMsgIdentity)) {
133     gIdentities[id.email.toLowerCase()] = id;
134   }
135   gIdentities["default"] = msgAccountManager.defaultAccount.defaultIdentity;
136 }
137 
138 /**
139  * A stupid formatting function that uses the i18nDateFormatter XPCOM component
140  * to format a date just like in the message list
141  * @param {Date} aDate a javascript Date object
142  * @return {String} a string containing the formatted date
143  */
144 function dateAsInMessageList(aDate) {
145   // Is it today? (Less stupid tests are welcome!)
146   let format = aDate.toLocaleDateString("%x") == (new Date()).toLocaleDateString("%x")
147     ? Ci.nsIScriptableDateFormat.dateFormatNone
148     : Ci.nsIScriptableDateFormat.dateFormatShort;
149   // That is an ugly XPCOM call!
150   return i18nDateFormatter.FormatDateTime(
151     "", format, Ci.nsIScriptableDateFormat.timeFormatNoSeconds,
152     aDate.getFullYear(), aDate.getMonth() + 1, aDate.getDate(),
153     aDate.getHours(), aDate.getMinutes(), aDate.getSeconds());
154 }
155 
156 /**
157  * Helper function to escape some XML chars, so they display properly in
158  *  innerHTML.
159  * @param {String} s input text
160  * @return {String} The string with <, >, and & replaced by the corresponding entities.
161  */
162 function escapeHtml(s) {
163   s += "";
164   // stolen from selectionsummaries.js (thanks davida!)
165   return s.replace(/[<>&]/g, function(s) {
166       switch (s) {
167           case "<": return "<";
168           case ">": return ">";
169           case "&": return "&";
170           default: throw Error("Unexpected match");
171           }
172       }
173   );
174 }
175 
176 /**
177  * Wraps the low-level header parser stuff.
178  * @param {String} aMimeLine a line that looks like "John <john@cheese.com>, Jane <jane@wine.com>"
179  * @return {Array} a list of { email, name } objects
180  */
181 function parseMimeLine (aMimeLine) {
182   let emails = {};
183   let fullNames = {};
184   let names = {};
185   let numAddresses = headerParser.parseHeadersWithArray(aMimeLine, emails, names, fullNames);
186   if (numAddresses)
187     return [{ email: emails.value[i], name: names.value[i], fullName: fullNames.value[i] }
188       for each (i in range(0, numAddresses))];
189   else
190     return [{ email: "", name: "-", fullName: "-" }];
191 }
192