Import from 1.9a8 tarball
[mozilla-extra.git] / extensions / irc / xul / content / mungers.js
blobc9643a1810c3ad9cc5192c34de6bd5092191cf5b
1 /* -*- Mode: C++; tab-width: 8; 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  * New Dimensions Consulting, Inc.
20  * Portions created by the Initial Developer are Copyright (C) 1999
21  * the Initial Developer. All Rights Reserved.
22  *
23  * Contributor(s):
24  *   Robert Ginda, rginda@ndcico.com, original author
25  *   Samuel Sieb, samuel@sieb.net, MIRC color codes
26  *
27  * Alternatively, the contents of this file may be used under the terms of
28  * either the GNU General Public License Version 2 or later (the "GPL"), or
29  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30  * in which case the provisions of the GPL or the LGPL are applicable instead
31  * of those above. If you wish to allow use of your version of this file only
32  * under the terms of either the GPL or the LGPL, and not to allow others to
33  * use your version of this file under the terms of the MPL, indicate your
34  * decision by deleting the provisions above and replace them with the notice
35  * and other provisions required by the GPL or the LGPL. If you do not delete
36  * the provisions above, a recipient may use your version of this file under
37  * the terms of any one of the MPL, the GPL or the LGPL.
38  *
39  * ***** END LICENSE BLOCK ***** */
41 /* This file contains the munger functions and rules used by ChatZilla.
42  * It's generally a bad idea to call munger functions inside ChatZilla for
43  * anything but munging (chat) output.
44  */
46 function initMunger()
48     /* linkRE: the general URL linkifier regular expression:
49      *
50      * - start with whitespace, non-word, or begining-of-line
51      * - then match:
52      *   - EITHER scheme (word + hyphen), colon, then lots of non-whitespace
53      *   - OR "www" followed by at least 2 sets of:
54      *     - "." plus some non-whitespace, non-"." characters
55      * - must end match with a word-break
56      * - include a "/" or "=" beyond break if present
57      * - end with whitespace, non-word, or end-of-line
58      */
59     client.linkRE =
60         /(?:\s|\W|^)((?:(\w[\w-]+):[^\s]+|www(\.[^.\s]+){2,})\b[\/=]?)(?=\s|\W|$)/;
62     const LOW_PRIORITY = 5;
63     const NORMAL_PRIORITY = 10;
64     const HIGH_PRIORITY = 15;
65     const HIGHER_PRIORITY = 20;
67     var munger = client.munger = new CMunger(insertHyphenatedWord);
68     // Special internal munger!
69     munger.addRule(".inline-buttons", /(\[\[.*?\]\])/, insertInlineButton,
70                    10, 5, false);
71     munger.addRule("quote", /(``|'')/, insertQuote,
72                    NORMAL_PRIORITY, NORMAL_PRIORITY);
73     munger.addRule("bold", /(?:[\s(\[]|^)(\*[^*()]*\*)(?:[\s\]).,;!\?]|$)/,
74                    "chatzilla-bold", NORMAL_PRIORITY, NORMAL_PRIORITY);
75     munger.addRule("underline", /(?:[\s(\[]|^)(\_[^_()]*\_)(?:[\s\]).,;!\?]|$)/,
76                    "chatzilla-underline", NORMAL_PRIORITY, NORMAL_PRIORITY);
77     munger.addRule("italic", /(?:\s|^)(\/[^\/()]*\/)(?:[\s.,]|$)/,
78                    "chatzilla-italic", NORMAL_PRIORITY, NORMAL_PRIORITY);
79     /* allow () chars inside |code()| blocks */
80     munger.addRule("teletype", /(?:\s|^)(\|[^|]*\|)(?:[\s.,]|$)/,
81                    "chatzilla-teletype", NORMAL_PRIORITY, NORMAL_PRIORITY);
82     munger.addRule(".mirc-colors", /(\x03((\d{1,2})(,\d{1,2}|)|))/,
83                    mircChangeColor, NORMAL_PRIORITY, NORMAL_PRIORITY);
84     munger.addRule(".mirc-bold", /(\x02)/, mircToggleBold,
85                    NORMAL_PRIORITY, NORMAL_PRIORITY);
86     munger.addRule(".mirc-underline", /(\x1f)/, mircToggleUnder,
87                    NORMAL_PRIORITY, NORMAL_PRIORITY);
88     munger.addRule(".mirc-color-reset", /(\x0f)/, mircResetColor,
89                    NORMAL_PRIORITY, NORMAL_PRIORITY);
90     munger.addRule(".mirc-reverse", /(\x16)/, mircReverseColor,
91                    NORMAL_PRIORITY, NORMAL_PRIORITY);
92     munger.addRule("ctrl-char", /([\x01-\x1f])/, showCtrlChar,
93                    NORMAL_PRIORITY, NORMAL_PRIORITY);
94     munger.addRule("link", client.linkRE, insertLink, NORMAL_PRIORITY, HIGH_PRIORITY);
96     // This has a higher starting priority so as to get it to match before the
97     // normal link, which won't know about mailto and then fail.
98     munger.addRule(".mailto",
99        /(?:\s|\W|^)((mailto:)?[^:;\\<>\[\]()\'\"\s\u201d]+@[^.<>\[\]()\'\"\s\u201d]+\.[^<>\[\]()\'\"\s\u201d]+)/i,
100                    insertMailToLink, NORMAL_PRIORITY, HIGHER_PRIORITY, false);
101     munger.addRule("bugzilla-link",
102                    /(?:\s|\W|^)(bug\s+(?:#?\d+|#[^\s,]{1,20}))/i,
103                    insertBugzillaLink, NORMAL_PRIORITY, NORMAL_PRIORITY);
104     munger.addRule("channel-link",
105                 /(?:\s|\W|^)[@%+]?(#[^<>,\[\](){}\"\s\u201d]*[^:,.<>\[\](){}\'\"\s\u201d])/i,
106                    insertChannelLink, NORMAL_PRIORITY, NORMAL_PRIORITY);
107     munger.addRule("talkback-link", /(?:\W|^)(TB\d{8,}[A-Z]?)(?:\W|$)/,
108                    insertTalkbackLink, NORMAL_PRIORITY, NORMAL_PRIORITY);
110     munger.addRule("face",
111          /((^|\s)(?:[>]?[B8=:;(xX][~']?[-^v"]?(?:[)|(PpSs0oO\?\[\]\/\\]|D+)|>[-^v]?\)|[oO9][._][oO9])(\s|$))/,
112          insertSmiley, NORMAL_PRIORITY, NORMAL_PRIORITY);
113     munger.addRule("rheet", /(?:\s|\W|^)(rhee+t\!*)(?:\s|$)/i, insertRheet, 10, 10);
114     munger.addRule("word-hyphenator",
115                    new RegExp ("(\\S{" + client.MAX_WORD_DISPLAY + ",})"),
116                    insertHyphenatedWord, LOW_PRIORITY, NORMAL_PRIORITY);
118     client.enableColors = client.prefs["munger.colorCodes"];
119     var branch = client.prefManager.prefBranch;
120     for (var entry in munger.entries)
121     {
122         if (!isinstance(munger.entries[entry], Object))
123             continue;
125         for (var rule in munger.entries[entry])
126         {
127             if (rule[0] == ".")
128                 continue;
130             try
131             {
132                 munger.entries[entry][rule].enabled =
133                     branch.getBoolPref("munger." + rule);
134             }
135             catch (ex)
136             {
137                 // nada
138             }
139         }
140     }
143 function insertLink(matchText, containerTag, data, mungerEntry)
145     var href;
146     var linkText;
148     var trailing;
149     ary = matchText.match(/([.,?]+)$/);
150     if (ary)
151     {
152         linkText = RegExp.leftContext;
153         trailing = ary[1];
154     }
155     else
156     {
157         linkText = matchText;
158     }
160     var ary = linkText.match(/^(\w[\w-]+):/);
161     if (ary)
162     {
163         if (!("schemes" in client))
164         {
165             var pfx = "@mozilla.org/network/protocol;1?name=";
166             var len = pfx.length;
168             client.schemes = new Object();
169             for (var c in Components.classes)
170             {
171                 if (c.indexOf(pfx) == 0)
172                     client.schemes[c.substr(len)] = true;
173             }
174         }
176         if (!(ary[1] in client.schemes))
177         {
178             mungerEntry.enabled = false;
179             client.munger.munge(matchText, containerTag, data);
180             mungerEntry.enabled = true;
181             return;
182         }
184         href = linkText;
185     }
186     else
187     {
188         href = "http://" + linkText;
189     }
191     /* This gives callers to the munger control over URLs being logged; the
192      * channel topic munger uses this, as well as the "is important" checker.
193      * If either of |dontLogURLs| or |noStateChange| is present and true, we
194      * don't log.
195      */
196     if ((!("dontLogURLs" in data) || !data.dontLogURLs) &&
197         (!("noStateChange" in data) || !data.noStateChange))
198     {
199         var max = client.prefs["urls.store.max"];
200         if (client.prefs["urls.list"].unshift(href) > max)
201             client.prefs["urls.list"].pop();
202         client.prefs["urls.list"].update();
203     }
205     var anchor = document.createElementNS("http://www.w3.org/1999/xhtml",
206                                           "html:a");
207     var mircRE = /\x1f|\x02|\x0f|\x16|\x03([0-9]{1,2}(,[0-9]{1,2})?)?/g;
208     anchor.setAttribute("href", href.replace(mircRE, ""));
210     // Carry over formatting.
211     var otherFormatting = calcClass(data);
212     if (otherFormatting)
213         anchor.setAttribute("class", "chatzilla-link " + otherFormatting);
214     else
215         anchor.setAttribute("class", "chatzilla-link");
217     anchor.setAttribute("target", "_content");
218     mungerEntry.enabled = false;
219     data.inLink = true;
220     client.munger.munge(linkText, anchor, data);
221     mungerEntry.enabled = true;
222     delete data.inLink;
223     containerTag.appendChild(anchor);
224     if (trailing)
225         insertHyphenatedWord(trailing, containerTag, data);
229 function insertMailToLink(matchText, containerTag, eventData, mungerEntry)
231     if (("inLink" in eventData) && eventData.inLink)
232     {
233         mungerEntry.enabled = false;
234         client.munger.munge(matchText, containerTag, eventData);
235         mungerEntry.enabled = true;
236         return;
237     }
239     var href;
241     if (matchText.indexOf("mailto:") != 0)
242         href = "mailto:" + matchText;
243     else
244         href = matchText;
246     var anchor = document.createElementNS("http://www.w3.org/1999/xhtml",
247                                           "html:a");
248     var mircRE = /\x1f|\x02|\x0f|\x16|\x03([0-9]{1,2}(,[0-9]{1,2})?)?/g;
249     anchor.setAttribute("href", href.replace(mircRE, ""));
251     // Carry over formatting.
252     var otherFormatting = calcClass(eventData);
253     if (otherFormatting)
254         anchor.setAttribute("class", "chatzilla-link " + otherFormatting);
255     else
256         anchor.setAttribute("class", "chatzilla-link");
258     //anchor.setAttribute ("target", "_content");
259     mungerEntry.enabled = false;
260     eventData.inLink = true;
261     client.munger.munge(matchText, anchor, eventData);
262     mungerEntry.enabled = true;
263     delete eventData.inLink;
264     containerTag.appendChild(anchor);
268 function insertChannelLink(matchText, containerTag, eventData, mungerEntry)
270     if (("inLink" in eventData) && eventData.inLink)
271     {
272         mungerEntry.enabled = false;
273         client.munger.munge(matchText, containerTag, eventData);
274         mungerEntry.enabled = true;
275         return;
276     }
278     var bogusChannels =
279         /^#(include|error|define|if|ifdef|else|elsif|endif|\d+)$/i;
281     if (!("network" in eventData) || !eventData.network ||
282         matchText.search(bogusChannels) != -1)
283     {
284         containerTag.appendChild(document.createTextNode(matchText));
285         return;
286     }
288     var encodedMatchText = fromUnicode(matchText, eventData.sourceObject);
289     var anchor = document.createElementNS("http://www.w3.org/1999/xhtml",
290                                           "html:a");
291     anchor.setAttribute("href", eventData.network.getURL() +
292                         ecmaEscape(encodedMatchText));
294     // Carry over formatting.
295     var otherFormatting = calcClass(eventData);
296     if (otherFormatting)
297         anchor.setAttribute("class", "chatzilla-link " + otherFormatting);
298     else
299         anchor.setAttribute("class", "chatzilla-link");
301     mungerEntry.enabled = false;
302     eventData.inLink = true;
303     client.munger.munge(matchText, anchor, eventData);
304     mungerEntry.enabled = true;
305     delete eventData.inLink;
306     containerTag.appendChild(anchor);
309 function insertTalkbackLink(matchText, containerTag, eventData, mungerEntry)
311     if (("inLink" in eventData) && eventData.inLink)
312     {
313         mungerEntry.enabled = false;
314         client.munger.munge(matchText, containerTag, eventData);
315         mungerEntry.enabled = true;
316         return;
317     }
319     var anchor = document.createElementNS("http://www.w3.org/1999/xhtml",
320                                           "html:a");
322     anchor.setAttribute("href", "http://talkback-public.mozilla.org/" +
323                         "search/start.jsp?search=2&type=iid&id=" + matchText);
325     // Carry over formatting.
326     var otherFormatting = calcClass(eventData);
327     if (otherFormatting)
328         anchor.setAttribute("class", "chatzilla-link " + otherFormatting);
329     else
330         anchor.setAttribute("class", "chatzilla-link");
332     mungerEntry.enabled = false;
333     client.munger.munge(matchText, anchor, eventData);
334     mungerEntry.enabled = true;
335     containerTag.appendChild(anchor);
338 function insertBugzillaLink (matchText, containerTag, eventData, mungerEntry)
340     if (("inLink" in eventData) && eventData.inLink)
341     {
342         mungerEntry.enabled = false;
343         client.munger.munge(matchText, containerTag, eventData);
344         mungerEntry.enabled = true;
345         return;
346     }
348     var bugURL;
349     if (eventData.channel)
350         bugURL = eventData.channel.prefs["bugURL"];
351     else if (eventData.network)
352         bugURL = eventData.network.prefs["bugURL"];
353     else
354         bugURL = client.prefs["bugURL"];
356     if (bugURL.length > 0)
357     {
358         var idOrAlias = matchText.match(/bug\s+#?(\d+|[^\s,]{1,20})/i)[1];
359         var anchor = document.createElementNS("http://www.w3.org/1999/xhtml",
360                                               "html:a");
362         anchor.setAttribute("href", bugURL.replace("%s", idOrAlias));
363         // Carry over formatting.
364         var otherFormatting = calcClass(eventData);
365         if (otherFormatting)
366             anchor.setAttribute("class", "chatzilla-link " + otherFormatting);
367         else
368             anchor.setAttribute("class", "chatzilla-link");
370         anchor.setAttribute("target", "_content");
371         mungerEntry.enabled = false;
372         eventData.inLink = true;
373         client.munger.munge(matchText, anchor, eventData);
374         mungerEntry.enabled = true;
375         delete eventData.inLink;
376         containerTag.appendChild(anchor);
377     }
378     else
379     {
380         mungerEntry.enabled = false;
381         client.munger.munge(matchText, containerTag, eventData);
382         mungerEntry.enabled = true;
383     }
386 function insertRheet(matchText, containerTag, eventData, mungerEntry)
388     if (("inLink" in eventData) && eventData.inLink)
389     {
390         mungerEntry.enabled = false;
391         client.munger.munge(matchText, containerTag, eventData);
392         mungerEntry.enabled = true;
393         return;
394     }
396     var anchor = document.createElementNS("http://www.w3.org/1999/xhtml",
397                                           "html:a");
398     anchor.setAttribute("href",
399                         "http://ftp.mozilla.org/pub/mozilla.org/mozilla/libraries/bonus-tracks/rheet.wav");
400     anchor.setAttribute("class", "chatzilla-rheet chatzilla-link");
401     //anchor.setAttribute ("target", "_content");
402     insertHyphenatedWord(matchText, anchor, eventData);
403     containerTag.appendChild(anchor);
406 function insertQuote (matchText, containerTag)
408     if (matchText == "``")
409         containerTag.appendChild(document.createTextNode("\u201c"));
410     else
411         containerTag.appendChild(document.createTextNode("\u201d"));
414 function insertSmiley(emoticon, containerTag)
416     var type = "error";
418     if (emoticon.search(/\>[-^v]?\)/) != -1)
419         type = "face-alien";
420     else if (emoticon.search(/\>[=:;][-^v]?[(|]/) != -1)
421         type = "face-angry";
422     else if (emoticon.search(/[=:;][-^v]?[Ss\\\/]/) != -1)
423         type = "face-confused";
424     else if (emoticon.search(/[B8][-^v]?[)\]]/) != -1)
425         type = "face-cool";
426     else if (emoticon.search(/[=:;][~'][-^v]?\(/) != -1)
427         type = "face-cry";
428     else if (emoticon.search(/o[._]O/) != -1)
429         type = "face-dizzy";
430     else if (emoticon.search(/O[._]o/) != -1)
431         type = "face-dizzy-back";
432     else if (emoticon.search(/o[._]o|O[._]O/) != -1)
433         type = "face-eek";
434     else if (emoticon.search(/\>[=:;][-^v]?D/) != -1)
435         type = "face-evil";
436     else if (emoticon.search(/[=:;][-^v]?DD/) != -1)
437         type = "face-lol";
438     else if (emoticon.search(/[=:;][-^v]?D/) != -1)
439         type = "face-laugh";
440     else if (emoticon.search(/\([-^v]?D|[xX][-^v]?D/) != -1)
441         type = "face-rofl";
442     else if (emoticon.search(/[=:;][-^v]?\|/) != -1)
443         type = "face-normal";
444     else if (emoticon.search(/[=:;][-^v]?\?/) != -1)
445         type = "face-question";
446     else if (emoticon.search(/[=:;]"[)\]]/) != -1)
447         type = "face-red";
448     else if (emoticon.search(/9[._]9/) != -1)
449         type = "face-rolleyes";
450     else if (emoticon.search(/[=:;][-^v]?[(\[]/) != -1)
451         type = "face-sad";
452     else if (emoticon.search(/[=:][-^v]?[)\]]/) != -1)
453         type = "face-smile";
454     else if (emoticon.search(/[=:;][-^v]?[0oO]/) != -1)
455         type = "face-surprised";
456     else if (emoticon.search(/[=:;][-^v]?[pP]/) != -1)
457         type = "face-tongue";
458     else if (emoticon.search(/;[-^v]?[)\]]/) != -1)
459         type = "face-wink";
461     if (type == "error")
462     {
463         // We didn't actually match anything, so it'll be a too-generic match
464         // from the munger RegExp.
465         containerTag.appendChild(document.createTextNode(emoticon));
466         return;
467     }
469     var span = document.createElementNS ("http://www.w3.org/1999/xhtml",
470                                          "html:span");
472     /* create a span to hold the emoticon text */
473     span.setAttribute ("class", "chatzilla-emote-txt");
474     span.setAttribute ("type", type);
475     span.appendChild (document.createTextNode (emoticon));
476     containerTag.appendChild (span);
478     /* create an empty span after the text.  this span will have an image added
479      * after it with a chatzilla-emote:after css rule. using
480      * chatzilla-emote-txt:after is not good enough because it does not allow us
481      * to turn off the emoticon text, but keep the image.  ie.
482      * chatzilla-emote-txt { display: none; } turns off
483      * chatzilla-emote-txt:after as well.*/
484     span = document.createElementNS ("http://www.w3.org/1999/xhtml",
485                                      "html:span");
486     span.setAttribute ("class", "chatzilla-emote");
487     span.setAttribute ("type", type);
488     span.setAttribute ("title", emoticon);
489     containerTag.appendChild (span);
493 function mircChangeColor (colorInfo, containerTag, data)
495     /* If colors are disabled, the caller doesn't want colors specifically, or
496      * the caller doesn't want any state-changing effects, we drop out.
497      */
498     if (!client.enableColors ||
499         (("noMircColors" in data) && data.noMircColors) ||
500         (("noStateChange" in data) && data.noStateChange))
501     {
502         return;
503     }
505     var ary = colorInfo.match (/.(\d{1,2}|)(,(\d{1,2})|)/);
507     // Do we have a BG color specified...?
508     if (!arrayHasElementAt(ary, 1) || !ary[1])
509     {
510         // Oops, no colors.
511         delete data.currFgColor;
512         delete data.currBgColor;
513         return;
514     }
516     var fgColor = String(Number(ary[1]) % 16);
518     if (fgColor.length == 1)
519         data.currFgColor = "0" + fgColor;
520     else
521         data.currFgColor = fgColor;
523     // Do we have a BG color specified...?
524     if (arrayHasElementAt(ary, 3) && ary[3])
525     {
526         var bgColor = String(Number(ary[3]) % 16);
528         if (bgColor.length == 1)
529             data.currBgColor = "0" + bgColor;
530         else
531             data.currBgColor = bgColor;
532     }
534     data.hasColorInfo = true;
537 function mircToggleBold (colorInfo, containerTag, data)
539     if (!client.enableColors ||
540         (("noMircColors" in data) && data.noMircColors) ||
541         (("noStateChange" in data) && data.noStateChange))
542     {
543         return;
544     }
546     if ("isBold" in data)
547         delete data.isBold;
548     else
549         data.isBold = true;
550     data.hasColorInfo = true;
553 function mircToggleUnder (colorInfo, containerTag, data)
555     if (!client.enableColors ||
556         (("noMircColors" in data) && data.noMircColors) ||
557         (("noStateChange" in data) && data.noStateChange))
558     {
559         return;
560     }
562     if ("isUnderline" in data)
563         delete data.isUnderline;
564     else
565         data.isUnderline = true;
566     data.hasColorInfo = true;
569 function mircResetColor (text, containerTag, data)
571     if (!client.enableColors ||
572         (("noMircColors" in data) && data.noMircColors) ||
573         (("noStateChange" in data) && data.noStateChange) ||
574         !("hasColorInfo" in data))
575     {
576         return;
577     }
579     delete data.currFgColor;
580     delete data.currBgColor;
581     delete data.isBold;
582     delete data.isUnderline;
583     delete data.hasColorInfo;
586 function mircReverseColor (text, containerTag, data)
588     if (!client.enableColors ||
589         (("noMircColors" in data) && data.noMircColors) ||
590         (("noStateChange" in data) && data.noStateChange))
591     {
592         return;
593     }
595     var tempColor = ("currFgColor" in data ? data.currFgColor : "");
597     if ("currBgColor" in data)
598         data.currFgColor = data.currBgColor;
599     else
600         delete data.currFgColor;
601     if (tempColor)
602         data.currBgColor = tempColor;
603     else
604         delete data.currBgColor;
605     data.hasColorInfo = true;
608 function showCtrlChar(c, containerTag)
610     var span = document.createElementNS ("http://www.w3.org/1999/xhtml",
611                                          "html:span");
612     span.setAttribute ("class", "chatzilla-control-char");
613     if (c == "\t")
614     {
615         containerTag.appendChild(document.createTextNode(c));
616         return;
617     }
619     var ctrlStr = c.charCodeAt(0).toString(16);
620     if (ctrlStr.length < 2)
621         ctrlStr = "0" + ctrlStr;
622     span.appendChild (document.createTextNode ("0x" + ctrlStr));
623     containerTag.appendChild (span);
626 function insertHyphenatedWord(longWord, containerTag, data)
628     var wordParts = splitLongWord(longWord, client.MAX_WORD_DISPLAY);
629     var newClass = "";
630     if (data && ("hasColorInfo" in data))
631         newClass = calcClass(data);
633     for (var i = 0; i < wordParts.length; ++i)
634     {
635         if (i > 0)
636         {
637             var wbr = document.createElementNS("http://www.w3.org/1999/xhtml",
638                                                "html:wbr");
639             containerTag.appendChild(wbr);
640         }
642         if (newClass)
643         {
644             var newTag = document.createElementNS(NS_XHTML, "html:span");
645             newTag.setAttribute("class", newClass);
646             newTag.appendChild(document.createTextNode(wordParts[i]));
647             containerTag.appendChild(newTag);
648         }
649         else
650         {
651             delete data.hasColorInfo;
652             containerTag.appendChild(document.createTextNode(wordParts[i]));
653         }
654     }
657 function insertInlineButton(text, containerTag, data)
659     var ary = text.match(/\[\[([^\]]+)\]\[([^\]]+)\]\[([^\]]+)\]\]/);
661     if (!ary)
662     {
663         containerTag.appendChild(document.createTextNode(text));
664         return;
665     }
667     var label = ary[1];
668     var title = ary[2];
669     var command = ary[3];
671     var link = document.createElementNS("http://www.w3.org/1999/xhtml", "a");
672     link.setAttribute("href", "x-cz-command:" + encodeURI(command));
673     link.setAttribute("title", title);
674     link.setAttribute("class", "chatzilla-link");
675     link.appendChild(document.createTextNode(label));
677     containerTag.appendChild(document.createTextNode("["));
678     containerTag.appendChild(link);
679     containerTag.appendChild(document.createTextNode("]"));
682 function calcClass(data)
684     var className = "";
685     if ("hasColorInfo" in data)
686     {
687         if ("currFgColor" in data)
688             className += " chatzilla-fg" + data.currFgColor;
689         if ("currBgColor" in data)
690             className += " chatzilla-bg" + data.currBgColor;
691         if ("isBold" in data)
692             className += " chatzilla-bold";
693         if ("isUnderline" in data)
694             className += " chatzilla-underline";
695     }
696     return className;