|
| 1 | +'use strict'; |
| 2 | + |
| 3 | +/*! (c) 2013-2018 Andrea Giammarchi (ISC) */ |
| 4 | +/** |
| 5 | + * Fully inspired by the work of John Gruber |
| 6 | + * <http://daringfireball.net/projects/markdown/> |
| 7 | + */ |
| 8 | +for (var |
| 9 | + isNodeJS = typeof process === 'object' && !process.browser, |
| 10 | + parse = isNodeJS ? |
| 11 | + // on NodeJS simply fallback to echomd |
| 12 | + (function (echomd, map) { |
| 13 | + function parse(value) { |
| 14 | + return typeof value === 'string' ? |
| 15 | + echomd(value) : value; |
| 16 | + } |
| 17 | + return function () { |
| 18 | + return map.call(arguments, parse); |
| 19 | + }; |
| 20 | + }(require('echomd').raw, [].map)) : |
| 21 | + // on browsers implement some %cmagic%c |
| 22 | + // The current algorithm is based on two passes: |
| 23 | + // 1. collect all info ordered by string index |
| 24 | + // 2. transform surrounding with special %c chars |
| 25 | + // Info are grouped together whenever is possible |
| 26 | + // since the console does not support one style per %c |
| 27 | + (function () { |
| 28 | + return function (txt) { |
| 29 | + var |
| 30 | + code = (Object.create || Object)(null), |
| 31 | + multiLineCode = transform.multiLineCode.re, |
| 32 | + singleLineCode = transform.singleLineCode.re, |
| 33 | + storeAndHide = function ($0, $1, $2, $3) { |
| 34 | + $3 = $3.replace(/%c/g, '%%c'); |
| 35 | + return $1 + $2 + (code[$3] = md5Base64($3)) + $2; |
| 36 | + }, |
| 37 | + restoreHidden = function ($0, $1, $2, $3) { |
| 38 | + return $1 + '%c' + getSource($3, code) + '%c'; |
| 39 | + }, |
| 40 | + out = [], |
| 41 | + args, i, j, length, css, key; |
| 42 | + |
| 43 | + // match and hide possible code (which should not be parsed) |
| 44 | + match(txt, 'multiLineCode', out); |
| 45 | + txt = txt.replace(multiLineCode, storeAndHide); |
| 46 | + match(txt, 'singleLineCode', out); |
| 47 | + txt = txt.replace(singleLineCode, storeAndHide); |
| 48 | + |
| 49 | + // find all special cases preserving the order |
| 50 | + // in which are these found |
| 51 | + match(txt, 'header2', out); |
| 52 | + match(txt, 'header1', out); |
| 53 | + match(txt, 'blink', out); |
| 54 | + match(txt, 'bold', out); |
| 55 | + match(txt, 'dim', out); |
| 56 | + match(txt, 'hidden', out); |
| 57 | + match(txt, 'reverse', out); |
| 58 | + match(txt, 'strike', out); |
| 59 | + match(txt, 'underline', out); |
| 60 | + match(txt, 'color', out); |
| 61 | + |
| 62 | + // transform using all info |
| 63 | + |
| 64 | + // - - - or ___ or * * * with or without space in between |
| 65 | + txt = txt.replace(/^[ ]{0,2}([ ]?[*_-][ ]?){3,}[ \t]*$/gm, line); |
| 66 | + |
| 67 | + // ## Header |
| 68 | + txt = replace(txt, 'header2'); |
| 69 | + |
| 70 | + // # Header |
| 71 | + txt = replace(txt, 'header1'); |
| 72 | + |
| 73 | + // :blink: *bold* -dim- ?hidden? !reverse! _underline_ ~strike~ |
| 74 | + txt = replace(txt, 'blink'); |
| 75 | + txt = replace(txt, 'bold'); |
| 76 | + txt = replace(txt, 'dim'); |
| 77 | + txt = replace(txt, 'hidden'); |
| 78 | + txt = replace(txt, 'reverse'); |
| 79 | + txt = replace(txt, 'strike'); |
| 80 | + txt = replace(txt, 'underline'); |
| 81 | + |
| 82 | + // * list bullets |
| 83 | + txt = txt.replace(/^([ \t]{1,})[*+-]([ \t]{1,})/gm, '$1•$2'); |
| 84 | + |
| 85 | + // > simple quotes |
| 86 | + txt = txt.replace(/^[ \t]*>([ \t]?)/gm, function ($0, $1) { |
| 87 | + return Array($1.length + 1).join('▌') + $1; |
| 88 | + }); |
| 89 | + |
| 90 | + // #RGBA(color) and !#RGBA(background-color) |
| 91 | + txt = replace(txt, 'color'); |
| 92 | + |
| 93 | + // cleanup duplicates |
| 94 | + txt = txt.replace(/(%c)+/g, '%c'); |
| 95 | + |
| 96 | + // put back code |
| 97 | + txt = txt.replace(singleLineCode, restoreHidden); |
| 98 | + txt = txt.replace(multiLineCode, restoreHidden); |
| 99 | + |
| 100 | + // create list of arguments to style the console |
| 101 | + args = [txt]; |
| 102 | + length = out.length; |
| 103 | + for (i = 0; i < length; i++) { |
| 104 | + css = ''; |
| 105 | + key = ''; |
| 106 | + // group styles by type (start/end) |
| 107 | + for (j = i; j < length; j++) { |
| 108 | + i = j; // update the i to move fast-forward |
| 109 | + if (j in out) { |
| 110 | + // first match or same kind of operation (start/end) |
| 111 | + if (!key || (key === out[j].k)) { |
| 112 | + key = out[j].k; |
| 113 | + css += out[j].v; |
| 114 | + } else { |
| 115 | + i--; // if key changed, next loop should update |
| 116 | + break; |
| 117 | + } |
| 118 | + } |
| 119 | + } |
| 120 | + if (css) args.push(css); |
| 121 | + } |
| 122 | + return args; |
| 123 | + }; |
| 124 | + }()) |
| 125 | + , |
| 126 | + line = Array(33).join('─'), |
| 127 | + // just using same name used in echomd, not actual md5 |
| 128 | + md5Base64 = function (txt) { |
| 129 | + for (var out = [], i = 0; i < txt.length; i++) { |
| 130 | + out[i] = txt.charCodeAt(i).toString(32); |
| 131 | + } |
| 132 | + return out.join('').slice(0, txt.length); |
| 133 | + }, |
| 134 | + getSource = function (hash, code) { |
| 135 | + for (var source in code) { |
| 136 | + if (code[source] === hash) { |
| 137 | + return source; |
| 138 | + } |
| 139 | + } |
| 140 | + }, |
| 141 | + commonReplacer = function ($0, $1, $2, $3) { |
| 142 | + return '%c' + $2 + $3 + '%c'; |
| 143 | + }, |
| 144 | + match = function (txt, what, stack) { |
| 145 | + var info = transform[what], i, match; |
| 146 | + while (match = info.re.exec(txt)) { |
| 147 | + i = match.index; |
| 148 | + stack[i] = { |
| 149 | + k: 'start', |
| 150 | + v: typeof info.start === 'string' ? |
| 151 | + info.start : info.start(match) |
| 152 | + }; |
| 153 | + i = i + match[0].length - 1; |
| 154 | + stack[i] = { |
| 155 | + k: 'end', |
| 156 | + v: typeof info.end === 'string' ? |
| 157 | + info.end : info.end(match) |
| 158 | + }; |
| 159 | + } |
| 160 | + }, |
| 161 | + replace = function (txt, what) { |
| 162 | + var info = transform[what]; |
| 163 | + return txt.replace(info.re, info.place); |
| 164 | + }, |
| 165 | + transform = { |
| 166 | + blink: { |
| 167 | + re: /(\:{1,2})(?=\S)(.*?)(\S)\1/g, |
| 168 | + place: commonReplacer, |
| 169 | + start: 'padding:0 2px;border:1px solid darkslategray;text-shadow:0 0 2px darkslategray;', |
| 170 | + end: 'padding:none;border:none;text-shadow:none;' |
| 171 | + }, |
| 172 | + bold: { |
| 173 | + re: /(\*{1,2})(?=\S)(.*?)(\S)\1/g, |
| 174 | + place: commonReplacer, |
| 175 | + start: 'font-weight:bold;', |
| 176 | + end: 'font-weight:default;' |
| 177 | + }, |
| 178 | + color: { |
| 179 | + re: /(!?)#([a-zA-Z0-9]{3,8})\((.+?)\)(?!\))/g, |
| 180 | + place: function ($0, bg, rgb, txt) { |
| 181 | + return '%c' + txt + '%c'; |
| 182 | + }, |
| 183 | + start: function (match) { |
| 184 | + return (match[1] ? 'background-' : '') + 'color:' + |
| 185 | + (/^[a-fA-F0-9]{3,8}$/.test(match[2]) ? '#' : '') + |
| 186 | + match[2] + ';'; |
| 187 | + }, |
| 188 | + end: function (match) { |
| 189 | + return (match[1] ? 'background-' : '') + 'color:initial;'; |
| 190 | + } |
| 191 | + }, |
| 192 | + dim: { |
| 193 | + re: /(-{1,2})(?=\S)(.*?)(\S)\1/g, |
| 194 | + place: commonReplacer, |
| 195 | + start: 'color:dimgray;', |
| 196 | + end: 'color:none;' |
| 197 | + }, |
| 198 | + header1: { |
| 199 | + re: /^(\#[ \t]+)(.+?)[ \t]*\#*([\r\n]+|$)/gm, |
| 200 | + place: commonReplacer, |
| 201 | + start: 'font-weight:bold;font-size:1.6em;', |
| 202 | + end: 'font-weight:default;font-size:default;' |
| 203 | + }, |
| 204 | + header2: { |
| 205 | + re: /^(\#{2,6}[ \t]+)(.+?)[ \t]*\#*([\r\n]+|$)/gm, |
| 206 | + place: commonReplacer, |
| 207 | + start: 'font-weight:bold;font-size:1.3em;', |
| 208 | + end: 'font-weight:default;font-size:default;' |
| 209 | + }, |
| 210 | + hidden: { |
| 211 | + re: /(\?{1,2})(?=\S)(.*?)(\S)\1/g, |
| 212 | + place: commonReplacer, |
| 213 | + start: 'color:rgba(0,0,0,0);', |
| 214 | + end: 'color:none;' |
| 215 | + }, |
| 216 | + reverse: { |
| 217 | + re: /(\!{1,2})(?=\S)(.*?)(\S)\1/g, |
| 218 | + place: commonReplacer, |
| 219 | + start: 'padding:0 2px;background:darkslategray;color:lightgray;', |
| 220 | + end: 'padding:none;background:none;color:none;' |
| 221 | + }, |
| 222 | + multiLineCode: { |
| 223 | + re: /(^|[^\\])(`{2,})([\s\S]+?)\2(?!`)/g, |
| 224 | + start: 'font-family:monospace;', |
| 225 | + end: 'font-family:default;' |
| 226 | + }, |
| 227 | + singleLineCode: { |
| 228 | + re: /(^|[^\\])(`)(.+?)\2/gm, |
| 229 | + start: 'font-family:monospace;', |
| 230 | + end: 'font-family:default;' |
| 231 | + }, |
| 232 | + strike: { |
| 233 | + re: /(~{1,2})(?=\S)(.*?)(\S)\1/g, |
| 234 | + place: commonReplacer, |
| 235 | + start: 'text-decoration:line-through;', |
| 236 | + end: 'text-decoration:default;' |
| 237 | + }, |
| 238 | + underline: { |
| 239 | + re: /(_{1,2})(?=\S)(.*?)(\S)\1/g, |
| 240 | + place: commonReplacer, |
| 241 | + start: 'border-bottom:1px solid;', |
| 242 | + end: 'border-bottom:default;' |
| 243 | + } |
| 244 | + }, |
| 245 | + // 'error', 'info', 'log', 'warn' are overwritten |
| 246 | + // it is possible to use original method at any time |
| 247 | + // simply accessing console.methodName.raw( ... ) instead |
| 248 | + overwrite = function (method) { |
| 249 | + var original = console[method]; |
| 250 | + if (original) (consolemd[method] = isNodeJS ? |
| 251 | + function () { |
| 252 | + return original.apply(console, parse.apply(null, arguments)); |
| 253 | + } : |
| 254 | + function () { |
| 255 | + return arguments.length === 1 && typeof arguments[0] === 'string' ? |
| 256 | + original.apply(console, parse(arguments[0])) : |
| 257 | + original.apply(console, arguments); |
| 258 | + }).raw = function () { |
| 259 | + return original.apply(console, arguments); |
| 260 | + }; |
| 261 | + }, |
| 262 | + consolemd = {}, |
| 263 | + methods = ['error', 'info', 'log', 'warn'], |
| 264 | + key, |
| 265 | + i = 0; i < methods.length; i++ |
| 266 | +) { |
| 267 | + overwrite(methods[i]); |
| 268 | +} |
| 269 | +// if this is a CommonJS module |
| 270 | +try { |
| 271 | + overwrite = function (original) { |
| 272 | + return function () { |
| 273 | + return original.apply(console, arguments); |
| 274 | + }; |
| 275 | + }; |
| 276 | + for (key in console) { |
| 277 | + if (!consolemd.hasOwnProperty(key)) { |
| 278 | + consolemd[key] = overwrite(console[key]); |
| 279 | + } |
| 280 | + } |
| 281 | +} catch(e) { |
| 282 | + // otherwise replace global console methods |
| 283 | + for (i = 0; i < methods.length; i++) { |
| 284 | + key = methods[i]; |
| 285 | + if (!console[key].raw) { |
| 286 | + console[key] = consolemd[key]; |
| 287 | + } |
| 288 | + } |
| 289 | +} |
| 290 | + |
| 291 | +module.exports = consolemd; |
0 commit comments