diff options
Diffstat (limited to 'comm/suite/chatzilla/xul/content/output-window.js')
-rw-r--r-- | comm/suite/chatzilla/xul/content/output-window.js | 588 |
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 |