in suite/chatzilla/content/static.js [3900:4353]
function (message, msgtype, sourceObj, destObj, tags) {
// We need a message type, assume "INFO".
if (!msgtype) {
msgtype = MT_INFO;
}
var msgprefix = "";
if (msgtype.includes("/")) {
var ary = msgtype.match(/^(.*)\/(.*)$/);
msgtype = ary[1];
msgprefix = ary[2];
}
var blockLevel = false;
/* true if this row should be rendered at block
* level, (like, if it has a really long nickname
* that might disturb the rest of the layout) */
var o = getObjectDetails(this); /* get the skinny on |this| */
// Get the 'me' object, so we can be sure to get the attributes right.
var me;
if ("me" in this) {
me = this.me;
} else if (o.server && "me" in o.server) {
me = o.server.me;
}
/* Allow for matching (but not identical) user objects here. This tends to
* happen with bouncers and proxies, when they send channel messages
* pretending to be from the user; the sourceObj is a CIRCChanUser
* instead of a CIRCUser so doesn't == 'me'.
*/
if (me) {
if (sourceObj && sourceObj.canonicalName == me.canonicalName) {
sourceObj = me;
}
if (destObj && destObj.canonicalName == me.canonicalName) {
destObj = me;
}
}
// Let callers get away with "ME!" and we have to substitute here.
if (sourceObj == "ME!") {
sourceObj = me;
}
if (destObj == "ME!") {
destObj = me;
}
// Get the TYPE of the source object.
var fromType = sourceObj && sourceObj.TYPE ? sourceObj.TYPE : "unk";
// Is the source a user?
var fromUser = fromType.search(/IRC.*User/) != -1;
// Get some sort of "name" for the source.
var fromAttr = "";
if (sourceObj) {
if ("canonicalName" in sourceObj) {
fromAttr = sourceObj.canonicalName;
} else if ("name" in sourceObj) {
fromAttr = sourceObj.name;
} else {
fromAttr = sourceObj.viewName;
}
}
// Get the dest TYPE too...
var toType = destObj ? destObj.TYPE : "unk";
// Is the dest a user?
var toUser = toType.search(/IRC.*User/) != -1;
// Get a dest name too...
var toAttr = "";
if (destObj) {
if ("canonicalName" in destObj) {
toAttr = destObj.canonicalName;
} else if ("name" in destObj) {
toAttr = destObj.name;
} else {
toAttr = destObj.viewName;
}
}
// Is the message 'to' or 'from' somewhere other than this view
var toOther = sourceObj == me && destObj && destObj != this;
var fromOther =
toUser &&
destObj == me &&
sourceObj != this &&
// Need extra check for DCC users:
!(this.TYPE == "IRCDCCChat" && this.user == sourceObj);
// Attach "ME!" if appropriate, so motifs can style differently.
if (sourceObj == me && !toOther) {
fromAttr = fromAttr + " ME!";
}
if (destObj && destObj == me) {
toAttr = me.canonicalName + " ME!";
}
/* isImportant means to style the messages as important, and flash the
* window, getAttention means just flash the window. */
var isImportant = false,
getAttention = false,
isSuperfluous = false;
var viewType = this.TYPE;
var code;
var time;
if (tags && "time" in tags) {
time = new Date(tags.time);
} else {
time = new Date();
}
var timeStamp = strftime(this.prefs["timestamps.log"], time);
// Statusbar text, and the line that gets saved to the log.
var statusString;
var logStringPfx = timeStamp + " ";
var logStrings = [];
if (fromUser) {
statusString = getMsg(MSG_FMT_STATUS, [
timeStamp,
sourceObj.unicodeName + "!" + sourceObj.name + "@" + sourceObj.host,
]);
} else {
var name;
if (sourceObj) {
name = sourceObj.viewName;
} else {
name = this.viewName;
}
statusString = getMsg(MSG_FMT_STATUS, [timeStamp, name]);
}
// The table row, and it's attributes.
var msgRow = document.createElementNS(XHTML_NS, "html:tr");
msgRow.setAttribute("class", "msg");
if (this.msgGroups) {
msgRow.setAttribute("msg-groups", this.msgGroups.join(", "));
}
msgRow.setAttribute("msg-type", msgtype);
msgRow.setAttribute("msg-prefix", msgprefix);
msgRow.setAttribute("msg-dest", toAttr);
msgRow.setAttribute("dest-type", toType);
msgRow.setAttribute("view-type", viewType);
msgRow.setAttribute("status-text", statusString);
msgRow.setAttribute("timestamp", Number(time));
if (fromAttr) {
if (fromUser) {
msgRow.setAttribute("msg-user", fromAttr);
// Set some mode information for channel users
if (fromType == "IRCChanUser") {
msgRow.setAttribute("msg-user-mode", sourceObj.modes.join(" "));
}
} else {
msgRow.setAttribute("msg-source", fromAttr);
}
}
if (toOther) {
msgRow.setAttribute("to-other", toOther);
}
if (fromOther) {
msgRow.setAttribute("from-other", fromOther);
}
// Timestamp cell.
var msgRowTimestamp = document.createElementNS(XHTML_NS, "html:td");
msgRowTimestamp.setAttribute("class", "msg-timestamp");
var canMergeData;
var msgRowSource, msgRowType, msgRowData;
if (fromUser && msgtype.match(/^(PRIVMSG|ACTION|NOTICE|WALLOPS)$/)) {
var nick = sourceObj.unicodeName;
var decorSt = "";
var decorEn = "";
// Set default decorations.
if (msgtype == "ACTION") {
decorSt = "* ";
} else {
decorSt = "<";
decorEn = ">";
}
var nickURL;
if (sourceObj != me && "getURL" in sourceObj) {
nickURL = sourceObj.getURL();
}
if (toOther && "getURL" in destObj) {
nickURL = destObj.getURL();
}
if (sourceObj != me) {
// Not from us...
if (destObj == me) {
// ...but to us. Messages from someone else to us.
getAttention = true;
this.defaultCompletion = "/msg " + nick + " ";
// If this is a private message, and it's not in a query view,
// use *nick* instead of <nick>.
if (msgtype != "ACTION" && this.TYPE != "IRCUser") {
decorSt = "*";
decorEn = "*";
}
} else {
// ...or to us. Messages from someone else to channel or similar.
if (typeof message == "string" && me) {
isImportant = msgIsImportant(message, nick, o.network);
} else if (message.hasAttribute("isImportant") && me) {
isImportant = true;
}
if (isImportant) {
this.defaultCompletion =
nick + client.prefs.nickCompleteStr + " ";
}
}
} else {
// Messages from us, to somewhere other than this view
if (toOther) {
nick = destObj.unicodeName;
decorSt = ">";
decorEn = "<";
}
}
// Log the nickname in the same format as we'll let the user copy.
// If the message has a prefix, show it after a "/".
if (msgprefix) {
logStringPfx += decorSt + nick + "/" + msgprefix + decorEn + " ";
} else {
logStringPfx += decorSt + nick + decorEn + " ";
}
if (!("lastNickDisplayed" in this) || this.lastNickDisplayed != nick) {
this.lastNickDisplayed = nick;
this.mark = "mark" in this && this.mark == "even" ? "odd" : "even";
}
msgRowSource = document.createElementNS(XHTML_NS, "html:td");
msgRowSource.setAttribute("class", "msg-user");
// Make excessive nicks get shunted.
if (nick && nick.length > client.MAX_NICK_DISPLAY) {
blockLevel = true;
}
if (decorSt) {
msgRowSource.appendChild(newInlineText(decorSt, "chatzilla-decor"));
}
if (nickURL) {
var nick_anchor = document.createElementNS(XHTML_NS, "html:a");
nick_anchor.setAttribute("class", "chatzilla-link");
nick_anchor.setAttribute("href", nickURL);
nick_anchor.appendChild(newInlineText(nick));
msgRowSource.appendChild(nick_anchor);
} else {
msgRowSource.appendChild(newInlineText(nick));
}
if (msgprefix) {
/* We don't style the "/" with chatzilla-decor because the one
* thing we don't want is it disappearing!
*/
msgRowSource.appendChild(newInlineText("/", ""));
msgRowSource.appendChild(
newInlineText(msgprefix, "chatzilla-prefix")
);
}
if (decorEn) {
msgRowSource.appendChild(newInlineText(decorEn, "chatzilla-decor"));
}
canMergeData = this.prefs.collapseMsgs;
} else if (msgprefix) {
decorSt = "<";
decorEn = ">";
logStringPfx += decorSt + "/" + msgprefix + decorEn + " ";
msgRowSource = document.createElementNS(XHTML_NS, "html:td");
msgRowSource.setAttribute("class", "msg-user");
msgRowSource.appendChild(newInlineText(decorSt, "chatzilla-decor"));
msgRowSource.appendChild(newInlineText("/", ""));
msgRowSource.appendChild(newInlineText(msgprefix, "chatzilla-prefix"));
msgRowSource.appendChild(newInlineText(decorEn, "chatzilla-decor"));
canMergeData = this.prefs.collapseMsgs;
} else {
isSuperfluous = true;
if (!client.debugHook.enabled && msgtype in client.responseCodeMap) {
code = client.responseCodeMap[msgtype];
} else if (!client.debugHook.enabled && client.HIDE_CODES) {
code = client.DEFAULT_RESPONSE_CODE;
} else {
code = "[" + msgtype + "]";
}
/* Display the message code */
msgRowType = document.createElementNS(XHTML_NS, "html:td");
msgRowType.setAttribute("class", "msg-type");
msgRowType.appendChild(newInlineText(code));
logStringPfx += code + " ";
}
if (message) {
msgRowData = document.createElementNS(XHTML_NS, "html:td");
msgRowData.setAttribute("class", "msg-data");
var tmpMsgs = message;
if (typeof message == "string") {
msgRowData.appendChild(stringToMsg(message, this));
} else {
msgRowData.appendChild(message);
tmpMsgs = tmpMsgs.innerHTML.replace(/<[^<]*>/g, "");
}
tmpMsgs = tmpMsgs.split(/\r?\n/);
for (var l = 0; l < tmpMsgs.length; l++) {
logStrings[l] = logStringPfx + tmpMsgs[l];
}
}
if ("mark" in this) {
msgRow.setAttribute("mark", this.mark);
}
if (isImportant) {
if ("importantMessages" in this) {
var importantId = "important" + this.importantMessages++;
msgRow.setAttribute("id", importantId);
}
msgRow.setAttribute("important", "true");
msgRow.setAttribute("aria-live", "assertive");
}
// Timestamps first...
msgRow.appendChild(msgRowTimestamp);
// Now do the rest of the row, after block-level stuff.
if (msgRowSource) {
msgRow.appendChild(msgRowSource);
} else {
msgRow.appendChild(msgRowType);
}
if (msgRowData) {
msgRow.appendChild(msgRowData);
}
updateTimestampFor(this, msgRow);
if (blockLevel) {
/* putting a div here crashes mozilla, so fake it with nested tables
* for now */
var tr = document.createElementNS(XHTML_NS, "html:tr");
tr.setAttribute("class", "msg-nested-tr");
var td = document.createElementNS(XHTML_NS, "html:td");
td.setAttribute("class", "msg-nested-td");
td.setAttribute("colspan", "3");
tr.appendChild(td);
var table = document.createElementNS(XHTML_NS, "html:table");
table.setAttribute("class", "msg-nested-table");
table.setAttribute("role", "presentation");
td.appendChild(table);
var tbody = document.createElementNS(XHTML_NS, "html:tbody");
tbody.appendChild(msgRow);
table.appendChild(tbody);
msgRow = tr;
}
// Actually add the item.
addHistory(this, msgRow, canMergeData);
// Update attention states...
if (isImportant || getAttention) {
setTabState(this, "attention");
if (client.prefs["notify.aggressive"]) {
window.getAttention();
}
} else if (isSuperfluous) {
setTabState(this, "superfluous");
} else {
setTabState(this, "activity");
}
// Copy Important Messages [to network view].
if (isImportant && client.prefs.copyMessages && o.network != this) {
if (importantId) {
// Create the linked inline button
var msgspan = document.createElementNS(XHTML_NS, "html:span");
msgspan.setAttribute("isImportant", "true");
var cmd = "jump-to-anchor " + importantId + " " + this.unicodeName;
var prefix = getMsg(MSG_JUMPTO_BUTTON, [this.unicodeName, cmd]);
// Munge prefix as a button
client.munger.getRule(".inline-buttons").enabled = true;
client.munger.munge(prefix + " ", msgspan, o);
// Munge rest of message normally
client.munger.getRule(".inline-buttons").enabled = false;
client.munger.munge(message, msgspan, o);
o.network.displayHere(msgspan, msgtype, sourceObj, destObj);
} else {
o.network.displayHere(message, msgtype, sourceObj, destObj);
}
}
// Log file time!
if (this.prefs.log) {
if (!this.logFile) {
client.openLogFile(this);
}
try {
var LE = client.lineEnd;
for (var l = 0; l < logStrings.length; l++) {
this.logFile.write(fromUnicode(logStrings[l] + LE, "utf-8"));
}
} catch (ex) {
// Stop logging before showing any messages!
this.prefs.log = false;
dd("Log file write error: " + formatException(ex));
this.displayHere(
getMsg(MSG_LOGFILE_WRITE_ERROR, getLogPath(this)),
"ERROR"
);
}
}
/* We want to show alerts if they're from a non-current view (optional),
* or we don't have focus at all.
*/
if (
client.prefs["alert.globalEnabled"] &&
this.prefs["alert.enabled"] &&
client.alert &&
(!window.isFocused ||
(!client.prefs["alert.nonFocusedOnly"] &&
!("currentObject" in client && client.currentObject == this)))
) {
if (isImportant) {
showEventAlerts(this.TYPE, "stalk", message, nick, o, this, msgtype);
} else if (isSuperfluous) {
showEventAlerts(this.TYPE, "event", message, nick, o, this, msgtype);
} else {
showEventAlerts(this.TYPE, "chat", message, nick, o, this, msgtype);
}
}
};