Import from firefox-3.0b1 tarball
[mozilla-extra.git] / extensions / irc / xul / content / output-window.js
blob7c0cf6cc3dff88b8d246459b79bb5f351b25812a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  *
3  * ***** BEGIN LICENSE BLOCK *****
4  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5  *
6  * The contents of this file are subject to the Mozilla Public License Version
7  * 1.1 (the "License"); you may not use this file except in compliance with
8  * the License. You may obtain a copy of the License at
9  * http://www.mozilla.org/MPL/
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13  * for the specific language governing rights and limitations under the
14  * License.
15  *
16  * The Original Code is ChatZilla.
17  *
18  * The Initial Developer of the Original Code is
19  * Netscape Communications Corporation.
20  * Portions created by the Initial Developer are Copyright (C) 1998
21  * the Initial Developer. All Rights Reserved.
22  *
23  * Contributor(s):
24  *   Robert Ginda, <rginda@netscape.com>, original author
25  *
26  * Alternatively, the contents of this file may be used under the terms of
27  * either the GNU General Public License Version 2 or later (the "GPL"), or
28  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29  * in which case the provisions of the GPL or the LGPL are applicable instead
30  * of those above. If you wish to allow use of your version of this file only
31  * under the terms of either the GPL or the LGPL, and not to allow others to
32  * use your version of this file under the terms of the MPL, indicate your
33  * decision by deleting the provisions above and replace them with the notice
34  * and other provisions required by the GPL or the LGPL. If you do not delete
35  * the provisions above, a recipient may use your version of this file under
36  * the terms of any one of the MPL, the GPL or the LGPL.
37  *
38  * ***** END LICENSE BLOCK ***** */
40 var initialized = false;
42 var view;
43 var client;
44 var mainWindow;
45 var clickHandler;
47 var dd;
48 var getMsg;
49 var getObjectDetails;
51 var header = null;
52 var headers = {
53     IRCClient: {
54         prefix: "cli-",
55         fields: ["container", "netcount", "version-container", "version",
56                  "connectcount"],
57         update: updateClient
58     },
60     IRCNetwork: {
61         prefix: "net-",
62         fields: ["container", "url-anchor", "status", "lag"],
63         update: updateNetwork
64     },
66     IRCChannel: {
67         prefix: "ch-",
68         fields: ["container", "url-anchor", "modestr", "usercount",
69                  "topicnodes", "topicinput"],
70         update: updateChannel
71     },
73     IRCUser: {
74         prefix: "usr-",
75         fields: ["container", "url-anchor", "serverstr", "title",
76                  "descnodes"],
77         update: updateUser
78     },
79     
80     IRCDCCChat: {
81         prefix: "dcc-chat-",
82         fields: ["container", "remotestr", "title"],
83         update: updateDCCChat
84     },
85     
86     IRCDCCFileTransfer: {
87         prefix: "dcc-file-",
88         fields: ["container", "file", "progress", "progressbar"],
89         update: updateDCCFile
90     }
93 var initOutputWindow = stock_initOutputWindow;
95 function stock_initOutputWindow(newClient, newView, newClickHandler)
97     function initHeader()
98     {
99         /* it's better if we wait a half a second before poking at these
100          * dom nodes. */
101         setHeaderState(view.prefs["displayHeader"]);
102         updateHeader();
103         var div = document.getElementById("messages-outer");
104         div.removeAttribute("hidden");
105         window.scrollTo(0, window.document.height);
106     };
108     client = newClient;
109     view = newView;
110     clickHandler = newClickHandler;
111     mainWindow = client.mainWindow;
112     
113     client.messageManager.importBundle(client.defaultBundle, window);
115     getMsg = mainWindow.getMsg;
116     getObjectDetails = mainWindow.getObjectDetails;
117     dd = mainWindow.dd;
119     // Wheee... localize stuff!
120     //var nodes = document.getElementsByAttribute("localize", "*");
121     var nodes = document.getElementsByTagName("*");
122     for (var i = 0; i < nodes.length; i++)
123     {
124         if (nodes[i].hasAttribute("localize"))
125         {
126             var msg = nodes[i].getAttribute("localize");
127             msg = getMsg("msg." + msg);
128             nodes[i].appendChild(document.createTextNode(msg));
129         }
130     }
132     changeCSS(view.prefs["motif.current"]);
133     updateMotifSettings();
135     var output = document.getElementById("output");
136     output.appendChild(adoptNode(view.messages));
138     if (view.TYPE in headers)
139     {
140         header = cacheNodes(headers[view.TYPE].prefix,
141                             headers[view.TYPE].fields);
142         // Turn off accessibility announcements: they're useless as all these
143         // changes are in the "log" as well, normally.
144         // We're setting the attribute here instead of in the HTML to cope with
145         // custom output windows and so we set it only on the Right header
146         // for this view.
147         header["container"].setAttribute("aria-live", "off");
148         header.update = headers[view.TYPE].update;
149     }
151     var splash = document.getElementById("splash");
152     var name;
153     if ("unicodeName" in view)
154         name = view.unicodeName;
155     else
156         name = view.name;
157     splash.appendChild(document.createTextNode(name));
159     setTimeout(initHeader, 500);
161     initialized = true;
164 function onTopicNodesClick(e)
166     if (!clickHandler(e))
167     {
168         if (e.which != 1)
169             return;
171         startTopicEdit();
172     }
174     e.stopPropagation();
177 function onTopicKeypress(e)
179     switch (e.keyCode)
180     {
181         case 13: /* enter */
182             var topic = header["topicinput"].value;
183             topic = mainWindow.replaceColorCodes(topic);
184             view.setTopic(topic);
185             view.dispatch("focus-input");
186             break;
187             
188         case 27: /* esc */
189             view.dispatch("focus-input");
190             break;
191             
192         default:
193             client.mainWindow.onInputKeyPress(e);
194     }
197 function startTopicEdit()
199     var me = view.getUser(view.parent.me.unicodeName);
200     if (!me || (!view.mode.publicTopic && !me.isOp && !me.isHalfOp) ||
201         !header["topicinput"].hasAttribute("hidden"))
202     {
203         return;
204     }
205     
206     header["topicinput"].value = mainWindow.decodeColorCodes(view.topic);
208     header["topicnodes"].setAttribute("hidden", "true")
209     header["topicinput"].removeAttribute("hidden");
210     header["topicinput"].focus();
211     header["topicinput"].selectionStart = 0;
214 function cancelTopicEdit()
216     if (!header["topicnodes"].hasAttribute("hidden"))
217         return;
218     
219     header["topicinput"].setAttribute("hidden", "true")
220     header["topicnodes"].removeAttribute("hidden");
223 function cacheNodes(pfx, ary, nodes)
225     if (!nodes)
226         nodes = new Object();
228     for (var i = 0; i < ary.length; ++i)
229         nodes[ary[i]] = document.getElementById(pfx + ary[i]);
231     return nodes;
234 function changeCSS(url, id)
236     if (!id)
237         id = "main-css";
238     
239     node = document.getElementById(id);
241     if (!node)
242     {
243         node = document.createElement("link");
244         node.setAttribute("id", id);
245         node.setAttribute("rel", "stylesheet");
246         node.setAttribute("type", "text/css");
247         var head = document.getElementsByTagName("head")[0];
248         head.appendChild(node);
249     }
250     else
251     {
252         if (node.getAttribute("href") == url)
253             return;
254     }
256     node.setAttribute("href", url);
257     window.scrollTo(0, window.document.height);
260 function updateMotifSettings(existingTimeout)
262     // Try... catch with a repeat to cope with the style sheet not being loaded
263     const TIMEOUT = 100;
264     try
265     {
266         existingTimeout += TIMEOUT;
267         view.motifSettings = getMotifSettings();
268     }
269     catch(ex) 
270     {
271         if (existingTimeout >= 30000) // Stop after trying for 30 seconds
272             return;
273         if (ex.name == "NS_ERROR_DOM_INVALID_ACCESS_ERR") //not ready, try again
274             setTimeout(updateMotifSettings, TIMEOUT, existingTimeout);
275         else // something else, panic!
276             dd(ex);
277     }
280 function getMotifSettings()
282     var re = new RegExp("czsettings\\.(\\w*)", "i");
283     var rules = document.getElementById("main-css").sheet.cssRules;
284     var rv = new Object();
285     var ary;
286     // Copy any settings, which are available in the motif using the
287     // "CZSETTINGS" selector. We only store the regexp match after checking
288     // the rule type because selectorText is not defined on other rule types.
289     for (var i = 0; i < rules.length; i++)
290     {
291         if ((rules[i].type == CSSRule.STYLE_RULE) &&
292             ((ary = rules[i].selectorText.match(re)) != null))
293         {
294             rv[ary[1]] = true;
295         }
296     }
297     return rv;
300 function adoptNode(node)
302     return client.adoptNode(node, document);
305 function setText(field, text, checkCondition)
307     if (!header[field].firstChild)
308         header[field].appendChild(document.createTextNode(""));
310     if (typeof text != "string")
311     {
312         text = MSG_UNKNOWN;
313         if (checkCondition)
314             setAttribute(field, "condition", "red");
315     }
316     else if (checkCondition)
317     {
318         setAttribute(field, "condition", "green");
319     }
320                 
321     header[field].firstChild.data = text;
324 function setAttribute(field, name, value)
326     if (!value)
327         value = "true";
329     header[field].setAttribute(name, value);
332 function removeAttribute(field, name)
334     header[field].removeAttribute(name);
337 function hasAttribute(field, name)
339     header[field].hasAttribute(name);
342 function setHeaderState(state)
344     if (header)
345     {
346         if (state)
347         {
348             updateHeader();
349             removeAttribute("container", "hidden");
350         }
351         else
352         {
353             setAttribute("container", "hidden");
354         }
355     }
358 function updateHeader()
360     document.title = view.getURL();
361     
362     if (!header || hasAttribute("container", "hidden"))
363         return;
365     for (var id in header)
366     {
367         var value;
369         if (id == "url-anchor")
370         {
371             value = view.getURL();
372             setAttribute("url-anchor", "href", value);
373             setText("url-anchor", value);
374         }
375         else if (id in view)
376         {
377             setText(id, view[id]);
378         }
379     }
381     if (header.update)
382         header.update();
385 function updateClient()
387     var n = 0, c = 0;
388     for (name in client.networks)
389     {
390         ++n;
391         if (client.networks[name].isConnected())
392             ++c;
393     }
395     setAttribute("version-container", "title", client.userAgent);
396     setAttribute("version-container", "condition", mainWindow.__cz_condition);
397     setText("version", mainWindow.__cz_version);
398     setText("netcount", String(n));
399     setText("connectcount", String(c));
402 function updateNetwork()
404     if (view.state == client.mainWindow.NET_CONNECTING)
405     {
406         setText("status", MSG_CONNECTING);
407         setAttribute("status","condition", "yellow");
408         removeAttribute("status", "title");
409         setText("lag", MSG_UNKNOWN);
410     }
411     else if (view.isConnected())
412     {
413         setText("status", MSG_CONNECTED);
414         setAttribute("status","condition", "green");
415         setAttribute("status", "title",
416                      getMsg(MSG_CONNECT_VIA, view.primServ.unicodeName));
417         if (view.primServ.lag != -1)
418             setText("lag", getMsg(MSG_FMT_SECONDS, view.primServ.lag));
419         else
420             setText("lag", MSG_UNKNOWN);
421         
422     }
423     else
424     {
425         setText("status", MSG_DISCONNECTED);
426         setAttribute("status","condition", "red");
427         removeAttribute("status", "title");
428         setText("lag", MSG_UNKNOWN);
429     }
432 function updateChannel()
434     header["topicnodes"].removeChild(header["topicnodes"].firstChild);
436     if (view.active)
437     {
438         var str = view.mode.getModeStr();
439         if (!str)
440             str = MSG_NO_MODE;
441         setText("modestr", str);
442         setAttribute("modestr", "condition", "green");
444         setText("usercount", getMsg(MSG_FMT_USERCOUNT,
445                                     [view.getUsersLength(), view.opCount,
446                                      view.halfopCount, view.voiceCount]));
447         setAttribute("usercount", "condition", "green");
449         if (view.topic)
450         {
451             var data = getObjectDetails(view);
452             data.dontLogURLs = true;
453             var mailto = client.prefs["munger.mailto"];
454             client.munger.getRule(".mailto").enabled = mailto;
455             var nodes = client.munger.munge(view.topic, null, data);
456             client.munger.getRule(".mailto").enabled = false;
457             header["topicnodes"].appendChild(adoptNode(nodes));
458         }
459         else
460         {
461             setText("topicnodes", MSG_NONE);
462         }
463     }
464     else
465     {
466         setText("modestr", MSG_UNKNOWN);
467         setAttribute("modestr", "condition", "red");
468         setText("usercount", MSG_UNKNOWN);
469         setAttribute("usercount", "condition", "red");
470         setText("topicnodes", MSG_UNKNOWN);
471     }
475 function updateUser()
477     var source;
478     if (view.name)
479         source = "<" + view.name + "@" + view.host + ">";
480     else
481         source = MSG_UNKNOWN;
483     if (view.parent.isConnected)
484         setText("serverstr", view.connectionHost, true);
485     else
486         setText("serverstr", null, true);
488     setText("title", getMsg(MSG_TITLE_USER, [view.unicodeName, source]));
490     header["descnodes"].removeChild(header["descnodes"].firstChild);
491     if (typeof view.desc != "undefined")
492     {
493         var data = getObjectDetails(view);
494         data.dontLogURLs = true;
495         var nodes = client.munger.munge(view.desc, null, data);
496         header["descnodes"].appendChild(adoptNode(nodes));
497     }
498     else
499     {
500         setText("descnodes", "");
501     }
504 function updateDCCChat()
506     if (view.state.state == 4)
507         setText("remotestr", view.remoteIP + ":" + view.port, true);
508     else
509         setText("remotestr", null, true);
511     setText("title", getMsg(MSG_TITLE_DCCCHAT, view.user.unicodeName));
514 function updateDCCFile()
516     var pcent = Math.floor(100 * view.position / view.size);
517     
518     setText("file", view.filename);
519     setText("progress", getMsg(MSG_DCCFILE_PROGRESS,
520                                [pcent, mainWindow.getSISize(view.position),
521                                 mainWindow.getSISize(view.size),
522                                 mainWindow.getSISpeed(view.speed)]));
524     setAttribute("progressbar", "width", pcent + "%");