PK \/1EW!content/chatzilla/ff/contents.rdf chrome://chatzilla/content/ff/overlay.xul PK -]#2_GG content/chatzilla/ff/overlay.xul
PK ha7#l8l8"content/chatzilla/output-window.js/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is ChatZilla. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Robert Ginda, , original author * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ var initialized = false; var view; var client; var mainWindow; var clickHandler; var dd; var getMsg; var getObjectDetails; var header = null; var headers = { IRCClient: { prefix: "cli-", fields: ["container", "netcount", "version-container", "version", "connectcount"], update: updateClient }, IRCNetwork: { prefix: "net-", fields: ["container", "url-anchor", "status", "lag"], update: updateNetwork }, IRCChannel: { prefix: "ch-", fields: ["container", "url-anchor", "modestr", "usercount", "topicnodes", "topicinput"], update: updateChannel }, IRCUser: { prefix: "usr-", fields: ["container", "url-anchor", "serverstr", "title", "descnodes"], update: updateUser }, IRCDCCChat: { prefix: "dcc-chat-", fields: ["container", "remotestr", "title"], update: updateDCCChat }, IRCDCCFileTransfer: { prefix: "dcc-file-", fields: ["container", "file", "progress", "progressbar"], update: updateDCCFile } }; var initOutputWindow = stock_initOutputWindow; function stock_initOutputWindow(newClient, newView, newClickHandler) { function initHeader() { /* it's better if we wait a half a second before poking at these * dom nodes. */ setHeaderState(view.prefs["displayHeader"]); updateHeader(); var div = document.getElementById("messages-outer"); div.removeAttribute("hidden"); window.scrollTo(0, window.document.height); }; client = newClient; view = newView; clickHandler = newClickHandler; mainWindow = client.mainWindow; client.messageManager.importBundle(client.defaultBundle, window); getMsg = mainWindow.getMsg; getObjectDetails = mainWindow.getObjectDetails; dd = mainWindow.dd; // Wheee... localize stuff! //var nodes = document.getElementsByAttribute("localize", "*"); var nodes = document.getElementsByTagName("*"); for (var i = 0; i < nodes.length; i++) { if (nodes[i].hasAttribute("localize")) { var msg = nodes[i].getAttribute("localize"); msg = getMsg("msg." + msg); nodes[i].appendChild(document.createTextNode(msg)); } } changeCSS(view.prefs["motif.current"]); updateMotifSettings(); var output = document.getElementById("output"); output.appendChild(adoptNode(view.messages)); if (view.TYPE in headers) { header = cacheNodes(headers[view.TYPE].prefix, headers[view.TYPE].fields); // Turn off accessibility announcements: they're useless as all these // changes are in the "log" as well, normally. // We're setting the attribute here instead of in the HTML to cope with // custom output windows and so we set it only on the Right header // for this view. header["container"].setAttribute("aria-live", "off"); header.update = headers[view.TYPE].update; } var splash = document.getElementById("splash"); var name; if ("unicodeName" in view) name = view.unicodeName; else name = view.name; splash.appendChild(document.createTextNode(name)); setTimeout(initHeader, 500); initialized = true; } function onTopicNodesClick(e) { if (!clickHandler(e)) { if (e.which != 1) return; startTopicEdit(); } e.stopPropagation(); } function onTopicKeypress(e) { switch (e.keyCode) { case 13: /* enter */ var topic = header["topicinput"].value; topic = mainWindow.replaceColorCodes(topic); view.setTopic(topic); view.dispatch("focus-input"); break; case 27: /* esc */ view.dispatch("focus-input"); break; default: client.mainWindow.onInputKeyPress(e); } } function startTopicEdit() { var me = view.getUser(view.parent.me.unicodeName); if (!me || (!view.mode.publicTopic && !me.isOp && !me.isHalfOp) || !header["topicinput"].hasAttribute("hidden")) { return; } header["topicinput"].value = mainWindow.decodeColorCodes(view.topic); header["topicnodes"].setAttribute("hidden", "true") header["topicinput"].removeAttribute("hidden"); header["topicinput"].focus(); header["topicinput"].selectionStart = 0; } function cancelTopicEdit() { if (!header["topicnodes"].hasAttribute("hidden")) return; header["topicinput"].setAttribute("hidden", "true") header["topicnodes"].removeAttribute("hidden"); } function cacheNodes(pfx, ary, nodes) { if (!nodes) nodes = new Object(); for (var i = 0; i < ary.length; ++i) nodes[ary[i]] = document.getElementById(pfx + ary[i]); return nodes; } function changeCSS(url, id) { if (!id) id = "main-css"; node = document.getElementById(id); if (!node) { node = document.createElement("link"); node.setAttribute("id", id); node.setAttribute("rel", "stylesheet"); node.setAttribute("type", "text/css"); var head = document.getElementsByTagName("head")[0]; head.appendChild(node); } else { if (node.getAttribute("href") == url) return; } node.setAttribute("href", url); window.scrollTo(0, window.document.height); } function updateMotifSettings(existingTimeout) { // Try... catch with a repeat to cope with the style sheet not being loaded const TIMEOUT = 100; try { existingTimeout += TIMEOUT; view.motifSettings = getMotifSettings(); } catch(ex) { if (existingTimeout >= 30000) // Stop after trying for 30 seconds return; if (ex.name == "NS_ERROR_DOM_INVALID_ACCESS_ERR") //not ready, try again setTimeout(updateMotifSettings, TIMEOUT, existingTimeout); else // something else, panic! dd(ex); } } function getMotifSettings() { var re = new RegExp("czsettings\\.(\\w*)", "i"); var rules = document.getElementById("main-css").sheet.cssRules; var rv = new Object(); var ary; // Copy any settings, which are available in the motif using the // "CZSETTINGS" selector. We only store the regexp match after checking // the rule type because selectorText is not defined on other rule types. for (var i = 0; i < rules.length; i++) { if ((rules[i].type == CSSRule.STYLE_RULE) && ((ary = rules[i].selectorText.match(re)) != null)) { rv[ary[1]] = true; } } return rv; } function adoptNode(node) { return client.adoptNode(node, document); } function setText(field, text, checkCondition) { if (!header[field].firstChild) header[field].appendChild(document.createTextNode("")); if (typeof text != "string") { text = MSG_UNKNOWN; if (checkCondition) setAttribute(field, "condition", "red"); } else if (checkCondition) { setAttribute(field, "condition", "green"); } header[field].firstChild.data = text; } function setAttribute(field, name, value) { if (!value) value = "true"; header[field].setAttribute(name, value); } function removeAttribute(field, name) { header[field].removeAttribute(name); } function hasAttribute(field, name) { header[field].hasAttribute(name); } function setHeaderState(state) { if (header) { if (state) { updateHeader(); removeAttribute("container", "hidden"); } else { setAttribute("container", "hidden"); } } } function updateHeader() { document.title = view.getURL(); if (!header || hasAttribute("container", "hidden")) return; for (var id in header) { var value; if (id == "url-anchor") { value = view.getURL(); setAttribute("url-anchor", "href", value); setText("url-anchor", value); } else if (id in view) { setText(id, view[id]); } } if (header.update) header.update(); } function updateClient() { var n = 0, c = 0; for (name in client.networks) { ++n; if (client.networks[name].isConnected()) ++c; } setAttribute("version-container", "title", client.userAgent); setAttribute("version-container", "condition", mainWindow.__cz_condition); setText("version", mainWindow.__cz_version); setText("netcount", String(n)); setText("connectcount", String(c)); } function updateNetwork() { if (view.state == client.mainWindow.NET_CONNECTING) { setText("status", MSG_CONNECTING); setAttribute("status","condition", "yellow"); removeAttribute("status", "title"); setText("lag", MSG_UNKNOWN); } else if (view.isConnected()) { setText("status", MSG_CONNECTED); setAttribute("status","condition", "green"); setAttribute("status", "title", getMsg(MSG_CONNECT_VIA, view.primServ.unicodeName)); if (view.primServ.lag != -1) setText("lag", getMsg(MSG_FMT_SECONDS, view.primServ.lag)); else setText("lag", MSG_UNKNOWN); } else { setText("status", MSG_DISCONNECTED); setAttribute("status","condition", "red"); removeAttribute("status", "title"); setText("lag", MSG_UNKNOWN); } } function updateChannel() { header["topicnodes"].removeChild(header["topicnodes"].firstChild); if (view.active) { var str = view.mode.getModeStr(); if (!str) str = MSG_NO_MODE; setText("modestr", str); setAttribute("modestr", "condition", "green"); setText("usercount", getMsg(MSG_FMT_USERCOUNT, [view.getUsersLength(), view.opCount, view.halfopCount, view.voiceCount])); setAttribute("usercount", "condition", "green"); if (view.topic) { var data = getObjectDetails(view); data.dontLogURLs = true; var mailto = client.prefs["munger.mailto"]; client.munger.getRule(".mailto").enabled = mailto; var nodes = client.munger.munge(view.topic, null, data); client.munger.getRule(".mailto").enabled = false; header["topicnodes"].appendChild(adoptNode(nodes)); } else { setText("topicnodes", MSG_NONE); } } else { setText("modestr", MSG_UNKNOWN); setAttribute("modestr", "condition", "red"); setText("usercount", MSG_UNKNOWN); setAttribute("usercount", "condition", "red"); setText("topicnodes", MSG_UNKNOWN); } } function updateUser() { var source; if (view.name) source = "<" + view.name + "@" + view.host + ">"; else source = MSG_UNKNOWN; if (view.parent.isConnected) setText("serverstr", view.connectionHost, true); else setText("serverstr", null, true); setText("title", getMsg(MSG_TITLE_USER, [view.unicodeName, source])); header["descnodes"].removeChild(header["descnodes"].firstChild); if (typeof view.desc != "undefined") { var data = getObjectDetails(view); data.dontLogURLs = true; var nodes = client.munger.munge(view.desc, null, data); header["descnodes"].appendChild(adoptNode(nodes)); } else { setText("descnodes", ""); } } function updateDCCChat() { if (view.state.state == 4) setText("remotestr", view.remoteIP + ":" + view.port, true); else setText("remotestr", null, true); setText("title", getMsg(MSG_TITLE_DCCCHAT, view.user.unicodeName)); } function updateDCCFile() { var pcent = Math.floor(100 * view.position / view.size); setText("file", view.filename); setText("progress", getMsg(MSG_DCCFILE_PROGRESS, [pcent, mainWindow.getSISize(view.position), mainWindow.getSISize(view.size), mainWindow.getSISpeed(view.speed)])); setAttribute("progressbar", "width", pcent + "%"); } PK `u7/  content/chatzilla/commands.js/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is ChatZilla. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Robert Ginda, , original author * Chiaki Koufugata, chiaki@mozilla.gr.jp, UI i18n * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ const CMD_CONSOLE = 0x01; const CMD_NEED_NET = 0x02; const CMD_NEED_SRV = 0x04; const CMD_NEED_CHAN = 0x08; const CMD_NEED_USER = 0x10; const CMD_NO_HELP = 0x20; function initCommands() { var cmdary = [/* "real" commands */ ["about", cmdAbout, CMD_CONSOLE], ["alias", cmdAlias, CMD_CONSOLE], ["attach", cmdAttach, CMD_CONSOLE], ["away", cmdAway, CMD_CONSOLE], ["back", cmdAway, CMD_CONSOLE], ["ban", cmdBanOrExcept, CMD_NEED_CHAN | CMD_CONSOLE], ["cancel", cmdCancel, CMD_NEED_NET | CMD_CONSOLE], ["charset", cmdCharset, CMD_CONSOLE], ["channel-motif", cmdMotif, CMD_NEED_CHAN | CMD_CONSOLE], ["channel-pref", cmdPref, CMD_NEED_CHAN | CMD_CONSOLE], ["cmd-undo", "cmd-docommand cmd_undo", 0], ["cmd-redo", "cmd-docommand cmd_redo", 0], ["cmd-cut", "cmd-docommand cmd_cut", 0], ["cmd-copy", "cmd-docommand cmd_copy", 0], ["cmd-paste", "cmd-docommand cmd_paste", 0], ["cmd-delete", "cmd-docommand cmd_delete", 0], ["cmd-selectall", "cmd-docommand cmd_selectAll", 0], ["cmd-copy-link-url", "cmd-docommand cmd_copyLink", 0], ["cmd-mozilla-prefs", "cmd-docommand cmd_mozillaPrefs", 0], ["cmd-prefs", "cmd-docommand cmd_chatzillaPrefs", 0], ["cmd-chatzilla-prefs", "cmd-docommand cmd_chatzillaPrefs", 0], ["cmd-chatzilla-opts", "cmd-docommand cmd_chatzillaPrefs", 0], ["cmd-docommand", cmdDoCommand, 0], ["create-tab-for-view", cmdCreateTabForView, 0], ["custom-away", cmdAway, 0], ["op", cmdChanUserMode, CMD_NEED_CHAN | CMD_CONSOLE], ["dcc-accept", cmdDCCAccept, CMD_CONSOLE], ["dcc-accept-list", cmdDCCAutoAcceptList, CMD_NEED_NET | CMD_CONSOLE], ["dcc-accept-list-add", cmdDCCAutoAcceptAdd, CMD_NEED_NET | CMD_CONSOLE], ["dcc-accept-list-remove", cmdDCCAutoAcceptDel, CMD_NEED_NET | CMD_CONSOLE], ["dcc-chat", cmdDCCChat, CMD_NEED_SRV | CMD_CONSOLE], ["dcc-close", cmdDCCClose, CMD_CONSOLE], ["dcc-decline", cmdDCCDecline, CMD_CONSOLE], ["dcc-list", cmdDCCList, CMD_CONSOLE], ["dcc-send", cmdDCCSend, CMD_NEED_SRV | CMD_CONSOLE], ["deop", cmdChanUserMode, CMD_NEED_CHAN | CMD_CONSOLE], ["describe", cmdDescribe, CMD_NEED_SRV | CMD_CONSOLE], ["hop", cmdChanUserMode, CMD_NEED_CHAN | CMD_CONSOLE], ["dehop", cmdChanUserMode, CMD_NEED_CHAN | CMD_CONSOLE], ["voice", cmdChanUserMode, CMD_NEED_CHAN | CMD_CONSOLE], ["devoice", cmdChanUserMode, CMD_NEED_CHAN | CMD_CONSOLE], ["clear-view", cmdClearView, CMD_CONSOLE], ["client", cmdClient, CMD_CONSOLE], ["commands", cmdCommands, CMD_CONSOLE], ["ctcp", cmdCTCP, CMD_NEED_SRV | CMD_CONSOLE], ["default-charset", cmdCharset, CMD_CONSOLE], ["delete-view", cmdDeleteView, CMD_CONSOLE], ["desc", cmdDesc, CMD_CONSOLE], ["disable-plugin", cmdAblePlugin, CMD_CONSOLE], ["disconnect", cmdDisconnect, CMD_NEED_SRV | CMD_CONSOLE], ["disconnect-all", cmdDisconnectAll, CMD_CONSOLE], ["echo", cmdEcho, CMD_CONSOLE], ["enable-plugin", cmdAblePlugin, CMD_CONSOLE], ["eval", cmdEval, CMD_CONSOLE], ["except", cmdBanOrExcept, CMD_NEED_CHAN | CMD_CONSOLE], ["find", cmdFind, 0], ["find-again", cmdFindAgain, 0], ["focus-input", cmdFocusInput, 0], ["font-family", cmdFont, CMD_CONSOLE], ["font-family-other", cmdFont, 0], ["font-size", cmdFont, CMD_CONSOLE], ["font-size-other", cmdFont, 0], ["goto-url", cmdGotoURL, 0], ["goto-url-newwin", cmdGotoURL, 0], ["goto-url-newtab", cmdGotoURL, 0], ["goto-url-external", cmdGotoURL, 0], ["help", cmdHelp, CMD_CONSOLE], ["hide-view", cmdHideView, CMD_CONSOLE], ["ignore", cmdIgnore, CMD_NEED_NET | CMD_CONSOLE], ["input-text-direction", cmdInputTextDirection, 0], ["invite", cmdInvite, CMD_NEED_SRV | CMD_CONSOLE], ["join", cmdJoin, CMD_NEED_SRV | CMD_CONSOLE], ["join-charset", cmdJoin, CMD_NEED_SRV | CMD_CONSOLE], ["kick", cmdKick, CMD_NEED_CHAN | CMD_CONSOLE], ["kick-ban", cmdKick, CMD_NEED_CHAN | CMD_CONSOLE], ["knock", cmdKnock, CMD_NEED_SRV | CMD_CONSOLE], ["leave", cmdLeave, CMD_NEED_CHAN | CMD_CONSOLE], ["links", cmdSimpleCommand, CMD_NEED_SRV | CMD_CONSOLE], ["list", cmdList, CMD_NEED_SRV | CMD_CONSOLE], ["list-plugins", cmdListPlugins, CMD_CONSOLE], ["load", cmdLoad, CMD_CONSOLE], ["log", cmdLog, CMD_CONSOLE], ["map", cmdSimpleCommand, CMD_NEED_SRV | CMD_CONSOLE], ["match-users", cmdMatchUsers, CMD_NEED_CHAN | CMD_CONSOLE], ["me", cmdMe, CMD_CONSOLE], ["motd", cmdSimpleCommand, CMD_NEED_SRV | CMD_CONSOLE], ["mode", cmdMode, CMD_NEED_SRV | CMD_CONSOLE], ["motif", cmdMotif, CMD_CONSOLE], ["msg", cmdMsg, CMD_NEED_SRV | CMD_CONSOLE], ["name", cmdName, CMD_CONSOLE], ["names", cmdNames, CMD_NEED_SRV | CMD_CONSOLE], ["network", cmdNetwork, CMD_CONSOLE], ["network-motif", cmdMotif, CMD_NEED_NET | CMD_CONSOLE], ["network-pref", cmdPref, CMD_NEED_NET | CMD_CONSOLE], ["networks", cmdNetworks, CMD_CONSOLE], ["nick", cmdNick, CMD_CONSOLE], ["notice", cmdNotice, CMD_NEED_SRV | CMD_CONSOLE], ["notify", cmdNotify, CMD_NEED_SRV | CMD_CONSOLE], ["open-at-startup", cmdOpenAtStartup, CMD_CONSOLE], ["oper", cmdOper, CMD_NEED_SRV | CMD_CONSOLE], ["ping", cmdPing, CMD_NEED_SRV | CMD_CONSOLE], ["plugin-pref", cmdPref, CMD_CONSOLE], ["pref", cmdPref, CMD_CONSOLE], ["print", cmdPrint, CMD_CONSOLE], ["query", cmdQuery, CMD_NEED_SRV | CMD_CONSOLE], ["quit", cmdQuit, CMD_CONSOLE], ["quote", cmdQuote, CMD_NEED_NET | CMD_CONSOLE], ["reload-plugin", cmdReload, CMD_CONSOLE], ["rlist", cmdRlist, CMD_NEED_SRV | CMD_CONSOLE], ["reconnect", cmdReconnect, CMD_NEED_NET | CMD_CONSOLE], ["reconnect-all", cmdReconnectAll, CMD_CONSOLE], ["rejoin", cmdRejoin, CMD_NEED_SRV | CMD_NEED_CHAN | CMD_CONSOLE], ["reload-ui", cmdReloadUI, 0], ["save", cmdSave, CMD_CONSOLE], ["say", cmdSay, CMD_CONSOLE], ["server", cmdServer, CMD_CONSOLE], ["set-current-view", cmdSetCurrentView, 0], ["stats", cmdSimpleCommand, CMD_NEED_SRV | CMD_CONSOLE], ["squery", cmdSquery, CMD_NEED_SRV | CMD_CONSOLE], ["sslserver", cmdSSLServer, CMD_CONSOLE], ["stalk", cmdStalk, CMD_CONSOLE], ["supports", cmdSupports, CMD_NEED_SRV | CMD_CONSOLE], ["sync-font", cmdSync, 0], ["sync-header", cmdSync, 0], ["sync-log", cmdSync, 0], ["sync-motif", cmdSync, 0], ["sync-timestamp", cmdSync, 0], ["sync-window", cmdSync, 0], ["testdisplay", cmdTestDisplay, CMD_CONSOLE], ["text-direction", cmdTextDirection, 0], ["time", cmdTime, CMD_NEED_SRV | CMD_CONSOLE], ["timestamps", cmdTimestamps, CMD_CONSOLE], ["toggle-ui", cmdToggleUI, CMD_CONSOLE], ["toggle-pref", cmdTogglePref, 0], ["topic", cmdTopic, CMD_NEED_CHAN | CMD_CONSOLE], ["unignore", cmdIgnore, CMD_NEED_NET | CMD_CONSOLE], ["unban", cmdBanOrExcept, CMD_NEED_CHAN | CMD_CONSOLE], ["unexcept", cmdBanOrExcept, CMD_NEED_CHAN | CMD_CONSOLE], ["unstalk", cmdUnstalk, CMD_CONSOLE], ["urls", cmdURLs, CMD_CONSOLE], ["user", cmdUser, CMD_CONSOLE], ["userhost", cmdUserhost, CMD_NEED_SRV | CMD_CONSOLE], ["userip", cmdUserip, CMD_NEED_SRV | CMD_CONSOLE], ["usermode", cmdUsermode, CMD_CONSOLE], ["user-motif", cmdMotif, CMD_NEED_USER | CMD_CONSOLE], ["user-pref", cmdPref, CMD_NEED_USER | CMD_CONSOLE], ["version", cmdVersion, CMD_NEED_SRV | CMD_CONSOLE], ["who", cmdWho, CMD_NEED_SRV | CMD_CONSOLE], ["whois", cmdWhoIs, CMD_NEED_SRV | CMD_CONSOLE], ["whowas", cmdWhoWas, CMD_NEED_SRV | CMD_CONSOLE], ["wii", cmdWhoIsIdle, CMD_NEED_SRV | CMD_CONSOLE], /* aliases */ ["exit", "quit", CMD_CONSOLE], ["j", "join", CMD_CONSOLE], ["pass", "quote PASS", CMD_CONSOLE], ["part", "leave", CMD_CONSOLE], ["raw", "quote", CMD_CONSOLE], // Shortcuts to useful URLs: ["faq", "goto-url faq", 0], ["homepage", "goto-url homepage", 0], // Used to display a nickname in the menu only. ["label-user", "echo", 0], // These are all the font family/size menu commands... ["font-family-default", "font-family default", 0], ["font-family-serif", "font-family serif", 0], ["font-family-sans-serif", "font-family sans-serif", 0], ["font-family-monospace", "font-family monospace", 0], ["font-size-default", "font-size default", 0], ["font-size-small", "font-size small", 0], ["font-size-medium", "font-size medium", 0], ["font-size-large", "font-size large", 0], ["font-size-bigger", "font-size bigger", 0], // This next command is not visible; it maps to Ctrl-=, which is what // you get when the user tries to do Ctrl-+ (previous command's key). ["font-size-bigger2", "font-size bigger", 0], ["font-size-smaller", "font-size smaller", 0], ["toggle-oas", "open-at-startup toggle", 0], ["toggle-ccm", "toggle-pref collapseMsgs", 0], ["toggle-copy", "toggle-pref copyMessages", 0], ["toggle-usort", "toggle-pref sortUsersByMode", 0], ["toggle-umode", "toggle-pref showModeSymbols", 0], ["toggle-timestamps","timestamps toggle", 0], ["motif-dark", "motif dark", 0], ["motif-light", "motif light", 0], ["motif-default", "motif default", 0], ["sync-output", "eval syncOutputFrame(this)", 0], ["userlist", "toggle-ui userlist", CMD_CONSOLE], ["tabstrip", "toggle-ui tabstrip", CMD_CONSOLE], ["statusbar", "toggle-ui status", CMD_CONSOLE], ["header", "toggle-ui header", CMD_CONSOLE], // text-direction aliases ["rtl", "text-direction rtl", CMD_CONSOLE], ["ltr", "text-direction ltr", CMD_CONSOLE], ["toggle-text-dir", "text-direction toggle", 0], ["irtl", "input-text-direction rtl", CMD_CONSOLE], ["iltr", "input-text-direction ltr", CMD_CONSOLE], // Instrumentation aliases ["allow-inst1", "pref instrumentation.inst1 1", 0], ["deny-inst1", "pref instrumentation.inst1 2", 0] ]; // set the stringbundle associated with these commands. cmdary.stringBundle = client.defaultBundle; client.commandManager = new CommandManager(client.defaultBundle); client.commandManager.defaultFlags = CMD_NO_HELP | CMD_CONSOLE; client.commandManager.isCommandSatisfied = isCommandSatisfied; client.commandManager.defineCommands(cmdary); var restList = ["reason", "action", "text", "message", "params", "font", "expression", "ircCommand", "prefValue", "newTopic", "file", "password", "commandList", "commands", "description"]; client.commandManager.argTypes.__aliasTypes__(restList, "rest"); client.commandManager.argTypes["plugin"] = parsePlugin; } function isCommandSatisfied(e, command) { if (typeof command == "undefined") command = e.command; else if (typeof command == "string") command = this.commands[command]; if (command.flags & CMD_NEED_USER) { if (!("user" in e) || !e.user) { e.parseError = getMsg(MSG_ERR_NEED_USER, command.name); return false; } } if (command.flags & CMD_NEED_CHAN) { if (!("channel" in e) || !e.channel) { e.parseError = getMsg(MSG_ERR_NEED_CHANNEL, command.name); return false; } } if (command.flags & CMD_NEED_SRV) { if (!("server" in e) || !e.server) { e.parseError = getMsg(MSG_ERR_NEED_SERVER, command.name); return false; } if (e.network.state != NET_ONLINE) { e.parseError = MSG_ERR_NOT_CONNECTED; return false; } } if (command.flags & (CMD_NEED_NET | CMD_NEED_SRV | CMD_NEED_CHAN)) { if (!("network" in e) || !e.network) { e.parseError = getMsg(MSG_ERR_NEED_NETWORK, command.name); return false; } } return CommandManager.prototype.isCommandSatisfied(e, command); } CIRCChannel.prototype.dispatch = CIRCNetwork.prototype.dispatch = CIRCUser.prototype.dispatch = CIRCDCCChat.prototype.dispatch = CIRCDCCFileTransfer.prototype.dispatch = client.dispatch = function this_dispatch(text, e, isInteractive, flags) { e = getObjectDetails(this, e); return dispatch(text, e, isInteractive, flags); } function dispatch(text, e, isInteractive, flags) { if (typeof isInteractive == "undefined") isInteractive = false; if (!e) e = new Object(); if (!("sourceObject" in e)) e.__proto__ = getObjectDetails(client.currentObject); if (!("isInteractive" in e)) e.isInteractive = isInteractive; if (!("inputData" in e)) e.inputData = ""; /* split command from arguments */ var ary = text.match(/(\S+) ?(.*)/); if (!ary) { display(getMsg(MSG_ERR_NO_COMMAND, "")); return null; } e.commandText = ary[1]; if (ary[2]) e.inputData = stringTrim(ary[2]); /* list matching commands */ ary = client.commandManager.list(e.commandText, flags); var rv = null; var i; switch (ary.length) { case 0: /* no match, try again */ if (e.server && e.server.isConnected && client.prefs["guessCommands"]) { /* Want to keep the source details. */ var e2 = getObjectDetails(e.sourceObject); e2.inputData = e.commandText + " " + e.inputData; return dispatch("quote", e2); } display(getMsg(MSG_NO_CMDMATCH, e.commandText), MT_ERROR); break; case 1: /* one match, good for you */ var cm = client.commandManager; if (cm.currentDispatchDepth >= cm.maxDispatchDepth) { /* We've reatched the max dispatch depth, so we need to unwind * the entire stack of commands. */ cm.dispatchUnwinding = true; } // Don't start any new commands while unwinding. if (cm.dispatchUnwinding) break; cm.currentDispatchDepth++; var ex; try { rv = dispatchCommand(ary[0], e, flags); } catch (ex) { display(getMsg(MSG_ERR_INTERNAL_DISPATCH, ary[0].name), MT_ERROR); display(formatException(ex), MT_ERROR); if (typeof ex == "object" && "stack" in ex) dd(formatException(ex) + "\n" + ex.stack); else dd(formatException(ex), MT_ERROR); } cm.currentDispatchDepth--; if (cm.dispatchUnwinding && (cm.currentDispatchDepth == 0)) { /* Last level to unwind, and this is where we display the * message. We need to leave it until here because displaying * a message invokes a couple of commands itself, and we need * to not be right on the dispatch limit for that. */ cm.dispatchUnwinding = false; display(getMsg(MSG_ERR_MAX_DISPATCH_DEPTH, ary[0].name), MT_ERROR); } break; default: /* more than one match, show the list */ var str = ""; for (i in ary) str += (str) ? MSG_COMMASP + ary[i].name : ary[i].name; display(getMsg(MSG_ERR_AMBIGCOMMAND, [e.commandText, ary.length, str]), MT_ERROR); } return rv; } function dispatchCommand (command, e, flags) { function displayUsageError (e, details) { if (!("isInteractive" in e) || !e.isInteractive) { var caller = Components.stack.caller.caller; if (caller.name == "dispatch") caller = caller.caller; var error = new Error (details); error.fileName = caller.filename; error.lineNumber = caller.lineNumber; error.name = caller.name; display (formatException(error), MT_ERROR); } else { display (details, MT_ERROR); } //display (getMsg(MSG_FMT_USAGE, [e.command.name, e.command.usage]), // MT_USAGE); }; function callHooks (command, isBefore) { var names, hooks; if (isBefore) hooks = command.beforeHooks; else hooks = command.afterHooks; for (var h in hooks) { if ("dbgDispatch" in client && client.dbgDispatch) { dd ("calling " + (isBefore ? "before" : "after") + " hook " + h); } try { hooks[h](e); } catch (ex) { if (e.command.name != "hook-session-display") { display(getMsg(MSG_ERR_INTERNAL_HOOK, h), MT_ERROR); display(formatException(ex), MT_ERROR); } else { dd(getMsg(MSG_ERR_INTERNAL_HOOK, h)); } dd("Caught exception calling " + (isBefore ? "before" : "after") + " hook " + h); dd(formatException(ex)); if (typeof ex == "object" && "stack" in ex) dd(ex.stack); else dd(getStackTrace()); } } }; e.command = command; if (!e.command.enabled) { /* disabled command */ display (getMsg(MSG_ERR_DISABLED, e.command.name), MT_ERROR); return null; } function parseAlias(aliasLine, e) { /* Only 1 of these will be presented to the user. Math.max is used to supply the 'worst' error */ const ALIAS_ERR_REQ_PRMS = 1; const ALIAS_ERR_REQ_SRV = 2; const ALIAS_ERR_REQ_RECIP = 3; /* double slashes because of the string to regexp conversion, which turns these into single slashes */ const SIMPLE_REPLACE = "\\$\\((\\d+)\\)"; const CUMUL_REPLACE = "\\$\\((\\d+)\\+\\)"; const RANGE_REPLACE = "\\$\\((\\d+)\\-(\\d+)\\)"; const NICK_REPLACE = "\\$\\((nick)\\)"; const RECIP_REPLACE = "\\$\\((recip)\\)"; const ALL_REPLACE = "\\$\\((all)\\)"; if (!aliasLine.match(/\$/)) { if (e.inputData) display(getMsg(MSG_EXTRA_PARAMS, e.inputData), MT_WARN); return aliasLine; } function replaceAll(match, single, cumulative, start, end, nick, recip, all) { if (single) { // Simple 1-parameter replace if (arrayHasElementAt(parameters, single - 1)) { paramsUsed = Math.max(paramsUsed, single); return parameters[single-1]; } maxParamsAsked = Math.max(maxParamsAsked, single); errorMsg = Math.max(ALIAS_ERR_REQ_PRMS, errorMsg); return match; } if (cumulative) { // Cumulative Replace: parameters cumulative and up if (arrayHasElementAt(parameters, cumulative - 1)) { paramsUsed = parameters.length; // there are never leftover parameters for $(somenumber+) return parameters.slice(cumulative - 1).join(" "); } maxParamsAsked = Math.max(maxParamsAsked, cumulative); errorMsg = Math.max(ALIAS_ERR_REQ_PRMS, errorMsg); return match; } if (start && end) { // Ranged replace: parameters start through end //'decrement to correct 0-based index. if (start > end) { var iTemp = end; end = start; start = iTemp; // We obviously have a very stupid user, but we're nice } start--; if (arrayHasElementAt(parameters, start) && arrayHasElementAt(parameters, end - 1)) { paramsUsed = Math.max(paramsUsed,end); return parameters.slice(start, end).join(" "); } maxParamsAsked = Math.max(maxParamsAsked, end); errorMsg = Math.max(ALIAS_ERR_REQ_PRMS, errorMsg); return match; } if (nick) { // Replace with own nickname if (e.network && e.server && e.network.state == NET_ONLINE) return e.server.me.unicodeName; errorMsg = Math.max(ALIAS_ERR_REQ_SRV, errorMsg); return null; } if (recip) { // Replace with current recipient if (e.channel) return e.channel.unicodeName; if (e.user) return e.user.unicodeName; errorMsg = ALIAS_ERR_REQ_RECIP; return null; } // Replace with all parameters paramsUsed = parameters.length; return parameters.join(" "); }; // If the replace function has a problem, this is an error constant: var errorMsg = 0; var paramsUsed = 0; var maxParamsAsked = 0; /* set parameters array and escaping \ and ; in parameters so the * parameters don't get split up by the command list split later on */ e.inputData = e.inputData.replace(/([\\;])/g, "\\$1"); var parameters = e.inputData.match(/\S+/g); if (!parameters) parameters = []; // replace in the command line. var expr = [SIMPLE_REPLACE, CUMUL_REPLACE, RANGE_REPLACE, NICK_REPLACE, RECIP_REPLACE, ALL_REPLACE].join("|"); aliasLine = aliasLine.replace(new RegExp(expr, "gi"), replaceAll); if (errorMsg) { switch (errorMsg) { case ALIAS_ERR_REQ_PRMS: display(getMsg(MSG_ERR_REQUIRED_NR_PARAM, [maxParamsAsked - parameters.length, maxParamsAsked]), MT_ERROR); break; case ALIAS_ERR_REQ_SRV: display(getMsg(MSG_ERR_NEED_SERVER, e.command.name), MT_ERROR); break; case ALIAS_ERR_REQ_RECIP: display(getMsg(MSG_ERR_NEED_RECIP, e.command.name), MT_ERROR); break; } return null; } // return the revised command line. if (paramsUsed < parameters.length) { var pmstring = parameters.slice(paramsUsed, parameters.length).join(" "); display(getMsg(MSG_EXTRA_PARAMS, pmstring), MT_WARN); } return aliasLine; }; var h, i; if (typeof e.command.func == "function") { /* dispatch a real function */ client.commandManager.parseArguments (e); if ("parseError" in e) { displayUsageError(e, e.parseError); } else { if ("beforeHooks" in client.commandManager) callHooks(client.commandManager, true); if ("beforeHooks" in e.command) callHooks(e.command, true); if ("dbgDispatch" in client && client.dbgDispatch) { var str = ""; for (i = 0; i < e.command.argNames.length; ++i) { var name = e.command.argNames[i]; if (name in e) str += " " + name + ": " + e[name]; else if (name != ":") str += " ?" + name; } dd (">>> " + e.command.name + str + " <<<"); e.returnValue = e.command.func(e); /* set client.lastEvent *after* dispatching, so the dispatched * function actually get's a chance to see the last event. */ client.lastEvent = e; } else { e.returnValue = e.command.func(e); } } } else if (typeof e.command.func == "string") { /* dispatch an alias (semicolon delimited list of subcommands) */ if ("beforeHooks" in client.commandManager) callHooks(client.commandManager, true); if ("beforeHooks" in e.command) callHooks(e.command, true); var commandList; //Don't make use of e.inputData if we have multiple commands in 1 alias if (e.command.func.match(/\$\(.*\)|(?:^|[^\\])(?:\\\\)*;/)) commandList = parseAlias(e.command.func, e); else commandList = e.command.func + " " + e.inputData; if (commandList == null) return null; commandList = commandList.split(";"); i = 0; while (i < commandList.length) { if (commandList[i].match(/(?:^|[^\\])(?:\\\\)*$/) || (i == commandList.length - 1)) { commandList[i] = commandList[i].replace(/\\(.)/g, "$1"); i++; } else { commandList[i] = commandList[i] + ";" + commandList[i + 1]; commandList.splice(i + 1, 1); } } for (i = 0; i < commandList.length; ++i) { var newEvent = Clone(e); delete newEvent.command; commandList[i] = stringTrim(commandList[i]); dispatch(commandList[i], newEvent, flags); } } else { display (getMsg(MSG_ERR_NOTIMPLEMENTED, e.command.name), MT_ERROR); return null; } if ("afterHooks" in e.command) callHooks(e.command, false); if ("afterHooks" in client.commandManager) callHooks(client.commandManager, false); return ("returnValue" in e) ? e.returnValue : null; } /* parse function for parameters */ function parsePlugin(e, name) { var ary = e.unparsedData.match (/(?:(\d+)|(\S+))(?:\s+(.*))?$/); if (!ary) return false; var plugin; if (ary[1]) { var i = parseInt(ary[1]); if (!(i in client.plugins)) return false; plugin = client.plugins[i]; } else { plugin = getPluginById(ary[2]); if (!plugin) return false; } e.unparsedData = arrayHasElementAt(ary, 3) ? ary[3] : ""; e[name] = plugin; return true; } function getToggle (toggle, currentState) { if (toggle == "toggle") toggle = !currentState; return toggle; } /****************************************************************************** * command definitions from here on down. */ function cmdAblePlugin(e) { if (e.command.name == "disable-plugin") { if (!e.plugin.enabled) { display(getMsg(MSG_IS_DISABLED, e.plugin.id)); return; } if (e.plugin.API > 0) { if (!e.plugin.disable()) { display(getMsg(MSG_CANT_DISABLE, e.plugin.id)); return; } e.plugin.prefs["enabled"] = false; } else if (!("disablePlugin" in e.plugin.scope)) { display(getMsg(MSG_CANT_DISABLE, e.plugin.id)); return; } else { e.plugin.scope.disablePlugin(); } display(getMsg(MSG_PLUGIN_DISABLED, e.plugin.id)); e.plugin.enabled = false; } else { if (e.plugin.enabled) { display(getMsg(MSG_IS_ENABLED, e.plugin.id)); return; } if (e.plugin.API > 0) { if (!e.plugin.enable()) { display(getMsg(MSG_CANT_ENABLE, e.plugin.id)); e.plugin.prefs["enabled"] = false; return; } e.plugin.prefs["enabled"] = true; } else if (!("enablePlugin" in e.plugin.scope)) { display(getMsg(MSG_CANT_ENABLE, e.plugin.id)); return; } else { e.plugin.scope.enablePlugin(); } display(getMsg(MSG_PLUGIN_ENABLED, e.plugin.id)); e.plugin.enabled = true; } } function cmdBanOrExcept(e) { /* If we're unbanning, or banning in odd cases, we may actually be talking * about a user who is not in the channel, so we need to check the server * for information as well. */ if (!e.user && e.nickname) e.user = e.channel.getUser(e.nickname); if (!e.user && e.nickname) e.user = e.server.getUser(e.nickname); var mask = ""; if (e.user) { // We have a real user object, so get their proper 'ban mask'. mask = fromUnicode(e.user.getBanMask(), e.server); } else if (e.nickname) { /* If we have either ! or @ in the nickname assume the user has given * us a complete mask and pass it directly, otherwise assume it is * only the nickname and use * for username/host. */ mask = fromUnicode(e.nickname, e.server); if (!/[!@]/.test(e.nickname)) mask = mask + "!*@*"; } var op; switch (e.command.name) { case "ban": op = " +b "; break; case "unban": op = " -b "; break; case "except": op = " +e "; break; case "unexcept": op = " -e "; break; } e.server.sendData("MODE " + e.channel.encodedName + op + mask + "\n"); } function cmdCancel(e) { var network = e.network; if (network.isRunningList()) { // We're running a /list, terminate the output so we return to sanity. display(MSG_CANCELLING_LIST); network.abortList(); } else if ((network.state == NET_CONNECTING) || (network.state == NET_WAITING)) { // We're trying to connect to a network, and want to cancel. Do so: if (e.deleteWhenDone) e.network.deleteWhenDone = true; display(getMsg(MSG_CANCELLING, network.unicodeName)); network.cancel(); } else { display(MSG_NOTHING_TO_CANCEL, MT_ERROR); } } function cmdChanUserMode(e) { var modestr; switch (e.command.name) { case "op": modestr = "+oooo"; break; case "deop": modestr = "-oooo"; break; case "hop": modestr = "+hhhh"; break; case "dehop": modestr = "-hhhh"; break; case "voice": modestr = "+vvvv"; break; case "devoice": modestr = "-vvvv"; break; default: ASSERT(0, "Dispatch from unknown name " + e.command.name); return; } var nicks; var user; // Prefer pre-canonicalised list, then a normal list, then finally a // sigular item (canon. or otherwise). if (e.canonNickList) { nicks = combineNicks(e.canonNickList); } else if (e.nicknameList) { var nickList = new Array(); for (i = 0; i < e.nicknameList.length; i++) { user = e.channel.getUser(e.nicknameList[i]); if (!user) { display(getMsg(MSG_ERR_UNKNOWN_USER, e.nicknameList[i]), MT_ERROR); return; } nickList.push(user.encodedName); } nicks = combineNicks(nickList); } else if (e.nickname) { user = e.channel.getUser(e.nickname); if (!user) { display(getMsg(MSG_ERR_UNKNOWN_USER, e.nickname), MT_ERROR); return; } var str = new String(user.encodedName); str.count = 1; nicks = [str]; } else { // Panic? dd("Help! Channel user mode command with no users...?"); } for (var i = 0; i < nicks.length; ++i) { e.server.sendData("MODE " + e.channel.encodedName + " " + modestr.substr(0, nicks[i].count + 1) + " " + nicks[i] + "\n"); } } function cmdCharset(e) { var pm; if (e.command.name == "default-charset") { pm = client.prefManager; msg = MSG_CURRENT_CHARSET; } else { pm = e.sourceObject.prefManager; msg = MSG_CURRENT_CHARSET_VIEW; } if (e.newCharset) { if (e.newCharset == "-") { pm.clearPref("charset"); } else { if(!checkCharset(e.newCharset)) { display(getMsg(MSG_ERR_INVALID_CHARSET, e.newCharset), MT_ERROR); return; } pm.prefs["charset"] = e.newCharset; } } display(getMsg(msg, pm.prefs["charset"])); // If we're on a channel, get the topic again so it can be re-decoded. if (e.newCharset && e.server && e.channel) e.server.sendData("TOPIC " + e.channel.encodedName + "\n"); } function cmdCreateTabForView(e) { return getTabForObject(e.view, true); } function cmdSync(e) { var fun; switch (e.command.name) { case "sync-font": fun = function () { if (view.prefs["displayHeader"]) view.setHeaderState(false); view.changeCSS(view.getFontCSS("data"), "cz-fonts"); if (view.prefs["displayHeader"]) view.setHeaderState(true); }; break; case "sync-header": fun = function () { view.setHeaderState(view.prefs["displayHeader"]); }; break; case "sync-motif": fun = function () { view.changeCSS(view.prefs["motif.current"]); updateAppMotif(view.prefs["motif.current"]); // Refresh the motif settings. view.updateMotifSettings(); }; break; case "sync-timestamp": fun = function () { updateTimestamps(view); }; break; case "sync-window": fun = function () { if (window && window.location && window.location.href != view.prefs["outputWindowURL"]) { syncOutputFrame(view); } }; break; case "sync-log": fun = function () { if (view.prefs["log"] ^ Boolean(view.logFile)) { if (view.prefs["log"]) client.openLogFile(view); else client.closeLogFile(view); } }; break; } var view = e.sourceObject; var window; if (("frame" in view) && view.frame) window = getContentWindow(view.frame); try { fun(); } catch(ex) { dd("Exception in " + e.command.name + " for " + e.sourceObject.unicodeName + ": " + ex); } } function cmdSimpleCommand(e) { e.server.sendData(e.command.name + " " + e.inputData + "\n"); } function cmdSquery(e) { var data; if (e.commands) data = "SQUERY " + e.service + " :" + e.commands + "\n"; else data = "SQUERY " + e.service + "\n"; e.server.sendData(data); } function cmdStatus(e) { function serverStatus (s) { if (!s.connection.isConnected) { display(getMsg(MSG_NOT_CONNECTED, s.parent.name), MT_STATUS); return; } var serverType = (s.parent.primServ == s) ? MSG_PRIMARY : MSG_SECONDARY; display(getMsg(MSG_CONNECTION_INFO, [s.parent.name, s.me.unicodeName, s.connection.host, s.connection.port, serverType]), MT_STATUS); var connectTime = Math.floor((new Date() - s.connection.connectDate) / 1000); connectTime = formatDateOffset(connectTime); var pingTime = ("lastPing" in s) ? formatDateOffset(Math.floor((new Date() - s.lastPing) / 1000)) : MSG_NA; var lag = (s.lag >= 0) ? s.lag : MSG_NA; display(getMsg(MSG_SERVER_INFO, [s.parent.name, connectTime, pingTime, lag]), MT_STATUS); } function channelStatus (c) { var cu; var net = c.parent.parent.name; if ((cu = c.users[c.parent.me.canonicalName])) { var mtype; if (cu.isOp && cu.isVoice) mtype = MSG_VOICEOP; else if (cu.isOp) mtype = MSG_OPERATOR; else if (cu.isVoice) mtype = MSG_VOICED; else mtype = MSG_MEMBER; var mode = c.mode.getModeStr(); if (!mode) mode = MSG_NO_MODE; display(getMsg(MSG_CHANNEL_INFO, [net, mtype, c.unicodeName, mode, (c.parent.isSecure ? "irc://" : "ircs://" ) + escape(net) + "/" + escape(c.encodedName) + "/"]), MT_STATUS); display(getMsg(MSG_CHANNEL_DETAIL, [net, c.unicodeName, c.getUsersLength(), c.opCount, c.voiceCount]), MT_STATUS); if (c.topic) { display(getMsg(MSG_TOPIC_INFO, [net, c.unicodeName, c.topic]), MT_STATUS); } else { display(getMsg(MSG_NOTOPIC_INFO, [net, c.unicodeName]), MT_STATUS); } } else { display(getMsg(MSG_NONMEMBER, [net, c.unicodeName]), MT_STATUS); } }; display(client.userAgent, MT_STATUS); display(getMsg(MSG_USER_INFO, [client.prefs["nickname"], client.prefs["username"], client.prefs["desc"]]), MT_STATUS); var n, s, c; if (e.channel) { serverStatus(e.server); channelStatus(e.channel); } else if (e.network) { for (s in e.network.servers) { serverStatus(e.network.servers[s]); for (c in e.network.servers[s].channels) channelStatus (e.network.servers[s].channels[c]); } } else { for (n in client.networks) { for (s in client.networks[n].servers) { var server = client.networks[n].servers[s] serverStatus(server); for (c in server.channels) channelStatus(server.channels[c]); } } } display(MSG_END_STATUS, MT_STATUS); } function cmdHelp(e) { if (!e.pattern) { if ("hello" in e) display(MSG_HELP_INTRO, "HELLO"); else display(MSG_HELP_INTRO); return; } var ary = client.commandManager.list(e.pattern, CMD_CONSOLE); if (ary.length == 0) { display(getMsg(MSG_ERR_NO_COMMAND, e.pattern), MT_ERROR); return; } for (var i in ary) { display(getMsg(MSG_FMT_USAGE, [ary[i].name, ary[i].usage]), MT_USAGE); display(ary[i].help, MT_HELP); } return; } function cmdTestDisplay(e) { display(MSG_TEST_HELLO, MT_HELLO); display(MSG_TEST_INFO, MT_INFO); display(MSG_TEST_ERROR, MT_ERROR); display(MSG_TEST_HELP, MT_HELP); display(MSG_TEST_USAGE, MT_USAGE); display(MSG_TEST_STATUS, MT_STATUS); if (e.server && e.server.me) { var me = e.server.me; var sampleUser = {TYPE: "IRCUser", encodedName: "ircmonkey", canonicalName: "ircmonkey", unicodeName: "IRCMonkey", viewName: "IRCMonkey", host: "", name: "chatzilla"}; var sampleChannel = {TYPE: "IRCChannel", encodedName: "#mojo", canonicalName: "#mojo", unicodeName: "#Mojo", viewName: "#Mojo"}; function test (from, to) { var fromText = (from != me) ? from.TYPE + " ``" + from.name + "''" : MSG_YOU; var toText = (to != me) ? to.TYPE + " ``" + to.name + "''" : MSG_YOU; display (getMsg(MSG_TEST_PRIVMSG, [fromText, toText]), "PRIVMSG", from, to); display (getMsg(MSG_TEST_ACTION, [fromText, toText]), "ACTION", from, to); display (getMsg(MSG_TEST_NOTICE, [fromText, toText]), "NOTICE", from, to); } test (sampleUser, me); /* from user to me */ test (me, sampleUser); /* me to user */ display(MSG_TEST_URL, "PRIVMSG", sampleUser, me); display(MSG_TEST_STYLES, "PRIVMSG", sampleUser, me); display(MSG_TEST_EMOTICON, "PRIVMSG", sampleUser, me); display(MSG_TEST_RHEET, "PRIVMSG", sampleUser, me); display(unescape(MSG_TEST_CTLCHR), "PRIVMSG", sampleUser, me); display(unescape(MSG_TEST_COLOR), "PRIVMSG", sampleUser, me); display(MSG_TEST_QUOTE, "PRIVMSG", sampleUser, me); if (e.channel) { test (sampleUser, sampleChannel); /* user to channel */ test (me, sampleChannel); /* me to channel */ display(MSG_TEST_TOPIC, "TOPIC", sampleUser, sampleChannel); display(MSG_TEST_JOIN, "JOIN", sampleUser, sampleChannel); display(MSG_TEST_PART, "PART", sampleUser, sampleChannel); display(MSG_TEST_KICK, "KICK", sampleUser, sampleChannel); display(MSG_TEST_QUIT, "QUIT", sampleUser, sampleChannel); display(getMsg(MSG_TEST_STALK, me.unicodeName), "PRIVMSG", sampleUser, sampleChannel); display(MSG_TEST_STYLES, "PRIVMSG", me, sampleChannel); } } } function cmdNetwork(e) { if (!(e.networkName in client.networks)) { display (getMsg(MSG_ERR_UNKNOWN_NETWORK, e.networkName), MT_ERROR); return; } var network = client.networks[e.networkName]; if (!("messages" in network)) network.displayHere(getMsg(MSG_NETWORK_OPENED, network.unicodeName)); dispatch("set-current-view", { view: network }); } function cmdNetworks(e) { var span = document.createElementNS(XHTML_NS, "html:span"); span.appendChild(newInlineText(MSG_NETWORKS_HEADA)); var netnames = keys(client.networks).sort(); var lastname = netnames[netnames.length - 1]; for (n in netnames) { var net = client.networks[netnames[n]]; var a = document.createElementNS(XHTML_NS, "html:a"); /* Test for an all-SSL network */ var isSecure = true; for (var s in client.networks[netnames[n]].serverList) { if (!client.networks[netnames[n]].serverList[s].isSecure) { isSecure = false; break; } } a.setAttribute("class", "chatzilla-link"); a.setAttribute("href", (isSecure ? "ircs://" : "irc://") + net.canonicalName); var t = newInlineText(net.unicodeName); a.appendChild(t); span.appendChild(a); if (netnames[n] != lastname) span.appendChild(newInlineText (MSG_COMMASP)); } span.appendChild(newInlineText(MSG_NETWORKS_HEADB)); display(span, MT_INFO); } function cmdServer(e) { var ary = e.hostname.match(/^(.*):(\d+)$/); if (ary) { // Foolish user obviously hasn't read the instructions, but we're nice. e.password = e.port; e.port = ary[2]; e.hostname = ary[1]; } var name = e.hostname.toLowerCase(); if (!e.port) e.port = 6667; else if (e.port != 6667) name += ":" + e.port; if (!(name in client.networks)) { /* if there wasn't already a network created for this server, * make one. */ client.addNetwork(name, [{name: e.hostname, port: e.port, password: e.password}], true); } else { // We are trying to connect without SSL, adjust for temporary networks if (client.networks[name].temporary) client.networks[name].serverList[0].isSecure = false; // update password on existing server. if (e.password) client.networks[name].serverList[0].password = e.password; } return client.connectToNetwork(name, false); } function cmdSSLServer(e) { var ary = e.hostname.match(/^(.*):(\d+)$/); if (ary) { // Foolish user obviously hasn't read the instructions, but we're nice. e.password = e.port; e.port = ary[2]; e.hostname = ary[1]; } var name = e.hostname.toLowerCase(); if (!e.port) e.port = 9999; if (e.port != 6667) name += ":" + e.port; if (!(name in client.networks)) { /* if there wasn't already a network created for this server, * make one. */ client.addNetwork(name, [{name: e.hostname, port: e.port, password: e.password, isSecure: true}], true); } else { // We are trying to connect using SSL, adjust for temporary networks if (client.networks[name].temporary) client.networks[name].serverList[0].isSecure = true; // update password on existing server. if (e.password) client.networks[name].serverList[0].password = e.password; } return client.connectToNetwork(name, true); } function cmdQuit(e) { // if we're not connected to anything, just close the window if (!("getConnectionCount" in client) || (client.getConnectionCount() == 0)) { client.userClose = true; window.close(); return; } // Otherwise, try to close gracefully: client.wantToQuit(e.reason, true); } function cmdDisconnect(e) { if ((typeof e.reason != "string") || !e.reason) e.reason = e.network.prefs["defaultQuitMsg"]; if (!e.reason) e.reason = client.userAgent; e.network.quit(e.reason); } function cmdDisconnectAll(e) { var netReason; if (confirmEx(MSG_CONFIRM_DISCONNECT_ALL, ["!yes", "!no"]) != 0) return; var conNetworks = client.getConnectedNetworks(); if (conNetworks.length <= 0) { display(MSG_NO_CONNECTED_NETS, MT_ERROR); return; } for (var i = 0; i < conNetworks.length; i++) { netReason = e.reason; if ((typeof netReason != "string") || !netReason) netReason = conNetworks[i].prefs["defaultQuitMsg"]; netReason = (netReason ? netReason : client.userAgent); conNetworks[i].quit(netReason); } } function cmdDeleteView(e) { if (!e.view) e.view = e.sourceObject; if (("lockView" in e.view) && e.view.lockView) { setTabState(e.view, "attention"); return; } if (e.view.TYPE == "IRCChannel" && e.view.active) { e.view.dispatch("part", { deleteWhenDone: true }); return; } if (e.view.TYPE == "IRCDCCChat" && e.view.active) e.view.disconnect(); if (e.view.TYPE == "IRCNetwork" && (e.view.state == NET_CONNECTING || e.view.state == NET_WAITING)) { e.view.dispatch("cancel", { deleteWhenDone: true }); return; } if (client.viewsArray.length < 2) { display(MSG_ERR_LAST_VIEW, MT_ERROR); return; } var tb = getTabForObject(e.view); if (tb) { var i = deleteTab (tb); if (i != -1) { if (e.view.logFile) { e.view.logFile.close(); e.view.logFile = null; } delete e.view.messageCount; delete e.view.messages; client.deck.removeChild(e.view.frame); delete e.view.frame; var oldView = client.currentObject; if (client.currentObject == e.view) { if (i >= client.viewsArray.length) i = client.viewsArray.length - 1; oldView = client.viewsArray[i].source } client.currentObject = null; oldView.dispatch("set-current-view", { view: oldView }); } } } function cmdHideView(e) { if (!e.view) e.view = e.sourceObject; if (client.viewsArray.length < 2) { display(MSG_ERR_LAST_VIEW_HIDE, MT_ERROR); return; } if ("messages" in e.view) { // Detach messages from output window content. if (e.view.messages.parentNode) e.view.messages.parentNode.removeChild(e.view.messages); } var tb = getTabForObject(e.view); if (tb) { var i = deleteTab (tb); if (i != -1) { client.deck.removeChild(e.view.frame); delete e.view.frame; var oldView = client.currentObject; if (client.currentObject == e.view) { if (i >= client.viewsArray.length) i = client.viewsArray.length - 1; oldView = client.viewsArray[i].source } client.currentObject = null; oldView.dispatch("set-current-view", { view: oldView }); } } } function cmdClearView(e) { if (!e.view) e.view = e.sourceObject; e.view.messages = null; e.view.messageCount = 0; e.view.displayHere(MSG_MESSAGES_CLEARED); syncOutputFrame(e.view); } function cmdDesc(e) { if (e.network != null) // somewhere on a network { dispatch("network-pref", {prefValue: e.description, prefName: "desc", network: e.network, isInteractive: e.isInteractive}); } else // no network, change the general pref { dispatch("pref", {prefName: "desc", prefValue: e.description, isInteractive: e.isInteractive}); } } function cmdName(e) { if (e.network != null) // somewhere on a network { dispatch("network-pref", {prefName: "username", prefValue: e.username, network: e.network, isInteractive: e.isInteractive}); } else // no network, change the general pref { dispatch("pref", {prefName: "username", prefValue: e.username, isInteractive: e.isInteractive}); } } function cmdNames(e) { if (e.hasOwnProperty("channelName")) { e.channel = new CIRCChannel(e.server, e.channelName); } else { if (!e.channel) { display(getMsg(MSG_ERR_REQUIRED_PARAM, "channel-name"), MT_ERROR); return; } } e.channel.pendingNamesReply = true; e.server.sendData("NAMES " + e.channel.encodedName + "\n"); } function cmdReconnect(e) { if (e.network.isConnected()) { // Set reconnect flag e.network.reconnect = true; if (typeof e.reason != "string") e.reason = MSG_RECONNECTING; // Now we disconnect. e.network.quit(e.reason); } else { e.network.connect(e.network.requireSecurity); } } function cmdReconnectAll(e) { var reconnected = false; for (var net in client.networks) { if (client.networks[net].isConnected() || ("messages" in client.networks[net])) { client.networks[net].dispatch("reconnect", { reason: e.reason }); reconnected = true; } } if (!reconnected) display(MSG_NO_RECONNECTABLE_NETS, MT_ERROR); } function cmdRejoin(e) { if (e.channel.joined) { if (!e.reason) e.reason = ""; e.channel.dispatch("part", { reason: e.reason, deleteWhenDone: false }); } e.channel.join(e.channel.mode.key); } function cmdTogglePref (e) { var state = !client.prefs[e.prefName]; client.prefs[e.prefName] = state; feedback(e, getMsg (MSG_FMT_PREF, [e.prefName, state ? MSG_VAL_ON : MSG_VAL_OFF])); } function cmdToggleUI(e) { var ids = new Array(); switch (e.thing) { case "tabstrip": ids = ["view-tabs"]; break; case "userlist": ids = ["main-splitter", "user-list-box"]; break; case "header": client.currentObject.prefs["displayHeader"] = !client.currentObject.prefs["displayHeader"]; return; case "status": ids = ["status-bar"]; break; default: ASSERT (0,"Unknown element ``" + e.thing + "'' passed to onToggleVisibility."); return; } var newState; var elem = document.getElementById(ids[0]); var sourceObject = e.sourceObject; var newState = !elem.collapsed; for (var i in ids) { elem = document.getElementById(ids[i]); elem.collapsed = newState; } updateTitle(); dispatch("focus-input"); } function cmdCommands(e) { display(MSG_COMMANDS_HEADER); var matchResult = client.commandManager.listNames(e.pattern, CMD_CONSOLE); matchResult = matchResult.join(MSG_COMMASP); if (e.pattern) display(getMsg(MSG_MATCHING_COMMANDS, [e.pattern, matchResult])); else display(getMsg(MSG_ALL_COMMANDS, matchResult)); } function cmdAttach(e) { if (e.ircUrl.search(/ircs?:\/\//i) != 0) e.ircUrl = "irc://" + e.ircUrl; var parsedURL = parseIRCURL(e.ircUrl); if (!parsedURL) { display(getMsg(MSG_ERR_BAD_IRCURL, e.ircUrl), MT_ERROR); return; } gotoIRCURL(e.ircUrl); } function cmdMatchUsers(e) { var matches = e.channel.findUsers(e.mask); var uc = matches.unchecked; var msgNotChecked = ""; // Get a pretty list of nicknames: var nicknames = []; for (var i = 0; i < matches.users.length; i++) nicknames.push(matches.users[i].unicodeName); var nicknameStr = arraySpeak(nicknames); // Were we unable to check one or more of the users? if (uc != 0) msgNotChecked = getMsg(MSG_MATCH_UNCHECKED, uc); if (matches.users.length == 0) display(getMsg(MSG_NO_MATCHING_NICKS, msgNotChecked)); else display(getMsg(MSG_MATCHING_NICKS, [nicknameStr, msgNotChecked])); } function cmdMe(e) { if (!("act" in e.sourceObject)) { display(getMsg(MSG_ERR_IMPROPER_VIEW, "me"), MT_ERROR); return; } var msg = filterOutput(e.action, "ACTION", e.sourceObject); client.munger.getRule(".mailto").enabled = client.prefs["munger.mailto"]; e.sourceObject.display(msg, "ACTION", "ME!", e.sourceObject); client.munger.getRule(".mailto").enabled = false; e.sourceObject.act(msg); } function cmdDescribe(e) { var target = e.server.addTarget(e.target); var msg = filterOutput(e.action, "ACTION", target); e.sourceObject.display(msg, "ACTION", "ME!", target); target.act(msg); } function cmdMode(e) { var chan; // Make sure the user can leave the channel name out from a channel view. if ((!e.target || /^[\+\-].+/.test(e.target)) && !(chan && e.server.getChannel(chan))) { if (e.channel) { chan = e.channel.canonicalName; if (e.param && e.modestr) { e.paramList.unshift(e.modestr); } else if (e.modestr) { e.paramList = [e.modestr]; e.param = e.modestr; } e.modestr = e.target; } else { display(getMsg(MSG_ERR_REQUIRED_PARAM, "target"), MT_ERROR); return; } } else { chan = fromUnicode(e.target, e.server); } // Check whether our mode string makes sense if (!e.modestr) { e.modestr = ""; if (!e.channel && arrayContains(e.server.channelTypes, chan[0])) e.channel = new CIRCChannel(e.server, null, chan); if (e.channel) e.channel.pendingModeReply = true; } else if (!(/^([+-][a-z]+)+$/i).test(e.modestr)) { display(getMsg(MSG_ERR_INVALID_MODE, e.modestr), MT_ERROR); return; } var params = (e.param) ? " " + e.paramList.join(" ") : ""; e.server.sendData("MODE " + chan + " " + fromUnicode(e.modestr, e.server) + params + "\n"); } function cmdMotif(e) { var pm; var msg; if (e.command.name == "channel-motif") { pm = e.channel.prefManager; msg = MSG_CURRENT_CSS_CHAN; } else if (e.command.name == "network-motif") { pm = e.network.prefManager; msg = MSG_CURRENT_CSS_NET; } else if (e.command.name == "user-motif") { pm = e.user.prefManager; msg = MSG_CURRENT_CSS_USER; } else { pm = client.prefManager; msg = MSG_CURRENT_CSS; } if (e.motif) { if (e.motif == "-") { // delete local motif in favor of default pm.clearPref("motif.current"); e.motif = pm.prefs["motif.current"]; } else if (e.motif.search(/^(file|https?|ftp):/i) != -1) { // specific css file pm.prefs["motif.current"] = e.motif; } else { // motif alias var prefName = "motif." + e.motif; if (client.prefManager.isKnownPref(prefName)) { e.motif = client.prefManager.prefs[prefName]; } else { display(getMsg(MSG_ERR_UNKNOWN_MOTIF, e.motif), MT_ERROR); return; } pm.prefs["motif.current"] = e.motif; } } display (getMsg(msg, pm.prefs["motif.current"])); } function cmdList(e) { if (!e.channelName) { e.channelName = ""; var c = e.server.channelCount; if ((c > client.SAFE_LIST_COUNT) && !("listWarned" in e.network)) { client.munger.getRule(".inline-buttons").enabled = true; display(getMsg(MSG_LIST_CHANCOUNT, [c, "list"]), MT_WARN); client.munger.getRule(".inline-buttons").enabled = false; e.network.listWarned = true; return; } } e.network.list(e.channelName); } function cmdListPlugins(e) { function listPlugin(plugin, i) { var enabled; if ((plugin.API > 0) || ("disablePlugin" in plugin.scope)) enabled = plugin.enabled; else enabled = MSG_ALWAYS; display(getMsg(MSG_FMT_PLUGIN1, [i, plugin.url])); display(getMsg(MSG_FMT_PLUGIN2, [plugin.id, plugin.version, enabled, plugin.status])); display(getMsg(MSG_FMT_PLUGIN3, plugin.description)); } if (e.plugin) { listPlugin(e.plugin, 0); return; } if (client.plugins.length > 0) { for (var i = 0; i < client.plugins.length; ++i) listPlugin(client.plugins[i], i); } else { display(MSG_NO_PLUGINS); } } function cmdRlist(e) { try { var re = new RegExp(e.regexp, "i"); } catch (ex) { display(MSG_ERR_INVALID_REGEX, MT_ERROR); return; } var c = e.server.channelCount; if ((c > client.SAFE_LIST_COUNT) && !("listWarned" in e.network)) { client.munger.getRule(".inline-buttons").enabled = true; display(getMsg(MSG_LIST_CHANCOUNT, [c, "rlist " + e.regexp]), MT_WARN); client.munger.getRule(".inline-buttons").enabled = false; e.network.listWarned = true; return; } e.network.list(re); } function cmdReloadUI(e) { if (!("getConnectionCount" in client) || client.getConnectionCount() == 0) { window.location.href = window.location.href; } } function cmdQuery(e) { // We'd rather *not* trigger the user.start event this time. blockEventSounds("user", "start"); var user = openQueryTab(e.server, e.nickname); dispatch("set-current-view", { view: user }); if (e.message) { e.message = filterOutput(e.message, "PRIVMSG", user); user.display(e.message, "PRIVMSG", "ME!", user); user.say(e.message); } return user; } function cmdSay(e) { if (!("say" in e.sourceObject)) { display(getMsg(MSG_ERR_IMPROPER_VIEW, "say"), MT_ERROR); return; } var msg = filterOutput(e.message, "PRIVMSG", e.sourceObject); client.munger.getRule(".mailto").enabled = client.prefs["munger.mailto"]; e.sourceObject.display(msg, "PRIVMSG", "ME!", e.sourceObject); client.munger.getRule(".mailto").enabled = false; e.sourceObject.say(msg); } function cmdMsg(e) { var target = e.server.addTarget(e.nickname); var msg = filterOutput(e.message, "PRIVMSG", target); client.munger.getRule(".mailto").enabled = client.prefs["munger.mailto"]; e.sourceObject.display(msg, "PRIVMSG", "ME!", target); client.munger.getRule(".mailto").enabled = false; target.say(msg); } function cmdNick(e) { if (!e.nickname) { var curNick; if (e.server && e.server.isConnected()) curNick = e.server.me.unicodeName; else if (e.network) curNick = e.network.prefs["nickname"]; else curNick = client.prefs["nickname"]; e.nickname = prompt(MSG_NICK_PROMPT, curNick); if (e.nickname == null) return; } if (e.server) e.server.changeNick(e.nickname); if (e.network) { e.network.prefs["nickname"] = e.nickname; e.network.preferredNick = e.nickname; } else { client.prefs["nickname"] = e.nickname; updateTitle(client); } } function cmdNotice(e) { var target = e.server.addTarget(e.nickname); var msg = filterOutput(e.message, "NOTICE", target); client.munger.getRule(".mailto").enabled = client.prefs["munger.mailto"]; e.sourceObject.display(msg, "NOTICE", "ME!", target); client.munger.getRule(".mailto").enabled = false; target.notice(msg); } function cmdQuote(e) { /* Check we are connected, or at least pretending to be connected, so this * can actually send something. The only thing that's allowed to send * before the 001 is PASS, so if the command is not that and the net is not * online, we stop too. */ if ((e.network.state != NET_ONLINE) && (!e.server.isConnected || !e.ircCommand.match(/^\s*PASS/i))) { feedback(e, MSG_ERR_NOT_CONNECTED); return; } e.server.sendData(fromUnicode(e.ircCommand) + "\n", e.sourceObject); } function cmdEval(e) { var sourceObject = e.sourceObject; try { sourceObject.doEval = function (__s) { return eval(__s); } sourceObject.display(e.expression, MT_EVALIN); var rv = String(sourceObject.doEval (e.expression)); sourceObject.display (rv, MT_EVALOUT); } catch (ex) { sourceObject.display (String(ex), MT_ERROR); } } function cmdFocusInput(e) { const WWATCHER_CTRID = "@mozilla.org/embedcomp/window-watcher;1"; const nsIWindowWatcher = Components.interfaces.nsIWindowWatcher; var watcher = Components.classes[WWATCHER_CTRID].getService(nsIWindowWatcher); if (watcher.activeWindow == window) client.input.focus(); else document.commandDispatcher.focusedElement = client.input; } function cmdGotoURL(e) { const EXT_PROTO_SVC = "@mozilla.org/uriloader/external-protocol-service;1"; if (e.url.search(/^ircs?:/i) == 0) { gotoIRCURL(e.url); return; } if (e.url.search(/^x-cz-command:/i) == 0) { var ary = e.url.match(/^x-cz-command:(.*)$/i); getContentWindow(e.sourceObject.frame).location.href = "javascript:void(view.dispatch('" + decodeURI(ary[1]) + "', null, true))"; return; } try { var uri = client.iosvc.newURI(e.url, "UTF-8", null); } catch (ex) { // Given "goto-url faq bar", expand to "http://.../faq/#bar" var localeURLKey = "msg.localeurl." + e.url; var hash = (("anchor" in e) && e.anchor) ? "#" + e.anchor : ""; if (localeURLKey != getMsg(localeURLKey)) dispatch(e.command.name + " " + getMsg(localeURLKey) + hash); else display(getMsg(MSG_ERR_INVALID_URL, e.url), MT_ERROR); dispatch("focus-input"); return; } if ((e.command.name == "goto-url-external") || (client.host == "XULrunner")) { const extProtoSvc = getService(EXT_PROTO_SVC, "nsIExternalProtocolService"); extProtoSvc.loadUrl(uri); dispatch("focus-input"); return; } var window = getWindowByType("navigator:browser"); if (!window) { openTopWin(e.url); dispatch("focus-input"); return; } if (client.prefs["link.focus"]) { try { window.focus(); } catch (ex) { dd(formatException(ex)); } } if (e.command.name == "goto-url-newwin") { try { if (client.host == "Mozilla") window.openNewWindowWith(e.url, false); else window.openNewWindowWith(e.url, null, null, null); } catch (ex) { dd(formatException(ex)); } dispatch("focus-input"); return; } if (e.command.name == "goto-url-newtab") { try { if (client.host == "Mozilla") window.openNewTabWith(e.url, false, false); else window.openNewTabWith(e.url, null, null, null, null); } catch (ex) { dd(formatException(ex)); } dispatch("focus-input"); return; } var location = window.content.document.location; if (location.href.indexOf("chrome://chatzilla/content/") == 0) { // don't replace chatzilla running in a tab openTopWin(e.url); dispatch("focus-input"); return; } try { location.href = e.url; } catch (ex) { dd(formatException(ex)); } dispatch("focus-input"); } function cmdCTCP(e) { var obj = e.server.addTarget(e.target); obj.ctcp(e.code, e.params); } function cmdJoin(e) { /* This check makes sure we only check if the *user* entered anything, and * ignore any contextual information, like the channel the command was * run on. */ if (!e.hasOwnProperty("channelName") || !e.channelName) { if (e.network.joinDialog) return e.network.joinDialog.focus(); window.openDialog("chrome://chatzilla/content/channels.xul", "", "resizable=yes", { client: client, network: e.network, opener: window }); return null; } if (!("charset" in e)) { e.charset = null; } else if (e.charset && !checkCharset(e.charset)) { display (getMsg(MSG_ERR_INVALID_CHARSET, e.charset), MT_ERROR); return null; } if (e.channelName.search(",") != -1) { // We can join multiple channels! Woo! var chan; var chans = e.channelName.split(","); var keys = []; if (e.key) keys = e.key.split(","); for (var c in chans) { chan = dispatch("join", { charset: e.charset, channelName: chans[c], key: keys.shift() }); } return chan; } if ((arrayIndexOf(["#", "&", "+", "!"], e.channelName[0]) == -1) && (arrayIndexOf(e.server.channelTypes, e.channelName[0]) == -1)) { e.channelName = e.server.channelTypes[0] + e.channelName; } var charset = e.charset ? e.charset : e.network.prefs["charset"]; e.channel = e.server.addChannel(e.channelName, charset); if (e.charset) e.channel.prefs["charset"] = e.charset; e.channel.join(e.key); /* !-channels are "safe" channels, and get a server-generated prefix. For * this reason, we shouldn't do anything client-side until the server * replies (since the reply will have the appropriate prefix). */ if (e.channelName[0] != "!") { if (!("messages" in e.channel)) { e.channel.displayHere(getMsg(MSG_CHANNEL_OPENED, e.channel.unicodeName), MT_INFO); } dispatch("set-current-view", { view: e.channel }); } return e.channel; } function cmdLeave(e) { if (!e.server) { display(MSG_ERR_IMPROPER_VIEW, MT_ERROR); return; } if (e.hasOwnProperty("channelName")) { if (arrayIndexOf(e.server.channelTypes, e.channelName[0]) == -1) { // No valid prefix character. Check they really meant a channel... var valid = false; for (var i = 0; i < e.server.channelTypes.length; i++) { // Hmm, not ideal... var chan = e.server.getChannel(e.server.channelTypes[i] + e.channelName); if (chan) { // Yes! They just missed that single character. e.channel = chan; valid = true; break; } } // We can only let them get away here if we've got a channel. if (!valid) { if (e.channel) { /* Their channel name was invalid, but we have a channel * view, so we'll assume they did "/leave part msg". */ e.reason = e.channelName + (e.reason ? " " + e.reason : ""); } else { display(getMsg(MSG_ERR_UNKNOWN_CHANNEL, e.channelName), MT_ERROR); return; } } } else { // Valid prefix, so get real channel (if it exists...). e.channel = e.server.getChannel(e.channelName); if (!e.channel) { display(getMsg(MSG_ERR_UNKNOWN_CHANNEL, e.channelName), MT_ERROR); return; } } } if (!("deleteWhenDone" in e)) e.deleteWhenDone = client.prefs["deleteOnPart"]; /* If it's not active, we're not actually in it, even though the view is * still here. */ if (e.channel.active) { e.channel.deleteWhenDone = e.deleteWhenDone; if (!e.reason) e.reason = ""; e.server.sendData("PART " + e.channel.encodedName + " :" + fromUnicode(e.reason, e.channel) + "\n"); } else { if (e.deleteWhenDone) e.channel.dispatch("delete-view"); } } function cmdReload(e) { dispatch("load " + e.plugin.url); } function cmdLoad(e) { var ex; var plugin; function removeOldPlugin(url) { var oldPlugin; var i = getPluginIndexByURL(url); if (i == -1) return -1; oldPlugin = client.plugins[i]; if (oldPlugin.enabled) { if (oldPlugin.API > 0) { if (!oldPlugin.disable()) { display(getMsg(MSG_CANT_DISABLE, oldPlugin.id)); display(getMsg(MSG_ERR_SCRIPTLOAD, e.url)); return null; } client.prefManager.removeObserver(oldPlugin.prefManager); oldPlugin.prefManager.destroy(); } else if ("disablePlugin" in oldPlugin.scope) { oldPlugin.scope.disablePlugin(); } else { display(getMsg(MSG_CANT_DISABLE, oldPlugin.id)); display(getMsg(MSG_ERR_SCRIPTLOAD, e.url)); return null; } display(getMsg(MSG_PLUGIN_DISABLED, oldPlugin.id)); } return i; } if (!e.scope) e.scope = new Object(); if (!("plugin" in e.scope)) { e.scope.plugin = { url: e.url, id: MSG_UNKNOWN, version: -1, description: "", status: MSG_LOADING, enabled: false, PluginAPI: 1, cwd: e.url.match(/^(.*?)[^\/]+$/)[1]}; } plugin = e.scope.plugin; plugin.scope = e.scope; try { var rvStr; var rv = rvStr = client.load(e.url, e.scope); var index = removeOldPlugin(e.url); if (index == null) return null; if ("init" in plugin) { // Sanity check plugin's methods and properties: if (!("id" in plugin) || (plugin.id == MSG_UNKNOWN)) display(getMsg(MSG_ERR_PLUGINAPI_NOID, e.url)); else if (!(plugin.id.match(/^[A-Za-z-_]+$/))) display(getMsg(MSG_ERR_PLUGINAPI_FAULTYID, e.url)); else if (!("enable" in plugin)) display(getMsg(MSG_ERR_PLUGINAPI_NOENABLE, e.url)); else if (!("disable" in plugin)) display(getMsg(MSG_ERR_PLUGINAPI_NODISABLE, e.url)); if (!("enable" in plugin) || !("disable" in plugin) || !("id" in plugin) || (plugin.id == MSG_UNKNOWN) || !(plugin.id.match(/^[A-Za-z-_]+$/))) { display (getMsg(MSG_ERR_SCRIPTLOAD, e.url)); return null; } plugin.API = 1; plugin.prefary = [["enabled", true, "hidden"]]; rv = rvStr = plugin.init(e.scope); var branch = "extensions.irc.plugins." + plugin.id + "."; var prefManager = new PrefManager(branch, client.defaultBundle); prefManager.addPrefs(plugin.prefary); plugin.prefManager = prefManager; plugin.prefs = prefManager.prefs; if ("onPrefChanged" in plugin) prefManager.addObserver(plugin); client.prefManager.addObserver(prefManager); client.prefManagers.push(prefManager); } else { plugin.API = 0; if ("initPlugin" in e.scope) rv = rvStr = e.scope.initPlugin(e.scope); plugin.enabled = true; } plugin.status = "loaded"; if (typeof rv == "function") rvStr = "function"; if (index != -1) client.plugins[index] = plugin; else index = client.plugins.push(plugin) - 1; feedback(e, getMsg(MSG_SUBSCRIPT_LOADED, [e.url, rvStr]), MT_INFO); if ((plugin.API > 0) && plugin.prefs["enabled"]) dispatch("enable-plugin " + index); return rv; } catch (ex) { display (getMsg(MSG_ERR_SCRIPTLOAD, e.url)); display (formatException(ex), MT_ERROR); } return null; } function cmdWho(e) { e.network.pendingWhoReply = true; e.server.LIGHTWEIGHT_WHO = false; e.server.who(e.rest); } function cmdWhoIs(e) { if (!isinstance(e.network.whoisList, Object)) e.network.whoisList = {}; for (var i = 0; i < e.nicknameList.length; i++) { if ((i < e.nicknameList.length - 1) && (e.server.toLowerCase(e.nicknameList[i]) == e.server.toLowerCase(e.nicknameList[i + 1]))) { e.server.whois(e.nicknameList[i] + " " + e.nicknameList[i]); i++; } else { e.server.whois(e.nicknameList[i]); } e.network.whoisList[e.server.toLowerCase(e.nicknameList[i])] = null; } } function cmdWhoIsIdle(e) { for (var i = 0; i < e.nicknameList.length; i++) e.server.whois(e.nicknameList[i] + " " + e.nicknameList[i]); } function cmdWhoWas(e) { e.server.whowas(e.nickname, e.limit); } function cmdTopic(e) { if (!e.newTopic) e.server.sendData("TOPIC " + e.channel.encodedName + "\n"); else e.channel.setTopic(e.newTopic); } function cmdAbout(e) { display(CIRCServer.prototype.VERSION_RPLY); display(MSG_HOMEPAGE); } function cmdAlias(e) { var aliasDefs = client.prefs["aliases"]; function getAlias(commandName) { for (var i = 0; i < aliasDefs.length; ++i) { var ary = aliasDefs[i].match(/^(.*?)\s*=\s*(.*)$/); if (ary[1] == commandName) return [i, ary[2]]; } return null; }; var ary; if (e.commandList == "-") { /* remove alias */ ary = getAlias(e.aliasName); if (!ary) { display(getMsg(MSG_NOT_AN_ALIAS, e.aliasName), MT_ERROR); return; } delete client.commandManager.commands[e.aliasName]; arrayRemoveAt(aliasDefs, ary[0]); aliasDefs.update(); feedback(e, getMsg(MSG_ALIAS_REMOVED, e.aliasName)); } else if (e.aliasName) { /* add/change alias */ client.commandManager.defineCommand(e.aliasName, e.commandList); ary = getAlias(e.aliasName); if (ary) aliasDefs[ary[0]] = e.aliasName + " = " + e.commandList; else aliasDefs.push(e.aliasName + " = " + e.commandList); aliasDefs.update(); feedback(e, getMsg(MSG_ALIAS_CREATED, [e.aliasName, e.commandList])); } else { /* list aliases */ if (aliasDefs.length == 0) { display(MSG_NO_ALIASES); } else { for (var i = 0; i < aliasDefs.length; ++i) { ary = aliasDefs[i].match(/^(.*?)\s*=\s*(.*)$/); if (ary) display(getMsg(MSG_FMT_ALIAS, [ary[1], ary[2]])); else display(getMsg(MSG_ERR_BADALIAS, aliasDefs[i])); } } } } function cmdAway(e) { function sendToAllNetworks(command, reason) { for (var n in client.networks) { if (client.networks[n].primServ && (client.networks[n].state == NET_ONLINE)) { client.networks[n].dispatch(command, { reason: reason }); } } }; if ((e.command.name == "away") || (e.command.name == "custom-away")) { /* going away */ if (e.command.name == "custom-away") { e.reason = prompt(MSG_AWAY_PROMPT); // prompt() returns null for cancelling, a string otherwise (even if empty). if (e.reason == null) return; } // No parameter, or user entered nothing in the prompt. if (!e.reason) e.reason = MSG_AWAY_DEFAULT; // Update away list (remove from current location). for (var i = 0; i < client.awayMsgs.length; i++) { if (client.awayMsgs[i].message == e.reason) { client.awayMsgs.splice(i, 1); break; } } // Always put new item at start. var newMsg = { message: e.reason }; client.awayMsgs.unshift(newMsg); // Make sure we've not exceeded the limit set. if (client.awayMsgs.length > client.awayMsgCount) client.awayMsgs.splice(client.awayMsgCount); // And now, to save the list! try { var awayFile = new nsLocalFile(client.prefs["profilePath"]); awayFile.append("awayMsgs.txt"); var awayLoader = new TextSerializer(awayFile); if (awayLoader.open(">")) { awayLoader.serialize(client.awayMsgs); awayLoader.close(); } } catch(ex) { display(getMsg(MSG_ERR_AWAY_SAVE, formatException(ex)), MT_ERROR); } if (e.server) { var normalNick = e.network.prefs["nickname"]; var awayNick = e.network.prefs["awayNick"]; if (e.network.state == NET_ONLINE) { // Postulate that if normal nick and away nick are the same, // user doesn't want to change nicks: if (awayNick && (normalNick != awayNick)) e.server.changeNick(awayNick); e.server.sendData("AWAY :" + fromUnicode(e.reason, e.network) + "\n"); } if (awayNick && (normalNick != awayNick)) e.network.preferredNick = awayNick; e.network.prefs["away"] = e.reason; } else { client.prefs["away"] = e.reason; // Client view, do command for all networks. sendToAllNetworks("away", e.reason); display(getMsg(MSG_AWAY_ON, e.reason)); } } else { /* returning */ if (e.server) { if (e.network.state == NET_ONLINE) { var curNick = e.server.me.unicodeName; var awayNick = e.network.prefs["awayNick"]; if (awayNick && (curNick == awayNick)) e.server.changeNick(e.network.prefs["nickname"]); e.server.sendData("AWAY\n"); } // Go back to old nick, even if not connected: if (awayNick && (curNick == awayNick)) e.network.preferredNick = e.network.prefs["nickname"]; e.network.prefs["away"] = ""; } else { client.prefs["away"] = ""; // Client view, do command for all networks. sendToAllNetworks("back"); display(MSG_AWAY_OFF); } } } function cmdOpenAtStartup(e) { var url = e.sourceObject.getURL(); var list = client.prefs["initialURLs"]; var index = arrayIndexOf(list, url); if (e.toggle == null) { if (index == -1) display(getMsg(MSG_STARTUP_NOTFOUND, url)); else display(getMsg(MSG_STARTUP_EXISTS, url)); return; } e.toggle = getToggle(e.toggle, (index != -1)); if (e.toggle) { // yes, please open at startup if (index == -1) { list.push(url); list.update(); display(getMsg(MSG_STARTUP_ADDED, url)); } else { display(getMsg(MSG_STARTUP_EXISTS, url)); } } else { // no, please don't open at startup if (index != -1) { arrayRemoveAt(list, index); list.update(); display(getMsg(MSG_STARTUP_REMOVED, url)); } else { display(getMsg(MSG_STARTUP_NOTFOUND, url)); } } } function cmdOper(e) { // Password is optional, if it is not given, we use a safe prompt. if (!e.password) e.password = promptPassword(MSG_NEED_OPER_PASSWORD, ""); if (!e.password) return; e.server.sendData("OPER " + fromUnicode(e.opername, e.server) + " " + fromUnicode(e.password, e.server) + "\n"); } function cmdPing (e) { e.network.dispatch("ctcp", { target: e.nickname, code: "PING" }); } function cmdPref (e) { var msg; var pm; if (e.command.name == "network-pref") { pm = e.network.prefManager; msg = MSG_FMT_NETPREF; } else if (e.command.name == "channel-pref") { pm = e.channel.prefManager; msg = MSG_FMT_CHANPREF; } else if (e.command.name == "plugin-pref") { pm = e.plugin.prefManager; msg = MSG_FMT_PLUGINPREF; } else if (e.command.name == "user-pref") { pm = e.user.prefManager; msg = MSG_FMT_USERPREF; } else { pm = client.prefManager; msg = MSG_FMT_PREF; } var ary = pm.listPrefs(e.prefName); if (ary.length == 0) { display (getMsg(MSG_ERR_UNKNOWN_PREF, [e.prefName]), MT_ERROR); return false; } if (e.prefValue == "-") e.deletePref = true; if (e.deletePref) { if (!(e.prefName in pm.prefRecords)) { display(getMsg(MSG_ERR_UNKNOWN_PREF, [e.prefName]), MT_ERROR); return false; } try { pm.clearPref(e.prefName); } catch (ex) { // ignore exception generated by clear of nonexistant pref if (!("result" in ex) || ex.result != Components.results.NS_ERROR_UNEXPECTED) { throw ex; } } var prefValue = pm.prefs[e.prefName]; feedback (e, getMsg(msg, [e.prefName, pm.prefs[e.prefName]])); return true; } if (e.prefValue) { if (!(e.prefName in pm.prefRecords)) { display(getMsg(MSG_ERR_UNKNOWN_PREF, [e.prefName]), MT_ERROR); return false; } var r = pm.prefRecords[e.prefName]; var def, type; if (typeof r.defaultValue == "function") def = r.defaultValue(e.prefName); else def = r.defaultValue; type = typeof def; switch (type) { case "number": e.prefValue = Number(e.prefValue); break; case "boolean": e.prefValue = (e.prefValue.toLowerCase() == "true"); break; case "string": break; default: if (isinstance(e.prefValue, Array)) e.prefValue = e.prefValue.join("; "); if (isinstance(def, Array)) e.prefValue = pm.stringToArray(e.prefValue); break; } pm.prefs[e.prefName] = e.prefValue; if (isinstance(e.prefValue, Array)) e.prefValue = e.prefValue.join("; "); feedback (e, getMsg(msg, [e.prefName, e.prefValue])); } else { for (var i = 0; i < ary.length; ++i) { var value; if (isinstance(pm.prefs[ary[i]], Array)) value = pm.prefs[ary[i]].join("; "); else value = pm.prefs[ary[i]]; feedback(e, getMsg(msg, [ary[i], value])); } } return true; } function cmdPrint(e) { if (("frame" in e.sourceObject) && e.sourceObject.frame && getContentWindow(e.sourceObject.frame)) { getContentWindow(e.sourceObject.frame).print(); } else { display(MSG_ERR_UNABLE_TO_PRINT); } } function cmdVersion(e) { if (e.nickname) e.network.dispatch("ctcp", { target: e.nickname, code: "VERSION"}); else e.server.sendData(fromUnicode("VERSION") + "\n", e.sourceObject); } function cmdEcho(e) { client.munger.getRule(".mailto").enabled = client.prefs["munger.mailto"]; display(e.message); client.munger.getRule(".mailto").enabled = false; } function cmdInvite(e) { var channel; if (!e.channelName) { channel = e.channel; } else { channel = e.server.getChannel(e.channelName); if (!channel) { display(getMsg(MSG_ERR_UNKNOWN_CHANNEL, e.channelName), MT_ERROR); return; } } channel.invite(e.nickname); } function cmdKick(e) { if (!e.user) e.user = e.channel.getUser(e.nickname); if (!e.user) { display(getMsg(MSG_ERR_UNKNOWN_USER, e.nickname), MT_ERROR); return; } if (e.command.name == "kick-ban") e.sourceObject.dispatch("ban", { nickname: e.user.encodedName }); e.user.kick(e.reason); } function cmdKnock(e) { var rest = (e.reason ? " :" + fromUnicode(e.reason, e.server) : "") + "\n"; e.server.sendData("KNOCK " + fromUnicode(e.channelName, e.server) + rest); } function cmdClient(e) { dispatch("create-tab-for-view", { view: client }); if (!client.messages) { client.display(MSG_CLIENT_OPENED); dispatch("set-current-view", { view: client }); client.display(MSG_WELCOME, "HELLO"); dispatch("networks"); dispatch("commands"); } else { dispatch("set-current-view", { view: client }); } } function cmdNotify(e) { var net = e.network; if (!e.nickname) { if (net.prefs["notifyList"].length > 0) { /* delete the lists and force a ISON check, this will * print the current online/offline status when the server * responds */ delete net.onList; delete net.offList; onNotifyTimeout(); } else { display(MSG_NO_NOTIFY_LIST); } } else { var adds = new Array(); var subs = new Array(); for (var i in e.nicknameList) { var nickname = e.server.toLowerCase(e.nicknameList[i]); var list = net.prefs["notifyList"]; list = e.server.toLowerCase(list.join(";")).split(";"); var idx = arrayIndexOf (list, nickname); if (idx == -1) { net.prefs["notifyList"].push (nickname); adds.push(nickname); } else { arrayRemoveAt (net.prefs["notifyList"], idx); subs.push(nickname); } } net.prefs["notifyList"].update(); var msgname; if (adds.length > 0) { msgname = (adds.length == 1) ? MSG_NOTIFY_ADDONE : MSG_NOTIFY_ADDSOME; display(getMsg(msgname, arraySpeak(adds))); } if (subs.length > 0) { msgname = (subs.length == 1) ? MSG_NOTIFY_DELONE : MSG_NOTIFY_DELSOME; display(getMsg(msgname, arraySpeak(subs))); } delete net.onList; delete net.offList; onNotifyTimeout(); } } function cmdStalk(e) { var list = client.prefs["stalkWords"]; if (!e.text) { if (list.length == 0) display(MSG_NO_STALK_LIST); else { function alphabetize(a, b) { var A = a.toLowerCase(); var B = b.toLowerCase(); if (A < B) return -1; if (B < A) return 1; return 0; } list.sort(alphabetize); display(getMsg(MSG_STALK_LIST, list.join(MSG_COMMASP))); } return; } var notStalkingWord = true; var loweredText = e.text.toLowerCase(); for (var i = 0; i < list.length; ++i) if (list[i].toLowerCase() == loweredText) notStalkingWord = false; if (notStalkingWord) { list.push(e.text); list.update(); display(getMsg(MSG_STALK_ADD, e.text)); } else { display(getMsg(MSG_STALKING_ALREADY, e.text)); } } function cmdUnstalk(e) { e.text = e.text.toLowerCase(); var list = client.prefs["stalkWords"]; for (var i = 0; i < list.length; ++i) { if (list[i].toLowerCase() == e.text) { list.splice(i, 1); list.update(); display(getMsg(MSG_STALK_DEL, e.text)); return; } } display(getMsg(MSG_ERR_UNKNOWN_STALK, e.text), MT_ERROR); } function cmdUser(e) { dispatch("name", {username: e.username, network: e.network, isInteractive: e.isInteractive}); dispatch("desc", {description: e.description, network: e.network, isInteractive: e.isInteractive}); } function cmdUserhost(e) { var nickList = combineNicks(e.nicknameList, 5); for (var i = 0; i < nickList.length; i++) { e.server.userhost(nickList[i]); } } function cmdUserip(e) { // Check if the server supports this if (!e.server.servCmds.userip) { display(getMsg(MSG_ERR_UNSUPPORTED_COMMAND, "USERIP"), MT_ERROR); return; } var nickList = combineNicks(e.nicknameList, 5); for (var i = 0; i < nickList.length; i++) e.server.userip(nickList[i]); } function cmdUsermode(e) { if (e.newMode) { if (e.sourceObject.network) e.sourceObject.network.prefs["usermode"] = e.newMode; else client.prefs["usermode"] = e.newMode; } else { if (e.server && e.server.isConnected) { e.server.sendData("mode " + e.server.me.encodedName + "\n"); } else { var prefs; if (e.network) prefs = e.network.prefs; else prefs = client.prefs; display(getMsg(MSG_USER_MODE, [prefs["nickname"], prefs["usermode"]]), MT_MODE); } } } function cmdLog(e) { var view = e.sourceObject; if (e.state != null) { e.state = getToggle(e.state, view.prefs["log"]) view.prefs["log"] = e.state; } else { if (view.prefs["log"]) display(getMsg(MSG_LOGGING_ON, getLogPath(view))); else display(MSG_LOGGING_OFF); } } function cmdSave(e) { var OutputProgressListener = { onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) { // Use this to access onStateChange flags var requestSpec; try { var channel = aRequest.QueryInterface(nsIChannel); requestSpec = channel.URI.spec; } catch (ex) { } // Detect end of file saving of any file: if (aStateFlags & nsIWebProgressListener.STATE_STOP) { if (aStatus == kErrorBindingAborted) aStatus = 0; // We abort saving for all errors except if image src file is // not found var abortSaving = (aStatus != 0 && aStatus != kFileNotFound); if (abortSaving) { // Cancel saving wbp.cancelSave(); display(getMsg(MSG_SAVE_ERR_FAILED, aMessage), MT_ERROR); return; } if (aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK && wbp.currentState == nsIWBP.PERSIST_STATE_FINISHED) { // Let the user know: pm = [e.sourceObject.viewName, e.filename]; display(getMsg(MSG_SAVE_SUCCESSFUL, pm), MT_INFO); } /* Check if we've finished. WebBrowserPersist screws up when we * don't save additional files. Cope when saving html only or * text. */ else if (!requestSpec && saveType > 0) { if (wbp) wbp.progressListener = null; pm = [e.sourceObject.viewName, e.filename]; display(getMsg(MSG_SAVE_SUCCESSFUL, pm), MT_INFO); } } }, onProgressChange: function(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress) {}, onLocationChange: function(aWebProgress, aRequest, aLocation) {}, onStatusChange: function(aWebProgress, aRequest, aStatus, aMessage) {}, onSecurityChange: function(aWebProgress, aRequest, state) {}, QueryInterface: function(aIID) { if (aIID.equals(Components.interfaces.nsIWebProgressListener) || aIID.equals(Components.interfaces.nsISupports) || aIID.equals(Components.interfaces.nsISupportsWeakReference)) { return this; } throw Components.results.NS_NOINTERFACE; } }; const kFileNotFound = 2152857618; const kErrorBindingAborted = 2152398850; const nsIWBP = Components.interfaces.nsIWebBrowserPersist; const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener; const nsIChannel = Components.interfaces.nsIChannel; var wbp = newObject("@mozilla.org/embedding/browser/nsWebBrowserPersist;1", nsIWBP); wbp.progressListener = OutputProgressListener; var file, saveType, saveFolder, docToBeSaved, title; var flags, fileType, charLimit; var dialogTitle, rv, pm; // We want proper descriptions and no "All Files" option. const TYPELIST = [[MSG_SAVE_COMPLETEVIEW,"*.htm;*.html"], [MSG_SAVE_HTMLONLYVIEW,"*.htm;*.html"], [MSG_SAVE_PLAINTEXTVIEW,"*.txt"], "$noAll"]; // constants and variables for the wbp.saveDocument call var saveTypes = { complete: 0, htmlonly: 1, text: 2 }; if (!e.filename) { dialogTitle = getMsg(MSG_SAVE_DIALOGTITLE, e.sourceObject.viewName); rv = pickSaveAs(dialogTitle, TYPELIST, e.sourceObject.viewName + ".html"); if (!rv.ok) return; saveType = rv.picker.filterIndex; file = rv.file; e.filename = rv.file.path; } else { try { // Try to use this as a path file = nsLocalFile(e.filename); } catch (ex) { // try to use it as an url try { file = getFileFromURLSpec(e.filename); } catch(ex) { // What is the user thinking? It's not rocket science... display(getMsg(MSG_SAVE_ERR_INVALID_PATH, e.filename), MT_ERROR); return; } } // Get extension and determine savetype if (!e.savetype) { var extension = file.path.substr(file.path.lastIndexOf(".")); if (extension == ".txt") { saveType = saveTypes["text"]; } else if (extension.match(/\.x?html?$/)) { saveType = saveTypes["complete"]; } else { // No saveType and no decent extension --> out! var errMsg; if (extension.indexOf(".") < 0) errMsg = MSG_SAVE_ERR_NO_EXT; else errMsg = getMsg(MSG_SAVE_ERR_INVALID_EXT, extension); display(errMsg, MT_ERROR); return; } } else { if (!(e.savetype in saveTypes)) { // no valid saveType display(getMsg(MSG_SAVE_ERR_INVALID_SAVETYPE, e.savetype), MT_ERROR); return; } saveType = saveTypes[e.savetype]; } var askforreplace = (e.isInteractive && file.exists()); if (askforreplace && !confirm(getMsg(MSG_SAVE_FILEEXISTS, e.filename))) return; } // We don't want to convert anything, leave everything as is and replace // old files, as the user has been prompted about that already. wbp.persistFlags |= nsIWBP.PERSIST_FLAGS_NO_CONVERSION | nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES | nsIWBP.PERSIST_FLAGS_NO_BASE_TAG_MODIFICATIONS | nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES | nsIWBP.PERSIST_FLAGS_DONT_FIXUP_LINKS | nsIWBP.PERSIST_FLAGS_DONT_CHANGE_FILENAMES; // Set the document from the current view, and set a usable title docToBeSaved = getContentDocument(e.sourceObject.frame); var headElement = docToBeSaved.getElementsByTagName("HEAD")[0]; var titleElements = docToBeSaved.getElementsByTagName("title"); // Remove an existing title, there shouldn't be more than one. if (titleElements.length > 0) titleElements[0].parentNode.removeChild(titleElements[0]); title = docToBeSaved.createElement("title"); title.appendChild(docToBeSaved.createTextNode(document.title + " (" + new Date() + ")")); headElement.appendChild(title); // Set standard flags, file type, saveFolder and character limit flags = nsIWBP.ENCODE_FLAGS_ENCODE_BASIC_ENTITIES; fileType = "text/html"; saveFolder = null; charLimit = 0; // Do saveType specific stuff switch (saveType) { case saveTypes["complete"]: // Get the directory into which to save associated files. saveFolder = file.clone(); var baseName = saveFolder.leafName; baseName = baseName.substring(0, baseName.lastIndexOf(".")); saveFolder.leafName = getMsg(MSG_SAVE_FILES_FOLDER, baseName); break; // html only does not need any additional configuration case saveTypes["text"]: // set flags for Plain Text flags = nsIWBP.ENCODE_FLAGS_FORMATTED; flags |= nsIWBP.ENCODE_FLAGS_ABSOLUTE_LINKS; flags |= nsIWBP.ENCODE_FLAGS_NOFRAMES_CONTENT; // set the file type and set character limit to 80 fileType = "text/plain"; charLimit = 80; break; } try { wbp.saveDocument(docToBeSaved, file, saveFolder, fileType, flags, charLimit); } catch (ex) { pm = [e.sourceObject.viewName, e.filename, ex.message]; display(getMsg(MSG_SAVE_ERR_FAILED, pm), MT_ERROR); } // Error handling and finishing message is done by the listener } function cmdSupports(e) { var server = e.server; var data = server.supports; if ("channelTypes" in server) display(getMsg(MSG_SUPPORTS_CHANTYPES, server.channelTypes.join(MSG_COMMASP))); if ("channelModes" in server) { display(getMsg(MSG_SUPPORTS_CHANMODESA, server.channelModes.a.join(MSG_COMMASP))); display(getMsg(MSG_SUPPORTS_CHANMODESB, server.channelModes.b.join(MSG_COMMASP))); display(getMsg(MSG_SUPPORTS_CHANMODESC, server.channelModes.c.join(MSG_COMMASP))); display(getMsg(MSG_SUPPORTS_CHANMODESD, server.channelModes.d.join(MSG_COMMASP))); } if ("userModes" in server) { var list = new Array(); for (var m in server.userModes) { list.push(getMsg(MSG_SUPPORTS_USERMODE, [ server.userModes[m].mode, server.userModes[m].symbol ])); } display(getMsg(MSG_SUPPORTS_USERMODES, list.join(MSG_COMMASP))); } var listB1 = new Array(); var listB2 = new Array(); var listN = new Array(); for (var k in data) { if (typeof data[k] == "boolean") { if (data[k]) listB1.push(k); else listB2.push(k); } else { listN.push(getMsg(MSG_SUPPORTS_MISCOPTION, [ k, data[k] ] )); } } listB1.sort(); listB2.sort(); listN.sort(); display(getMsg(MSG_SUPPORTS_FLAGSON, listB1.join(MSG_COMMASP))); display(getMsg(MSG_SUPPORTS_FLAGSOFF, listB2.join(MSG_COMMASP))); display(getMsg(MSG_SUPPORTS_MISCOPTIONS, listN.join(MSG_COMMASP))); } function cmdDoCommand(e) { if (e.cmdName == "cmd_mozillaPrefs") { goPreferences('navigator', 'chrome://chatzilla/content/prefpanel/pref-irc.xul', 'navigator'); } else if (e.cmdName == "cmd_chatzillaPrefs") { window.openDialog('chrome://chatzilla/content/config.xul', '', 'chrome,resizable,dialog=no', window); } else { doCommand(e.cmdName); } } function cmdTime(e) { if (e.nickname) e.network.dispatch("ctcp", { target: e.nickname, code: "TIME"}); else e.server.sendData(fromUnicode("TIME") + "\n", e.sourceObject); } function cmdTimestamps(e) { var view = e.sourceObject; if (e.toggle != null) { e.toggle = getToggle(e.toggle, view.prefs["timestamps"]) view.prefs["timestamps"] = e.toggle; } else { display(getMsg(MSG_FMT_PREF, ["timestamps", view.prefs["timestamps"]])); } } function cmdSetCurrentView(e) { if ("lockView" in e.view) delete e.view.lockView; setCurrentObject(e.view); } function cmdIgnore(e) { if (("mask" in e) && e.mask) { e.mask = e.server.toLowerCase(e.mask); if (e.command.name == "ignore") { if (e.network.ignore(e.mask)) display(getMsg(MSG_IGNORE_ADD, e.mask)); else display(getMsg(MSG_IGNORE_ADDERR, e.mask)); } else { if (e.network.unignore(e.mask)) display(getMsg(MSG_IGNORE_DEL, e.mask)); else display(getMsg(MSG_IGNORE_DELERR, e.mask)); } // Update pref: var ignoreList = keys(e.network.ignoreList); e.network.prefs["ignoreList"] = ignoreList; e.network.prefs["ignoreList"].update(); } else { var list = new Array(); for (var m in e.network.ignoreList) list.push(m); if (list.length == 0) display(MSG_IGNORE_LIST_1); else display(getMsg(MSG_IGNORE_LIST_2, arraySpeak(list))); } } function cmdFont(e) { var view = client; var pref, val, pVal; if (e.command.name == "font-family") { pref = "font.family"; val = e.font; // Save new value, then display pref value. if (val) view.prefs[pref] = val; display(getMsg(MSG_FONTS_FAMILY_FMT, view.prefs[pref])); } else if (e.command.name == "font-size") { pref = "font.size"; val = e.fontSize; // Ok, we've got an input. if (val) { // Get the current value, use user's default if needed. pVal = view.prefs[pref]; if (pVal == 0) pVal = getDefaultFontSize(); // Handle user's input... switch(val) { case "default": val = 0; break; case "small": val = getDefaultFontSize() - 2; break; case "medium": val = getDefaultFontSize(); break; case "large": val = getDefaultFontSize() + 2; break; case "smaller": val = pVal - 2; break; case "bigger": val = pVal + 2; break; default: val = Number(val); } // Save the new value. view.prefs[pref] = val; } // Show the user what the pref is set to. if (view.prefs[pref] == 0) display(MSG_FONTS_SIZE_DEFAULT); else display(getMsg(MSG_FONTS_SIZE_FMT, view.prefs[pref])); } else if (e.command.name == "font-family-other") { val = prompt(MSG_FONTS_FAMILY_PICK, view.prefs["font.family"]); if (!val) return; dispatch("font-family", { font: val }); } else if (e.command.name == "font-size-other") { pVal = view.prefs["font.size"]; if (pVal == 0) pVal = getDefaultFontSize(); val = prompt(MSG_FONTS_SIZE_PICK, pVal); if (!val) return; dispatch("font-size", { fontSize: val }); } } function cmdDCCChat(e) { if (!jsenv.HAS_SERVER_SOCKETS) return display(MSG_DCC_NOT_POSSIBLE); if (!client.prefs["dcc.enabled"]) return display(MSG_DCC_NOT_ENABLED); if (!e.nickname && !e.user) return display(MSG_DCC_ERR_NOUSER); var user; if (e.nickname) user = e.server.addUser(e.nickname); else user = e.server.addUser(e.user.unicodeName); var u = client.dcc.addUser(user); var c = client.dcc.addChat(u, client.dcc.getNextPort()); c.request(); client.munger.getRule(".inline-buttons").enabled = true; var cmd = getMsg(MSG_DCC_COMMAND_CANCEL, "dcc-close " + c.id); display(getMsg(MSG_DCCCHAT_SENT_REQUEST, c._getParams().concat(cmd)), "DCC-CHAT"); client.munger.getRule(".inline-buttons").enabled = false; return true; } function cmdDCCClose(e) { if (!jsenv.HAS_SERVER_SOCKETS) return display(MSG_DCC_NOT_POSSIBLE); if (!client.prefs["dcc.enabled"]) return display(MSG_DCC_NOT_ENABLED); // If there is no nickname specified, use current view. if (!e.nickname) { if (client.currentObject.TYPE == "IRCDCCChat") return client.currentObject.abort(); // ...if there is one. return display(MSG_DCC_ERR_NOCHAT); } var o = client.dcc.findByID(e.nickname); if (o) // Direct ID --> object request. return o.abort(); if (e.type) e.type = [e.type.toLowerCase()]; else e.type = ["chat", "file"]; // Go ask the DCC code for some matching requets. var list = client.dcc.getMatches (e.nickname, e.file, e.type, [DCC_DIR_GETTING, DCC_DIR_SENDING], [DCC_STATE_REQUESTED, DCC_STATE_ACCEPTED, DCC_STATE_CONNECTED]); // Disconnect if only one match. if (list.length == 1) return list[0].abort(); // Oops, couldn't figure the user's requets out, so give them some help. display(getMsg(MSG_DCC_ACCEPTED_MATCHES, [list.length])); display(MSG_DCC_MATCHES_HELP); return true; } function cmdDCCSend(e) { if (!jsenv.HAS_SERVER_SOCKETS) return display(MSG_DCC_NOT_POSSIBLE); if (!client.prefs["dcc.enabled"]) return display(MSG_DCC_NOT_ENABLED); const DIRSVC_CID = "@mozilla.org/file/directory_service;1"; const nsIProperties = Components.interfaces.nsIProperties; if (!e.nickname && !e.user) return display(MSG_DCC_ERR_NOUSER); // Accept the request passed in... var file; if (!e.file) { var pickerRv = pickOpen(MSG_DCCFILE_SEND); if (!pickerRv.ok) return false; file = pickerRv.file; } else { // Wrap in try/catch because nsILocalFile creation throws a freaking // error if it doesn't get a FULL path. try { file = nsLocalFile(e.file); } catch(ex) { // Ok, try user's home directory. var fl = Components.classes[DIRSVC_CID].getService(nsIProperties); file = fl.get("Home", Components.interfaces.nsILocalFile); // Another freaking try/catch wrapper. try { // NOTE: This is so pathetic it can't cope with any path // separators in it, so don't even THINK about lobing a // relative path at it. file.append(e.file); // Wow. We survived. } catch (ex) { return display(MSG_DCCFILE_ERR_NOTFOUND); } } } if (!file.exists()) return display(MSG_DCCFILE_ERR_NOTFOUND); if (!file.isFile()) return display(MSG_DCCFILE_ERR_NOTAFILE); if (!file.isReadable()) return display(MSG_DCCFILE_ERR_NOTREADABLE); var user; if (e.nickname) user = e.server.addUser(e.nickname); else user = e.server.addUser(e.user.unicodeName); var u = client.dcc.addUser(user); var c = client.dcc.addFileTransfer(u, client.dcc.getNextPort()); c.request(file); client.munger.getRule(".inline-buttons").enabled = true; var cmd = getMsg(MSG_DCC_COMMAND_CANCEL, "dcc-close " + c.id); display(getMsg(MSG_DCCFILE_SENT_REQUEST, [c.user.unicodeName, c.localIP, c.port, c.filename, getSISize(c.size), cmd]), "DCC-FILE"); client.munger.getRule(".inline-buttons").enabled = false; return true; } function cmdDCCList(e) { if (!jsenv.HAS_SERVER_SOCKETS) return display(MSG_DCC_NOT_POSSIBLE); if (!client.prefs["dcc.enabled"]) return display(MSG_DCC_NOT_ENABLED); var counts = { pending: 0, connected: 0, failed: 0 }; var k; // Get all the DCC sessions. var list = client.dcc.getMatches(); for (k = 0; k < list.length; k++) { var c = list[k]; var type = c.TYPE.substr(6, c.TYPE.length - 6); var dir = MSG_UNKNOWN; var tf = MSG_UNKNOWN; if (c.state.dir == DCC_DIR_SENDING) { dir = MSG_DCCLIST_DIR_OUT; tf = MSG_DCCLIST_TO; } else if (c.state.dir == DCC_DIR_GETTING) { dir = MSG_DCCLIST_DIR_IN; tf = MSG_DCCLIST_FROM; } var state = MSG_UNKNOWN; var cmds = ""; switch (c.state.state) { case DCC_STATE_REQUESTED: state = MSG_DCC_STATE_REQUEST; if (c.state.dir == DCC_DIR_GETTING) { cmds = getMsg(MSG_DCC_COMMAND_ACCEPT, "dcc-accept " + c.id) + " " + getMsg(MSG_DCC_COMMAND_DECLINE, "dcc-decline " + c.id); } else { cmds = getMsg(MSG_DCC_COMMAND_CANCEL, "dcc-close " + c.id); } counts.pending++; break; case DCC_STATE_ACCEPTED: state = MSG_DCC_STATE_ACCEPT; counts.connected++; break; case DCC_STATE_DECLINED: state = MSG_DCC_STATE_DECLINE; break; case DCC_STATE_CONNECTED: state = MSG_DCC_STATE_CONNECT; cmds = getMsg(MSG_DCC_COMMAND_CLOSE, "dcc-close " + c.id); if (c.TYPE == "IRCDCCFileTransfer") { state = getMsg(MSG_DCC_STATE_CONNECTPRO, [Math.floor(100 * c.position / c.size), getSISize(c.position), getSISize(c.size), getSISpeed(c.speed)]); } counts.connected++; break; case DCC_STATE_DONE: state = MSG_DCC_STATE_DISCONNECT; break; case DCC_STATE_ABORTED: state = MSG_DCC_STATE_ABORT; counts.failed++; break; case DCC_STATE_FAILED: state = MSG_DCC_STATE_FAIL; counts.failed++; break; } client.munger.getRule(".inline-buttons").enabled = true; display(getMsg(MSG_DCCLIST_LINE, [k + 1, state, dir, type, tf, c.unicodeName, c.remoteIP, c.port, cmds])); client.munger.getRule(".inline-buttons").enabled = false; } display(getMsg(MSG_DCCLIST_SUMMARY, [counts.pending, counts.connected, counts.failed])); return true; } function cmdDCCAutoAcceptList(e) { if (!jsenv.HAS_SERVER_SOCKETS) return display(MSG_DCC_NOT_POSSIBLE); if (!client.prefs["dcc.enabled"]) return display(MSG_DCC_NOT_ENABLED); var list = e.network.prefs["dcc.autoAccept.list"]; if (list.length == 0) display(MSG_DCCACCEPT_DISABLED); else display(getMsg(MSG_DCCACCEPT_LIST, arraySpeak(list))); return true; } function cmdDCCAutoAcceptAdd(e) { if (!jsenv.HAS_SERVER_SOCKETS) return display(MSG_DCC_NOT_POSSIBLE); if (!client.prefs["dcc.enabled"]) return display(MSG_DCC_NOT_ENABLED); var list = e.network.prefs["dcc.autoAccept.list"]; if (!e.user && e.server) e.user = e.server.getUser(e.nickname); var mask = e.user ? "*!" + e.user.name + "@" + e.user.host : e.nickname; var index = arrayIndexOf(list, mask); if (index == -1) { list.push(mask); list.update(); display(getMsg(MSG_DCCACCEPT_ADD, mask)); } else { display(getMsg(MSG_DCCACCEPT_ADDERR, e.user ? e.user.unicodeName : e.nickname)); } return true; } function cmdDCCAutoAcceptDel(e) { if (!jsenv.HAS_SERVER_SOCKETS) return display(MSG_DCC_NOT_POSSIBLE); if (!client.prefs["dcc.enabled"]) return display(MSG_DCC_NOT_ENABLED); var list = e.network.prefs["dcc.autoAccept.list"]; if (!e.user && e.server) e.user = e.server.getUser(e.nickname); var maskObj, newList = new Array(); for (var m = 0; m < list.length; ++m) { maskObj = getHostmaskParts(list[m]); if (e.nickname == list[m] || (e.user && hostmaskMatches(e.user, maskObj, e.server))) { display(getMsg(MSG_DCCACCEPT_DEL, list[m])); } else { newList.push(list[m]); } } if (list.length > newList.length) e.network.prefs["dcc.autoAccept.list"] = newList; else display(getMsg(MSG_DCCACCEPT_DELERR, e.user ? e.user.unicodeName : e.nickname)); return true; } function cmdDCCAccept(e) { if (!jsenv.HAS_SERVER_SOCKETS) return display(MSG_DCC_NOT_POSSIBLE); if (!client.prefs["dcc.enabled"]) return display(MSG_DCC_NOT_ENABLED); function accept(c) { if (c.TYPE == "IRCDCCChat") { if (!c.accept()) return false; display(getMsg(MSG_DCCCHAT_ACCEPTED, c._getParams()), "DCC-CHAT"); return true; } // Accept the request passed in... var filename = c.filename; var ext = "*"; var m = filename.match(/...\.([a-z]+)$/i); if (m) ext = "*." + m[1]; var pickerRv = pickSaveAs(getMsg(MSG_DCCFILE_SAVE_TO, filename), ["$all", ext], filename); if (!pickerRv.ok) return false; if (!c.accept(pickerRv.file)) return false; display(getMsg(MSG_DCCFILE_ACCEPTED, c._getParams()), "DCC-FILE"); return true; }; // If there is no nickname specified, use the "last" item. // This is the last DCC request that arrvied. if (!e.nickname && client.dcc.last) { if ((new Date() - client.dcc.lastTime) >= 10000) return accept(client.dcc.last); return display(MSG_DCC_ERR_ACCEPT_TIME); } var o = client.dcc.findByID(e.nickname); if (o) // Direct ID --> object request. return accept(o); if (e.type) e.type = [e.type.toLowerCase()]; else e.type = ["chat", "file"]; // Go ask the DCC code for some matching requets. var list = client.dcc.getMatches(e.nickname, e.file, e.type, [DCC_DIR_GETTING], [DCC_STATE_REQUESTED]); // Accept if only one match. if (list.length == 1) return accept(list[0]); // Oops, couldn't figure the user's request out, so give them some help. display(getMsg(MSG_DCC_PENDING_MATCHES, [list.length])); display(MSG_DCC_MATCHES_HELP); return true; } function cmdDCCDecline(e) { if (!jsenv.HAS_SERVER_SOCKETS) return display(MSG_DCC_NOT_POSSIBLE); if (!client.prefs["dcc.enabled"]) return display(MSG_DCC_NOT_ENABLED); function decline(c) { // Decline the request passed in... c.decline(); if (c.TYPE == "IRCDCCChat") display(getMsg(MSG_DCCCHAT_DECLINED, c._getParams()), "DCC-CHAT"); else display(getMsg(MSG_DCCFILE_DECLINED, c._getParams()), "DCC-FILE"); }; // If there is no nickname specified, use the "last" item. // This is the last DCC request that arrvied. if (!e.nickname && client.dcc.last) return decline(client.dcc.last); var o = client.dcc.findByID(e.nickname); if (o) // Direct ID --> object request. return decline(o); if (!e.type) e.type = ["chat", "file"]; // Go ask the DCC code for some matching requets. var list = client.dcc.getMatches(e.nickname, e.file, e.type, [DCC_DIR_GETTING], [DCC_STATE_REQUESTED]); // Decline if only one match. if (list.length == 1) return decline(list[0]); // Oops, couldn't figure the user's requets out, so give them some help. display(getMsg(MSG_DCC_PENDING_MATCHES, [list.length])); display(MSG_DCC_MATCHES_HELP); return true; } function cmdTextDirection(e) { var direction; var sourceObject = getContentDocument(e.sourceObject.frame).body; switch (e.dir) { case "toggle": if (sourceObject.getAttribute("dir") == "rtl") direction = 'ltr'; else direction = 'rtl'; break; case "rtl": direction = 'rtl'; break; default: // that is "case "ltr":", // but even if !e.dir OR e.dir is an invalid value -> set to // default direction direction = 'ltr'; } client.input.setAttribute("dir", direction); sourceObject.setAttribute("dir", direction); return true; } function cmdInputTextDirection(e) { var direction; switch (e.dir) { case "rtl": client.input.setAttribute("dir", "rtl"); break default: // that is "case "ltr":", but even if !e.dir OR e.dir is an //invalid value -> set to default direction client.input.setAttribute("dir", "ltr"); } return true; } function cmdFind(e) { if (!e.rest) { findInPage(getFindData(e)); return; } // Used from the inputbox, set the search string and find the first // occurrence using find-again. const FINDSVC_ID = "@mozilla.org/find/find_service;1"; var findService = getService(FINDSVC_ID, "nsIFindService"); // Make sure it searches the entire document, but don't lose the old setting var oldWrap = findService.wrapFind; findService.wrapFind = true; findService.searchString = e.rest; findAgainInPage(getFindData(e)); // Restore wrap setting: findService.wrapFind = oldWrap; } function cmdFindAgain(e) { if (canFindAgainInPage()) findAgainInPage(getFindData(e)); } function cmdURLs(e) { if (client.prefs["urls.list"].length == 0) { display(MSG_URLS_NONE); } else { /* Store the current URL list, so we can put it back afterwards. This * is needed because the process of displaying the list changes the * list! (think about it for a second) */ var oldList = client.prefs["urls.list"]; client.prefs["urls.list"] = new Array(); var num = e.number || client.prefs["urls.display"]; if (num > oldList.length) num = oldList.length; display(getMsg(MSG_URLS_HEADER, num)); for (var i = 0; i < num; i++) display(getMsg(MSG_URLS_ITEM, [i + 1, oldList[i]])); // Restore old URL list so displaying it has no effect. client.prefs["urls.list"] = oldList; } } PK `u7jjcontent/chatzilla/handlers.js/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is ChatZilla. * * The Initial Developer of the Original Code is * New Dimensions Consulting, Inc. * Portions created by the Initial Developer are Copyright (C) 1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Robert Ginda, rginda@netscape.com, original author * Samuel Sieb, samuel@sieb.net * Chiaki Koufugata chiaki@mozilla.gr.jp UI i18n * Shawn Wilsher * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ window.onresize = function onresize() { for (var i = 0; i < client.deck.childNodes.length; i++) scrollDown(client.deck.childNodes[i], true); } function onInputFocus() { } function showErrorDlg(message) { const XUL_MIME = "application/vnd.mozilla.xul+xml"; const XUL_KEY = "http://www.mozilla.org/keymaster/" + "gatekeeper/there.is.only.xul"; const TITLE = "ChatZilla run-time error"; const HEADER = "There was a run-time error with ChatZilla. " + "Please report the following information:"; const OL_JS = "document.getElementById('tb').value = '%S';"; const TB_STYLE = ' multiline="true" readonly="true"' + ' style="width: 60ex; height: 20em;"'; const ERROR_DLG = '' + '' + '' + '' + ''; var content = message.replace(/\n/g, "\\n"); content = content.replace(/'/g, "\\'").replace(/"/g, """); content = content.replace(//g, ">"); content = ERROR_DLG.replace("%S", content); content = encodeURIComponent(content); content = "data:" + XUL_MIME + "," + content; setTimeout(function() { window.openDialog(content, "_blank", "chrome,modal"); }, 100); } function onLoad() { dd ("Initializing ChatZilla {"); try { init(); } catch (ex) { dd("caught exception while initializing:\n" + dumpObjectTree(ex)); showErrorDlg(formatException(ex) + "\n" + dumpObjectTree(ex)); } dd("}"); mainStep(); } function initHandlers() { var node; node = document.getElementById("input"); node.addEventListener("keypress", onInputKeyPress, false); node = document.getElementById("multiline-input"); node.addEventListener("keypress", onMultilineInputKeyPress, false); node.active = false; node = document.getElementById("security-button"); node.addEventListener("dblclick", onSecurityIconDblClick, false); window.onkeypress = onWindowKeyPress; window.isFocused = false; window.addEventListener("focus", onWindowFocus, true); window.addEventListener("blur", onWindowBlue, true); client.inputPopup = null; // Should fail silently pre-moz1.4 doCommandWithParams("cmd_clipboardDragDropHook", {addhook: CopyPasteHandler}); } function onClose() { if ("userClose" in client && client.userClose) return true; // if we're not connected to anything, just close the window if (!("getConnectionCount" in client) || (client.getConnectionCount() == 0)) return true; /* otherwise, try to close out gracefully */ client.wantToQuit(); return false; } function onUnload() { dd("Shutting down ChatZilla."); uninitOfflineIcon(); destroy(); } function onNotImplemented() { alert (getMsg("onNotImplementedMsg")); } /* tab click */ function onTabClick(e, id) { if (e.which != 2) return; var tbi = document.getElementById(id); var view = client.viewsArray[tbi.getAttribute("viewKey")]; if (e.which == 2) { dispatch("hide", { view: view.source }); return; } } function onTabSelect(e) { var tabs = e.target; /* Hackaround, bug 314230. XBL likes focusing a tab before onload has fired. * That means the tab we're trying to select here will be the hidden one, * which doesn't have a viewKey. We catch that case. */ if (!tabs.selectedItem.hasAttribute("viewKey")) return; var key = tabs.selectedItem.getAttribute("viewKey"); var view = client.viewsArray[key]; dispatch("set-current-view", {view:view.source}); } function onMessageViewClick(e) { if ((e.which != 1) && (e.which != 2)) return true; var cx = getMessagesContext(null, e.target); var command = getEventCommand(e); if (!client.commandManager.isCommandSatisfied(cx, command)) return false; dispatch(command, cx); dispatch("focus-input"); e.preventDefault(); return true; } function onMessageViewMouseDown(e) { if ((typeof startScrolling != "function") || ((e.which != 1) && (e.which != 2))) { return true; } var cx = getMessagesContext(null, e.target); var command = getEventCommand(e); if (!client.commandManager.isCommandSatisfied(cx, command)) startScrolling(e); return true; } function getEventCommand(e) { if (e.which == 2) return client.prefs["messages.middleClick"]; if (e.metaKey || e.altKey) return client.prefs["messages.metaClick"]; if (e.ctrlKey) return client.prefs["messages.ctrlClick"]; return client.prefs["messages.click"]; } function onMouseOver (e) { var i = 0; var target = e.target; var status = ""; while (!status && target && i < 5) { if ("getAttribute" in target) { status = target.getAttribute("href"); if (!status) status = target.getAttribute ("statusText"); } ++i; target = target.parentNode; } if (status) { client.status = status; } else { if (client && "defaultStatus" in client) client.status = client.defaultStatus; } } function onSecurityIconDblClick(e) { if (e.button == 0) displayCertificateInfo(); } function onMultilineInputKeyPress (e) { if ((e.ctrlKey || e.metaKey) && e.keyCode == 13) { /* meta-enter, execute buffer */ onMultilineSend(e); } else { if ((e.ctrlKey || e.metaKey) && e.keyCode == 40) { /* ctrl/meta-down, switch to single line mode */ dispatch ("pref multiline false"); } } } function onMultilineSend(e) { var multiline = document.getElementById("multiline-input"); e.line = multiline.value; if (e.line.search(/\S/) == -1) return; onInputCompleteLine (e); multiline.value = ""; } function onTooltip(event) { const XLinkNS = "http://www.w3.org/1999/xlink"; var tipNode = event.originalTarget; var titleText = null; var XLinkTitleText = null; var element = document.tooltipNode; while (element) { if (element.nodeType == Node.ELEMENT_NODE) { var text; if (element.hasAttribute("title")) text = element.getAttribute("title"); else if (element.hasAttributeNS(XLinkNS, "title")) text = element.getAttributeNS(XLinkNS, "title"); if (text) { tipNode.setAttribute("label", text); return true; } } element = element.parentNode; } return false; } function onInputKeyPress (e) { if (client.prefs["outgoing.colorCodes"]) setTimeout(onInputKeyPressCallback, 100, e.target); switch (e.keyCode) { case 9: /* tab */ if (!e.ctrlKey && !e.metaKey) { onTabCompleteRequest(e); e.preventDefault(); } break; case 13: /* CR */ e.line = e.target.value; e.target.value = ""; if (e.line.search(/\S/) == -1) return; if (e.ctrlKey) e.line = client.COMMAND_CHAR + "say " + e.line; onInputCompleteLine (e); break; case 37: /* left */ if (e.altKey && e.metaKey) cycleView(-1); break; case 38: /* up */ if (e.ctrlKey || e.metaKey) { /* ctrl/meta-up, switch to multi line mode */ dispatch ("pref multiline true"); } else { if (client.lastHistoryReferenced == -2) { client.lastHistoryReferenced = -1; e.target.value = client.incompleteLine; } else if (client.lastHistoryReferenced < client.inputHistory.length - 1) { e.target.value = client.inputHistory[++client.lastHistoryReferenced]; } } e.preventDefault(); break; case 39: /* right */ if (e.altKey && e.metaKey) cycleView(+1); break; case 40: /* down */ if (client.lastHistoryReferenced > 0) e.target.value = client.inputHistory[--client.lastHistoryReferenced]; else if (client.lastHistoryReferenced == -1) { e.target.value = ""; client.lastHistoryReferenced = -2; } else { client.lastHistoryReferenced = -1; e.target.value = client.incompleteLine; } e.preventDefault(); break; default: client.lastHistoryReferenced = -1; client.incompleteLine = e.target.value; break; } } function onTabCompleteRequest (e) { var elem = document.commandDispatcher.focusedElement; var singleInput = document.getElementById("input"); if (document.getBindingParent(elem) != singleInput) return; var selStart = singleInput.selectionStart; var selEnd = singleInput.selectionEnd; var line = singleInput.value; if (!line) { if ("defaultCompletion" in client.currentObject) singleInput.value = client.currentObject.defaultCompletion; // If there was nothing to complete, help the user: if (!singleInput.value) display(MSG_LEAVE_INPUTBOX, MT_INFO); return; } if (selStart != selEnd) { /* text is highlighted, just move caret to end and exit */ singleInput.selectionStart = singleInput.selectionEnd = line.length; return; } var wordStart = line.substr(0, selStart).search(/\s\S*$/); if (wordStart == -1) wordStart = 0; else ++wordStart; var wordEnd = line.substr(selStart).search(/\s/); if (wordEnd == -1) wordEnd = line.length; else wordEnd += selStart; // Double tab on nothing, inform user how to get out of the input box if (wordEnd == wordStart) { display(MSG_LEAVE_INPUTBOX, MT_INFO); return; } if ("performTabMatch" in client.currentObject) { var word = line.substring(wordStart, wordEnd); var wordLower = word.toLowerCase(); var d = getObjectDetails(client.currentObject); if (d.server) wordLower = d.server.toLowerCase(word); var co = client.currentObject; // We need some special knowledge of how to lower-case strings. var lcFn; if ("getLCFunction" in co) lcFn = co.getLCFunction(); var matches = co.performTabMatch(line, wordStart, wordEnd, wordLower, selStart, lcFn); /* if we get null back, we're supposed to fail silently */ if (!matches) return; var doubleTab = false; var date = new Date(); if ((date - client.lastTabUp) <= client.DOUBLETAB_TIME) doubleTab = true; else client.lastTabUp = date; if (doubleTab) { /* if the user hit tab twice quickly, */ if (matches.length > 0) { /* then list possible completions, */ display(getMsg(MSG_FMT_MATCHLIST, [matches.length, word, matches.join(MSG_COMMASP)])); } else { /* or display an error if there are none. */ display(getMsg(MSG_ERR_NO_MATCH, word), MT_ERROR); } } else if (matches.length >= 1) { var match; if (matches.length == 1) match = matches[0]; else match = getCommonPfx(matches, lcFn); singleInput.value = line.substr(0, wordStart) + match + line.substr(wordEnd); if (wordEnd < line.length) { /* if the word we completed was in the middle if the line * then move the cursor to the end of the completed word. */ var newpos = wordStart + match.length; if (matches.length == 1) { /* word was fully completed, move one additional space */ ++newpos; } singleInput.selectionEnd = e.target.selectionStart = newpos; } } } } function onWindowKeyPress (e) { var code = Number(e.keyCode); var w; var newOfs; var userList = document.getElementById("user-list"); var elemFocused = document.commandDispatcher.focusedElement; switch (code) { case 9: /* tab */ if (e.ctrlKey || e.metaKey) { cycleView(e.shiftKey ? -1: 1); e.preventDefault(); } break; case 33: /* pgup */ if (e.ctrlKey) { cycleView(-1); e.preventDefault(); break; } if (elemFocused == userList) break; w = client.currentFrame; newOfs = w.pageYOffset - (w.innerHeight * 0.75); if (newOfs > 0) w.scrollTo (w.pageXOffset, newOfs); else w.scrollTo (w.pageXOffset, 0); e.preventDefault(); break; case 34: /* pgdn */ if (e.ctrlKey) { cycleView(1); e.preventDefault(); break; } if (elemFocused == userList) break; w = client.currentFrame; newOfs = w.pageYOffset + (w.innerHeight * 0.75); if (newOfs < (w.innerHeight + w.pageYOffset)) w.scrollTo (w.pageXOffset, newOfs); else w.scrollTo (w.pageXOffset, (w.innerHeight + w.pageYOffset)); e.preventDefault(); break; case 117: /* F6 */ advanceKeyboardFocus(e.shiftKey ? -1 : 1); e.preventDefault(); break; } // Code is zero if we have an alphanumeric being given to us in the event. if (code != 0) return; // The following code is copied from: // /mozilla/browser/base/content/browser.js // Revision: 1.748 // Lines: 1397-1421 // \d in a RegExp will find any Unicode character with the "decimal digit" // property (Nd) var regExp = /\d/; if (!regExp.test(String.fromCharCode(e.charCode))) return; // Some Unicode decimal digits are in the range U+xxx0 - U+xxx9 and some are // in the range U+xxx6 - U+xxxF. Find the digit 1 corresponding to our // character. var digit1 = (e.charCode & 0xFFF0) | 1; if (!regExp.test(String.fromCharCode(digit1))) digit1 += 6; var idx = e.charCode - digit1; const isMac = client.platform == "Mac"; const isLinux = client.platform == "Linux"; const isWindows = client.platform == "Windows"; const isOS2 = client.platform == "OS/2"; const isUnknown = !(isMac || isLinux || isWindows || isOS2); const isSuite = client.host == "Mozilla"; if ((0 <= idx) && (idx <= 8)) { if ((!isSuite && isMac && e.metaKey) || (!isSuite && (isLinux || isOS2) && e.altKey) || (!isSuite && (isWindows || isUnknown) && e.ctrlKey) || (isSuite && e.altKey)) { // Pressing 1-8 takes you to that tab, while pressing 9 takes you // to the last tab always. if (idx == 8) idx = client.viewsArray.length - 1; if ((idx in client.viewsArray) && client.viewsArray[idx].source) { var newView = client.viewsArray[idx].source; dispatch("set-current-view", { view: newView }); } e.preventDefault(); return; } } } function onWindowFocus(e) { window.isFocused = true; } function onWindowBlue(e) { window.isFocused = false; } function onInputCompleteLine(e) { if (!client.inputHistory.length || client.inputHistory[0] != e.line) client.inputHistory.unshift (e.line); if (client.inputHistory.length > client.MAX_HISTORY) client.inputHistory.pop(); client.lastHistoryReferenced = -1; client.incompleteLine = ""; if (e.line[0] == client.COMMAND_CHAR) { if (client.prefs["outgoing.colorCodes"]) e.line = replaceColorCodes(e.line); dispatch(e.line.substr(1), null, true); } else /* plain text */ { /* color codes */ if (client.prefs["outgoing.colorCodes"]) e.line = replaceColorCodes(e.line); client.sayToCurrentTarget (e.line); } } function onNotifyTimeout() { /* Workaround: bug 318419 - timers sometimes fire way too quickly. * This catches us out, as it causes the notify code (this) and the who * code (below) to fire continuously, which completely floods the * sendQueue. We work around this for now by reporting the probable * error condition here, but don't attempt to stop it. */ for (var n in client.networks) { var net = client.networks[n]; if (net.isConnected()) { // WORKAROUND BEGIN // if (!("bug318419" in client) && (net.primServ.sendQueue.length >= 1000)) { client.bug318419 = 10; display(MSG_BUG318419_WARNING, MT_WARN); window.getAttention(); return; } else if (("bug318419" in client) && (client.bug318419 > 0) && (net.primServ.sendQueue.length >= (1000 * client.bug318419))) { client.bug318419++; display(MSG_BUG318419_ERROR, MT_ERROR); window.getAttention(); return; } // WORKAROUND END // if (net.prefs["notifyList"].length > 0) { var isonList = client.networks[n].prefs["notifyList"]; net.primServ.sendData ("ISON " + isonList.join(" ") + "\n"); } else { /* if the notify list is empty, just send a ping to see if we're * alive. */ net.primServ.sendData ("PING :ALIVECHECK\n"); } } } } function onWhoTimeout() { function checkWho() { var checkNext = (net.lastWhoCheckChannel == null); for (var c in net.primServ.channels) { var chan = net.primServ.channels[c]; if (checkNext && chan.active && chan.getUsersLength() < client.prefs["autoAwayCap"]) { net.primServ.LIGHTWEIGHT_WHO = true; net.primServ.sendData("WHO " + chan.encodedName + "\n"); net.lastWhoCheckChannel = chan; net.lastWhoCheckTime = Number(new Date()); return; } if (chan == net.lastWhoCheckChannel) checkNext = true; } if (net.lastWhoCheckChannel) { net.lastWhoCheckChannel = null; checkWho(); } }; for (var n in client.networks) { var net = client.networks[n]; var period = net.prefs["autoAwayPeriod"]; // The time since the last check, with a 5s error margin to // stop us from not checking because the timer fired a tad early: var waited = Number(new Date()) - net.lastWhoCheckTime + 5000; if (net.isConnected() && (period != 0) && (period * 60000 < waited)) checkWho(); } } function onInputKeyPressCallback (el) { function doPopup(popup) { if (client.inputPopup && client.inputPopup != popup) client.inputPopup.hidePopup(); client.inputPopup = popup; if (popup) { if (el.nodeName == "textbox") { popup.showPopup(el, -1, -1, "tooltip", "topleft", "bottomleft"); } else { var box = el.ownerDocument.getBoxObjectFor(el); var pos = { x: client.mainWindow.screenX + box.screenX + 5, y: client.mainWindow.screenY + box.screenY + box.height + 25 }; popup.moveTo(pos.x, pos.y); popup.showPopup(el, 0, 0, "tooltip"); } } } var text = " " + el.value.substr(0, el.selectionStart); if (el.selectionStart != el.selectionEnd) text = ""; if (text.match(/[^%]%C[0-9]{0,2},?[0-9]{0,2}$/)) doPopup(document.getElementById("colorTooltip")); else if (text.match(/[^%]%$/)) doPopup(document.getElementById("percentTooltip")); else doPopup(null); } CIRCChannel.prototype._updateConferenceMode = function my_updateconfmode() { const minDiff = client.CONFERENCE_LOW_PASS; var enabled = this.prefs["conference.enabled"]; var userLimit = this.prefs["conference.limit"]; var userCount = this.getUsersLength(); if (userLimit == 0) { // userLimit == 0 --> always off. if (enabled) this.prefs["conference.enabled"] = false; } else if (userLimit == 1) { // userLimit == 1 --> always on. if (!enabled) this.prefs["conference.enabled"] = true; } else if (enabled && (userCount < userLimit - minDiff)) { this.prefs["conference.enabled"] = false; } else if (!enabled && (userCount > userLimit + minDiff)) { this.prefs["conference.enabled"] = true; } } CIRCServer.prototype.CTCPHelpClientinfo = function serv_ccinfohelp() { return MSG_CTCPHELP_CLIENTINFO; } CIRCServer.prototype.CTCPHelpAction = function serv_ccinfohelp() { return MSG_CTCPHELP_ACTION; } CIRCServer.prototype.CTCPHelpTime = function serv_ccinfohelp() { return MSG_CTCPHELP_TIME; } CIRCServer.prototype.CTCPHelpVersion = function serv_ccinfohelp() { return MSG_CTCPHELP_VERSION; } CIRCServer.prototype.CTCPHelpSource = function serv_csrchelp() { return MSG_CTCPHELP_SOURCE; } CIRCServer.prototype.CTCPHelpOs = function serv_oshelp() { return MSG_CTCPHELP_OS; } CIRCServer.prototype.CTCPHelpHost = function serv_hosthelp() { return MSG_CTCPHELP_HOST; } CIRCServer.prototype.CTCPHelpPing = function serv_ccinfohelp() { return MSG_CTCPHELP_PING; } CIRCServer.prototype.CTCPHelpDcc = function serv_ccinfohelp() { return MSG_CTCPHELP_DCC; } /** * Calculates delay before the next automatic connection attempt. * * If the number of connection attempts is limited, use fixed interval * MIN_RECONNECT_MS. For unlimited attempts (-1), use exponential backoff: the * interval between connection attempts to the network (not individual * servers) is doubled after each attempt, up to MAX_RECONNECT_MS. */ CIRCNetwork.prototype.getReconnectDelayMs = function my_getReconnectDelayMs() { var nServers = this.serverList.length; if ((-1 != this.MAX_CONNECT_ATTEMPTS) || (0 != this.connectCandidate % nServers)) { return this.MIN_RECONNECT_MS; } var networkRound = Math.ceil(this.connectCandidate / nServers); var rv = this.MIN_RECONNECT_MS * Math.pow(2, networkRound - 1); // clamp rv between MIN/MAX_RECONNECT_MS rv = Math.min(Math.max(rv, this.MIN_RECONNECT_MS), this.MAX_RECONNECT_MS); return rv; } CIRCNetwork.prototype.onInit = function net_oninit () { this.logFile = null; this.lastServer = null; } CIRCNetwork.prototype.onInfo = function my_netinfo (e) { this.display (e.msg, "INFO"); } CIRCNetwork.prototype.onUnknown = function my_unknown (e) { if ("pendingWhoisLines" in e.server) { /* whois lines always have the nick in param 2 */ e.user = new CIRCUser(e.server, null, e.params[2]); e.destMethod = "onUnknownWhois"; e.destObject = this; return; } e.params.shift(); /* remove the code */ e.params.shift(); /* and the dest. nick (always me) */ // Handle random IRC numerics automatically. var msg = getMsg("msg.irc." + e.code, null, ""); if (msg) { if (arrayIndexOf(e.server.channelTypes, e.params[0][0]) != -1) { // Message about a channel (e.g. join failed). e.channel = new CIRCChannel(e.server, null, e.params[0]); } var targetDisplayObj = this; if (e.channel && ("messages" in e.channel)) targetDisplayObj = e.channel; // Check for /knock support for the +i message. if (((e.code == 471) || (e.code == 473) || (e.code == 475)) && ("knock" in e.server.servCmds)) { var args = [msg, e.channel.unicodeName, "knock " + e.channel.unicodeName]; msg = getMsg("msg.irc." + e.code + ".knock", args, ""); client.munger.getRule(".inline-buttons").enabled = true; targetDisplayObj.display(msg); client.munger.getRule(".inline-buttons").enabled = false; } else { targetDisplayObj.display(msg); } if (e.channel) { if (e.channel.busy) { e.channel.busy = false; updateProgress(); } } else { // Network type error? if (this.busy) { this.busy = false; updateProgress(); } } return; } /* if it looks like some kind of "end of foo" code, and we don't * already have a mapping for it, make one up */ var length = e.params.length; if (!(e.code in client.responseCodeMap) && (e.params[length - 1].search (/^end of/i) != -1)) { client.responseCodeMap[e.code] = "---"; } this.display(toUnicode(e.params.join(" "), this), e.code.toUpperCase()); } CIRCNetwork.prototype.lastWhoCheckChannel = null; CIRCNetwork.prototype.lastWhoCheckTime = 0; CIRCNetwork.prototype.on001 = /* Welcome! */ CIRCNetwork.prototype.on002 = /* your host is */ CIRCNetwork.prototype.on003 = /* server born-on date */ CIRCNetwork.prototype.on004 = /* server id */ CIRCNetwork.prototype.on005 = /* server features */ CIRCNetwork.prototype.on250 = /* highest connection count */ CIRCNetwork.prototype.on251 = /* users */ CIRCNetwork.prototype.on252 = /* opers online (in params[2]) */ CIRCNetwork.prototype.on254 = /* channels found (in params[2]) */ CIRCNetwork.prototype.on255 = /* link info */ CIRCNetwork.prototype.on265 = /* local user details */ CIRCNetwork.prototype.on266 = /* global user details */ CIRCNetwork.prototype.on375 = /* start of MOTD */ CIRCNetwork.prototype.on372 = /* MOTD line */ CIRCNetwork.prototype.on376 = /* end of MOTD */ CIRCNetwork.prototype.on422 = /* no MOTD */ function my_showtonet (e) { var p = (3 in e.params) ? e.params[2] + " " : ""; var str = ""; switch (e.code) { case "004": case "005": str = e.params.slice(3).join(" "); break; case "001": // Code moved to lower down to speed this bit up. :) var c, u; // If we've switched servers, *first* we must rehome our objects. if (this.lastServer && (this.lastServer != this.primServ)) { for (c in this.lastServer.channels) this.lastServer.channels[c].rehome(this.primServ); for (u in this.lastServer.users) this.lastServer.users[u].rehome(this.primServ); // This makes sure we have the *right* me object. this.primServ.me.rehome(this.primServ); } // Update the list of ignored users from the prefs: var ignoreAry = this.prefs["ignoreList"]; for (var j = 0; j < ignoreAry.length; ++j) this.ignoreList[ignoreAry[j]] = getHostmaskParts(ignoreAry[j]); // Update everything. // Welcome to history. if (client.globalHistory) client.globalHistory.addPage(this.getURL()); updateTitle(this); this.updateHeader(); client.updateHeader(); updateSecurityIcon(); updateStalkExpression(this); client.ident.removeNetwork(this); // Figure out what nick we *really* want: if (this.prefs["away"] && this.prefs["awayNick"]) this.preferredNick = this.prefs["awayNick"]; else this.preferredNick = this.prefs["nickname"]; str = e.decodeParam(2); break; case "251": /* users */ var cmdary = this.prefs["autoperform"]; for (var i = 0; i < cmdary.length; ++i) { if (cmdary[i][0] == "/") this.dispatch(cmdary[i].substr(1)); else this.dispatch(cmdary[i]); } if (this.prefs["away"]) this.dispatch("away", { reason: this.prefs["away"] }); if (this.lastServer) { // Re-join channels from previous connection. for (c in this.primServ.channels) { var chan = this.primServ.channels[c]; if (chan.joined) chan.join(chan.mode.key); } } this.lastServer = this.primServ; if ("pendingURLs" in this) { var url = this.pendingURLs.pop(); while (url) { gotoIRCURL(url); url = this.pendingURLs.pop(); } delete this.pendingURLs; } // Do this after the JOINs, so they are quicker. // This is not time-critical code. if (jsenv.HAS_SERVER_SOCKETS && client.prefs["dcc.enabled"] && this.prefs["dcc.useServerIP"]) { var delayFn = function(t) { // This is the quickest way to get out host/IP. t.pendingUserhostReply = true; t.primServ.sendData("USERHOST " + t.primServ.me.encodedName + "\n"); }; setTimeout(delayFn, 1000 * Math.random(), this); } // Had some collision during connect. if (this.primServ.me.unicodeName != this.preferredNick) { this.reclaimLeft = this.RECLAIM_TIMEOUT; this.reclaimName(); } if ("onLogin" in this) { ev = new CEvent("network", "login", this, "onLogin"); client.eventPump.addEvent(ev); } str = e.decodeParam(e.params.length - 1); break; case "376": /* end of MOTD */ case "422": /* no MOTD */ this.busy = false; updateProgress(); /* no break */ case "372": case "375": case "376": if (this.IGNORE_MOTD) return; /* no break */ default: str = e.decodeParam(e.params.length - 1); break; } this.displayHere(p + str, e.code.toUpperCase()); } CIRCNetwork.prototype.onUnknownCTCPReply = function my_ctcprunk (e) { this.display(getMsg(MSG_FMT_CTCPREPLY, [toUnicode(e.CTCPCode, this), toUnicode(e.CTCPData, this), e.user.unicodeName]), "CTCP_REPLY", e.user, e.server.me, this); } CIRCNetwork.prototype.onNotice = function my_notice (e) { client.munger.getRule(".mailto").enabled = client.prefs["munger.mailto"]; this.display(e.decodeParam(2), "NOTICE", this, e.server.me); client.munger.getRule(".mailto").enabled = false; } /* userhost reply */ CIRCNetwork.prototype.on302 = function my_302(e) { if (jsenv.HAS_SERVER_SOCKETS && client.prefs["dcc.enabled"] && this.prefs["dcc.useServerIP"] && ("pendingUserhostReply" in this)) { var me = new RegExp("^" + this.primServ.me.encodedName + "\\*?=", "i"); if (e.params[2].match(me)) client.dcc.addHost(this.primServ.me.host, true); delete this.pendingUserhostReply; return true; } e.destMethod = "onUnknown"; e.destObject = this; return true; } CIRCNetwork.prototype.on303 = /* ISON (aka notify) reply */ function my_303 (e) { function lower(text) { return e.server.toLowerCase(text); }; var onList = new Array(); // split() gives an array of one item ("") when splitting "", which we // don't want, so only do the split if there's something to split. if (e.params[2]) onList = stringTrim(e.server.toLowerCase(e.params[2])).split(/\s+/); var offList = new Array(); var newArrivals = new Array(); var newDepartures = new Array(); var o = getObjectDetails(client.currentObject); var displayTab; var i; if ("network" in o && o.network == this && client.currentObject != this) displayTab = client.currentObject; for (i = 0; i < this.prefs["notifyList"].length; i++) { if (!arrayContains(onList, lower(this.prefs["notifyList"][i]))) /* user is not on */ offList.push(lower(this.prefs["notifyList"][i])); } if ("onList" in this) { for (i in onList) if (!arrayContains(this.onList, onList[i])) /* we didn't know this person was on */ newArrivals.push(onList[i]); } else this.onList = newArrivals = onList; if ("offList" in this) { for (i in offList) if (!arrayContains(this.offList, offList[i])) /* we didn't know this person was off */ newDepartures.push(offList[i]); } else this.offList = newDepartures = offList; if (newArrivals.length > 0) { this.displayHere (arraySpeak (newArrivals, "is", "are") + " online.", "NOTIFY-ON"); if (displayTab) displayTab.displayHere (arraySpeak (newArrivals, "is", "are") + " online.", "NOTIFY-ON"); } if (newDepartures.length > 0) { this.displayHere (arraySpeak (newDepartures, "is", "are") + " offline.", "NOTIFY-OFF"); if (displayTab) displayTab.displayHere (arraySpeak (newDepartures, "is", "are") + " offline.", "NOTIFY-OFF"); } this.onList = onList; this.offList = offList; } /* away off reply */ CIRCNetwork.prototype.on305 = function my_305(e) { this.display(MSG_AWAY_OFF); return true; } /* away on reply */ CIRCNetwork.prototype.on306 = function my_306(e) { this.display(getMsg(MSG_AWAY_ON, this.prefs["away"])); return true; } CIRCNetwork.prototype.on263 = /* 'try again' */ function my_263 (e) { /* Urgh, this one's a pain. We need to abort whatever we tried, and start * it again if appropriate. * * Known causes of this message: * - LIST, with or without a parameter. */ if (("_list" in this) && !this._list.done && (this._list.count == 0)) { // We attempted a LIST, and we think it failed. :) this._list.done = true; this._list.error = e.decodeParam(2); // Return early for this one if we're saving it. if ("file" in this._list) return true; } e.destMethod = "onUnknown"; e.destObject = this; return true; } CIRCNetwork.prototype.isRunningList = function my_running_list() { /* The list is considered "running" when a cancel is effective. This means * that even if _list.done is true (finished recieving data), we will still * be "running" whilst we have undisplayed items. */ return (("_list" in this) && (!this._list.done || (this._list.length > this._list.displayed)) && !this._list.cancelled); } CIRCNetwork.prototype.list = function my_list(word, file) { const NORMAL_FILE_TYPE = Components.interfaces.nsIFile.NORMAL_FILE_TYPE; if (("_list" in this) && !this._list.done) return false; this._list = new Array(); this._list.string = word; this._list.regexp = null; this._list.done = false; this._list.count = 0; if (file) { var lfile = new LocalFile(file); if (!lfile.localFile.exists()) { // futils.umask may be 0022. Result is 0644. lfile.localFile.create(NORMAL_FILE_TYPE, 0666 & ~futils.umask); } this._list.file = new LocalFile(lfile.localFile, ">"); } if (isinstance(word, RegExp)) { this._list.regexp = word; this._list.string = ""; word = ""; } if (word) this.primServ.sendData("LIST " + fromUnicode(word, this) + "\n"); else this.primServ.sendData("LIST\n"); return true; } CIRCNetwork.prototype.listInit = function my_list_init () { function checkEndList (network) { if (network._list.count == network._list.lastLength) { network.on323(); } else { network._list.lastLength = network._list.count; network._list.endTimeout = setTimeout(checkEndList, 5000, network); } } function outputList (network) { const CHUNK_SIZE = 5; var list = network._list; if (list.cancelled) { if (list.done) { /* The server is no longer throwing stuff at us, so now * we can safely kill the list. */ network.display(getMsg(MSG_LIST_END, [list.displayed, list.count])); delete network._list; } else { /* We cancelled the list, but we're still getting data. * Handle that data, but don't display, and do it more * slowly, so we cause less lag. */ setTimeout(outputList, 1000, network); } return; } if (list.length > list.displayed) { var start = list.displayed; var end = list.length; if (end - start > CHUNK_SIZE) end = start + CHUNK_SIZE; for (var i = start; i < end; ++i) network.displayHere (getMsg(MSG_FMT_CHANLIST, list[i]), "322"); list.displayed = end; } if (list.done && (list.displayed == list.length)) { if (list.event323) { var length = list.event323.params.length; network.displayHere (list.event323.params[length - 1], "323"); } network.displayHere(getMsg(MSG_LIST_END, [list.displayed, list.count])); } else { setTimeout(outputList, 250, network); } } if (!("_list" in this)) { this._list = new Array(); this._list.string = MSG_UNKNOWN; this._list.regexp = null; this._list.done = false; this._list.count = 0; } if (!("file" in this._list)) { this._list.displayed = 0; if (client.currentObject != this) display (getMsg(MSG_LIST_REROUTED, this.unicodeName)); setTimeout(outputList, 250, this); } this._list.lastLength = 0; this._list.endTimeout = setTimeout(checkEndList, 5000, this); } CIRCNetwork.prototype.abortList = function my_abortList() { this._list.cancelled = true; } CIRCNetwork.prototype.on321 = /* LIST reply header */ function my_321 (e) { this.listInit(); if (!("file" in this._list)) this.displayHere (e.params[2] + " " + e.params[3], "321"); } CIRCNetwork.prototype.on323 = /* end of LIST reply */ function my_323 (e) { if (this._list.endTimeout) { clearTimeout(this._list.endTimeout); delete this._list.endTimeout; } if (("file" in this._list)) this._list.file.close(); this._list.done = true; this._list.event323 = e; } CIRCNetwork.prototype.on322 = /* LIST reply */ function my_listrply (e) { if (!("_list" in this) || !("lastLength" in this._list)) this.listInit(); ++this._list.count; /* If the list has been cancelled, don't bother adding all this info * anymore. Do increase the count (above), otherwise we never truly notice * the list being finished. */ if (this._list.cancelled) return; var chanName = e.decodeParam(2); var topic = e.decodeParam(4); if (!this._list.regexp || chanName.match(this._list.regexp) || topic.match(this._list.regexp)) { if (!("file" in this._list)) { this._list.push([chanName, e.params[3], topic]); } else { this._list.file.write(fromUnicode(chanName, "UTF-8") + " " + e.params[3] + " " + fromUnicode(topic, "UTF-8") + "\n"); } } } CIRCNetwork.prototype.on401 = function my_401 (e) { var target = e.server.toLowerCase(e.params[2]); if (target in this.users && "messages" in this.users[target]) { this.users[target].displayHere(e.params[3]); } else if (target in this.primServ.channels && "messages" in this.primServ.channels[target]) { this.primServ.channels[target].displayHere(e.params[3]); } else { if (this.whoisList && (target in this.whoisList)) { // if this is from a whois, send a whowas and don't display anything this.primServ.whowas(target, 1); this.whoisList[target] = false; } else { display(toUnicode(e.params[3], this)); } } } /* 464; "invalid or missing password", occurs as a reply to both OPER and * sometimes initially during user registration. */ CIRCNetwork.prototype.on464 = function my_464(e) { if (this.state == NET_CONNECTING) { // If we are in the process of connecting we are needing a login // password, subtly different from after user registration. this.display(MSG_IRC_464_LOGIN); } else { e.destMethod = "onUnknown"; e.destObject = this; } } /* end of WHO */ CIRCNetwork.prototype.on315 = function my_315 (e) { var matches; if ("whoMatches" in this) matches = this.whoMatches; else matches = 0; if ("pendingWhoReply" in this) this.display(getMsg(MSG_WHO_END, [e.params[2], matches]), e.code); if ("whoUpdates" in this) { var userlist = document.getElementById("user-list"); for (var c in this.whoUpdates) { for (var i = 0; i < this.whoUpdates[c].length; i++) { var index = this.whoUpdates[c][i].chanListEntry.childIndex; userlist.treeBoxObject.invalidateRow(index); } this.primServ.channels[c].updateUsers(this.whoUpdates[c]); } delete this.whoUpdates; } delete this.pendingWhoReply; delete this.whoMatches; } CIRCNetwork.prototype.on352 = function my_352 (e) { //0-352 1-rginda_ 2-#chatzilla 3-chatzilla 4-h-64-236-139-254.aoltw.net //5-irc.mozilla.org 6-rginda 7-H if ("pendingWhoReply" in this) { var status; if (e.user.isAway) status = MSG_GONE; else status = MSG_HERE; this.display(getMsg(MSG_WHO_MATCH, [e.params[6], e.params[3], e.params[4], e.user.desc, status, e.decodeParam(2), e.params[5], e.user.hops]), e.code, e.user); } updateTitle(e.user); if ("whoMatches" in this) ++this.whoMatches; else this.whoMatches = 1; if (!("whoUpdates" in this)) this.whoUpdates = new Object(); if (e.userHasChanges) { for (var c in e.server.channels) { var chan = e.server.channels[c]; if (chan.active && (e.user.canonicalName in chan.users)) { if (!(c in this.whoUpdates)) this.whoUpdates[c] = new Array(); this.whoUpdates[c].push(chan.users[e.user.canonicalName]); } } } } CIRCNetwork.prototype.on301 = /* user away message */ function my_301(e) { if (e.user.awayMessage != e.user.lastShownAwayMessage) { var params = [e.user.unicodeName, e.user.awayMessage]; e.user.display(getMsg(MSG_WHOIS_AWAY, params), e.code); e.user.lastShownAwayMessage = e.user.awayMessage; } } CIRCNetwork.prototype.on311 = /* whois name */ CIRCNetwork.prototype.on319 = /* whois channels */ CIRCNetwork.prototype.on312 = /* whois server */ CIRCNetwork.prototype.on317 = /* whois idle time */ CIRCNetwork.prototype.on318 = /* whois end of whois*/ CIRCNetwork.prototype.onUnknownWhois = /* misc whois line */ function my_whoisreply (e) { var text = "egads!"; var nick = e.params[2]; var lowerNick = this.primServ.toLowerCase(nick); var user; if (this.whoisList && (e.code != 318) && (lowerNick in this.whoisList)) this.whoisList[lowerNick] = true; if (e.user) { user = e.user; nick = user.unicodeName; } switch (Number(e.code)) { case 311: // Clear saved away message so it appears and can be reset. if (e.user) e.user.lastShownAwayMessage = ""; text = getMsg(MSG_WHOIS_NAME, [nick, e.params[3], e.params[4], e.decodeParam(6)]); break; case 319: var ary = stringTrim(e.decodeParam(3)).split(" "); text = getMsg(MSG_WHOIS_CHANNELS, [nick, arraySpeak(ary)]); break; case 312: text = getMsg(MSG_WHOIS_SERVER, [nick, e.params[3], e.params[4]]); break; case 317: text = getMsg(MSG_WHOIS_IDLE, [nick, formatDateOffset(Number(e.params[3])), new Date(Number(e.params[4]) * 1000)]); break; case 318: // If the user isn't here, then we sent a whowas in on401. // Don't display the "end of whois" message. if (this.whoisList && (lowerNick in this.whoisList) && !this.whoisList[lowerNick]) { delete this.whoisList[lowerNick]; return; } if (this.whoisList) delete this.whoisList[lowerNick]; text = getMsg(MSG_WHOIS_END, nick); if (user) user.updateHeader(); break; default: text = toUnicode(e.params.splice(2, e.params.length).join(" "), this); } if (e.user) e.user.display(text, e.code); else this.display(text, e.code); } CIRCNetwork.prototype.on330 = /* ircu's 330 numeric ("X is logged in as Y") */ function my_330 (e) { var msg = getMsg(MSG_FMT_LOGGED_ON, [e.decodeParam(2), e.params[3]]); if (e.user) e.user.display(msg, "330"); else this.display(msg, "330"); } CIRCNetwork.prototype.on341 = /* invite reply */ function my_341 (e) { this.display (getMsg(MSG_YOU_INVITE, [e.decodeParam(2), e.decodeParam(3)]), "341"); } CIRCNetwork.prototype.onInvite = /* invite message */ function my_invite (e) { this.display(getMsg(MSG_INVITE_YOU, [e.user.unicodeName, e.user.name, e.user.host, e.channel.unicodeName]), "INVITE"); if ("messages" in e.channel) e.channel.join(); } CIRCNetwork.prototype.on433 = /* nickname in use */ function my_433 (e) { var nick = toUnicode(e.params[2], this); if ("pendingReclaimCheck" in this) { delete this.pendingReclaimCheck; return; } if (this.state == NET_CONNECTING) { // Force a number, thanks. var nickIndex = 1 * arrayIndexOf(this.prefs["nicknameList"], nick); var newnick; dd("433: failed with " + nick + " (" + nickIndex + ")"); var tryList = true; if ((("_firstNick" in this) && (this._firstNick == -1)) || (this.prefs["nicknameList"].length == 0) || ((nickIndex != -1) && (this.prefs["nicknameList"].length < 2))) { tryList = false; } if (tryList) { nickIndex = (nickIndex + 1) % this.prefs["nicknameList"].length; if (("_firstNick" in this) && (this._firstNick == nickIndex)) { // We're back where we started. Give up with this method. this._firstNick = -1; tryList = false; } } if (tryList) { newnick = this.prefs["nicknameList"][nickIndex]; dd(" trying " + newnick + " (" + nickIndex + ")"); // Save first index we've tried. if (!("_firstNick" in this)) this._firstNick = nickIndex; } else { newnick = this.INITIAL_NICK + "_"; dd(" trying " + newnick); } this.INITIAL_NICK = newnick; this.display(getMsg(MSG_RETRY_NICK, [nick, newnick]), "433"); this.primServ.changeNick(newnick); } else { this.display(getMsg(MSG_NICK_IN_USE, nick), "433"); } } CIRCNetwork.prototype.onStartConnect = function my_sconnect (e) { this.busy = true; updateProgress(); if ("_firstNick" in this) delete this._firstNick; client.munger.getRule(".inline-buttons").enabled = true; this.display(getMsg(MSG_CONNECTION_ATTEMPT, [this.getURL(), e.server.getURL(), this.unicodeName, "cancel"]), "INFO"); client.munger.getRule(".inline-buttons").enabled = false; if (this.prefs["identd.enabled"]) { if (jsenv.HAS_SERVER_SOCKETS) client.ident.addNetwork(this, e.server); else display(MSG_IDENT_SERVER_NOT_POSSIBLE, MT_WARN); } } CIRCNetwork.prototype.onError = function my_neterror (e) { var msg; var type = "ERROR"; if (typeof e.errorCode != "undefined") { switch (e.errorCode) { case JSIRC_ERR_NO_SOCKET: msg = MSG_ERR_NO_SOCKET; break; case JSIRC_ERR_EXHAUSTED: // error already displayed in onDisconnect break; case JSIRC_ERR_OFFLINE: msg = MSG_ERR_OFFLINE; break; case JSIRC_ERR_NO_SECURE: msg = getMsg(MSG_ERR_NO_SECURE, this.unicodeName); break; case JSIRC_ERR_CANCELLED: msg = MSG_ERR_CANCELLED; type = "INFO"; break; } } else msg = e.params[e.params.length - 1]; dispatch("sync-header"); updateTitle(); if (this.state == NET_OFFLINE) { this.busy = false; updateProgress(); } client.ident.removeNetwork(this); if (msg) this.display(msg, type); if (this.deleteWhenDone) this.dispatch("delete-view"); delete this.deleteWhenDone; } CIRCNetwork.prototype.onDisconnect = function my_netdisconnect (e) { var msg, msgNetwork; var msgType = "ERROR"; if (typeof e.disconnectStatus != "undefined") { switch (e.disconnectStatus) { case 0: msg = getMsg(MSG_CONNECTION_CLOSED, [this.getURL(), e.server.getURL()]); break; case NS_ERROR_CONNECTION_REFUSED: msg = getMsg(MSG_CONNECTION_REFUSED, [this.getURL(), e.server.getURL()]); break; case NS_ERROR_NET_TIMEOUT: msg = getMsg(MSG_CONNECTION_TIMEOUT, [this.getURL(), e.server.getURL()]); break; case NS_ERROR_NET_RESET: msg = getMsg(MSG_CONNECTION_RESET, [this.getURL(), e.server.getURL()]); break; case NS_ERROR_UNKNOWN_HOST: msg = getMsg(MSG_UNKNOWN_HOST, [e.server.hostname, this.getURL(), e.server.getURL()]); break; case NS_ERROR_UNKNOWN_PROXY_HOST: msg = getMsg(MSG_UNKNOWN_PROXY_HOST, [this.getURL(), e.server.getURL()]); break; case NS_ERROR_PROXY_CONNECTION_REFUSED: msg = MSG_PROXY_CONNECTION_REFUSED; break; case NS_ERROR_ABORT: if (client.iosvc.offline) { msg = getMsg(MSG_CONNECTION_ABORT_OFFLINE, [this.getURL(), e.server.getURL()]); } else { msg = getMsg(MSG_CONNECTION_ABORT_UNKNOWN, [this.getURL(), e.server.getURL(), formatException(e.exception)]); } break; default: msg = getMsg(MSG_CLOSE_STATUS, [this.getURL(), e.server.getURL(), e.disconnectStatus]); break; } } else { msg = getMsg(MSG_CONNECTION_CLOSED, [this.getURL(), e.server.getURL()]); } // e.quitting signals the disconnect was intended: don't use "ERROR". if (e.quitting) { msgType = "DISCONNECT"; msg = getMsg(MSG_CONNECTION_QUIT, [this.getURL(), e.server.getURL(), this.unicodeName, "reconnect"]); msgNetwork = msg; } // We won't reconnect if the error was really bad. else if ((typeof e.disconnectStatus != "undefined") && (e.disconnectStatus == NS_ERROR_ABORT)) { msgNetwork = msg; } else { var delayStr = formatDateOffset(this.getReconnectDelayMs() / 1000); if (this.MAX_CONNECT_ATTEMPTS == -1) { msgNetwork = getMsg(MSG_RECONNECTING_IN, [msg, delayStr, this.unicodeName, "cancel"]); } else if (this.connectAttempt < this.MAX_CONNECT_ATTEMPTS) { var left = this.MAX_CONNECT_ATTEMPTS - this.connectAttempt; if (left == 1) { msgNetwork = getMsg(MSG_RECONNECTING_IN_LEFT1, [msg, delayStr, this.unicodeName, "cancel"]); } else { msgNetwork = getMsg(MSG_RECONNECTING_IN_LEFT, [msg, left, delayStr, this.unicodeName, "cancel"]); } } else { msgNetwork = getMsg(MSG_CONNECTION_EXHAUSTED, msg); } } /* If we were connected ok, put an error on all tabs. If we were only * /trying/ to connect, and failed, just put it on the network tab. */ client.munger.getRule(".inline-buttons").enabled = true; if (this.state == NET_ONLINE) { for (var v in client.viewsArray) { var obj = client.viewsArray[v].source; if (obj == this) { obj.displayHere(msgNetwork, msgType); } else if (obj != client) { var details = getObjectDetails(obj); if ("server" in details && details.server == e.server) obj.displayHere(msg, msgType); } } } else { this.busy = false; updateProgress(); // Don't do anything if we're cancelling. if (this.state != NET_CANCELLING) { this.displayHere(msgNetwork, msgType); } } client.munger.getRule(".inline-buttons").enabled = false; for (var c in this.primServ.channels) { var channel = this.primServ.channels[c]; channel._clearUserList(); } dispatch("sync-header"); updateTitle(); updateProgress(); updateSecurityIcon(); client.ident.removeNetwork(this); if ("userClose" in client && client.userClose && client.getConnectionCount() == 0) window.close(); if (("reconnect" in this) && this.reconnect) { this.connect(this.requireSecurity); delete this.reconnect; } } CIRCNetwork.prototype.onCTCPReplyPing = function my_replyping (e) { // see bug 326523 if (stringTrim(e.CTCPData).length != 13) { this.display(getMsg(MSG_PING_REPLY_INVALID, e.user.unicodeName), "INFO", e.user, "ME!"); return; } var delay = formatDateOffset((new Date() - new Date(Number(e.CTCPData))) / 1000); this.display(getMsg(MSG_PING_REPLY, [e.user.unicodeName, delay]), "INFO", e.user, "ME!"); } CIRCNetwork.prototype.on221 = CIRCNetwork.prototype.onUserMode = function my_umode (e) { if ("user" in e && e.user) { e.user.updateHeader(); this.display(getMsg(MSG_USER_MODE, [e.user.unicodeName, e.params[2]]), MT_MODE); } else { this.display(getMsg(MSG_USER_MODE, [e.params[1], e.params[2]]), MT_MODE); } } CIRCNetwork.prototype.onNick = function my_cnick (e) { if (!ASSERT(userIsMe(e.user), "network nick event for third party")) return; if (getTabForObject(this)) { this.displayHere(getMsg(MSG_NEWNICK_YOU, e.user.unicodeName), "NICK", "ME!", e.user, this); } this.updateHeader(); updateStalkExpression(this); } CIRCNetwork.prototype.onPing = function my_netping (e) { this.updateHeader(this); } CIRCNetwork.prototype.onPong = function my_netpong (e) { this.updateHeader(this); } CIRCNetwork.prototype.reclaimName = function my_reclaimname() { var network = this; function callback() { network.reclaimName(); }; if ("pendingReclaimCheck" in this) delete this.pendingReclaimCheck; // Function to attempt to get back the nickname the user wants. if ((this.state != NET_ONLINE) || !this.primServ) return false; if (this.primServ.me.unicodeName == this.preferredNick) return false; this.reclaimLeft -= this.RECLAIM_WAIT; if (this.reclaimLeft <= 0) return false; this.pendingReclaimCheck = true; this.primServ.changeNick(this.preferredNick); setTimeout(callback, this.RECLAIM_WAIT); return true; } /* We want to override the base implementations. */ CIRCChannel.prototype._join = CIRCChannel.prototype.join; CIRCChannel.prototype._part = CIRCChannel.prototype.part; CIRCChannel.prototype.join = function chan_join(key) { var joinFailedFn = function _joinFailedFn(t) { delete t.joinTimer; t.busy = false; updateProgress(); } if (!this.joined) { this.joinTimer = setTimeout(joinFailedFn, 30000, this); this.busy = true; updateProgress(); } this._join(key); } CIRCChannel.prototype.part = function chan_part(reason) { var partFailedFn = function _partFailedFn(t) { delete t.partTimer; t.busy = false; updateProgress(); } this.partTimer = setTimeout(partFailedFn, 30000, this); this.busy = true; updateProgress(); this._part(reason); } CIRCChannel.prototype.onInit = function chan_oninit () { this.logFile = null; this.pendingNamesReply = false; } CIRCChannel.prototype.onPrivmsg = function my_cprivmsg (e) { var msg = e.decodeParam(2); client.munger.getRule(".mailto").enabled = client.prefs["munger.mailto"]; this.display (msg, "PRIVMSG", e.user, this); client.munger.getRule(".mailto").enabled = false; } /* end of names */ CIRCChannel.prototype.on366 = function my_366 (e) { var entries = new Array(), updates = new Array(); for (var u in this.users) { entries.push(new UserEntry(this.users[u], this.userListShare)); updates.push(this.users[u]); } this.addUsers(updates); this.userList.childData.appendChildren(entries); if (this.pendingNamesReply) { this.parent.parent.display (e.channel.unicodeName + ": " + e.params[3], "366"); } this.pendingNamesReply = false; // Update conference mode now we have a complete user list. this._updateConferenceMode(); } CIRCChannel.prototype.onTopic = /* user changed topic */ CIRCChannel.prototype.on332 = /* TOPIC reply */ function my_topic (e) { client.munger.getRule(".mailto").enabled = client.prefs["munger.mailto"]; if (e.code == "TOPIC") this.display (getMsg(MSG_TOPIC_CHANGED, [this.topicBy, this.topic]), "TOPIC"); if (e.code == "332") { if (this.topic) { this.display (getMsg(MSG_TOPIC, [this.unicodeName, this.topic]), "TOPIC"); } else { this.display (getMsg(MSG_NO_TOPIC, this.unicodeName), "TOPIC"); } } this.updateHeader(); updateTitle(this); client.munger.getRule(".mailto").enabled = false; } CIRCChannel.prototype.on333 = /* Topic setter information */ function my_topicinfo (e) { this.display (getMsg(MSG_TOPIC_DATE, [this.unicodeName, this.topicBy, this.topicDate]), "TOPIC"); } CIRCChannel.prototype.on353 = /* names reply */ function my_topic (e) { if (this.pendingNamesReply) { this.parent.parent.display (e.channel.unicodeName + ": " + e.params[4], "NAMES"); } } CIRCChannel.prototype.on367 = /* channel ban stuff */ function my_bans(e) { if ("pendingBanList" in this) return; var msg = getMsg(MSG_BANLIST_ITEM, [e.user.unicodeName, e.ban, this.unicodeName, e.banTime]); if (this.iAmHalfOp() || this.iAmOp()) msg += " " + getMsg(MSG_BANLIST_BUTTON, "mode -b " + e.ban); client.munger.getRule(".inline-buttons").enabled = true; this.display(msg, "BAN"); client.munger.getRule(".inline-buttons").enabled = false; } CIRCChannel.prototype.on368 = function my_endofbans(e) { if ("pendingBanList" in this) return; this.display(getMsg(MSG_BANLIST_END, this.unicodeName), "BAN"); } CIRCChannel.prototype.on348 = /* channel except stuff */ function my_excepts(e) { if ("pendingExceptList" in this) return; var msg = getMsg(MSG_EXCEPTLIST_ITEM, [e.user.unicodeName, e.except, this.unicodeName, e.exceptTime]); if (this.iAmHalfOp() || this.iAmOp()) msg += " " + getMsg(MSG_EXCEPTLIST_BUTTON, "mode -e " + e.except); client.munger.getRule(".inline-buttons").enabled = true; this.display(msg, "EXCEPT"); client.munger.getRule(".inline-buttons").enabled = false; } CIRCChannel.prototype.on349 = function my_endofexcepts(e) { if ("pendingExceptList" in this) return; this.display(getMsg(MSG_EXCEPTLIST_END, this.unicodeName), "EXCEPT"); } CIRCChannel.prototype.on482 = function my_needops(e) { if ("pendingExceptList" in this) return; this.display(getMsg(MSG_CHANNEL_NEEDOPS, this.unicodeName), MT_ERROR); } CIRCChannel.prototype.onNotice = function my_notice (e) { client.munger.getRule(".mailto").enabled = client.prefs["munger.mailto"]; this.display(e.decodeParam(2), "NOTICE", e.user, this); client.munger.getRule(".mailto").enabled = false; } CIRCChannel.prototype.onCTCPAction = function my_caction (e) { client.munger.getRule(".mailto").enabled = client.prefs["munger.mailto"]; this.display (e.CTCPData, "ACTION", e.user, this); client.munger.getRule(".mailto").enabled = false; } CIRCChannel.prototype.onUnknownCTCP = function my_unkctcp (e) { this.display (getMsg(MSG_UNKNOWN_CTCP, [e.CTCPCode, e.CTCPData, e.user.unicodeName]), "BAD-CTCP", e.user, this); } CIRCChannel.prototype.onJoin = function my_cjoin (e) { if (!("messages" in this)) this.displayHere(getMsg(MSG_CHANNEL_OPENED, this.unicodeName), MT_INFO); if (userIsMe(e.user)) { var params = [e.user.unicodeName, e.channel.unicodeName]; this.display(getMsg(MSG_YOU_JOINED, params), "JOIN", e.server.me, this); /* Tell the user that conference mode is on, lest they forget (if it * subsequently turns itself off, they'll get a message anyway). */ if (this.prefs["conference.enabled"]) this.display(MSG_CONF_MODE_STAYON); if (client.globalHistory) client.globalHistory.addPage(this.getURL()); if ("joinTimer" in this) { clearTimeout(this.joinTimer); delete this.joinTimer; this.busy = false; updateProgress(); } /* !-channels are "safe" channels, and get a server-generated prefix. * For this reason, creating the channel is delayed until this point. */ if (e.channel.unicodeName[0] == "!") dispatch("set-current-view", { view: e.channel }); } else { if (!this.prefs["conference.enabled"]) { this.display(getMsg(MSG_SOMEONE_JOINED, [e.user.unicodeName, e.user.name, e.user.host, e.channel.unicodeName]), "JOIN", e.user, this); } /* Only do this for non-me joins so us joining doesn't reset it (when * we join the usercount is always 1). Also, do this after displaying * the join message so we don't get cryptic effects such as a user * joining causes *only* a "Conference mode enabled" message. */ this._updateConferenceMode(); } /* We don't want to add ourself here, since the names reply we'll be * getting right after the join will include us as well! (FIXME) */ if (!userIsMe(e.user)) { this.addUsers([e.user]); var entry = new UserEntry(e.user, this.userListShare); this.userList.childData.appendChild(entry); this.userList.childData.reSort(); } this.updateHeader(); } CIRCChannel.prototype.onPart = function my_cpart (e) { this.removeUsers([e.user]); this.updateHeader(); if (userIsMe (e.user)) { var params = [e.user.unicodeName, e.channel.unicodeName]; this.display (getMsg(MSG_YOU_LEFT, params), "PART", e.user, this); this._clearUserList(); if ("partTimer" in this) { clearTimeout(this.partTimer); delete this.partTimer; this.busy = false; updateProgress(); } if (this.deleteWhenDone) this.dispatch("delete-view"); delete this.deleteWhenDone; } else { /* We're ok to update this before the message, because the only thing * that can happen is *disabling* of conference mode. */ this._updateConferenceMode(); if (!this.prefs["conference.enabled"]) { var msg = MSG_SOMEONE_LEFT; if (e.reason) msg = MSG_SOMEONE_LEFT_REASON; this.display(getMsg(msg, [e.user.unicodeName, e.channel.unicodeName, e.reason]), "PART", e.user, this); } this.removeFromList(e.user); } } CIRCChannel.prototype.onKick = function my_ckick (e) { if (userIsMe (e.lamer)) { if (e.user) { this.display (getMsg(MSG_YOURE_GONE, [e.lamer.unicodeName, e.channel.unicodeName, e.user.unicodeName, e.reason]), "KICK", e.user, this); } else { this.display (getMsg(MSG_YOURE_GONE, [e.lamer.unicodeName, e.channel.unicodeName, MSG_SERVER, e.reason]), "KICK", (void 0), this); } this._clearUserList(); /* Try 1 re-join attempt if allowed. */ if (this.prefs["autoRejoin"]) this.join(this.mode.key); } else { var enforcerProper, enforcerNick; if (e.user && userIsMe(e.user)) { enforcerProper = "YOU"; enforcerNick = "ME!"; } else if (e.user) { enforcerProper = e.user.unicodeName; enforcerNick = e.user.encodedName; } else { enforcerProper = MSG_SERVER; enforcerNick = MSG_SERVER; } this.display(getMsg(MSG_SOMEONE_GONE, [e.lamer.unicodeName, e.channel.unicodeName, enforcerProper, e.reason]), "KICK", e.user, this); this.removeFromList(e.lamer); } this.removeUsers([e.lamer]); this.updateHeader(); } CIRCChannel.prototype.removeFromList = function my_removeFromList(user) { // Remove the user from the list and 'disconnect' the user from their entry: var idx = user.chanListEntry.childIndex; this.userList.childData.removeChildAtIndex(idx); delete user.chanListEntry._userObj; delete user.chanListEntry; } CIRCChannel.prototype.onChanMode = function my_cmode (e) { if (e.code == "MODE") { var msg = e.decodeParam(1); for (var i = 2; i < e.params.length; i++) msg += " " + e.decodeParam(i); var source = e.user ? e.user.unicodeName : e.source; this.display(getMsg(MSG_MODE_CHANGED, [msg, source]), "MODE", (e.user || null), this); } else if ("pendingModeReply" in this) { var msg = e.decodeParam(3); for (var i = 4; i < e.params.length; i++) msg += " " + e.decodeParam(i); var view = ("messages" in this && this.messages) ? this : e.network; view.display(getMsg(MSG_MODE_ALL, [this.unicodeName, msg]), "MODE"); delete this.pendingModeReply; } var updates = new Array(); for (var u in e.usersAffected) updates.push(e.usersAffected[u]); this.updateUsers(updates); this.updateHeader(); updateTitle(this); if (client.currentObject == this) updateUserList(); } CIRCChannel.prototype.onNick = function my_cnick (e) { if (userIsMe (e.user)) { if (getTabForObject(this)) { this.displayHere(getMsg(MSG_NEWNICK_YOU, e.user.unicodeName), "NICK", "ME!", e.user, this); } this.parent.parent.updateHeader(); } else if (!this.prefs["conference.enabled"]) { this.display(getMsg(MSG_NEWNICK_NOTYOU, [e.oldNick, e.user.unicodeName]), "NICK", e.user, this); } this.updateUsers([e.user]); if (client.currentObject == this) updateUserList(); } CIRCChannel.prototype.onQuit = function my_cquit (e) { if (userIsMe(e.user)) { /* I dont think this can happen */ var pms = [e.user.unicodeName, e.server.parent.unicodeName, e.reason]; this.display (getMsg(MSG_YOU_QUIT, pms),"QUIT", e.user, this); this._clearUserList(); } else { // See onPart for why this is ok before the message. this._updateConferenceMode(); if (!this.prefs["conference.enabled"]) { this.display(getMsg(MSG_SOMEONE_QUIT, [e.user.unicodeName, e.server.parent.unicodeName, e.reason]), "QUIT", e.user, this); } } this.removeUsers([e.user]); this.removeFromList(e.user); this.updateHeader(); } CIRCChannel.prototype._clearUserList = function _my_clearuserlist() { if (this.userList && this.userList.childData && this.userList.childData.childData) { this.userList.freeze(); var len = this.userList.childData.childData.length; while (len > 0) { var entry = this.userList.childData.childData[--len]; this.userList.childData.removeChildAtIndex(len); delete entry._userObj.chanListEntry; delete entry._userObj; } this.userList.thaw(); } } CIRCUser.prototype.onInit = function user_oninit () { this.logFile = null; this.lastShownAwayMessage = ""; } CIRCUser.prototype.onPrivmsg = function my_cprivmsg(e) { if (!("messages" in this)) { var limit = client.prefs["newTabLimit"]; if (limit == 0 || client.viewsArray.length < limit) openQueryTab(e.server, e.user.unicodeName); } client.munger.getRule(".mailto").enabled = client.prefs["munger.mailto"]; this.display(e.decodeParam(2), "PRIVMSG", e.user, e.server.me); client.munger.getRule(".mailto").enabled = false; } CIRCUser.prototype.onNick = function my_unick (e) { if (userIsMe(e.user)) { this.parent.parent.updateHeader(); updateTitle(); } else if ("messages" in this && this.messages) { this.display(getMsg(MSG_NEWNICK_NOTYOU, [e.oldNick, e.user.unicodeName]), "NICK", e.user, this); } this.updateHeader(); var tab = getTabForObject(this); if (tab) tab.setAttribute("label", this.unicodeName); } CIRCUser.prototype.onNotice = function my_notice (e) { var msg = e.decodeParam(2); var displayMailto = client.prefs["munger.mailto"]; var ary = msg.match(/^\[([^ ]+)\]\s+/); if (ary) { var channel = e.server.getChannel(ary[1]); if (channel) { client.munger.getRule(".mailto").enabled = displayMailto; channel.display(msg, "NOTICE", this, e.server.me); client.munger.getRule(".mailto").enabled = false; return; } } client.munger.getRule(".mailto").enabled = displayMailto; this.display(msg, "NOTICE", this, e.server.me); client.munger.getRule(".mailto").enabled = false; } CIRCUser.prototype.onCTCPAction = function my_uaction(e) { if (!("messages" in this)) { var limit = client.prefs["newTabLimit"]; if (limit == 0 || client.viewsArray.length < limit) openQueryTab(e.server, e.user.unicodeName); } client.munger.getRule(".mailto").enabled = client.prefs["munger.mailto"]; this.display(e.CTCPData, "ACTION", this, e.server.me); client.munger.getRule(".mailto").enabled = false; } CIRCUser.prototype.onUnknownCTCP = function my_unkctcp (e) { this.parent.parent.display (getMsg(MSG_UNKNOWN_CTCP, [e.CTCPCode, e.CTCPData, e.user.unicodeName]), "BAD-CTCP", this, e.server.me); } function onDCCAutoAcceptTimeout(o, folder) { // user may have already accepted or declined if (o.state.state != DCC_STATE_REQUESTED) return; if (o.TYPE == "IRCDCCChat") { o.accept(); display(getMsg(MSG_DCCCHAT_ACCEPTED, o._getParams()), "DCC-CHAT"); } else { var dest, leaf, tries = 0; while (true) { leaf = escapeFileName(o.filename); if (++tries > 1) { // A file with the same name as the offered file already exists // in the user's download folder. Add [x] before the extension. // The extension is the last dot to the end of the string, // unless it is one of the special-cased compression extensions, // in which case the second to last dot is used. The second // extension can only contain letters, to avoid mistakes like // "patch-version1[2].0.gz". If no file extension is present, // the [x] is just appended to the filename. leaf = leaf.replace(/(\.[a-z]*\.(gz|bz2|z)|\.[^\.]*|)$/i, "[" + tries + "]$&"); } dest = getFileFromURLSpec(folder); dest.append(leaf); if (!dest.exists()) break; } o.accept(dest); display(getMsg(MSG_DCCFILE_ACCEPTED, o._getParams()), "DCC-FILE"); } } CIRCUser.prototype.onDCCChat = function my_dccchat(e) { if (!jsenv.HAS_SERVER_SOCKETS || !client.prefs["dcc.enabled"]) return; var u = client.dcc.addUser(e.user, e.host); var c = client.dcc.addChat(u, e.port); var str = MSG_DCCCHAT_GOT_REQUEST; var cmds = getMsg(MSG_DCC_COMMAND_ACCEPT, "dcc-accept " + c.id) + " " + getMsg(MSG_DCC_COMMAND_DECLINE, "dcc-decline " + c.id); var allowList = this.parent.parent.prefs["dcc.autoAccept.list"]; for (var m = 0; m < allowList.length; ++m) { if (hostmaskMatches(e.user, getHostmaskParts(allowList[m]))) { var acceptDelay = client.prefs["dcc.autoAccept.delay"]; if (acceptDelay == 0) { str = MSG_DCCCHAT_ACCEPTING_NOW; } else { str = MSG_DCCCHAT_ACCEPTING; cmds = [(acceptDelay / 1000), cmds]; } setTimeout(onDCCAutoAcceptTimeout, acceptDelay, c); break; } } client.munger.getRule(".inline-buttons").enabled = true; this.parent.parent.display(getMsg(str, c._getParams().concat(cmds)), "DCC-CHAT"); client.munger.getRule(".inline-buttons").enabled = false; // Pass the event over to the DCC Chat object. e.set = "dcc-chat"; e.destObject = c; e.destMethod = "onGotRequest"; } CIRCUser.prototype.onDCCSend = function my_dccsend(e) { if (!jsenv.HAS_SERVER_SOCKETS || !client.prefs["dcc.enabled"]) return; var u = client.dcc.addUser(e.user, e.host); var f = client.dcc.addFileTransfer(u, e.port, e.file, e.size); var str = MSG_DCCFILE_GOT_REQUEST; var cmds = getMsg(MSG_DCC_COMMAND_ACCEPT, "dcc-accept " + f.id) + " " + getMsg(MSG_DCC_COMMAND_DECLINE, "dcc-decline " + f.id); var allowList = this.parent.parent.prefs["dcc.autoAccept.list"]; for (var m = 0; m < allowList.length; ++m) { if (hostmaskMatches(e.user, getHostmaskParts(allowList[m]), this.parent)) { var acceptDelay = client.prefs["dcc.autoAccept.delay"]; if (acceptDelay == 0) { str = MSG_DCCFILE_ACCEPTING_NOW; } else { str = MSG_DCCFILE_ACCEPTING; cmds = [(acceptDelay / 1000), cmds]; } setTimeout(onDCCAutoAcceptTimeout, acceptDelay, f, this.parent.parent.prefs["dcc.downloadsFolder"]); break; } } client.munger.getRule(".inline-buttons").enabled = true; this.parent.parent.display(getMsg(str,[e.user.unicodeName, e.host, e.port, e.file, getSISize(e.size)].concat(cmds)), "DCC-FILE"); client.munger.getRule(".inline-buttons").enabled = false; // Pass the event over to the DCC File object. e.set = "dcc-file"; e.destObject = f; e.destMethod = "onGotRequest"; } CIRCUser.prototype.onDCCReject = function my_dccreject(e) { if (!client.prefs["dcc.enabled"]) return; //FIXME: Uh... cope. // // Pass the event over to the DCC Chat object. //e.set = "dcc-file"; //e.destObject = f; //e.destMethod = "onGotReject"; } CIRCDCCChat.prototype.onInit = function my_dccinit(e) { } CIRCDCCChat.prototype._getParams = function my_dccgetparams() { return [this.user.unicodeName, this.localIP, this.port]; } CIRCDCCChat.prototype.onPrivmsg = function my_dccprivmsg(e) { client.munger.getRule(".mailto").enabled = client.prefs["munger.mailto"]; this.displayHere(toUnicode(e.line, this), "PRIVMSG", e.user, "ME!"); client.munger.getRule(".mailto").enabled = false; } CIRCDCCChat.prototype.onCTCPAction = function my_uaction(e) { client.munger.getRule(".mailto").enabled = client.prefs["munger.mailto"]; this.displayHere(e.CTCPData, "ACTION", e.user, "ME!"); client.munger.getRule(".mailto").enabled = false; } CIRCDCCChat.prototype.onUnknownCTCP = function my_unkctcp(e) { this.displayHere(getMsg(MSG_UNKNOWN_CTCP, [e.CTCPCode, e.CTCPData, e.user.unicodeName]), "BAD-CTCP", e.user, "ME!"); } CIRCDCCChat.prototype.onConnect = function my_dccconnect(e) { playEventSounds("dccchat", "connect"); this.displayHere(getMsg(MSG_DCCCHAT_OPENED, this._getParams()), "DCC-CHAT"); } CIRCDCCChat.prototype.onAbort = function my_dccabort(e) { this.display(getMsg(MSG_DCCCHAT_ABORTED, this._getParams()), "DCC-CHAT"); } CIRCDCCChat.prototype.onFail = function my_dccfail(e) { this.display(getMsg(MSG_DCCCHAT_FAILED, this._getParams()), "DCC-CHAT"); } CIRCDCCChat.prototype.onDisconnect = function my_dccdisconnect(e) { playEventSounds("dccchat", "disconnect"); this.display(getMsg(MSG_DCCCHAT_CLOSED, this._getParams()), "DCC-CHAT"); } CIRCDCCFileTransfer.prototype.onInit = function my_dccfileinit(e) { this.busy = false; this.progress = -1; updateProgress(); } CIRCDCCFileTransfer.prototype._getParams = function my_dccfilegetparams() { var dir = MSG_UNKNOWN; if (this.state.dir == DCC_DIR_GETTING) dir = MSG_DCCLIST_FROM; if (this.state.dir == DCC_DIR_SENDING) dir = MSG_DCCLIST_TO; return [this.filename, dir, this.user.unicodeName, this.localIP/*FIXME*/, this.port/*FIXME?*/]; } CIRCDCCFileTransfer.prototype.onConnect = function my_dccfileconnect(e) { this.displayHere(getMsg(MSG_DCCFILE_OPENED, this._getParams()), "DCC-FILE"); this.busy = true; this.progress = 0; this.speed = 0; updateProgress(); this._lastUpdate = new Date(); this._lastPosition = 0; this._lastSpeedTime = new Date(); } CIRCDCCFileTransfer.prototype.onProgress = function my_dccfileprogress(e) { var now = new Date(); var pcent = Math.floor(100 * this.position / this.size); var tab = getTabForObject(this); // If we've moved 100KiB or waited 10s, update the progress bar. if ((this.position > this._lastPosition + 102400) || (now - this._lastUpdate > 10000)) { this.progress = pcent; updateProgress(); updateTitle(); if (tab) tab.setAttribute("label", this.viewName + " (" + pcent + "%)"); var change = (this.position - this._lastPosition); var speed = change / ((now - this._lastSpeedTime) / 1000); // B/s this._lastSpeedTime = now; /* Use an average of the last speed, and this speed, so we get a little * smoothing to it. */ this.speed = (this.speed + speed) / 2; this.updateHeader(); this._lastPosition = this.position; } // If it's also been 10s or more since we last displayed a msg... if (now - this._lastUpdate > 10000) { this._lastUpdate = now; var args = [pcent, getSISize(this.position), getSISize(this.size), getSISpeed(this.speed)]; // We supress this message if the view is hidden. if (tab) this.displayHere(getMsg(MSG_DCCFILE_PROGRESS, args), "DCC-FILE"); } } CIRCDCCFileTransfer.prototype.onAbort = function my_dccfileabort(e) { this.busy = false; updateProgress(); this.display(getMsg(MSG_DCCFILE_ABORTED, this._getParams()), "DCC-FILE"); } CIRCDCCFileTransfer.prototype.onFail = function my_dccfilefail(e) { this.busy = false; updateProgress(); this.display(getMsg(MSG_DCCFILE_FAILED, this._getParams()), "DCC-FILE"); } CIRCDCCFileTransfer.prototype.onDisconnect = function my_dccfiledisconnect(e) { this.busy = false; updateProgress(); this.updateHeader(); var msg, tab = getTabForObject(this); if (tab) tab.setAttribute("label", this.viewName + " (DONE)"); if (this.state.dir == DCC_DIR_GETTING) { msg = getMsg(MSG_DCCFILE_CLOSED_SAVED, this._getParams().concat(this.localPath)); } else { msg = getMsg(MSG_DCCFILE_CLOSED_SENT, this._getParams()); } this.display(msg, "DCC-FILE"); } var CopyPasteHandler = new Object(); CopyPasteHandler.allowDrop = CopyPasteHandler.allowStartDrag = CopyPasteHandler.onCopyOrDrag = function phand_bogus() { return true; } CopyPasteHandler.onPasteOrDrop = function phand_onpaste(e, data) { // XXXbug 329487: The effect of onPasteOrDrop's return value is actually the // exact opposite of the definition in the IDL. // Don't mess with the multiline box at all. if (client.prefs["multiline"]) return true; var str = new Object(); var strlen = new Object(); data.getTransferData("text/unicode", str, strlen); str.value.QueryInterface(Components.interfaces.nsISupportsString); str.value.data = str.value.data.replace(/(^\s*[\r\n]+|[\r\n]+\s*$)/g, ""); // XXX part of what follows is a very ugly hack to make links (with a title) // not open the multiline box. We 'should' be able to ask the transferable // what flavours it supports, but testing showed that by the time we can ask // for that info, it's forgotten about everything apart from text/unicode. var lines = str.value.data.split("\n"); var m = lines[0].match(client.linkRE); if ((str.value.data.indexOf("\n") == -1) || (m && (m[0] == lines[0]) && (lines.length == 2))) { // If, after stripping leading/trailing empty lines, the string is a // single line, or it's a link with a title, put it back in // the transferable and return. data.setTransferData("text/unicode", str.value, str.value.data.length * 2); return true; } // If it's a drop, move the text cursor to the mouse position. if (e && ("rangeOffset" in e)) client.input.setSelectionRange(e.rangeOffset, e.rangeOffset); str = client.input.value.substr(0, client.input.selectionStart) + str.value.data + client.input.value.substr(client.input.selectionEnd); client.prefs["multiline"] = true; client.input.value = str; return false; } CopyPasteHandler.QueryInterface = function phand_qi(iid) { if (iid.equals(Components.interfaces.nsISupports) || iid.equals(Components.interfaces.nsIClipboardDragDropHooks)) return this; throw Components.results.NS_ERROR_NO_INTERFACE; } function UserEntry(userObj, channelListShare) { var self = this; function getUName() { return userObj.unicodeName; }; function getSortFn() { if (client.prefs["sortUsersByMode"]) return ule_sortByMode; return ule_sortByName; }; // This object is used to represent a user in the userlist. To work with our // JS tree view, it needs a bunch of stuff that is set through the // constructor and the prototype (see also a couple of lines down). Here we // call the original constructor to do some work for us: XULTreeViewRecord.call(this, channelListShare); // This magic function means the unicodeName is used for display: this.setColumnPropertyName("usercol", getUName); // We need this for sorting by mode (op, hop, voice, etc.) this._userObj = userObj; // When the user leaves, we need to have the entry so we can remove it: userObj.chanListEntry = this; // Gross hack: we set up the sort function by getter so we don't have to go // back (array sort -> xpc -> our pref lib -> xpc -> pref interfaces) for // every bloody compare. Now it will be a function that doesn't need prefs // after being retrieved, which is much much faster. this.__defineGetter__("sortCompare", getSortFn); } // See explanation in the constructor. UserEntry.prototype = XULTreeViewRecord.prototype; function ule_sortByName(a, b) { if (a._userObj.unicodeName == b._userObj.unicodeName) return 0; var aName = a._userObj.unicodeName.toLowerCase(); var bName = b._userObj.unicodeName.toLowerCase(); return (aName < bName ? -1 : 1); } function ule_sortByMode(a, b) { if (a._userObj.sortName == b._userObj.sortName) return 0; var aName = a._userObj.sortName.toLowerCase(); var bName = b._userObj.sortName.toLowerCase(); return (aName < bName ? -1 : 1); } PK Ry76* >>content/chatzilla/prefs.js/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is ChatZilla. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Robert Ginda, , original author * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ const DEFAULT_NICK = "IRCMonkey" function initPrefs() { function makeLogNameClient() { return makeLogName(client, "client"); }; client.prefManager = new PrefManager("extensions.irc.", client.defaultBundle); client.prefManagers = [client.prefManager]; client.prefs = client.prefManager.prefs; var profilePath = getSpecialDirectory("ProfD"); profilePath.append("chatzilla"); client.prefManager.addPref("profilePath", profilePath.path, null, null, "hidden"); profilePath = new nsLocalFile(client.prefs["profilePath"]); if (!profilePath.exists()) mkdir(profilePath); client.prefManager.profilePath = profilePath; var scriptPath = profilePath.clone(); scriptPath.append("scripts"); if (!scriptPath.exists()) mkdir(scriptPath); client.prefManager.scriptPath = scriptPath; var logPath = profilePath.clone(); logPath.append("logs"); if (!logPath.exists()) mkdir(logPath); client.prefManager.logPath = logPath; var downloadsPath = profilePath.clone(); downloadsPath.append("downloads"); if (!downloadsPath.exists()) mkdir(downloadsPath); var logDefault = client.prefManager.logPath.clone(); logDefault.append(escapeFileName("client.log")); var gotos = ["goto-url", "goto-url-newwin", "goto-url-newtab", "goto-url-newtab"]; if (client.host == "XULrunner") { gotos = ["goto-url-external", "goto-url-external", "goto-url-external", "goto-url-external"]; } // Set up default nickname, if possible. var defaultNick = DEFAULT_NICK; var en = getService("@mozilla.org/process/environment;1", "nsIEnvironment"); if (en) { /* Get the enviroment variables used by various OSes: * USER - Linux, Mac OSX and other *nix-types. * USERNAME - Windows. * LOGNAME - *nix again. */ const vars = ["USER", "USERNAME", "LOGNAME"]; for (var i = 0; i < vars.length; i++) { var nick = en.get(vars[i]); if (nick) { defaultNick = nick; break; } } } // Set a property so network ident prefs get the same group later: client.prefManager.identGroup = ".connect"; // Linux and OS X won't let non-root listen on port 113. if ((client.platform == "Linux") || (client.platform == "Mac")) client.prefManager.identGroup = "hidden"; var prefs = [ ["activityFlashDelay", 200, "global"], ["aliases", [], "lists.aliases"], ["autoAwayCap", 300, "global"], ["autoAwayPeriod", 2, "appearance.misc"], ["autoRejoin", false, ".connect"], ["away", "", "hidden"], ["awayNick", "", ".ident"], ["bugURL", "https://bugzilla.mozilla.org/show_bug.cgi?id=%s", "appearance.misc"], ["channelHeader", true, "global.header"], ["channelLog", false, "global.log"], ["channelMaxLines", 500, "global.maxLines"], ["charset", "utf-8", ".connect"], ["clientMaxLines", 200, "global.maxLines"], ["collapseActions", true, "appearance.misc"], ["collapseMsgs", false, "appearance.misc"], ["conference.limit", 150, "appearance.misc"], ["connectTries", -1, ".connect"], ["copyMessages", true, "global"], ["dccUserHeader", true, "global.header"], ["dccUserLog", false, "global.log"], ["dccUserMaxLines", 500, "global.maxLines"], ["dcc.enabled", true, "dcc"], ["dcc.autoAccept.delay", 10000, "hidden"], ["dcc.downloadsFolder", getURLSpecFromFile(downloadsPath.path), "dcc"], ["dcc.listenPorts", [], "dcc.ports"], ["dcc.useServerIP", true, "dcc"], ["debugMode", "", "global"], ["defaultQuitMsg", "", ".connect"], ["desc", "New Now Know How", ".ident"], ["deleteOnPart", true, "global"], ["displayHeader", true, "appearance.misc"], ["guessCommands", true, "global"], ["hasPrefs", false, "hidden"], ["font.family", "default", "appearance.misc"], ["font.size", 0, "appearance.misc"], ["identd.enabled", false, client.prefManager.identGroup], ["initialURLs", [], "startup.initialURLs"], ["initialScripts", [getURLSpecFromFile(scriptPath.path)], "startup.initialScripts"], ["instrumentation.key", 0, "hidden"], ["instrumentation.inst1", 0, "hidden"], ["link.focus", true, "global.links"], ["log", false, ".log"], ["logFileName", makeLogNameClient, ".log"], ["logFile.client", "client.$y-$m-$d.log", ".log"], ["logFile.network", "$(network)/$(network).$y-$m-$d.log", ".log"], ["logFile.channel", "$(network)/channels/$(channel).$y-$m-$d.log", ".log"], ["logFile.user", "$(network)/users/$(user).$y-$m-$d.log",".log"], ["logFile.dccuser", "dcc/$(user)/$(user).$y-$m-$d.log", ".log"], ["logFolder", getURLSpecFromFile(logPath.path), ".log"], ["messages.click", gotos[0], "global.links"], ["messages.ctrlClick", gotos[1], "global.links"], ["messages.metaClick", gotos[2], "global.links"], ["messages.middleClick", gotos[3], "global.links"], ["motif.dark", "chrome://chatzilla/skin/output-dark.css", "appearance.motif"], ["motif.light", "chrome://chatzilla/skin/output-light.css", "appearance.motif"], ["motif.default", "chrome://chatzilla/skin/output-default.css", "appearance.motif"], ["motif.current", "chrome://chatzilla/skin/output-default.css", "appearance.motif"], //["msgBeep", "beep beep", "global.sounds"], ["multiline", false, "global"], ["munger.bold", true, "munger"], ["munger.bugzilla-link", true, "munger"], ["munger.channel-link",true, "munger"], ["munger.colorCodes", true, "munger"], ["munger.ctrl-char", true, "munger"], ["munger.face", true, "munger"], ["munger.italic", true, "munger"], ["munger.link", true, "munger"], ["munger.mailto", true, "munger"], ["munger.quote", true, "munger"], ["munger.rheet", true, "munger"], ["munger.talkback-link", true, "munger"], ["munger.teletype", true, "munger"], ["munger.underline", true, "munger"], ["munger.word-hyphenator", true, "munger"], ["networkHeader", true, "global.header"], ["networkLog", false, "global.log"], ["networkMaxLines", 200, "global.maxLines"], ["newTabLimit", 15, "global"], ["notify.aggressive", true, "global"], ["nickCompleteStr", ":", "global"], ["nickname", defaultNick, ".ident"], ["nicknameList", [], "lists.nicknameList"], ["outgoing.colorCodes", false, "global"], ["outputWindowURL", "chrome://chatzilla/content/output-window.html", "appearance.misc"], ["proxy.typeOverride", "", ".connect"], ["sortUsersByMode", true, "appearance.userlist"], //["queryBeep", "beep", "global.sounds"], ["reconnect", true, ".connect"], ["showModeSymbols", false, "appearance.userlist"], //["stalkBeep", "beep", "global.sounds"], ["stalkWholeWords", true, "lists.stalkWords"], ["stalkWords", [], "lists.stalkWords"], // Start == When view it opened. // Event == "Superfluous" activity. // Chat == "Activity" activity. // Stalk == "Attention" activity. ["sound.enabled", true, "global.sounds"], ["sound.overlapDelay", 2000, "global.sounds"], //["sound.surpressActive",false, "global.sounds"], ["sound.channel.start", "", "global.soundEvts"], ["sound.channel.event", "", "global.soundEvts"], ["sound.channel.chat", "", "global.soundEvts"], ["sound.channel.stalk", "beep", "global.soundEvts"], ["sound.user.start", "beep beep", "global.soundEvts"], ["sound.user.stalk", "beep", "global.soundEvts"], ["inputSpellcheck", true, "global"], ["timestamps", false, "appearance.timestamps"], ["timestamps.display", "[%H:%M]", "appearance.timestamps"], ["timestamps.log", "[%Y-%m-%d %H:%M:%S]", "hidden"], ["urls.list", [], "hidden"], ["urls.store.max", 100, "global"], ["urls.display", 10, "hidden"], ["username", "chatzilla", ".ident"], ["usermode", "+i", ".ident"], ["userHeader", true, "global.header"], ["userlistLeft", true, "appearance.userlist"], ["userLog", false, "global.log"], ["userMaxLines", 200, "global.maxLines"], ["warnOnClose", true, "global"] ]; client.prefManager.addPrefs(prefs); client.prefManager.addObserver({ onPrefChanged: onPrefChanged }); CIRCNetwork.prototype.stayingPower = client.prefs["reconnect"]; CIRCNetwork.prototype.MAX_CONNECT_ATTEMPTS = client.prefs["connectTries"]; CIRCNetwork.prototype.INITIAL_NICK = client.prefs["nickname"]; CIRCNetwork.prototype.INITIAL_NAME = client.prefs["username"]; CIRCNetwork.prototype.INITIAL_DESC = client.prefs["desc"]; CIRCNetwork.prototype.INITIAL_UMODE = client.prefs["usermode"]; CIRCNetwork.prototype.MAX_MESSAGES = client.prefs["networkMaxLines"]; CIRCNetwork.prototype.PROXY_TYPE_OVERRIDE = client.prefs["proxy.typeOverride"]; CIRCChannel.prototype.MAX_MESSAGES = client.prefs["channelMaxLines"]; CIRCChanUser.prototype.MAX_MESSAGES = client.prefs["userMaxLines"]; var dccUserMaxLines = client.prefs["dccUserMaxLines"]; CIRCDCCChat.prototype.MAX_MESSAGES = dccUserMaxLines; CIRCDCCFileTransfer.prototype.MAX_MESSAGES = dccUserMaxLines; CIRCDCC.prototype.listenPorts = client.prefs["dcc.listenPorts"]; client.MAX_MESSAGES = client.prefs["clientMaxLines"]; client.charset = client.prefs["charset"]; initAliases(); } function makeLogName(obj, type) { 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; }; function encode(text) { text = text.replace(/[^-A-Z0-9_#!.,'@~\[\]{}()%$"]/gi, replaceNonPrintables); return encodeURIComponent(text); }; /* /\$\(([^)]+)\)|\$(\w)/g * * <-----> <--> * * longName shortName * */ function replaceParam(match, longName, shortName) { if (typeof longName != "undefined" && longName) { // Remember to encode these, don't want some dodgy # breaking stuff. if (longName in longCodes) return encode(longCodes[longName]); dd("Unknown long code: " + longName); } else if (typeof shortName != "undefined" && shortName) { if (shortName in shortCodes) return encode(shortCodes[shortName]); dd("Unknown short code: " + shortName); } else { dd("Unknown match: " + match); } return match; }; var base = client.prefs["logFolder"]; var specific = client.prefs["logFile." + type]; // Make sure we got ourselves a slash, or we'll be in trouble with the // concatenation. if (!base.match(/\/$/)) base = base + "/"; var file = base + specific; // Get details for $-replacement variables. var info = getObjectDetails(obj); // Store the most specific time short code on the object. obj.smallestLogInterval = ""; if (file.indexOf("$y") != -1) obj.smallestLogInterval = "y"; if (file.indexOf("$m") != -1) obj.smallestLogInterval = "m"; if (file.indexOf("$d") != -1) obj.smallestLogInterval = "d"; if (file.indexOf("$h") != -1) obj.smallestLogInterval = "h"; // Three longs codes: $(network), $(channel) and $(user). // Each is available only if appropriate for the object. var longCodes = new Object(); if (info.network) longCodes["network"] = info.network.unicodeName; if (info.channel) longCodes["channel"] = info.channel.unicodeName; if (info.user) longCodes["user"] = info.user.unicodeName; // 4 short codes: $y, $m, $d, $h. // These are time codes, each replaced with a fixed-length number. var d = new Date(); var shortCodes = { y: padNumber(d.getFullYear(), 4), m: padNumber(d.getMonth() + 1, 2), d: padNumber(d.getDate(), 2), h: padNumber(d.getHours(), 2) }; // Replace all $-variables in one go. file = file.replace(/\$\(([^)]+)\)|\$(\w)/g, replaceParam); // Convert from file: URL to local OS format. try { file = getFileFromURLSpec(file).path; } catch(ex) { dd("Error converting '" + base + specific + "' to a local file path."); } return file; } function pref_mungeName(name) { var safeName = name.replace(/\./g, "-").replace(/:/g, "_").toLowerCase(); return ecmaEscape(safeName); } function getNetworkPrefManager(network) { function defer(prefName) { return client.prefs[prefName]; }; function makeLogNameNetwork() { return makeLogName(network, "network"); }; function onPrefChanged(prefName, newValue, oldValue) { onNetworkPrefChanged (network, prefName, newValue, oldValue); }; var logDefault = client.prefManager.logPath.clone(); logDefault.append(escapeFileName(pref_mungeName(network.encodedName)) + ".log"); var prefs = [ ["autoAwayPeriod", defer, "appearance.misc"], ["autoRejoin", defer, ".connect"], ["away", defer, "hidden"], ["awayNick", defer, ".ident"], ["bugURL", defer, "appearance.misc"], ["charset", defer, ".connect"], ["collapseActions", defer, "appearance.misc"], ["collapseMsgs", defer, "appearance.misc"], ["conference.limit", defer, "appearance.misc"], ["connectTries", defer, ".connect"], ["dcc.useServerIP", defer, "dcc"], ["dcc.downloadsFolder", defer, "dcc"], ["dcc.autoAccept.list", [], "dcc.autoAccept"], ["defaultQuitMsg", defer, ".connect"], ["desc", defer, ".ident"], ["displayHeader", client.prefs["networkHeader"], "appearance.misc"], ["font.family", defer, "appearance.misc"], ["font.size", defer, "appearance.misc"], ["hasPrefs", false, "hidden"], ["identd.enabled", defer, client.prefManager.identGroup], ["ignoreList", [], "hidden"], ["log", client.prefs["networkLog"], ".log"], ["logFileName", makeLogNameNetwork, ".log"], ["motif.current", defer, "appearance.motif"], ["nickname", defer, ".ident"], ["nicknameList", defer, "lists.nicknameList"], ["notifyList", [], "lists.notifyList"], ["outputWindowURL", defer, "appearance.misc"], ["proxy.typeOverride", defer, ".connect"], ["reconnect", defer, ".connect"], ["timestamps", defer, "appearance.timestamps"], ["timestamps.display", defer, "appearance.timestamps"], ["timestamps.log", defer, "hidden"], ["username", defer, ".ident"], ["usermode", defer, ".ident"], ["autoperform", [], "lists.autoperform"] ]; var branch = "extensions.irc.networks." + pref_mungeName(network.encodedName) + "."; var prefManager = new PrefManager(branch, client.defaultBundle); prefManager.addPrefs(prefs); prefManager.addObserver({ onPrefChanged: onPrefChanged }); client.prefManager.addObserver(prefManager); var value = prefManager.prefs["nickname"]; if (value != CIRCNetwork.prototype.INITIAL_NICK) network.INITIAL_NICK = value; value = prefManager.prefs["username"]; if (value != CIRCNetwork.prototype.INITIAL_NAME) network.INITIAL_NAME = value; value = prefManager.prefs["desc"]; if (value != CIRCNetwork.prototype.INITIAL_DESC) network.INITIAL_DESC = value; value = prefManager.prefs["usermode"]; if (value != CIRCNetwork.prototype.INITIAL_UMODE) network.INITIAL_UMODE = value; value = prefManager.prefs["proxy.typeOverride"]; if (value != CIRCNetwork.prototype.PROXY_TYPE_OVERRIDE) network.PROXY_TYPE_OVERRIDE = value; network.stayingPower = prefManager.prefs["reconnect"]; network.MAX_CONNECT_ATTEMPTS = prefManager.prefs["connectTries"]; client.prefManagers.push(prefManager); return prefManager; } function getChannelPrefManager(channel) { var network = channel.parent.parent; function defer(prefName) { return network.prefs[prefName]; }; function makeLogNameChannel() { return makeLogName(channel, "channel"); }; function onPrefChanged(prefName, newValue, oldValue) { onChannelPrefChanged (channel, prefName, newValue, oldValue); }; var logDefault = client.prefManager.logPath.clone(); var filename = pref_mungeName(network.encodedName) + "," + pref_mungeName(channel.encodedName); logDefault.append(escapeFileName(filename) + ".log"); var prefs = [ ["autoRejoin", defer, ".connect"], ["bugURL", defer, "appearance.misc"], ["charset", defer, ".connect"], ["collapseActions", defer, "appearance.misc"], ["collapseMsgs", defer, "appearance.misc"], ["conference.enabled", false, "hidden"], ["conference.limit", defer, "appearance.misc"], ["displayHeader", client.prefs["channelHeader"], "appearance.misc"], ["font.family", defer, "appearance.misc"], ["font.size", defer, "appearance.misc"], ["hasPrefs", false, "hidden"], ["log", client.prefs["channelLog"], ".log"], ["logFileName", makeLogNameChannel, ".log"], ["motif.current", defer, "appearance.motif"], ["timestamps", defer, "appearance.timestamps"], ["timestamps.display", defer, "appearance.timestamps"], ["timestamps.log", defer, "hidden"], ["outputWindowURL", defer, "appearance.misc"] ]; var branch = "extensions.irc.networks." + pref_mungeName(network.encodedName) + ".channels." + pref_mungeName(channel.encodedName) + "." var prefManager = new PrefManager(branch, client.defaultBundle); prefManager.addPrefs(prefs); prefManager.addObserver({ onPrefChanged: onPrefChanged }); network.prefManager.addObserver(prefManager); client.prefManagers.push(prefManager); return prefManager; } function getUserPrefManager(user) { var network = user.parent.parent; function defer(prefName) { return network.prefs[prefName]; }; function makeLogNameUser() { return makeLogName(user, "user"); }; function onPrefChanged(prefName, newValue, oldValue) { onUserPrefChanged (user, prefName, newValue, oldValue); }; var logDefault = client.prefManager.logPath.clone(); var filename = pref_mungeName(network.encodedName); filename += "," + pref_mungeName(user.encodedName); logDefault.append(escapeFileName(filename) + ".log"); var prefs = [ ["charset", defer, ".connect"], ["collapseActions", defer, "appearance.misc"], ["collapseMsgs", defer, "appearance.misc"], ["displayHeader", client.prefs["userHeader"], "appearance.misc"], ["font.family", defer, "appearance.misc"], ["font.size", defer, "appearance.misc"], ["hasPrefs", false, "hidden"], ["motif.current", defer, "appearance.motif"], ["outputWindowURL", defer, "appearance.misc"], ["log", client.prefs["userLog"], ".log"], ["logFileName", makeLogNameUser, ".log"], ["timestamps", defer, "appearance.timestamps"], ["timestamps.display", defer, "appearance.timestamps"], ["timestamps.log", defer, "hidden"] ]; var branch = "extensions.irc.networks." + pref_mungeName(network.encodedName) + ".users." + pref_mungeName(user.encodedName) + "."; var prefManager = new PrefManager(branch, client.defaultBundle); prefManager.addPrefs(prefs); prefManager.addObserver({ onPrefChanged: onPrefChanged }); network.prefManager.addObserver(prefManager); client.prefManagers.push(prefManager); return prefManager; } function getDCCUserPrefManager(user) { function defer(prefName) { return client.prefs[prefName]; }; function makeLogNameUser() { return makeLogName(user, "dccuser"); }; function onPrefChanged(prefName, newValue, oldValue) { onDCCUserPrefChanged(user, prefName, newValue, oldValue); }; var prefs = [ ["charset", defer, ".connect"], ["collapseMsgs", defer, "appearance.misc"], ["displayHeader", client.prefs["dccUserHeader"], "appearance.misc"], ["font.family", defer, "appearance.misc"], ["font.size", defer, "appearance.misc"], ["hasPrefs", false, "hidden"], ["motif.current", defer, "appearance.motif"], ["outputWindowURL", defer, "appearance.misc"], ["log", client.prefs["dccUserLog"], ".log"], ["logFileName", makeLogNameUser, ".log"], ["timestamps", defer, "appearance.timestamps"], ["timestamps.display", defer, "appearance.timestamps"], ["timestamps.log", defer, "hidden"] ]; var branch = "extensions.irc.dcc.users." + pref_mungeName(user.canonicalName) + "."; var prefManager = new PrefManager(branch, client.defaultBundle); prefManager.addPrefs(prefs); prefManager.addObserver({ onPrefChanged: onPrefChanged }); client.prefManager.addObserver(prefManager); client.prefManagers.push(prefManager); return prefManager; } function destroyPrefs() { if ("prefManagers" in client) { for (var i = 0; i < client.prefManagers.length; ++i) client.prefManagers[i].destroy(); client.prefManagers = []; } } function onPrefChanged(prefName, newValue, oldValue) { switch (prefName) { case "channelMaxLines": CIRCChannel.prototype.MAX_MESSAGES = newValue; break; case "charset": client.charset = newValue; break; case "clientMaxLines": client.MAX_MESSAGES = newValue; break; case "connectTries": CIRCNetwork.prototype.MAX_CONNECT_ATTEMPTS = newValue; break; case "dcc.listenPorts": CIRCDCC.prototype.listenPorts = newValue; break; case "dccUserMaxLines": CIRCDCCFileTransfer.prototype.MAX_MESSAGES = newValue; CIRCDCCChat.prototype.MAX_MESSAGES = newValue; break; case "font.family": case "font.size": client.dispatch("sync-font"); break; case "instrumentation.inst1": if ((oldValue == 0) && (newValue == 1)) runInstrumentation("inst1", true); else runInstrumentation("inst1", false); break; case "proxy.typeOverride": CIRCNetwork.prototype.PROXY_TYPE_OVERRIDE = newValue; break; case "showModeSymbols": if (newValue) setListMode("symbol"); else setListMode("graphic"); break; case "nickname": CIRCNetwork.prototype.INITIAL_NICK = newValue; break; case "username": CIRCNetwork.prototype.INITIAL_NAME = newValue; break; case "usermode": CIRCNetwork.prototype.INITIAL_UMODE = newValue; break; case "userMaxLines": CIRCChanUser.prototype.MAX_MESSAGES = newValue; break; case "userlistLeft": updateUserlistSide(newValue); break; case "debugMode": setDebugMode(newValue); break; case "desc": CIRCNetwork.prototype.INITIAL_DESC = newValue; break; case "stalkWholeWords": case "stalkWords": updateAllStalkExpressions(); break; case "sortUsersByMode": if (client.currentObject.TYPE == "IRCChannel") updateUserList(); case "motif.current": client.dispatch("sync-motif"); break; case "multiline": multilineInputMode(newValue); break; case "munger.colorCodes": client.enableColors = newValue; break; case "networkMaxLines": CIRCNetwork.prototype.MAX_MESSAGES = newValue; break; case "outputWindowURL": client.dispatch("sync-window"); break; case "displayHeader": client.dispatch("sync-header"); break; case "timestamps": case "timestamps.display": client.dispatch("sync-timestamp"); break; case "log": client.dispatch("sync-log"); break; case "aliases": initAliases(); break; case "inputSpellcheck": updateSpellcheck(newValue); break; default: // Make munger prefs apply without a restart var m, rule; if ((m = prefName.match(/^munger\.(\S+)$/)) && (rule = client.munger.getRule(m[1]))) { rule.enabled = newValue; } } } function onNetworkPrefChanged(network, prefName, newValue, oldValue) { if (network != client.networks[network.canonicalName]) { /* this is a stale observer, remove it */ network.prefManager.destroy(); return; } network.updateHeader(); switch (prefName) { case "nickname": network.INITIAL_NICK = newValue; break; case "username": network.INITIAL_NAME = newValue; break; case "usermode": network.INITIAL_UMODE = newValue; if (network.isConnected()) { network.primServ.sendData("mode " + network.server.me + " :" + newValue + "\n"); } break; case "desc": network.INITIAL_DESC = newValue; break; case "proxy.typeOverride": network.PROXY_TYPE_OVERRIDE = newValue; break; case "reconnect": network.stayingPower = newValue; break; case "font.family": case "font.size": network.dispatch("sync-font"); break; case "motif.current": network.dispatch("sync-motif"); break; case "outputWindowURL": network.dispatch("sync-window"); break; case "displayHeader": network.dispatch("sync-header"); break; case "timestamps": case "timestamps.display": network.dispatch("sync-timestamp"); break; case "log": network.dispatch("sync-log"); break; case "connectTries": network.MAX_CONNECT_ATTEMPTS = newValue; break; } } function onChannelPrefChanged(channel, prefName, newValue, oldValue) { var network = channel.parent.parent; if (network != client.networks[network.canonicalName] || channel.parent != network.primServ || channel != network.primServ.channels[channel.canonicalName]) { /* this is a stale observer, remove it */ channel.prefManager.destroy(); return; } channel.updateHeader(); switch (prefName) { case "conference.enabled": // Wouldn't want to display a message to a hidden view. if ("messages" in channel) { if (newValue) channel.display(MSG_CONF_MODE_ON); else channel.display(MSG_CONF_MODE_OFF); } break; case "conference.limit": channel._updateConferenceMode(); break; case "font.family": case "font.size": channel.dispatch("sync-font"); break; case "motif.current": channel.dispatch("sync-motif"); break; case "outputWindowURL": channel.dispatch("sync-window"); break; case "displayHeader": channel.dispatch("sync-header"); break; case "timestamps": case "timestamps.display": channel.dispatch("sync-timestamp"); break; case "log": channel.dispatch("sync-log"); break; } } function onUserPrefChanged(user, prefName, newValue, oldValue) { var network = user.parent.parent; if (network != client.networks[network.canonicalName] || user.parent != network.primServ || user != network.primServ.users[user.canonicalName]) { /* this is a stale observer, remove it */ user.prefManager.destroy(); return; } user.updateHeader(); switch (prefName) { case "font.family": case "font.size": user.dispatch("sync-font"); break; case "motif.current": user.dispatch("sync-motif"); break; case "outputWindowURL": user.dispatch("sync-window"); break; case "displayHeader": user.dispatch("sync-header"); break; case "timestamps": case "timestamps.display": user.dispatch("sync-timestamp"); break; case "log": user.dispatch("sync-log"); break; } } function onDCCUserPrefChanged(user, prefName, newValue, oldValue) { if (client.dcc.users[user.key] != user) { /* this is a stale observer, remove it */ user.prefManager.destroy(); return; } // DCC Users are a pain, they can have multiple views! function updateDCCView(view) { switch (prefName) { case "font.family": case "font.size": view.dispatch("sync-font"); break; case "motif.current": view.dispatch("sync-motif"); break; case "outputWindowURL": view.dispatch("sync-window"); break; case "displayHeader": view.dispatch("sync-header"); break; case "timestamps": case "timestamps.display": view.dispatch("sync-timestamp"); break; case "log": view.dispatch("sync-log"); break; } }; for (var i = 0; client.dcc.chats.length; i++) { var chat = client.dcc.chats[i]; if (chat.user == user) updateDCCView(chat); } } function initAliases() { var aliasDefs = client.prefs["aliases"]; for (var i = 0; i < aliasDefs.length; ++i) { var ary = aliasDefs[i].match(/^(.*?)\s*=\s*(.*)$/); if (ary) { var name = ary[1]; var list = ary[2]; client.commandManager.defineCommand(name, list); } else { dd("Malformed alias: " + aliasDefs[i]); } } } PK u6Z[Jcontent/chatzilla/messages.js/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is ChatZilla. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Robert Ginda, , original author * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ function initMessages() { var path = "chrome://chatzilla/locale/chatzilla.properties"; client.messageManager = new MessageManager(client.entities); client.messageManager.enableHankakuToZenkaku = true; client.messageManager.loadBrands(); client.defaultBundle = client.messageManager.addBundle(path); // Check we have a compatible locale version! var localeVer = getMsg("locale.version"); // We only care if the required version is newer than the selected (== -1). if (compareVersions(__cz_locale, localeVer) < 0) { var selectedLocale = MSG_UNKNOWN; try { var chromeReg = getService("@mozilla.org/chrome/chrome-registry;1", "nsIXULChromeRegistry"); selectedLocale = chromeReg.getSelectedLocale("chatzilla"); } catch (ex) {} var msg = getMsg("locale.error", [__cz_version, __cz_locale, selectedLocale, localeVer]); if (msg == "locale.error") { // Yes, this is not localized - but that's only for when we don't // *have* the localized string! msg = "ChatZilla " + __cz_version + " locale mismatch!\n\n" + "Expected locale version " + __cz_locale + ", but currently selected locale (" + selectedLocale + ") is version " + localeVer + "."; } if (typeof MSG_ALERT == "undefined") MSG_ALERT = "Alert"; alert(msg); } client.viewName = client.unicodeName = MSG_CLIENT_NAME; client.responseCodeMap = { "HELLO": MSG_RSP_HELLO, "HELP" : MSG_RSP_HELP, "USAGE": MSG_RSP_USAGE, "ERROR": MSG_RSP_ERROR, "WARNING": MSG_RSP_WARN, "INFO": MSG_RSP_INFO, "EVAL-IN": MSG_RSP_EVIN, "EVAL-OUT": MSG_RSP_EVOUT, "DISCONNECT": MSG_RSP_DISCONNECT, "JOIN": "-->|", "PART": "<--|", "QUIT": "|<--", "NICK": "=-=", "TOPIC": "=-=", "KICK": "=-=", "MODE": "=-=", "END_STATUS": "---", "DCC-CHAT": "[DCC]", "DCC-FILE": "[DCC]", "315": "---", /* end of WHO */ "318": "---", /* end of WHOIS */ "366": "---", /* end of NAMES */ "376": "---" /* end of MOTD */ }; } function checkCharset(charset) { return client.messageManager.checkCharset(charset); } function toUnicode (msg, charsetOrView) { if (!msg) return msg; var charset; if (typeof charsetOrView == "object") charset = charsetOrView.prefs["charset"]; else if (typeof charsetOrView == "string") charset = charsetOrView; else charset = client.currentObject.prefs["charset"]; return client.messageManager.toUnicode(msg, charset); } function fromUnicode (msg, charsetOrView) { if (!msg) return msg; var charset; if (typeof charsetOrView == "object") charset = charsetOrView.prefs["charset"]; else if (typeof charsetOrView == "string") charset = charsetOrView; else charset = client.currentObject.prefs["charset"]; return client.messageManager.fromUnicode(msg, charset); } function getMsg(msgName, params, deflt) { return client.messageManager.getMsg(msgName, params, deflt); } function getMsgFrom(bundle, msgName, params, deflt) { return client.messageManager.getMsgFrom(bundle, msgName, params, deflt); } /* message types, don't localize */ const MT_ATTENTION = "ATTENTION"; const MT_ERROR = "ERROR"; const MT_HELLO = "HELLO"; const MT_HELP = "HELP"; const MT_MODE = "MODE"; const MT_WARN = "WARNING"; const MT_INFO = "INFO"; const MT_USAGE = "USAGE"; const MT_STATUS = "STATUS"; const MT_EVALIN = "EVAL-IN"; const MT_EVALOUT = "EVAL-OUT"; PK Ry7btHHcontent/chatzilla/menus.js/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is ChatZilla. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Robert Ginda, , original author * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ function initMenus() { function isMotif(name) { return "client.prefs['motif.current'] == " + "client.prefs['motif." + name + "']"; }; function isFontFamily(name) { return "cx.sourceObject.prefs['font.family'] == '" + name + "'"; }; function isFontFamilyCustom() { return "!cx.sourceObject.prefs['font.family']." + "match(/^(default|(sans-)?serif|monospace)$/)"; }; function isFontSize(size) { return "cx.fontSize == cx.fontSizeDefault + " + size; }; function isFontSizeCustom() { // It's "custom" if it's set (non-zero/not default), not the default // size (medium) and not +/-2 (small/large). return "'fontSize' in cx && cx.fontSize != 0 && " + "cx.fontSizeDefault != cx.fontSize && " + "Math.abs((cx.fontSizeDefault - cx.fontSize) / 2) != 1"; }; function onMenuCommand (event, window) { var params; var commandName = event.originalTarget.getAttribute("commandname"); if ("cx" in client.menuManager && client.menuManager.cx) { client.menuManager.cx.sourceWindow = window; params = client.menuManager.cx; } else { params = { sourceWindow: window }; } dispatch (commandName, params); delete client.menuManager.cx; }; client.onMenuCommand = onMenuCommand; client.menuSpecs = new Object(); var menuManager = new MenuManager(client.commandManager, client.menuSpecs, getCommandContext, "client.onMenuCommand(event, window);"); client.menuManager = menuManager; client.menuSpecs["maintoolbar"] = { items: [ ["disconnect"], ["quit"], ["part"] ] }; // OS values var Win = "(client.platform == 'Windows')"; var NotWin = "(client.platform != 'Windows')"; var Linux = "(client.platform == 'Linux')"; var NotLinux = "(client.platform != 'Linux')"; var Mac = "(client.platform == 'Mac')"; var NotMac = "(client.platform != 'Mac')"; // Platform values var Mozilla = "(client.host == 'Mozilla')"; var NotMozilla = "(client.host != 'Mozilla')"; var Toolkit = NotMozilla; var XULRunner = "(client.host == 'XULrunner')"; // Useful combinations var ToolkitOnLinux = "(" + Toolkit + " and " + Linux + ")"; var ToolkitNotOnLinux = "(" + Toolkit + " and " + NotLinux + ")"; var ToolkitOnMac = "(" + Toolkit + " and " + Mac + ")"; var ToolkitNotOnMac = "(" + Toolkit + " and " + NotMac + ")"; // IRC specific values var ViewClient = "(cx.TYPE == 'IRCClient')"; var ViewNetwork = "(cx.TYPE == 'IRCNetwork')"; var ViewChannel = "(cx.TYPE == 'IRCChannel')"; var ViewUser = "(cx.TYPE == 'IRCUser')"; // IRC specific combinations var ChannelActive = "(" + ViewChannel + " and cx.channel.active)"; var ChannelInactive = "(" + ViewChannel + " and !cx.channel.active)"; var NetConnected = "(cx.network and cx.network.isConnected())"; var NetDisconnected = "(cx.network and !cx.network.isConnected())"; client.menuSpecs["mainmenu:chatzilla"] = { label: MSG_MNU_CHATZILLA, getContext: getDefaultContext, items: [ ["cmd-prefs", {id: "menu_preferences"}], ["print"], ["save"], ["-", {visibleif: NotMac}], ["exit", {visibleif: Win}], ["quit", {visibleif: NotMac + " and " + NotWin}] ] }; client.menuSpecs["mainmenu:irc"] = { label: MSG_MNU_IRC, getContext: getDefaultContext, items: [ ["join"], // Planned future menu items, not implemented yet. //["attach"], //["-"], //["manage-networks"], //["manage-plugins"], ["-"], // Planned future menu items, not implemented yet. //["-"] //[">popup:current_networks"] [">popup:nickname"], ["-"], ["leave", {visibleif: ChannelActive}], ["rejoin", {visibleif: ChannelInactive}], ["disconnect", {visibleif: NetConnected}], ["reconnect", {visibleif: NetDisconnected}], ["-", {visibleif: "cx.network"}], ["clear-view"], ["hide-view", {enabledif: "client.viewsArray.length > 1"}], ["delete-view", {enabledif: "client.viewsArray.length > 1"}], ["-"], ["toggle-oas", {type: "checkbox", checkedif: "isStartupURL(cx.sourceObject.getURL())"}] ] }; client.menuSpecs["mainmenu:edit"] = { label: MSG_MNU_EDIT, getContext: getDefaultContext, items: [ ["cmd-undo", {enabledif: "getCommandEnabled('cmd_undo')"}], ["cmd-redo", {enabledif: "getCommandEnabled('cmd_redo')"}], ["-"], ["cmd-cut", {enabledif: "getCommandEnabled('cmd_cut')"}], ["cmd-copy", {enabledif: "getCommandEnabled('cmd_copy')"}], ["cmd-paste", {enabledif: "getCommandEnabled('cmd_paste')"}], ["cmd-delete", {enabledif: "getCommandEnabled('cmd_delete')"}], ["-"], ["cmd-selectall", {enabledif: "getCommandEnabled('cmd_selectAll')"}], ["-"], ["find"], ["find-again", {enabledif: "canFindAgainInPage()"}], // Mozilla (suite) gets : separator, Mozilla Prefs, ChatZilla prefs. // Toolkit Linux apps get: separator, ChatZilla prefs. // Toolkit Mac apps get : ChatZilla prefs (special Mac ID). ["-", {visibleif: Mozilla}], ["cmd-mozilla-prefs", {visibleif: Mozilla}] ] }; client.menuSpecs["popup:motifs"] = { label: MSG_MNU_MOTIFS, items: [ ["motif-default", {type: "checkbox", checkedif: isMotif("default")}], ["motif-dark", {type: "checkbox", checkedif: isMotif("dark")}], ["motif-light", {type: "checkbox", checkedif: isMotif("light")}], ] }; client.menuSpecs["mainmenu:view"] = { label: MSG_MNU_VIEW, getContext: getDefaultContext, items: [ ["tabstrip", {type: "checkbox", checkedif: "isVisible('view-tabs')"}], ["header", {type: "checkbox", checkedif: "cx.sourceObject.prefs['displayHeader']"}], ["userlist", {type: "checkbox", checkedif: "isVisible('user-list-box')"}], ["statusbar", {type: "checkbox", checkedif: "isVisible('status-bar')"}], ["-"], [">popup:motifs"], [">popup:fonts"], ["-"], ["toggle-ccm", {type: "checkbox", checkedif: "client.prefs['collapseMsgs']"}], ["toggle-copy", {type: "checkbox", checkedif: "client.prefs['copyMessages']"}], ["toggle-timestamps", {type: "checkbox", checkedif: "cx.sourceObject.prefs['timestamps']"}] ] }; /* Mac expects a help menu with this ID, and there is nothing we can do * about it. */ client.menuSpecs["mainmenu:help"] = { label: MSG_MNU_HELP, domID: "menu_Help", items: [ ["-", {visibleif: Mozilla}], ["homepage"], ["faq"], ["-"], ["about"] ] }; client.menuSpecs["popup:fonts"] = { label: MSG_MNU_FONTS, getContext: getFontContext, items: [ ["font-size-bigger", {}], ["font-size-smaller", {}], ["-"], ["font-size-default", {type: "checkbox", checkedif: "!cx.fontSize"}], ["font-size-small", {type: "checkbox", checkedif: isFontSize(-2)}], ["font-size-medium", {type: "checkbox", checkedif: isFontSize(0)}], ["font-size-large", {type: "checkbox", checkedif: isFontSize(+2)}], ["font-size-other", {type: "checkbox", checkedif: isFontSizeCustom()}], ["-"], ["font-family-default", {type: "checkbox", checkedif: isFontFamily("default")}], ["font-family-serif", {type: "checkbox", checkedif: isFontFamily("serif")}], ["font-family-sans-serif", {type: "checkbox", checkedif: isFontFamily("sans-serif")}], ["font-family-monospace", {type: "checkbox", checkedif: isFontFamily("monospace")}], ["font-family-other", {type: "checkbox", checkedif: isFontFamilyCustom()}] ] }; // Me is op. var isop = "(cx.channel.iAmOp()) && "; // Me is op or half-op. var isopish = "(cx.channel.iAmOp() || cx.channel.iAmHalfOp()) && "; // Server has half-ops. var shop = "(cx.server.supports.prefix.indexOf('h') > 0) && "; client.menuSpecs["popup:opcommands"] = { label: MSG_MNU_OPCOMMANDS, items: [ ["op", {visibleif: isop + "!cx.user.isOp"}], ["deop", {visibleif: isop + "cx.user.isOp"}], ["hop", {visibleif: isopish + shop + "!cx.user.isHalfOp"}], ["dehop", {visibleif: isopish + shop + "cx.user.isHalfOp"}], ["voice", {visibleif: isopish + "!cx.user.isVoice"}], ["devoice", {visibleif: isopish + "cx.user.isVoice"}], ["-"], ["ban", {enabledif: "(" + isop + "1) || (" + isopish + "!cx.user.isOp)"}], ["unban", {enabledif: "(" + isop + "1) || (" + isopish + "!cx.user.isOp)"}], ["kick", {enabledif: "(" + isop + "1) || (" + isopish + "!cx.user.isOp)"}], ["kick-ban", {enabledif: "(" + isop + "1) || (" + isopish + "!cx.user.isOp)"}] ] }; client.menuSpecs["popup:usercommands"] = { label: MSG_MNU_USERCOMMANDS, items: [ ["query", {visibleif: "cx.user"}], ["whois", {visibleif: "cx.user"}], ["whowas", {visibleif: "cx.nickname && !cx.user"}], ["ping", {visibleif: "cx.user"}], ["time", {visibleif: "cx.user"}], ["version", {visibleif: "cx.user"}], ["-", {visibleif: "cx.user"}], ["dcc-chat", {visibleif: "cx.user"}], ["dcc-send", {visibleif: "cx.user"}], ] }; client.menuSpecs["context:userlist"] = { getContext: getUserlistContext, items: [ ["toggle-usort", {type: "checkbox", checkedif: "client.prefs['sortUsersByMode']"}], ["toggle-umode", {type: "checkbox", checkedif: "client.prefs['showModeSymbols']"}], ["-", {visibleif: "cx.nickname"}], ["label-user", {visibleif: "cx.nickname", header: true}], [">popup:opcommands", {visibleif: "cx.nickname", enabledif: isopish + "true"}], [">popup:usercommands", {visibleif: "cx.nickname"}], ] }; var urlenabled = "has('url')"; var urlexternal = "has('url') && cx.url.search(/^ircs?:/i) == -1"; var textselected = "getCommandEnabled('cmd_copy')"; client.menuSpecs["context:messages"] = { getContext: getMessagesContext, items: [ ["goto-url", {visibleif: urlenabled}], ["goto-url-newwin", {visibleif: urlexternal + " && !" + XULRunner}], ["goto-url-newtab", {visibleif: urlexternal + " && !" + XULRunner}], ["cmd-copy-link-url", {visibleif: urlenabled}], ["cmd-copy", {visibleif: "!" + urlenabled, enabledif: textselected }], ["cmd-selectall", {visibleif: "!" + urlenabled }], ["-", {visibleif: "cx.channel && cx.nickname"}], ["label-user", {visibleif: "cx.channel && cx.nickname", header: true}], [">popup:opcommands", {visibleif: "cx.channel && cx.nickname", enabledif: isopish + "cx.user"}], [">popup:usercommands", {visibleif: "cx.channel && cx.nickname"}], ["-"], ["clear-view"], ["hide-view", {enabledif: "client.viewsArray.length > 1"}], ["toggle-oas", {type: "checkbox", checkedif: "isStartupURL(cx.sourceObject.getURL())"}], ["-"], ["leave", {visibleif: ChannelActive}], ["rejoin", {visibleif: ChannelInactive}], ["delete-view", {visibleif: "!" + ChannelActive}], ["disconnect", {visibleif: NetConnected}], ["reconnect", {visibleif: NetDisconnected}], ["-"], ["toggle-text-dir"] ] }; client.menuSpecs["context:tab"] = { getContext: getTabContext, items: [ ["clear-view"], ["hide-view", {enabledif: "client.viewsArray.length > 1"}], ["toggle-oas", {type: "checkbox", checkedif: "isStartupURL(cx.sourceObject.getURL())"}], ["-"], ["leave", {visibleif: ChannelActive}], ["rejoin", {visibleif: ChannelInactive}], ["delete-view", {visibleif: "!" + ChannelActive}], ["disconnect", {visibleif: NetConnected}], ["reconnect", {visibleif: NetDisconnected}], ["-"], ["toggle-text-dir"] ] }; // Gross hacks to figure out if we're away: var netAway = "cx.network.prefs['away']"; var cliAway = "client.prefs['away']"; var awayCheckNet = "(cx.network and (" + netAway + " == item.message))"; var awayCheckCli = "(!cx.network and (" + cliAway + " == item.message))"; var awayChecked = awayCheckNet + " or " + awayCheckCli; var areBack = "(cx.network and !" + netAway + ") or " + "(!cx.network and !" + cliAway + ")"; client.menuSpecs["mainmenu:nickname"] = { label: client.prefs["nickname"], domID: "server-nick", getContext: getDefaultContext, items: [ ["nick"], ["-"], ["back", {type: "checkbox", checkedif: areBack}], ["away", {type: "checkbox", checkedif: awayChecked, repeatfor: "client.awayMsgs", repeatmap: "cx.reason = item.message" }], ["-"], ["custom-away"] ] }; client.menuSpecs["popup:nickname"] = { label: MSG_STATUS, getContext: getDefaultContext, items: client.menuSpecs["mainmenu:nickname"].items }; } function createMenus() { client.menuManager.createMenus(document, "mainmenu"); client.menuManager.createContextMenus(document); // The menus and the component bar need to be hidden on some hosts. var winMenu = document.getElementById("windowMenu"); var tasksMenu = document.getElementById("tasksMenu"); var comBar = document.getElementById("component-bar"); if (client.host != "Mozilla") { tasksMenu.parentNode.removeChild(tasksMenu); winMenu.parentNode.removeChild(winMenu); } else { comBar.collapsed = false; } if (client.host == "XULrunner") { // This is a hack to work around Gecko bug 98997, which means that // :empty causes menus to be hidden until we force a reflow. var menuBar = document.getElementById("mainmenu"); menuBar.hidden = true; menuBar.hidden = false; } } function getCommandContext (id, event) { var cx = { originalEvent: event }; if (id in client.menuSpecs) { if ("getContext" in client.menuSpecs[id]) cx = client.menuSpecs[id].getContext(cx); else if ("cx" in client.menuManager) { //dd ("using existing context"); cx = client.menuManager.cx; } else { //no context. } } else { dd ("getCommandContext: unknown menu id " + id); } if (typeof cx == "object") { if (!("menuManager" in cx)) cx.menuManager = client.menuManager; if (!("contextSource" in cx)) cx.contextSource = id; if ("dbgContexts" in client && client.dbgContexts) dd ("context '" + id + "'\n" + dumpObjectTree(cx)); } return cx; } PK $7D}u0]]content/chatzilla/mungers.js/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is ChatZilla. * * The Initial Developer of the Original Code is * New Dimensions Consulting, Inc. * Portions created by the Initial Developer are Copyright (C) 1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Robert Ginda, rginda@ndcico.com, original author * Samuel Sieb, samuel@sieb.net, MIRC color codes * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* This file contains the munger functions and rules used by ChatZilla. * It's generally a bad idea to call munger functions inside ChatZilla for * anything but munging (chat) output. */ function initMunger() { /* linkRE: the general URL linkifier regular expression: * * - start with whitespace, non-word, or begining-of-line * - then match: * - EITHER scheme (word + hyphen), colon, then lots of non-whitespace * - OR "www" followed by at least 2 sets of: * - "." plus some non-whitespace, non-"." characters * - must end match with a word-break * - include a "/" or "=" beyond break if present * - end with whitespace, non-word, or end-of-line */ client.linkRE = /(?:\s|\W|^)((?:(\w[\w-]+):[^\s]+|www(\.[^.\s]+){2,})\b[\/=]?)(?=\s|\W|$)/; const LOW_PRIORITY = 5; const NORMAL_PRIORITY = 10; const HIGH_PRIORITY = 15; const HIGHER_PRIORITY = 20; var munger = client.munger = new CMunger(insertHyphenatedWord); // Special internal munger! munger.addRule(".inline-buttons", /(\[\[.*?\]\])/, insertInlineButton, 10, 5, false); munger.addRule("quote", /(``|'')/, insertQuote, NORMAL_PRIORITY, NORMAL_PRIORITY); munger.addRule("bold", /(?:[\s(\[]|^)(\*[^*()]*\*)(?:[\s\]).,;!\?]|$)/, "chatzilla-bold", NORMAL_PRIORITY, NORMAL_PRIORITY); munger.addRule("underline", /(?:[\s(\[]|^)(\_[^_()]*\_)(?:[\s\]).,;!\?]|$)/, "chatzilla-underline", NORMAL_PRIORITY, NORMAL_PRIORITY); munger.addRule("italic", /(?:\s|^)(\/[^\/()]*\/)(?:[\s.,]|$)/, "chatzilla-italic", NORMAL_PRIORITY, NORMAL_PRIORITY); /* allow () chars inside |code()| blocks */ munger.addRule("teletype", /(?:\s|^)(\|[^|]*\|)(?:[\s.,]|$)/, "chatzilla-teletype", NORMAL_PRIORITY, NORMAL_PRIORITY); munger.addRule(".mirc-colors", /(\x03((\d{1,2})(,\d{1,2}|)|))/, mircChangeColor, NORMAL_PRIORITY, NORMAL_PRIORITY); munger.addRule(".mirc-bold", /(\x02)/, mircToggleBold, NORMAL_PRIORITY, NORMAL_PRIORITY); munger.addRule(".mirc-underline", /(\x1f)/, mircToggleUnder, NORMAL_PRIORITY, NORMAL_PRIORITY); munger.addRule(".mirc-color-reset", /(\x0f)/, mircResetColor, NORMAL_PRIORITY, NORMAL_PRIORITY); munger.addRule(".mirc-reverse", /(\x16)/, mircReverseColor, NORMAL_PRIORITY, NORMAL_PRIORITY); munger.addRule("ctrl-char", /([\x01-\x1f])/, showCtrlChar, NORMAL_PRIORITY, NORMAL_PRIORITY); munger.addRule("link", client.linkRE, insertLink, NORMAL_PRIORITY, HIGH_PRIORITY); // This has a higher starting priority so as to get it to match before the // normal link, which won't know about mailto and then fail. munger.addRule(".mailto", /(?:\s|\W|^)((mailto:)?[^:;\\<>\[\]()\'\"\s\u201d]+@[^.<>\[\]()\'\"\s\u201d]+\.[^<>\[\]()\'\"\s\u201d]+)/i, insertMailToLink, NORMAL_PRIORITY, HIGHER_PRIORITY, false); munger.addRule("bugzilla-link", /(?:\s|\W|^)(bug\s+(?:#?\d+|#[^\s,]{1,20}))/i, insertBugzillaLink, NORMAL_PRIORITY, NORMAL_PRIORITY); munger.addRule("channel-link", /(?:\s|\W|^)[@%+]?(#[^<>,\[\](){}\"\s\u201d]*[^:,.<>\[\](){}\'\"\s\u201d])/i, insertChannelLink, NORMAL_PRIORITY, NORMAL_PRIORITY); munger.addRule("talkback-link", /(?:\W|^)(TB\d{8,}[A-Z]?)(?:\W|$)/, insertTalkbackLink, NORMAL_PRIORITY, NORMAL_PRIORITY); munger.addRule("face", /((^|\s)(?:[>]?[B8=:;(xX][~']?[-^v"]?(?:[)|(PpSs0oO\?\[\]\/\\]|D+)|>[-^v]?\)|[oO9][._][oO9])(\s|$))/, insertSmiley, NORMAL_PRIORITY, NORMAL_PRIORITY); munger.addRule("rheet", /(?:\s|\W|^)(rhee+t\!*)(?:\s|$)/i, insertRheet, 10, 10); munger.addRule("word-hyphenator", new RegExp ("(\\S{" + client.MAX_WORD_DISPLAY + ",})"), insertHyphenatedWord, LOW_PRIORITY, NORMAL_PRIORITY); client.enableColors = client.prefs["munger.colorCodes"]; var branch = client.prefManager.prefBranch; for (var entry in munger.entries) { if (!isinstance(munger.entries[entry], Object)) continue; for (var rule in munger.entries[entry]) { if (rule[0] == ".") continue; try { munger.entries[entry][rule].enabled = branch.getBoolPref("munger." + rule); } catch (ex) { // nada } } } } function insertLink(matchText, containerTag, data, mungerEntry) { var href; var linkText; var trailing; ary = matchText.match(/([.,?]+)$/); if (ary) { linkText = RegExp.leftContext; trailing = ary[1]; } else { linkText = matchText; } var ary = linkText.match(/^(\w[\w-]+):/); if (ary) { if (!("schemes" in client)) { var pfx = "@mozilla.org/network/protocol;1?name="; var len = pfx.length; client.schemes = new Object(); for (var c in Components.classes) { if (c.indexOf(pfx) == 0) client.schemes[c.substr(len)] = true; } } if (!(ary[1] in client.schemes)) { mungerEntry.enabled = false; client.munger.munge(matchText, containerTag, data); mungerEntry.enabled = true; return; } href = linkText; } else { href = "http://" + linkText; } /* This gives callers to the munger control over URLs being logged; the * channel topic munger uses this, as well as the "is important" checker. * If either of |dontLogURLs| or |noStateChange| is present and true, we * don't log. */ if ((!("dontLogURLs" in data) || !data.dontLogURLs) && (!("noStateChange" in data) || !data.noStateChange)) { var max = client.prefs["urls.store.max"]; if (client.prefs["urls.list"].unshift(href) > max) client.prefs["urls.list"].pop(); client.prefs["urls.list"].update(); } var anchor = document.createElementNS("http://www.w3.org/1999/xhtml", "html:a"); var mircRE = /\x1f|\x02|\x0f|\x16|\x03([0-9]{1,2}(,[0-9]{1,2})?)?/g; anchor.setAttribute("href", href.replace(mircRE, "")); // Carry over formatting. var otherFormatting = calcClass(data); if (otherFormatting) anchor.setAttribute("class", "chatzilla-link " + otherFormatting); else anchor.setAttribute("class", "chatzilla-link"); anchor.setAttribute("target", "_content"); mungerEntry.enabled = false; data.inLink = true; client.munger.munge(linkText, anchor, data); mungerEntry.enabled = true; delete data.inLink; containerTag.appendChild(anchor); if (trailing) insertHyphenatedWord(trailing, containerTag, data); } function insertMailToLink(matchText, containerTag, eventData, mungerEntry) { if (("inLink" in eventData) && eventData.inLink) { mungerEntry.enabled = false; client.munger.munge(matchText, containerTag, eventData); mungerEntry.enabled = true; return; } var href; if (matchText.indexOf("mailto:") != 0) href = "mailto:" + matchText; else href = matchText; var anchor = document.createElementNS("http://www.w3.org/1999/xhtml", "html:a"); var mircRE = /\x1f|\x02|\x0f|\x16|\x03([0-9]{1,2}(,[0-9]{1,2})?)?/g; anchor.setAttribute("href", href.replace(mircRE, "")); // Carry over formatting. var otherFormatting = calcClass(eventData); if (otherFormatting) anchor.setAttribute("class", "chatzilla-link " + otherFormatting); else anchor.setAttribute("class", "chatzilla-link"); //anchor.setAttribute ("target", "_content"); mungerEntry.enabled = false; eventData.inLink = true; client.munger.munge(matchText, anchor, eventData); mungerEntry.enabled = true; delete eventData.inLink; containerTag.appendChild(anchor); } function insertChannelLink(matchText, containerTag, eventData, mungerEntry) { if (("inLink" in eventData) && eventData.inLink) { mungerEntry.enabled = false; client.munger.munge(matchText, containerTag, eventData); mungerEntry.enabled = true; return; } var bogusChannels = /^#(include|error|define|if|ifdef|else|elsif|endif|\d+)$/i; if (!("network" in eventData) || !eventData.network || matchText.search(bogusChannels) != -1) { containerTag.appendChild(document.createTextNode(matchText)); return; } var encodedMatchText = fromUnicode(matchText, eventData.sourceObject); var anchor = document.createElementNS("http://www.w3.org/1999/xhtml", "html:a"); anchor.setAttribute("href", eventData.network.getURL() + ecmaEscape(encodedMatchText)); // Carry over formatting. var otherFormatting = calcClass(eventData); if (otherFormatting) anchor.setAttribute("class", "chatzilla-link " + otherFormatting); else anchor.setAttribute("class", "chatzilla-link"); mungerEntry.enabled = false; eventData.inLink = true; client.munger.munge(matchText, anchor, eventData); mungerEntry.enabled = true; delete eventData.inLink; containerTag.appendChild(anchor); } function insertTalkbackLink(matchText, containerTag, eventData, mungerEntry) { if (("inLink" in eventData) && eventData.inLink) { mungerEntry.enabled = false; client.munger.munge(matchText, containerTag, eventData); mungerEntry.enabled = true; return; } var anchor = document.createElementNS("http://www.w3.org/1999/xhtml", "html:a"); anchor.setAttribute("href", "http://talkback-public.mozilla.org/" + "search/start.jsp?search=2&type=iid&id=" + matchText); // Carry over formatting. var otherFormatting = calcClass(eventData); if (otherFormatting) anchor.setAttribute("class", "chatzilla-link " + otherFormatting); else anchor.setAttribute("class", "chatzilla-link"); mungerEntry.enabled = false; client.munger.munge(matchText, anchor, eventData); mungerEntry.enabled = true; containerTag.appendChild(anchor); } function insertBugzillaLink (matchText, containerTag, eventData, mungerEntry) { if (("inLink" in eventData) && eventData.inLink) { mungerEntry.enabled = false; client.munger.munge(matchText, containerTag, eventData); mungerEntry.enabled = true; return; } var bugURL; if (eventData.channel) bugURL = eventData.channel.prefs["bugURL"]; else if (eventData.network) bugURL = eventData.network.prefs["bugURL"]; else bugURL = client.prefs["bugURL"]; if (bugURL.length > 0) { var idOrAlias = matchText.match(/bug\s+#?(\d+|[^\s,]{1,20})/i)[1]; var anchor = document.createElementNS("http://www.w3.org/1999/xhtml", "html:a"); anchor.setAttribute("href", bugURL.replace("%s", idOrAlias)); // Carry over formatting. var otherFormatting = calcClass(eventData); if (otherFormatting) anchor.setAttribute("class", "chatzilla-link " + otherFormatting); else anchor.setAttribute("class", "chatzilla-link"); anchor.setAttribute("target", "_content"); mungerEntry.enabled = false; eventData.inLink = true; client.munger.munge(matchText, anchor, eventData); mungerEntry.enabled = true; delete eventData.inLink; containerTag.appendChild(anchor); } else { mungerEntry.enabled = false; client.munger.munge(matchText, containerTag, eventData); mungerEntry.enabled = true; } } function insertRheet(matchText, containerTag, eventData, mungerEntry) { if (("inLink" in eventData) && eventData.inLink) { mungerEntry.enabled = false; client.munger.munge(matchText, containerTag, eventData); mungerEntry.enabled = true; return; } var anchor = document.createElementNS("http://www.w3.org/1999/xhtml", "html:a"); anchor.setAttribute("href", "http://ftp.mozilla.org/pub/mozilla.org/mozilla/libraries/bonus-tracks/rheet.wav"); anchor.setAttribute("class", "chatzilla-rheet chatzilla-link"); //anchor.setAttribute ("target", "_content"); insertHyphenatedWord(matchText, anchor, eventData); containerTag.appendChild(anchor); } function insertQuote (matchText, containerTag) { if (matchText == "``") containerTag.appendChild(document.createTextNode("\u201c")); else containerTag.appendChild(document.createTextNode("\u201d")); } function insertSmiley(emoticon, containerTag) { var type = "error"; if (emoticon.search(/\>[-^v]?\)/) != -1) type = "face-alien"; else if (emoticon.search(/\>[=:;][-^v]?[(|]/) != -1) type = "face-angry"; else if (emoticon.search(/[=:;][-^v]?[Ss\\\/]/) != -1) type = "face-confused"; else if (emoticon.search(/[B8][-^v]?[)\]]/) != -1) type = "face-cool"; else if (emoticon.search(/[=:;][~'][-^v]?\(/) != -1) type = "face-cry"; else if (emoticon.search(/o[._]O/) != -1) type = "face-dizzy"; else if (emoticon.search(/O[._]o/) != -1) type = "face-dizzy-back"; else if (emoticon.search(/o[._]o|O[._]O/) != -1) type = "face-eek"; else if (emoticon.search(/\>[=:;][-^v]?D/) != -1) type = "face-evil"; else if (emoticon.search(/[=:;][-^v]?DD/) != -1) type = "face-lol"; else if (emoticon.search(/[=:;][-^v]?D/) != -1) type = "face-laugh"; else if (emoticon.search(/\([-^v]?D|[xX][-^v]?D/) != -1) type = "face-rofl"; else if (emoticon.search(/[=:;][-^v]?\|/) != -1) type = "face-normal"; else if (emoticon.search(/[=:;][-^v]?\?/) != -1) type = "face-question"; else if (emoticon.search(/[=:;]"[)\]]/) != -1) type = "face-red"; else if (emoticon.search(/9[._]9/) != -1) type = "face-rolleyes"; else if (emoticon.search(/[=:;][-^v]?[(\[]/) != -1) type = "face-sad"; else if (emoticon.search(/[=:][-^v]?[)\]]/) != -1) type = "face-smile"; else if (emoticon.search(/[=:;][-^v]?[0oO]/) != -1) type = "face-surprised"; else if (emoticon.search(/[=:;][-^v]?[pP]/) != -1) type = "face-tongue"; else if (emoticon.search(/;[-^v]?[)\]]/) != -1) type = "face-wink"; if (type == "error") { // We didn't actually match anything, so it'll be a too-generic match // from the munger RegExp. containerTag.appendChild(document.createTextNode(emoticon)); return; } var span = document.createElementNS ("http://www.w3.org/1999/xhtml", "html:span"); /* create a span to hold the emoticon text */ span.setAttribute ("class", "chatzilla-emote-txt"); span.setAttribute ("type", type); span.appendChild (document.createTextNode (emoticon)); containerTag.appendChild (span); /* create an empty span after the text. this span will have an image added * after it with a chatzilla-emote:after css rule. using * chatzilla-emote-txt:after is not good enough because it does not allow us * to turn off the emoticon text, but keep the image. ie. * chatzilla-emote-txt { display: none; } turns off * chatzilla-emote-txt:after as well.*/ span = document.createElementNS ("http://www.w3.org/1999/xhtml", "html:span"); span.setAttribute ("class", "chatzilla-emote"); span.setAttribute ("type", type); span.setAttribute ("title", emoticon); containerTag.appendChild (span); } function mircChangeColor (colorInfo, containerTag, data) { /* If colors are disabled, the caller doesn't want colors specifically, or * the caller doesn't want any state-changing effects, we drop out. */ if (!client.enableColors || (("noMircColors" in data) && data.noMircColors) || (("noStateChange" in data) && data.noStateChange)) { return; } var ary = colorInfo.match (/.(\d{1,2}|)(,(\d{1,2})|)/); // Do we have a BG color specified...? if (!arrayHasElementAt(ary, 1) || !ary[1]) { // Oops, no colors. delete data.currFgColor; delete data.currBgColor; return; } var fgColor = String(Number(ary[1]) % 16); if (fgColor.length == 1) data.currFgColor = "0" + fgColor; else data.currFgColor = fgColor; // Do we have a BG color specified...? if (arrayHasElementAt(ary, 3) && ary[3]) { var bgColor = String(Number(ary[3]) % 16); if (bgColor.length == 1) data.currBgColor = "0" + bgColor; else data.currBgColor = bgColor; } data.hasColorInfo = true; } function mircToggleBold (colorInfo, containerTag, data) { if (!client.enableColors || (("noMircColors" in data) && data.noMircColors) || (("noStateChange" in data) && data.noStateChange)) { return; } if ("isBold" in data) delete data.isBold; else data.isBold = true; data.hasColorInfo = true; } function mircToggleUnder (colorInfo, containerTag, data) { if (!client.enableColors || (("noMircColors" in data) && data.noMircColors) || (("noStateChange" in data) && data.noStateChange)) { return; } if ("isUnderline" in data) delete data.isUnderline; else data.isUnderline = true; data.hasColorInfo = true; } function mircResetColor (text, containerTag, data) { if (!client.enableColors || (("noMircColors" in data) && data.noMircColors) || (("noStateChange" in data) && data.noStateChange) || !("hasColorInfo" in data)) { return; } delete data.currFgColor; delete data.currBgColor; delete data.isBold; delete data.isUnderline; delete data.hasColorInfo; } function mircReverseColor (text, containerTag, data) { if (!client.enableColors || (("noMircColors" in data) && data.noMircColors) || (("noStateChange" in data) && data.noStateChange)) { return; } var tempColor = ("currFgColor" in data ? data.currFgColor : ""); if ("currBgColor" in data) data.currFgColor = data.currBgColor; else delete data.currFgColor; if (tempColor) data.currBgColor = tempColor; else delete data.currBgColor; data.hasColorInfo = true; } function showCtrlChar(c, containerTag) { var span = document.createElementNS ("http://www.w3.org/1999/xhtml", "html:span"); span.setAttribute ("class", "chatzilla-control-char"); if (c == "\t") { containerTag.appendChild(document.createTextNode(c)); return; } var ctrlStr = c.charCodeAt(0).toString(16); if (ctrlStr.length < 2) ctrlStr = "0" + ctrlStr; span.appendChild (document.createTextNode ("0x" + ctrlStr)); containerTag.appendChild (span); } function insertHyphenatedWord(longWord, containerTag, data) { var wordParts = splitLongWord(longWord, client.MAX_WORD_DISPLAY); var newClass = ""; if (data && ("hasColorInfo" in data)) newClass = calcClass(data); for (var i = 0; i < wordParts.length; ++i) { if (i > 0) { var wbr = document.createElementNS("http://www.w3.org/1999/xhtml", "html:wbr"); containerTag.appendChild(wbr); } if (newClass) { var newTag = document.createElementNS(NS_XHTML, "html:span"); newTag.setAttribute("class", newClass); newTag.appendChild(document.createTextNode(wordParts[i])); containerTag.appendChild(newTag); } else { delete data.hasColorInfo; containerTag.appendChild(document.createTextNode(wordParts[i])); } } } function insertInlineButton(text, containerTag, data) { var ary = text.match(/\[\[([^\]]+)\]\[([^\]]+)\]\[([^\]]+)\]\]/); if (!ary) { containerTag.appendChild(document.createTextNode(text)); return; } var label = ary[1]; var title = ary[2]; var command = ary[3]; var link = document.createElementNS("http://www.w3.org/1999/xhtml", "a"); link.setAttribute("href", "x-cz-command:" + encodeURI(command)); link.setAttribute("title", title); link.setAttribute("class", "chatzilla-link"); link.appendChild(document.createTextNode(label)); containerTag.appendChild(document.createTextNode("[")); containerTag.appendChild(link); containerTag.appendChild(document.createTextNode("]")); } function calcClass(data) { var className = ""; if ("hasColorInfo" in data) { if ("currFgColor" in data) className += " chatzilla-fg" + data.currFgColor; if ("currBgColor" in data) className += " chatzilla-bg" + data.currBgColor; if ("isBold" in data) className += " chatzilla-bold"; if ("isUnderline" in data) className += " chatzilla-underline"; } return className; } PK 697' &44content/chatzilla/static.js/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is ChatZilla. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Robert Ginda, , original author * Chiaki Koufugata chiaki@mozilla.gr.jp UI i18n * Samuel Sieb, samuel@sieb.net, MIRC color codes, munger menu, and various * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ const __cz_version = "0.9.79"; const __cz_condition = "green"; const __cz_suffix = ""; const __cz_guid = "59c81df5-4b7a-477b-912d-4e0fdf64e5f2"; const __cz_locale = "0.9.79"; var warn; var ASSERT; var TEST; if (DEBUG) { _dd_pfx = "cz: "; warn = function (msg) { dumpln ("** WARNING " + msg + " **"); } TEST = ASSERT = function _assert(expr, msg) { if (!expr) { dd("** ASSERTION FAILED: " + msg + " **\n" + getStackTrace() + "\n"); return false; } else { return true; } } } else dd = warn = TEST = ASSERT = function (){}; var client = new Object(); client.TYPE = "IRCClient"; client.COMMAND_CHAR = "/"; client.STEP_TIMEOUT = 500; client.MAX_MESSAGES = 200; client.MAX_HISTORY = 50; /* longest nick to show in display before forcing the message to a block level * element */ client.MAX_NICK_DISPLAY = 14; /* longest word to show in display before abbreviating */ client.MAX_WORD_DISPLAY = 20; client.NOTIFY_TIMEOUT = 5 * 60 * 1000; /* update notify list every 5 minutes */ // Check every minute which networks have away statuses that need an update. client.AWAY_TIMEOUT = 60 * 1000; client.SLOPPY_NETWORKS = true; /* true if msgs from a network can be displayed * on the current object if it is related to * the network (ie, /whois results will appear * on the channel you're viewing, if that channel * is on the network that the results came from) */ client.DOUBLETAB_TIME = 500; client.HIDE_CODES = true; /* true if you'd prefer to show numeric response * codes as some default value (ie, "===") */ client.DEFAULT_RESPONSE_CODE = "==="; /* Maximum number of channels we'll try to list without complaining */ client.SAFE_LIST_COUNT = 500; /* Minimum number of users above or below the conference limit the user count * must go, before it is changed. This allows the user count to fluctuate * around the limit without continously going on and off. */ client.CONFERENCE_LOW_PASS = 10; // Namespaces we happen to need: const XHTML_NS = "http://www.w3.org/1999/xhtml"; client.viewsArray = new Array(); client.activityList = new Object(); client.hostCompat = new Object(); client.inputHistory = new Array(); client.lastHistoryReferenced = -1; client.incompleteLine = ""; client.lastTabUp = new Date(); client.awayMsgs = new Array(); client.awayMsgCount = 5; CIRCNetwork.prototype.INITIAL_CHANNEL = ""; CIRCNetwork.prototype.MAX_MESSAGES = 100; CIRCNetwork.prototype.IGNORE_MOTD = false; CIRCNetwork.prototype.RECLAIM_WAIT = 15000; CIRCNetwork.prototype.RECLAIM_TIMEOUT = 400000; CIRCNetwork.prototype.MIN_RECONNECT_MS = 15 * 1000; // 15s CIRCNetwork.prototype.MAX_RECONNECT_MS = 2 * 60 * 60 * 1000; // 2h CIRCServer.prototype.READ_TIMEOUT = 0; CIRCServer.prototype.PRUNE_OLD_USERS = 0; // prune on user quit. CIRCUser.prototype.MAX_MESSAGES = 200; CIRCChannel.prototype.MAX_MESSAGES = 300; CIRCChanUser.prototype.MAX_MESSAGES = 200; function init() { if (("initialized" in client) && client.initialized) return; client.initialized = false; client.networks = new Object(); client.entities = new Object(); client.eventPump = new CEventPump (200); if (DEBUG) { /* hook all events EXCEPT server.poll and *.event-end types * (the 4th param inverts the match) */ client.debugHook = client.eventPump.addHook([{type: "poll", set:/^(server|dcc-chat)$/}, {type: "event-end"}], event_tracer, "event-tracer", true /* negate */, false /* disable */); } initApplicationCompatibility(); initMessages(); if (client.host == "") showErrorDlg(getMsg(MSG_ERR_UNKNOWN_HOST, client.unknownUID)); initCommands(); initPrefs(); initMunger(); initNetworks(); initMenus(); initStatic(); initHandlers(); // Create DCC handler. client.dcc = new CIRCDCC(client); client.ident = new IdentServer(client); // start logging. nothing should call display() before this point. if (client.prefs["log"]) client.openLogFile(client); // kick-start a log-check interval to make sure we change logfiles in time: // It will fire 2 seconds past the next full hour. setTimeout("checkLogFiles()", 3602000 - (Number(new Date()) % 3600000)); // Make sure the userlist is on the correct side. updateUserlistSide(client.prefs["userlistLeft"]); client.display(MSG_WELCOME, "HELLO"); client.dispatch("set-current-view", { view: client }); importFromFrame("updateHeader"); importFromFrame("setHeaderState"); importFromFrame("changeCSS"); importFromFrame("updateMotifSettings"); importFromFrame("addUsers"); importFromFrame("updateUsers"); importFromFrame("removeUsers"); processStartupScripts(); client.commandManager.installKeys(document); createMenus(); initIcons(); client.busy = false; updateProgress(); initOfflineIcon(); client.initialized = true; dispatch("help", { hello: true }); dispatch("networks"); initInstrumentation(); setTimeout("dispatch('focus-input')", 0); setTimeout(processStartupURLs, 0); } function initStatic() { client.mainWindow = window; try { var io = Components.classes['@mozilla.org/network/io-service;1']; client.iosvc = io.getService(Components.interfaces.nsIIOService); } catch (ex) { dd("IO service failed to initialize: " + ex); } // Need this for the userlist client.atomSvc = getService("@mozilla.org/atom-service;1", "nsIAtomService"); try { const nsISound = Components.interfaces.nsISound; client.sound = Components.classes["@mozilla.org/sound;1"].createInstance(nsISound); client.soundList = new Object(); } catch (ex) { dd("Sound failed to initialize: " + ex); } try { const nsIGlobalHistory = Components.interfaces.nsIGlobalHistory; const GHIST_CONTRACTID = "@mozilla.org/browser/global-history;1"; client.globalHistory = Components.classes[GHIST_CONTRACTID].getService(nsIGlobalHistory); } catch (ex) { dd("Global History failed to initialize: " + ex); } try { const nsISDateFormat = Components.interfaces.nsIScriptableDateFormat; const DTFMT_CID = "@mozilla.org/intl/scriptabledateformat;1"; client.dtFormatter = Components.classes[DTFMT_CID].createInstance(nsISDateFormat); // Mmmm, fun. This ONLY affects the ChatZilla window, don't worry! Date.prototype.toStringInt = Date.prototype.toString; Date.prototype.toString = function() { var dtf = client.dtFormatter; return dtf.FormatDateTime("", dtf.dateFormatLong, dtf.timeFormatSeconds, this.getFullYear(), this.getMonth() + 1, this.getDate(), this.getHours(), this.getMinutes(), this.getSeconds() ); } } catch (ex) { dd("Locale-correct date formatting failed to initialize: " + ex); } multilineInputMode(client.prefs["multiline"]); updateSpellcheck(client.prefs["inputSpellcheck"]); if (client.prefs["showModeSymbols"]) setListMode("symbol"); else setListMode("graphic"); var tree = document.getElementById('user-list'); tree.setAttribute("ondraggesture", "nsDragAndDrop.startDrag(event, userlistDNDObserver);"); setDebugMode(client.prefs["debugMode"]); var ver = __cz_version + (__cz_suffix ? "-" + __cz_suffix : ""); var ua = navigator.userAgent; var app = getService("@mozilla.org/xre/app-info;1", "nsIXULAppInfo"); if (app) { // Use the XUL host app info, and Gecko build ID. if (app.ID == "{" + __cz_guid + "}") { // We ARE the app, in other words, we're running in XULrunner. // Because of this, we must disregard app.(name|vendor|version). // "XULRunner 1.7+/2005071506" ua = "XULRunner " + app.platformVersion + "/" + app.platformBuildID; // "XULRunner 1.7+/2005071506, Windows" CIRCServer.prototype.HOST_RPLY = ua + ", " + client.platform; } else { // "Firefox 1.0+/2005071506" ua = app.name + " " + app.version + "/"; if ("platformBuildID" in app) // 1.1 and up ua += app.platformBuildID; else if ("geckoBuildID" in app) // 1.0 - 1.1 trunk only ua += app.geckoBuildID; else // Uh oh! ua += "??????????"; // "Mozilla Firefox 1.0+, Windows" CIRCServer.prototype.HOST_RPLY = app.vendor + " " + app.name + " " + app.version + ", " + client.platform; } } else { // Extract the revision number, and Gecko build ID. var ary = navigator.userAgent.match(/(rv:[^;)\s]+).*?Gecko\/(\d+)/); if (ary) { if (navigator.vendor) ua = navigator.vendor + " " + navigator.vendorSub; // FF 1.0 else ua = client.entities.brandShortName + " " + ary[1]; // Suite ua = ua + "/" + ary[2]; } CIRCServer.prototype.HOST_RPLY = client.entities.brandShortName + ", " + client.platform; } client.userAgent = getMsg(MSG_VERSION_REPLY, [ver, ua]); CIRCServer.prototype.VERSION_RPLY = client.userAgent; CIRCServer.prototype.SOURCE_RPLY = MSG_SOURCE_REPLY; client.statusBar = new Object(); client.statusBar["server-nick"] = document.getElementById("server-nick"); client.statusElement = document.getElementById("status-text"); client.defaultStatus = MSG_DEFAULT_STATUS; client.progressPanel = document.getElementById("status-progress-panel"); client.progressBar = document.getElementById("status-progress-bar"); client.logFile = null; setInterval("onNotifyTimeout()", client.NOTIFY_TIMEOUT); // Call every minute, will check only the networks necessary. setInterval("onWhoTimeout()", client.AWAY_TIMEOUT); client.awayMsgs = [{ message: MSG_AWAY_DEFAULT }]; var awayFile = new nsLocalFile(client.prefs["profilePath"]); awayFile.append("awayMsgs.txt"); if (awayFile.exists()) { var awayLoader = new TextSerializer(awayFile); if (awayLoader.open("<")) { // Load the first item from the file. var item = awayLoader.deserialize(); if (isinstance(item, Array)) { // If the first item is an array, it is the entire thing. client.awayMsgs = item; } else { /* Not an array, so we have the old format of a single object * per entry. */ client.awayMsgs = [item]; while ((item = awayLoader.deserialize())) client.awayMsgs.push(item); } awayLoader.close(); } } client.defaultCompletion = client.COMMAND_CHAR + "help "; client.deck = document.getElementById('output-deck'); } function initApplicationCompatibility() { // This function does nothing more than tweak the UI based on the host // application. // Set up simple host and platform information. client.host = "Unknown"; // Do we need to copy the icons? (not necessary on Gecko 1.8 and onwards, // and install.js does it for us on SeaMonkey) client.hostCompat.needToCopyIcons = false; var app = getService("@mozilla.org/xre/app-info;1", "nsIXULAppInfo"); // nsIXULAppInfo wasn't implemented before 1.8... if (app) { // Use the XULAppInfo.ID to find out what host we run on. switch (app.ID) { case "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}": client.host = "Firefox"; break; case "{" + __cz_guid + "}": // We ARE the app, in other words, we're running in XULrunner. client.host = "XULrunner"; break; case "{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}": // SeaMonkey client.host = "Mozilla"; break; case "{a463f10c-3994-11da-9945-000d60ca027b}": // Flock client.host = "Flock"; break; case "{3db10fab-e461-4c80-8b97-957ad5f8ea47}": // Netscape client.host = "Netscape"; break; default: client.unknownUID = app.ID; client.host = ""; // Unknown host, show an error later. } } else if ("getBrowserURL" in window) { var url = getBrowserURL(); if (url == "chrome://navigator/content/navigator.xul") { client.host = "Mozilla"; } else if (url == "chrome://browser/content/browser.xul") { client.hostCompat.needToCopyIcons = true; client.host = "Firefox"; } else { client.host = ""; // We don't know this host. Show an error later. client.unknownUID = url; } } client.platform = "Unknown"; if (navigator.platform.search(/mac/i) > -1) client.platform = "Mac"; if (navigator.platform.search(/win/i) > -1) client.platform = "Windows"; if (navigator.platform.search(/linux/i) > -1) client.platform = "Linux"; if (navigator.platform.search(/os\/2/i) > -1) client.platform = "OS/2"; client.hostPlatform = client.host + client.platform; CIRCServer.prototype.OS_RPLY = navigator.oscpu + " (" + navigator.platform + ")"; // Windows likes \r\n line endings, as wussy-notepad can't cope with just // \n logs. if (client.platform == "Windows") client.lineEnd = "\r\n"; else client.lineEnd = "\n"; } function initIcons() { // Make sure we got the ChatZilla icon(s) in place first. const iconName = "chatzilla-window"; const suffixes = [".ico", ".xpm", "16.xpm"]; /* when installing on Mozilla, the XPI has the power to put the icons where * they are needed - in older versions of Firefox, it doesn't. */ if (!client.hostCompat.needToCopyIcons) return; var sourceDir = getSpecialDirectory("ProfD"); sourceDir.append("extensions"); sourceDir.append("{" + __cz_guid + "}"); sourceDir.append("chrome"); sourceDir.append("icons"); sourceDir.append("default"); var destDir = getSpecialDirectory("AChrom"); destDir.append("icons"); destDir.append("default"); if (!destDir.exists()) { try { mkdir(destDir); } catch(ex) { return; } } for (var i = 0; i < suffixes.length; i++) { var iconDest = destDir.clone(); iconDest.append(iconName + suffixes[i]); var iconSrc = sourceDir.clone(); iconSrc.append(iconName + suffixes[i]); if (iconSrc.exists() && !iconDest.exists()) { try { iconSrc.copyTo(iconDest.parent, iconDest.leafName); } catch(ex){} } } } function initInstrumentation() { // Make sure we assign the user a random key - this is not used for // anything except percentage chance of participation. if (client.prefs["instrumentation.key"] == 0) { var rand = 1 + Math.round(Math.random() * 10000); client.prefs["instrumentation.key"] = rand; } runInstrumentation("inst1"); } function runInstrumentation(name, firstRun) { if (!/^inst\d+$/.test(name)) return; // Values: // 0 = not answered question // 1 = allowed inst // 2 = denied inst if (client.prefs["instrumentation." + name] == 0) { // We only want 1% of people to be asked here. if (client.prefs["instrumentation.key"] > 100) return; // User has not seen the info about this system. Show them the info. var cmdYes = "allow-" + name; var cmdNo = "deny-" + name; var btnYes = getMsg(MSG_INST1_COMMAND_YES, cmdYes); var btnNo = getMsg(MSG_INST1_COMMAND_NO, cmdNo); client.munger.getRule(".inline-buttons").enabled = true; client.display(getMsg("msg." + name + ".msg1", [btnYes, btnNo])); client.display(getMsg("msg." + name + ".msg2", [cmdYes, cmdNo])); client.munger.getRule(".inline-buttons").enabled = false; // Don't hide *client* if we're asking the user about the startup ping. client.lockView = true; return; } if (client.prefs["instrumentation." + name] != 1) return; if (name == "inst1") runInstrumentation1(firstRun); } function runInstrumentation1(firstRun) { function inst1onLoad() { if (/OK/.test(req.responseText)) client.display(MSG_INST1_MSGRPLY2); else client.display(getMsg(MSG_INST1_MSGRPLY1, MSG_UNKNOWN)); }; function inst1onError() { client.display(getMsg(MSG_INST1_MSGRPLY1, req.statusText)); }; try { const baseURI = "http://silver.warwickcompsoc.co.uk/" + "mozilla/chatzilla/instrumentation/startup?"; if (firstRun) { // Do a first-run ping here. var frReq = new XMLHttpRequest(); frReq.open("GET", baseURI + "first-run"); frReq.send(null); } var data = new Array(); data.push("ver=" + encodeURIComponent(CIRCServer.prototype.VERSION_RPLY)); data.push("host=" + encodeURIComponent(client.hostPlatform)); data.push("chost=" + encodeURIComponent(CIRCServer.prototype.HOST_RPLY)); data.push("cos=" + encodeURIComponent(CIRCServer.prototype.OS_RPLY)); var url = baseURI + data.join("&"); var req = new XMLHttpRequest(); req.onload = inst1onLoad; req.onerror = inst1onError; req.open("GET", url); req.send(null); } catch (ex) { client.display(getMsg(MSG_INST1_MSGRPLY1, formatException(ex))); } } function getFindData(e) { var findData = new nsFindInstData(); findData.browser = e.sourceObject.frame; findData.rootSearchWindow = getContentWindow(e.sourceObject.frame); findData.currentSearchWindow = getContentWindow(e.sourceObject.frame); /* Yay, evil hacks! findData.init doesn't care about the findService, it * gets option settings from webBrowserFind. As we want the wrap option *on* * when we use /find foo, we set it on the findService there. However, * restoring the original value afterwards doesn't help, because init() here * overrides that value. Unless we make .init do something else, of course: */ findData._init = findData.init; findData.init = function init() { this._init(); const FINDSVC_ID = "@mozilla.org/find/find_service;1"; var findService = getService(FINDSVC_ID, "nsIFindService"); this.webBrowserFind.wrapFind = findService.wrapFind; }; return findData; } function importFromFrame(method) { client.__defineGetter__(method, import_wrapper); CIRCNetwork.prototype.__defineGetter__(method, import_wrapper); CIRCChannel.prototype.__defineGetter__(method, import_wrapper); CIRCUser.prototype.__defineGetter__(method, import_wrapper); CIRCDCCChat.prototype.__defineGetter__(method, import_wrapper); CIRCDCCFileTransfer.prototype.__defineGetter__(method, import_wrapper); function import_wrapper() { var dummy = function(){}; if (!("frame" in this)) return dummy; try { var window = getContentWindow(this.frame); if (window && "initialized" in window && window.initialized && method in window) { return function import_wrapper_apply() { window[method].apply(this, arguments); }; } } catch (ex) { ASSERT(0, "Caught exception calling: " + method + "\n" + ex); } return dummy; }; } function processStartupScripts() { client.plugins = new Array(); var scripts = client.prefs["initialScripts"]; for (var i = 0; i < scripts.length; ++i) { if (scripts[i].search(/^file:|chrome:/i) != 0) { display(getMsg(MSG_ERR_INVALID_SCHEME, scripts[i]), MT_ERROR); continue; } var path = getFileFromURLSpec(scripts[i]); if (!path.exists()) { display(getMsg(MSG_ERR_ITEM_NOT_FOUND, scripts[i]), MT_WARN); continue; } if (path.isDirectory()) loadPluginDirectory(path); else loadLocalFile(path); } } function loadPluginDirectory(localPath, recurse) { if (typeof recurse == "undefined") recurse = 1; var initPath = localPath.clone(); initPath.append("init.js"); if (initPath.exists()) loadLocalFile(initPath); if (recurse < 1) return; var enumer = localPath.directoryEntries; while (enumer.hasMoreElements()) { var entry = enumer.getNext(); entry = entry.QueryInterface(Components.interfaces.nsILocalFile); if (entry.isDirectory()) loadPluginDirectory(entry, recurse - 1); } } function loadLocalFile(localFile) { var url = getURLSpecFromFile(localFile); var glob = new Object(); dispatch("load", {url: url, scope: glob}); } function getPluginById(id) { for (var i = 0; i < client.plugins.length; ++i) { if (client.plugins[i].id == id) return client.plugins[i]; } return null; } function getPluginIndexById(id) { for (var i = 0; i < client.plugins.length; ++i) { if (client.plugins[i].id == id) return i; } return -1; } function getPluginByURL(url) { for (var i = 0; i < client.plugins.length; ++i) { if (client.plugins[i].url == url) return client.plugins[i]; } return null; } function getPluginIndexByURL(url) { for (var i = 0; i < client.plugins.length; ++i) { if (client.plugins[i].url == url) return i; } return -1; } function processStartupURLs() { var wentSomewhere = false; if ("arguments" in window && 0 in window.arguments && typeof window.arguments[0] == "object" && "url" in window.arguments[0]) { var url = window.arguments[0].url; if (url.search(/^ircs?:\/?\/?\/?$/i) == -1) { /* if the url is not irc: irc:/, irc://, or ircs equiv then go to it. */ gotoIRCURL(url); wentSomewhere = true; } } /* check to see whether the URL has been passed via the command line instead. */ else if ("arguments" in window && 0 in window.arguments && typeof window.arguments[0] == "string") { var url = window.arguments[0] var urlMatches = url.match(/^ircs?:\/\/\/?(.*)$/) if (urlMatches) { if (urlMatches[1]) { /* if the url is not "irc://", "irc:///" or an ircs equiv then go to it. */ gotoIRCURL(url); wentSomewhere = true; } } else if (url) { /* URL parameter is not blank, but does not not conform to the irc[s] scheme. */ display(getMsg(MSG_ERR_INVALID_SCHEME, url), MT_ERROR); } } if (!wentSomewhere) { /* if we had nowhere else to go, connect to any default urls */ var ary = client.prefs["initialURLs"]; for (var i = 0; i < ary.length; ++i) { if (ary[i] && ary[i] == "irc:///") { // Clean out "default network" entries, which we don't // support any more; replace with the harmless irc:// URL. ary[i] = "irc://"; client.prefs["initialURLs"].update(); } if (ary[i] && ary[i] != "irc://") gotoIRCURL(ary[i]); } } if (client.viewsArray.length > 1 && !isStartupURL("irc://")) { dispatch("delete-view", {view: client}); } } function destroy() { destroyPrefs(); } function setStatus (str) { client.statusElement.setAttribute ("label", str); return str; } client.__defineSetter__ ("status", setStatus); function getStatus () { return client.statusElement.getAttribute ("label"); } client.__defineGetter__ ("status", getStatus); function isVisible (id) { var e = document.getElementById(id); if (!ASSERT(e,"Bogus id ``" + id + "'' passed to isVisible() **")) return false; return (e.getAttribute ("collapsed") != "true"); } client.getConnectedNetworks = function getConnectedNetworks() { var rv = []; for (var n in client.networks) { if (client.networks[n].isConnected()) rv.push(client.networks[n]); } return rv; } function combineNicks(nickList, max) { if (!max) max = 4; var combinedList = []; for (var i = 0; i < nickList.length; i += max) { count = Math.min(max, nickList.length - i); var nicks = nickList.slice(i, i + count); var str = new String(nicks.join(" ")); str.count = count; combinedList.push(str); } return combinedList; } function updateAllStalkExpressions() { var list = client.prefs["stalkWords"]; for (var name in client.networks) { if ("stalkExpression" in client.networks[name]) updateStalkExpression(client.networks[name], list); } } function updateStalkExpression(network) { function escapeChar(ch) { return "\\" + ch; }; var list = client.prefs["stalkWords"]; var ary = new Array(); ary.push(network.primServ.me.unicodeName.replace(/[^\w\d]/g, escapeChar)); for (var i = 0; i < list.length; ++i) ary.push(list[i].replace(/[^\w\d]/g, escapeChar)); var re; if (client.prefs["stalkWholeWords"]) re = "(^|[\\W\\s])((" + ary.join(")|(") + "))([\\W\\s]|$)"; else re = "(" + ary.join(")|(") + ")"; network.stalkExpression = new RegExp(re, "i"); } function getDefaultFontSize() { const PREF_CTRID = "@mozilla.org/preferences-service;1"; const nsIPrefService = Components.interfaces.nsIPrefService; const nsIPrefBranch = Components.interfaces.nsIPrefBranch; var prefSvc = Components.classes[PREF_CTRID].getService(nsIPrefService); var prefBranch = prefSvc.getBranch(null); // PX size pref: font.size.variable.x-western var pxSize = 16; try { pxSize = prefBranch.getIntPref("font.size.variable.x-western"); } catch(ex) { } var dpi = 96; try { // Get the DPI the fun way (make Mozilla do the work). var b = document.createElement("box"); b.style.width = "1in"; dpi = window.getComputedStyle(b, null).width.match(/^\d+/); } catch(ex) { try { // Get the DPI the fun way (make Mozilla do the work). b = document.createElementNS("box", XHTML_NS); b.style.width = "1in"; dpi = window.getComputedStyle(b, null).width.match(/^\d+/); } catch(ex) { } } return Math.round((pxSize / dpi) * 72); } function getDefaultContext(cx) { if (!cx) cx = new Object(); /* Use __proto__ here and in all other get*Context so that the command can * tell the difference between getObjectDetails and actual parameters. See * cmdJoin for more details. */ cx.__proto__ = getObjectDetails(client.currentObject); return cx; } function getMessagesContext(cx, element) { if (!cx) cx = new Object(); cx.__proto__ = getObjectDetails(client.currentObject); if (!element) element = document.popupNode; while (element) { switch (element.localName) { case "a": var href = element.getAttribute("href"); cx.url = href; break; case "tr": var nickname = element.getAttribute("msg-user"); if (!nickname) break; // strip out a potential ME! suffix var ary = nickname.match(/([^ ]+)/); nickname = ary[1]; if (!cx.network) break; // NOTE: nickname is the unicodeName here! if (cx.channel) cx.user = cx.channel.getUser(nickname); else cx.user = cx.network.getUser(nickname); if (cx.user) { cx.nickname = cx.user.unicodeName; cx.canonNick = cx.user.canonicalName; } else { cx.nickname = nickname; } break; } element = element.parentNode; } return cx; } function getTabContext(cx, element) { if (!cx) cx = new Object(); if (!element) element = document.popupNode; while (element) { if (element.localName == "tab") { cx.__proto__ = getObjectDetails(element.view); return cx; } element = element.parentNode; } return cx; } function getUserlistContext(cx) { if (!cx) cx = new Object(); cx.__proto__ = getObjectDetails(client.currentObject); if (!cx.channel) return cx; var user, tree = document.getElementById("user-list"); cx.userList = new Array(); cx.canonNickList = new Array(); cx.nicknameList = getSelectedNicknames(tree); for (var i = 0; i < cx.nicknameList.length; ++i) { user = cx.channel.getUser(cx.nicknameList[i]) cx.userList.push(user); cx.canonNickList.push(user.canonicalName); if (i == 0) { cx.user = user; cx.nickname = user.unicodeName; cx.canonNick = user.canonicalName; } } return cx; } function getSelectedNicknames(tree) { var rv = []; if (!tree || !tree.view || !tree.view.selection) return rv; var rangeCount = tree.view.selection.getRangeCount(); // Loop through the selection ranges. for (var i = 0; i < rangeCount; ++i) { var start = {}, end = {}; tree.view.selection.getRangeAt(i, start, end); // If they == -1, we've got no selection, so bail. if ((start.value == -1) && (end.value == -1)) continue; /* Workaround: Because we use select(-1) instead of clearSelection() * (see bug 197667) the tree will then give us selection ranges * starting from -1 instead of 0! (See bug 319066.) */ if (start.value == -1) start.value = 0; // Loop through the contents of the current selection range. for (var k = start.value; k <= end.value; ++k) rv.push(getNicknameForUserlistRow(k)); } return rv; } function getFontContext(cx) { if (!cx) cx = new Object(); cx.__proto__ = getObjectDetails(client.currentObject); cx.fontSizeDefault = getDefaultFontSize(); var view = client; if ("prefs" in cx.sourceObject) { cx.fontFamily = view.prefs["font.family"]; if (cx.fontFamily.match(/^(default|(sans-)?serif|monospace)$/)) delete cx.fontFamily; cx.fontSize = view.prefs["font.size"]; if (cx.fontSize == 0) delete cx.fontSize; } return cx; } function msgIsImportant (msg, sourceNick, network) { /* This is a huge hack, but it works. What we want is to match against the * plain text of a message, ignoring color codes, bold, etc. so we put it * through the munger. This produces a tree of HTML elements, which we use * |.innerHTML| to convert to a textual representation. * * Then we remove all the HTML tags, using a RegExp. * * It certainly isn't ideal, and there has to be a better way, but it: * a) works, and * b) is fast enough to not cause problems, * so it will do for now. * * Note also that we don't want to log URLs munged here, or generally do * any state-changing stuff. */ var plainMsg = client.munger.munge(msg, null, { noStateChange: true }); plainMsg = plainMsg.innerHTML.replace(/<[^>]+>/g, ""); var re = network.stalkExpression; if (plainMsg.search(re) != -1 || sourceNick && sourceNick.search(re) == 0) return true; return false; } function isStartupURL(url) { return arrayContains(client.prefs["initialURLs"], url); } function cycleView (amount) { var len = client.viewsArray.length; if (len <= 1) return; var tb = getTabForObject (client.currentObject); if (!tb) return; var vk = Number(tb.getAttribute("viewKey")); var destKey = (vk + amount) % len; /* wrap around */ if (destKey < 0) destKey += len; dispatch("set-current-view", { view: client.viewsArray[destKey].source }); } // Plays the sound for a particular event on a type of object. function playEventSounds(type, event) { if (!client.sound || !client.prefs["sound.enabled"]) return; // Converts .TYPE values into the event object names. // IRCChannel => channel, IRCUser => user, etc. if (type.match(/^IRC/)) type = type.substr(3, type.length).toLowerCase(); var ev = type + "." + event; if (ev in client.soundList) return; if (!(("sound." + ev) in client.prefs)) return; var s = client.prefs["sound." + ev]; if (!s) return; if (client.prefs["sound.overlapDelay"] > 0) { client.soundList[ev] = true; setTimeout("delete client.soundList['" + ev + "']", client.prefs["sound.overlapDelay"]); } if (event == "start") { blockEventSounds(type, "event"); blockEventSounds(type, "chat"); blockEventSounds(type, "stalk"); } playSounds(s); } // Blocks a particular type of event sound occuring. function blockEventSounds(type, event) { if (!client.sound || !client.prefs["sound.enabled"]) return; // Converts .TYPE values into the event object names. // IRCChannel => channel, IRCUser => user, etc. if (type.match(/^IRC/)) type = type.substr(3, type.length).toLowerCase(); var ev = type + "." + event; if (client.prefs["sound.overlapDelay"] > 0) { client.soundList[ev] = true; setTimeout("delete client.soundList['" + ev + "']", client.prefs["sound.overlapDelay"]); } } function playSounds(list) { var ary = list.split (" "); if (ary.length == 0) return; playSound(ary[0]); for (var i = 1; i < ary.length; ++i) setTimeout(playSound, 250 * i, ary[i]); } function playSound(file) { if (!client.sound || !client.prefs["sound.enabled"] || !file) return; if (file == "beep") { client.sound.beep(); } else { try { var uri = client.iosvc.newURI(file, null, null); client.sound.play(uri); } catch (ex) { // ignore exceptions from this pile of code. } } } /* timer-based mainloop */ function mainStep() { try { var count = client.eventPump.stepEvents(); if (count > 0) setTimeout("mainStep()", client.STEP_TIMEOUT); else setTimeout("mainStep()", client.STEP_TIMEOUT / 5); } catch(ex) { dd("Exception in mainStep!"); dd(formatException(ex)); setTimeout("mainStep()", client.STEP_TIMEOUT); } } function openQueryTab(server, nick) { var user = server.addUser(nick); if (client.globalHistory) client.globalHistory.addPage(user.getURL()); if (!("messages" in user)) { var value = ""; var same = true; for (var c in server.channels) { var chan = server.channels[c]; if (!(user.canonicalName in chan.users)) continue; /* This takes a boolean value for each channel (true - channel has * same value as first), and &&-s them all together. Thus, |same| * will tell us, at the end, if all the channels found have the * same value for charset. */ if (value) same = same && (value == chan.prefs["charset"]); else value = chan.prefs["charset"]; } /* If we've got a value, and it's the same accross all channels, * we use it as the *default* for the charset pref. If not, it'll * just keep the "defer" default which pulls it off the network. */ if (value && same) { user.prefManager.prefRecords["charset"].defaultValue = value; } user.displayHere (getMsg(MSG_QUERY_OPENED, user.unicodeName)); } user.whois(); return user; } function arraySpeak (ary, single, plural) { var rv = ""; var and = MSG_AND; switch (ary.length) { case 0: break; case 1: rv = ary[0]; if (single) rv += " " + single; break; case 2: rv = ary[0] + " " + and + " " + ary[1]; if (plural) rv += " " + plural; break; default: for (var i = 0; i < ary.length - 1; ++i) rv += ary[i] + ", "; rv += and + " " + ary[ary.length - 1]; if (plural) rv += " " + plural; break; } return rv; } function getObjectDetails (obj, rv) { if (!rv) rv = new Object(); if (!ASSERT(obj && typeof obj == "object", "INVALID OBJECT passed to getObjectDetails (" + obj + "). **")) { return rv; } rv.sourceObject = obj; rv.TYPE = obj.TYPE; rv.parent = ("parent" in obj) ? obj.parent : null; rv.user = null; rv.channel = null; rv.server = null; rv.network = null; switch (obj.TYPE) { case "IRCChannel": rv.viewType = MSG_CHANNEL; rv.channel = obj; rv.channelName = obj.unicodeName; rv.server = rv.channel.parent; rv.network = rv.server.parent; break; case "IRCUser": rv.viewType = MSG_USER; rv.user = obj; rv.userName = obj.unicodeName; rv.server = rv.user.parent; rv.network = rv.server.parent; break; case "IRCChanUser": rv.viewType = MSG_USER; rv.user = obj; rv.userName = obj.unicodeName; rv.channel = rv.user.parent; rv.server = rv.channel.parent; rv.network = rv.server.parent; break; case "IRCNetwork": rv.network = obj; rv.viewType = MSG_NETWORK; if ("primServ" in rv.network) rv.server = rv.network.primServ; else rv.server = null; break; case "IRCClient": rv.viewType = MSG_TAB; break; case "IRCDCCUser": //rv.viewType = MSG_USER; rv.user = obj; rv.userName = obj.unicodeName; break; case "IRCDCCChat": //rv.viewType = MSG_USER; rv.chat = obj; rv.user = obj.user; rv.userName = obj.unicodeName; break; case "IRCDCCFileTransfer": //rv.viewType = MSG_USER; rv.file = obj; rv.user = obj.user; rv.userName = obj.unicodeName; rv.fileName = obj.filename; break; default: /* no setup for unknown object */ break; } if (rv.network) rv.networkName = rv.network.unicodeName; return rv; } function findDynamicRule (selector) { var rules = frames[0].document.styleSheets[1].cssRules; if (isinstance(selector, RegExp)) fun = "search"; else fun = "indexOf"; for (var i = 0; i < rules.length; ++i) { var rule = rules.item(i); if (rule.selectorText && rule.selectorText[fun](selector) == 0) return {sheet: frames[0].document.styleSheets[1], rule: rule, index: i}; } return null; } function addDynamicRule (rule) { var rules = frames[0].document.styleSheets[1]; var pos = rules.cssRules.length; rules.insertRule (rule, pos); } function getCommandEnabled(command) { try { var dispatcher = document.commandDispatcher; var controller = dispatcher.getControllerForCommand(command); return controller.isCommandEnabled(command); } catch (e) { return false; } } function doCommand(command) { try { var dispatcher = document.commandDispatcher; var controller = dispatcher.getControllerForCommand(command); if (controller && controller.isCommandEnabled(command)) controller.doCommand(command); } catch (e) { } } function doCommandWithParams(command, params) { try { var dispatcher = document.commandDispatcher; var controller = dispatcher.getControllerForCommand(command); controller.QueryInterface(Components.interfaces.nsICommandController); if (!controller || !controller.isCommandEnabled(command)) return; var cmdparams = newObject("@mozilla.org/embedcomp/command-params;1", "nsICommandParams"); for (var i in params) cmdparams.setISupportsValue(i, params[i]); controller.doCommandWithParams(command, cmdparams); } catch (e) { } } var testURLs = ["irc:", "irc://", "irc:///", "irc:///help", "irc:///help,needkey", "irc://irc.foo.org", "irc://foo:6666", "irc://foo", "irc://irc.foo.org/", "irc://foo:6666/", "irc://foo/", "irc://irc.foo.org/,needpass", "irc://foo/,isserver", "irc://moznet/,isserver", "irc://moznet/", "irc://foo/chatzilla", "irc://foo/chatzilla/", "irc://irc.foo.org/?msg=hello%20there", "irc://irc.foo.org/?msg=hello%20there&ignorethis", "irc://irc.foo.org/%23mozilla,needkey?msg=hello%20there&ignorethis", "invalids", "irc://irc.foo.org/,isnick"]; function doURLTest() { for (var u in testURLs) { dd("testing url \"" + testURLs[u] + "\""); var o = parseIRCURL(testURLs[u]); if (!o) dd("PARSE FAILED!"); else dd(dumpObjectTree(o)); dd("---"); } } var testIRCURLObjects = [ [{}, "irc://"], [{host: "undernet"}, "irc://undernet/"], [{host: "irc.undernet.org"}, "irc://irc.undernet.org/"], [{host: "irc.undernet.org", isserver: true}, "irc://irc.undernet.org/"], [{host: "undernet", isserver: true}, "irc://undernet/,isserver"], [{host: "irc.undernet.org", port: 6667}, "irc://irc.undernet.org/"], [{host: "irc.undernet.org", port: 1}, "irc://irc.undernet.org:1/"], [{host: "irc.undernet.org", port: 1, scheme: "ircs"}, "ircs://irc.undernet.org:1/"], [{host: "irc.undernet.org", port: 9999, scheme: "ircs"}, "ircs://irc.undernet.org/"], [{host: "undernet", needpass: true}, "irc://undernet/,needpass"], [{host: "undernet", pass: "cz"}, "irc://undernet/?pass=cz"], [{host: "undernet", charset: "utf-8"}, "irc://undernet/?charset=utf-8"], [{host: "undernet", target: "#foo"}, "irc://undernet/%23foo"], [{host: "undernet", target: "#foo", needkey: true}, "irc://undernet/%23foo,needkey"], [{host: "undernet", target: "John", isnick: true}, "irc://undernet/John,isnick"], [{host: "undernet", target: "#foo", key: "cz"}, "irc://undernet/%23foo?key=cz"], [{host: "undernet", charset: "utf-8"}, "irc://undernet/?charset=utf-8"], [{host: "undernet", target: "John", msg: "spam!"}, "irc://undernet/John?msg=spam%21"], [{host: "undernet", target: "foo", isnick: true, msg: "spam!", pass: "cz"}, "irc://undernet/foo,isnick?msg=spam%21&pass=cz"] ]; function doObjectURLtest() { var passed = 0, total = testIRCURLObjects.length; for (var i = 0; i < total; i++) { var obj = testIRCURLObjects[i][0]; var url = testIRCURLObjects[i][1]; var parsedURL = constructIRCURL(obj) if (url != parsedURL) { display("Parsed IRC Object incorrectly! Expected '" + url + "', got '" + parsedURL, MT_ERROR); } else { passed++; } } display("Passed " + passed + " out of " + total + " tests (" + passed / total * 100 + "%).", MT_INFO); } function gotoIRCURL (url) { var urlspec = url; if (typeof url == "string") url = parseIRCURL(url); if (!url) { window.alert (getMsg(MSG_ERR_BAD_IRCURL, urlspec)); return; } if (!url.host) { /* focus the *client* view for irc:, irc:/, and irc:// (the only irc * urls that don't have a host. (irc:/// implies a connect to the * default network.) */ dispatch("client"); return; } var network; if (url.isserver) { var alreadyThere = false; var gettingThere = false; for (var n in client.networks) { if ((client.networks[n].isConnected()) && (client.networks[n].primServ.hostname == url.host) && (client.networks[n].primServ.port == url.port)) { /* already connected to this server/port */ network = client.networks[n]; gettingThere = true; // Have we actually had the 001 reply yet? alreadyThere = (client.networks[n].state == NET_ONLINE); break; } } if (!alreadyThere) { if (!gettingThere) { /* dd ("gotoIRCURL: not yet had connected to server" + url.host + " trying to connect..."); */ // Do we have a password, if needed? var pass = ""; if (url.needpass) { if (url.pass) { pass = url.pass; } else { pass = promptPassword(getMsg(MSG_HOST_PASSWORD, url.host)); } } var params = {hostname: url.host, port: url.port, password: pass}; var cmd = (url.scheme == "ircs" ? "sslserver" : "server"); network = dispatch(cmd, params); } if (!url.target) return; if (!("pendingURLs" in network)) network.pendingURLs = new Array(); network.pendingURLs.unshift(url); return; } } else { /* parsed as a network name */ if (!(url.host in client.networks)) { display(getMsg(MSG_ERR_UNKNOWN_NETWORK, url.host)); return; } network = client.networks[url.host]; if (network.state != NET_ONLINE) { /* dd ("gotoIRCURL: not already connected to " + "network " + url.host + " trying to connect..."); */ var secure = (url.scheme == "ircs" ? true : false); if (!network.isConnected()) client.connectToNetwork(network, secure); if (!url.target) return; if (!("pendingURLs" in network)) network.pendingURLs = new Array(); network.pendingURLs.unshift(url); return; } } /* already connected, do whatever comes next in the url */ //dd ("gotoIRCURL: connected, time to finish parsing ``" + url + "''"); if (url.target) { var targetObject; var ev; if (url.isnick) { /* url points to a person. */ var nick = url.target; var ary = url.target.split("!"); if (ary) nick = ary[0]; targetObject = network.dispatch("query", {nickname: nick}); } else { /* url points to a channel */ var key; if (url.needkey) { if (url.key) key = url.key; else key = window.promptPassword(getMsg(MSG_URL_KEY, url.spec)); } if (url.charset) { var d = { channelName: url.target, key: key, charset: url.charset }; targetObject = network.dispatch("join", d); } else { // Must do this the hard way... we have the server's format // for the channel name here, and all our commands only work // with the Unicode forms. var serv = network.primServ; var target = url.target; /* If we don't have a valid prefix, stick a "#" on it. * NOTE: This is always a "#" so that URLs may be compared * properly without involving the server (e.g. off-line). */ if ((arrayIndexOf(["#", "&", "+", "!"], target[0]) == -1) && (arrayIndexOf(serv.channelTypes, target[0]) == -1)) { target = "#" + target; } var chan = new CIRCChannel(serv, null, target); d = { channelName: chan.unicodeName, key: key, charset: url.charset }; targetObject = network.dispatch("join", d); } if (!targetObject) return; } if (url.msg) { var msg; if (url.msg.indexOf("\01ACTION") == 0) { msg = filterOutput(url.msg, "ACTION", targetObject); targetObject.display(msg, "ACTION", "ME!", client.currentObject); } else { msg = filterOutput(url.msg, "PRIVMSG", targetObject); targetObject.display(msg, "PRIVMSG", "ME!", client.currentObject); } targetObject.say(msg); dispatch("set-current-view", { view: targetObject }); } } else { if (!network.messages) network.displayHere (getMsg(MSG_NETWORK_OPENED, network.unicodeName)); dispatch("set-current-view", { view: network }); } } function updateProgress() { var busy; var progress = -1; if ("busy" in client.currentObject) busy = client.currentObject.busy; if ("progress" in client.currentObject) progress = client.currentObject.progress; if (!busy) progress = 0; client.progressPanel.collapsed = !busy; client.progressBar.mode = (progress < 0 ? "undetermined" : "determined"); if (progress >= 0) client.progressBar.value = progress; } function updateSecurityIcon() { var o = getObjectDetails(client.currentObject); var securityButton = window.document.getElementById("security-button"); securityButton.firstChild.value = ""; securityButton.removeAttribute("level"); securityButton.removeAttribute("tooltiptext"); if (!o.server || !o.server.isConnected) // No server or connection? { securityButton.setAttribute("tooltiptext", MSG_SECURITY_INFO); return; } var securityState = o.server.connection.getSecurityState() switch (securityState[0]) { case STATE_IS_SECURE: securityButton.firstChild.value = o.server.hostname; if (securityState[1] == STATE_SECURE_HIGH) securityButton.setAttribute("level", "high"); else // Because low security is the worst we have when being secure securityButton.setAttribute("level", "low"); // Add the tooltip: var issuer = o.server.connection.getCertificate().issuerOrganization; var tooltiptext = getMsg(MSG_SECURE_CONNECTION, issuer); securityButton.setAttribute("tooltiptext", tooltiptext); securityButton.firstChild.setAttribute("tooltiptext", tooltiptext); securityButton.lastChild.setAttribute("tooltiptext", tooltiptext); break; case STATE_IS_BROKEN: securityButton.setAttribute("level", "broken"); // No break to make sure we get the correct tooltip case STATE_IS_INSECURE: default: securityButton.setAttribute("tooltiptext", MSG_SECURITY_INFO); } } function initOfflineIcon() { const IOSVC2_CID = "@mozilla.org/network/io-service;1"; const PRBool_CID = "@mozilla.org/supports-PRBool;1"; const OS_CID = "@mozilla.org/observer-service;1"; const nsISupportsPRBool = Components.interfaces.nsISupportsPRBool; client.offlineObserver = { _element: document.getElementById("offline-status"), _getNewIOSvc: function offline_getNewIOSvc() { try { return getService(IOSVC2_CID, "nsIIOService2"); } catch (ex) {} // If it failed, it's probably just not there. We don't care. return null; }, state: function offline_state() { return (client.iosvc.offline ? "offline" : "online"); }, observe: function offline_observe(subject, topic, state) { if ((topic == "offline-requested") && (client.getConnectionCount() > 0)) { var buttonAry = [MSG_REALLY_GO_OFFLINE, MSG_DONT_GO_OFFLINE]; var rv = confirmEx(MSG_GOING_OFFLINE, buttonAry); if (rv == 1) // Don't go offline, please! { subject.QueryInterface(nsISupportsPRBool); subject.data = true; } } else if (topic == "network:offline-status-changed") { this.updateOfflineUI(); } }, updateOfflineUI: function offline_uiUpdate() { this._element.setAttribute("offlinestate", this.state()); var tooltipMsgId = "MSG_OFFLINESTATE_" + this.state().toUpperCase(); this._element.setAttribute("tooltiptext", window[tooltipMsgId]); }, toggleOffline: function offline_toggle() { // Check whether people are OK with us going offline: if (!client.iosvc.offline && !this.canGoOffline()) return; // Stop automatic management of the offline status, if existing. try { var ioSvc2 = this._getNewIOSvc(); if (ioSvc2 && ("manageOfflineStatus" in ioSvc2)) ioSvc2.manageOfflineStatus = false; } catch (ex) { dd("Turning off managed offline status failed!\n" + ex); } // Actually change the offline state. client.iosvc.offline = !client.iosvc.offline; // Update the pref: this.updatePrefFromOffline(); }, canGoOffline: function offline_check() { try { var os = getService(OS_CID, "nsIObserverService"); var canGoOffline = newObject(PRBool_CID, "nsISupportsPRBool"); os.notifyObservers(canGoOffline, "offline-requested", null); // Someone called for a halt if (canGoOffline.data) return false; } catch (ex) { dd("Exception when trying to ask if we could go offline:" + ex); } return true; }, updateOfflineFromPref: function offline_syncFromPref() { // On toolkit, we might have smart management of offline mode. // Don't interfere. var ioSvc2 = this._getNewIOSvc(); if (ioSvc2 && ioSvc2.manageOfflineStatus) return; // This is app-managed, or should be, on startup: if (client.host == "Mozilla") return; var isOffline = false; var prefSvc = getService("@mozilla.org/preferences-service;1", "nsIPrefBranch"); // Let the app-specific hacks begin: try { if (client.host == "XULrunner") isOffline = !prefSvc.getBoolPref("network.online"); else // Toolkit based, but not standalone isOffline = prefSvc.getBoolPref("browser.offline"); } catch (ex) { /* Whatever. */ } // Actually do it: client.iosvc.offline = isOffline; }, updatePrefFromOffline: function offline_syncToPref() { // This is app-managed, or should be. if (client.host == "Mozilla") return; var isOffline = client.iosvc.offline; var prefSvc = getService("@mozilla.org/preferences-service;1", "nsIPrefBranch"); // Let the app-specific hacks begin: try { if (client.host == "XULrunner") prefSvc.setBoolPref("network.online", !isOffline); else // Toolkit based, but not standalone prefSvc.setBoolPref("browser.offline", isOffline); } catch (ex) { dd("Couldn't set offline pref! Error:" + ex); } } }; try { var os = getService(OS_CID, "nsIObserverService"); os.addObserver(client.offlineObserver, "offline-requested", false); os.addObserver(client.offlineObserver, "network:offline-status-changed", false); } catch (ex) { dd("Exception when trying to register offline observers: " + ex); } var elem = client.offlineObserver._element; elem.setAttribute("onclick", "client.offlineObserver.toggleOffline()"); client.offlineObserver.updateOfflineFromPref(); client.offlineObserver.updateOfflineUI(); // Don't leak: delete os; delete elem; } function uninitOfflineIcon() { const OS_CID = "@mozilla.org/observer-service;1"; try { var os = getService(OS_CID, "nsIObserverService"); os.removeObserver(client.offlineObserver, "offline-requested", false); os.removeObserver(client.offlineObserver, "network:offline-status-changed", false); } catch (ex) { dd("Exception when trying to unregister offline observers: " + ex); } } function updateAppMotif(motifURL) { var node = document.firstChild; while (node && ((node.nodeType != node.PROCESSING_INSTRUCTION_NODE) || !(/name="dyn-motif"/).test(node.data))) { node = node.nextSibling; } motifURL = motifURL.replace(/"/g, "%22"); var dataStr = "href=\"" + motifURL + "\" name=\"dyn-motif\""; try { // No dynamic style node yet. if (!node) { node = document.createProcessingInstruction("xml-stylesheet", dataStr); document.insertBefore(node, document.firstChild); } else { node.data = dataStr; } } catch (ex) { dd(formatException(ex)); var err = ex.name; // Mozilla 1.0 doesn't like document.insertBefore(..., // document.firstChild); though it has a prototype for it - // check for the right error: if (err == "NS_ERROR_NOT_IMPLEMENTED") { display(MSG_NO_DYNAMIC_STYLE, MT_INFO); updateAppMotif = function() {}; } } } function updateSpellcheck(value) { value = value.toString(); document.getElementById("input").setAttribute("spellcheck", value); document.getElementById("multiline-input").setAttribute("spellcheck", value); } function updateNetwork() { var o = getObjectDetails (client.currentObject); var lag = MSG_UNKNOWN; var nick = ""; if (o.server) { if (o.server.me) nick = o.server.me.unicodeName; lag = (o.server.lag != -1) ? o.server.lag : MSG_UNKNOWN; } client.statusBar["header-url"].setAttribute("value", client.currentObject.getURL()); client.statusBar["header-url"].setAttribute("href", client.currentObject.getURL()); client.statusBar["header-url"].setAttribute("name", client.currentObject.unicodeName); } function updateTitle (obj) { if (!(("currentObject" in client) && client.currentObject) || (obj && obj != client.currentObject)) return; var tstring = MSG_TITLE_UNKNOWN; var o = getObjectDetails(client.currentObject); var net = o.network ? o.network.unicodeName : ""; var nick = ""; client.statusBar["server-nick"].disabled = false; switch (client.currentObject.TYPE) { case "IRCNetwork": var serv = "", port = ""; if (client.currentObject.isConnected()) { serv = o.server.hostname; port = o.server.port; if (o.server.me) nick = o.server.me.unicodeName; tstring = getMsg(MSG_TITLE_NET_ON, [nick, net, serv, port]); } else { nick = client.currentObject.INITIAL_NICK; tstring = getMsg(MSG_TITLE_NET_OFF, [nick, net]); } break; case "IRCChannel": var chan = "", mode = "", topic = ""; if ("me" in o.parent) { nick = o.parent.me.unicodeName; if (o.parent.me.canonicalName in client.currentObject.users) { var cuser = client.currentObject.users[o.parent.me.canonicalName]; if (cuser.isFounder) nick = "~" + nick; else if (cuser.isAdmin) nick = "&" + nick; else if (cuser.isOp) nick = "@" + nick; else if (cuser.isHalfOp) nick = "%" + nick; else if (cuser.isVoice) nick = "+" + nick; } } else { nick = MSG_TITLE_NONICK; } chan = o.channel.unicodeName; mode = o.channel.mode.getModeStr(); if (!mode) mode = MSG_TITLE_NO_MODE; topic = o.channel.topic ? o.channel.topic : MSG_TITLE_NO_TOPIC; var re = /\x1f|\x02|\x0f|\x16|\x03([0-9]{1,2}(,[0-9]{1,2})?)?/g; topic = topic.replace(re, ""); tstring = getMsg(MSG_TITLE_CHANNEL, [nick, chan, mode, topic]); break; case "IRCUser": nick = client.currentObject.unicodeName; var source = ""; if (client.currentObject.name) { source = "<" + client.currentObject.name + "@" + client.currentObject.host +">"; } tstring = getMsg(MSG_TITLE_USER, [nick, source]); nick = "me" in o.parent ? o.parent.me.unicodeName : MSG_TITLE_NONICK; break; case "IRCClient": nick = client.prefs["nickname"]; break; case "IRCDCCChat": client.statusBar["server-nick"].disabled = true; nick = o.chat.me.unicodeName; tstring = getMsg(MSG_TITLE_DCCCHAT, o.userName); break; case "IRCDCCFileTransfer": client.statusBar["server-nick"].disabled = true; nick = o.file.me.unicodeName; var data = [o.file.progress, o.file.filename, o.userName]; if (o.file.state.dir == 1) tstring = getMsg(MSG_TITLE_DCCFILE_SEND, data); else tstring = getMsg(MSG_TITLE_DCCFILE_GET, data); break; } if (0 && !client.uiState["tabstrip"]) { var actl = new Array(); for (var i in client.activityList) actl.push ((client.activityList[i] == "!") ? (Number(i) + 1) + "!" : (Number(i) + 1)); if (actl.length > 0) tstring = getMsg(MSG_TITLE_ACTIVITY, [tstring, actl.join (MSG_COMMASP)]); } document.title = tstring; client.statusBar["server-nick"].setAttribute("label", nick); } // Where 'right' is orientation, not wrong/right: function updateUserlistSide(shouldBeLeft) { var listParent = document.getElementById("tabpanels-contents-box"); var isLeft = (listParent.childNodes[0].id == "user-list-box"); if (isLeft == shouldBeLeft) return; if (shouldBeLeft) // Move from right to left. { listParent.insertBefore(listParent.childNodes[1], listParent.childNodes[0]); listParent.insertBefore(listParent.childNodes[2], listParent.childNodes[0]); listParent.childNodes[1].setAttribute("collapse", "before"); } else // Move from left to right. { listParent.appendChild(listParent.childNodes[1]); listParent.appendChild(listParent.childNodes[0]); listParent.childNodes[1].setAttribute("collapse", "after"); } var userlist = document.getElementById("user-list") if (client.currentObject && (client.currentObject.TYPE == "IRCChannel")) userlist.view = client.currentObject.userList; } function multilineInputMode (state) { var multiInput = document.getElementById("multiline-input"); var multiInputBox = document.getElementById("multiline-box"); var singleInput = document.getElementById("input"); var singleInputBox = document.getElementById("singleline-box"); var splitter = document.getElementById("input-splitter"); var iw = document.getElementById("input-widgets"); var h; client._mlMode = state; if (state) /* turn on multiline input mode */ { h = iw.getAttribute ("lastHeight"); if (h) iw.setAttribute ("height", h); /* restore the slider position */ singleInputBox.setAttribute ("collapsed", "true"); splitter.setAttribute ("collapsed", "false"); multiInputBox.setAttribute ("collapsed", "false"); // multiInput should have the same direction as singleInput multiInput.setAttribute("dir", singleInput.getAttribute("dir")); multiInput.value = (client.input ? client.input.value : ""); client.input = multiInput; } else /* turn off multiline input mode */ { h = iw.getAttribute ("height"); iw.setAttribute ("lastHeight", h); /* save the slider position */ iw.removeAttribute ("height"); /* let the slider drop */ splitter.setAttribute ("collapsed", "true"); multiInputBox.setAttribute ("collapsed", "true"); singleInputBox.setAttribute ("collapsed", "false"); // singleInput should have the same direction as multiInput singleInput.setAttribute("dir", multiInput.getAttribute("dir")); singleInput.value = (client.input ? client.input.value : ""); client.input = singleInput; } client.input.focus(); } function displayCertificateInfo() { var o = getObjectDetails(client.currentObject); if (!o.server) return; if (!o.server.isSecure) { alert(getMsg(MSG_INSECURE_SERVER, o.server.hostname)); return; } viewCert(o.server.connection.getCertificate()); } function newInlineText (data, className, tagName) { if (typeof tagName == "undefined") tagName = "html:span"; var a = document.createElementNS(XHTML_NS, tagName); if (className) a.setAttribute ("class", className); switch (typeof data) { case "string": a.appendChild (document.createTextNode (data)); break; case "object": for (var p in data) if (p != "data") a.setAttribute (p, data[p]); else a.appendChild (document.createTextNode (data[p])); break; case "undefined": break; default: ASSERT(0, "INVALID TYPE ('" + typeof data + "') passed to " + "newInlineText."); break; } return a; } function stringToMsg (message, obj) { var ary = message.split ("\n"); var span = document.createElementNS(XHTML_NS, "html:span"); var data = getObjectDetails(obj); if (ary.length == 1) client.munger.munge(ary[0], span, data); else { for (var l = 0; l < ary.length - 1; ++l) { client.munger.munge(ary[l], span, data); span.appendChild(document.createElementNS(XHTML_NS, "html:br")); } client.munger.munge(ary[l], span, data); } return span; } function getFrame() { if (client.deck.childNodes.length == 0) return undefined; var panel = client.deck.selectedPanel; return getContentWindow(panel); } client.__defineGetter__ ("currentFrame", getFrame); function setCurrentObject (obj) { if (!ASSERT(obj.messages, "INVALID OBJECT passed to setCurrentObject **")) return; if ("currentObject" in client && client.currentObject == obj) return; // Set window.content to make screenreader apps find the chat content. if (obj.frame && getContentWindow(obj.frame)) window.content = getContentWindow(obj.frame); var tb, userList; userList = document.getElementById("user-list"); if ("currentObject" in client && client.currentObject) tb = getTabForObject(client.currentObject); if (tb) tb.setAttribute("state", "normal"); client.currentObject = obj; /* If the splitter's collapsed, the userlist *isn't* visible, but we'll not * get told when it becomes visible, so update it even if it's only the * splitter visible. */ if (isVisible("user-list-box") || isVisible("main-splitter")) { userList.view = null; if (obj.TYPE == "IRCChannel") { userList.view = obj.userList; updateUserList(); } } tb = dispatch("create-tab-for-view", { view: obj }); if (tb) { tb.parentNode.selectedItem = tb; tb.setAttribute("state", "current"); } var vk = Number(tb.getAttribute("viewKey")); delete client.activityList[vk]; client.deck.selectedIndex = vk; // Style userlist and the like: updateAppMotif(obj.prefs["motif.current"]); updateTitle(); updateProgress(); updateSecurityIcon(); scrollDown(obj.frame, false); // Input area should have the same direction as the output area if (("frame" in client.currentObject) && client.currentObject.frame && getContentDocument(client.currentObject.frame) && ("body" in getContentDocument(client.currentObject.frame)) && getContentDocument(client.currentObject.frame).body) { var contentArea = getContentDocument(client.currentObject.frame).body; client.input.setAttribute("dir", contentArea.getAttribute("dir")); } client.input.focus(); } function checkScroll(frame) { var window = getContentWindow(frame); if (!window || !("document" in window)) return false; return (window.document.height - window.innerHeight - window.pageYOffset) < 160; } function scrollDown(frame, force) { var window = getContentWindow(frame); if (window && (force || checkScroll(frame))) window.scrollTo(0, window.document.height); } function advanceKeyboardFocus(amount) { var contentWin = getContentWindow(client.currentObject.frame); var contentDoc = getContentDocument(client.currentObject.frame); var userList = document.getElementById("user-list"); // Focus userlist, inputbox and outputwindow in turn: var focusableElems = [userList, client.input.inputField, contentWin]; var elem = document.commandDispatcher.focusedElement; // Finding focus in the content window is "hard". It's going to be null // if the window itself is focused, and "some element" inside of it if the // user starts tabbing through. if (!elem || (elem.ownerDocument == contentDoc)) elem = contentWin; var newIndex = (arrayIndexOf(focusableElems, elem) * 1 + 3 + amount) % 3; focusableElems[newIndex].focus(); // Make it obvious this element now has focus. var outlinedElem; if (focusableElems[newIndex] == client.input.inputField) outlinedElem = client.input.parentNode.id; else if (focusableElems[newIndex] == userList) outlinedElem = "user-list-box" else outlinedElem = "browser-box"; // Do magic, and make sure it gets undone at the right time: if (("focusedElemTimeout" in client) && client.focusedElemTimeout) clearTimeout(client.focusedElemTimeout); outlineFocusedElem(outlinedElem); client.focusedElemTimeout = setTimeout(outlineFocusedElem, 1000, ""); } function outlineFocusedElem(id) { var outlinedElements = ["user-list-box", "browser-box", "multiline-hug-box", "singleline-hug-box"]; for (var i = 0; i < outlinedElements.length; i++) { var elem = document.getElementById(outlinedElements[i]); if (outlinedElements[i] == id) elem.setAttribute("focusobvious", "true"); else elem.removeAttribute("focusobvious"); } client.focusedElemTimeout = 0; } /* valid values for |what| are "superfluous", "activity", and "attention". * final value for state is dependant on priority of the current state, and the * new state. the priority is: normal < superfluous < activity < attention. */ function setTabState(source, what, callback) { if (typeof source != "object") { if (!ASSERT(source in client.viewsArray, "INVALID SOURCE passed to setTabState")) return; source = client.viewsArray[source].source; } callback = callback || false; var tb = source.dispatch("create-tab-for-view", { view: source }); var vk = Number(tb.getAttribute("viewKey")); var current = ("currentObject" in client && client.currentObject == source); /* We want to play sounds if they're from a non-current view, or we don't * have focus at all. Also make sure stalk matches always play sounds. * Also make sure we don't play on the 2nd half of the flash (Callback). */ if (!callback && (!window.isFocused || !current || (what == "attention"))) { if (what == "attention") playEventSounds(source.TYPE, "stalk"); else if (what == "activity") playEventSounds(source.TYPE, "chat"); else if (what == "superfluous") playEventSounds(source.TYPE, "event"); } // Only change the tab's colour if it's not the active view. if (!current) { var state = tb.getAttribute("state"); if (state == what) { /* if the tab state has an equal priority to what we are setting * then blink it */ if (client.prefs["activityFlashDelay"] > 0) { tb.setAttribute("state", "normal"); setTimeout(setTabState, client.prefs["activityFlashDelay"], vk, what, true); } } else { if (state == "normal" || state == "superfluous" || (state == "activity" && what == "attention")) { /* if the tab state has a lower priority than what we are * setting, change it to the new state */ tb.setAttribute("state", what); /* we only change the activity list if priority has increased */ if (what == "attention") client.activityList[vk] = "!"; else if (what == "activity") client.activityList[vk] = "+"; else { /* this is functionally equivalent to "+" for now */ client.activityList[vk] = "-"; } updateTitle(); } else { /* the current state of the tab has a higher priority than the * new state. * blink the new lower state quickly, then back to the old */ if (client.prefs["activityFlashDelay"] > 0) { tb.setAttribute("state", what); setTimeout(setTabState, client.prefs["activityFlashDelay"], vk, state, true); } } } } } function notifyAttention (source) { if (typeof source != "object") source = client.viewsArray[source].source; if (client.currentObject != source) { var tb = dispatch("create-tab-for-view", { view: source }); var vk = Number(tb.getAttribute("viewKey")); tb.setAttribute ("state", "attention"); client.activityList[vk] = "!"; updateTitle(); } if (client.prefs["notify.aggressive"]) window.getAttention(); } function setDebugMode(mode) { if (mode.indexOf("t") != -1) client.debugHook.enabled = true; else client.debugHook.enabled = false; if (mode.indexOf("c") != -1) client.dbgContexts = true; else delete client.dbgContexts; if (mode.indexOf("d") != -1) client.dbgDispatch = true; else delete client.dbgDispatch; } function setListMode(mode) { var elem = document.getElementById("user-list"); if (mode) elem.setAttribute("mode", mode); else elem.removeAttribute("mode"); if (elem && elem.view && elem.treeBoxObject) { elem.treeBoxObject.clearStyleAndImageCaches(); elem.treeBoxObject.invalidate(); } } function updateUserList() { var node, chan; node = document.getElementById("user-list"); if (!node.view) return; if (("currentObject" in client) && client.currentObject && client.currentObject.TYPE == "IRCChannel") { reSortUserlist(client.currentObject); } } function reSortUserlist(channel) { if (!channel || !channel.userList) return; channel.userList.childData.reSort(); } function getNicknameForUserlistRow(index) { // This wouldn't be so hard if APIs didn't change so much... see bug 221619 var userlist = document.getElementById("user-list"); if (userlist.columns) var col = userlist.columns.getNamedColumn("usercol"); else col = "usercol"; return userlist.view.getCellText(index, col); } function getFrameForDOMWindow(window) { var frame; for (var i = 0; i < client.deck.childNodes.length; i++) { frame = client.deck.childNodes[i]; if (getContentWindow(frame) == window) return frame; } return undefined; } function replaceColorCodes(msg) { // find things that look like URLs and escape the color code inside of those to // prevent munging the URLs resulting in broken links msg = msg.replace(new RegExp(client.linkRE.source, "g"), function(url) { return url.replace(/%[BC][0-9A-Fa-f]/g, function(hex, index) { // as JS does not support lookbehind and we don't want to consume the // preceding character, we test for an existing %% manually return (("%" == url.substr(index - 1, 1)) ? "" : "%") + hex; }); }); // mIRC codes: underline, bold, Original (reset), colors, reverse colors. msg = msg.replace(/(^|[^%])%U/g, "$1\x1f"); msg = msg.replace(/(^|[^%])%B/g, "$1\x02"); msg = msg.replace(/(^|[^%])%O/g, "$1\x0f"); msg = msg.replace(/(^|[^%])%C/g, "$1\x03"); msg = msg.replace(/(^|[^%])%R/g, "$1\x16"); // %%[UBOCR] --> %[UBOCR]. msg = msg.replace(/%(%[UBOCR])/g, "$1"); return msg; } function decodeColorCodes(msg) { // %[UBOCR] --> %%[UBOCR]. msg = msg.replace(/(%[UBOCR])/g, "%$1"); // Put %-codes back in place of special character codes. msg = msg.replace(/\x1f/g, "%U"); msg = msg.replace(/\x02/g, "%B"); msg = msg.replace(/\x0f/g, "%O"); msg = msg.replace(/\x03/g, "%C"); msg = msg.replace(/\x16/g, "%R"); return msg; } client.progressListener = new Object(); client.progressListener.QueryInterface = function qi(iid) { return this; } client.progressListener.onStateChange = function client_statechange (webProgress, request, stateFlags, status) { const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener; const START = nsIWebProgressListener.STATE_START; const STOP = nsIWebProgressListener.STATE_STOP; const IS_NETWORK = nsIWebProgressListener.STATE_IS_NETWORK; const IS_DOCUMENT = nsIWebProgressListener.STATE_IS_DOCUMENT; var frame; // We only care about the initial start of loading, not the loading of // and page sub-components (IS_DOCUMENT, etc.). if ((stateFlags & START) && (stateFlags & IS_NETWORK) && (stateFlags & IS_DOCUMENT)) { frame = getFrameForDOMWindow(webProgress.DOMWindow); if (!frame) { dd("can't find frame for window (start)"); try { webProgress.removeProgressListener(this); } catch(ex) { dd("Exception removing progress listener (start): " + ex); } } } // We only want to know when the *network* stops, not the page's // individual components (STATE_IS_REQUEST/STATE_IS_DOCUMENT/somesuch). else if ((stateFlags & STOP) && (stateFlags & IS_NETWORK)) { frame = getFrameForDOMWindow(webProgress.DOMWindow); if (!frame) { dd("can't find frame for window (stop)"); try { webProgress.removeProgressListener(this); } catch(ex) { dd("Exception removing progress listener (stop): " + ex); } } else { var cwin = getContentWindow(frame); if (cwin && "initOutputWindow" in cwin) { cwin.getMsg = getMsg; cwin.initOutputWindow(client, frame.source, onMessageViewClick); cwin.changeCSS(frame.source.getFontCSS("data"), "cz-fonts"); scrollDown(frame, true); try { webProgress.removeProgressListener(this); } catch(ex) { dd("Exception removing progress listener (done): " + ex); } } // XXX: For about:blank it won't find initOutputWindow. Cope. else if (!cwin || !cwin.location || (cwin.location.href != "about:blank")) { // This should totally never ever happen. It will if we get in a // fight with xpcnativewrappers, though. Oops: dd("Couldn't find a content window or its initOutputWindow " + "function. This is BAD!"); } } } } client.progressListener.onProgressChange = function client_progresschange (webProgress, request, currentSelf, totalSelf, currentMax, selfMax) { } client.progressListener.onLocationChange = function client_locationchange (webProgress, request, uri) { } client.progressListener.onStatusChange = function client_statuschange (webProgress, request, status, message) { } client.progressListener.onSecurityChange = function client_securitychange (webProgress, request, state) { } function syncOutputFrame(obj, nesting) { const nsIWebProgress = Components.interfaces.nsIWebProgress; const WINDOW = nsIWebProgress.NOTIFY_STATE_WINDOW; const NETWORK = nsIWebProgress.NOTIFY_STATE_NETWORK; const ALL = nsIWebProgress.NOTIFY_ALL; var iframe = obj.frame; function tryAgain(nLevel) { syncOutputFrame(obj, nLevel); }; if (typeof nesting != "number") nesting = 0; if (nesting > 10) { dd("Aborting syncOutputFrame, taken too many tries."); return; } try { if (getContentDocument(iframe) && ("webProgress" in iframe)) { var url = obj.prefs["outputWindowURL"]; iframe.addProgressListener(client.progressListener, ALL); iframe.loadURI(url); } else { setTimeout(tryAgain, 500, nesting + 1); } } catch (ex) { dd("caught exception showing session view, will try again later."); dd(dumpObjectTree(ex) + "\n"); setTimeout(tryAgain, 500, nesting + 1); } } function createMessages(source) { playEventSounds(source.TYPE, "start"); source.messages = document.createElementNS(XHTML_NS, "html:table"); source.messages.setAttribute("class", "msg-table"); source.messages.setAttribute("view-type", source.TYPE); source.messages.setAttribute("role", "log"); source.messages.setAttribute("aria-live", "polite"); var tbody = document.createElementNS(XHTML_NS, "html:tbody"); source.messages.appendChild (tbody); source.messageCount = 0; } /* gets the toolbutton associated with an object * if |create| is present, and true, create if not found */ function getTabForObject (source, create) { var name; if (!ASSERT(source, "UNDEFINED passed to getTabForObject")) return null; if ("viewName" in source) { name = source.viewName; } else { ASSERT(0, "INVALID OBJECT passed to getTabForObject"); return null; } var tb, id = "tb[" + name + "]"; var matches = 1; for (var i in client.viewsArray) { if (client.viewsArray[i].source == source) { tb = client.viewsArray[i].tb; break; } else if (client.viewsArray[i].tb.getAttribute("id") == id) id = "tb[" + name + "<" + (++matches) + ">]"; } if (!tb && create) /* not found, create one */ { if (!("messages" in source) || source.messages == null) createMessages(source); var views = document.getElementById ("views-tbar-inner"); tb = document.createElement ("tab"); tb.setAttribute("ondraggesture", "nsDragAndDrop.startDrag(event, tabDNDObserver);"); tb.setAttribute("href", source.getURL()); tb.setAttribute("name", source.unicodeName); tb.setAttribute("onclick", "onTabClick(event, " + id.quote() + ");"); // This wouldn't be here if there was a supported CSS property for it. tb.setAttribute("crop", "center"); tb.setAttribute("context", "context:tab"); tb.setAttribute("tooltip", "xul-tooltip-node"); tb.setAttribute("class", "tab-bottom view-button"); tb.setAttribute("id", id); tb.setAttribute("state", "normal"); client.viewsArray.push ({source: source, tb: tb}); tb.setAttribute ("viewKey", client.viewsArray.length - 1); tb.view = source; if (matches > 1) tb.setAttribute("label", name + "<" + matches + ">"); else tb.setAttribute("label", name); views.appendChild(tb); var browser = document.createElement ("browser"); browser.setAttribute("class", "output-container"); browser.setAttribute("type", "content"); browser.setAttribute("flex", "1"); browser.setAttribute("tooltip", "html-tooltip-node"); browser.setAttribute("context", "context:messages"); //browser.setAttribute ("onload", "scrollDown(true);"); browser.setAttribute("onclick", "return onMessageViewClick(event)"); browser.setAttribute("onmousedown", "return onMessageViewMouseDown(event)"); browser.setAttribute("ondragover", "nsDragAndDrop.dragOver(event, " + "contentDropObserver);"); browser.setAttribute("ondragdrop", "nsDragAndDrop.drop(event, contentDropObserver);"); browser.setAttribute("ondraggesture", "nsDragAndDrop.startDrag(event, " + "contentAreaDNDObserver);"); browser.source = source; source.frame = browser; ASSERT(client.deck, "no deck?"); client.deck.appendChild (browser); syncOutputFrame (source); if (!("userList" in source) && (source.TYPE == "IRCChannel")) { source.userListShare = new Object(); source.userList = new XULTreeView(source.userListShare); source.userList.getCellProperties = ul_getcellprops; source.userList.childData.setSortDirection(1); } } return tb; } // Properties getter for user list tree view function ul_getcellprops(index, column, properties) { if ((index < 0) || (index >= this.childData.childData.length) || !properties) { return; } var userObj = this.childData.childData[index]._userObj; properties.AppendElement(client.atomSvc.getAtom("voice-" + userObj.isVoice)); properties.AppendElement(client.atomSvc.getAtom("op-" + userObj.isOp)); properties.AppendElement(client.atomSvc.getAtom("halfop-" + userObj.isHalfOp)); properties.AppendElement(client.atomSvc.getAtom("admin-" + userObj.isAdmin)); properties.AppendElement(client.atomSvc.getAtom("founder-" + userObj.isFounder)); properties.AppendElement(client.atomSvc.getAtom("away-" + userObj.isAway)); } var contentDropObserver = new Object(); contentDropObserver.onDragOver = function tabdnd_dover (aEvent, aFlavour, aDragSession) { if (aEvent.getPreventDefault()) return; if (aDragSession.sourceDocument == aEvent.view.document) { aDragSession.canDrop = false; return; } } contentDropObserver.onDrop = function tabdnd_drop (aEvent, aXferData, aDragSession) { var url = transferUtils.retrieveURLFromData(aXferData.data, aXferData.flavour.contentType); if (!url || url.search(client.linkRE) == -1) return; if (url.search(/\.css$/i) != -1 && confirm (getMsg(MSG_TABDND_DROP, url))) dispatch("motif", {"motif": url}); else if (url.search(/^ircs?:\/\//i) != -1) dispatch("goto-url", {"url": url}); } contentDropObserver.getSupportedFlavours = function tabdnd_gsf () { var flavourSet = new FlavourSet(); flavourSet.appendFlavour("text/x-moz-url"); flavourSet.appendFlavour("application/x-moz-file", "nsIFile"); flavourSet.appendFlavour("text/unicode"); return flavourSet; } var tabDNDObserver = new Object(); tabDNDObserver.onDragStart = function tabdnd_dstart (aEvent, aXferData, aDragAction) { var tb = aEvent.currentTarget; var href = tb.getAttribute("href"); var name = tb.getAttribute("name"); aXferData.data = new TransferData(); /* x-moz-url has the format "\n", goodie */ aXferData.data.addDataForFlavour("text/x-moz-url", href + "\n" + name); aXferData.data.addDataForFlavour("text/unicode", href); aXferData.data.addDataForFlavour("text/html", "" + name + ""); } var userlistDNDObserver = new Object(); userlistDNDObserver.onDragStart = function userlistdnd_dstart(event, transferData, dragAction) { var col = new Object(), row = new Object(), cell = new Object(); var tree = document.getElementById('user-list'); tree.treeBoxObject.getCellAt(event.clientX, event.clientY, row, col, cell); // Check whether we're actually on a normal row and cell if (!cell.value || (row.value == -1)) return; var nickname = getNicknameForUserlistRow(row.value); transferData.data = new TransferData(); transferData.data.addDataForFlavour("text/unicode", nickname); } function deleteTab (tb) { if (!ASSERT(tb.hasAttribute("viewKey"), "INVALID OBJECT passed to deleteTab (" + tb + ")")) { return null; } var i; var key = Number(tb.getAttribute("viewKey")); /* re-index higher toolbuttons */ for (i = key + 1; i < client.viewsArray.length; i++) { client.viewsArray[i].tb.setAttribute ("viewKey", i - 1); } arrayRemoveAt(client.viewsArray, key); var tbinner = document.getElementById("views-tbar-inner"); tbinner.removeChild(tb); return key; } function filterOutput(msg, msgtype, dest) { if ("outputFilters" in client) { for (var f in client.outputFilters) { if (client.outputFilters[f].enabled) msg = client.outputFilters[f].func(msg, msgtype, dest); } } return msg; } function updateTimestamps(view) { if (!("messages" in view)) return; view._timestampLast = ""; var node = view.messages.firstChild.firstChild; var nested; while (node) { if(node.className == "msg-nested-tr") { nested = node.firstChild.firstChild.firstChild.firstChild; while (nested) { updateTimestampFor(view, nested); nested = nested.nextSibling; } } else { updateTimestampFor(view, node); } node = node.nextSibling; } } function updateTimestampFor(view, displayRow) { var time = new Date(1 * displayRow.getAttribute("timestamp")); var tsCell = displayRow.firstChild; if (!tsCell) return; var fmt; if (view.prefs["timestamps"]) fmt = strftime(view.prefs["timestamps.display"], time); while (tsCell.lastChild) tsCell.removeChild(tsCell.lastChild); if (fmt && (!client.prefs["collapseMsgs"] || (fmt != view._timestampLast))) tsCell.appendChild(document.createTextNode(fmt)); view._timestampLast = fmt; } client.updateMenus = function c_updatemenus(menus) { // Don't bother if the menus aren't even created yet. if (!client.initialized) return null; return this.menuManager.updateMenus(document, menus); } client.adoptNode = function cli_adoptnode(node, doc) { try { doc.adoptNode(node); } catch(ex) { dd(formatException(ex)); var err = ex.name; // TypeError from before adoptNode was added; NOT_IMPL after. if ((err == "TypeError") || (err == "NS_ERROR_NOT_IMPLEMENTED")) client.adoptNode = cli_adoptnode_noop; } return node; } function cli_adoptnode_noop(node, doc) { return node; } client.addNetwork = function cli_addnet(name, serverList, temporary) { client.networks[name] = new CIRCNetwork(name, serverList, client.eventPump, temporary); } client.removeNetwork = function cli_removenet(name) { // Allow network a chance to clean up any mess. if (typeof client.networks[name].destroy == "function") client.networks[name].destroy(); delete client.networks[name]; } client.connectToNetwork = function cli_connect(networkOrName, requireSecurity) { var network; var name; if (isinstance(networkOrName, CIRCNetwork)) { network = networkOrName; } else { name = networkOrName; if (!(name in client.networks)) { display(getMsg(MSG_ERR_UNKNOWN_NETWORK, name), MT_ERROR); return null; } network = client.networks[name]; } name = network.unicodeName; if (!("messages" in network)) network.displayHere(getMsg(MSG_NETWORK_OPENED, name)); dispatch("set-current-view", { view: network }); if (network.isConnected()) { network.display(getMsg(MSG_ALREADY_CONNECTED, name)); return network; } if (network.state != NET_OFFLINE) return network; if (network.prefs["nickname"] == DEFAULT_NICK) network.prefs["nickname"] = prompt(MSG_ENTER_NICK, DEFAULT_NICK); if (!("connecting" in network)) network.display(getMsg(MSG_NETWORK_CONNECTING, name)); network.connect(requireSecurity); network.updateHeader(); client.updateHeader(); updateTitle(); return network; } client.getURL = function cli_geturl () { return "irc://"; } client.load = function cli_load(url, scope) { if (!("_loader" in client)) { const LOADER_CTRID = "@mozilla.org/moz/jssubscript-loader;1"; const mozIJSSubScriptLoader = Components.interfaces.mozIJSSubScriptLoader; var cls; if ((cls = Components.classes[LOADER_CTRID])) client._loader = cls.getService(mozIJSSubScriptLoader); } return client._loader.loadSubScript(url, scope); } client.sayToCurrentTarget = function cli_say(msg) { if ("say" in client.currentObject) { client.currentObject.dispatch("say", {message: msg}); return; } switch (client.currentObject.TYPE) { case "IRCClient": dispatch("eval", {expression: msg}); break; default: if (msg != "") display(MSG_ERR_NO_DEFAULT, MT_ERROR); break; } } CIRCNetwork.prototype.__defineGetter__("prefs", net_getprefs); function net_getprefs() { if (!("_prefs" in this)) { this._prefManager = getNetworkPrefManager(this); this._prefs = this._prefManager.prefs; } return this._prefs; } CIRCNetwork.prototype.__defineGetter__("prefManager", net_getprefmgr); function net_getprefmgr() { if (!("_prefManager" in this)) { this._prefManager = getNetworkPrefManager(this); this._prefs = this._prefManager.prefs; } return this._prefManager; } CIRCServer.prototype.__defineGetter__("prefs", srv_getprefs); function srv_getprefs() { return this.parent.prefs; } CIRCServer.prototype.__defineGetter__("prefManager", srv_getprefmgr); function srv_getprefmgr() { return this.parent.prefManager; } CIRCChannel.prototype.__defineGetter__("prefs", chan_getprefs); function chan_getprefs() { if (!("_prefs" in this)) { this._prefManager = getChannelPrefManager(this); this._prefs = this._prefManager.prefs; } return this._prefs; } CIRCChannel.prototype.__defineGetter__("prefManager", chan_getprefmgr); function chan_getprefmgr() { if (!("_prefManager" in this)) { this._prefManager = getChannelPrefManager(this); this._prefs = this._prefManager.prefs; } return this._prefManager; } CIRCUser.prototype.__defineGetter__("prefs", usr_getprefs); function usr_getprefs() { if (!("_prefs" in this)) { this._prefManager = getUserPrefManager(this); this._prefs = this._prefManager.prefs; } return this._prefs; } CIRCUser.prototype.__defineGetter__("prefManager", usr_getprefmgr); function usr_getprefmgr() { if (!("_prefManager" in this)) { this._prefManager = getUserPrefManager(this); this._prefs = this._prefManager.prefs; } return this._prefManager; } CIRCDCCUser.prototype.__defineGetter__("prefs", dccusr_getprefs); function dccusr_getprefs() { if (!("_prefs" in this)) { this._prefManager = getDCCUserPrefManager(this); this._prefs = this._prefManager.prefs; } return this._prefs; } CIRCDCCUser.prototype.__defineGetter__("prefManager", dccusr_getprefmgr); function dccusr_getprefmgr() { if (!("_prefManager" in this)) { this._prefManager = getDCCUserPrefManager(this); this._prefs = this._prefManager.prefs; } return this._prefManager; } CIRCDCCChat.prototype.__defineGetter__("prefs", dccchat_getprefs); function dccchat_getprefs() { return this.user.prefs; } CIRCDCCChat.prototype.__defineGetter__("prefManager", dccchat_getprefmgr); function dccchat_getprefmgr() { return this.user.prefManager; } CIRCDCCFileTransfer.prototype.__defineGetter__("prefs", dccfile_getprefs); function dccfile_getprefs() { return this.user.prefs; } CIRCDCCFileTransfer.prototype.__defineGetter__("prefManager", dccfile_getprefmgr); function dccfile_getprefmgr() { return this.user.prefManager; } CIRCNetwork.prototype.display = function net_display (message, msgtype, sourceObj, destObj) { var o = getObjectDetails(client.currentObject); if (client.SLOPPY_NETWORKS && client.currentObject != this && o.network == this && o.server && o.server.isConnected) { client.currentObject.display (message, msgtype, sourceObj, destObj); } else { this.displayHere (message, msgtype, sourceObj, destObj); } } CIRCUser.prototype.display = function usr_display(message, msgtype, sourceObj, destObj) { if ("messages" in this) { this.displayHere (message, msgtype, sourceObj, destObj); } else { var o = getObjectDetails(client.currentObject); if (o.server && o.server.isConnected && o.network == this.parent.parent && client.currentObject.TYPE != "IRCUser") client.currentObject.display (message, msgtype, sourceObj, destObj); else this.parent.parent.displayHere (message, msgtype, sourceObj, destObj); } } CIRCDCCChat.prototype.display = CIRCDCCFileTransfer.prototype.display = function dcc_display(message, msgtype, sourceObj, destObj) { var o = getObjectDetails(client.currentObject); if ("messages" in this) this.displayHere(message, msgtype, sourceObj, destObj); else client.currentObject.display(message, msgtype, sourceObj, destObj); } function feedback(e, message, msgtype, sourceObj, destObj) { if ("isInteractive" in e && e.isInteractive) display(message, msgtype, sourceObj, destObj); } CIRCChannel.prototype.feedback = CIRCNetwork.prototype.feedback = CIRCUser.prototype.feedback = CIRCDCCChat.prototype.feedback = CIRCDCCFileTransfer.prototype.feedback = client.feedback = function this_feedback(e, message, msgtype, sourceObj, destObj) { if ("isInteractive" in e && e.isInteractive) this.displayHere(message, msgtype, sourceObj, destObj); } function display (message, msgtype, sourceObj, destObj) { client.currentObject.display (message, msgtype, sourceObj, destObj); } client.getFontCSS = CIRCNetwork.prototype.getFontCSS = CIRCChannel.prototype.getFontCSS = CIRCUser.prototype.getFontCSS = CIRCDCCChat.prototype.getFontCSS = CIRCDCCFileTransfer.prototype.getFontCSS = function this_getFontCSS(format) { /* Wow, this is cool. We just put together a CSS-rule string based on the * font preferences. *This* is what CSS is all about. :) * We also provide a "data: URL" format, to simplify other code. */ var css; var fs; var fn; if (this.prefs["font.family"] != "default") fn = "font-family: " + this.prefs["font.family"] + ";"; else fn = "font-family: inherit;"; if (this.prefs["font.size"] != 0) fs = "font-size: " + this.prefs["font.size"] + "pt;"; else fs = "font-size: medium;"; css = "body.chatzilla-body { " + fs + fn + " }"; if (format == "data") return "data:text/css," + encodeURIComponent(css); return css; } client.display = client.displayHere = CIRCNetwork.prototype.displayHere = CIRCChannel.prototype.display = CIRCChannel.prototype.displayHere = CIRCUser.prototype.displayHere = CIRCDCCChat.prototype.displayHere = CIRCDCCFileTransfer.prototype.displayHere = function __display(message, msgtype, sourceObj, destObj) { // We need a message type, assume "INFO". if (!msgtype) msgtype = MT_INFO; var blockLevel = false; /* true if this row should be rendered at block * level, (like, if it has a really long nickname * that might disturb the rest of the layout) */ var o = getObjectDetails(this); /* get the skinny on |this| */ // Get the 'me' object, so we can be sure to get the attributes right. var me; if ("me" in this) me = this.me; else if (o.server && "me" in o.server) me = o.server.me; // Let callers get away with "ME!" and we have to substitute here. if (sourceObj == "ME!") sourceObj = me; if (destObj == "ME!") destObj = me; // Get the TYPE of the source object. var fromType = (sourceObj && sourceObj.TYPE) ? sourceObj.TYPE : "unk"; // Is the source a user? var fromUser = (fromType.search(/IRC.*User/) != -1); // Get some sort of "name" for the source. var fromAttr = ""; if (sourceObj) { if ("unicodeName" in sourceObj) fromAttr = sourceObj.unicodeName; else if ("name" in sourceObj) fromAttr = sourceObj.name; else fromAttr = sourceObj.viewName; } // Attach "ME!" if appropriate, so motifs can style differently. if (sourceObj == me) fromAttr = fromAttr + " ME!"; // Get the dest TYPE too... var toType = (destObj) ? destObj.TYPE : "unk"; // Is the dest a user? var toUser = (toType.search(/IRC.*User/) != -1); // Get a dest name too... var toAttr = ""; if (destObj) { if ("unicodeName" in destObj) toAttr = destObj.unicodeName; else if ("name" in destObj) toAttr = destObj.name; else toAttr = destObj.viewName; } // Also do "ME!" work for the dest. if (destObj && destObj == me) toAttr = me.unicodeName + " ME!"; /* isImportant means to style the messages as important, and flash the * window, getAttention means just flash the window. */ var isImportant = false, getAttention = false, isSuperfluous = false; var viewType = this.TYPE; var code; var time = new Date(); var timeStamp = strftime(this.prefs["timestamps.log"], time); // Statusbar text, and the line that gets saved to the log. var statusString; var logStringPfx = timeStamp + " "; var logStrings = new Array(); if (fromUser) { statusString = getMsg(MSG_FMT_STATUS, [timeStamp, sourceObj.unicodeName + "!" + sourceObj.name + "@" + sourceObj.host]); } else { var name; if (sourceObj) name = sourceObj.viewName; else name = this.viewName; statusString = getMsg(MSG_FMT_STATUS, [timeStamp, name]); } // The table row, and it's attributes. var msgRow = document.createElementNS(XHTML_NS, "html:tr"); msgRow.setAttribute("class", "msg"); msgRow.setAttribute("msg-type", msgtype); msgRow.setAttribute("msg-dest", toAttr); msgRow.setAttribute("dest-type", toType); msgRow.setAttribute("view-type", viewType); msgRow.setAttribute("statusText", statusString); msgRow.setAttribute("timestamp", Number(time)); if (fromAttr) { if (fromUser) msgRow.setAttribute("msg-user", fromAttr); else msgRow.setAttribute("msg-source", fromAttr); } // Timestamp cell. var msgRowTimestamp = document.createElementNS(XHTML_NS, "html:td"); msgRowTimestamp.setAttribute("class", "msg-timestamp"); var canMergeData; var msgRowSource, msgRowType, msgRowData; if (fromUser && msgtype.match(/^(PRIVMSG|ACTION|NOTICE)$/)) { var nick = sourceObj.unicodeName; var decorSt = ""; var decorEn = ""; // Set default decorations. if (msgtype == "ACTION") { decorSt = "* "; } else { decorSt = "<"; decorEn = ">"; } var nickURL; if ((sourceObj != me) && ("getURL" in sourceObj)) nickURL = sourceObj.getURL(); if (sourceObj != me) { // Not from us... if (destObj == me) { // ...but to us. Messages from someone else to us. getAttention = true; this.defaultCompletion = "/msg " + nick + " "; // If this is a private message, and it's not in a query view, // use *nick* instead of . if ((msgtype != "ACTION") && (this.TYPE != "IRCUser")) { decorSt = "*"; decorEn = "*"; } } else { // ...or to us. Messages from someone else to channel or similar. if ((typeof message == "string") && me) { isImportant = msgIsImportant(message, nick, o.network); if (isImportant) { this.defaultCompletion = nick + client.prefs["nickCompleteStr"] + " "; } } } } else { // Messages from us, on a channel or network view, to a user if (toUser && (this.TYPE != "IRCUser")) { nick = destObj.unicodeName; decorSt = ">"; decorEn = "<"; } } // Log the nickname in the same format as we'll let the user copy. logStringPfx += decorSt + nick + decorEn + " "; // Mark makes alternate "talkers" show up in different shades. //if (!("mark" in this)) // this.mark = "odd"; if (!("lastNickDisplayed" in this) || this.lastNickDisplayed != nick) { this.lastNickDisplayed = nick; this.mark = (("mark" in this) && this.mark == "even") ? "odd" : "even"; } msgRowSource = document.createElementNS(XHTML_NS, "html:td"); msgRowSource.setAttribute("class", "msg-user"); // Make excessive nicks get shunted. if (nick && (nick.length > client.MAX_NICK_DISPLAY)) blockLevel = true; if (decorSt) msgRowSource.appendChild(newInlineText(decorSt, "chatzilla-decor")); if (nickURL) { var nick_anchor = document.createElementNS(XHTML_NS, "html:a"); nick_anchor.setAttribute("class", "chatzilla-link"); nick_anchor.setAttribute("href", nickURL); nick_anchor.appendChild(newInlineText(nick)); msgRowSource.appendChild(nick_anchor); } else { msgRowSource.appendChild(newInlineText(nick)); } if (decorEn) msgRowSource.appendChild(newInlineText(decorEn, "chatzilla-decor")); canMergeData = this.prefs["collapseMsgs"]; } else { isSuperfluous = true; if (!client.debugHook.enabled && msgtype in client.responseCodeMap) { code = client.responseCodeMap[msgtype]; } else { if (!client.debugHook.enabled && client.HIDE_CODES) code = client.DEFAULT_RESPONSE_CODE; else code = "[" + msgtype + "]"; } /* Display the message code */ msgRowType = document.createElementNS(XHTML_NS, "html:td"); msgRowType.setAttribute("class", "msg-type"); msgRowType.appendChild(newInlineText(code)); logStringPfx += code + " "; } if (message) { msgRowData = document.createElementNS(XHTML_NS, "html:td"); msgRowData.setAttribute("class", "msg-data"); var tmpMsgs = message; if (typeof message == "string") { msgRowData.appendChild(stringToMsg(message, this)); } else { msgRowData.appendChild(message); tmpMsgs = tmpMsgs.innerHTML.replace(/<[^<]*>/g, ""); } tmpMsgs = tmpMsgs.split(/\r?\n/); for (var l = 0; l < tmpMsgs.length; l++) logStrings[l] = logStringPfx + tmpMsgs[l]; } if ("mark" in this) msgRow.setAttribute("mark", this.mark); if (isImportant) { msgRow.setAttribute("important", "true"); msgRow.setAttribute("aria-channel", "notify"); } // Timestamps first... msgRow.appendChild(msgRowTimestamp); // Now do the rest of the row, after block-level stuff. if (msgRowSource) msgRow.appendChild(msgRowSource); else msgRow.appendChild(msgRowType); if (msgRowData) msgRow.appendChild(msgRowData); updateTimestampFor(this, msgRow); if (blockLevel) { /* putting a div here crashes mozilla, so fake it with nested tables * for now */ var tr = document.createElementNS(XHTML_NS, "html:tr"); tr.setAttribute ("class", "msg-nested-tr"); var td = document.createElementNS(XHTML_NS, "html:td"); td.setAttribute ("class", "msg-nested-td"); td.setAttribute ("colspan", "3"); tr.appendChild(td); var table = document.createElementNS(XHTML_NS, "html:table"); table.setAttribute ("class", "msg-nested-table"); table.setAttribute("role", "presentation"); td.appendChild (table); var tbody = document.createElementNS(XHTML_NS, "html:tbody"); tbody.appendChild(msgRow); table.appendChild(tbody); msgRow = tr; } // Actually add the item. addHistory (this, msgRow, canMergeData); // Update attention states... if (isImportant || getAttention) { setTabState(this, "attention"); if (client.prefs["notify.aggressive"]) window.getAttention(); } else { if (isSuperfluous) { setTabState(this, "superfluous"); } else { setTabState(this, "activity"); } } // Copy Important Messages [to network view]. if (isImportant && client.prefs["copyMessages"] && (o.network != this)) { o.network.displayHere("{" + this.unicodeName + "} " + message, msgtype, sourceObj, destObj); } // Log file time! if (this.prefs["log"]) { if (!this.logFile) client.openLogFile(this); try { var LE = client.lineEnd; for (var l = 0; l < logStrings.length; l++) this.logFile.write(fromUnicode(logStrings[l] + LE, "utf-8")); } catch (ex) { // Stop logging before showing any messages! this.prefs["log"] = false; dd("Log file write error: " + formatException(ex)); this.displayHere(getMsg(MSG_LOGFILE_WRITE_ERROR, getLogPath(this)), "ERROR"); } } } function addHistory (source, obj, mergeData) { if (!("messages" in source) || (source.messages == null)) createMessages(source); var tbody = source.messages.firstChild; var appendTo = tbody; var needScroll = false; if (mergeData) { var inobj = obj; // This gives us the non-nested row when there is nesting. if (inobj.className == "msg-nested-tr") inobj = inobj.firstChild.firstChild.firstChild.firstChild; var thisUserCol = inobj.firstChild; while (thisUserCol && !thisUserCol.className.match(/^(msg-user|msg-type)$/)) thisUserCol = thisUserCol.nextSibling; var thisMessageCol = inobj.firstChild; while (thisMessageCol && !(thisMessageCol.className == "msg-data")) thisMessageCol = thisMessageCol.nextSibling; var ci = findPreviousColumnInfo(source.messages); var nickColumns = ci.nickColumns; var rowExtents = ci.extents; var nickColumnCount = nickColumns.length; var lastRowSpan, sameNick, sameDest, haveSameType, needSameType; var isAction, collapseActions; if (nickColumnCount == 0) // No message to collapse to. { sameNick = sameDest = needSameType = haveSameType = false; lastRowSpan = 0; } else // 1 or more messages, check for doubles { var lastRow = nickColumns[nickColumnCount - 1].parentNode; // What was the span last time? lastRowSpan = Number(nickColumns[0].getAttribute("rowspan")); // Are we the same user as last time? sameNick = (lastRow.getAttribute("msg-user") == inobj.getAttribute("msg-user")); // Do we have the same destination as last time? sameDest = (lastRow.getAttribute("msg-dest") == inobj.getAttribute("msg-dest")); // Is this message the same type as the last one? haveSameType = (lastRow.getAttribute("msg-type") == inobj.getAttribute("msg-type")); // Is either of the messages an action? We may not want to collapse // depending on the collapseActions pref isAction = ((inobj.getAttribute("msg-type") == "ACTION") || (lastRow.getAttribute("msg-type") == "ACTION")); // Do we collapse actions? collapseActions = source.prefs["collapseActions"]; // Does the motif collapse everything, regardless of type? // NOTE: the collapseActions pref can override this for actions needSameType = !(("motifSettings" in source) && source.motifSettings && ("collapsemore" in source.motifSettings)); } if (sameNick && sameDest && (haveSameType || !needSameType) && (!isAction || collapseActions)) { obj = inobj; if (ci.nested) appendTo = source.messages.firstChild.lastChild.firstChild.firstChild.firstChild; if (obj.getAttribute("important")) { nickColumns[nickColumnCount - 1].setAttribute("important", true); } // Remove nickname column from new row. obj.removeChild(thisUserCol); // Expand previous grouping's nickname cell(s) to fill-in the gap. for (var i = 0; i < nickColumns.length; ++i) nickColumns[i].setAttribute("rowspan", rowExtents.length + 1); } } if ("frame" in source) needScroll = checkScroll(source.frame); if (obj) appendTo.appendChild(client.adoptNode(obj, appendTo.ownerDocument)); if (source.MAX_MESSAGES) { if (typeof source.messageCount != "number") source.messageCount = 1; else source.messageCount++; if (source.messageCount > source.MAX_MESSAGES) { // Get the top of row 2, and subtract the top of row 1. var height = tbody.firstChild.nextSibling.firstChild.offsetTop - tbody.firstChild.firstChild.offsetTop; var window = getContentWindow(source.frame); var x = window.pageXOffset; var y = window.pageYOffset; tbody.removeChild (tbody.firstChild); --source.messageCount; while (tbody.firstChild && tbody.firstChild.childNodes[1] && tbody.firstChild.childNodes[1].getAttribute("class") == "msg-data") { --source.messageCount; tbody.removeChild (tbody.firstChild); } if (!checkScroll(source.frame) && (y > height)) window.scrollTo(x, y - height); } } if (needScroll) { scrollDown(source.frame, true); setTimeout(scrollDown, 500, source.frame, false); setTimeout(scrollDown, 1000, source.frame, false); setTimeout(scrollDown, 2000, source.frame, false); } } function findPreviousColumnInfo(table) { // All the rows in the grouping (for merged rows). var extents = new Array(); // Get the last row in the table. var tr = table.firstChild.lastChild; // Bail if there's no rows. if (!tr) return {extents: [], nickColumns: [], nested: false}; // Get message type. if (tr.className == "msg-nested-tr") { var rv = findPreviousColumnInfo(tr.firstChild.firstChild); rv.nested = true; return rv; } // Now get the read one... var className = (tr && tr.childNodes[1]) ? tr.childNodes[1].getAttribute("class") : ""; // Keep going up rows until you find the first in a group. // This will go up until it hits the top of a multiline/merged block. while (tr && tr.childNodes[1] && className.search(/msg-user|msg-type/) == -1) { extents.push(tr); tr = tr.previousSibling; if (tr && tr.childNodes[1]) className = tr.childNodes[1].getAttribute("class"); } // If we ran out of rows, or it's not a talking line, we're outta here. if (!tr || className != "msg-user") return {extents: [], nickColumns: [], nested: false}; extents.push(tr); // Time to collect the nick data... var nickCol = tr.firstChild; // All the cells that contain nickname info. var nickCols = new Array(); while (nickCol) { // Just collect nickname column cells. if (nickCol.getAttribute("class") == "msg-user") nickCols.push(nickCol); nickCol = nickCol.nextSibling; } // And we're done. return {extents: extents, nickColumns: nickCols, nested: false}; } function getLogPath(obj) { // If we're logging, return the currently-used URL. if (obj.logFile) return getURLSpecFromFile(obj.logFile.path); // If not, return the ideal URL. return getURLSpecFromFile(obj.prefs["logFileName"]); } client.getConnectionCount = function cli_gccount () { var count = 0; for (var n in client.networks) { if (client.networks[n].isConnected()) ++count; } return count; } client.quit = function cli_quit (reason) { var net, netReason; for (var n in client.networks) { net = client.networks[n]; if (net.isConnected()) { netReason = (reason ? reason : net.prefs["defaultQuitMsg"]); netReason = (netReason ? netReason : client.userAgent); net.quit(netReason); } } } client.wantToQuit = function cli_wantToQuit(reason, deliberate) { var close = true; if (client.prefs["warnOnClose"] && !deliberate) { const buttons = [MSG_QUIT_ANYWAY, MSG_DONT_QUIT]; var checkState = { value: true }; var rv = confirmEx(MSG_CONFIRM_QUIT, buttons, 0, MSG_WARN_ON_EXIT, checkState); close = (rv == 0); client.prefs["warnOnClose"] = checkState.value; } if (close) { client.userClose = true; display(MSG_CLOSING); client.quit(reason); } } /* gets a tab-complete match for the line of text specified by |line|. * wordStart is the position within |line| that starts the word being matched, * wordEnd marks the end position. |cursorPos| marks the position of the caret * in the textbox. */ client.performTabMatch = function gettabmatch_usr (line, wordStart, wordEnd, word, cursorPos) { if (wordStart != 0 || line[0] != client.COMMAND_CHAR) return null; var matches = client.commandManager.listNames(word.substr(1), CMD_CONSOLE); if (matches.length == 1 && wordEnd == line.length) { matches[0] = client.COMMAND_CHAR + matches[0] + " "; } else { for (var i in matches) matches[i] = client.COMMAND_CHAR + matches[i]; } return matches; } client.openLogFile = function cli_startlog (view) { function getNextLogFileDate() { var d = new Date(); d.setMilliseconds(0); d.setSeconds(0); d.setMinutes(0); switch (view.smallestLogInterval) { case "h": return d.setHours(d.getHours() + 1); case "d": d.setHours(0); return d.setDate(d.getDate() + 1); case "m": d.setHours(0); d.setDate(1); return d.setMonth(d.getMonth() + 1); case "y": d.setHours(0); d.setDate(1); d.setMonth(0); return d.setFullYear(d.getFullYear() + 1); } //XXXhack: This should work... return Infinity; }; const NORMAL_FILE_TYPE = Components.interfaces.nsIFile.NORMAL_FILE_TYPE; try { var file = new LocalFile(view.prefs["logFileName"]); if (!file.localFile.exists()) { // futils.umask may be 0022. Result is 0644. file.localFile.create(NORMAL_FILE_TYPE, 0666 & ~futils.umask); } view.logFile = fopen(file.localFile, ">>"); // If we're here, it's safe to say when we should re-open: view.nextLogFileDate = getNextLogFileDate(); } catch (ex) { view.prefs["log"] = false; dd("Log file open error: " + formatException(ex)); view.displayHere(getMsg(MSG_LOGFILE_ERROR, getLogPath(view)), MT_ERROR); return; } if (!("logFileWrapping" in view) || !view.logFileWrapping) view.displayHere(getMsg(MSG_LOGFILE_OPENED, getLogPath(view))); view.logFileWrapping = false; } client.closeLogFile = function cli_stoplog(view, wrapping) { if ("frame" in view && !wrapping) view.displayHere(getMsg(MSG_LOGFILE_CLOSING, getLogPath(view))); view.logFileWrapping = Boolean(wrapping); if (view.logFile) { view.logFile.close(); view.logFile = null; } } function checkLogFiles() { // For every view that has a logfile, check if we need a different file // based on the current date and the logfile preference. We close the // current logfile, and display will open the new one based on the pref // when it's needed. var d = new Date(); for (var n in client.networks) { var net = client.networks[n]; if (net.logFile && (d > net.nextLogFileDate)) client.closeLogFile(net, true); if (("primServ" in net) && net.primServ && ("channels" in net.primServ)) { for (var c in net.primServ.channels) { var chan = net.primServ.channels[c]; if (chan.logFile && (d > chan.nextLogFileDate)) client.closeLogFile(chan, true); } } if ("users" in net) { for (var u in net.users) { var user = net.users[u]; if (user.logFile && (d > user.nextLogFileDate)) client.closeLogFile(user, true); } } } for (var dc in client.dcc.chats) { var dccChat = client.dcc.chats[dc]; if (dccChat.logFile && (d > dccChat.nextLogFileDate)) client.closeLogFile(dccChat, true); } for (var df in client.dcc.files) { var dccFile = client.dcc.files[df]; if (dccFile.logFile && (d > dccFile.nextLogFileDate)) client.closeLogFile(dccFile, true); } // Don't forget about the client tab: if (client.logFile && (d > client.nextLogFileDate)) client.closeLogFile(client, true); // We use the same line again to make sure we keep a constant offset // from the full hour, in case the timers go crazy at some point. setTimeout("checkLogFiles()", 3602000 - (Number(new Date()) % 3600000)); } CIRCChannel.prototype.getLCFunction = CIRCNetwork.prototype.getLCFunction = CIRCUser.prototype.getLCFunction = CIRCDCCChat.prototype.getLCFunction = CIRCDCCFileTransfer.prototype.getLCFunction = function getlcfn() { var details = getObjectDetails(this); var lcFn; if (details.server) { lcFn = function(text) { return details.server.toLowerCase(text); } } return lcFn; } CIRCChannel.prototype.performTabMatch = CIRCNetwork.prototype.performTabMatch = CIRCUser.prototype.performTabMatch = CIRCDCCChat.prototype.performTabMatch = CIRCDCCFileTransfer.prototype.performTabMatch = function gettabmatch_other (line, wordStart, wordEnd, word, cursorpos, lcFn) { if (wordStart == 0 && line[0] == client.COMMAND_CHAR) { return client.performTabMatch(line, wordStart, wordEnd, word, cursorpos); } var matchList = new Array(); var users; var channels; var userIndex = -1; var details = getObjectDetails(this); if (details.channel && word == details.channel.unicodeName[0]) { /* When we have #, we just want the current channel, if possible. */ matchList.push(details.channel.unicodeName); } else { /* Ok, not # or no current channel, so get the full list. */ if (details.channel) users = details.channel.users; if (details.server) { channels = details.server.channels; for (var c in channels) matchList.push(channels[c].unicodeName); if (!users) users = details.server.users; } if (users) { userIndex = matchList.length; for (var n in users) matchList.push(users[n].unicodeName); } } var matches = matchEntry(word, matchList, lcFn); var list = new Array(); for (var i = 0; i < matches.length; i++) list.push(matchList[matches[i]]); if (list.length == 1) { if (users && (userIndex >= 0) && (matches[0] >= userIndex)) { if (wordStart == 0) list[0] += client.prefs["nickCompleteStr"]; } if (wordEnd == line.length) { /* add a space if the word is at the end of the line. */ list[0] += " "; } } return list; } PK pk7,Z-Z-content/chatzilla/networks.js/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is ChatZilla. * * The Initial Developer of the Original Code is James Ross. * Portions created by the Initial Developer are Copyright (C) 2006 * the Initial Developer. All Rights Reserved. * * Contributor(s): * James Ross * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ function initNetworks() { var networks = new Object(); // Set up default network list. networks["moznet"] = { displayName: "moznet", isupportsKey: "Mozilla", servers: [{hostname: "irc.mozilla.org", port:6667}, {hostname: "irc.mozilla.org", port:6697, isSecure: true}]}; networks["hybridnet"] = { displayName: "hybridnet", isupportsKey: "", servers: [{hostname: "irc.ssc.net", port: 6667}]}; networks["slashnet"] = { displayName: "slashnet", isupportsKey: "", servers: [{hostname: "irc.slashnet.org", port:6667}]}; networks["dalnet"] = { displayName: "dalnet", isupportsKey: "", servers: [{hostname: "irc.dal.net", port:6667}]}; networks["undernet"] = { displayName: "undernet", isupportsKey: "", servers: [{hostname: "irc.undernet.org", port:6667}]}; networks["webbnet"] = { displayName: "webbnet", isupportsKey: "", servers: [{hostname: "irc.webbnet.info", port:6667}]}; networks["quakenet"] = { displayName: "quakenet", isupportsKey: "", servers: [{hostname: "irc.quakenet.org", port:6667}]}; networks["ircnet"] = { displayName: "ircnet", isupportsKey: "", servers: [{hostname: "irc.ircnet.com", port:6667}]}; networks["freenode"] = { displayName: "freenode", isupportsKey: "", servers: [{hostname: "irc.freenode.net", port:6667}]}; networks["serenia"] = { displayName: "serenia", isupportsKey: "", servers: [{hostname: "chat.serenia.net", port:9999, isSecure: true}]}; networks["efnet"] = { displayName: "efnet", isupportsKey: "", servers: [{hostname: "irc.prison.net", port: 6667}, {hostname: "irc.magic.ca", port: 6667}]}; networks["hispano"] = { displayName: "hispano", isupportsKey: "", servers: [{hostname: "irc.irc-hispano.org", port: 6667}]}; for (var name in networks) networks[name].name = name; var builtInNames = keys(networks); var userNetworkList = new Array(); // Load the user's network list. var networksFile = new nsLocalFile(client.prefs["profilePath"]); networksFile.append("networks.txt"); if (networksFile.exists()) { var networksLoader = new TextSerializer(networksFile); if (networksLoader.open("<")) { var item = networksLoader.deserialize(); if (isinstance(item, Array)) userNetworkList = item; else dd("Malformed networks file!"); networksLoader.close(); } } // Merge the user's network list with the default ones. for (var i = 0; i < userNetworkList.length; i++) { var lowerNetName = userNetworkList[i].name.toLowerCase(); networks[lowerNetName] = userNetworkList[i]; networks[lowerNetName].name = lowerNetName; } /* Flag up all networks that are built-in, so they can be handled properly. * We need to do this last so that it ensures networks overridden by the * user's networks.txt are still flagged properly. */ for (var i = 0; i < builtInNames.length; i++) networks[builtInNames[i]].isBuiltIn = true; // Push network list over to client.networkList. client.networkList = new Array(); for (var name in networks) client.networkList.push(networks[name]); // Sync to client.networks. networksSyncFromList(); } function networksSyncToList() { // Stores indexes of networks that should be kept. var networkMap = new Object(); // Copy to and update client.networkList from client.networks. for (var name in client.networks) { var net = client.networks[name]; /* Skip temporary networks, as they're created to wrap standalone * servers only. */ if (net.temporary) continue; // Find the network in the networkList, if it exists. var listNet = null; for (var i = 0; i < client.networkList.length; i++) { if (client.networkList[i].name == name) { listNet = client.networkList[i]; networkMap[i] = true; break; } } // Network not in list, so construct a shiny new one. if (listNet == null) { var listNet = { name: name, displayName: name, isupportsKey: "" }; // Collect the RPL_ISUPPORTS "network name" if available. if (("primServ" in net) && net.primServ && ("supports" in net.primServ) && net.primServ.supports && ("network" in net.primServ.supports)) { listNet.isupportsKey = net.primServ.supports["network"]; } client.networkList.push(listNet); networkMap[client.networkList.length - 1] = true; } // Populate server list (no merging here). listNet.servers = new Array(); for (i = 0; i < net.serverList.length; i++) { var serv = net.serverList[i]; // Find this server in the list... var listServ = null; for (var j = 0; j < listNet.servers.length; j++) { if ((serv.hostname == listNet.servers[j].hostname) && (serv.port == listNet.servers[j].port)) { listServ = listNet.servers[j]; break; } } // ...and add a new one if it isn't found. if (listServ == null) { listServ = { hostname: serv.hostname, port: serv.port, isSecure: serv.isSecure, password: null }; listNet.servers.push(listServ); } listServ.isSecure = serv.isSecure; // Update the saved password (!! FIXME: plaintext password !!). listServ.password = serv.password; } } // Remove any no-longer existing networks. var index = 0; // (current pointer into client.networkList) var mapIndex = 0; // (original position pointer) while (index < client.networkList.length) { if (mapIndex in networkMap) { index++; mapIndex++; continue; } var listNet = client.networkList[index]; // Not seen this network in the client.networks collection. if (("isBuiltIn" in listNet) && listNet.isBuiltIn) { // Network is a built-in. Replace with dummy. client.networkList[index] = { name: listNet.name, isDeleted: true }; index++; } else { // Network is not a built-in, just nuke. // Note: don't do index++ here because we've removed the item. client.networkList.splice(index, 1); } mapIndex++; } } function networksSyncFromList() { var networkMap = new Object(); // Copy to and update client.networks from client.networkList. for (var i = 0; i < client.networkList.length; i++) { var listNet = client.networkList[i]; networkMap[listNet.name] = true; if ("isDeleted" in listNet) { /* This is a dummy entry that indicates a removed built-in network. * Remove the network from client.networks if it exists, and then * skip onto the next... */ if (listNet.name in client.networks) client.removeNetwork(listNet.name); continue; } // Create new network object if necessary. var net = null; if (!(listNet.name in client.networks)) client.addNetwork(listNet.name, []); // Get network object and make sure server list is empty. net = client.networks[listNet.name]; net.clearServerList(); // Make sure real network knows if it is a built-in one. if ("isBuiltIn" in listNet) net.isBuiltIn = listNet.isBuiltIn; // Update server list. for (var j = 0; j < listNet.servers.length; j++) { var listServ = listNet.servers[j]; // Make sure these exist. if (!("isSecure" in listServ)) listServ.isSecure = false; if (!("password" in listServ)) listServ.password = null; // NOTE: this must match the name given by CIRCServer. var servName = listServ.hostname + ":" + listServ.port; var serv = null; if (!(servName in net.servers)) { net.addServer(listServ.hostname, listServ.port, listServ.isSecure, listServ.password); } serv = net.servers[servName]; serv.isSecure = listServ.isSecure; serv.password = listServ.password; } } // Remove network objects that aren't in networkList. for (var name in client.networks) { // Skip temporary networks, as they don't matter. if (client.networks[name].temporary) continue; if (!(name in networkMap)) client.removeNetwork(name); } } function networksSaveList() { try { var networksFile = new nsLocalFile(client.prefs["profilePath"]); networksFile.append("networks.txt"); var networksLoader = new TextSerializer(networksFile); if (networksLoader.open(">")) { networksLoader.serialize(client.networkList); networksLoader.close(); } } catch(ex) { display("ERROR: " + formatException(ex), MT_ERROR); } } PK j+Srm@@content/chatzilla/dynamic.css/* empty css file. rules are appended to this dynamically */ PK tm7:KK!content/chatzilla/output-base.css/* -*- Mode: Text; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Chatzilla. * * The Initial Developer of the Original Code is * New Dimensions Consulting, Inc. * Portions created by the Initial Developer are Copyright (C) 1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Robert Ginda, rginda@ndcico.com, original author * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * This file contains the CSS rules for the output window in ChatZilla. * The output window is layed out as a table with two columns. The first * column holds a message type or a nickname, depending on what view the * message is contained by, and what kind of message it is. The second column * contains the text of the message. For most message types, ChatZilla displays * ascii-art instead of the actual code. For example, messages of type "JOIN" * are displayed as "-->|", and most unclassified message types are displayed * as "===". If you turn on debug messages (using the options->debug messages * menuitem) ChatZilla will always show the actual message type. This can be * helpful when styling a particular response from the IRC server. See the * description of the msg-type attribute below. * * You can modify these styles on your local system by placing your desired * styles in a file called chatzilla.css in your /chrome directory. * (the file won't be there already, you have to create it.) Add the line * * @import url(chatzilla.css); * * to the to your userContent.css (also in your /chrome directory, and * also not there unless you created it already.) End all CSS rules in your * new chatzilla.css with !important to override any styles declared here. * For example, on a Linux system, you would create a file called * /home//.mozilla//chrome/userContent.css (if it * doesn't already exist), and add the line @import url(chatzilla.css) to it. * Next, create /home//.mozilla//chrome/chatzilla.css, and * add the text: * * .msg { * font-size: 14pt !important; * } * * body.chatzilla-body { * background: green !important; * } * * Close your browser and restart. When you bring up ChatZilla, it should have * a 14pt font and a green background. * * To learn how to make more useful changes to the ChatZilla output style, read * on. * * All of the output in the message window is contained in an html . * New messages are composed of and
elements inside this . * The message table and it's children have the following CSS classes assigned * to them: * * + .msg-table is used as the class for the surrounding
. * Styles applied to this class will affect all parts of all messages. * * + .msg-nested-table is used as the class for the surrounding
for * messages sent from users with long nicknames. A new table is created, and * nested inside a of the .msg-table. The rows of this * table have their classes set as if they were direct children of the * .msg-table. Placing messages from users with long nicknames in a nested * table keeps the nickname column from getting too wide. * * + .msg is used as the class for the surrounding . This means that * any styles applied here will affect the entire message. * * + .msg-timestamp is used as the class for the
that has all the time * information on it. Styles on this class will affect the time stamps * against messages (but not the format of the time). * * + .msg-type is used as the class for the surrounding the message type * portion of messages. Styles applied here will only affect message * types. ie. "-->|", or "[HELP]". * * + .msg-user is used as the class for the surrounding the nickname * portion of messages. ChatZilla makes use of the :before and :after * pseudoclasses to decorate nicknames with different characters depending * on their message type. For example, when a user performs a /me, their * nickname may be surrounded by asterisks. * * + .msg-data is used as the class for the surrounding the actual text * of the message. * * In addition to CSS class properties, portions of a message may have one * or mode of the following attributes set: * * + view-type is the type of view the message is contained in. The types * are: * "IRCClient" for the *client* view * "IRCNetwork" for network and server views * "IRCChannel" for channel views * "IRCUser" for query views * * + msg-type is the message type described above. There are too many types * to list here. Turn on debug messages to force message types to print * in the left column of the output. * * + msg-user is the nickname (in lowercase) of the nickname who sent the * message. If you sent the message, msg-user will be set to "ME!", so * that you can style your messages regardless of your nickname. * * + msg-dest is the name of the object that the message is directed to. It * could be a channel name, or a nickname (both in lowercase.) * * + dest-type is the type of object the message is directed to. The types * are: * "IRCChannel" for messages sent to a channel. * "IRCUser" for messages sent directly to another user, including * private messages that appear in a network or channel view (when * a dedicated query view does not exist.) * * + mark is either the text "even" or "odd". When the first user speaks on * a channel, that message is marked as "even". Messages will continue to * be marked "even" until a different user speaks, when the mark switches * to "odd". Each view maintains it's own mark state. An example of how * ChatZilla marks messages would be: * * EVEN: this deep fat fry-o-later is great. * EVEN: It'll deep fat fry a whole buffalo in 30 seconds. * ODD: but I'm hungry *now*! * * + important is either the text "true", or it is not set at all. If * important is true, then the message triggered ChatZilla /stalk function. * This occurs when someone with a nickname matching a pattern in your * /stalk list speaks, when someone says a word that matches a pattern in * your /stalk list, or when someone says your nickname. * * */ /****************************************************************************** * basic classes * ******************************************************************************/ body.chatzilla-body { /* The topmost container in the ChatZilla */ margin: 0px 0px 0px 0px; /* output window. */ background: #FFFFFF; color: #000000; } a:link { color: #0000EE; } a:active { color: #EE0000; } a:visited { color: #551A8B; } /* links */ a.chatzilla-link { text-decoration: none; direction: ltr; unicode-bidi: embed; } /* link hover effect */ a.chatzilla-link:hover { text-decoration: underline; } /* basic styles */ .chatzilla-highlight[name="Large"] { font-size: larger; } .chatzilla-highlight[name="Small"] { font-size: smaller; } .chatzilla-highlight[name="Bold text"], .chatzilla-bold, a.chatzilla-bold.chatzilla-link { font-weight: bold; } .chatzilla-italic { font-style: italic; } .chatzilla-underline, a.chatzilla-underline.chatzilla-link { text-decoration: underline; } .chatzilla-teletype { font-family: monospace; } .chatzilla-rheet { font-weight: bold; } .chatzilla-decor { display: none; } /* mirc colors */ .chatzilla-fg00, a.chatzilla-fg00.chatzilla-link { color: #FFFFFF; } .chatzilla-fg01, a.chatzilla-fg01.chatzilla-link { color: #000000; } .chatzilla-fg02, a.chatzilla-fg02.chatzilla-link { color: #00007F; } .chatzilla-fg03, a.chatzilla-fg03.chatzilla-link { color: #009300; } .chatzilla-fg04, a.chatzilla-fg04.chatzilla-link { color: #FF0000; } .chatzilla-fg05, a.chatzilla-fg05.chatzilla-link { color: #7F0000; } .chatzilla-fg06, a.chatzilla-fg06.chatzilla-link { color: #9C009C; } .chatzilla-fg07, a.chatzilla-fg07.chatzilla-link { color: #FC7F00; } .chatzilla-fg08, a.chatzilla-fg08.chatzilla-link { color: #FFFF00; } .chatzilla-fg09, a.chatzilla-fg09.chatzilla-link { color: #00FC00; } .chatzilla-fg10, a.chatzilla-fg10.chatzilla-link { color: #009393; } .chatzilla-fg11, a.chatzilla-fg11.chatzilla-link { color: #00FFFF; } .chatzilla-fg12, a.chatzilla-fg12.chatzilla-link { color: #0000FC; } .chatzilla-fg13, a.chatzilla-fg13.chatzilla-link { color: #FF00FF; } .chatzilla-fg14, a.chatzilla-fg14.chatzilla-link { color: #7F7F7F; } .chatzilla-fg15, a.chatzilla-fg15.chatzilla-link { color: #D2D2D2; } .chatzilla-bg00, a.chatzilla-bg00.chatzilla-link { background-color: #FFFFFF; } .chatzilla-bg01, a.chatzilla-bg01.chatzilla-link { background-color: #000000; } .chatzilla-bg02, a.chatzilla-bg02.chatzilla-link { background-color: #00007F; } .chatzilla-bg03, a.chatzilla-bg03.chatzilla-link { background-color: #009300; } .chatzilla-bg04, a.chatzilla-bg04.chatzilla-link { background-color: #FF0000; } .chatzilla-bg05, a.chatzilla-bg05.chatzilla-link { background-color: #7F0000; } .chatzilla-bg06, a.chatzilla-bg06.chatzilla-link { background-color: #9C009C; } .chatzilla-bg07, a.chatzilla-bg07.chatzilla-link { background-color: #FC7F00; } .chatzilla-bg08, a.chatzilla-bg08.chatzilla-link { background-color: #FFFF00; } .chatzilla-bg09, a.chatzilla-bg09.chatzilla-link { background-color: #00FC00; } .chatzilla-bg10, a.chatzilla-bg10.chatzilla-link { background-color: #009393; } .chatzilla-bg11, a.chatzilla-bg11.chatzilla-link { background-color: #00FFFF; } .chatzilla-bg12, a.chatzilla-bg12.chatzilla-link { background-color: #0000FC; } .chatzilla-bg13, a.chatzilla-bg13.chatzilla-link { background-color: #FF00FF; } .chatzilla-bg14, a.chatzilla-bg14.chatzilla-link { background-color: #7F7F7F; } .chatzilla-bg15, a.chatzilla-bg15.chatzilla-link { background-color: #D2D2D2; } .chatzilla-control-char:before { content: "[\\"; } .chatzilla-control-char:after { content: "]"; } /* smiley faces */ .chatzilla-emote-txt { /* emoticon text inside */ display: none; } .chatzilla-emote:after { /* empty span to attach :after images to */ margin-left: 3px; margin-right: 3px; content: url(chrome://chatzilla/skin/images/face-question.png); } .chatzilla-emote[type="face-alien"]:after { content: url(chrome://chatzilla/skin/images/face-alien.png); } .chatzilla-emote[type="face-angry"]:after { content: url(chrome://chatzilla/skin/images/face-angry.png); } .chatzilla-emote[type="face-cry"]:after { content: url(chrome://chatzilla/skin/images/face-cry.png); } .chatzilla-emote[type="face-confused"]:after { content: url(chrome://chatzilla/skin/images/face-confused.png); } .chatzilla-emote[type="face-cool"]:after { content: url(chrome://chatzilla/skin/images/face-cool.png); } .chatzilla-emote[type="face-dizzy"]:after { content: url(chrome://chatzilla/skin/images/face-dizzy.png); } .chatzilla-emote[type="face-dizzy-back"]:after { content: url(chrome://chatzilla/skin/images/face-dizzy-back.png); } .chatzilla-emote[type="face-eek"]:after { content: url(chrome://chatzilla/skin/images/face-eek.png); } .chatzilla-emote[type="face-evil"]:after { content: url(chrome://chatzilla/skin/images/face-evil.png); } .chatzilla-emote[type="face-laugh"]:after { content: url(chrome://chatzilla/skin/images/face-laugh.png); } .chatzilla-emote[type="face-lol"]:after { content: url(chrome://chatzilla/skin/images/face-lol.png); } .chatzilla-emote[type="face-normal"]:after { content: url(chrome://chatzilla/skin/images/face-normal.png); } .chatzilla-emote[type="face-question"]:after { content: url(chrome://chatzilla/skin/images/face-question.png); } .chatzilla-emote[type="face-red"]:after { content: url(chrome://chatzilla/skin/images/face-red.png); } .chatzilla-emote[type="face-rofl"]:after { content: url(chrome://chatzilla/skin/images/face-rofl.png); } .chatzilla-emote[type="face-rolleyes"]:after { content: url(chrome://chatzilla/skin/images/face-rolleyes.png); } .chatzilla-emote[type="face-sad"]:after { content: url(chrome://chatzilla/skin/images/face-sad.png); } .chatzilla-emote[type="face-smile"]:after { content: url(chrome://chatzilla/skin/images/face-smile.png); } .chatzilla-emote[type="face-surprised"]:after { content: url(chrome://chatzilla/skin/images/face-surprised.png); } .chatzilla-emote[type="face-tongue"]:after { content: url(chrome://chatzilla/skin/images/face-tongue.png); } .chatzilla-emote[type="face-wink"]:after { content: url(chrome://chatzilla/skin/images/face-wink.png); } /****************************************************************************** * message class base definitions * ******************************************************************************/ .msg-table { /* containing all of the */ width: 100%; /* messages. */ } .msg-nested-table { /*
nested inside */ width: 100%; /* .msg-table for users with long */ margin: 0px; /* nicknames. */ border: 0px; border-spacing: 0px; padding: 0px; } .msg { /* .msg = a single message in the */ width: 100%; /* output window */ } .msg-timestamp { /* .msg-timestamp = timestamp for */ font-style: normal !important; /* the message, done using */ vertical-align: top; /* :before and content. */ white-space: nowrap; } .msg-type { /* .msg-type = message type */ font-variant: small-caps; /* indicator */ font-size: 90%; padding-right: 10px; text-align: right; vertical-align: top; white-space: nowrap; } .msg-user { /* msg-user = nickname portion of */ text-align: right; /* a message (channel and query */ vertical-align: top; /* views) */ } .msg-data { /* .msg-data = the text portion */ padding: 1px 1px 1px 3px; /* of a message */ width: 100%; white-space: -moz-pre-wrap; } /****************************************************************************** * message class specific definitions * ******************************************************************************/ /* msg-user is the nickname of the person who spoke, or "ME!" if you said it. * msg-type is the type of the message, taken from the irc message. If you * turn on debug messages (options->debug messages), the msg-types will be * displayed to the left of the messages for all messages except: * PRIVMSG: when a user sends you, or a channel you are on a message. * ACTION: when a user performs a /me. * NOTIFY: when a server or user sends you a notification. */ .msg[msg-user="|"] .msg-data, /* messages from common "bulk */ .msg[msg-user="||"] .msg-data, /* paste" nicks */ .msg[msg-user="|||"] .msg-data, .msg[msg-user="]"] .msg-data, .msg[msg-user="["] .msg-data, .msg[msg-type="372"] .msg-data, /* MOTD */ .msg[msg-type="EVAL-IN"] .msg-data, /* /eval results */ .msg[msg-type="EVAL-OUT"] .msg-data { font-size: 90%; font-family: monospace; } .msg[msg-type="USAGE"] .msg-data { font-style: italic; } .msg[msg-type="HELP"] .msg-data { font-weight: normal; } .msg[msg-type="ACTION"] .msg-user { font-style: italic; } .msg[important="true"] .msg-user { font-weight: bold; } /****************************************************************************** * nickname decorations * ******************************************************************************/ /* :before and :after pseudoclasses form the decorations around nicknames */ .msg-user:before { content: "<"; } .msg-user:after { content: ">"; } .msg[important="true"] .msg-user:before { font-weight: bold; } .msg[important="true"] .msg-user:after { font-weight: bold; } .msg[msg-user$="ME!"] .msg-user:before { content: "<"; } .msg[msg-user$="ME!"] .msg-user:after { content: ">"; } .msg[msg-type="ACTION"] .msg-user:before, .msg[msg-type="ACTION"] .msg-user:after { content: ""; } .msg[msg-type="NOTICE"] .msg-user:before { content: "["; } .msg[msg-type="NOTICE"] .msg-user:after { content: "]"; } /* private messages *not* in a query window */ .msg[dest-type="IRCUser"] .msg-user:before { content: "to("; } .msg[dest-type="IRCUser"][msg-dest$="ME!"] .msg-user:before { content: "from("; } .msg[dest-type="IRCUser"] .msg-user:after { content: ")"; } /* private messages in a query window */ .msg[view-type="IRCUser"] .msg-user:before { content: "{"; } .msg[view-type="IRCUser"] .msg-user:after { content: "}"; } .msg[view-type="IRCUser"][msg-dest$="ME!"] .msg-user:before { content: "{"; } .msg[view-type="IRCUser"][msg-dest$="ME!"] .msg-user:after { content: "}"; } PK 52 PK \/1Q] ] content/chatzilla/pref-irc.xul