home *** CD-ROM | disk | FTP | other *** search
/ ftp.swcp.com / ftp.swcp.com.zip / ftp.swcp.com / mac / mozilla-mac-0.9.sea.hqx / mozilla-mac-0.9 / Chrome / chatzilla.jar / content / chatzilla / static.js < prev   
Text File  |  2001-05-05  |  53KB  |  1,712 lines

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2.  *
  3.  * The contents of this file are subject to the Mozilla Public
  4.  * License Version 1.1 (the "License"); you may not use this file
  5.  * except in compliance with the License. You may obtain a copy of
  6.  * the License at http://www.mozilla.org/MPL/
  7.  *
  8.  * Software distributed under the License is distributed on an "AS
  9.  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  10.  * implied. See the License for the specific language governing
  11.  * rights and limitations under the License.
  12.  *
  13.  * The Original Code is ChatZilla
  14.  *
  15.  * The Initial Developer of the Original Code is New Dimensions Consulting,
  16.  * Inc. Portions created by New Dimensions Consulting, Inc. are
  17.  * Copyright (C) 1999 New Dimenstions Consulting, Inc. All
  18.  * Rights Reserved.
  19.  *
  20.  * Contributor(s):
  21.  *  Robert Ginda, rginda@ndcico.com, original author
  22.  */
  23.  
  24. if (DEBUG)
  25.     dd = function (m) { dump ("-*- chatzilla: " + m + "\n"); }
  26. else
  27.     dd = function (){};
  28.  
  29. var client = new Object();
  30.  
  31. client.defaultNick = "IRCMonkey";
  32.  
  33. client.TYPE = "IRCClient";
  34. client.COMMAND_CHAR = "/";
  35. client.STEP_TIMEOUT = 500;
  36. client.MAX_MESSAGES = 200;
  37. client.MAX_HISTORY = 50;
  38. /* longest nick to show in display before forcing the message to a block level
  39.  * element */
  40. client.MAX_NICK_DISPLAY = 14;
  41. /* longest word to show in display before abbreviating */
  42. client.MAX_WORD_DISPLAY = 40;
  43. client.PRINT_DIRECTION = 1; /*1 => new messages at bottom, -1 => at top */
  44. client.ADDRESSED_NICK_SEP = ": ";
  45.  
  46. client.NOTIFY_TIMEOUT = 5 * 60 * 100; /* update notify list every 5 minutes */
  47.  
  48. client.SLOPPY_NETWORKS = true; /* true if msgs from a network can be displayed
  49.                                 * on the current object if it is related to
  50.                                 * the network (ie, /whois results will appear
  51.                                 * on the channel you're viewing, if that channel
  52.                                 * is on the network that the results came from)
  53.                                 */
  54. client.DOUBLETAB_TIME = 500;
  55. client.IMAGEDIR = "chrome://chatzilla/skin/images/";
  56. client.HIDE_CODES = true;      /* true if you'd prefer to show numeric response 
  57.                                 * codes as some default value (ie, "===") */
  58. client.DEFAULT_RESPONSE_CODE = "===";
  59.  
  60.  
  61. /* XXX maybe move this into css */
  62. client.responseCodeMap = new Object();
  63. client.responseCodeMap["HELLO"]  = "[HELLO]";
  64. client.responseCodeMap["HELP"]  = "[HELP]";
  65. client.responseCodeMap["USAGE"]  = "[USAGE]";
  66. client.responseCodeMap["ERROR"]  = "[ERROR]";
  67. client.responseCodeMap["INFO"]  = "[INFO]";
  68. client.responseCodeMap["EVAL-IN"]  = "[EVAL-IN]";
  69. client.responseCodeMap["EVAL-OUT"]  = "[EVAL-OUT]";
  70. client.responseCodeMap["JOIN"]  = "-->|";
  71. client.responseCodeMap["PART"]  = "<--|";
  72. client.responseCodeMap["QUIT"]  = "|<--";
  73. client.responseCodeMap["NICK"]  = "=-=";
  74. client.responseCodeMap["TOPIC"] = "=-=";
  75. client.responseCodeMap["KICK"]  = "=-=";
  76. client.responseCodeMap["MODE"]  = "=-=";
  77. client.responseCodeMap["END_STATUS"] = "---";
  78. client.responseCodeMap["376"]  = "---"; /* end of MOTD */
  79. client.responseCodeMap["318"]  = "---"; /* end of WHOIS */
  80. client.responseCodeMap["366"]  = "---"; /* end of NAMES */
  81.  
  82. client.name = "*client*";
  83. client.viewsArray = new Array();
  84. client.activityList = new Object();
  85. client.uiState = new Object(); /* state of ui elements (visible/collapsed) */
  86. client.inputHistory = new Array();
  87. client.lastHistoryReferenced = -1;
  88. client.incompleteLine = "";
  89. client.lastTabUp = new Date();
  90. client.stalkingVictims = new Array();
  91.  
  92. CIRCNetwork.prototype.INITIAL_NICK = client.defaultNick;
  93. CIRCNetwork.prototype.INITIAL_NAME = "chatzilla";
  94. CIRCNetwork.prototype.INITIAL_DESC = "New Now Know How";
  95. CIRCNetwork.prototype.INITIAL_CHANNEL = "";
  96. CIRCNetwork.prototype.MAX_MESSAGES = 100;
  97. CIRCNetwork.prototype.IGNORE_MOTD = false;
  98.  
  99. CIRCServer.prototype.READ_TIMEOUT = 0;
  100. CIRCServer.prototype.VERSION_RPLY = "ChatZilla 0.8 [" + navigator.userAgent +
  101.     "]";
  102.  
  103. CIRCUser.prototype.MAX_MESSAGES = 200;
  104.  
  105. CIRCChannel.prototype.MAX_MESSAGES = 300;
  106.  
  107. CIRCChanUser.prototype.MAX_MESSAGES = 200;
  108.  
  109. window.onresize =
  110. function ()
  111. {
  112.     scrollDown();
  113. }
  114.  
  115. function initStatic()
  116. {
  117.     var obj;
  118.  
  119.     const nsISound = Components.interfaces.nsISound;
  120.     client.sound =
  121.         Components.classes["@mozilla.org/sound;1"].createInstance(nsISound);
  122.     
  123.     obj = document.getElementById("input");
  124.     obj.addEventListener("keyup", onInputKeyUp, false);
  125.     obj = document.getElementById("multiline-input");
  126.     obj.addEventListener("keyup", onMultilineInputKeyUp, false);
  127.  
  128.     window.onkeypress = onWindowKeyPress;
  129.  
  130.     setMenuCheck ("menu-dmessages", 
  131.                   client.eventPump.getHook ("event-tracer").enabled);
  132.     setMenuCheck ("menu-munger", client.munger.enabled);
  133.  
  134.     client.uiState["toolbar"] =
  135.         setMenuCheck ("menu-view-toolbar", isVisible("views-tbar"));
  136.     client.uiState["info"] =
  137.         setMenuCheck ("menu-view-info", isVisible("user-list-box"));
  138.     client.uiState["status"] =
  139.         setMenuCheck ("menu-view-status", isVisible("status-bar-tbar"));
  140.  
  141.     client.statusBar = new Object();
  142.     
  143.     client.statusBar["net-name"] =
  144.         document.getElementById ("net-name");
  145.     client.statusBar["server-name"] =
  146.         document.getElementById ("server-name");
  147.     client.statusBar["server-nick"] =
  148.         document.getElementById ("server-nick");
  149.     client.statusBar["server-lag"] =
  150.         document.getElementById ("server-lag");
  151.     client.statusBar["last-ping"] =
  152.         document.getElementById ("last-ping");
  153.     client.statusBar["channel-name"] =
  154.         document.getElementById ("channel-name");
  155.     client.statusBar["channel-limit"] =
  156.         document.getElementById ("channel-limit");
  157.     client.statusBar["channel-key"] =
  158.         document.getElementById ("channel-key");
  159.     client.statusBar["channel-mode"] =
  160.         document.getElementById ("channel-mode");
  161.     client.statusBar["channel-users"] =
  162.         document.getElementById ("channel-users");
  163.     client.statusBar["channel-topic"] =
  164.         document.getElementById ("channel-topic");
  165.     client.statusBar["channel-topicby"] =
  166.         document.getElementById ("channel-topicby");
  167.  
  168.     onSortCol ("usercol-nick");
  169.  
  170.     client.display ("Welcome to ChatZilla...\n" +
  171.                     "Use /attach <network-name> connect to a network, or " +
  172.                     "click on one of the network names below.\n" +
  173.                     "For general IRC help and FAQs, please go to " +
  174.                     "<http://www.irchelp.org>.", "HELLO");
  175.     setCurrentObject (client);
  176.  
  177.     client.onInputNetworks();
  178.     client.onInputCommands();
  179.  
  180.     var ary = client.INITIAL_URLS.split(";");
  181.     for (var i in ary)
  182.     {
  183.         ary[i] = stringTrim(ary[i]);
  184.         if (ary[i])
  185.         {
  186.             if (ary[i].indexOf("irc://") != 0)
  187.                 ary[i] = "irc://" + ary[i];
  188.             gotoIRCURL (ary[i]);
  189.         }
  190.     }
  191.  
  192.     ary = client.INITIAL_VICTIMS.split(";");
  193.     for (i in ary)
  194.     {
  195.         ary[i] = stringTrim(ary[i]);
  196.         if (ary[i])
  197.             client.stalkingVictims.push (ary[i]);
  198.     }
  199.  
  200.     var m = document.getElementById ("menu-settings-autosave");
  201.     m.setAttribute ("checked", String(client.SAVE_SETTINGS));
  202.                     
  203.     if (document.location.search)
  204.         gotoIRCURL (document.location.search.substr(1));
  205.  
  206.     setInterval ("onNotifyTimeout()", client.NOTIFY_TIMEOUT);
  207.     
  208. }
  209.  
  210. function setMenuCheck (id, state)
  211. {
  212.     var m = document.getElementById(id);
  213.     m.setAttribute ("checked", String(Boolean(state)));
  214.     return state;
  215. }
  216.  
  217. function isVisible (id)
  218. {
  219.     var e = document.getElementById(id);
  220.  
  221.     if (!e)
  222.     {
  223.         dd ("** Bogus id ``" + id + "'' passed to isVisible() **");
  224.         return false;
  225.     }
  226.     
  227.     return (e.getAttribute ("collapsed") != "true");
  228. }
  229.  
  230. function initHost(obj)
  231. {
  232.  
  233.     client.commands = new CCommandManager();
  234.     addCommands (obj.commands);
  235.     
  236.     obj.networks = new Object();
  237.     obj.eventPump = new CEventPump (200);
  238.     
  239.     obj.networks["efnet"] =
  240.         new CIRCNetwork ("efnet",
  241.                          [{name: "irc.mcs.net", port: 6667},
  242.                           {name: "irc.prison.net", port: 6667},
  243.                           {name: "irc.freei.net", port: 6667},
  244.                           {name: "irc.magic.ca", port: 6667}],
  245.                          obj.eventPump);
  246.     obj.networks["moznet"] =
  247.         new CIRCNetwork ("moznet", [{name: "irc.mozilla.org", port: 6667}],
  248.                          obj.eventPump);
  249.     obj.networks["hybridnet"] =
  250.         new CIRCNetwork ("hybridnet", [{name: "irc.ssc.net", port: 6667}],
  251.                          obj.eventPump);
  252.     obj.networks["slashnet"] =
  253.         new CIRCNetwork ("slashnet", [{name: "irc.slashnet.org", port:6667}],
  254.                          obj.eventPump);
  255.     obj.networks["dalnet"] =
  256.         new CIRCNetwork ("dalnet", [{name: "irc.dal.net", port:6667}],
  257.                          obj.eventPump);
  258.     obj.networks["undernet"] =
  259.         new CIRCNetwork ("undernet", [{name: "irc.undernet.org", port:6667}],
  260.                          obj.eventPump);
  261.     obj.networks["webbnet"] =
  262.         new CIRCNetwork ("webbnet", [{name: "irc.webbnet.org", port:6667}],
  263.                          obj.eventPump);
  264.     obj.networks["opennet"] =
  265.         new CIRCNetwork ("opennet",         
  266.                          [{name:"irc.openprojects.net", port:6667},
  267.                           {name: "eu.opirc.nu", port:6667},
  268.                           {name: "au.opirc.nu", port:6667},
  269.                           {name: "us.opirc.nu", port:6667}],
  270.                          obj.eventPump);
  271.  
  272.     obj.primNet = obj.networks["efnet"];
  273.  
  274.     if (DEBUG)
  275.         /* hook all events EXCEPT server.poll and *.event-end types
  276.          * (the 4th param inverts the match) */
  277.         obj.eventPump.addHook ([{type: "poll", set: /^(server|dcc-chat)$/},
  278.                                {type: "event-end"}], event_tracer,
  279.                                "event-tracer", true /* negate */,
  280.                                false /* disable */);
  281.  
  282.     obj.munger = new CMunger();
  283.     obj.munger.enabled = true;
  284.     obj.munger.addRule
  285.         ("link", /((\w+):\/\/[^<>()\'\"\s]+|www(\.[^.<>()\'\"\s]+){2,})/,
  286.          insertLink);
  287.     obj.munger.addRule ("face",
  288.          /((^|\s)[\<\>]?[\;\=\:]\~?[\-\^\v]?[\)\|\(pP\<\>oO0\[\]\/\\](\s|$))/,
  289.          insertSmiley);
  290.     obj.munger.addRule ("ear", /(?:\s|^)(\(\*)(?:\s|$)/, insertEar);
  291.     obj.munger.addRule ("rheet", /(?:\s|^)(rhee+t\!*)(?:\s|$)/i, insertRheet);
  292.     obj.munger.addRule ("bold", /(?:\s|^)(\*[^*,.()]*\*)(?:[\s.,]|$)/, 
  293.                         "chatzilla-bold");
  294.     obj.munger.addRule ("italic", /(?:\s|^)(\/[^\/,.()]*\/)(?:[\s.,]|$)/,
  295.                         "chatzilla-italic");
  296.     /* allow () chars inside |code()| blocks */
  297.     obj.munger.addRule ("teletype", /(?:\s|^)(\|[^|,.]*\|)(?:[\s.,]|$)/,
  298.                         "chatzilla-teletype");
  299.     obj.munger.addRule ("underline", /(?:\s|^)(\_[^_,.()]*\_)(?:[\s.,]|$)/,
  300.                         "chatzilla-underline");
  301.     obj.munger.addRule ("smallcap", /(?:\s|^)(\#[^#,.()]*\#)(?:[\s.,]|$)/,
  302.                         "chatzilla-smallcap");
  303.     obj.munger.addRule ("word-hyphenator",
  304.                         new RegExp ("(\\S{" + client.MAX_WORD_DISPLAY + ",})"),
  305.                         insertHyphenatedWord);
  306.  
  307.     obj.rdf = new RDFHelper();
  308.     
  309.     obj.rdf.initTree("user-list");
  310.     obj.rdf.setTreeRoot("user-list", obj.rdf.resNullChan);
  311.     
  312. }
  313.  
  314. function insertLink (matchText, containerTag)
  315. {
  316.  
  317.     var href;
  318.     
  319.     if (matchText.indexOf ("://") == -1)
  320.         href = "http://" + matchText;
  321.     else
  322.         href = matchText;
  323.     
  324.     var anchor = document.createElementNS ("http://www.w3.org/1999/xhtml",
  325.                                            "html:a");
  326.     anchor.setAttribute ("href", href);
  327.     anchor.setAttribute ("class", "chatzilla-link");
  328.     anchor.setAttribute ("target", "_content");
  329.     insertHyphenatedWord (matchText, anchor);
  330.     containerTag.appendChild (anchor);
  331.     
  332. }
  333.  
  334. function insertRheet (matchText, containerTag)
  335. {
  336.  
  337.     var anchor = document.createElementNS ("http://www.w3.org/1999/xhtml",
  338.                                            "html:a");
  339.     anchor.setAttribute ("href",
  340.                          "ftp://ftp.mozilla.org/pub/mozilla/libraries/bonus-tracks/rheet.wav");
  341.     anchor.setAttribute ("class", "chatzilla-rheet chatzilla-link");
  342.     anchor.setAttribute ("target", "_content");
  343.     insertHyphenatedWord (matchText, anchor);
  344.     containerTag.appendChild (anchor);    
  345. }
  346.  
  347. function insertEar (matchText, containerTag)
  348. {
  349.     if (client.smileyText)
  350.         containerTag.appendChild (document.createTextNode (matchText));
  351.  
  352.     var img = document.createElementNS ("http://www.w3.org/1999/xhtml",
  353.                                         "html:img");
  354.     img.setAttribute ("src", client.IMAGEDIR + "face-ear.gif");
  355.     containerTag.appendChild (img);
  356.     
  357. }
  358.  
  359. function insertSmiley (emoticon, containerTag)
  360. {
  361.     var src = "";
  362.  
  363.     if (emoticon.search (/\;[-^v]?[\)>\]]/) != -1)
  364.         src = "face-wink.gif";
  365.     else if (emoticon.search (/[=:;][-^v]?[\)>\]]/) != -1)
  366.         src = "face-smile.gif";
  367.     else if (emoticon.search (/[=:;][-^v]?[\/\\]/) != -1)
  368.         src = "face-screw.gif";
  369.     else if (emoticon.search (/[=:;]\~[-^v]?\(/) != -1)
  370.         src = "face-cry.gif";
  371.     else if (emoticon.search (/[=:;][-^v]?[\(<\[]/) != -1)
  372.         src = "face-frown.gif";
  373.     else if (emoticon.search (/\<?[=:;][-^v]?[0oO]/) != -1)
  374.         src = "face-surprise.gif";
  375.     else if (emoticon.search (/[=:;][-^v]?[pP]/) != -1)
  376.         src = "face-tongue.gif";
  377.     else if (emoticon.search (/\>?[=:;][\-\^\v]?[\(\|]/) != -1)
  378.         src = "face-angry.gif";
  379.  
  380.     if (!src || client.smileyText)
  381.         containerTag.appendChild (document.createTextNode (emoticon));
  382.  
  383.     if (src)
  384.     {
  385.         var img = document.createElementNS ("http://www.w3.org/1999/xhtml",
  386.                                             "html:img");
  387.         img.setAttribute ("src", client.IMAGEDIR + src);
  388.         img.setAttribute ("class", "smiley-image");
  389.         containerTag.appendChild (img);
  390.     }
  391.  
  392.     
  393. }
  394.  
  395. function insertHyphenatedWord (longWord, containerTag)
  396. {
  397.     var wordParts = splitLongWord (longWord, client.MAX_WORD_DISPLAY);
  398.     for (var i = 0; i < wordParts.length; ++i)
  399.     {
  400.         containerTag.appendChild (document.createTextNode (wordParts[i]));
  401.         if (i != wordParts.length)
  402.         {
  403.             var img = document.createElementNS ("http://www.w3.org/1999/xhtml",
  404.                                                 "html:img");
  405.             img.setAttribute ("class", "spacer-image");
  406.             containerTag.appendChild (img);
  407.         }
  408.     }
  409. }
  410.  
  411. function msgIsImportant (msg, sourceNick, myNick)
  412. {    
  413.     var sv = "(" + myNick + ")";
  414.     if (client.stalkingVictims.length > 0)
  415.         sv += "|(" + client.stalkingVictims.join(")|(") + ")";
  416.     
  417.     var str = "(^|[\\W\\s])" + sv + "([\\W\\s]|$)";
  418.     var re = new RegExp(str, "i");
  419.     if (msg.search(re) != -1 || sourceNick && sourceNick.search(re) != -1)
  420.         return true;
  421.  
  422.     return false;    
  423. }
  424.  
  425. /* timer-based mainloop */
  426. function mainStep()
  427. {
  428.     if (!frames[0].initialized)
  429.     {  /* voodoo required for skin switching.  When the user changes a skin,
  430.         * the output document is reloaded. this document *cannot* tell us
  431.         * it has been reloaded, because the type="content" attribute used
  432.         * on the iframe (to allow selection) also keeps the iframe from getting
  433.         * to the chrome above it.  Instead, we poll the document looking for
  434.         * the "initialized" variable. If it's not there, we reset the current
  435.         * object, and set initialized in the document. */
  436.         setClientOutput(frames[0].document);        
  437.         if (client.output)
  438.         {
  439.             var o = client.currentObject;
  440.             client.currentObject = null;
  441.             setCurrentObject (o);
  442.             frames[0].initialized = true;
  443.         }
  444.     }
  445.  
  446.     client.eventPump.stepEvents();
  447.     setTimeout ("mainStep()", client.STEP_TIMEOUT);
  448.     
  449. }
  450.  
  451. function quicklistCallback (element, ndx, ary) 
  452. {   
  453.     /* Check whether the selected attribute == true */
  454.     if (element.getAttribute("selected") == "true")
  455.     {
  456.         /* extract the nick data from the element */
  457.         /* Hmmm, nice walk ... */
  458.         ary.push(element.childNodes[0].childNodes[2].childNodes[0].nodeValue);
  459.     }    
  460. }
  461.  
  462. function getObjectDetails (obj, rv)
  463. {
  464.     if (!rv)
  465.         rv = new Object();
  466.  
  467.     if (!obj || (typeof obj != "object"))
  468.     {
  469.         dd ("** INVALID OBJECT passed to getObjectDetails (" + obj + "). **");
  470.         dd (getStackTrace());
  471.     }
  472.     
  473.     rv.orig = obj;
  474.     
  475.     switch (obj.TYPE)
  476.     {
  477.         case "IRCChannel":
  478.             rv.channel = obj;
  479.             rv.server = rv.channel.parent;
  480.             rv.network = rv.server.parent;
  481.             break;
  482.  
  483.         case "IRCUser":
  484.             rv.user = obj;
  485.             rv.server = rv.user.parent;
  486.             rv.network = rv.server.parent;
  487.             break;
  488.  
  489.         case "IRCChanUser":
  490.             rv.user = obj;
  491.             rv.channel = rv.user.parent;
  492.             rv.server = rv.channel.parent;
  493.             rv.network = rv.server.parent;
  494.             break;
  495.  
  496.         case "IRCNetwork":
  497.             rv.network = obj;
  498.             if (rv.network.isConnected())
  499.                 rv.server = rv.network.primServ;
  500.             break;
  501.  
  502.         case "IRCClient":
  503.             if (obj.lastNetwork)
  504.             {
  505.                 rv.network = obj.lastNetwork;
  506.                 if (rv.network.isConnected())
  507.                     rv.server = rv.network.primServ;
  508.             }
  509.             break;
  510.             
  511.         default:
  512.             /* no setup for unknown object */
  513.             break;
  514.     }
  515.  
  516.     return rv;
  517.     
  518. }
  519.  
  520. function setClientOutput(doc) 
  521. {
  522.     client.output = frames[0].document.getElementById("output");
  523. }
  524.  
  525. var testURLs =
  526.     ["irc:", "irc://", "irc:///", "irc:///help", "irc:///help,needkey",
  527.     "irc://irc.foo.org", "irc://foo:6666",
  528.     "irc://foo", "irc://irc.foo.org/", "irc://foo:6666/", "irc://foo/",
  529.     "irc://irc.foo.org/,needpass", "irc://foo/,isserver",
  530.     "irc://moznet/,isserver", "irc://moznet/",
  531.     "irc://foo/chatzilla", "irc://irc.foo.org/?msg=hello%20there",
  532.     "irc://irc.foo.org/?msg=hello%20there&ignorethis",
  533.     "irc://irc.foo.org/%23mozilla,needkey?msg=hello%20there&ignorethis",
  534.     "invalids",
  535.     "irc://irc.foo.org/,isnick"];
  536.  
  537. function doURLTest()
  538. {
  539.     for (var u in testURLs)
  540.     {
  541.         dd ("testing url \"" + testURLs[u] + "\"");
  542.         var o = parseIRCURL(testURLs[u]);
  543.         if (!o)
  544.             dd ("PARSE FAILED!");
  545.         else
  546.             dd (dumpObjectTree(o));
  547.         dd ("---");
  548.     }
  549. }
  550.  
  551. function parseIRCURL (url)
  552. {
  553.     var specifiedHost = "";
  554.     
  555.     var rv = new Object();
  556.     rv.spec = url;
  557.     rv.host = client.DEFAULT_NETWORK;
  558.     rv.target = "";
  559.     rv.port = 6667;
  560.     rv.msg = "";
  561.     rv.needpass = false;
  562.     rv.needkey = false;
  563.     rv.isnick = false;
  564.     rv.isserver = false;
  565.     
  566.     if (url.search(/^(irc:|irc:\/\/)$/i) != -1)
  567.         return rv;
  568.  
  569.     /* split url into <host>/<everything-else> pieces */
  570.     var ary = url.match (/^irc:\/\/([^\/\s]+)?(\/.*)?\s*$/i);
  571.     if (!ary)
  572.     {
  573.         dd ("parseIRCURL: initial split failed");
  574.         return null;
  575.     }
  576.     var host = ary[1];
  577.     var rest = ary[2];
  578.  
  579.     /* split <host> into server (or network) / port */
  580.     ary = host.match (/^([^\s\:]+)?(\:\d+)?$/);
  581.     if (!ary)
  582.     {
  583.         dd ("parseIRCURL: host/port split failed");
  584.         return null;
  585.     }
  586.     
  587.     if (ary[2])
  588.     {
  589.         if (!ary[1])
  590.         {
  591.             dd ("parseIRCURL: port with no host");
  592.             return null;
  593.         }
  594.         specifiedHost = rv.host = ary[1].toLowerCase();
  595.         rv.isserver = true;
  596.         rv.port = parseInt(ary[2].substr(1));
  597.     }
  598.     else if (ary[1])
  599.     {
  600.         if (client.networks[ary[1]])
  601.             specifiedHost = rv.host = ary[1].toLowerCase();
  602.         else
  603.         {
  604.             specifiedHost = rv.host = ary[1].toLowerCase();
  605.             rv.isserver = true;
  606.         }
  607.     }
  608.  
  609.     if (rest)
  610.     {
  611.         ary = rest.match (/^\/([^\,\?\s]*)?(,[^\?]*)?(\?.*)?$/);
  612.         if (!ary)
  613.         {
  614.             dd ("parseIRCURL: rest split failed ``" + rest + "''");
  615.             return null;
  616.         }
  617.         
  618.         rv.target = (ary[1]) ? 
  619.             unescape(ary[1]).toLowerCase().replace("\n", "\\n"): "";
  620.         var params = (ary[2]) ? ary[2].toLowerCase() : "";
  621.         var query = (ary[3]) ? ary[3] : "";
  622.  
  623.         if (params)
  624.         {
  625.             rv.isnick =
  626.                 (params.search (/,\s*isnick\s*,|,\s*isnick\s*$/) != -1);
  627.             if (rv.isnick && !rv.target)
  628.             {
  629.                 dd ("parseIRCURL: isnick w/o target");
  630.                     /* isnick w/o a target is bogus */
  631.                 return null;
  632.             }
  633.         
  634.             rv.isserver =
  635.                 (params.search (/,\s*isserver\s*,|,\s*isserver\s*$/) != -1);
  636.             if (rv.isserver && !specifiedHost)
  637.             {
  638.                 dd ("parseIRCURL: isserver w/o host");
  639.                     /* isserver w/o a host is bogus */
  640.                 return null;
  641.             }
  642.                 
  643.             rv.needpass =
  644.                 (params.search (/,\s*needpass\s*,|,\s*needpass\s*$/) != -1);
  645.  
  646.             rv.needkey =
  647.                 (params.search (/,\s*needkey\s*,|,\s*needkey\s*$/) != -1);
  648.  
  649.         }
  650.  
  651.         if (query)
  652.         {
  653.             ary = query.match
  654.                 (/^\?msg=([^\&]*)$|^\?msg=([^\&]*)\&|\&msg=([^\&]*)\&|\&msg=([^\&]*)$/);
  655.             if (ary)
  656.                 for (var i = 1; i < ary.length; i++)
  657.                     if (ary[i])
  658.                     {
  659.                         rv.msg = unescape(ary[i]).replace ("\n", "\\n");
  660.                         break;
  661.                     }
  662.             
  663.         }
  664.     }
  665.  
  666.     return rv;
  667.     
  668. }
  669.  
  670. function gotoIRCURL (url)
  671. {
  672.     if (typeof url == "string")
  673.         url = parseIRCURL(url);
  674.     
  675.     if (!url)
  676.     {
  677.         window.alert ("Invalid IRC URL ``" + url + "''");
  678.         return;
  679.     }
  680.  
  681.     var net;
  682.     var pass = "";
  683.     
  684.     if (url.needpass)
  685.         pass = window.prompt ("Enter a password for the url " + url.spec);
  686.     
  687.     if (url.isserver)
  688.     {
  689.         var alreadyThere = false;
  690.         for (var n in client.networks)
  691.         {
  692.             if ((client.networks[n].isConnected()) &&
  693.                 (client.networks[n].primServ.connection.host == url.host) &&
  694.                 (client.networks[n].primServ.connection.port == url.port))
  695.             {
  696.                 /* already connected to this server/port */
  697.                 net = client.networks[n];
  698.                 alreadyThere = true;
  699.                 break;
  700.             }
  701.         }
  702.  
  703.         if (!alreadyThere)
  704.         {
  705.             dd ("gotoIRCURL: not already connected to " +
  706.                 "server " + url.host + " trying to connect...");
  707.             client.onInputServer ({inputData: url.host + " " + url.port +
  708.                                                   " " + pass});
  709.             net = client.networks[url.host];
  710.             if (!net.pendingURLs)
  711.                 net.pendingURLs = new Array();
  712.             net.pendingURLs.push (url);            
  713.             return;
  714.         }
  715.     }
  716.     else
  717.     /* parsed as a network name */
  718.     {
  719.         net = client.networks[url.host];
  720.         if (!net.isConnected())
  721.         {
  722.             dd ("gotoIRCURL: not already connected to " +
  723.                 "network " + url.host + " trying to connect...");
  724.             client.onInputAttach ({inputData: url.host + " " + pass});
  725.             if (!net.pendingURLs)
  726.                 net.pendingURLs = new Array();
  727.             net.pendingURLs.push (url);            
  728.             return;
  729.         }
  730.     }
  731.     
  732.     /* already connected, do whatever comes next in the url */
  733.     dd ("gotoIRCURL: connected, time to finish parsing ``" +
  734.         url + "''");
  735.     if (url.target)
  736.     {
  737.         var key = "";
  738.         if (url.needkey)
  739.             key = window.prompt ("Enter key for url " + url.spec);
  740.  
  741.         if (url.isnick)
  742.         {
  743.                 /* eek, do nick stuff here */
  744.         }
  745.         else
  746.         {
  747.             var ev = {inputData: url.target + " " + key,
  748.                       network: net, server: net.primServ}
  749.             client.onInputJoin (ev);
  750.             if (url.msg)
  751.             {
  752.                 var msg;
  753.                 if (url.msg.indexOf("\01ACTION") == 0)
  754.                 {
  755.                     msg = filterOutput (url.msg, "ACTION", "ME!");
  756.                     ev.channel.display (msg, "ACTION", "ME!",
  757.                                         client.currentObject);
  758.                 }
  759.                 else
  760.                 {
  761.                     msg = filterOutput (url.msg, "PRIVMSG", "ME!");
  762.                     ev.channel.display (msg, "PRIVMSG", "ME!",
  763.                                         client.currentObject);
  764.                 }
  765.                 ev.channel.say (msg);
  766.                 setCurrentObject (ev.channel);
  767.             }
  768.         }
  769.     }
  770.     else
  771.     {
  772.         if (!net.messages)
  773.             net.displayHere ("Network view for ``" + net.name + "'' opened.",
  774.                              "INFO");
  775.         setCurrentObject (net);
  776.     }
  777.  
  778. }
  779.  
  780. function updateNetwork(obj)
  781. {
  782.     var o = new Object();
  783.     getObjectDetails (client.currentObject, o);
  784.  
  785.     if (obj && obj != o.network)
  786.         return;
  787.     
  788.     var net = o.network ? o.network.name : "(none)";
  789.     var serv = "(none)", nick = "(unknown)", lag = "(unknown)",
  790.         ping = "(never)";
  791.     if (o.server)
  792.     {
  793.         serv  = o.server.connection.host;
  794.         if (o.server.me)
  795.             nick = o.server.me.properNick;
  796.         lag = (o.server.lag != -1) ? o.server.lag : "(unknown)";
  797.         
  798.         if (o.server.lastPing)
  799.         {
  800.             var mins = String(o.server.lastPing.getMinutes());
  801.             if (mins.length == 1)
  802.                 mins = "0" + mins;
  803.             ping = o.server.lastPing.getHours() + ":" + mins;
  804.         }
  805.         else
  806.             ping = "(never)";
  807.     }
  808.  
  809.     client.statusBar["net-name"].setAttribute("value", net);
  810.     client.statusBar["server-name"].setAttribute("value", serv);
  811.     client.statusBar["server-nick"].setAttribute("value", nick);
  812.     client.statusBar["server-lag"].setAttribute("value", lag);
  813.     client.statusBar["last-ping"].setAttribute("value", ping);
  814.  
  815. }
  816.  
  817. function updateChannel (obj)
  818. {
  819.     if (obj && obj != client.currentObject)
  820.         return;
  821.     
  822.     var o = new Object();
  823.     getObjectDetails (client.currentObject, o);
  824.  
  825.     var chan = "(none)", l = "(none)", k = "(none)", mode = "(none)",
  826.         users = 0, topicBy = "(nobody)", topic = "(unknown)";
  827.  
  828.     if (o.channel)
  829.     {
  830.         chan = o.channel.name;
  831.         l = (o.channel.mode.limit != -1) ? o.channel.mode.limit : "(none)";
  832.         k = o.channel.mode.key ? o.channel.mode.key : "(none)";
  833.         mode = o.channel.mode.getModeStr();
  834.         if (!mode)
  835.             mode = "(none)";
  836.         users = o.channel.getUsersLength();
  837.         topic = o.channel.topic ? o.channel.topic : "(none)";
  838.         topicBy = o.channel.topicBy ? o.channel.topicBy : "(nobody)";
  839.     }
  840.     
  841.     client.statusBar["channel-name"].setAttribute("value", chan);
  842.     client.statusBar["channel-limit"].setAttribute("value", l);
  843.     client.statusBar["channel-key"].setAttribute("value", k);
  844.     client.statusBar["channel-mode"].setAttribute("value", mode);
  845.     client.statusBar["channel-users"].setAttribute("value", users);
  846.     client.statusBar["channel-topic"].setAttribute("value", topic);
  847.     client.statusBar["channel-topicby"].setAttribute("value", topicBy);
  848.  
  849. }
  850.  
  851. function updateTitle (obj)
  852. {
  853.     if ((obj && obj != client.currentObject) || !client.currentObject)
  854.         return;
  855.  
  856.     var tstring = "";
  857.     var o = new Object();
  858.     
  859.     getObjectDetails (client.currentObject, o);
  860.  
  861.     var net = o.network ? o.network.name : "";
  862.  
  863.     switch (client.currentObject.TYPE)
  864.     {
  865.         case "IRCServer":
  866.         case "IRCNetwork":
  867.             var serv = "", nick = "";
  868.             if (o.server)
  869.             {
  870.                 serv  = o.server.connection.host;
  871.                 if (o.server.me)
  872.                     nick = o.server.me.properNick;
  873.             }
  874.             
  875.             if (nick) /* user might be disconnected, nick would be undefined */
  876.                 tstring += "user '" + nick + "' ";
  877.             
  878.             if (net)
  879.                 if (serv)
  880.                     tstring += "attached to '" + net + "' via " + serv;
  881.                 else
  882.                     if (o.network.connecting)
  883.                         tstring += "attaching to '" + net + "'";
  884.                     else
  885.                         tstring += "no longer attached to '" + net + "'";
  886.             
  887.             if (tstring)
  888.                 tstring = "ChatZilla: " + tstring;
  889.             else
  890.                 tstring = "ChatZilla!!";
  891.             break;
  892.             
  893.         case "IRCChannel":
  894.             var chan = "(none)", mode = "", topic = "";
  895.  
  896.             chan = o.channel.name;
  897.             mode = o.channel.mode.getModeStr();
  898.             if (client.uiState["toolbar"])
  899.                 topic = o.channel.topic ? " " + o.channel.topic :
  900.                     " --no topic--";
  901.  
  902.             if (!mode)
  903.                 mode = "no mode";
  904.             tstring = "ChatZilla: " + chan + " (" + mode + ") " + topic;
  905.             break;
  906.  
  907.         case "IRCUser":
  908.             tstring = "ChatZilla: Conversation with " +
  909.                 client.currentObject.properNick;
  910.             break;
  911.  
  912.         default:
  913.             tstring = "ChatZilla!";
  914.             break;
  915.     }
  916.  
  917.     if (!client.uiState["toolbar"])
  918.     {
  919.         var actl = new Array();
  920.         for (var i in client.activityList)
  921.             actl.push ((client.activityList[i] == "!") ?
  922.                        (Number(i) + 1) + "!" : (Number(i) + 1));
  923.         if (actl.length > 0)
  924.             tstring += " --  Activity [" + actl.join (", ") + "]";
  925.     }
  926.  
  927.     document.title = tstring;
  928.  
  929. }
  930.  
  931. function multilineInputMode (state)
  932. {
  933.     var multiInput = document.getElementById("multiline-input");
  934.     var singleInput = document.getElementById("input");
  935.     var splitter = document.getElementById("input-splitter");
  936.     var iw = document.getElementById("input-widgets");
  937.     var h;
  938.     
  939.     if (state)  /* turn on multiline input mode */
  940.     {
  941.         
  942.         h = iw.getAttribute ("lastHeight");
  943.         if (h)
  944.             iw.setAttribute ("height", h); /* restore the slider position */
  945.  
  946.         singleInput.setAttribute ("collapsed", "true");
  947.         splitter.setAttribute ("collapsed", "false");
  948.         multiInput.setAttribute ("collapsed", "false");
  949.         multiInput.focus();
  950.     }
  951.     else  /* turn off multiline input mode */
  952.     {
  953.         h = iw.getAttribute ("height");
  954.         iw.setAttribute ("lastHeight", h); /* save the slider position */
  955.         iw.removeAttribute ("height");     /* let the slider drop */
  956.         
  957.         splitter.setAttribute ("collapsed", "true");
  958.         multiInput.setAttribute ("collapsed", "true");
  959.         singleInput.setAttribute ("collapsed", "false");
  960.         singleInput.focus();
  961.     }
  962. }
  963.  
  964. function newInlineText (data, className, tagName)
  965. {
  966.     if (typeof tagName == "undefined")
  967.         tagName = "html:span";
  968.     
  969.     var a = document.createElementNS ("http://www.w3.org/1999/xhtml",
  970.                                       tagName);
  971.     a.setAttribute ("class", className);
  972.  
  973.     switch (typeof data)
  974.     {
  975.         case "string":
  976.             a.appendChild (document.createTextNode (data));
  977.             break;
  978.             
  979.         case "object":
  980.             for (var p in data)
  981.                 if (p != "data")
  982.                     a.setAttribute (p, data[p]);
  983.                 else
  984.                     a.appendChild (document.createTextNode (data[p]));
  985.             break;
  986.  
  987.         case "undefined":
  988.             break;
  989.  
  990.         default:
  991.             dd ("** INVALID TYPE ('" + typeof data + "') passed to " +
  992.                 "newInlineText.");
  993.             break;
  994.             
  995.     }
  996.  
  997.     return a;
  998.     
  999. }
  1000.  
  1001. function stringToMsg (message)
  1002. {
  1003.     var ary = message.split ("\n");
  1004.     var span = document.createElementNS ("http://www.w3.org/1999/xhtml",
  1005.                                          "html:span")
  1006.     for (var l in ary)
  1007.     {
  1008.         client.munger.munge(ary[l], span,
  1009.                             getObjectDetails(this));
  1010.         span.appendChild
  1011.             (document.createElementNS ("http://www.w3.org/1999/xhtml",
  1012.                                        "html:br"));
  1013.     }
  1014.  
  1015.     return span;
  1016. }
  1017.  
  1018. function setCurrentObject (obj)
  1019. {
  1020.     if (!obj.messages)
  1021.     {
  1022.         dd ("** INVALID OBJECT passed to setCurrentObject **");
  1023.         return;
  1024.     }
  1025.  
  1026.     if (client.currentObject == obj)
  1027.         return;
  1028.         
  1029.     var tb, userList;
  1030.  
  1031.     if (client.currentObject)
  1032.     {
  1033.         tb = getTBForObject(client.currentObject);
  1034.     }
  1035.     if (tb)
  1036.     {
  1037.         tb.setAttribute ("selected", "false");
  1038.         tb.setAttribute ("state", "normal");
  1039.     }
  1040.     
  1041.     if (client.output.firstChild)
  1042.         client.output.removeChild (client.output.firstChild);
  1043.     client.output.appendChild (obj.messages);
  1044.  
  1045.     /* Unselect currently selected users. */
  1046.     userList = document.getElementById("user-list");
  1047.     if (userList) 
  1048.         /* Remove curently selection items before this tree gets rerooted,
  1049.          * because it seems to remember the selections for eternity if not. */
  1050.         userList.clearItemSelection ();    
  1051.     else
  1052.         dd ("setCurrentObject: could not find element with ID='user-list'");
  1053.  
  1054.     if (obj.TYPE == "IRCChannel")
  1055.         client.rdf.setTreeRoot ("user-list", obj.getGraphResource());
  1056.     else
  1057.         client.rdf.setTreeRoot ("user-list", client.rdf.resNullChan);
  1058.  
  1059.     client.currentObject = obj;
  1060.     tb = getTBForObject(obj);
  1061.     if (tb)
  1062.     {
  1063.         tb.setAttribute ("selected", "true");
  1064.         tb.setAttribute ("state", "current");
  1065.     }
  1066.     
  1067.     var vk = Number(tb.getAttribute("viewKey"));
  1068.     delete client.activityList[vk];
  1069.  
  1070.     updateNetwork();
  1071.     updateChannel();
  1072.     updateTitle ();
  1073.  
  1074.     if (client.PRINT_DIRECTION == 1)
  1075.         scrollDown();
  1076.     
  1077. }
  1078.  
  1079. function scrollDown ()
  1080. {
  1081.     window.frames[0].scrollTo(0, window.frames[0].document.height);
  1082. }
  1083.  
  1084. function addHistory (source, obj)
  1085. {
  1086.     var tbody;
  1087.     
  1088.     if (!source.messages)
  1089.     {
  1090.         source.messages =
  1091.             document.createElementNS ("http://www.w3.org/1999/xhtml",
  1092.                                       "html:table");
  1093.  
  1094.         source.messages.setAttribute ("class", "msg-table");
  1095.         source.messages.setAttribute ("view-type", source.TYPE);
  1096.         tbody = document.createElementNS ("http://www.w3.org/1999/xhtml",
  1097.                                           "html:tbody");
  1098.         source.messages.appendChild (tbody);
  1099.     }
  1100.     else
  1101.         tbody = source.messages.firstChild;
  1102.  
  1103.     var needScroll = false;
  1104.     var w = window.frames[0];
  1105.     
  1106.     if (client.PRINT_DIRECTION == 1)
  1107.     {
  1108.         if ((w.document.height - (w.innerHeight + w.pageYOffset)) <
  1109.             (w.innerHeight / 3))
  1110.             needScroll = true;
  1111.         tbody.appendChild (obj);
  1112.     }
  1113.     else
  1114.         tbody.insertBefore (obj, source.messages.firstChild);
  1115.     
  1116.     if (source.MAX_MESSAGES)
  1117.     {
  1118.         if (typeof source.messageCount != "number")
  1119.             source.messageCount = 1;
  1120.         else
  1121.             source.messageCount++;
  1122.  
  1123.         if (source.messageCount > source.MAX_MESSAGES)
  1124.             if (client.PRINT_DIRECTION == 1)
  1125.                 tbody.removeChild (tbody.firstChild);
  1126.             else
  1127.                 tbody.removeChild (tbody.lastChild);
  1128.     }
  1129.  
  1130.     if (needScroll && client.currentObject == source)
  1131.         w.scrollTo (w.pageXOffset, w.document.height);
  1132.     
  1133. }
  1134.  
  1135. function notifyActivity (source)
  1136. {
  1137.     if (typeof source != "object")
  1138.         source = client.viewsArray[source].source;
  1139.     
  1140.     var tb = getTBForObject (source, true);
  1141.     var vk = Number(tb.getAttribute("viewKey"));
  1142.     
  1143.     if (client.currentObject != source)
  1144.     {
  1145.         if (tb.getAttribute ("state") == "normal")
  1146.         {       
  1147.             tb.setAttribute ("state", "activity");
  1148.             if (!client.activityList[vk])
  1149.             {
  1150.                 client.activityList[vk] = "+";
  1151.                 updateTitle();
  1152.             }
  1153.         }
  1154.         else if (tb.getAttribute("state") == "activity")
  1155.             /* if act light is already lit, blink it real quick */
  1156.         {
  1157.             tb.setAttribute ("state", "normal");
  1158.             setTimeout ("notifyActivity(" + vk + ");", 200);
  1159.         }
  1160.         
  1161.     }
  1162.     
  1163. }
  1164.  
  1165. function notifyAttention (source)
  1166. {
  1167.     if (typeof source != "object")
  1168.         source = client.viewsArray[source].source;
  1169.     
  1170.     if (client.currentObject != source)
  1171.     {
  1172.         var tb = getTBForObject (source, true);
  1173.         var vk = Number(tb.getAttribute("viewKey"));
  1174.  
  1175.         tb.setAttribute ("state", "attention");
  1176.         client.activityList[vk] = "!";
  1177.         updateTitle();
  1178.     }
  1179.  
  1180.     if (client.FLASH_WINDOW)
  1181.         window.GetAttention();
  1182.     
  1183. }
  1184.  
  1185. /* gets the toolbutton associated with an object
  1186.  * if |create| is present, and true, create if not found */
  1187. function getTBForObject (source, create)
  1188. {
  1189.     var name;
  1190.  
  1191.     if (!source)
  1192.     {
  1193.         dd ("** UNDEFINED  passed to getTBForObject **");
  1194.         dd (getStackTrace());
  1195.         return null;
  1196.     }
  1197.     
  1198.     create = (typeof create != "undefined") ? Boolean(create) : false;
  1199.  
  1200.     switch (source.TYPE)
  1201.     {
  1202.         case "IRCChanUser":
  1203.         case "IRCUser":
  1204.             name = source.nick;
  1205.             break;
  1206.             
  1207.         case "IRCNetwork":
  1208.         case "IRCChannel":
  1209.         case "IRCClient":
  1210.             name = source.name;
  1211.             break;
  1212.  
  1213.         default:
  1214.             dd ("** INVALID OBJECT passed to getTBForObject **");
  1215.             return null;
  1216.     }
  1217.  
  1218.     var tb, id = "tb[" + name + "]";
  1219.     var matches = 1;
  1220.  
  1221.     for (var i in client.viewsArray)
  1222.     {
  1223.         if (client.viewsArray[i].source == source)
  1224.         {
  1225.             tb = client.viewsArray[i].tb;
  1226.             break;
  1227.         }
  1228.         else
  1229.             if (client.viewsArray[i].tb.getAttribute("id") == id)
  1230.                 id = "tb[" + name + "<" + (++matches) + ">]";
  1231.     }
  1232.  
  1233.     if (!tb && create) /* not found, create one */
  1234.     {
  1235.         var views = document.getElementById ("views-tbar-inner");
  1236.         tb = document.createElement ("tab");
  1237.         tb.setAttribute ("onclick", "onTBIClick('" + id + "');");
  1238.         tb.setAttribute ("crop", "right");
  1239.         
  1240.         //tb.addEventListener("command", onTBIClickTempHandler, false);
  1241.         
  1242.         tb.setAttribute ("class", "tab-bottom view-button");
  1243.         tb.setAttribute ("id", id);
  1244.         tb.setAttribute ("state", "normal");
  1245.  
  1246.         client.viewsArray.push ({source: source, tb: tb});
  1247.         tb.setAttribute ("viewKey", client.viewsArray.length - 1);
  1248.         if (matches > 1)
  1249.             tb.setAttribute ("label", name + "<" + matches + ">");
  1250.         else
  1251.             tb.setAttribute ("label", name);
  1252.  
  1253.         views.appendChild (tb);
  1254.     }
  1255.  
  1256.     return tb;
  1257.     
  1258. }
  1259.  
  1260. /*
  1261.  * This is used since setAttribute is funked up right now.
  1262.  */
  1263. function onTBIClickTempHandler (e)
  1264.   
  1265.     dd ("onTBIClickTempHandler called");
  1266.     
  1267.     var id = "tb[" + e.target.getAttribute("value") + "]";
  1268.  
  1269.     var tb = document.getElementById (id);
  1270.     var view = client.viewsArray[tb.getAttribute("viewKey")];
  1271.    
  1272.     setCurrentObject (view.source);
  1273.  
  1274. }
  1275.  
  1276. function deleteToolbutton (tb)
  1277. {
  1278.     var i, key = Number(tb.getAttribute("viewKey"));
  1279.     
  1280.     if (!isNaN(key))
  1281.     {
  1282.         if (!client.viewsArray[key].source.isPermanent)
  1283.         {
  1284.             /* re-index higher toolbuttons */
  1285.             for (i = key + 1; i < client.viewsArray.length; i++)
  1286.             {
  1287.                 client.viewsArray[i].tb.setAttribute ("viewKey", i - 1);
  1288.             }
  1289.             arrayRemoveAt(client.viewsArray, key);
  1290.             var tbinner = document.getElementById("views-tbar-inner");
  1291.             tbinner.removeChild(tb);
  1292.         }
  1293.         else
  1294.         {
  1295.             window.alert ("Current view cannot be deleted.");
  1296.             return -1;
  1297.         }
  1298.             
  1299.     }
  1300.     else
  1301.         dd  ("*** INVALID OBJECT passed to deleteToolButton (" + tb + ") " +
  1302.              "no viewKey attribute. (" + key + ")");
  1303.  
  1304.     return key;
  1305.  
  1306. }
  1307.  
  1308. function filterOutput (msg, msgtype)
  1309. {
  1310.  
  1311.     for (var f in client.outputFilters)
  1312.         if (client.outputFilters[f].enabled)
  1313.             msg = client.outputFilters[f].func(msg, msgtype);
  1314.  
  1315.     return msg;
  1316.     
  1317. }
  1318.  
  1319. client.load =
  1320. function cli_load(url, obj)
  1321. {
  1322.     if (!client._loader)
  1323.     {
  1324.         const LOADER_CTRID = "@mozilla.org/moz/jssubscript-loader;1";
  1325.         const mozIJSSubScriptLoader = 
  1326.             Components.interfaces.mozIJSSubScriptLoader;
  1327.  
  1328.         var cls;
  1329.         if ((cls = Components.classes[LOADER_CTRID]))
  1330.             client._loader = cls.createInstance (mozIJSSubScriptLoader);
  1331.     }
  1332.     
  1333.     try {
  1334.         client._loader.loadSubScript (url, obj);
  1335.     }
  1336.     catch (ex)
  1337.     {
  1338.         var msg = "Error loading subscript: " + ex;
  1339.         if (ex.fileName)
  1340.             msg += " file:" + ex.fileName;
  1341.         if (ex.lineNumber)
  1342.             msg += " line:" + ex.lineNumber;
  1343.  
  1344.         client.currentObject.display (msg, "ERROR");        
  1345.     }
  1346. }
  1347.  
  1348.     
  1349. client.sayToCurrentTarget =
  1350. function cli_say(msg)
  1351. {
  1352.  
  1353.     switch (client.currentObject.TYPE)
  1354.     {
  1355.         case "IRCChannel":
  1356.         case "IRCUser":
  1357.         case "IRCChanUser":
  1358.             msg = filterOutput (msg, "PRIVMSG");
  1359.             client.currentObject.display (msg, "PRIVMSG", "ME!",
  1360.                                           client.currentObject);
  1361.             client.currentObject.say (msg);
  1362.             break;
  1363.  
  1364.         case "IRCClient":
  1365.             client.onInputEval ({inputData: msg});
  1366.             break;
  1367.  
  1368.         default:
  1369.             client.display ("No default action for objects of type ``" +
  1370.                             client.currentObject.TYPE + "''", "ERROR");
  1371.             break;
  1372.     }
  1373.  
  1374. }
  1375.  
  1376. CIRCNetwork.prototype.display =
  1377. function n_display (message, msgtype, sourceObj, destObj)
  1378. {
  1379.     var o = getObjectDetails(client.currentObject);
  1380.  
  1381.     if (client.SLOPPY_NETWORKS && client.currentObject != client &&
  1382.         client.currentObject != this && o.network == this &&
  1383.         o.server.connection.isConnected)
  1384.         client.currentObject.display (message, msgtype, sourceObj, destObj);
  1385.     else
  1386.         this.displayHere (message, msgtype, sourceObj, destObj);
  1387. }
  1388.  
  1389. CIRCUser.prototype.display =
  1390. function u_display(message, msgtype, sourceObj, destObj)
  1391. {
  1392.     if (this.messages)
  1393.         this.displayHere (message, msgtype, sourceObj, destObj);
  1394.     else
  1395.     {
  1396.         var o = getObjectDetails(client.currentObject);
  1397.         if (o.server.connection.isConnected &&
  1398.             o.network == this.parent.parent &&
  1399.             client.currentObject.TYPE != "IRCUser")
  1400.             client.currentObject.display (message, msgtype, sourceObj, destObj);
  1401.         else
  1402.             this.parent.parent.displayHere (message, msgtype, sourceObj,
  1403.                                             destObj);
  1404.     }
  1405. }
  1406.  
  1407. client.display =
  1408. CIRCNetwork.prototype.displayHere =
  1409. CIRCChannel.prototype.display =
  1410. CIRCUser.prototype.displayHere =
  1411. function display(message, msgtype, sourceObj, destObj)
  1412. {            
  1413.     function setAttribs (obj, c, attrs)
  1414.     {
  1415.         for (var a in attrs)
  1416.             obj.setAttribute (a, attrs[a]);
  1417.         obj.setAttribute ("class", c);
  1418.         obj.setAttribute ("msg-type", msgtype);
  1419.         obj.setAttribute ("msg-user", fromAttr);
  1420.         obj.setAttribute ("msg-dest", toAttr);
  1421.         obj.setAttribute ("dest-type", toType);
  1422.         obj.setAttribute ("view-type", viewType);
  1423.     }
  1424.  
  1425.     var blockLevel = false; /* true if this row should be rendered at block
  1426.                              * level, (like, if it has a really long nickname
  1427.                              * that might disturb the rest of the layout)     */
  1428.     var o = getObjectDetails (this);          /* get the skinny on |this|     */
  1429.     var me = (o.server) ? o.server.me : null; /* get the object representing
  1430.                                                * the user
  1431.                                                */
  1432.     if (sourceObj == "ME!") sourceObj = me;   /* if the caller to passes "ME!"*/
  1433.     if (destObj == "ME!") destObj = me;       /* substitute the actual object */
  1434.  
  1435.     var fromType = (sourceObj && sourceObj.TYPE) ? sourceObj.TYPE : "unk";
  1436.     var fromAttr;
  1437.     
  1438.     if      (sourceObj == me)                    fromAttr = "ME!";
  1439.     else if (fromType.search(/IRC.*User/) != -1) fromAttr = sourceObj.nick;
  1440.     else if (typeof sourceObj == "object")       fromAttr = sourceObj.name;
  1441.         
  1442.     var toType = (destObj) ? destObj.TYPE : "unk";
  1443.     var toAttr;
  1444.     
  1445.     if (destObj == me)
  1446.         toAttr = "ME!";
  1447.     else if (toType == "IRCUser")
  1448.         toAttr = destObj.nick;
  1449.     else if (typeof destObj == "object")
  1450.         toAttr = destObj.name;
  1451.  
  1452.     /* isImportant means to style the messages as important, and flash the
  1453.      * window, getAttention means just flash the window. */
  1454.     var isImportant = false, getAttention = false;
  1455.     var viewType = this.TYPE;
  1456.     var code;
  1457.     var msgRow = document.createElementNS("http://www.w3.org/1999/xhtml",
  1458.                                           "html:tr");
  1459.     setAttribs(msgRow, "msg");
  1460.  
  1461.     //dd ("fromType is " + fromType + ", fromAttr is " + fromAttr);
  1462.     
  1463.     if (fromType.search(/IRC.*User/) != -1 &&
  1464.         msgtype.search(/PRIVMSG|ACTION|NOTICE/) != -1)
  1465.     {
  1466.         /* do nick things here */
  1467.         var nick;
  1468.         
  1469.         if (sourceObj != me)
  1470.         {
  1471.             if (toType == "IRCUser") /* msg from user to me */
  1472.             {
  1473.                 getAttention = true;
  1474.                 nick = sourceObj.properNick;
  1475.             }
  1476.             else /* msg from user to channel */
  1477.             {
  1478.                 if (typeof (message == "string") && me)
  1479.                     isImportant = msgIsImportant (message, nick, me.nick);
  1480.                 nick = sourceObj.properNick;
  1481.             }
  1482.         }
  1483.         else if (toType == "IRCUser") /* msg from me to user */
  1484.         {
  1485.             nick = (this.TYPE == "IRCUser") ? sourceObj.properNick :
  1486.                 destObj.properNick;
  1487.         }
  1488.         else /* msg from me to channel */
  1489.         {
  1490.             nick = sourceObj.properNick;
  1491.         }
  1492.         
  1493.         if (typeof this.mark == "undefined") 
  1494.             this.mark = "odd";
  1495.         
  1496.         if (this.lastNickDisplayed != nick)
  1497.         {
  1498.             this.lastNickDisplayed = nick;
  1499.             this.mark = (this.mark == "even") ? "odd" : "even";
  1500.         }
  1501.  
  1502.         var msgSource = document.createElementNS("http://www.w3.org/1999/xhtml",
  1503.                                                  "html:td");
  1504.         setAttribs (msgSource, "msg-user", {important: isImportant});
  1505.         if (nick.length > client.MAX_NICK_DISPLAY)
  1506.             blockLevel = true;
  1507.         msgSource.appendChild (newInlineText (nick));
  1508.         msgRow.appendChild (msgSource);
  1509.  
  1510.     }
  1511.     else if ((client.debugMode && (code = "[" + msgtype + "]")) ||
  1512.              (code = client.responseCodeMap[msgtype]) != "none")
  1513.     {
  1514.         /* Display the message code */
  1515.         var msgType = document.createElementNS("http://www.w3.org/1999/xhtml",
  1516.                                                "html:td");
  1517.         setAttribs (msgType, "msg-type");
  1518.  
  1519.         if (!code)
  1520.             if (client.HIDE_CODES)
  1521.                 code = client.DEFAULT_RESPONSE_CODE;
  1522.             else
  1523.                 code = "[" + msgtype + "]";
  1524.         msgType.appendChild (newInlineText (code));
  1525.         msgRow.appendChild (msgType);
  1526.     }
  1527.              
  1528.     if (message)
  1529.     {
  1530.         var msgData = document.createElementNS("http://www.w3.org/1999/xhtml",
  1531.                                                "html:td");
  1532.         setAttribs (msgData, "msg-data", {important: isImportant});
  1533.         if (this.mark)
  1534.             msgData.setAttribute ("mark", this.mark);
  1535.         
  1536.         if (typeof message == "string")
  1537.         {
  1538.             msgData.appendChild (stringToMsg (message));
  1539.         }
  1540.         else
  1541.             msgData.appendChild (message);
  1542.  
  1543.         msgRow.appendChild (msgData);
  1544.     }
  1545.  
  1546.     if (isImportant)
  1547.         msgRow.setAttribute ("important", "true");
  1548.  
  1549.     if (blockLevel)
  1550.     {
  1551.         /* putting a div here crashes mozilla, so fake it with nested tables
  1552.          * for now */
  1553.         var tr = document.createElementNS ("http://www.w3.org/1999/xhtml",
  1554.                                            "html:tr");
  1555.         tr.setAttribute ("class", "msg-nested-tr");
  1556.         var td = document.createElementNS ("http://www.w3.org/1999/xhtml",
  1557.                                            "html:td");        
  1558.         td.setAttribute ("class", "msg-nested-td");
  1559.         td.setAttribute ("colspan", "2");
  1560.         
  1561.         tr.appendChild(td);
  1562.         var table = document.createElementNS ("http://www.w3.org/1999/xhtml",
  1563.                                               "html:table");
  1564.         table.setAttribute ("class", "msg-nested-table");
  1565.         table.setAttribute ("cellpadding", "0");
  1566.         
  1567.         td.appendChild (table);
  1568.         var tbody =  document.createElementNS ("http://www.w3.org/1999/xhtml",
  1569.                                                "html:tbody");
  1570.         tbody.appendChild (msgRow);
  1571.         table.appendChild (tbody);
  1572.         msgRow = tr;
  1573.     }
  1574.  
  1575.     addHistory (this, msgRow);
  1576.     if (isImportant || getAttention)
  1577.         notifyAttention(this);
  1578.     else
  1579.         notifyActivity (this);
  1580. }
  1581.  
  1582. client.quit =
  1583. function cli_quit (reason)
  1584. {
  1585.     
  1586.     for (var n in client.networks)
  1587.         if (client.networks[n].primServ)
  1588.             client.networks[n].quit (reason);
  1589.     
  1590. }
  1591.  
  1592. /**
  1593.  * Retrieves the selected nicks from the user-list
  1594.  * tree object. This grabs the tree element's
  1595.  * selected items, extracts the appropriate text
  1596.  * for the nick, promotes each nick to a CIRCChanUser
  1597.  * instance and returns an array of these objects.
  1598.  */
  1599. CIRCChannel.prototype.getSelectedUsers =
  1600. function my_getselectedusers () 
  1601. {
  1602.  
  1603.     /* Grab a reference to the tree element with ID = user-list . See chatzilla.xul */
  1604.     var tree = document.getElementById("user-list");
  1605.     var cell; /* reference to each selected cell of the tree object */
  1606.     var rv_ary = new Array; /* return value arrray for CIRCChanUser objects */
  1607.  
  1608.     for (var i = 0; i < tree.selectedItems.length; i++)
  1609.     {
  1610.         /* First, set the reference to the XUL element. */
  1611.         cell = tree.selectedItems[i].firstChild.childNodes[2].firstChild;
  1612.  
  1613.         /* Now, create an instance of CIRCChaneUser by passing the text
  1614.        *  of the cell to the getUser function of this CIRCChannel instance.
  1615.        */
  1616.         rv_ary[i] = this.getUser( cell.getAttribute("value") );
  1617.     }
  1618.  
  1619.     /* 
  1620.      *  USAGE NOTE: If the return value is non-null, the caller
  1621.      *  can assume the array is valid, and NOT 
  1622.      *  need to check the length, and vice versa.
  1623.      */
  1624.  
  1625.     return rv_ary.length > 0 ? rv_ary : null;
  1626.  
  1627. }
  1628.  
  1629. CIRCChannel.prototype.getGraphResource =
  1630. function my_graphres ()
  1631. {
  1632.     if (!this.rdfRes)
  1633.     {
  1634.         this.rdfRes = 
  1635.             client.rdf.GetResource(RES_PFX + "CHANNEL:" +
  1636.                                    this.parent.parent.name +
  1637.                                    ":" + this.name);
  1638.             //dd ("created channel resource " + this.rdfRes.Value);
  1639.  
  1640.     }
  1641.     
  1642.     return this.rdfRes;
  1643. }
  1644.  
  1645. CIRCUser.prototype.getGraphResource =
  1646. function usr_graphres()
  1647. {
  1648.     if (this.TYPE != "IRCChanUser")
  1649.         dd ("** WARNING: cuser.getGraphResource called on wrong object **");
  1650.     
  1651.     var rdf = client.rdf;
  1652.     
  1653.     if (!this.rdfRes)
  1654.     {
  1655.         if (!CIRCUser.nextResID)
  1656.             CIRCUser.nextResID = 0;
  1657.         
  1658.         this.rdfRes = rdf.GetResource (RES_PFX + "CUSER:" + 
  1659.                                        this.parent.parent.parent.name + ":" +
  1660.                                        this.parent.name + ":" +
  1661.                                        CIRCUser.nextResID++);
  1662.         
  1663.             //dd ("created cuser resource " + this.rdfRes.Value);
  1664.         
  1665.         rdf.Assert (this.rdfRes, rdf.resNick, rdf.GetLiteral(this.properNick));
  1666.         if (this.name)
  1667.             rdf.Assert (this.rdfRes, rdf.resUser, rdf.GetLiteral(this.name));
  1668.         else
  1669.             rdf.Assert (this.rdfRes, rdf.resUser, rdf.litUnk);
  1670.         if (this.host)
  1671.             rdf.Assert (this.rdfRes, rdf.resHost, rdf.GetLiteral(this.host));
  1672.         else
  1673.             rdf.Assert (this.rdfRes, rdf.resHost, rdf.litUnk);
  1674.  
  1675.         rdf.Assert (this.rdfRes, rdf.resOp, 
  1676.                     this.isOp ? rdf.litTrue : rdf.litFalse);
  1677.         rdf.Assert (this.rdfRes, rdf.resVoice,
  1678.                     this.isVoice ? rdf.litTrue : rdf.litFalse);
  1679.     }
  1680.  
  1681.     return this.rdfRes;
  1682.  
  1683. }
  1684.  
  1685. CIRCUser.prototype.updateGraphResource =
  1686. function usr_updres()
  1687. {
  1688.     if (this.TYPE != "IRCChanUser")
  1689.         dd ("** WARNING: cuser.updateGraphResource called on wrong object **");
  1690.  
  1691.     if (!this.rdfRes)
  1692.         this.getGraphResource();
  1693.     
  1694.     var rdf = client.rdf;
  1695.     
  1696.     rdf.Change (this.rdfRes, rdf.resNick, rdf.GetLiteral(this.properNick));
  1697.     if (this.name)
  1698.         rdf.Change (this.rdfRes, rdf.resUser, rdf.GetLiteral(this.name));
  1699.     else
  1700.         rdf.Change (this.rdfRes, rdf.resUser, rdf.litUnk);
  1701.     if (this.host)
  1702.         rdf.Change (this.rdfRes, rdf.resHost, rdf.GetLiteral(this.host));
  1703.     else
  1704.         rdf.Change (this.rdfRes, rdf.resHost, rdf.litUnk);
  1705.     
  1706.     rdf.Change (this.rdfRes, rdf.resOp, 
  1707.                 this.isOp ? rdf.litTrue : rdf.litFalse);
  1708.     rdf.Change (this.rdfRes, rdf.resVoice,
  1709.                 this.isVoice ? rdf.litTrue : rdf.litFalse);
  1710. }
  1711.