summaryrefslogtreecommitdiffstats
path: root/comm/suite/chatzilla/xul/content/output-window.js
diff options
context:
space:
mode:
Diffstat (limited to 'comm/suite/chatzilla/xul/content/output-window.js')
-rw-r--r--comm/suite/chatzilla/xul/content/output-window.js588
1 files changed, 588 insertions, 0 deletions
diff --git a/comm/suite/chatzilla/xul/content/output-window.js b/comm/suite/chatzilla/xul/content/output-window.js
new file mode 100644
index 0000000000..8f670ec3ec
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/output-window.js
@@ -0,0 +1,588 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var initialized = false;
+
+var view;
+var client;
+var mainWindow;
+var clickHandler;
+
+var dd;
+var getMsg;
+var getObjectDetails;
+
+var header = null;
+var headers = {
+ IRCClient: {
+ prefix: "cli-",
+ fields: ["container", "netcount", "version-container", "version",
+ "connectcount"],
+ update: updateClient
+ },
+
+ IRCNetwork: {
+ prefix: "net-",
+ fields: ["container", "url-anchor", "status", "lag"],
+ update: updateNetwork
+ },
+
+ IRCChannel: {
+ prefix: "ch-",
+ fields: ["container", "url-anchor", "modestr", "usercount",
+ "topicnodes", "topicinput", "topiccancel"],
+ update: updateChannel
+ },
+
+ IRCUser: {
+ prefix: "usr-",
+ fields: ["container", "url-anchor", "serverstr", "title",
+ "descnodes"],
+ update: updateUser
+ },
+
+ IRCDCCChat: {
+ prefix: "dcc-chat-",
+ fields: ["container", "remotestr", "title"],
+ update: updateDCCChat
+ },
+
+ IRCDCCFileTransfer: {
+ prefix: "dcc-file-",
+ fields: ["container", "file", "progress", "progressbar"],
+ update: updateDCCFile
+ }
+};
+
+var initOutputWindow = stock_initOutputWindow;
+
+function stock_initOutputWindow(newClient, newView, newClickHandler)
+{
+ function initHeader()
+ {
+ /* it's better if we wait a half a second before poking at these
+ * dom nodes. */
+ setHeaderState(view.prefs["displayHeader"]);
+ updateHeader();
+ var div = document.getElementById("messages-outer");
+ div.removeAttribute("hidden");
+ window.scrollTo(0, window.document.body.clientHeight);
+ };
+
+ client = newClient;
+ view = newView;
+ clickHandler = newClickHandler;
+ mainWindow = client.mainWindow;
+
+ client.messageManager.importBundle(client.defaultBundle, window);
+
+ getMsg = mainWindow.getMsg;
+ getObjectDetails = mainWindow.getObjectDetails;
+ dd = mainWindow.dd;
+
+ // Wheee... localize stuff!
+ //var nodes = document.getElementsByAttribute("localize", "*");
+ var nodes = document.getElementsByTagName("*");
+ for (var i = 0; i < nodes.length; i++)
+ {
+ if (nodes[i].hasAttribute("localize"))
+ {
+ var msg = nodes[i].getAttribute("localize");
+ msg = getMsg("msg." + msg);
+ if (nodes[i].nodeName.toLowerCase() == "input")
+ nodes[i].value = msg;
+ else
+ nodes[i].appendChild(document.createTextNode(msg));
+ }
+ }
+
+ changeCSS("chrome://chatzilla/content/output-base.css", "cz-css-base");
+ changeCSS(view.prefs["motif.current"]);
+ updateMotifSettings();
+
+ var output = document.getElementById("output");
+ output.appendChild(adoptNode(view.messages));
+
+ if (view.TYPE in headers)
+ {
+ header = cacheNodes(headers[view.TYPE].prefix,
+ headers[view.TYPE].fields);
+ // Turn off accessibility announcements: they're useless as all these
+ // changes are in the "log" as well, normally.
+ // We're setting the attribute here instead of in the HTML to cope with
+ // custom output windows and so we set it only on the Right header
+ // for this view.
+ header["container"].setAttribute("aria-live", "off");
+ header.update = headers[view.TYPE].update;
+ }
+
+ var name;
+ if ("unicodeName" in view)
+ name = view.unicodeName;
+ else
+ name = view.name;
+ updateSplash(name);
+
+ setTimeout(initHeader, 500);
+
+ initialized = true;
+}
+
+function onTopicNodesClick(e)
+{
+ if (!clickHandler(e))
+ {
+ if (e.which != 1)
+ return;
+
+ startTopicEdit();
+ }
+
+ e.stopPropagation();
+}
+
+function onTopicKeypress(e)
+{
+ switch (e.keyCode)
+ {
+ case 13: /* enter */
+ var topic = header["topicinput"].value;
+ topic = mainWindow.replaceColorCodes(topic);
+ view.setTopic(topic);
+ cancelTopicEdit(true);
+ view.dispatch("focus-input");
+ break;
+
+ case 27: /* esc */
+ cancelTopicEdit(true);
+ view.dispatch("focus-input");
+ break;
+
+ default:
+ client.mainWindow.onInputKeyPress(e);
+ }
+}
+
+function startTopicEdit()
+{
+ var me = view.getUser(view.parent.me.unicodeName);
+ if (!me || (!view.mode.publicTopic && !me.isOp && !me.isHalfOp) ||
+ !hasAttribute("topicinput", "hidden"))
+ {
+ return;
+ }
+
+ header["topicinput"].value = mainWindow.decodeColorCodes(view.topic);
+
+ header["topicnodes"].setAttribute("hidden", "true")
+ header["topicinput"].removeAttribute("hidden");
+ header["topiccancel"].removeAttribute("hidden");
+ header["topicinput"].focus();
+ header["topicinput"].selectionStart = 0;
+}
+
+function cancelTopicEdit(force)
+{
+ var originalTopic = mainWindow.decodeColorCodes(view.topic);
+ if (!hasAttribute("topicnodes", "hidden") ||
+ (!force && (header["topicinput"].value != originalTopic)))
+ {
+ return;
+ }
+
+ header["topicinput"].setAttribute("hidden", "true");
+ header["topiccancel"].setAttribute("hidden", "true");
+ header["topicnodes"].removeAttribute("hidden");
+}
+
+function cacheNodes(pfx, ary, nodes)
+{
+ if (!nodes)
+ nodes = new Object();
+
+ for (var i = 0; i < ary.length; ++i)
+ nodes[ary[i]] = document.getElementById(pfx + ary[i]);
+
+ return nodes;
+}
+
+function changeCSS(url, id)
+{
+ if (!id)
+ id = "main-css";
+
+ var node = document.getElementById(id);
+
+ if (!node)
+ {
+ node = document.createElement("link");
+ node.setAttribute("id", id);
+ node.setAttribute("rel", "stylesheet");
+ node.setAttribute("type", "text/css");
+ var head = document.getElementsByTagName("head")[0];
+ head.appendChild(node);
+ }
+ else
+ {
+ if (node.getAttribute("href") == url)
+ return;
+ }
+
+ node.setAttribute("href", url);
+ window.scrollTo(0, window.document.body.clientHeight);
+}
+
+function scrollToElement(element, position)
+{
+ /* The following values can be used for element:
+ * selection - current selected text.
+ * marker - the activity marker.
+ * [any DOM node] - anything :)
+ *
+ * The following values can be used for position:
+ * top - scroll so it is at the top.
+ * center - scroll so it is in the middle.
+ * bottom - scroll so it is at the bottom.
+ * inview - scroll so it is in view.
+ */
+ switch (element)
+ {
+ case "selection":
+ var sel = window.getSelection();
+ if (sel)
+ element = sel.anchorNode;
+ else
+ element = null;
+ break;
+
+ case "marker":
+ if ("getActivityMarker" in view)
+ element = view.getActivityMarker();
+ else
+ element = null;
+ break;
+ }
+ if (!element)
+ return;
+
+ // Calculate element's position in document.
+ var pos = { top: 0, center: 0, bottom: 0 };
+ // Find first parent with offset data.
+ while (element && !("offsetParent" in element))
+ element = element.parentNode;
+ var elt = element;
+ // Calc total offset data.
+ while (elt)
+ {
+ pos.top += 0 + elt.offsetTop;
+ elt = elt.offsetParent;
+ }
+ pos.center = pos.top + element.offsetHeight / 2;
+ pos.bottom = pos.top + element.offsetHeight;
+
+ // Store the positions to align the element with.
+ var cont = { top: 0, center: window.innerHeight / 2,
+ bottom: window.innerHeight };
+ if (!hasAttribute("container", "hidden"))
+ {
+ /* Offset height doesn't include the margins, so we get to do that
+ * ourselves via getComputedStyle(). We're assuming that will return
+ * a px value, which is all but guaranteed.
+ */
+ var headerHeight = header["container"].offsetHeight;
+ var css = getComputedStyle(header["container"], null);
+ headerHeight += parseInt(css.marginTop) + parseInt(css.marginBottom);
+ cont.top += headerHeight;
+ cont.center += headerHeight / 2;
+ }
+
+ // Pick between 'top' and 'bottom' for 'inview' position.
+ if (position == "inview")
+ {
+ if (pos.top - window.scrollY < cont.top)
+ position = "top";
+ else if (pos.bottom - window.scrollY > cont.bottom)
+ position = "bottom";
+ else
+ return;
+ }
+
+ window.scrollTo(0, pos[position] - cont[position]);
+}
+
+function updateMotifSettings(existingTimeout)
+{
+ // Try... catch with a repeat to cope with the style sheet not being loaded
+ const TIMEOUT = 100;
+ try
+ {
+ existingTimeout += TIMEOUT;
+ view.motifSettings = getMotifSettings();
+ }
+ catch(ex)
+ {
+ if (existingTimeout >= 30000) // Stop after trying for 30 seconds
+ return;
+ if (ex.name == "NS_ERROR_DOM_INVALID_ACCESS_ERR") //not ready, try again
+ setTimeout(updateMotifSettings, TIMEOUT, existingTimeout);
+ else // something else, panic!
+ dd(ex);
+ }
+}
+
+function getMotifSettings()
+{
+ var re = new RegExp("czsettings\\.(\\w*)", "i");
+ var rules = document.getElementById("main-css").sheet.cssRules;
+ var rv = new Object();
+ var ary;
+ // Copy any settings, which are available in the motif using the
+ // "CZSETTINGS" selector. We only store the regexp match after checking
+ // the rule type because selectorText is not defined on other rule types.
+ for (var i = 0; i < rules.length; i++)
+ {
+ if ((rules[i].type == CSSRule.STYLE_RULE) &&
+ ((ary = rules[i].selectorText.match(re)) != null))
+ {
+ rv[ary[1]] = true;
+ }
+ }
+ return rv;
+}
+
+function adoptNode(node)
+{
+ return client.adoptNode(node, document);
+}
+
+function setText(field, text, checkCondition)
+{
+ if (!header[field].firstChild)
+ header[field].appendChild(document.createTextNode(""));
+
+ if (typeof text != "string")
+ {
+ text = MSG_UNKNOWN;
+ if (checkCondition)
+ setAttribute(field, "condition", "red");
+ }
+ else if (checkCondition)
+ {
+ setAttribute(field, "condition", "green");
+ }
+
+ header[field].firstChild.data = text;
+}
+
+function setAttribute(field, name, value)
+{
+ if (!value)
+ value = "true";
+
+ header[field].setAttribute(name, value);
+}
+
+function removeAttribute(field, name)
+{
+ header[field].removeAttribute(name);
+}
+
+function hasAttribute(field, name)
+{
+ return header[field].hasAttribute(name);
+}
+
+function setHeaderState(state)
+{
+ if (header)
+ {
+ if (state)
+ {
+ removeAttribute("container", "hidden");
+ updateHeader();
+ }
+ else
+ {
+ setAttribute("container", "hidden");
+ }
+ }
+}
+
+function updateHeader()
+{
+ document.title = view.getURL();
+
+ if (!header || hasAttribute("container", "hidden"))
+ return;
+
+ for (var id in header)
+ {
+ var value;
+
+ if (id == "url-anchor")
+ {
+ value = view.getURL();
+ setAttribute("url-anchor", "href", value);
+ setText("url-anchor", value);
+ }
+ else if (id in view)
+ {
+ setText(id, view[id]);
+ }
+ }
+
+ if (header.update)
+ header.update();
+}
+
+function updateClient()
+{
+ var n = 0, c = 0;
+ for (name in client.networks)
+ {
+ ++n;
+ if (client.networks[name].isConnected())
+ ++c;
+ }
+
+ setAttribute("version-container", "title", client.userAgent);
+ setAttribute("version-container", "condition", mainWindow.__cz_condition);
+ setText("version", mainWindow.__cz_version);
+ setText("netcount", String(n));
+ setText("connectcount", String(c));
+}
+
+function updateNetwork()
+{
+ if (view.state == mainWindow.NET_CONNECTING)
+ {
+ setText("status", MSG_CONNECTING);
+ setAttribute("status","condition", "yellow");
+ removeAttribute("status", "title");
+ setText("lag", MSG_UNKNOWN);
+ }
+ else if (view.isConnected())
+ {
+ setText("status", MSG_CONNECTED);
+ setAttribute("status","condition", "green");
+ setAttribute("status", "title",
+ getMsg(MSG_CONNECT_VIA, view.primServ.unicodeName));
+ var lag = view.primServ.lag;
+ if (lag != -1)
+ setText("lag", getMsg(MSG_FMT_SECONDS, lag.toFixed(2)));
+ else
+ setText("lag", MSG_UNKNOWN);
+
+ }
+ else
+ {
+ setText("status", MSG_DISCONNECTED);
+ setAttribute("status","condition", "red");
+ removeAttribute("status", "title");
+ setText("lag", MSG_UNKNOWN);
+ }
+}
+
+function updateChannel()
+{
+ header["topicnodes"].removeChild(header["topicnodes"].firstChild);
+
+ if (view.active)
+ {
+ var str = view.mode.getModeStr();
+ if (!str)
+ str = MSG_NO_MODE;
+ setText("modestr", str);
+ setAttribute("modestr", "condition", "green");
+
+ setText("usercount", getMsg(MSG_FMT_USERCOUNT,
+ [view.getUsersLength(), view.opCount,
+ view.halfopCount, view.voiceCount]));
+ setAttribute("usercount", "condition", "green");
+
+ if (view.topic)
+ {
+ var data = getObjectDetails(view);
+ data.dontLogURLs = true;
+ var mailto = client.prefs["munger.mailto"];
+ client.munger.getRule(".mailto").enabled = mailto;
+ var nodes = client.munger.munge(view.topic, null, data);
+ client.munger.getRule(".mailto").enabled = false;
+ header["topicnodes"].appendChild(adoptNode(nodes));
+ }
+ else
+ {
+ setText("topicnodes", MSG_NONE);
+ }
+ }
+ else
+ {
+ setText("modestr", MSG_UNKNOWN);
+ setAttribute("modestr", "condition", "red");
+ setText("usercount", MSG_UNKNOWN);
+ setAttribute("usercount", "condition", "red");
+ setText("topicnodes", MSG_UNKNOWN);
+ }
+
+}
+
+function updateUser()
+{
+ var source;
+ if (view.name)
+ source = "<" + view.name + "@" + view.host + ">";
+ else
+ source = MSG_UNKNOWN;
+
+ if (view.parent.isConnected)
+ setText("serverstr", view.connectionHost, true);
+ else
+ setText("serverstr", null, true);
+
+ setText("title", getMsg(MSG_TITLE_USER, [view.unicodeName, source]));
+
+ header["descnodes"].removeChild(header["descnodes"].firstChild);
+ if (typeof view.desc != "undefined")
+ {
+ var data = getObjectDetails(view);
+ data.dontLogURLs = true;
+ var nodes = client.munger.munge(view.desc, null, data);
+ header["descnodes"].appendChild(adoptNode(nodes));
+ }
+ else
+ {
+ setText("descnodes", "");
+ }
+}
+
+function updateDCCChat()
+{
+ if (view.state.state == 4)
+ setText("remotestr", view.remoteIP + ":" + view.port, true);
+ else
+ setText("remotestr", null, true);
+
+ setText("title", getMsg(MSG_TITLE_DCCCHAT, view.user.unicodeName));
+}
+
+function updateDCCFile()
+{
+ var pcent = view.progress;
+
+ setText("file", view.filename);
+ setText("progress", getMsg(MSG_DCCFILE_PROGRESS,
+ [pcent, mainWindow.getSISize(view.position),
+ mainWindow.getSISize(view.size),
+ mainWindow.getSISpeed(view.speed)]));
+
+ setAttribute("progressbar", "width", pcent + "%");
+}
+
+function updateSplash(content)
+{
+ var splash = document.getElementById("splash");
+ splash.appendChild(document.createTextNode(content));
+} \ No newline at end of file