/* -*- 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("\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 ""; 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, "'"); } 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 = ""; 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() {};