summaryrefslogtreecommitdiffstats
path: root/comm/suite/chatzilla/js/lib/utils.js
diff options
context:
space:
mode:
Diffstat (limited to 'comm/suite/chatzilla/js/lib/utils.js')
-rw-r--r--comm/suite/chatzilla/js/lib/utils.js1490
1 files changed, 1490 insertions, 0 deletions
diff --git a/comm/suite/chatzilla/js/lib/utils.js b/comm/suite/chatzilla/js/lib/utils.js
new file mode 100644
index 0000000000..d8aa88baef
--- /dev/null
+++ b/comm/suite/chatzilla/js/lib/utils.js
@@ -0,0 +1,1490 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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/. */
+
+// Namespaces we happen to need:
+const XHTML_NS = "http://www.w3.org/1999/xhtml";
+
+var utils = new Object();
+
+var DEBUG = true;
+var dd, warn, TEST, ASSERT;
+
+if (DEBUG) {
+ var _dd_pfx = "";
+ var _dd_singleIndent = " ";
+ var _dd_indentLength = _dd_singleIndent.length;
+ var _dd_currentIndent = "";
+ var _dd_lastDumpWasOpen = false;
+ var _dd_timeStack = new Array();
+ var _dd_disableDepth = Number.MAX_VALUE;
+ var _dd_currentDepth = 0;
+ dd = function _dd(str) {
+ if (typeof str != "string") {
+ dump(str + "\n");
+ } else if (str == "") {
+ dump("<empty-string>\n");
+ } else if (str[str.length - 1] == "{") {
+ ++_dd_currentDepth;
+ if (_dd_currentDepth >= _dd_disableDepth)
+ return;
+ if (str.indexOf("OFF") == 0)
+ _dd_disableDepth = _dd_currentDepth;
+ _dd_timeStack.push (new Date());
+ if (_dd_lastDumpWasOpen)
+ dump("\n");
+ dump (_dd_pfx + _dd_currentIndent + str);
+ _dd_currentIndent += _dd_singleIndent;
+ _dd_lastDumpWasOpen = true;
+ } else if (str[0] == "}") {
+ if (--_dd_currentDepth >= _dd_disableDepth)
+ return;
+ _dd_disableDepth = Number.MAX_VALUE;
+ var sufx = (new Date() - _dd_timeStack.pop()) / 1000 + " sec";
+ _dd_currentIndent =
+ _dd_currentIndent.substr(0, _dd_currentIndent.length -
+ _dd_indentLength);
+ if (_dd_lastDumpWasOpen)
+ dump(str + " " + sufx + "\n");
+ else
+ dump(_dd_pfx + _dd_currentIndent + str + " " +
+ sufx + "\n");
+ _dd_lastDumpWasOpen = false;
+ } else {
+ if (_dd_currentDepth >= _dd_disableDepth)
+ return;
+ if (_dd_lastDumpWasOpen)
+ dump("\n");
+ dump(_dd_pfx + _dd_currentIndent + str + "\n");
+ _dd_lastDumpWasOpen = false;
+ }
+ }
+ warn = function (msg) { dd("** WARNING " + msg + " **"); }
+ TEST = ASSERT = function _assert(expr, msg) {
+ if (!expr) {
+ var m = "** ASSERTION FAILED: " + msg + " **\n" +
+ getStackTrace();
+ try {
+ alert(m);
+ } catch(ex) {}
+ dd(m);
+ return false;
+ } else {
+ return true;
+ }
+ }
+} else {
+ dd = warn = TEST = ASSERT = function (){};
+}
+
+function dumpObject (o, pfx, sep)
+{
+ var p;
+ var s = "";
+
+ sep = (typeof sep == "undefined") ? " = " : sep;
+ pfx = (typeof pfx == "undefined") ? "" : pfx;
+
+ for (p in o)
+ {
+ if (typeof (o[p]) != "function")
+ s += pfx + p + sep + o[p] + "\n";
+ else
+ s += pfx + p + sep + "function\n";
+ }
+
+ return s;
+
+}
+
+/* Dumps an object in tree format, recurse specifiec the the number of objects
+ * to recurse, compress is a boolean that can uncompress (true) the output
+ * format, and level is the number of levels to intitialy indent (only useful
+ * internally.) A sample dumpObjectTree (o, 1) is shown below.
+ *
+ * + parent (object)
+ * + users (object)
+ * | + jsbot (object)
+ * | + mrjs (object)
+ * | + nakkezzzz (object)
+ * | *
+ * + bans (object)
+ * | *
+ * + topic (string) 'ircclient.js:59: nothing is not defined'
+ * + getUsersLength (function) 9 lines
+ * *
+ */
+function dumpObjectTree (o, recurse, compress, level)
+{
+ var s = "";
+ var pfx = "";
+
+ if (typeof recurse == "undefined")
+ recurse = 0;
+ if (typeof level == "undefined")
+ level = 0;
+ if (typeof compress == "undefined")
+ compress = true;
+
+ for (var i = 0; i < level; i++)
+ pfx += (compress) ? "| " : "| ";
+
+ var tee = (compress) ? "+ " : "+- ";
+
+ for (i in o)
+ {
+ var t, ex;
+
+ try
+ {
+ t = typeof o[i];
+ }
+ catch (ex)
+ {
+ t = "ERROR";
+ }
+
+ switch (t)
+ {
+ case "function":
+ var sfunc = String(o[i]).split("\n");
+ if (sfunc[2] == " [native code]")
+ sfunc = "[native code]";
+ else
+ if (sfunc.length == 1)
+ sfunc = String(sfunc);
+ else
+ sfunc = sfunc.length + " lines";
+ s += pfx + tee + i + " (function) " + sfunc + "\n";
+ break;
+
+ case "object":
+ s += pfx + tee + i + " (object)";
+ if (o[i] == null)
+ {
+ s += " null\n";
+ break;
+ }
+
+ s += "\n";
+
+ if (!compress)
+ s += pfx + "|\n";
+ if ((i != "parent") && (recurse))
+ s += dumpObjectTree (o[i], recurse - 1,
+ compress, level + 1);
+ break;
+
+ case "string":
+ if (o[i].length > 200)
+ s += pfx + tee + i + " (" + t + ") " +
+ o[i].length + " chars\n";
+ else
+ s += pfx + tee + i + " (" + t + ") '" + o[i] + "'\n";
+ break;
+
+ case "ERROR":
+ s += pfx + tee + i + " (" + t + ") ?\n";
+ break;
+
+ default:
+ s += pfx + tee + i + " (" + t + ") " + o[i] + "\n";
+
+ }
+
+ if (!compress)
+ s += pfx + "|\n";
+
+ }
+
+ s += pfx + "*\n";
+
+ return s;
+
+}
+
+function ecmaEscape(str)
+{
+ function replaceNonPrintables(ch)
+ {
+ var rv = ch.charCodeAt().toString(16);
+ if (rv.length == 1)
+ rv = "0" + rv;
+ else if (rv.length == 3)
+ rv = "u0" + rv;
+ else if (rv.length == 4)
+ rv = "u" + rv;
+
+ return "%" + rv;
+ };
+
+ // Replace any character that is not in the 69 character set
+ // [ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@*_+-./]
+ // with an escape sequence. Two digit sequences in the form %XX are used
+ // for characters whose codepoint is less than 255, %uXXXX for all others.
+ // See section B.2.1 of ECMA-262 rev3 for more information.
+ return str.replace(/[^A-Za-z0-9@*_+.\-\/]/g, replaceNonPrintables);
+}
+
+function ecmaUnescape(str)
+{
+ function replaceEscapes(seq)
+ {
+ var ary = seq.match(/([\da-f]{1,2})(.*)|u([\da-f]{1,4})/i);
+ if (!ary)
+ return "<ERROR>";
+
+ var rv;
+ if (ary[1])
+ {
+ // two digit escape, possibly with cruft after
+ rv = String.fromCharCode(parseInt(ary[1], 16)) + ary[2];
+ }
+ else
+ {
+ // four digits, no cruft
+ rv = String.fromCharCode(parseInt(ary[3], 16));
+ }
+
+ return rv;
+ };
+
+ // Replace the escape sequences %X, %XX, %uX, %uXX, %uXXX, and %uXXXX with
+ // the characters they represent, where X is a hexadecimal digit.
+ // See section B.2.2 of ECMA-262 rev3 for more information.
+ return str.replace(/%u?([\da-f]{1,4})/ig, replaceEscapes);
+}
+
+function encodeForXMLAttribute(value) {
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;")
+ .replace(/>/g, "&gt;").replace(/"/g, "&quot;")
+ .replace(/'/g, "&apos;");
+}
+
+function replaceVars(str, vars)
+{
+ // replace "string $with a $variable", with
+ // "string " + vars["with"] + " with a " + vars["variable"]
+
+ function doReplace(symbol)
+ {
+ var name = symbol.substr(1);
+ if (name in vars)
+ return vars[name];
+
+ return "$" + name;
+ };
+
+ return str.replace(/(\$\w[\w\d\-]+)/g, doReplace);
+}
+
+function formatException(ex)
+{
+ if (isinstance(ex, Error))
+ {
+ return getMsg(MSG_FMT_JSEXCEPTION, [ex.name, ex.message, ex.fileName,
+ ex.lineNumber]);
+ }
+ if ((typeof ex == "object") && ("filename" in ex))
+ {
+ return getMsg(MSG_FMT_JSEXCEPTION, [ex.name, ex.message, ex.filename,
+ ex.lineNumber]);
+ }
+
+ return String(ex);
+}
+
+/*
+ * Clones an existing object (Only the enumerable properties
+ * of course.) use as a function..
+ * var c = Clone (obj);
+ * or a constructor...
+ * var c = new Clone (obj);
+ */
+function Clone (obj)
+{
+ var robj = new Object();
+
+ if ("__proto__" in obj)
+ {
+ // Special clone for Spidermonkey.
+ for (var p in obj)
+ {
+ if (obj.hasOwnProperty(p))
+ robj[p] = obj[p];
+ }
+ robj.__proto__ = obj.__proto__;
+ }
+ else
+ {
+ for (var p in obj)
+ robj[p] = obj[p];
+ }
+
+ return robj;
+
+}
+
+function Copy(source, dest, overwrite)
+{
+ if (!dest)
+ dest = new Object();
+
+ for (var p in source)
+ {
+ if (overwrite || !(p in dest))
+ dest[p] = source[p];
+ }
+
+ return dest;
+}
+
+/*
+ * matches a real object against one or more pattern objects.
+ * if you pass an array of pattern objects, |negate| controls whether to check
+ * if the object matches ANY of the patterns, or NONE of the patterns.
+ */
+function matchObject (o, pattern, negate)
+{
+ negate = Boolean(negate);
+
+ function _match (o, pattern)
+ {
+ if (isinstance(pattern, Function))
+ return pattern(o);
+
+ for (var p in pattern)
+ {
+ var val;
+ /* nice to have, but slow as molases, allows you to match
+ * properties of objects with obj$prop: "foo" syntax */
+ /*
+ if (p[0] == "$")
+ val = eval ("o." +
+ p.substr(1,p.length).replace (/\$/g, "."));
+ else
+ */
+ val = o[p];
+
+ if (isinstance(pattern[p], Function))
+ {
+ if (!pattern[p](val))
+ return false;
+ }
+ else
+ {
+ var ary = (new String(val)).match(pattern[p]);
+ if (ary == null)
+ return false;
+ else
+ o.matchresult = ary;
+ }
+ }
+
+ return true;
+
+ }
+
+ if (!isinstance(pattern, Array))
+ return Boolean (negate ^ _match(o, pattern));
+
+ for (var i in pattern)
+ if (_match (o, pattern[i]))
+ return !negate;
+
+ return negate;
+
+}
+
+function equalsObject(o1, o2)
+{
+ for (var p in o1)
+ {
+ if (!(p in o2) || (o1[p] != o2[p]))
+ return false;
+ }
+ for (p in o2)
+ {
+ // If the property did exist in o1, the previous loop tested it:
+ if (!(p in o1))
+ return false;
+ }
+ return true;
+}
+
+function utils_lcfn(text)
+{
+ return text.toLowerCase();
+}
+
+function matchEntry (partialName, list, lcFn)
+{
+
+ if ((typeof partialName == "undefined") ||
+ (String(partialName) == ""))
+ {
+ var ary = new Array();
+ for (var i in list)
+ ary.push(i);
+ return ary;
+ }
+
+ if (typeof lcFn != "function")
+ lcFn = utils_lcfn;
+
+ ary = new Array();
+
+ for (i in list)
+ {
+ if (lcFn(list[i]).indexOf(lcFn(partialName)) == 0)
+ ary.push(i);
+ }
+
+ return ary;
+
+}
+
+function encodeChar(ch)
+{
+ return "%" + ch.charCodeAt(0).toString(16);
+}
+
+function escapeFileName(fileName)
+{
+ // Escape / \ : * ? " < > | so they don't cause trouble.
+ return fileName.replace(/[\/\\\:\*\?"<>\|]/g, encodeChar);
+}
+
+function getCommonPfx (list, lcFn)
+{
+ var pfx = list[0];
+ var l = list.length;
+
+ if (typeof lcFn != "function")
+ lcFn = utils_lcfn;
+
+ for (var i = 0; i < l; i++)
+ {
+ for (var c = 0; c < pfx.length; ++c)
+ {
+ if (c >= list[i].length)
+ {
+ pfx = pfx.substr(0, c);
+ break;
+ }
+ else
+ {
+ if (lcFn(pfx[c]) != lcFn(list[i][c]))
+ pfx = pfx.substr(0, c);
+ }
+ }
+ }
+
+ return pfx;
+
+}
+
+function openTopWin (url)
+{
+ return openDialog (getBrowserURL(), "_blank", "chrome,all,dialog=no", url);
+}
+
+function getWindowByType (windowType)
+{
+ const MEDIATOR_CONTRACTID =
+ "@mozilla.org/appshell/window-mediator;1";
+ const nsIWindowMediator = Components.interfaces.nsIWindowMediator;
+
+ var windowManager =
+ Components.classes[MEDIATOR_CONTRACTID].getService(nsIWindowMediator);
+
+ return windowManager.getMostRecentWindow(windowType);
+}
+
+function toOpenWindowByType(inType, url, features)
+{
+ var topWindow = getWindowByType(inType);
+
+ if (typeof features == "undefined")
+ features = "chrome,extrachrome,menubar,resizable," +
+ "scrollbars,status,toolbar";
+
+ if (topWindow)
+ topWindow.focus();
+ else
+ window.open(url, "_blank", features);
+}
+
+function renameProperty (obj, oldname, newname)
+{
+
+ if (oldname == newname)
+ return;
+
+ obj[newname] = obj[oldname];
+ delete obj[oldname];
+
+}
+
+function newObject(contractID, iface)
+{
+ var rv;
+ var cls = Components.classes[contractID];
+
+ if (!cls)
+ return null;
+
+ switch (typeof iface)
+ {
+ case "undefined":
+ rv = cls.createInstance();
+ break;
+
+ case "string":
+ rv = cls.createInstance(Components.interfaces[iface]);
+ break;
+
+ case "object":
+ rv = cls.createInstance(iface);
+ break;
+
+ default:
+ rv = null;
+ break;
+ }
+
+ return rv;
+
+}
+
+function getService(contractID, iface)
+{
+ var rv;
+ var cls = Components.classes[contractID];
+
+ if (!cls)
+ return null;
+
+ switch (typeof iface)
+ {
+ case "undefined":
+ rv = cls.getService();
+ break;
+
+ case "string":
+ rv = cls.getService(Components.interfaces[iface]);
+ break;
+
+ case "object":
+ rv = cls.getService(iface);
+ break;
+
+ default:
+ rv = null;
+ break;
+ }
+
+ return rv;
+
+}
+
+function getNSSErrorClass(errorCode)
+{
+ var nssErrSvc = getService("@mozilla.org/nss_errors_service;1", "nsINSSErrorsService");
+
+ try
+ {
+ return nssErrSvc.getErrorClass(errorCode);
+ }
+ catch
+ {
+ return 0;
+ }
+}
+
+function getContentWindow(frame)
+{
+ try
+ {
+ if (!frame || !("contentWindow" in frame))
+ return false;
+
+ // The "in" operator does not detect wrappedJSObject, so don't bother.
+ if (frame.contentWindow.wrappedJSObject)
+ return frame.contentWindow.wrappedJSObject;
+ return frame.contentWindow;
+ }
+ catch (ex)
+ {
+ // throws exception is contentWindow is gone
+ return null;
+ }
+}
+
+function getContentDocument(frame)
+{
+ try
+ {
+ if (!frame || !("contentDocument" in frame))
+ return false;
+
+ // The "in" operator does not detect wrappedJSObject, so don't bother.
+ if (frame.contentDocument.wrappedJSObject)
+ return frame.contentDocument.wrappedJSObject;
+ return frame.contentDocument;
+ }
+ catch (ex)
+ {
+ // throws exception is contentDocument is gone
+ return null;
+ }
+}
+
+function getPriv (priv)
+{
+ var rv = true;
+
+ try
+ {
+ netscape.security.PrivilegeManager.enablePrivilege(priv);
+ }
+ catch (e)
+ {
+ dd ("getPriv: unable to get privlege '" + priv + "': " + e);
+ rv = false;
+ }
+
+ return rv;
+
+}
+
+function len(o)
+{
+ var l = 0;
+
+ for (var p in o)
+ ++l;
+
+ return l;
+}
+
+function keys (o)
+{
+ var rv = new Array();
+
+ for (var p in o)
+ rv.push(p);
+
+ return rv;
+
+}
+
+function stringTrim (s)
+{
+ if (!s)
+ return "";
+ s = s.replace (/^\s+/, "");
+ return s.replace (/\s+$/, "");
+
+}
+
+/* the offset should be in seconds, it will be rounded to 2 decimal places */
+function formatDateOffset (offset, format)
+{
+ var seconds = roundTo(offset % 60, 2);
+ var minutes = Math.floor(offset / 60);
+ var hours = Math.floor(minutes / 60);
+ minutes = minutes % 60;
+ var days = Math.floor(hours / 24);
+ hours = hours % 24;
+
+ if (!format)
+ {
+ var ary = new Array();
+
+ if (days == 1)
+ ary.push(MSG_DAY);
+ else if (days > 0)
+ ary.push(getMsg(MSG_DAYS, days));
+
+ if (hours == 1)
+ ary.push(MSG_HOUR);
+ else if (hours > 0)
+ ary.push(getMsg(MSG_HOURS, hours));
+
+ if (minutes == 1)
+ ary.push(MSG_MINUTE);
+ else if (minutes > 0)
+ ary.push(getMsg(MSG_MINUTES, minutes));
+
+ if (seconds == 1)
+ ary.push(MSG_SECOND);
+ else if (seconds > 0 || offset == 0)
+ ary.push(getMsg(MSG_SECONDS, seconds));
+
+ format = ary.join(", ");
+ }
+ else
+ {
+ format = format.replace ("%d", days);
+ format = format.replace ("%h", hours);
+ format = format.replace ("%m", minutes);
+ format = format.replace ("%s", seconds);
+ }
+
+ return format;
+}
+
+function arrayHasElementAt(ary, i)
+{
+ return typeof ary[i] != "undefined";
+}
+
+function arrayContains (ary, elem)
+{
+ return (arrayIndexOf (ary, elem) != -1);
+}
+
+function arrayIndexOf (ary, elem)
+{
+ for (var i in ary)
+ if (ary[i] == elem)
+ return i;
+
+ return -1;
+}
+
+function arrayInsertAt (ary, i, o)
+{
+ ary.splice (i, 0, o);
+}
+
+function arrayRemoveAt (ary, i)
+{
+ ary.splice (i, 1);
+}
+
+function objectContains(o, p)
+{
+ return Object.hasOwnProperty.call(o, p);
+}
+
+/* length should be an even number >= 6 */
+function abbreviateWord (str, length)
+{
+ if (str.length <= length || length < 6)
+ return str;
+
+ var left = str.substr (0, (length / 2) - 1);
+ var right = str.substr (str.length - (length / 2) + 1);
+
+ return left + "..." + right;
+}
+
+/*
+ * Inserts the string |hyphen| into string |str| every |pos| characters.
+ * If there are any wordbreaking characters in |str| within -/+5 characters of
+ * of a |pos| then the hyphen is inserted there instead, in order to produce a
+ * "cleaner" break.
+ */
+function hyphenateWord (str, pos, hyphen)
+{
+ if (str.length <= pos)
+ return str;
+ if (typeof hyphen == "undefined")
+ hyphen = " ";
+
+ /* search for a nice place to break the word, fuzzfactor of +/-5, centered
+ * around |pos| */
+ var splitPos =
+ str.substring(pos - 5, pos + 5).search(/[^A-Za-z0-9]/);
+
+ splitPos = (splitPos != -1) ? pos - 4 + splitPos : pos;
+ var left = str.substr (0, splitPos);
+ var right = hyphenateWord(str.substr (splitPos), pos, hyphen);
+
+ return left + hyphen + right;
+}
+
+/*
+ * Like hyphenateWord, except individual chunks of the word are returned as
+ * elements of an array.
+ */
+function splitLongWord (str, pos)
+{
+ if (str.length <= pos)
+ return [str];
+
+ var ary = new Array();
+ var right = str;
+
+ while (right.length > pos)
+ {
+ /* search for a nice place to break the word, fuzzfactor of +/-5,
+ * centered around |pos| */
+ var splitPos =
+ right.substring(pos - 5, pos + 5).search(/[^A-Za-z0-9]/);
+
+ splitPos = (splitPos != -1) ? pos - 4 + splitPos : pos;
+ ary.push(right.substr (0, splitPos));
+ right = right.substr (splitPos);
+ }
+
+ ary.push (right);
+
+ return ary;
+}
+
+function getRandomElement (ary)
+{
+
+ return ary[Math.floor(Math.random() * ary.length)];
+
+}
+
+function roundTo (num, prec)
+{
+
+ return Math.round(num * Math.pow (10, prec)) / Math.pow (10, prec);
+
+}
+
+function randomRange (min, max)
+{
+
+ if (typeof min == "undefined")
+ min = 0;
+
+ if (typeof max == "undefined")
+ max = 1;
+
+ return Math.floor(Math.random() * (max - min + 1)) + min;
+
+}
+
+// Creates a random string of |len| characters from a-z, A-Z, 0-9.
+function randomString(len) {
+ var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+ var rv = "";
+
+ for (var i = 0; i < len; i++)
+ rv += chars.substr(Math.floor(Math.random() * chars.length), 1);
+
+ return rv;
+}
+
+function getStackTrace ()
+{
+ var frame = Components.stack.caller;
+ var str = "<top>";
+
+ while (frame)
+ {
+ var name = frame.name ? frame.name : "[anonymous]";
+ str += "\n" + name + "@" + frame.lineNumber;
+ frame = frame.caller;
+ }
+
+ return str;
+
+}
+
+function getInterfaces (cls)
+{
+ var rv = new Object();
+ var e;
+
+ for (var i in Components.interfaces)
+ {
+ try
+ {
+ var ifc = Components.interfaces[i];
+ cls.QueryInterface(ifc);
+ rv[i] = ifc;
+ }
+ catch (e)
+ {
+ /* nada */
+ }
+ }
+
+ return rv;
+
+}
+
+/**
+ * Calls a named function for each element in an array, sending
+ * the same parameter each call.
+ *
+ * @param ary an array of objects
+ * @param func_name string name of function to call.
+ * @param data data object to pass to each object.
+ */
+function mapObjFunc(ary, func_name, data)
+{
+ /*
+ * WARNING: Caller assumes resonsibility to verify ary
+ * and func_name
+ */
+
+ for (var i in ary)
+ ary[i][func_name](data);
+}
+
+/**
+ * Passes each element of an array to a given function object.
+ *
+ * @param func a function object.
+ * @param ary an array of values.
+ */
+function map(func, ary) {
+
+ /*
+ * WARNING: Caller assumnes responsibility to verify
+ * func and ary.
+ */
+
+ for (var i in ary)
+ func(ary[i]);
+
+}
+
+function getSpecialDirectory(name)
+{
+ if (!("directoryService" in utils))
+ {
+ const DS_CTR = "@mozilla.org/file/directory_service;1";
+ const nsIProperties = Components.interfaces.nsIProperties;
+
+ utils.directoryService =
+ Components.classes[DS_CTR].getService(nsIProperties);
+ }
+
+ return utils.directoryService.get(name, Components.interfaces.nsIFile);
+}
+
+function getFileFromURLSpec(url)
+{
+ var handler = Services.io.getProtocolHandler("file")
+ .QueryInterface(Ci.nsIFileProtocolHandler);
+ return handler.getFileFromURLSpec(url);
+}
+
+function getURLSpecFromFile(file)
+{
+ if (!file)
+ return null;
+
+ if (typeof file == "string")
+ {
+ let fileObj = Cc["@mozilla.org/file/local;1"]
+ .createInstance(Ci.nsIFile);
+ fileObj.initWithPath(file);
+ file = fileObj;
+ }
+
+ var fileHandler = Services.io.getProtocolHandler("file")
+ .QueryInterface(Ci.nsIFileProtocolHandler);
+ return fileHandler.getURLSpecFromFile(file);
+}
+
+function alert(msg, parent, title)
+{
+ var PROMPT_CTRID = "@mozilla.org/embedcomp/prompt-service;1";
+ var nsIPromptService = Components.interfaces.nsIPromptService;
+ var ps = Components.classes[PROMPT_CTRID].getService(nsIPromptService);
+ if (!parent)
+ parent = window;
+ if (!title)
+ title = MSG_ALERT;
+ ps.alert (parent, title, msg);
+}
+
+function confirm(msg, parent, title)
+{
+ var PROMPT_CTRID = "@mozilla.org/embedcomp/prompt-service;1";
+ var nsIPromptService = Components.interfaces.nsIPromptService;
+ var ps = Components.classes[PROMPT_CTRID].getService(nsIPromptService);
+ if (!parent)
+ parent = window;
+ if (!title)
+ title = MSG_CONFIRM;
+ return ps.confirm (parent, title, msg);
+}
+
+function confirmEx(msg, buttons, defaultButton, checkText,
+ checkVal, parent, title)
+{
+ /* Note that on versions before Mozilla 0.9, using 3 buttons,
+ * the revert or dontsave button, or custom button titles will NOT work.
+ *
+ * The buttons should be listed in the 'accept', 'cancel' and 'extra' order,
+ * and the exact button order is host app- and platform-dependant.
+ * For example, on Windows this is usually [button 1] [button 3] [button 2],
+ * and on Linux [button 3] [button 2] [button 1].
+ */
+ var PROMPT_CTRID = "@mozilla.org/embedcomp/prompt-service;1";
+ var nsIPromptService = Components.interfaces.nsIPromptService;
+ var ps = Components.classes[PROMPT_CTRID].getService(nsIPromptService);
+
+ var buttonConstants = {
+ ok: ps.BUTTON_TITLE_OK,
+ cancel: ps.BUTTON_TITLE_CANCEL,
+ yes: ps.BUTTON_TITLE_YES,
+ no: ps.BUTTON_TITLE_NO,
+ save: ps.BUTTON_TITLE_SAVE,
+ revert: ps.BUTTON_TITLE_REVERT,
+ dontsave: ps.BUTTON_TITLE_DONT_SAVE
+ };
+ var buttonFlags = 0;
+ var buttonText = [null, null, null];
+
+ if (!isinstance(buttons, Array))
+ throw "buttons parameter must be an Array";
+ if ((buttons.length < 1) || (buttons.length > 3))
+ throw "the buttons array must have 1, 2 or 3 elements";
+
+ for (var i = 0; i < buttons.length; i++)
+ {
+ var buttonFlag = ps.BUTTON_TITLE_IS_STRING;
+ if ((buttons[i][0] == "!") && (buttons[i].substr(1) in buttonConstants))
+ buttonFlag = buttonConstants[buttons[i].substr(1)];
+ else
+ buttonText[i] = buttons[i];
+
+ buttonFlags += ps["BUTTON_POS_" + i] * buttonFlag;
+ }
+
+ // ignore anything but a proper number
+ var defaultIsNumber = (typeof defaultButton == "number");
+ if (defaultIsNumber && arrayHasElementAt(buttons, defaultButton))
+ buttonFlags += ps["BUTTON_POS_" + defaultButton + "_DEFAULT"];
+
+ if (!parent)
+ parent = window;
+ if (!title)
+ title = MSG_CONFIRM;
+ if (!checkVal)
+ checkVal = new Object();
+
+ var rv = ps.confirmEx(parent, title, msg, buttonFlags, buttonText[0],
+ buttonText[1], buttonText[2], checkText, checkVal);
+ return rv;
+}
+
+function prompt(msg, initial, parent, title)
+{
+ var PROMPT_CTRID = "@mozilla.org/embedcomp/prompt-service;1";
+ var nsIPromptService = Components.interfaces.nsIPromptService;
+ var ps = Components.classes[PROMPT_CTRID].getService(nsIPromptService);
+ if (!parent)
+ parent = window;
+ if (!title)
+ title = MSG_PROMPT;
+ var rv = { value: initial };
+
+ if (!ps.prompt (parent, title, msg, rv, null, {value: null}))
+ return null;
+
+ return rv.value;
+}
+
+function promptPassword(msg, initial, parent, title)
+{
+ var PROMPT_CTRID = "@mozilla.org/embedcomp/prompt-service;1";
+ var nsIPromptService = Components.interfaces.nsIPromptService;
+ var ps = Components.classes[PROMPT_CTRID].getService(nsIPromptService);
+ if (!parent)
+ parent = window;
+ if (!title)
+ title = MSG_PROMPT;
+ var rv = { value: initial };
+
+ if (!ps.promptPassword (parent, title, msg, rv, null, {value: null}))
+ return null;
+
+ return rv.value;
+}
+
+function viewCert(cert, parent)
+{
+ var cd = getService("@mozilla.org/nsCertificateDialogs;1",
+ "nsICertificateDialogs");
+ if (!parent)
+ parent = window;
+ cd.viewCert(parent, cert);
+}
+
+function addOrUpdateLogin(url, type, username, password)
+{
+ username = username.toLowerCase();
+ var newinfo = newObject("@mozilla.org/login-manager/loginInfo;1",
+ "nsILoginInfo");
+ newinfo.init(url, null, type, username, password, "", "");
+ var oldinfo = getLogin(url, type, username);
+
+ if (oldinfo) {
+ Services.logins.modifyLogin(oldinfo, newinfo);
+ return true; //updated
+ }
+
+ Services.logins.addLogin(newinfo);
+ return false; //added
+}
+
+function getLogin(url, realm, username)
+{
+ username = username.toLowerCase();
+
+ let logins = Services.logins.findLogins({}, url, null, realm);
+ for (let login of logins) {
+ if (login.username == username) {
+ return login;
+ }
+ }
+
+ return null;
+}
+
+function getHostmaskParts(hostmask)
+{
+ var rv;
+ // A bit cheeky this, we try the matches here, and then branch
+ // according to the ones we like.
+ var ary1 = hostmask.match(/([^ ]*)!([^ ]*)@(.*)/);
+ var ary2 = hostmask.match(/([^ ]*)@(.*)/);
+ var ary3 = hostmask.match(/([^ ]*)!(.*)/);
+ if (ary1)
+ rv = { nick: ary1[1], user: ary1[2], host: ary1[3] };
+ else if (ary2)
+ rv = { nick: "*", user: ary2[1], host: ary2[2] };
+ else if (ary3)
+ rv = { nick: ary3[1], user: ary3[2], host: "*" };
+ else
+ rv = { nick: hostmask, user: "*", host: "*" };
+ // Make sure we got something for all fields.
+ if (!rv.nick)
+ rv.nick = "*";
+ if (!rv.user)
+ rv.user = "*";
+ if (!rv.host)
+ rv.host = "*";
+ // And re-construct the 'parsed' hostmask.
+ rv.mask = rv.nick + "!" + rv.user + "@" + rv.host;
+ return rv;
+}
+
+function makeMaskRegExp(text)
+{
+ function escapeChars(c)
+ {
+ if (c == "*")
+ return ".*";
+ if (c == "?")
+ return ".";
+ return "\\" + c;
+ }
+ // Anything that's not alpha-numeric gets escaped.
+ // "*" and "?" are 'escaped' to ".*" and ".".
+ // Optimisation; * translates as 'match all'.
+ return new RegExp("^" + text.replace(/[^\w\d]/g, escapeChars) + "$", "i");
+}
+
+function hostmaskMatches(user, mask)
+{
+ // Need to match .nick, .user, and .host.
+ if (!("nickRE" in mask))
+ {
+ // We cache all the regexp objects, but use null if the term is
+ // just "*", so we can skip having the object *and* the .match
+ // later on.
+ if (mask.nick == "*")
+ mask.nickRE = null;
+ else
+ mask.nickRE = makeMaskRegExp(mask.nick);
+
+ if (mask.user == "*")
+ mask.userRE = null;
+ else
+ mask.userRE = makeMaskRegExp(mask.user);
+
+ if (mask.host == "*")
+ mask.hostRE = null;
+ else
+ mask.hostRE = makeMaskRegExp(mask.host);
+ }
+
+ var lowerNick;
+ if (user.TYPE == "IRCChanUser")
+ lowerNick = user.parent.parent.toLowerCase(user.unicodeName);
+ else
+ lowerNick = user.parent.toLowerCase(user.unicodeName);
+
+ if ((!mask.nickRE || lowerNick.match(mask.nickRE)) &&
+ (!mask.userRE || user.name.match(mask.userRE)) &&
+ (!mask.hostRE || user.host.match(mask.hostRE)))
+ return true;
+ return false;
+}
+
+function isinstance(inst, base)
+{
+ /* Returns |true| if |inst| was constructed by |base|. Not 100% accurate,
+ * but plenty good enough for us. This is to work around the fix for bug
+ * 254067 which makes instanceof fail if the two sides are 'from'
+ * different windows (something we don't care about).
+ */
+ return (inst && base &&
+ ((inst instanceof base) ||
+ (inst.constructor && (inst.constructor.name == base.name))));
+}
+
+function isDefaultPrevented(ev)
+{
+ if ("defaultPrevented" in ev)
+ return ev.defaultPrevented;
+ return ev.getPreventDefault();
+}
+
+function scaleNumberBy1024(number)
+{
+ var scale = 0;
+ while ((number >= 1000) && (scale < 6))
+ {
+ scale++;
+ number /= 1024;
+ }
+
+ return [scale, number];
+}
+
+function getSISize(size)
+{
+ var data = scaleNumberBy1024(size);
+
+ if (data[1] < 10)
+ data[1] = data[1].toFixed(2);
+ else if (data[1] < 100)
+ data[1] = data[1].toFixed(1);
+ else
+ data[1] = data[1].toFixed(0);
+
+ return getMsg(MSG_SI_SIZE, [data[1], getMsg("msg.si.size." + data[0])]);
+}
+
+function getSISpeed(speed)
+{
+ var data = scaleNumberBy1024(speed);
+
+ if (data[1] < 10)
+ data[1] = data[1].toFixed(2);
+ else if (data[1] < 100)
+ data[1] = data[1].toFixed(1);
+ else
+ data[1] = data[1].toFixed(0);
+
+ return getMsg(MSG_SI_SPEED, [data[1], getMsg("msg.si.speed." + data[0])]);
+}
+
+// Returns -1 if version 1 is newer, +1 if version 2 is newer, and 0 for same.
+function compareVersions(ver1, ver2)
+{
+ var ver1parts = ver1.split(".");
+ var ver2parts = ver2.split(".");
+
+ while ((ver1parts.length > 0) && (ver2parts.length > 0))
+ {
+ if (ver1parts[0] < ver2parts[0])
+ return 1;
+ if (ver1parts[0] > ver2parts[0])
+ return -1;
+ ver1parts.shift();
+ ver2parts.shift();
+ }
+ if (ver1parts.length > 0)
+ return -1;
+ if (ver2parts.length > 0)
+ return 1;
+ return 0;
+}
+
+// Zero-pad Numbers (or pad with something else if you wish)
+function padNumber(num, digits, pad)
+{
+ pad = pad || "0";
+ var rv = num.toString();
+ while (rv.length < digits)
+ rv = pad + rv;
+ return rv;
+}
+
+const timestr = {
+ A: { method: "getDay" },
+ a: { method: "getDay" },
+ B: { method: "getMonth" },
+ b: { method: "getMonth" },
+ c: { replace: null },
+ D: { replace: "%m/%d/%y" },
+ d: { method: "getDate", pad: 2 },
+ e: { method: "getDate", pad: 2, padwith: " " },
+ F: { replace: "%Y-%m-%d" },
+ h: { replace: "%b" },
+ H: { method: "getHours", pad: 2 },
+ k: { method: "getHours", pad: 2, padwith: " " },
+ M: { method: "getMinutes", pad: 2 },
+ p: { AM: null, PM: null },
+ P: { AM: null, PM: null },
+ r: { replace: null },
+ R: { replace: "%H:%M" },
+ S: { method: "getSeconds", pad: 2 },
+ T: { replace: "%H:%M:%S" },
+ w: { method: "getDay" },
+ x: { replace: null },
+ X: { replace: null },
+ Y: { method: "getFullYear" },
+ initialized: false
+}
+
+function strftime(format, time)
+{
+ /* Javascript implementation of standard C strftime */
+
+ if (!timestr.initialized)
+ {
+ timestr.A.values = getMsg("datetime.day.long").split("^");
+ timestr.a.values = getMsg("datetime.day.short").split("^");
+ timestr.B.values = getMsg("datetime.month.long").split("^");
+ timestr.b.values = getMsg("datetime.month.short").split("^");
+ // Just make sure the locale isn't playing silly with us.
+ ASSERT(timestr.A.values.length == 7, "datetime.day.long bad!");
+ ASSERT(timestr.a.values.length == 7, "datetime.day.short bad!");
+ ASSERT(timestr.B.values.length == 12, "datetime.month.long bad!");
+ ASSERT(timestr.b.values.length == 12, "datetime.month.short bad!");
+
+ timestr.p.AM = getMsg("datetime.uam");
+ timestr.p.PM = getMsg("datetime.upm");
+ timestr.P.AM = getMsg("datetime.lam");
+ timestr.P.PM = getMsg("datetime.lpm");
+
+ timestr.c.replace = getMsg("datetime.presets.lc");
+ timestr.r.replace = getMsg("datetime.presets.lr");
+ timestr.x.replace = getMsg("datetime.presets.lx");
+ timestr.X.replace = getMsg("datetime.presets.ux");
+
+ timestr.initialized = true;
+ }
+
+
+ function getDayOfYear(dateobj)
+ {
+ var yearobj = new Date(dateobj.getFullYear(), 0, 1, 0, 0, 0, 0);
+ return Math.floor((dateobj - yearobj) / 86400000) + 1;
+ };
+
+ time = time || new Date();
+ if (!isinstance(time, Date))
+ throw "Expected date object";
+
+ var ary;
+ while ((ary = format.match(/(^|[^%])%(\w)/)))
+ {
+ var start = ary[1] ? (ary.index + 1) : ary.index;
+ var rpl = "";
+ if (ary[2] in timestr)
+ {
+ var tbranch = timestr[ary[2]];
+ if (("method" in tbranch) && ("values" in tbranch))
+ rpl = tbranch.values[time[tbranch.method]()];
+ else if ("method" in tbranch)
+ rpl = time[tbranch.method]().toString();
+ else if ("replace" in tbranch)
+ rpl = tbranch.replace;
+
+ if ("pad" in tbranch)
+ {
+ var padwith = ("padwith" in tbranch) ? tbranch.padwith : "0";
+ rpl = padNumber(rpl, tbranch.pad, padwith);
+ }
+ }
+ if (!rpl)
+ {
+ switch (ary[2])
+ {
+ case "C":
+ var century = Math.floor(time.getFullYear() / 100);
+ rpl = padNumber(century, 2);
+ break;
+ case "I":
+ case "l":
+ var hour = (time.getHours() + 11) % 12 + 1;
+ var padwith = (ary[2] == "I") ? "0" : " ";
+ rpl = padNumber(hour, 2, padwith);
+ break;
+ case "j":
+ rpl = padNumber(getDayOfYear(time), 3);
+ break;
+ case "m":
+ rpl = padNumber(time.getMonth() + 1, 2);
+ break;
+ case "p":
+ case "P":
+ var bit = (time.getHours() < 12) ? "AM" : "PM";
+ rpl = timestr[ary[2]][bit];
+ break;
+ case "s":
+ rpl = Math.round(time.getTime() / 1000);
+ break;
+ case "u":
+ rpl = (time.getDay() + 6) % 7 + 1;
+ break;
+ case "y":
+ rpl = time.getFullYear().toString().substr(2);
+ break;
+ case "z":
+ var mins = time.getTimezoneOffset();
+ rpl = (mins > 0) ? "-" : "+";
+ mins = Math.abs(mins);
+ var hours = Math.floor(mins / 60);
+ rpl += padNumber(hours, 2) + padNumber(mins - (hours * 60), 2);
+ break;
+ }
+ }
+ if (!rpl)
+ rpl = "%%" + ary[2];
+ format = format.substr(0, start) + rpl + format.substr(start + 2);
+ }
+ return format.replace(/%%/, "%");
+}
+
+// This used to be strres.js, copied here to help remove that...
+var strBundleService = null;
+function srGetStrBundle(path)
+{
+ const STRBSCID = "@mozilla.org/intl/stringbundle;1";
+ const STRBSIF = "nsIStringBundleService";
+ var strBundle = null;
+ if (!strBundleService)
+ {
+ try
+ {
+ strBundleService = getService(STRBSCID, STRBSIF);
+ }
+ catch (ex)
+ {
+ dump("\n--** strBundleService failed: " + ex + "\n");
+ return null;
+ }
+ }
+
+ strBundle = strBundleService.createBundle(path);
+ if (!strBundle)
+ dump("\n--** strBundle createInstance failed **--\n");
+
+ return strBundle;
+}
+
+// No-op window.getAttention if it's not found, this is for in-a-tab mode.
+if (typeof getAttention == "undefined")
+ getAttention = function() {};