summaryrefslogtreecommitdiffstats
path: root/comm/suite/chatzilla/xul/content/mungers.js
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--comm/suite/chatzilla/xul/content/mungers.js904
1 files changed, 904 insertions, 0 deletions
diff --git a/comm/suite/chatzilla/xul/content/mungers.js b/comm/suite/chatzilla/xul/content/mungers.js
new file mode 100644
index 0000000000..dce8e9980f
--- /dev/null
+++ b/comm/suite/chatzilla/xul/content/mungers.js
@@ -0,0 +1,904 @@
+/* -*- Mode: C++; tab-width: 8; 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/. */
+
+/* This file contains the munger functions and rules used by ChatZilla.
+ * It's generally a bad idea to call munger functions inside ChatZilla for
+ * anything but munging (chat) output.
+ */
+
+function initMunger()
+{
+ /* linkRE: the general URL linkifier regular expression:
+ *
+ * - start with whitespace, non-word, or begining-of-line
+ * - then match:
+ * - EITHER scheme (word + hyphen), colon, then lots of non-whitespace
+ * - OR "www" followed by at least 2 sets of:
+ * - "." plus some non-whitespace, non-"." characters
+ * - must end match with a word-break
+ * - include a "/" or "=" beyond break if present
+ * - end with whitespace, non-word, or end-of-line
+ */
+ client.linkRE =
+ /(?:\W|^)((?:(\w[\w-]+):[^\s]+|www(\.[^.\s]+){2,})\b[\/=\)]?)(?=\s|\W|$)/;
+
+ // Colours: \x03, with optional foreground and background colours
+ client.colorRE = /(\x03((\d{1,2})(,\d{1,2}|)|))/;
+
+ client.whitespaceRE = new RegExp("(\\S{" + client.MAX_WORD_DISPLAY + ",})");
+
+ const LOW_PRIORITY = 5;
+ const NORMAL_PRIORITY = 10;
+ const HIGH_PRIORITY = 15;
+ const HIGHER_PRIORITY = 20;
+
+ var munger = client.munger = new CMunger(insertText);
+ // Special internal munger!
+ munger.addRule(".inline-buttons", /(\[\[.*?\]\])/, insertInlineButton,
+ HIGH_PRIORITY, LOW_PRIORITY, false);
+ munger.addRule("quote", /(``|'')/, insertQuote,
+ NORMAL_PRIORITY, NORMAL_PRIORITY);
+ munger.addRule("bold", /(?:[\s(\[]|^)(\*[^*()]*\*)(?:[\s\]).,;!\?]|$)/,
+ "chatzilla-bold", NORMAL_PRIORITY, NORMAL_PRIORITY);
+ munger.addRule("underline", /(?:[\s(\[]|^)(\_[^_()]*\_)(?:[\s\]).,;!\?]|$)/,
+ "chatzilla-underline", NORMAL_PRIORITY, NORMAL_PRIORITY);
+ munger.addRule("italic", /(?:\s|^)(\/[^\/()]*\/)(?:[\s.,]|$)/,
+ "chatzilla-italic", NORMAL_PRIORITY, NORMAL_PRIORITY);
+ /* allow () chars inside |code()| blocks */
+ munger.addRule("teletype", /(?:\s|^)(\|[^|]*\|)(?:[\s.,]|$)/,
+ "chatzilla-teletype", NORMAL_PRIORITY, NORMAL_PRIORITY);
+ munger.addRule(".mirc-colors", client.colorRE, mircChangeColor,
+ NORMAL_PRIORITY, NORMAL_PRIORITY);
+ munger.addRule(".mirc-bold", /(\x02)/, mircToggleBold,
+ NORMAL_PRIORITY, NORMAL_PRIORITY);
+ munger.addRule(".mirc-underline", /(\x1f)/, mircToggleUnder,
+ NORMAL_PRIORITY, NORMAL_PRIORITY);
+ munger.addRule(".mirc-color-reset", /(\x0f)/, mircResetColor,
+ NORMAL_PRIORITY, NORMAL_PRIORITY);
+ munger.addRule(".mirc-reverse", /(\x16)/, mircReverseColor,
+ NORMAL_PRIORITY, NORMAL_PRIORITY);
+ munger.addRule(".ansi-escape-sgr", /(\x1b\[([\d;]*)m)/,
+ ansiEscapeSGR, NORMAL_PRIORITY, NORMAL_PRIORITY);
+ munger.addRule("ctrl-char", /([\x01-\x1f])/, showCtrlChar,
+ NORMAL_PRIORITY, NORMAL_PRIORITY);
+ munger.addRule("link", client.linkRE, insertLink, NORMAL_PRIORITY, HIGH_PRIORITY);
+
+ // This has a higher starting priority so as to get it to match before the
+ // normal link, which won't know about mailto and then fail.
+ munger.addRule(".mailto",
+ /(?:\W|^)((mailto:)?[^:;\\<>\[\]()\'\"\s\u201d]+@[^.<>\[\]()\'\"\s\u201d]+\.[^<>\[\]()\'\"\s\u201d]+)/i,
+ insertMailToLink, NORMAL_PRIORITY, HIGHER_PRIORITY, false);
+
+ addBugzillaLinkMungerRule(client.prefs["bugKeyword"], NORMAL_PRIORITY, NORMAL_PRIORITY);
+
+ munger.addRule("channel-link",
+ /(?:[^\w#]|^)[@%+]?(#[^<>,\[\](){}\"\s\u201d]*[^:,.<>\[\](){}\'\"\s\u201d])/i,
+ insertChannelLink, NORMAL_PRIORITY, NORMAL_PRIORITY);
+ munger.addRule("talkback-link", /(?:\W|^)(TB\d{8,}[A-Z]?)(?:\W|$)/,
+ insertTalkbackLink, NORMAL_PRIORITY, NORMAL_PRIORITY);
+
+ munger.addRule("face",
+ /((^|\s)(?:[>O]?[B8=:;(xX%][~']?[-^v"]?(?:[)|(PpSs0oO#\?\*\[\]\/\\]|D+)|>[-^v]?\)|[oO9][._][oO9])(\s|$))/,
+ insertSmiley, NORMAL_PRIORITY, NORMAL_PRIORITY);
+ munger.addRule("rheet", /(?:\W|^)(rhee+t\!*)(?:\s|$)/i, insertRheet, 10, 10);
+ munger.addRule("word-hyphenator", client.whitespaceRE,
+ insertHyphenatedWord, LOW_PRIORITY, NORMAL_PRIORITY);
+
+ client.enableColors = client.prefs["munger.colorCodes"];
+ var branch = client.prefManager.prefBranch;
+ for (var entry in munger.entries)
+ {
+ if (!isinstance(munger.entries[entry], Object))
+ continue;
+
+ for (var rule in munger.entries[entry])
+ {
+ if (rule[0] == ".")
+ continue;
+
+ try
+ {
+ munger.entries[entry][rule].enabled =
+ branch.getBoolPref("munger." + rule);
+ }
+ catch (ex)
+ {
+ // nada
+ }
+ }
+ }
+}
+
+function addBugzillaLinkMungerRule(keywords, priority, startPriority)
+{
+ client.munger.addRule("bugzilla-link",
+ new RegExp("(?:\\W|^)(("+keywords+")\\s+(?:#?\\d+|#[^\\s,]{1,20})(?:\\s+comment\\s+#?\\d+)?)","i"),
+ insertBugzillaLink, priority, startPriority);
+
+}
+
+function insertLink(matchText, containerTag, data, mungerEntry)
+{
+ var href;
+ var linkText;
+
+ var trailing;
+ ary = matchText.match(/([.,?\)]+)$/);
+ if (ary)
+ {
+ linkText = RegExp.leftContext;
+ trailing = ary[1];
+
+ // We special-case links that end with (something), often found on wikis
+ // if "trailing" starts with ) and there's an unclosed ( in the
+ // "linkText"; then we put the final ) back in
+ if ((trailing.indexOf(")") == 0) && (linkText.match(/\([^\)]*$/)))
+ {
+
+ linkText += ")";
+ trailing = trailing.substr(1);
+ }
+ }
+ else
+ {
+ linkText = matchText;
+ }
+
+ var ary = linkText.match(/^(\w[\w-]+):/);
+ if (ary)
+ {
+ if (!client.checkURLScheme(ary[1]))
+ {
+ mungerEntry.enabled = false;
+ client.munger.munge(matchText, containerTag, data);
+ mungerEntry.enabled = true;
+ return;
+ }
+
+ href = linkText;
+ }
+ else
+ {
+ href = "http://" + linkText;
+ }
+
+ /* This gives callers to the munger control over URLs being logged; the
+ * channel topic munger uses this, as well as the "is important" checker.
+ * If either of |dontLogURLs| or |noStateChange| is present and true, we
+ * don't log.
+ */
+ if ((!("dontLogURLs" in data) || !data.dontLogURLs) &&
+ (!("noStateChange" in data) || !data.noStateChange) &&
+ client.urlLogger)
+ {
+ client.urlLogger.append(href);
+ }
+
+ var anchor = document.createElementNS(XHTML_NS, "html:a");
+ var mircRE = /\x1f|\x02|\x0f|\x16|\x03([0-9]{1,2}(,[0-9]{1,2})?)?/g;
+ anchor.setAttribute("href", href.replace(mircRE, ""));
+
+ // Carry over formatting.
+ var otherFormatting = calcClass(data);
+ if (otherFormatting)
+ anchor.setAttribute("class", "chatzilla-link " + otherFormatting);
+ else
+ anchor.setAttribute("class", "chatzilla-link");
+
+ anchor.setAttribute("target", "_content");
+ mungerEntry.enabled = false;
+ data.inLink = true;
+ client.munger.munge(linkText, anchor, data);
+ mungerEntry.enabled = true;
+ delete data.inLink;
+ containerTag.appendChild(anchor);
+ if (trailing)
+ insertText(trailing, containerTag, data);
+
+}
+
+function insertMailToLink(matchText, containerTag, eventData, mungerEntry)
+{
+ if (("inLink" in eventData) && eventData.inLink)
+ {
+ mungerEntry.enabled = false;
+ client.munger.munge(matchText, containerTag, eventData);
+ mungerEntry.enabled = true;
+ return;
+ }
+
+ var href;
+
+ if (matchText.toLowerCase().indexOf("mailto:") != 0)
+ href = "mailto:" + matchText;
+ else
+ href = matchText;
+
+ var anchor = document.createElementNS(XHTML_NS, "html:a");
+ var mircRE = /\x1f|\x02|\x0f|\x16|\x03([0-9]{1,2}(,[0-9]{1,2})?)?/g;
+ anchor.setAttribute("href", href.replace(mircRE, ""));
+
+ // Carry over formatting.
+ var otherFormatting = calcClass(eventData);
+ if (otherFormatting)
+ anchor.setAttribute("class", "chatzilla-link " + otherFormatting);
+ else
+ anchor.setAttribute("class", "chatzilla-link");
+
+ //anchor.setAttribute ("target", "_content");
+ mungerEntry.enabled = false;
+ eventData.inLink = true;
+ client.munger.munge(matchText, anchor, eventData);
+ mungerEntry.enabled = true;
+ delete eventData.inLink;
+ containerTag.appendChild(anchor);
+
+}
+
+function insertChannelLink(matchText, containerTag, eventData, mungerEntry)
+{
+ if (("inLink" in eventData) && eventData.inLink)
+ {
+ mungerEntry.enabled = false;
+ client.munger.munge(matchText, containerTag, eventData);
+ mungerEntry.enabled = true;
+ return;
+ }
+
+ var bogusChannels =
+ /^#(include|error|define|if|ifdef|else|elsif|endif)$/i;
+
+ if (!("network" in eventData) || !eventData.network ||
+ matchText.search(bogusChannels) != -1)
+ {
+ containerTag.appendChild(document.createTextNode(matchText));
+ return;
+ }
+
+ var linkText = removeColorCodes(matchText);
+ var encodedLinkText = fromUnicode(linkText, eventData.sourceObject);
+ var anchor = document.createElementNS(XHTML_NS, "html:a");
+ anchor.setAttribute("href", eventData.network.getURL(encodedLinkText));
+
+ // Carry over formatting.
+ var otherFormatting = calcClass(eventData);
+ if (otherFormatting)
+ anchor.setAttribute("class", "chatzilla-link " + otherFormatting);
+ else
+ anchor.setAttribute("class", "chatzilla-link");
+
+ mungerEntry.enabled = false;
+ eventData.inLink = true;
+ client.munger.munge(matchText, anchor, eventData);
+ mungerEntry.enabled = true;
+ delete eventData.inLink;
+ containerTag.appendChild(anchor);
+}
+
+function insertTalkbackLink(matchText, containerTag, eventData, mungerEntry)
+{
+ if (("inLink" in eventData) && eventData.inLink)
+ {
+ mungerEntry.enabled = false;
+ client.munger.munge(matchText, containerTag, eventData);
+ mungerEntry.enabled = true;
+ return;
+ }
+
+ var anchor = document.createElementNS(XHTML_NS, "html:a");
+
+ anchor.setAttribute("href", "http://talkback-public.mozilla.org/" +
+ "search/start.jsp?search=2&type=iid&id=" + matchText);
+
+ // Carry over formatting.
+ var otherFormatting = calcClass(eventData);
+ if (otherFormatting)
+ anchor.setAttribute("class", "chatzilla-link " + otherFormatting);
+ else
+ anchor.setAttribute("class", "chatzilla-link");
+
+ mungerEntry.enabled = false;
+ client.munger.munge(matchText, anchor, eventData);
+ mungerEntry.enabled = true;
+ containerTag.appendChild(anchor);
+}
+
+function insertBugzillaLink (matchText, containerTag, eventData, mungerEntry)
+{
+ if (("inLink" in eventData) && eventData.inLink)
+ {
+ mungerEntry.enabled = false;
+ client.munger.munge(matchText, containerTag, eventData);
+ mungerEntry.enabled = true;
+ return;
+ }
+
+ var prefs = client.prefs;
+ if (eventData.channel)
+ prefs = eventData.channel.prefs;
+ else if (eventData.network)
+ prefs = eventData.network.prefs;
+
+ var bugURL = prefs["bugURL"];
+ var bugURLcomment = prefs["bugURL.comment"];
+
+ if (bugURL.length > 0)
+ {
+ var idOrAlias = matchText.match(new RegExp("(?:"+client.prefs["bugKeyword"]+")\\s+#?(\\d+|[^\\s,]{1,20})","i"))[1];
+ bugURL = bugURL.replace("%s", idOrAlias);
+
+ var commentNum = matchText.match(/comment\s+#?(\d+)/i);
+ if (commentNum)
+ {
+ /* If the comment is a complete URL, use only that, replacing %1$s
+ * and %2$s with the bug number and comment number, respectively.
+ * Otherwise, append the comment preference to the main one,
+ * replacing just %s in each.
+ */
+ if (bugURLcomment.match(/^\w+:/))
+ {
+ bugURL = bugURLcomment;
+ bugURL = bugURL.replace("%1$s", idOrAlias);
+ bugURL = bugURL.replace("%2$s", commentNum[1]);
+ }
+ else
+ {
+ bugURL += bugURLcomment.replace("%s", commentNum[1]);
+ }
+ }
+
+ var anchor = document.createElementNS(XHTML_NS, "html:a");
+ anchor.setAttribute("href", bugURL);
+ // Carry over formatting.
+ var otherFormatting = calcClass(eventData);
+ if (otherFormatting)
+ anchor.setAttribute("class", "chatzilla-link " + otherFormatting);
+ else
+ anchor.setAttribute("class", "chatzilla-link");
+
+ anchor.setAttribute("target", "_content");
+ mungerEntry.enabled = false;
+ eventData.inLink = true;
+ client.munger.munge(matchText, anchor, eventData);
+ mungerEntry.enabled = true;
+ delete eventData.inLink;
+ containerTag.appendChild(anchor);
+ }
+ else
+ {
+ mungerEntry.enabled = false;
+ client.munger.munge(matchText, containerTag, eventData);
+ mungerEntry.enabled = true;
+ }
+}
+
+function insertRheet(matchText, containerTag, eventData, mungerEntry)
+{
+ if (("inLink" in eventData) && eventData.inLink)
+ {
+ mungerEntry.enabled = false;
+ client.munger.munge(matchText, containerTag, eventData);
+ mungerEntry.enabled = true;
+ return;
+ }
+
+ var anchor = document.createElementNS(XHTML_NS, "html:a");
+ anchor.setAttribute("href",
+ "http://ftp.mozilla.org/pub/mozilla.org/mozilla/libraries/bonus-tracks/rheet.wav");
+ anchor.setAttribute("class", "chatzilla-rheet chatzilla-link");
+ //anchor.setAttribute ("target", "_content");
+ insertText(matchText, anchor, eventData);
+ containerTag.appendChild(anchor);
+}
+
+function insertQuote (matchText, containerTag)
+{
+ if (matchText == "``")
+ containerTag.appendChild(document.createTextNode("\u201c"));
+ else
+ containerTag.appendChild(document.createTextNode("\u201d"));
+ containerTag.appendChild(document.createElementNS(XHTML_NS, "html:wbr"));
+}
+
+function insertSmiley(emoticon, containerTag, eventData, mungerEntry)
+{
+ let smilies = {
+ "face-alien": "\uD83D\uDC7D",
+ "face-lol": "\uD83D\uDE02",
+ "face-laugh": "\uD83D\uDE04",
+ "face-sweat_smile": "\uD83D\uDE05",
+ "face-innocent": "\uD83D\uDE07",
+ "face-evil": "\uD83D\uDE08",
+ "face-wink": "\uD83D\uDE09",
+ "face-smile": "\uD83D\uDE0A",
+ "face-cool": "\uD83D\uDE0E",
+ "face-neutral": "\uD83D\uDE10",
+ "face-thinking": "\uD83D\uDE14",
+ "face-confused": "\uD83D\uDE15",
+ "face-kissing": "\uD83D\uDE17",
+ "face-tongue": "\uD83D\uDE1B",
+ "face-worried": "\uD83D\uDE1F",
+ "face-angry": "\uD83D\uDE20",
+ "face-cry": "\uD83D\uDE22",
+ "face-surprised": "\uD83D\uDE2D",
+ "face-eek": "\uD83D\uDE31",
+ "face-red": "\uD83D\uDE33",
+ "face-dizzy": "\uD83D\uDE35",
+ "face-sad": "\uD83D\uDE41",
+ "face-rolleyes": "\uD83D\uDE44",
+ "face-zipped": "\uD83E\uDD10",
+ "face-rofl": "\uD83E\uDD23",
+ "face-woozy": "\uD83E\uDD74",
+ };
+
+ let type;
+
+ if (emoticon.search(/\>[-^v]?\)/) != -1)
+ type = "face-alien";
+ else if (emoticon.search(/\>[=:;][-^v]?[(|]|[Xx][-^v]?[(\[]/) != -1)
+ type = "face-angry";
+ else if (emoticon.search(/[=:;][-^v]?[Ss]/) != -1)
+ type = "face-confused";
+ else if (emoticon.search(/[B8][-^v]?[)\]]/) != -1)
+ type = "face-cool";
+ else if (emoticon.search(/[=:;][~'][-^v]?\(/) != -1)
+ type = "face-cry";
+ else if (emoticon.search(/o[._]O|O[._]o/) != -1)
+ type = "face-dizzy";
+ else if (emoticon.search(/o[._]o|O[._]O/) != -1)
+ type = "face-eek";
+ else if (emoticon.search(/\>[=:;][-^v]?D/) != -1)
+ type = "face-evil";
+ else if (emoticon.search(/O[=:][-^v]?[)]/) != -1)
+ type = "face-innocent";
+ else if (emoticon.search(/[=:;][-^v]?[*]/) != -1)
+ type = "face-kissing";
+ else if (emoticon.search(/[=:;][-^v]?DD/) != -1)
+ type = "face-lol";
+ else if (emoticon.search(/[=:;][-^v]?D/) != -1)
+ type = "face-laugh";
+ else if (emoticon.search(/\([-^v]?D|[xX][-^v]?D/) != -1)
+ type = "face-rofl";
+ else if (emoticon.search(/[=:;][-^v]?\|/) != -1)
+ type = "face-neutral";
+ else if (emoticon.search(/[=:;][-^v]?\?/) != -1)
+ type = "face-thinking";
+ else if (emoticon.search(/[=:;]"[)\]]/) != -1)
+ type = "face-red";
+ else if (emoticon.search(/9[._]9/) != -1)
+ type = "face-rolleyes";
+ else if (emoticon.search(/[=:;][-^v]?[(\[]/) != -1)
+ type = "face-sad";
+ else if (emoticon.search(/[=:][-^v]?[)]/) != -1)
+ type = "face-smile";
+ else if (emoticon.search(/[=:;][-^v]?[0oO]/) != -1)
+ type = "face-surprised";
+ else if (emoticon.search(/[=:][-^v]?[\]]/) != -1)
+ type = "face-sweat_smile";
+ else if (emoticon.search(/[=:;][-^v]?[pP]/) != -1)
+ type = "face-tongue";
+ else if (emoticon.search(/;[-^v]?[)\]]/) != -1)
+ type = "face-wink";
+ else if (emoticon.search(/%[-^v][)\]]/) != -1)
+ type = "face-woozy";
+ else if (emoticon.search(/[=:;][-^v]?[\/\\]/) != -1)
+ type = "face-worried";
+ else if (emoticon.search(/[=:;][-^v]?[#]/) != -1)
+ type = "face-zipped";
+
+ let glyph = smilies[type];
+ if (!glyph) {
+ // We didn't actually match anything, so it'll be a too-generic match
+ // from the munger RegExp.
+ mungerEntry.enabled = false;
+ client.munger.munge(emoticon, containerTag, eventData);
+ mungerEntry.enabled = true;
+ return;
+ }
+
+ // Add spaces to beginning / end where appropriate.
+ if (emoticon.search(/^\s/) != -1)
+ glyph = " " + glyph;
+ if (emoticon.search(/\s$/) != -1)
+ glyph = glyph + " ";
+
+ // Create a span to hold the emoticon.
+ let span = document.createElementNS(XHTML_NS, "html:span");
+ span.appendChild(document.createTextNode(glyph));
+ span.setAttribute("class", "chatzilla-emote-txt");
+ // Add the title attribute (to show the original text in a tooltip) in case
+ // the replacement was done incorrectly.
+ span.setAttribute("title", emoticon);
+ span.setAttribute("type", type);
+ containerTag.appendChild(span);
+}
+
+function mircChangeColor (colorInfo, containerTag, data)
+{
+ /* If colors are disabled, the caller doesn't want colors specifically, or
+ * the caller doesn't want any state-changing effects, we drop out.
+ */
+ if (!client.enableColors ||
+ (("noMircColors" in data) && data.noMircColors) ||
+ (("noStateChange" in data) && data.noStateChange))
+ {
+ return;
+ }
+
+ // Entry 0 will contain all colors specified,
+ // entry 1 will have any specified foreground color or be undefined,
+ // entry 2 will have any specified background color or be undefined.
+ // Valid color codes are 0-99 with 99 having special meaning.
+ let ary = colorInfo.match(/^\x03(?:(\d\d?)(?:,(\d\d?))?)?/);
+
+ // If no foreground color specified or somehow the array does not have 3
+ // entries then it has invalid syntax.
+ if (ary.length != 3 || !ary[1]) {
+ delete data.currFgColor;
+ delete data.currBgColor;
+ return;
+ }
+
+ let fgColor = Number(ary[1]);
+
+ if (fgColor != 99) {
+ data.currFgColor = (fgColor % 16).toString().padStart(2, "0");
+ } else {
+ delete data.currFgColor;
+ }
+
+ // If no background color then default to 99.
+ let bgColor = Number(ary[2] || "99");
+
+ if (bgColor != 99) {
+ data.currBgColor = (bgColor % 16).toString().padStart(2, "0");
+ } else {
+ delete data.currBgColor;
+ }
+
+ // Only set hasColorInfo if we have something set.
+ if (fgColor != 99 || bgColor != 99) {
+ data.hasColorInfo = true;
+ }
+}
+
+function mircToggleBold (colorInfo, containerTag, data)
+{
+ if (!client.enableColors ||
+ (("noMircColors" in data) && data.noMircColors) ||
+ (("noStateChange" in data) && data.noStateChange))
+ {
+ return;
+ }
+
+ if ("isBold" in data)
+ delete data.isBold;
+ else
+ data.isBold = true;
+ data.hasColorInfo = true;
+}
+
+function mircToggleUnder (colorInfo, containerTag, data)
+{
+ if (!client.enableColors ||
+ (("noMircColors" in data) && data.noMircColors) ||
+ (("noStateChange" in data) && data.noStateChange))
+ {
+ return;
+ }
+
+ if ("isUnderline" in data)
+ delete data.isUnderline;
+ else
+ data.isUnderline = true;
+ data.hasColorInfo = true;
+}
+
+function mircResetColor (text, containerTag, data)
+{
+ if (!client.enableColors ||
+ (("noMircColors" in data) && data.noMircColors) ||
+ (("noStateChange" in data) && data.noStateChange) ||
+ !("hasColorInfo" in data))
+ {
+ return;
+ }
+
+ removeColorInfo(data);
+}
+
+function mircReverseColor (text, containerTag, data)
+{
+ if (!client.enableColors ||
+ (("noMircColors" in data) && data.noMircColors) ||
+ (("noStateChange" in data) && data.noStateChange))
+ {
+ return;
+ }
+
+ var tempColor = ("currFgColor" in data ? data.currFgColor : "");
+
+ if ("currBgColor" in data)
+ data.currFgColor = data.currBgColor;
+ else
+ delete data.currFgColor;
+ if (tempColor)
+ data.currBgColor = tempColor;
+ else
+ delete data.currBgColor;
+ data.hasColorInfo = true;
+}
+
+function ansiEscapeSGR(text, containerTag, data)
+{
+ if (!client.enableColors ||
+ (("noANSIColors" in data) && data.noANSIColors) ||
+ (("noStateChange" in data) && data.noStateChange))
+ {
+ return;
+ }
+
+ /* ANSI SGR (Select Graphic Rendition) escape support. Matched text may
+ * have any number of effects, each a number separated by a semicolon. If
+ * there are no effects listed, it is treated as effect "0" (reset/normal).
+ */
+
+ text = text.substr(2, text.length - 3) || "0";
+
+ const ansiToMircColor = [
+ "01", "05", "03", "07", "02", "06", "10", "15",
+ "14", "04", "09", "08", "12", "13", "11", "00"
+ ];
+
+ var effects = text.split(";");
+ for (var i = 0; i < effects.length; i++)
+ {
+ data.hasColorInfo = true;
+
+ switch (Number(effects[i]))
+ {
+ case 0: // Reset/normal.
+ removeColorInfo(data);
+ break;
+
+ case 1: // Intensity: bold.
+ data.isBold = true;
+ break;
+
+ case 3: // Italic: on.
+ data.isItalic = true;
+ break;
+
+ case 4: // Underline: single.
+ data.isUnderline = true;
+ break;
+
+ case 9: // Strikethrough: on.
+ data.isStrikethrough = true;
+ break;
+
+ case 22: // Intensity: normal.
+ delete data.isBold;
+ break;
+
+ case 23: // Italic: off.
+ delete data.isItalic;
+ break;
+
+ case 24: // Underline: off.
+ delete data.isUnderline;
+ break;
+
+ case 29: // Strikethrough: off.
+ delete data.isStrikethrough;
+ break;
+
+ case 53: // Overline: on.
+ data.isOverline = true;
+ break;
+
+ case 55: // Overline: off.
+ delete data.isOverline;
+ break;
+
+ case 30: // FG: Black.
+ case 31: // FG: Red.
+ case 32: // FG: Green.
+ case 33: // FG: Yellow.
+ case 34: // FG: Blue.
+ case 35: // FG: Magenta.
+ case 36: // FG: Cyan.
+ case 37: // FG: While (light grey).
+ data.currFgColor = ansiToMircColor[effects[i] - 30];
+ break;
+
+ case 39: // FG: default.
+ delete data.currFgColor;
+ break;
+
+ case 40: // BG: Black.
+ case 41: // BG: Red.
+ case 42: // BG: Green.
+ case 43: // BG: Yellow.
+ case 44: // BG: Blue.
+ case 45: // BG: Magenta.
+ case 46: // BG: Cyan.
+ case 47: // BG: While (light grey).
+ data.currBgColor = ansiToMircColor[effects[i] - 40];
+ break;
+
+ case 49: // BG: default.
+ delete data.currBgColor;
+ break;
+
+ case 90: // FG: Bright Black (dark grey).
+ case 91: // FG: Bright Red.
+ case 92: // FG: Bright Green.
+ case 93: // FG: Bright Yellow.
+ case 94: // FG: Bright Blue.
+ case 95: // FG: Bright Magenta.
+ case 96: // FG: Bright Cyan.
+ case 97: // FG: Bright While.
+ data.currFgColor = ansiToMircColor[effects[i] - 90 + 8];
+ break;
+
+ case 100: // BG: Bright Black (dark grey).
+ case 101: // BG: Bright Red.
+ case 102: // BG: Bright Green.
+ case 103: // BG: Bright Yellow.
+ case 104: // BG: Bright Blue.
+ case 105: // BG: Bright Magenta.
+ case 106: // BG: Bright Cyan.
+ case 107: // BG: Bright While.
+ data.currBgColor = ansiToMircColor[effects[i] - 100 + 8];
+ break;
+ }
+ }
+}
+
+function removeColorInfo(data)
+{
+ delete data.currFgColor;
+ delete data.currBgColor;
+ delete data.isBold;
+ delete data.isItalic;
+ delete data.isOverline;
+ delete data.isStrikethrough;
+ delete data.isUnderline;
+ delete data.hasColorInfo;
+}
+
+function showCtrlChar(c, containerTag)
+{
+ var span = document.createElementNS(XHTML_NS, "html:span");
+ span.setAttribute("class", "chatzilla-control-char");
+ if (c == "\t")
+ {
+ containerTag.appendChild(document.createTextNode(c));
+ return;
+ }
+
+ var ctrlStr = c.charCodeAt(0).toString(16);
+ if (ctrlStr.length < 2)
+ ctrlStr = "0" + ctrlStr;
+ span.appendChild(document.createTextNode("0x" + ctrlStr));
+ containerTag.appendChild(span);
+ containerTag.appendChild(document.createElementNS(XHTML_NS, "html:wbr"));
+}
+
+function insertText(text, containerTag, data)
+{
+ var newClass = "";
+ if (data && ("hasColorInfo" in data))
+ newClass = calcClass(data);
+ if (!newClass)
+ delete data.hasColorInfo;
+
+ if (newClass)
+ {
+ var spanTag = document.createElementNS(XHTML_NS, "html:span");
+ spanTag.setAttribute("class", newClass);
+ containerTag.appendChild(spanTag);
+ containerTag = spanTag;
+ }
+
+ var arg;
+ while ((arg = text.match(client.whitespaceRE)))
+ {
+ // Find the start of the match so we can insert the preceding text.
+ var start = text.indexOf(arg[0]);
+ if (start > 0)
+ containerTag.appendChild(document.createTextNode(text.substr(0, start)));
+
+ // Process the long word itself.
+ insertHyphenatedWord(arg[1], containerTag, { dontStyleText: true });
+
+ // Continue with the rest of the text.
+ text = text.substr(start + arg[0].length);
+ }
+
+ // Insert any left-over text on the end.
+ if (text)
+ containerTag.appendChild(document.createTextNode(text));
+}
+
+function insertHyphenatedWord(longWord, containerTag, data)
+{
+ var wordParts = splitLongWord(longWord, client.MAX_WORD_DISPLAY);
+
+ if (!data || !("dontStyleText" in data))
+ {
+ var newClass = "";
+ if (data && ("hasColorInfo" in data))
+ newClass = calcClass(data);
+ if (!newClass)
+ delete data.hasColorInfo;
+
+ if (newClass)
+ {
+ var spanTag = document.createElementNS(XHTML_NS, "html:span");
+ spanTag.setAttribute("class", newClass);
+ containerTag.appendChild(spanTag);
+ containerTag = spanTag;
+ }
+ }
+
+ var wbr = document.createElementNS(XHTML_NS, "html:wbr");
+ for (var i = 0; i < wordParts.length; ++i)
+ {
+ containerTag.appendChild(document.createTextNode(wordParts[i]));
+ containerTag.appendChild(wbr.cloneNode(true));
+ }
+}
+
+function insertInlineButton(text, containerTag, data)
+{
+ var ary = text.match(/\[\[([^\]]+)\]\[([^\]]+)\]\[([^\]]+)\]\]/);
+
+ if (!ary)
+ {
+ containerTag.appendChild(document.createTextNode(text));
+ return;
+ }
+
+ var label = ary[1];
+ var title = ary[2];
+ var command = ary[3];
+
+ var link = document.createElementNS(XHTML_NS, "html:a");
+ link.setAttribute("href", "x-cz-command:" + encodeURI(command));
+ link.setAttribute("title", title);
+ link.setAttribute("class", "chatzilla-link");
+ link.appendChild(document.createTextNode(label));
+
+ containerTag.appendChild(document.createTextNode("["));
+ containerTag.appendChild(link);
+ containerTag.appendChild(document.createTextNode("]"));
+}
+
+function calcClass(data)
+{
+ var className = "";
+ if ("hasColorInfo" in data)
+ {
+ if ("currFgColor" in data)
+ className += " chatzilla-fg" + data.currFgColor;
+ if ("currBgColor" in data)
+ className += " chatzilla-bg" + data.currBgColor;
+ if ("isBold" in data)
+ className += " chatzilla-bold";
+ if ("isItalic" in data)
+ className += " chatzilla-italic";
+ if ("isOverline" in data)
+ className += " chatzilla-overline";
+ if ("isStrikethrough" in data)
+ className += " chatzilla-strikethrough";
+ if ("isUnderline" in data)
+ className += " chatzilla-underline";
+ }
+ return className;
+}
+