diff --git a/client/css/style.css b/client/css/style.css index f3e20e0c..2d8b281c 100644 --- a/client/css/style.css +++ b/client/css/style.css @@ -915,6 +915,18 @@ button { .bg-14 { background: #aaa; } .bg-15 { background: #ddd; } +.irc-bold { + font-weight: bold; +} + +.irc-underline { + text-decoration: underline; +} + +.irc-italic { + font-style:italic +} + /** * TrackpadScrollEmulator * Version: 1.0.6 diff --git a/client/js/libs/handlebars/parse.js b/client/js/libs/handlebars/parse.js index b0950d54..a8e6a770 100644 --- a/client/js/libs/handlebars/parse.js +++ b/client/js/libs/handlebars/parse.js @@ -48,46 +48,96 @@ function uri(text) { }); } -var regex = { - color: /\003([0-9]{1,2})[,]?([0-9]{1,2})?([^\003]+)/, - terminator: /\x0D/, - styles: [ - [/\002([^\002]+)(\002)?/, ["", ""]], - [/\037([^\037]+)(\037)?/, ["", ""]], - ] + +/** + * MIRC compliant colour and style parser + * Unfortuanately this is a non trivial operation + * See this branch for source and tests + * https://github.com/megawac/irc-style-parser/tree/shout + */ +var styleCheck_Re = /[\x00-\x1F]/, + back_re = /^(\d{1,2})(,(\d{1,2}))?/, + colourKey = "\x03", colour_re = /\x03/g, + // breaks all open styles ^O (\x0F) + styleBreak = "\x0F"; + + +var styleTemplate = function(settings) { + return "" + settings.text + ""; }; -function colors(text) { - if (!text) { - return text; - } - if (regex.terminator.test(text)) { - return $.map(text.split(regex.terminator), colors); - } - if (regex.color.test(text)) { - var match, bg; - while (match = regex.color.exec(text)) { - var color = "color-" + match[1]; - if (match[2]) { - bg = match[2]; - } - if (bg) { - color += " bg-" + bg; - } - var text = text.replace( - match[0], - "" + match[3] + "" - ); - } - } - for (var i in regex.styles) { - var pattern = regex.styles[i][0]; - var style = regex.styles[i][1]; - if (pattern.test(text)) { - var match; - while (match = pattern.exec(text)) { - text = text.replace(match[0], style[0] + match[1] + style[1]); - } - } - } - return text; + +var styles = [ + ["normal", "\x00", ""], ["underline", "\x1F"], + ["bold", "\x02"], ["italic", "\x1D"] +].map(function(style) { + var escaped = encodeURI(style[1]).replace("%", "\\x"); + return { + name: style[0], + style: style[2] != null ? style[2] : "irc-" + style[0], + key: style[1], + keyregex: new RegExp(escaped + "(.*?)(" + escaped + "|$)") + }; +}); + +//http://www.mirc.com/colors.html +var colourMap = {}; +for (var colour = 0; colour < 16; colour++) { + colourMap[colour] = { + fore: "color-" + colour, + back: "bg-" + colour + }; +} + +function colors(line) { + // http://www.mirc.com/colors.html + // http://www.aviran.org/stripremove-irc-client-control-characters/ + // https://github.com/perl6/mu/blob/master/examples/rules/Grammar-IRC.pm + // regexs are cruel to parse this thing + + // already done? + if (!styleCheck_Re.test(line)) return line; + + // split up by the irc style break character ^O + if (line.indexOf(styleBreak) >= 0) { + return line.split(styleBreak).map(colors).join(""); + } + + var result = line; + var parseArr = result.split(colourKey); + var text, match, colour, background = ""; + for (var i = 0; i < parseArr.length; i++) { + text = parseArr[i]; + match = text.match(back_re); + colour = match && colourMap[+match[1]]; + if (!match || !colour) { + // ^C (no colour) ending. Escape current colour and carry on + background = ""; + continue; + } + // set the background colour + // we don't overide the background local var to support nesting + if (colourMap[+match[3]]) { + background = " " + colourMap[+match[3]].back; + } + // update the parsed text result + result = result.replace(colourKey + text, styleTemplate({ + style: colour.fore + background, + text: text.slice(match[0].length) + })); + } + + // Matching styles (italics/bold/underline) + // if only colours were this easy... + styles.forEach(function(style) { + if (result.indexOf(style.key) < 0) return; + result = result.replace(style.keyregex, function(match, text) { + return styleTemplate({ + "style": style.style, + "text": text + }); + }); + }); + + //replace the reminent colour terminations and be done with it + return result.replace(colour_re, ""); }