bug 165901, "Clicking on network/channel name has no effect" bug 165875, "Support CTCP CLIENTINFO in Chatzilla" bug 165868, "Support CTCP TIME in Chatzilla" bug 137892, "Pref for new tab threshold is ignored" fix strict warning in static.js git-svn-id: svn://10.0.0.236/trunk@128768 18797224-902f-48f8-a5cc-f745e15eee43
2918 lines
88 KiB
JavaScript
2918 lines
88 KiB
JavaScript
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
*
|
|
* 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 New Dimensions Consulting, Inc. are
|
|
* Copyright (C) 1999 New Dimenstions Consulting, Inc. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Robert Ginda, rginda@netscape.com, original author
|
|
* Chiaki Koufugata chiaki@mozilla.gr.jp UI i18n
|
|
* Samuel Sieb, samuel@sieb.net, MIRC color codes, munger menu, and various
|
|
*/
|
|
|
|
if (DEBUG)
|
|
dd = function (m) { dump ("-*- chatzilla: " + m + "\n"); }
|
|
else
|
|
dd = function (){};
|
|
|
|
var client = new Object();
|
|
|
|
const MSG_CSP = getMsg ("commaSpace", " ");
|
|
const MSG_NONE = getMsg ("none");
|
|
const MSG_UNKNOWN = getMsg ("unknown");
|
|
|
|
client.defaultNick = getMsg( "defaultNick" );
|
|
|
|
client.version = "0.8.10";
|
|
|
|
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.PRINT_DIRECTION = 1; /*1 => new messages at bottom, -1 => at top */
|
|
client.ADDRESSED_NICK_SEP = ":";
|
|
|
|
client.MAX_MSG_PER_ROW = 3; /* default number of messages to collapse into a
|
|
* single row, max. */
|
|
client.INITIAL_COLSPAN = 5; /* MAX_MSG_PER_ROW cannot grow to greater than
|
|
* one half INITIAL_COLSPAN + 1. */
|
|
client.COLLAPSE_ROWS = false;
|
|
client.NOTIFY_TIMEOUT = 5 * 60 * 1000; /* update notify list every 5 minutes */
|
|
|
|
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.IMAGEDIR = "chrome://chatzilla/skin/images/";
|
|
client.HIDE_CODES = true; /* true if you'd prefer to show numeric response
|
|
* codes as some default value (ie, "===") */
|
|
client.DEFAULT_RESPONSE_CODE = "===";
|
|
|
|
|
|
/* XXX maybe move this into css */
|
|
client.responseCodeMap = new Object();
|
|
client.responseCodeMap["HELLO"] = getMsg("responseCodeMapHello");
|
|
client.responseCodeMap["HELP"] = getMsg("responseCodeMapHelp");
|
|
client.responseCodeMap["USAGE"] = getMsg("responseCodeMapUsage");
|
|
client.responseCodeMap["ERROR"] = getMsg("responseCodeMapError");
|
|
client.responseCodeMap["WARNING"] = getMsg("responseCodeMapWarning");
|
|
client.responseCodeMap["INFO"] = getMsg("responseCodeMapInfo");
|
|
client.responseCodeMap["EVAL-IN"] = getMsg("responseCodeMapEvalIn");
|
|
client.responseCodeMap["EVAL-OUT"] = getMsg("responseCodeMapEvalOut");
|
|
client.responseCodeMap["JOIN"] = "-->|";
|
|
client.responseCodeMap["PART"] = "<--|";
|
|
client.responseCodeMap["QUIT"] = "|<--";
|
|
client.responseCodeMap["NICK"] = "=-=";
|
|
client.responseCodeMap["TOPIC"] = "=-=";
|
|
client.responseCodeMap["KICK"] = "=-=";
|
|
client.responseCodeMap["MODE"] = "=-=";
|
|
client.responseCodeMap["END_STATUS"] = "---";
|
|
client.responseCodeMap["315"] = "---"; /* end of WHO */
|
|
client.responseCodeMap["318"] = "---"; /* end of WHOIS */
|
|
client.responseCodeMap["366"] = "---"; /* end of NAMES */
|
|
client.responseCodeMap["376"] = "---"; /* end of MOTD */
|
|
|
|
client.name = getMsg("clientname");
|
|
client.viewsArray = new Array();
|
|
client.activityList = new Object();
|
|
client.uiState = new Object(); /* state of ui elements (visible/collapsed) */
|
|
client.inputHistory = new Array();
|
|
client.lastHistoryReferenced = -1;
|
|
client.incompleteLine = "";
|
|
client.lastTabUp = new Date();
|
|
client.stalkingVictims = new Array();
|
|
|
|
CIRCNetwork.prototype.INITIAL_NICK = client.defaultNick;
|
|
CIRCNetwork.prototype.INITIAL_NAME = "chatzilla";
|
|
CIRCNetwork.prototype.INITIAL_DESC = getMsg("circnetworkInitialDesc");
|
|
CIRCNetwork.prototype.INITIAL_CHANNEL = "";
|
|
CIRCNetwork.prototype.MAX_MESSAGES = 100;
|
|
CIRCNetwork.prototype.IGNORE_MOTD = false;
|
|
|
|
CIRCServer.prototype.READ_TIMEOUT = 0;
|
|
CIRCServer.prototype.VERSION_RPLY = getMsg("circserverVersionRply",
|
|
[client.version,
|
|
navigator.userAgent]);
|
|
CIRCUser.prototype.MAX_MESSAGES = 200;
|
|
|
|
CIRCChannel.prototype.MAX_MESSAGES = 300;
|
|
|
|
CIRCChanUser.prototype.MAX_MESSAGES = 200;
|
|
|
|
window.onresize =
|
|
function ()
|
|
{
|
|
for (var i = 0; i < client.deck.childNodes.length; i++)
|
|
scrollDown(client.deck.childNodes[i], true);
|
|
}
|
|
|
|
function ucConvertIncomingMessage (e)
|
|
{
|
|
e.meat = toUnicode(e.meat);
|
|
return true;
|
|
}
|
|
|
|
function toUnicode (msg)
|
|
{
|
|
if (!("ucConverter" in client))
|
|
return msg;
|
|
|
|
/* XXX set charset again to force the encoder to reset, see bug 114923 */
|
|
client.ucConverter.charset = client.CHARSET;
|
|
try
|
|
{
|
|
return client.ucConverter.ConvertToUnicode(msg);
|
|
}
|
|
catch (ex)
|
|
{
|
|
dd ("caught exception " + ex + " converting " + msg + " to charset " +
|
|
client.CHARSET);
|
|
}
|
|
|
|
return msg;
|
|
}
|
|
|
|
function fromUnicode (msg)
|
|
{
|
|
if (!("ucConverter" in client))
|
|
return msg;
|
|
|
|
/* XXX set charset again to force the encoder to reset, see bug 114923 */
|
|
client.ucConverter.charset = client.CHARSET;
|
|
return client.ucConverter.ConvertFromUnicode(msg);
|
|
}
|
|
|
|
function setCharset (charset)
|
|
{
|
|
client.CHARSET = charset;
|
|
|
|
if (!charset)
|
|
{
|
|
delete client.ucConverter;
|
|
client.eventPump.removeHookByName("uc-hook");
|
|
return true;
|
|
}
|
|
|
|
var ex;
|
|
|
|
try
|
|
{
|
|
|
|
if (!("ucConverter" in client))
|
|
{
|
|
const UC_CTRID = "@mozilla.org/intl/scriptableunicodeconverter";
|
|
const nsIUnicodeConverter =
|
|
Components.interfaces.nsIScriptableUnicodeConverter;
|
|
client.ucConverter =
|
|
Components.classes[UC_CTRID].getService(nsIUnicodeConverter);
|
|
}
|
|
|
|
client.ucConverter.charset = charset;
|
|
|
|
if (!client.eventPump.getHook("uc-hook"))
|
|
{
|
|
client.eventPump.addHook ([{type: "parseddata", set: "server"}],
|
|
ucConvertIncomingMessage, "uc-hook");
|
|
}
|
|
}
|
|
catch (ex)
|
|
{
|
|
dd ("Caught exception setting charset to " + charset + "\n" + ex);
|
|
delete client.ucConverter;
|
|
client.CHARSET = "";
|
|
client.eventPump.removeHookByName("uc-hook");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function initStatic()
|
|
{
|
|
var obj;
|
|
|
|
const nsISound = Components.interfaces.nsISound;
|
|
client.sound =
|
|
Components.classes["@mozilla.org/sound;1"].createInstance(nsISound);
|
|
|
|
if (client.CHARSET)
|
|
setCharset(client.CHARSET);
|
|
|
|
var ary = navigator.userAgent.match (/;\s*([^;\s]+\s*)\).*\/(\d+)/);
|
|
if (ary)
|
|
client.userAgent = "ChatZilla " + client.version + " [Mozilla " +
|
|
ary[1] + "/" + ary[2] + "]";
|
|
else
|
|
client.userAgent = "ChatZilla " + client.version + " [" +
|
|
navigator.userAgent + "]";
|
|
|
|
obj = document.getElementById("input");
|
|
obj.addEventListener("keypress", onInputKeyPress, false);
|
|
obj = document.getElementById("multiline-input");
|
|
obj.addEventListener("keypress", onMultilineInputKeyPress, false);
|
|
obj = document.getElementById("channel-topicedit");
|
|
obj.addEventListener("keypress", onTopicKeyPress, false);
|
|
obj.active = false;
|
|
|
|
window.onkeypress = onWindowKeyPress;
|
|
|
|
setMenuCheck ("menu-dmessages",
|
|
client.eventPump.getHook ("event-tracer").enabled);
|
|
setMenuCheck ("menu-munger-global", !client.munger.enabled);
|
|
setMenuCheck ("menu-colors", client.enableColors);
|
|
|
|
setupMungerMenu(client.munger);
|
|
|
|
client.uiState["tabstrip"] =
|
|
setMenuCheck ("menu-view-tabstrip", isVisible("view-tabs"));
|
|
client.uiState["info"] =
|
|
setMenuCheck ("menu-view-info", isVisible("user-list-box"));
|
|
client.uiState["header"] =
|
|
setMenuCheck ("menu-view-header", isVisible("header-bar-tbox"));
|
|
client.uiState["status"] =
|
|
setMenuCheck ("menu-view-status", isVisible("status-bar"));
|
|
|
|
client.statusBar = new Object();
|
|
|
|
client.statusBar["header-url"] =
|
|
document.getElementById ("header-url");
|
|
client.statusBar["server-nick"] =
|
|
document.getElementById ("server-nick");
|
|
client.statusBar["channel-mode"] =
|
|
document.getElementById ("channel-mode");
|
|
client.statusBar["channel-users"] =
|
|
document.getElementById ("channel-users");
|
|
client.statusBar["channel-topic"] =
|
|
document.getElementById ("channel-topic");
|
|
client.statusBar["channel-topicedit"] =
|
|
document.getElementById ("channel-topicedit");
|
|
|
|
client.statusElement = document.getElementById ("status-text");
|
|
client.defaultStatus = getMsg ("defaultStatus");
|
|
|
|
onSortCol ("usercol-nick");
|
|
|
|
client.display (getMsg("welcome"), "HELLO");
|
|
setCurrentObject (client);
|
|
|
|
client.onInputNetworks();
|
|
client.onInputCommands();
|
|
|
|
ary = client.INITIAL_VICTIMS.split(/\s*;\s*/);
|
|
for (i in ary)
|
|
{
|
|
if (ary[i])
|
|
client.stalkingVictims.push (ary[i]);
|
|
}
|
|
|
|
var m = document.getElementById ("menu-settings-autosave");
|
|
m.setAttribute ("checked", String(client.SAVE_SETTINGS));
|
|
|
|
setInterval ("onNotifyTimeout()", client.NOTIFY_TIMEOUT);
|
|
|
|
}
|
|
|
|
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(/^irc:\/?\/?$/i) == -1)
|
|
{
|
|
/* if the url is not irc: irc:/ or irc://, then go to it. */
|
|
gotoIRCURL (url);
|
|
wentSomewhere = true;
|
|
}
|
|
}
|
|
|
|
if (!wentSomewhere)
|
|
{
|
|
/* if we had nowhere else to go, connect to any default urls */
|
|
var ary = client.INITIAL_URLS.split(/\s*;\s*/).reverse();
|
|
for (var i in ary)
|
|
{
|
|
if (ary[i] && ary[i] != "irc://")
|
|
gotoIRCURL (ary[i]);
|
|
}
|
|
}
|
|
|
|
if (client.viewsArray.length > 1 && !isStartupURL("irc://"))
|
|
{
|
|
var tb = getTabForObject (client);
|
|
deleteTab(tb);
|
|
client.deck.removeChild(client.frame);
|
|
client.deck.selectedIndex = 0;
|
|
}
|
|
}
|
|
|
|
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 setMenuCheck (id, state)
|
|
{
|
|
var m = document.getElementById(id);
|
|
m.setAttribute ("checked", String(Boolean(state)));
|
|
return state;
|
|
}
|
|
|
|
function isVisible (id)
|
|
{
|
|
var e = document.getElementById(id);
|
|
|
|
if (!e)
|
|
{
|
|
dd ("** Bogus id ``" + id + "'' passed to isVisible() **");
|
|
return false;
|
|
}
|
|
|
|
return (e.getAttribute ("collapsed") != "true");
|
|
}
|
|
|
|
function initHost(obj)
|
|
{
|
|
obj.commands = new CCommandManager();
|
|
addCommands (obj.commands);
|
|
|
|
obj.networks = new Object();
|
|
obj.eventPump = new CEventPump (200);
|
|
|
|
obj.defaultCompletion = client.COMMAND_CHAR + "help ";
|
|
|
|
obj.networks["efnet"] =
|
|
new CIRCNetwork ("efnet",
|
|
[{name: "irc.mcs.net", port: 6667},
|
|
{name: "irc.prison.net", port: 6667},
|
|
{name: "irc.freei.net", port: 6667},
|
|
{name: "irc.magic.ca", port: 6667}],
|
|
obj.eventPump);
|
|
obj.networks["moznet"] =
|
|
new CIRCNetwork ("moznet", [{name: "irc.mozilla.org", port: 6667}],
|
|
obj.eventPump);
|
|
obj.networks["hybridnet"] =
|
|
new CIRCNetwork ("hybridnet", [{name: "irc.ssc.net", port: 6667}],
|
|
obj.eventPump);
|
|
obj.networks["slashnet"] =
|
|
new CIRCNetwork ("slashnet", [{name: "irc.slashnet.org", port:6667}],
|
|
obj.eventPump);
|
|
obj.networks["dalnet"] =
|
|
new CIRCNetwork ("dalnet", [{name: "irc.dal.net", port:6667}],
|
|
obj.eventPump);
|
|
obj.networks["undernet"] =
|
|
new CIRCNetwork ("undernet", [{name: "irc.undernet.org", port:6667}],
|
|
obj.eventPump);
|
|
obj.networks["webbnet"] =
|
|
new CIRCNetwork ("webbnet", [{name: "irc.webbnet.org", port:6667}],
|
|
obj.eventPump);
|
|
obj.networks["quakenet"] =
|
|
new CIRCNetwork ("quakenet", [{name: "irc.quakenet.org", port:6667}],
|
|
obj.eventPump);
|
|
obj.networks["opennet"] =
|
|
new CIRCNetwork ("opennet",
|
|
[{name: "irc.openprojects.net", port:6667},
|
|
{name: "eu.opirc.nu", port:6667},
|
|
{name: "au.opirc.nu", port:6667},
|
|
{name: "us.opirc.nu", port:6667}],
|
|
obj.eventPump);
|
|
|
|
obj.primNet = obj.networks["efnet"];
|
|
|
|
if (DEBUG)
|
|
/* hook all events EXCEPT server.poll and *.event-end types
|
|
* (the 4th param inverts the match) */
|
|
obj.eventPump.addHook ([{type: "poll", set: /^(server|dcc-chat)$/},
|
|
{type: "event-end"}], event_tracer,
|
|
"event-tracer", true /* negate */,
|
|
false /* disable */);
|
|
|
|
obj.linkRE = /((\w+):[^<>\[\]()\'\"\s]+|www(\.[^.<>\[\]()\'\"\s]+){2,})/;
|
|
|
|
obj.munger = new CMunger();
|
|
obj.munger.enabled = true;
|
|
obj.munger.addRule ("link", obj.linkRE, insertLink);
|
|
obj.munger.addRule ("mailto",
|
|
/(?:\s|\W|^)((mailto:)?[^<>\[\]()\'\"\s]+@[^.<>\[\]()\'\"\s]+\.[^<>\[\]()\'\"\s]+)/i,
|
|
insertMailToLink);
|
|
obj.munger.addRule ("bugzilla-link", /(?:\s|\W|^)(bug\s+#?\d{3,6})/i,
|
|
insertBugzillaLink);
|
|
obj.munger.addRule ("channel-link",
|
|
/(?:\s|\W|^)[@+]?(#[^<>\[\](){}\"\s]*[^:,.<>\[\](){}\'\"\s])/i,
|
|
insertChannelLink);
|
|
|
|
obj.munger.addRule ("face",
|
|
/((^|\s)[\<\>]?[\;\=\:]\~?[\-\^\v]?[\)\|\(pP\<\>oO0\[\]\/\\](\s|$))/,
|
|
insertSmiley);
|
|
obj.munger.addRule ("ear", /(?:\s|^)(\(\*)(?:\s|$)/, insertEar, false);
|
|
obj.munger.addRule ("rheet", /(?:\s|\W|^)(rhee+t\!*)(?:\s|$)/i, insertRheet);
|
|
obj.munger.addRule ("bold", /(?:\s|^)(\*[^*,.()]*\*)(?:[\s.,]|$)/,
|
|
"chatzilla-bold");
|
|
obj.munger.addRule ("italic", /(?:\s|^)(\/[^\/,.()]*\/)(?:[\s.,]|$)/,
|
|
"chatzilla-italic");
|
|
/* allow () chars inside |code()| blocks */
|
|
obj.munger.addRule ("teletype", /(?:\s|^)(\|[^|,.]*\|)(?:[\s.,]|$)/,
|
|
"chatzilla-teletype");
|
|
obj.munger.addRule ("underline", /(?:\s|^)(\_[^_,.()]*\_)(?:[\s.,]|$)/,
|
|
"chatzilla-underline");
|
|
obj.munger.addRule (".mirc-colors", /(\x03((\d{1,2})(,\d{1,2}|)|))/,
|
|
mircChangeColor);
|
|
obj.munger.addRule (".mirc-bold", /(\x02)/, mircToggleBold);
|
|
obj.munger.addRule (".mirc-underline", /(\x1f)/, mircToggleUnder);
|
|
obj.munger.addRule (".mirc-color-reset", /(\x0f)/, mircResetColor);
|
|
obj.munger.addRule (".mirc-reverse", /(\x16)/, mircReverseColor);
|
|
obj.munger.addRule ("ctrl-char", /([\x01-\x1f])/, showCtrlChar);
|
|
obj.munger.addRule ("word-hyphenator",
|
|
new RegExp ("(\\S{" + client.MAX_WORD_DISPLAY + ",})"),
|
|
insertHyphenatedWord);
|
|
|
|
obj.rdf = new RDFHelper();
|
|
|
|
obj.rdf.initTree("user-list");
|
|
obj.rdf.setTreeRoot("user-list", obj.rdf.resNullChan);
|
|
|
|
multilineInputMode(false);
|
|
}
|
|
|
|
function insertLink (matchText, containerTag)
|
|
{
|
|
|
|
var href;
|
|
|
|
if (matchText.match (/^[a-zA-Z-]+:/))
|
|
href = matchText;
|
|
else
|
|
href = "http://" + matchText;
|
|
|
|
var anchor = document.createElementNS ("http://www.w3.org/1999/xhtml",
|
|
"html:a");
|
|
anchor.setAttribute ("href", href);
|
|
anchor.setAttribute ("class", "chatzilla-link");
|
|
anchor.setAttribute ("target", "_content");
|
|
insertHyphenatedWord (matchText, anchor);
|
|
containerTag.appendChild (anchor);
|
|
|
|
}
|
|
|
|
function insertMailToLink (matchText, containerTag)
|
|
{
|
|
|
|
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");
|
|
anchor.setAttribute ("href", href);
|
|
anchor.setAttribute ("class", "chatzilla-link");
|
|
//anchor.setAttribute ("target", "_content");
|
|
insertHyphenatedWord (matchText, anchor);
|
|
containerTag.appendChild (anchor);
|
|
|
|
}
|
|
|
|
function insertChannelLink (matchText, containerTag, eventData)
|
|
{
|
|
var encodedMatchText = fromUnicode(matchText + " ");
|
|
/* bug 114923 */
|
|
encodedMatchText = encodedMatchText.substr(0, encodedMatchText.length - 1);
|
|
|
|
if (!("network" in eventData) ||
|
|
matchText.search
|
|
(/^#(include|error|define|if|ifdef|else|elsif|endif|\d+)$/i) != -1)
|
|
|
|
{
|
|
containerTag.appendChild (document.createTextNode(matchText));
|
|
return;
|
|
}
|
|
|
|
var anchor = document.createElementNS ("http://www.w3.org/1999/xhtml",
|
|
"html:a");
|
|
anchor.setAttribute ("href", "irc://" + escape(eventData.network.name) +
|
|
"/" + escape (encodedMatchText));
|
|
anchor.setAttribute ("class", "chatzilla-link");
|
|
//anchor.setAttribute ("target", "_content");
|
|
insertHyphenatedWord (matchText, anchor);
|
|
containerTag.appendChild (anchor);
|
|
|
|
}
|
|
|
|
function insertBugzillaLink (matchText, containerTag)
|
|
{
|
|
|
|
var number = matchText.match (/(\d+)/)[1];
|
|
|
|
var anchor = document.createElementNS ("http://www.w3.org/1999/xhtml",
|
|
"html:a");
|
|
anchor.setAttribute ("href",
|
|
"http://bugzilla.mozilla.org/show_bug.cgi?id=" +
|
|
number);
|
|
anchor.setAttribute ("class", "chatzilla-link");
|
|
anchor.setAttribute ("target", "_content");
|
|
insertHyphenatedWord (matchText, anchor);
|
|
containerTag.appendChild (anchor);
|
|
|
|
}
|
|
|
|
function insertRheet (matchText, containerTag)
|
|
{
|
|
|
|
var anchor = document.createElementNS ("http://www.w3.org/1999/xhtml",
|
|
"html:a");
|
|
anchor.setAttribute ("href",
|
|
"ftp://ftp.mozilla.org/pub/mozilla/libraries/bonus-tracks/rheet.wav");
|
|
anchor.setAttribute ("class", "chatzilla-rheet chatzilla-link");
|
|
//anchor.setAttribute ("target", "_content");
|
|
insertHyphenatedWord (matchText, anchor);
|
|
containerTag.appendChild (anchor);
|
|
}
|
|
|
|
function insertEar (matchText, containerTag)
|
|
{
|
|
if (client.smileyText)
|
|
containerTag.appendChild (document.createTextNode (matchText));
|
|
|
|
var img = document.createElementNS ("http://www.w3.org/1999/xhtml",
|
|
"html:img");
|
|
img.setAttribute ("src", client.IMAGEDIR + "face-ear.gif");
|
|
img.setAttribute ("title", matchText);
|
|
containerTag.appendChild (img);
|
|
|
|
}
|
|
|
|
function insertSmiley (emoticon, containerTag)
|
|
{
|
|
var type = "error";
|
|
|
|
if (emoticon.search (/\;[-^v]?[\)>\]]/) != -1)
|
|
type = "face-wink";
|
|
else if (emoticon.search (/[=:;][-^v]?[\)>\]]/) != -1)
|
|
type = "face-smile";
|
|
else if (emoticon.search (/[=:;][-^v]?[\/\\]/) != -1)
|
|
type = "face-screw";
|
|
else if (emoticon.search (/[=:;]\~[-^v]?\(/) != -1)
|
|
type = "face-cry";
|
|
else if (emoticon.search (/[=:;][-^v]?[\(<\[]/) != -1)
|
|
type = "face-frown";
|
|
else if (emoticon.search (/\<?[=:;][-^v]?[0oO]/) != -1)
|
|
type = "face-surprise";
|
|
else if (emoticon.search (/[=:;][-^v]?[pP]/) != -1)
|
|
type = "face-tongue";
|
|
else if (emoticon.search (/\>?[=:;][\-\^\v]?[\(\|]/) != -1)
|
|
type = "face-angry";
|
|
|
|
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 (!client.enableColors)
|
|
return;
|
|
|
|
var ary = colorInfo.match (/.(\d{1,2}|)(,(\d{1,2})|)/);
|
|
|
|
var fgColor = ary[1];
|
|
if (fgColor > 16)
|
|
fgColor &= 16;
|
|
switch (fgColor.length)
|
|
{
|
|
case 0:
|
|
delete data.currFgColor;
|
|
delete data.currBgColor;
|
|
return;
|
|
case 1:
|
|
data.currFgColor = "0" + fgColor;
|
|
break;
|
|
case 2:
|
|
data.currFgColor = fgColor;
|
|
break;
|
|
}
|
|
if (fgColor == 1)
|
|
delete data.currFgColor;
|
|
if (ary.length >= 4)
|
|
{
|
|
var bgColor = ary[3];
|
|
if (bgColor > 16)
|
|
bgColor &= 16;
|
|
if (bgColor.length == 1)
|
|
data.currBgColor = "0" + bgColor;
|
|
else
|
|
data.currBgColor = bgColor;
|
|
if (bgColor == 0)
|
|
delete data.currBgColor;
|
|
}
|
|
data.hasColorInfo = true;
|
|
}
|
|
|
|
function mircToggleBold (colorInfo, containerTag, data)
|
|
{
|
|
if (!client.enableColors)
|
|
return;
|
|
|
|
if ("isBold" in data)
|
|
delete data.isBold;
|
|
else
|
|
data.isBold = true;
|
|
data.hasColorInfo = true;
|
|
}
|
|
|
|
function mircToggleUnder (colorInfo, containerTag, data)
|
|
{
|
|
if (!client.enableColors)
|
|
return;
|
|
|
|
if ("isUnderline" in data)
|
|
delete data.isUnderline;
|
|
else
|
|
data.isUnderline = true;
|
|
data.hasColorInfo = true;
|
|
}
|
|
|
|
function mircResetColor (text, containerTag, data)
|
|
{
|
|
if (!client.enableColors || !("hasColorInfo" in data))
|
|
return;
|
|
|
|
delete data.currFgColor;
|
|
delete data.currBgColor;
|
|
delete data.isBold;
|
|
delete data.isUnder;
|
|
delete data.hasColorInfo;
|
|
}
|
|
|
|
function mircReverseColor (text, containerTag, data)
|
|
{
|
|
if (!client.enableColors)
|
|
return;
|
|
|
|
var tempColor = ("currFgColor" in data ? data.currFgColor : "01");
|
|
|
|
if ("currBgColor" in data)
|
|
data.currFgColor = data.currBgColor;
|
|
else
|
|
data.currFgColor = "00";
|
|
data.currBgColor = tempColor;
|
|
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");
|
|
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)
|
|
{
|
|
var wordParts = splitLongWord (longWord, client.MAX_WORD_DISPLAY);
|
|
for (var i = 0; i < wordParts.length; ++i)
|
|
{
|
|
containerTag.appendChild (document.createTextNode (wordParts[i]));
|
|
if (i != wordParts.length)
|
|
{
|
|
var wbr = document.createElementNS ("http://www.w3.org/1999/xhtml",
|
|
"html:wbr");
|
|
containerTag.appendChild (wbr);
|
|
}
|
|
}
|
|
}
|
|
|
|
function msgIsImportant (msg, sourceNick, myNick)
|
|
{
|
|
var sv = "(" + myNick + ")";
|
|
if (client.stalkingVictims.length > 0)
|
|
sv += "|(" + client.stalkingVictims.join(")|(") + ")";
|
|
|
|
var str = "(^|[\\W\\s])" + sv + "([\\W\\s]|$)";
|
|
var re = new RegExp(str, "i");
|
|
if (msg.search(re) != -1 || sourceNick && sourceNick.search(re) != -1)
|
|
{
|
|
playSounds(client.STALK_BEEP);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function isStartupURL(url)
|
|
{
|
|
var ary = client.INITIAL_URLS.split(/\s*;\s*/);
|
|
return arrayContains(ary, url);
|
|
}
|
|
|
|
function cycleView (amount)
|
|
{
|
|
var len = client.viewsArray.length;
|
|
if (len <= 1)
|
|
return;
|
|
|
|
if (amount > len)
|
|
amount = amount % client.viewsArray.length;
|
|
|
|
var tb = getTabForObject (client.currentObject);
|
|
if (!tb)
|
|
return;
|
|
|
|
var vk = Number(tb.getAttribute("viewKey"));
|
|
var destKey = vk + amount;
|
|
if (destKey > len - 1)
|
|
{
|
|
/* wrap past max */
|
|
destKey -= client.viewsArray.length;
|
|
}
|
|
else if (destKey < 0)
|
|
{
|
|
/* wrap past 0 */
|
|
destKey += client.viewsArray.length;
|
|
}
|
|
|
|
setCurrentObject (client.viewsArray[destKey].source);
|
|
}
|
|
|
|
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 || !file)
|
|
return;
|
|
|
|
if (file == "beep")
|
|
{
|
|
client.sound.beep();
|
|
}
|
|
else
|
|
{
|
|
var uri = Components.classes["@mozilla.org/network/standard-url;1"];
|
|
uri = uri.createInstance(Components.interfaces.nsIURI);
|
|
uri.spec = file;
|
|
client.sound.play (uri);
|
|
}
|
|
}
|
|
|
|
function fillInTooltip(tipElement, id)
|
|
{
|
|
const XLinkNS = "http://www.w3.org/1999/xlink";
|
|
|
|
var retVal = false;
|
|
|
|
var titleText = null;
|
|
var XLinkTitleText = null;
|
|
|
|
while (!titleText && !XLinkTitleText && tipElement) {
|
|
if (tipElement.nodeType == Node.ELEMENT_NODE) {
|
|
titleText = tipElement.getAttribute("title");
|
|
XLinkTitleText = tipElement.getAttributeNS(XLinkNS, "title");
|
|
}
|
|
tipElement = tipElement.parentNode;
|
|
}
|
|
|
|
var texts = [titleText, XLinkTitleText];
|
|
var tipNode = document.getElementById(id);
|
|
|
|
for (var i = 0; i < texts.length; ++i) {
|
|
var t = texts[i];
|
|
if (t && t.search(/\S/) >= 0) {
|
|
tipNode.setAttribute("label", t);
|
|
retVal = true;
|
|
}
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
/* timer-based mainloop */
|
|
function mainStep()
|
|
{
|
|
client.eventPump.stepEvents();
|
|
setTimeout ("mainStep()", client.STEP_TIMEOUT);
|
|
}
|
|
|
|
function getMsg (msgName)
|
|
{
|
|
var restCount = arguments.length - 1;
|
|
|
|
if (!("bundle" in client))
|
|
{
|
|
client.bundle =
|
|
srGetStrBundle("chrome://chatzilla/locale/chatzilla.properties");
|
|
}
|
|
|
|
try
|
|
{
|
|
if (restCount == 1 && arguments[1] instanceof Array)
|
|
{
|
|
return client.bundle.formatStringFromName (msgName, arguments[1],
|
|
arguments[1].length);
|
|
}
|
|
else if (restCount > 0)
|
|
{
|
|
var subPhrases = new Array();
|
|
for (var i = 1; i < arguments.length; ++i)
|
|
subPhrases.push(arguments[i]);
|
|
return client.bundle.formatStringFromName (msgName, subPhrases,
|
|
subPhrases.length);
|
|
}
|
|
|
|
return client.bundle.GetStringFromName (msgName);
|
|
}
|
|
catch (ex)
|
|
{
|
|
dd ("caught exception getting value for ``" + msgName + "''\n" + ex +
|
|
"\n" + getStackTrace());
|
|
return msgName;
|
|
}
|
|
}
|
|
|
|
function openQueryTab (server, nick)
|
|
{
|
|
var usr = server.addUser(nick.toLowerCase());
|
|
if (!("messages" in usr))
|
|
usr.displayHere (getMsg("cli_imsgMsg3", usr.properNick), "INFO");
|
|
server.sendData ("WHO " + nick + "\n");
|
|
return usr;
|
|
}
|
|
|
|
function arraySpeak (ary, single, plural)
|
|
{
|
|
var rv = "";
|
|
var and = getMsg ("arraySpeakAnd");
|
|
|
|
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 quicklistCallback (element, ndx, ary)
|
|
{
|
|
/* Check whether the selected attribute == true */
|
|
if (element.getAttribute("selected") == "true")
|
|
{
|
|
/* extract the nick data from the element */
|
|
/* Hmmm, nice walk ... */
|
|
ary.push(element.childNodes[0].childNodes[2].childNodes[0].nodeValue);
|
|
}
|
|
}
|
|
|
|
function getObjectDetails (obj, rv)
|
|
{
|
|
if (!rv)
|
|
rv = new Object();
|
|
|
|
if (!obj || (typeof obj != "object"))
|
|
{
|
|
dd ("** INVALID OBJECT passed to getObjectDetails (" + obj + "). **");
|
|
dd (getStackTrace());
|
|
}
|
|
|
|
rv.orig = obj;
|
|
rv.parent = ("parent" in obj) ? obj.parent : null;
|
|
|
|
switch (obj.TYPE)
|
|
{
|
|
case "IRCChannel":
|
|
rv.channel = obj;
|
|
rv.server = rv.channel.parent;
|
|
rv.network = rv.server.parent;
|
|
break;
|
|
|
|
case "IRCUser":
|
|
rv.user = obj;
|
|
rv.server = rv.user.parent;
|
|
rv.network = rv.server.parent;
|
|
break;
|
|
|
|
case "IRCChanUser":
|
|
rv.user = obj;
|
|
rv.channel = rv.user.parent;
|
|
rv.server = rv.channel.parent;
|
|
rv.network = rv.server.parent;
|
|
break;
|
|
|
|
case "IRCNetwork":
|
|
rv.network = obj;
|
|
if ("primServ" in rv.network)
|
|
rv.server = rv.network.primServ;
|
|
break;
|
|
|
|
case "IRCClient":
|
|
if ("lastNetwork" in obj)
|
|
{
|
|
rv.network = obj.lastNetwork;
|
|
if (rv.network.isConnected())
|
|
rv.server = rv.network.primServ;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
/* no setup for unknown object */
|
|
break;
|
|
}
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
function findDynamicRule (selector)
|
|
{
|
|
var rules = frames[0].document.styleSheets[1].cssRules;
|
|
|
|
if (selector instanceof 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 setClientOutput(doc)
|
|
{
|
|
client.deck = document.getElementById('output-deck');
|
|
//XXXcreateHighlightMenu();
|
|
}
|
|
|
|
function createHighlightMenu()
|
|
{
|
|
/* Look for "special" highlighting rules int he motif. These special rules
|
|
* are in the format ``.chatzilla-highlight[name="<display-name>"] { ... }''
|
|
* where <display-name> is a textual description to be placed in the
|
|
* Highlight submenu of the message area context menu. The body of
|
|
* these rules can be applied by the user to different irc messages. They
|
|
* are special becaus they do not actually match an element in the content
|
|
* model. The style body is copied into a new rule that matches a pettern
|
|
* determined by the user.
|
|
*/
|
|
function processStyleRules(rules)
|
|
{
|
|
for (var i = 0; i < rules.length; ++i)
|
|
{
|
|
var rule = rules.item(i);
|
|
if (rule instanceof CSSStyleRule)
|
|
{
|
|
var ary = rule.selectorText.
|
|
match(/\.chatzilla-highlight\[name=\"?([^\"]+)\"?\]/i);
|
|
if (ary)
|
|
{
|
|
menuitem = document.createElement("menuitem");
|
|
menuitem.setAttribute ("class", "highlight-menu-item");
|
|
menuitem.setAttribute ("label", ary[1]);
|
|
menuitem.setAttribute ("oncommand", "onPopupHighlight('" +
|
|
rule.style.cssText + "');");
|
|
menuitem.setAttribute ("style", rule.style.cssText);
|
|
menu.appendChild(menuitem);
|
|
}
|
|
}
|
|
else if (rule instanceof CSSImportRule)
|
|
{
|
|
processStyleRules(rule.styleSheet.cssRules);
|
|
}
|
|
}
|
|
}
|
|
|
|
var menu = document.getElementById("highlightMenu");
|
|
while (menu.firstChild)
|
|
menu.removeChild(menu.firstChild);
|
|
|
|
var menuitem = document.createElement("menuitem");
|
|
menuitem.setAttribute ("label", getMsg("noStyle"));
|
|
menuitem.setAttribute ("class", "highlight-menu-item");
|
|
menuitem.setAttribute ("oncommand", "onPopupHighlight('');");
|
|
menu.appendChild(menuitem);
|
|
|
|
processStyleRules(frames[0].document.styleSheets[0].cssRules);
|
|
}
|
|
|
|
function setupMungerMenu(munger)
|
|
{
|
|
var menu = document.getElementById("menu-munger");
|
|
for (var entry in munger.entries)
|
|
{
|
|
if (entry[0] != ".")
|
|
{
|
|
var menuitem = document.createElement("menuitem");
|
|
menuitem.setAttribute ("label", munger.entries[entry].description);
|
|
menuitem.setAttribute ("id", "menu-munger-" + entry);
|
|
menuitem.setAttribute ("type", "checkbox");
|
|
if (munger.entries[entry].enabled)
|
|
menuitem.setAttribute ("checked", "true");
|
|
menuitem.setAttribute ("oncommand", "onToggleMungerEntry('" +
|
|
entry + "');");
|
|
menu.appendChild(menuitem);
|
|
}
|
|
}
|
|
}
|
|
|
|
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 ("---");
|
|
}
|
|
}
|
|
|
|
function parseIRCURL (url)
|
|
{
|
|
var specifiedHost = "";
|
|
|
|
var rv = new Object();
|
|
rv.spec = url;
|
|
rv.host = null;
|
|
rv.target = "";
|
|
rv.port = 6667;
|
|
rv.msg = "";
|
|
rv.needpass = false;
|
|
rv.needkey = false;
|
|
rv.isnick = false;
|
|
rv.isserver = false;
|
|
|
|
if (url.search(/^(irc:\/?\/?)$/i) != -1)
|
|
return rv;
|
|
|
|
rv.host = client.DEFAULT_NETWORK;
|
|
|
|
/* split url into <host>/<everything-else> pieces */
|
|
var ary = url.match (/^irc:\/\/([^\/\s]+)?(\/.*)?\s*$/i);
|
|
if (!ary)
|
|
{
|
|
dd ("parseIRCURL: initial split failed");
|
|
return null;
|
|
}
|
|
var host = ary[1];
|
|
var rest = (2 in ary) ? ary[2] : "";
|
|
|
|
/* split <host> into server (or network) / port */
|
|
ary = host.match (/^([^\s\:]+)?(\:\d+)?$/);
|
|
if (!ary)
|
|
{
|
|
dd ("parseIRCURL: host/port split failed");
|
|
return null;
|
|
}
|
|
|
|
if (2 in ary)
|
|
{
|
|
if (!(1 in ary))
|
|
{
|
|
dd ("parseIRCURL: port with no host");
|
|
return null;
|
|
}
|
|
specifiedHost = rv.host = ary[1].toLowerCase();
|
|
rv.isserver = true;
|
|
rv.port = parseInt(ary[2].substr(1));
|
|
}
|
|
else if (1 in ary)
|
|
{
|
|
specifiedHost = rv.host = ary[1].toLowerCase();
|
|
if (specifiedHost.indexOf(".") != -1)
|
|
rv.isserver = true;
|
|
}
|
|
|
|
if (rest)
|
|
{
|
|
ary = rest.match (/^\/([^\,\?\s\/]*)?\/?(,[^\?]*)?(\?.*)?$/);
|
|
if (!ary)
|
|
{
|
|
dd ("parseIRCURL: rest split failed ``" + rest + "''");
|
|
return null;
|
|
}
|
|
|
|
rv.target = (1 in ary) ?
|
|
unescape(ary[1]).replace("\n", "\\n") : "";
|
|
var params = (2 in ary) ? ary[2].toLowerCase() : "";
|
|
var query = (3 in ary) ? ary[3] : "";
|
|
|
|
if (params)
|
|
{
|
|
rv.isnick =
|
|
(params.search (/,\s*isnick\s*,|,\s*isnick\s*$/) != -1);
|
|
if (rv.isnick && !rv.target)
|
|
{
|
|
dd ("parseIRCURL: isnick w/o target");
|
|
/* isnick w/o a target is bogus */
|
|
return null;
|
|
}
|
|
|
|
rv.isserver =
|
|
(params.search (/,\s*isserver\s*,|,\s*isserver\s*$/) != -1);
|
|
if (rv.isserver && !specifiedHost)
|
|
{
|
|
dd ("parseIRCURL: isserver w/o host");
|
|
/* isserver w/o a host is bogus */
|
|
return null;
|
|
}
|
|
|
|
rv.needpass =
|
|
(params.search (/,\s*needpass\s*,|,\s*needpass\s*$/) != -1);
|
|
|
|
rv.needkey =
|
|
(params.search (/,\s*needkey\s*,|,\s*needkey\s*$/) != -1);
|
|
|
|
}
|
|
|
|
if (query)
|
|
{
|
|
ary = query.match
|
|
(/^\?msg=([^\&]*)$|^\?msg=([^\&]*)\&|\&msg=([^\&]*)\&|\&msg=([^\&]*)$/);
|
|
if (ary)
|
|
for (var i = 1; i < ary.length; i++)
|
|
if (i in ary)
|
|
{
|
|
rv.msg = unescape(ary[i]).replace ("\n", "\\n");
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
function gotoIRCURL (url)
|
|
{
|
|
if (typeof url == "string")
|
|
url = parseIRCURL(url);
|
|
|
|
if (!url)
|
|
{
|
|
window.alert (getMsg("badIRCURL",url));
|
|
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.)
|
|
*/
|
|
onSimulateCommand("/client");
|
|
return;
|
|
}
|
|
|
|
var net;
|
|
var pass = "";
|
|
|
|
if (url.needpass)
|
|
pass = window.prompt (getMsg("gotoIRCURLMsg2",url.spec));
|
|
|
|
if (url.isserver)
|
|
{
|
|
var alreadyThere = false;
|
|
for (var n in client.networks)
|
|
{
|
|
if ((client.networks[n].isConnected()) &&
|
|
(client.networks[n].primServ.connection.host == url.host) &&
|
|
(client.networks[n].primServ.connection.port == url.port))
|
|
{
|
|
/* already connected to this server/port */
|
|
net = client.networks[n];
|
|
alreadyThere = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!alreadyThere)
|
|
{
|
|
/*
|
|
dd ("gotoIRCURL: not already connected to " +
|
|
"server " + url.host + " trying to connect...");
|
|
*/
|
|
client.onInputServer ({inputData: url.host + " " + url.port +
|
|
" " + pass});
|
|
net = client.networks[url.host];
|
|
if (!("pendingURLs" in net))
|
|
net.pendingURLs = new Array();
|
|
net.pendingURLs.push (url);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
/* parsed as a network name */
|
|
{
|
|
net = client.networks[url.host];
|
|
if (!net.isConnected())
|
|
{
|
|
/*
|
|
dd ("gotoIRCURL: not already connected to " +
|
|
"network " + url.host + " trying to connect...");
|
|
*/
|
|
client.connectToNetwork (url.host, pass);
|
|
if (!("pendingURLs" in net))
|
|
net.pendingURLs = new Array();
|
|
net.pendingURLs.push (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];
|
|
}
|
|
|
|
ev = {inputData: nick, network: net, server: net.primServ};
|
|
client.onInputQuery(ev);
|
|
targetObject = ev.user;
|
|
}
|
|
else
|
|
{
|
|
/* url points to a channel */
|
|
var key = "";
|
|
if (url.needkey)
|
|
key = window.prompt (getMsg("gotoIRCURLMsg3", url.spec));
|
|
ev = {inputData: url.target + " " + key,
|
|
network: net, server: net.primServ};
|
|
client.onInputJoin (ev);
|
|
targetObject = ev.channel;
|
|
}
|
|
|
|
if (url.msg)
|
|
{
|
|
var msg;
|
|
if (url.msg.indexOf("\01ACTION") == 0)
|
|
{
|
|
msg = filterOutput (url.msg, "ACTION", "ME!");
|
|
targetObject.display (msg, "ACTION", "ME!",
|
|
client.currentObject);
|
|
}
|
|
else
|
|
{
|
|
msg = filterOutput (url.msg, "PRIVMSG", "ME!");
|
|
targetObject.display (msg, "PRIVMSG", "ME!",
|
|
client.currentObject);
|
|
}
|
|
targetObject.say (fromUnicode(msg));
|
|
setCurrentObject (targetObject);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!net.messages)
|
|
net.displayHere (getMsg("gotoIRCURLMsg4",net.name),
|
|
"INFO");
|
|
setCurrentObject (net);
|
|
}
|
|
|
|
}
|
|
|
|
function setTopicText (text)
|
|
{
|
|
var topic = client.statusBar["channel-topic"];
|
|
var span = document.createElementNS ("http://www.w3.org/1999/xhtml",
|
|
"html:span");
|
|
|
|
span.appendChild(stringToMsg(text, client.currentObject));
|
|
topic.removeChild(topic.firstChild);
|
|
topic.appendChild(span);
|
|
}
|
|
|
|
function updateNetwork(obj)
|
|
{
|
|
var o = getObjectDetails (client.currentObject);
|
|
|
|
var lag = MSG_UNKNOWN;
|
|
var nick = "";
|
|
if ("server" in o)
|
|
{
|
|
if (o.server.me)
|
|
nick = o.server.me.properNick;
|
|
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.name);
|
|
client.statusBar["server-nick"].setAttribute("value", nick);
|
|
}
|
|
|
|
function updateChannel (obj)
|
|
{
|
|
if (obj && obj != client.currentObject)
|
|
return;
|
|
|
|
var o = getObjectDetails (client.currentObject);
|
|
|
|
var mode = MSG_NONE, users = MSG_NONE, topic = MSG_UNKNOWN;
|
|
|
|
if ("channel" in o)
|
|
{
|
|
mode = o.channel.mode.getModeStr();
|
|
if (!mode)
|
|
mode = MSG_NONE;
|
|
users = getMsg("userCountDetails",
|
|
[o.channel.getUsersLength(), o.channel.opCount,
|
|
o.channel.voiceCount]);
|
|
topic = o.channel.topic ? o.channel.topic : MSG_NONE;
|
|
}
|
|
|
|
client.statusBar["channel-mode"].setAttribute("value", mode);
|
|
client.statusBar["channel-users"].setAttribute("value", users);
|
|
var regex = new RegExp ("(\\S{" + client.MAX_WORD_DISPLAY + ",})", "g");
|
|
var ary = topic.match(regex);
|
|
if (ary && ary.length)
|
|
{
|
|
for (var i = 0; i < ary.length; ++i)
|
|
{
|
|
var hyphenated = hyphenateWord(ary[i], client.MAX_WORD_DISPLAY);
|
|
topic = topic.replace(ary[i], hyphenated);
|
|
}
|
|
}
|
|
|
|
client.statusBar["channel-topic"].firstChild.data = topic;
|
|
|
|
}
|
|
|
|
function updateTitle (obj)
|
|
{
|
|
if (!("currentObject" in client) || (obj && obj != client.currentObject))
|
|
return;
|
|
|
|
var tstring;
|
|
var o = getObjectDetails (client.currentObject);
|
|
var net = "network" in o ? o.network.name : "";
|
|
var nick = "";
|
|
|
|
switch (client.currentObject.TYPE)
|
|
{
|
|
case "IRCNetwork":
|
|
var serv = "", port = "";
|
|
if ("server" in o)
|
|
{
|
|
serv = o.server.connection.host;
|
|
port = o.server.connection.port;
|
|
if (o.server.me)
|
|
nick = o.server.me.properNick;
|
|
tstring = getMsg("updateTitleNetwork", [nick, net, serv, port]);
|
|
}
|
|
else
|
|
{
|
|
nick = client.currentObject.INITIAL_NICK;
|
|
tstring = getMsg("updateTitleNetwork2", [nick, net]);
|
|
}
|
|
break;
|
|
|
|
case "IRCChannel":
|
|
var chan = "", mode = "", topic = "";
|
|
nick = "me" in o.parent ? o.parent.me.properNick :
|
|
getMsg ("updateTitleNoNick");
|
|
chan = o.channel.unicodeName;
|
|
mode = o.channel.mode.getModeStr();
|
|
if (!mode)
|
|
mode = getMsg("updateTitleNoMode");
|
|
topic = o.channel.topic ? o.channel.topic :
|
|
getMsg("updateTitleNoTopic");
|
|
|
|
tstring = getMsg("updateTitleChannel", [nick, chan, mode, topic]);
|
|
break;
|
|
|
|
case "IRCUser":
|
|
nick = client.currentObject.properNick;
|
|
var source = "";
|
|
if (client.currentObject.name)
|
|
{
|
|
source = client.currentObject.name + "@" +
|
|
client.currentObject.host;
|
|
}
|
|
tstring = getMsg("updateTitleUser", [nick, source]);
|
|
//client.statusBar["channel-topic"].setAttribute("value", tstring);
|
|
client.statusBar["channel-topic"].firstChild.data = tstring;
|
|
break;
|
|
|
|
default:
|
|
tstring = getMsg("updateTitleUnknown");
|
|
break;
|
|
}
|
|
|
|
if (!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("updateTitleWithActivity",
|
|
[tstring, actl.join (MSG_CSP)]);
|
|
}
|
|
|
|
document.title = tstring;
|
|
|
|
}
|
|
|
|
function multilineInputMode (state)
|
|
{
|
|
var multiInput = document.getElementById("multiline-input");
|
|
var singleInput = document.getElementById("input");
|
|
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 */
|
|
|
|
singleInput.setAttribute ("collapsed", "true");
|
|
splitter.setAttribute ("collapsed", "false");
|
|
multiInput.setAttribute ("collapsed", "false");
|
|
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");
|
|
multiInput.setAttribute ("collapsed", "true");
|
|
singleInput.setAttribute ("collapsed", "false");
|
|
client.input = singleInput;
|
|
}
|
|
|
|
client.input.focus();
|
|
}
|
|
|
|
function focusInput ()
|
|
{
|
|
client.input.focus();
|
|
}
|
|
|
|
function newInlineText (data, className, tagName)
|
|
{
|
|
if (typeof tagName == "undefined")
|
|
tagName = "html:span";
|
|
|
|
var a = document.createElementNS ("http://www.w3.org/1999/xhtml",
|
|
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:
|
|
dd ("** INVALID TYPE ('" + typeof data + "') passed to " +
|
|
"newInlineText.");
|
|
break;
|
|
|
|
}
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
function stringToMsg (message, obj)
|
|
{
|
|
var ary = message.split ("\n");
|
|
var span = document.createElementNS ("http://www.w3.org/1999/xhtml",
|
|
"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 ("http://www.w3.org/1999/xhtml",
|
|
"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 ("contentWindow" in panel ? panel.contentWindow : undefined);
|
|
}
|
|
|
|
client.__defineGetter__ ("currentFrame", getFrame);
|
|
|
|
function setCurrentObject (obj)
|
|
{
|
|
if (!obj.messages)
|
|
{
|
|
dd ("** INVALID OBJECT passed to setCurrentObject **");
|
|
return;
|
|
}
|
|
|
|
if ("currentObject" in client && client.currentObject == obj)
|
|
return;
|
|
|
|
var tb, userList;
|
|
|
|
if ("currentObject" in client && client.currentObject)
|
|
{
|
|
tb = getTabForObject(client.currentObject);
|
|
}
|
|
if (tb)
|
|
{
|
|
tb.selected = false;
|
|
tb.setAttribute ("state", "normal");
|
|
}
|
|
|
|
/* Unselect currently selected users. */
|
|
userList = document.getElementById("user-list");
|
|
if (isVisible("user-list-box"))
|
|
{
|
|
/* Remove currently selected items before this tree gets rerooted,
|
|
* because it seems to remember the selections for eternity if not. */
|
|
if (userList.treeBoxObject.selection)
|
|
userList.treeBoxObject.selection.clearSelection ();
|
|
|
|
if (obj.TYPE == "IRCChannel")
|
|
client.rdf.setTreeRoot ("user-list", obj.getGraphResource());
|
|
else
|
|
client.rdf.setTreeRoot ("user-list", client.rdf.resNullChan);
|
|
}
|
|
|
|
client.currentObject = obj;
|
|
tb = getTabForObject(obj);
|
|
if (tb)
|
|
{
|
|
tb.selected = true;
|
|
tb.setAttribute ("state", "current");
|
|
}
|
|
|
|
var vk = Number(tb.getAttribute("viewKey"));
|
|
delete client.activityList[vk];
|
|
client.deck.selectedIndex = vk;
|
|
|
|
updateNetwork();
|
|
updateChannel();
|
|
updateTitle ();
|
|
|
|
if (client.PRINT_DIRECTION == 1)
|
|
scrollDown(obj.frame, false);
|
|
|
|
onTopicEditEnd();
|
|
|
|
if (client.currentObject.TYPE == "IRCChannel")
|
|
client.statusBar["channel-topic"].setAttribute ("editable", "true");
|
|
else
|
|
client.statusBar["channel-topic"].removeAttribute ("editable");
|
|
|
|
var status = document.getElementById("offline-status");
|
|
if (client.currentObject == client)
|
|
{
|
|
status.setAttribute ("hidden", "true");
|
|
}
|
|
else
|
|
{
|
|
status.removeAttribute ("hidden");
|
|
var details = getObjectDetails(client.currentObject);
|
|
if ("network" in details && details.network.isConnected())
|
|
status.removeAttribute ("offline");
|
|
else
|
|
status.setAttribute ("offline", "true");
|
|
}
|
|
}
|
|
|
|
function checkScroll(frame)
|
|
{
|
|
if (!frame || !("contentWindow" in frame))
|
|
return false;
|
|
|
|
var w = frame.contentWindow;
|
|
return ((w.document.height - (w.innerHeight + w.pageYOffset)) < 160);
|
|
}
|
|
|
|
function scrollDown(frame, force)
|
|
{
|
|
if (!frame || !("contentWindow" in frame))
|
|
return;
|
|
|
|
var w = frame.contentWindow;
|
|
|
|
if (force || checkScroll(frame))
|
|
w.scrollTo(0, w.document.height);
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
if (typeof source != "object")
|
|
source = client.viewsArray[source].source;
|
|
|
|
var tb = getTabForObject (source, true);
|
|
var vk = Number(tb.getAttribute("viewKey"));
|
|
|
|
if ("currentObject" in client && client.currentObject != source)
|
|
{
|
|
var state = tb.getAttribute ("state");
|
|
if (state == what)
|
|
{
|
|
/* if the tab state has an equal priority to what we are setting
|
|
* then blink it */
|
|
tb.setAttribute ("state", "normal");
|
|
setTimeout (setTabState, 200, vk, what);
|
|
}
|
|
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 */
|
|
tb.setAttribute ("state", what);
|
|
setTimeout (setTabState, 200, vk, state);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function notifyAttention (source)
|
|
{
|
|
if (typeof source != "object")
|
|
source = client.viewsArray[source].source;
|
|
|
|
if (client.currentObject != source)
|
|
{
|
|
var tb = getTabForObject (source, true);
|
|
var vk = Number(tb.getAttribute("viewKey"));
|
|
|
|
tb.setAttribute ("state", "attention");
|
|
client.activityList[vk] = "!";
|
|
updateTitle();
|
|
}
|
|
|
|
if (client.FLASH_WINDOW)
|
|
window.getAttention();
|
|
|
|
}
|
|
|
|
function getFrameForDOMWindow(window)
|
|
{
|
|
var frame;
|
|
for (i = 0; i < client.deck.childNodes.length; i++)
|
|
{
|
|
frame = client.deck.childNodes[i];
|
|
if (frame.contentWindow == window)
|
|
return frame;
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
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;
|
|
|
|
var frame;
|
|
if (stateFlags & START)
|
|
{
|
|
frame = getFrameForDOMWindow(webProgress.DOMWindow);
|
|
if (!frame)
|
|
{
|
|
dd("can't find frame for window")
|
|
webProgress.removeProgressListener (this);
|
|
return;
|
|
}
|
|
}
|
|
else if (stateFlags == 786448)
|
|
{
|
|
frame = getFrameForDOMWindow(webProgress.DOMWindow);
|
|
if (!frame)
|
|
{
|
|
dd("can't find frame for window")
|
|
webProgress.removeProgressListener (this);
|
|
return;
|
|
}
|
|
frame.contentDocument.body.appendChild(frame.source.messages);
|
|
}
|
|
}
|
|
|
|
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(iframe)
|
|
{
|
|
const nsIWebProgress = Components.interfaces.nsIWebProgress;
|
|
const ALL = nsIWebProgress.NOTIFY_ALL;
|
|
const DOCUMENT = nsIWebProgress.NOTIFY_STATE_DOCUMENT;
|
|
const WINDOW = nsIWebProgress.NOTIFY_STATE_WINDOW;
|
|
|
|
function tryAgain ()
|
|
{
|
|
syncOutputFrame(iframe);
|
|
};
|
|
|
|
try
|
|
{
|
|
if ("contentDocument" in iframe && "webProgress" in iframe)
|
|
{
|
|
|
|
iframe.addProgressListener (client.progressListener, WINDOW);
|
|
iframe.loadURI ("chrome://chatzilla/content/outputwindow.html?" +
|
|
client.DEFAULT_STYLE);
|
|
}
|
|
else
|
|
{
|
|
setTimeout (tryAgain, 500);
|
|
}
|
|
}
|
|
catch (ex)
|
|
{
|
|
dd ("caught exception showing session view, will try again later.");
|
|
dd (dumpObjectTree(ex)+"\n");
|
|
setTimeout (tryAgain, 500);
|
|
}
|
|
}
|
|
|
|
function createMessages(source)
|
|
{
|
|
source.messages =
|
|
document.createElementNS ("http://www.w3.org/1999/xhtml",
|
|
"html:table");
|
|
|
|
source.messages.setAttribute ("class", "msg-table");
|
|
source.messages.setAttribute ("view-type", source.TYPE);
|
|
var tbody =
|
|
document.createElementNS ("http://www.w3.org/1999/xhtml",
|
|
"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 (!source)
|
|
{
|
|
dd ("** UNDEFINED passed to getTabForObject **");
|
|
dd (getStackTrace());
|
|
return null;
|
|
}
|
|
|
|
switch (source.TYPE)
|
|
{
|
|
case "IRCChanUser":
|
|
case "IRCUser":
|
|
name = source.nick;
|
|
break;
|
|
|
|
case "IRCNetwork":
|
|
case "IRCClient":
|
|
name = source.name;
|
|
break;
|
|
case "IRCChannel":
|
|
name = source.unicodeName;
|
|
break;
|
|
|
|
default:
|
|
dd ("** 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.name);
|
|
tb.setAttribute ("onclick", "onTabClick(" + id.quote() + ");");
|
|
tb.setAttribute ("crop", "right");
|
|
|
|
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);
|
|
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", "aHTMLTooltip");
|
|
browser.setAttribute ("context", "outputContext");
|
|
//browser.setAttribute ("onload", "scrollDown(true);");
|
|
browser.setAttribute ("onclick", "focusInput()");
|
|
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;
|
|
client.deck.appendChild (browser);
|
|
syncOutputFrame (browser);
|
|
}
|
|
|
|
return tb;
|
|
|
|
}
|
|
|
|
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("tabdnd_drop", url)))
|
|
{
|
|
onSimulateCommand("/css " + url);
|
|
}
|
|
else if (url.search(/^irc:\/\//i) != -1)
|
|
{
|
|
gotoIRCURL (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 "<url>\n<name>", goodie */
|
|
aXferData.data.addDataForFlavour("text/x-moz-url", href + "\n" + name);
|
|
aXferData.data.addDataForFlavour("text/unicode", href);
|
|
aXferData.data.addDataForFlavour("text/html", "<a href='" + href + "'>" +
|
|
name + "</a>");
|
|
}
|
|
|
|
function deleteTab (tb)
|
|
{
|
|
var i, key = Number(tb.getAttribute("viewKey"));
|
|
|
|
if (!isNaN(key))
|
|
{
|
|
if ("isPermanent" in client.viewsArray[key].source &&
|
|
client.viewsArray[key].source.isPermanent)
|
|
{
|
|
window.alert (getMsg("deleteTabMsg"));
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
/* 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);
|
|
}
|
|
}
|
|
else
|
|
dd ("*** INVALID OBJECT passed to deleteTab (" + tb + ") " +
|
|
"no viewKey attribute. (" + key + ")");
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
function filterOutput (msg, msgtype)
|
|
{
|
|
if ("outputFilters" in client)
|
|
{
|
|
for (var f in client.outputFilters)
|
|
{
|
|
if (client.outputFilters[f].enabled)
|
|
msg = client.outputFilters[f].func(msg, msgtype);
|
|
}
|
|
}
|
|
|
|
return msg;
|
|
}
|
|
|
|
client.connectToNetwork =
|
|
function cli_connet (netname, pass)
|
|
{
|
|
if (!(netname in client.networks))
|
|
{
|
|
display (getMsg("cli_attachNoNet", netname), "ERROR");
|
|
return false;
|
|
}
|
|
|
|
var netobj = client.networks[netname];
|
|
|
|
if (!("messages" in netobj))
|
|
netobj.displayHere (getMsg("cli_attachOpened", netname), "INFO");
|
|
setCurrentObject(netobj);
|
|
|
|
if (netobj.isConnected())
|
|
{
|
|
netobj.display (getMsg("cli_attachAlreadyThere", netname), "ERROR");
|
|
return true;
|
|
}
|
|
|
|
if (CIRCNetwork.prototype.INITIAL_NICK == client.defaultNick)
|
|
CIRCNetwork.prototype.INITIAL_NICK =
|
|
prompt (getMsg("cli_attachGetNick"), client.defaultNick);
|
|
|
|
if (!("connecting" in netobj))
|
|
netobj.display (getMsg("cli_attachWorking",netobj.name), "INFO");
|
|
netobj.connect(pass);
|
|
return true;
|
|
}
|
|
|
|
|
|
client.getURL =
|
|
function cli_geturl ()
|
|
{
|
|
return "irc://";
|
|
}
|
|
|
|
client.load =
|
|
function cli_load(url, obj)
|
|
{
|
|
if (!client._loader)
|
|
{
|
|
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.createInstance (mozIJSSubScriptLoader);
|
|
}
|
|
|
|
try {
|
|
client.currentObject.display (getMsg("cli_loadLoading", url));
|
|
client._loader.loadSubScript (url, obj);
|
|
}
|
|
catch (ex)
|
|
{
|
|
var msg = getMsg("cli_loadError", ex.lineNumber, ex.fileName, ex);
|
|
client.currentObject.display (msg, "ERROR");
|
|
}
|
|
}
|
|
|
|
|
|
client.sayToCurrentTarget =
|
|
function cli_say(msg)
|
|
{
|
|
switch (client.currentObject.TYPE)
|
|
{
|
|
case "IRCChannel":
|
|
case "IRCUser":
|
|
case "IRCChanUser":
|
|
msg = filterOutput (msg, "PRIVMSG");
|
|
client.currentObject.display (msg, "PRIVMSG", "ME!",
|
|
client.currentObject);
|
|
client.currentObject.say (fromUnicode(msg));
|
|
break;
|
|
|
|
case "IRCClient":
|
|
client.onInputEval ({inputData: msg});
|
|
break;
|
|
|
|
default:
|
|
if (msg != "")
|
|
client.currentObject.display
|
|
(getMsg("cli_sayMsg", client.currentObject.TYPE), "ERROR");
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
CIRCNetwork.prototype.display =
|
|
function n_display (message, msgtype, sourceObj, destObj)
|
|
{
|
|
var o = getObjectDetails(client.currentObject);
|
|
|
|
if (client.SLOPPY_NETWORKS && client.currentObject != client &&
|
|
client.currentObject != this && o.network == this &&
|
|
o.server.connection.isConnected)
|
|
client.currentObject.display (message, msgtype, sourceObj, destObj);
|
|
else
|
|
this.displayHere (message, msgtype, sourceObj, destObj);
|
|
}
|
|
|
|
CIRCUser.prototype.display =
|
|
function u_display(message, msgtype, sourceObj, destObj)
|
|
{
|
|
if ("messages" in this)
|
|
this.displayHere (message, msgtype, sourceObj, destObj);
|
|
else
|
|
{
|
|
var o = getObjectDetails(client.currentObject);
|
|
if (o.server.connection.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);
|
|
}
|
|
}
|
|
|
|
function display (message, msgtype, sourceObj, destObj)
|
|
{
|
|
client.currentObject.display (message, msgtype, sourceObj, destObj);
|
|
}
|
|
|
|
client.display =
|
|
client.displayHere =
|
|
CIRCNetwork.prototype.displayHere =
|
|
CIRCChannel.prototype.display =
|
|
CIRCChannel.prototype.displayHere =
|
|
CIRCUser.prototype.displayHere =
|
|
function __display(message, msgtype, sourceObj, destObj)
|
|
{
|
|
var canMergeData = false;
|
|
var canCollapseRow = false;
|
|
|
|
function setAttribs (obj, c, attrs)
|
|
{
|
|
if (attrs)
|
|
{
|
|
for (var a in attrs)
|
|
obj.setAttribute (a, attrs[a]);
|
|
}
|
|
obj.setAttribute ("class", c);
|
|
obj.setAttribute ("msg-type", msgtype);
|
|
obj.setAttribute ("msg-dest", toAttr);
|
|
obj.setAttribute ("dest-type", toType);
|
|
obj.setAttribute ("view-type", viewType);
|
|
if (fromAttr)
|
|
if (fromUser)
|
|
obj.setAttribute ("msg-user", fromAttr);
|
|
else
|
|
obj.setAttribute ("msg-source", fromAttr);
|
|
}
|
|
|
|
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| */
|
|
var me;
|
|
if ("server" in o && "me" in o.server)
|
|
{
|
|
me = o.server.me; /* get the object representing the user */
|
|
}
|
|
if (sourceObj == "ME!") sourceObj = me; /* if the caller to passes "ME!"*/
|
|
if (destObj == "ME!") destObj = me; /* substitute the actual object */
|
|
|
|
var fromType = (sourceObj && sourceObj.TYPE) ? sourceObj.TYPE : "unk";
|
|
var fromUser = (fromType.search(/IRC.*User/) != -1);
|
|
var fromAttr;
|
|
if (fromUser && sourceObj == me) fromAttr = me.nick + " ME!";
|
|
else if (fromUser) fromAttr = sourceObj.nick;
|
|
else if (typeof sourceObj == "object") fromAttr = sourceObj.name;
|
|
|
|
var toType = (destObj) ? destObj.TYPE : "unk";
|
|
var toAttr;
|
|
|
|
if (destObj && destObj == me)
|
|
toAttr = me.nick + " ME!";
|
|
else if (toType == "IRCUser")
|
|
toAttr = destObj.nick;
|
|
else if (typeof destObj == "object")
|
|
toAttr = destObj.name;
|
|
|
|
/* 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 msgRow = document.createElementNS("http://www.w3.org/1999/xhtml",
|
|
"html:tr");
|
|
setAttribs(msgRow, "msg");
|
|
|
|
//dd ("fromType is " + fromType + ", fromAttr is " + fromAttr);
|
|
var d = new Date();
|
|
var mins = d.getMinutes();
|
|
if (mins < 10)
|
|
mins = "0" + mins;
|
|
var statusString;
|
|
|
|
if (fromUser)
|
|
{
|
|
statusString =
|
|
getMsg("cli_statusString", [d.getMonth() + 1, d.getDate(),
|
|
d.getHours(), mins,
|
|
sourceObj.nick + "!" +
|
|
sourceObj.name + "@" + sourceObj.host]);
|
|
}
|
|
else
|
|
{
|
|
var name;
|
|
if (sourceObj)
|
|
{
|
|
name = (sourceObj.TYPE == "CIRCChannel") ?
|
|
sourceObj.unicodeName : sourceObj.name;
|
|
}
|
|
else
|
|
{
|
|
name = (this.TYPE == "CIRCChannel") ?
|
|
this.unicodeName : this.name;
|
|
}
|
|
|
|
statusString =
|
|
getMsg("cli_statusString", [d.getMonth() + 1, d.getDate(),
|
|
d.getHours(), mins, name]);
|
|
}
|
|
|
|
if (fromType.search(/IRC.*User/) != -1 &&
|
|
msgtype.search(/PRIVMSG|ACTION|NOTICE/) != -1)
|
|
{
|
|
/* do nick things here */
|
|
var nick;
|
|
var nickURL;
|
|
|
|
if (sourceObj != me)
|
|
{
|
|
nick = sourceObj.properNick;
|
|
if (!nick)
|
|
nick = sourceObj.name + "@" + sourceObj.host;
|
|
else if ("getURL" in sourceObj)
|
|
nickURL = sourceObj.getURL();
|
|
|
|
if (toType == "IRCUser") /* msg from user to me */
|
|
{
|
|
getAttention = true;
|
|
this.defaultCompletion = "/msg " + nick + " ";
|
|
}
|
|
else /* msg from user to channel */
|
|
{
|
|
if (typeof (message == "string") && me)
|
|
{
|
|
isImportant = msgIsImportant (message, nick, me.nick);
|
|
if (isImportant)
|
|
this.defaultCompletion = nick +
|
|
client.ADDRESSED_NICK_SEP + " ";
|
|
}
|
|
}
|
|
}
|
|
else if (toType == "IRCUser") /* msg from me to user */
|
|
{
|
|
if (this.TYPE == "IRCUser")
|
|
nick = sourceObj.properNick;
|
|
else
|
|
nick = destObj.properNick;
|
|
}
|
|
else /* msg from me to channel */
|
|
{
|
|
nick = sourceObj.properNick;
|
|
}
|
|
|
|
if (!("mark" in this))
|
|
this.mark = "odd";
|
|
|
|
if (!("lastNickDisplayed" in this) ||
|
|
this.lastNickDisplayed != nick)
|
|
{
|
|
this.lastNickDisplayed = nick;
|
|
this.mark = (this.mark == "even") ? "odd" : "even";
|
|
}
|
|
|
|
var msgSource = document.createElementNS("http://www.w3.org/1999/xhtml",
|
|
"html:td");
|
|
setAttribs (msgSource, "msg-user", {statusText: statusString});
|
|
if (isImportant)
|
|
msgSource.setAttribute ("important", "true");
|
|
if (nick.length > client.MAX_NICK_DISPLAY)
|
|
blockLevel = true;
|
|
if (nickURL)
|
|
{
|
|
var nick_anchor =
|
|
document.createElementNS("http://www.w3.org/1999/xhtml",
|
|
"html:a");
|
|
nick_anchor.setAttribute ("class", "chatzilla-link");
|
|
nick_anchor.setAttribute ("href", nickURL);
|
|
nick_anchor.appendChild (newInlineText (nick));
|
|
msgSource.appendChild (nick_anchor);
|
|
}
|
|
else
|
|
{
|
|
msgSource.appendChild (newInlineText (nick));
|
|
}
|
|
msgRow.appendChild (msgSource);
|
|
canMergeData = client.COLLAPSE_MSGS;
|
|
canCollapseRow = client.COLLAPSE_ROWS;
|
|
|
|
}
|
|
else
|
|
{
|
|
isSuperfluous = true;
|
|
if (!client.debugMode && msgtype in client.responseCodeMap)
|
|
{
|
|
code = client.responseCodeMap[msgtype];
|
|
}
|
|
else
|
|
{
|
|
if (!client.debugMode && client.HIDE_CODES)
|
|
code = client.DEFAULT_RESPONSE_CODE;
|
|
else
|
|
code = "[" + msgtype + "]";
|
|
}
|
|
|
|
/* Display the message code */
|
|
var msgType = document.createElementNS("http://www.w3.org/1999/xhtml",
|
|
"html:td");
|
|
setAttribs (msgType, "msg-type", {statusText: statusString});
|
|
|
|
msgType.appendChild (newInlineText (code));
|
|
msgRow.appendChild (msgType);
|
|
}
|
|
|
|
if (message)
|
|
{
|
|
var msgData = document.createElementNS("http://www.w3.org/1999/xhtml",
|
|
"html:td");
|
|
setAttribs (msgData, "msg-data", {statusText: statusString,
|
|
colspan: client.INITIAL_COLSPAN});
|
|
if (isImportant)
|
|
msgData.setAttribute ("important", "true");
|
|
|
|
if ("mark" in this)
|
|
msgData.setAttribute ("mark", this.mark);
|
|
|
|
if (typeof message == "string")
|
|
{
|
|
msgData.appendChild (stringToMsg (message, this));
|
|
}
|
|
else
|
|
msgData.appendChild (message);
|
|
|
|
msgRow.appendChild (msgData);
|
|
}
|
|
|
|
if (isImportant)
|
|
msgRow.setAttribute ("important", "true");
|
|
|
|
if (blockLevel)
|
|
{
|
|
/* putting a div here crashes mozilla, so fake it with nested tables
|
|
* for now */
|
|
var tr = document.createElementNS ("http://www.w3.org/1999/xhtml",
|
|
"html:tr");
|
|
tr.setAttribute ("class", "msg-nested-tr");
|
|
var td = document.createElementNS ("http://www.w3.org/1999/xhtml",
|
|
"html:td");
|
|
td.setAttribute ("class", "msg-nested-td");
|
|
td.setAttribute ("colspan", "2");
|
|
|
|
tr.appendChild(td);
|
|
var table = document.createElementNS ("http://www.w3.org/1999/xhtml",
|
|
"html:table");
|
|
table.setAttribute ("class", "msg-nested-table");
|
|
table.setAttribute ("cellpadding", "0");
|
|
|
|
td.appendChild (table);
|
|
var tbody = document.createElementNS ("http://www.w3.org/1999/xhtml",
|
|
"html:tbody");
|
|
|
|
tbody.appendChild (msgRow);
|
|
table.appendChild (tbody);
|
|
msgRow = tr;
|
|
canMergeData = false;
|
|
canCollapseRow = false;
|
|
}
|
|
|
|
addHistory (this, msgRow, canMergeData, canCollapseRow);
|
|
if (isImportant || getAttention)
|
|
{
|
|
setTabState(this, "attention");
|
|
if (client.FLASH_WINDOW)
|
|
window.getAttention();
|
|
}
|
|
else
|
|
{
|
|
if (isSuperfluous)
|
|
setTabState(this, "superfluous");
|
|
else
|
|
setTabState(this, "activity");
|
|
}
|
|
|
|
if (isImportant && client.COPY_MESSAGES)
|
|
{
|
|
if ("network" in o && o.network != this)
|
|
o.network.displayHere("{" + this.name + "} " + message, msgtype,
|
|
sourceObj, destObj);
|
|
}
|
|
}
|
|
|
|
function addHistory (source, obj, mergeData, collapseRow)
|
|
{
|
|
if (!("messages" in source) || (source.messages == null))
|
|
createMessages(source);
|
|
|
|
var tbody = source.messages.firstChild;
|
|
|
|
var needScroll = false;
|
|
|
|
if (client.PRINT_DIRECTION == 1)
|
|
{
|
|
if (mergeData || collapseRow)
|
|
{
|
|
var thisUserCol = obj.firstChild;
|
|
var thisMessageCol = thisUserCol.nextSibling;
|
|
var ci = findPreviousColumnInfo(source.messages);
|
|
var nickColumns = ci.nickColumns;
|
|
var rowExtents = ci.extents;
|
|
var nickColumnCount = nickColumns.length;
|
|
var sameNick = (nickColumnCount > 0 &&
|
|
nickColumns[nickColumnCount - 1].
|
|
getAttribute("msg-user") ==
|
|
thisUserCol.getAttribute("msg-user"));
|
|
var lastRowSpan = (nickColumnCount > 0) ?
|
|
Number(nickColumns[0].getAttribute("rowspan")) : 0;
|
|
if (sameNick && mergeData)
|
|
{
|
|
if (obj.getAttribute("important"))
|
|
{
|
|
nickColumns[nickColumnCount - 1].setAttribute ("important",
|
|
true);
|
|
}
|
|
/* message is from the same person as last time,
|
|
* strip the nick first... */
|
|
obj.removeChild(obj.firstChild);
|
|
/* Adjust height of previous cells, maybe. */
|
|
for (i = 0; i < rowExtents.length - 1; ++i)
|
|
{
|
|
var myLastData =
|
|
rowExtents[i].childNodes[nickColumnCount - 1];
|
|
var myLastRowSpan = (myLastData) ?
|
|
myLastData.getAttribute("rowspan") : 0;
|
|
if (myLastData && myLastRowSpan > 1)
|
|
{
|
|
myLastData.removeAttribute("rowspan");
|
|
}
|
|
}
|
|
/* then add one to the colspan for the previous user columns */
|
|
if (!lastRowSpan)
|
|
lastRowSpan = 1;
|
|
for (var i = 0; i < nickColumns.length; ++i)
|
|
nickColumns[i].setAttribute ("rowspan", lastRowSpan + 1);
|
|
}
|
|
else if (!sameNick && collapseRow && nickColumnCount > 0 &&
|
|
nickColumnCount < client.MAX_MSG_PER_ROW)
|
|
{
|
|
/* message is from a different person, but is elegible to
|
|
* be contained by the previous row. */
|
|
var tr = nickColumns[0].parentNode;
|
|
for (i = 0; i < rowExtents.length; ++i)
|
|
rowExtents[i].lastChild.removeAttribute("colspan");
|
|
obj.firstChild.setAttribute ("rowspan", lastRowSpan);
|
|
tr.appendChild (obj.firstChild);
|
|
var lastColSpan =
|
|
Number(rowExtents[0].lastChild.getAttribute("colspan"));
|
|
obj.lastChild.setAttribute ("colspan", lastColSpan - 2);
|
|
obj.lastChild.setAttribute ("rowspan", lastRowSpan);
|
|
tr.appendChild (obj.lastChild);
|
|
obj = null;
|
|
}
|
|
}
|
|
|
|
if ("frame" in source)
|
|
needScroll = checkScroll (source.frame);
|
|
if (obj)
|
|
tbody.appendChild (obj);
|
|
}
|
|
else
|
|
tbody.insertBefore (obj, source.messages.firstChild);
|
|
|
|
if (source.MAX_MESSAGES)
|
|
{
|
|
if (typeof source.messageCount != "number")
|
|
source.messageCount = 1;
|
|
else
|
|
source.messageCount++;
|
|
|
|
if (source.messageCount > source.MAX_MESSAGES)
|
|
if (client.PRINT_DIRECTION == 1)
|
|
{
|
|
tbody.removeChild (tbody.firstChild);
|
|
--source.messageCount;
|
|
while (tbody.firstChild &&
|
|
tbody.firstChild.firstChild.getAttribute("class") ==
|
|
"msg-data")
|
|
{
|
|
--source.messageCount;
|
|
tbody.removeChild (tbody.firstChild);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tbody.removeChild (tbody.lastChild);
|
|
--source.messageCount;
|
|
while (tbody.lastChild &&
|
|
tbody.lastChild.firstChild.getAttribute("class") ==
|
|
"msg-data")
|
|
{
|
|
--source.messageCount;
|
|
tbody.removeChild (tbody.lastChild);
|
|
}
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
var extents = new Array();
|
|
var tr = table.firstChild.lastChild;
|
|
var className = tr ? tr.firstChild.getAttribute("class") : "";
|
|
while (tr && className.search(/msg-user|msg-type|msg-nested-td/) == -1)
|
|
{
|
|
extents.push(tr);
|
|
tr = tr.previousSibling;
|
|
if (tr)
|
|
className = tr.firstChild.getAttribute("class");
|
|
}
|
|
|
|
if (!tr || className != "msg-user")
|
|
return {extents: [], nickColumns: []};
|
|
|
|
extents.push(tr);
|
|
var nickCol = tr.firstChild;
|
|
var nickCols = new Array();
|
|
while (nickCol)
|
|
{
|
|
if (nickCol.getAttribute("class") == "msg-user")
|
|
nickCols.push (nickCol);
|
|
nickCol = nickCol.nextSibling.nextSibling;
|
|
}
|
|
|
|
return {extents: extents, nickColumns: nickCols};
|
|
}
|
|
|
|
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)
|
|
{
|
|
for (var n in client.networks)
|
|
if ("primServ" in client.networks[n])
|
|
client.networks[n].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.commands.listNames(word.substr(1));
|
|
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;
|
|
}
|
|
|
|
CIRCChannel.prototype.performTabMatch =
|
|
CIRCNetwork.prototype.performTabMatch =
|
|
CIRCUser.prototype.performTabMatch =
|
|
function gettabmatch_usr (line, wordStart, wordEnd, word, cursorpos)
|
|
{
|
|
if (wordStart == 0 && line[0] == client.COMMAND_CHAR)
|
|
return client.performTabMatch (line, wordStart, wordEnd, word,
|
|
cursorpos);
|
|
|
|
if (!("users" in this))
|
|
return [];
|
|
|
|
var users = this.users;
|
|
var nicks = new Array();
|
|
|
|
for (var n in users)
|
|
nicks.push (users[n].nick);
|
|
|
|
var matches = matchEntry (word, nicks);
|
|
if (matches.length == 1)
|
|
{
|
|
matches[0] = this.users[matches[0]].properNick;
|
|
if (wordStart == 0)
|
|
matches[0] += client.ADDRESSED_NICK_SEP;
|
|
|
|
if (wordEnd == line.length)
|
|
{
|
|
/* add a space if the word is at the end of the line. */
|
|
matches[0] += " ";
|
|
}
|
|
}
|
|
|
|
return matches;
|
|
}
|
|
|
|
/**
|
|
* Retrieves the selected nicks from the user-list
|
|
* tree object. This grabs the tree element's
|
|
* selected items, extracts the appropriate text
|
|
* for the nick, promotes each nick to a CIRCChanUser
|
|
* instance and returns an array of these objects.
|
|
*/
|
|
CIRCChannel.prototype.getSelectedUsers =
|
|
function my_getselectedusers ()
|
|
{
|
|
var tree = document.getElementById("user-list");
|
|
var cell; /* reference to each selected cell of the tree object */
|
|
var rv_ary = new Array; /* return value arrray for CIRCChanUser objects */
|
|
|
|
var rangeCount = tree.view.selection.getRangeCount();
|
|
for (var i = 0; i < rangeCount; ++i)
|
|
{
|
|
var start = {}, end = {};
|
|
tree.view.selection.getRangeAt(i, start, end);
|
|
for (var k = start.value; k <= end.value; ++k)
|
|
{
|
|
var item = tree.contentView.getItemAtIndex(k);
|
|
|
|
/* First, set the reference to the XUL element. */
|
|
cell = item.firstChild.childNodes[2];
|
|
|
|
/* Now, create an instance of CIRCChaneUser by passing the text
|
|
* of the cell to the getUser function of this CIRCChannel instance.
|
|
*/
|
|
rv_ary[i] = this.getUser( cell.getAttribute("label") );
|
|
}
|
|
}
|
|
|
|
/*
|
|
* USAGE NOTE: If the return value is non-null, the caller
|
|
* can assume the array is valid, and NOT
|
|
* need to check the length, and vice versa.
|
|
*/
|
|
|
|
return rv_ary.length > 0 ? rv_ary : null;
|
|
|
|
}
|
|
|
|
CIRCChannel.prototype.getGraphResource =
|
|
function my_graphres ()
|
|
{
|
|
if (!("rdfRes" in this))
|
|
{
|
|
this.rdfRes =
|
|
client.rdf.GetResource(RES_PFX + "CHANNEL:" +
|
|
this.parent.parent.name +
|
|
":" + this.name);
|
|
//dd ("created channel resource " + this.rdfRes.Value);
|
|
|
|
}
|
|
|
|
return this.rdfRes;
|
|
}
|
|
|
|
CIRCUser.prototype.getGraphResource =
|
|
function usr_graphres()
|
|
{
|
|
if (this.TYPE != "IRCChanUser")
|
|
dd ("** WARNING: cuser.getGraphResource called on wrong object **");
|
|
|
|
var rdf = client.rdf;
|
|
|
|
if (!("rdfRes" in this))
|
|
{
|
|
if (!("nextResID" in CIRCUser))
|
|
CIRCUser.nextResID = 0;
|
|
|
|
this.rdfRes = rdf.GetResource (RES_PFX + "CUSER:" +
|
|
this.parent.parent.parent.name + ":" +
|
|
this.parent.name + ":" +
|
|
CIRCUser.nextResID++);
|
|
|
|
//dd ("created cuser resource " + this.rdfRes.Value);
|
|
|
|
rdf.Assert (this.rdfRes, rdf.resNick, rdf.GetLiteral(this.properNick));
|
|
if (this.name)
|
|
rdf.Assert (this.rdfRes, rdf.resUser, rdf.GetLiteral(this.name));
|
|
else
|
|
rdf.Assert (this.rdfRes, rdf.resUser, rdf.litUnk);
|
|
if (this.host)
|
|
rdf.Assert (this.rdfRes, rdf.resHost, rdf.GetLiteral(this.host));
|
|
else
|
|
rdf.Assert (this.rdfRes, rdf.resHost, rdf.litUnk);
|
|
|
|
rdf.Assert (this.rdfRes, rdf.resOp,
|
|
this.isOp ? rdf.litTrue : rdf.litFalse);
|
|
rdf.Assert (this.rdfRes, rdf.resVoice,
|
|
this.isVoice ? rdf.litTrue : rdf.litFalse);
|
|
}
|
|
|
|
return this.rdfRes;
|
|
|
|
}
|
|
|
|
CIRCUser.prototype.updateGraphResource =
|
|
function usr_updres()
|
|
{
|
|
if (this.TYPE != "IRCChanUser")
|
|
dd ("** WARNING: cuser.updateGraphResource called on wrong object **");
|
|
|
|
if (!("rdfRes" in this))
|
|
this.getGraphResource();
|
|
|
|
var rdf = client.rdf;
|
|
|
|
rdf.Change (this.rdfRes, rdf.resNick, rdf.GetLiteral(this.properNick));
|
|
if (this.name)
|
|
rdf.Change (this.rdfRes, rdf.resUser, rdf.GetLiteral(this.name));
|
|
else
|
|
rdf.Change (this.rdfRes, rdf.resUser, rdf.litUnk);
|
|
if (this.host)
|
|
rdf.Change (this.rdfRes, rdf.resHost, rdf.GetLiteral(this.host));
|
|
else
|
|
rdf.Change (this.rdfRes, rdf.resHost, rdf.litUnk);
|
|
|
|
rdf.Change (this.rdfRes, rdf.resOp,
|
|
this.isOp ? rdf.litTrue : rdf.litFalse);
|
|
rdf.Change (this.rdfRes, rdf.resVoice,
|
|
this.isVoice ? rdf.litTrue : rdf.litFalse);
|
|
}
|