diff options
Diffstat (limited to 'comm/suite/chatzilla/js/lib/utils.js')
-rw-r--r-- | comm/suite/chatzilla/js/lib/utils.js | 1490 |
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, "&").replace(/</g, "<") + .replace(/>/g, ">").replace(/"/g, """) + .replace(/'/g, "'"); +} + +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() {}; |