You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

176 lines
4.2 KiB

4 years ago
  1. 'use strict'
  2. module.exports = ansiHTML
  3. // Reference to https://github.com/sindresorhus/ansi-regex
  4. var _regANSI = /(?:(?:\u001b\[)|\u009b)(?:(?:[0-9]{1,3})?(?:(?:;[0-9]{0,3})*)?[A-M|f-m])|\u001b[A-M]/
  5. var _defColors = {
  6. reset: ['fff', '000'], // [FOREGROUD_COLOR, BACKGROUND_COLOR]
  7. black: '000',
  8. red: 'ff0000',
  9. green: '209805',
  10. yellow: 'e8bf03',
  11. blue: '0000ff',
  12. magenta: 'ff00ff',
  13. cyan: '00ffee',
  14. lightgrey: 'f0f0f0',
  15. darkgrey: '888'
  16. }
  17. var _styles = {
  18. 30: 'black',
  19. 31: 'red',
  20. 32: 'green',
  21. 33: 'yellow',
  22. 34: 'blue',
  23. 35: 'magenta',
  24. 36: 'cyan',
  25. 37: 'lightgrey'
  26. }
  27. var _openTags = {
  28. '1': 'font-weight:bold', // bold
  29. '2': 'opacity:0.5', // dim
  30. '3': '<i>', // italic
  31. '4': '<u>', // underscore
  32. '8': 'display:none', // hidden
  33. '9': '<del>' // delete
  34. }
  35. var _closeTags = {
  36. '23': '</i>', // reset italic
  37. '24': '</u>', // reset underscore
  38. '29': '</del>' // reset delete
  39. }
  40. ;[0, 21, 22, 27, 28, 39, 49].forEach(function (n) {
  41. _closeTags[n] = '</span>'
  42. })
  43. /**
  44. * Converts text with ANSI color codes to HTML markup.
  45. * @param {String} text
  46. * @returns {*}
  47. */
  48. function ansiHTML (text) {
  49. // Returns the text if the string has no ANSI escape code.
  50. if (!_regANSI.test(text)) {
  51. return text
  52. }
  53. // Cache opened sequence.
  54. var ansiCodes = []
  55. // Replace with markup.
  56. var ret = text.replace(/\033\[(\d+)*m/g, function (match, seq) {
  57. var ot = _openTags[seq]
  58. if (ot) {
  59. // If current sequence has been opened, close it.
  60. if (!!~ansiCodes.indexOf(seq)) { // eslint-disable-line no-extra-boolean-cast
  61. ansiCodes.pop()
  62. return '</span>'
  63. }
  64. // Open tag.
  65. ansiCodes.push(seq)
  66. return ot[0] === '<' ? ot : '<span style="' + ot + ';">'
  67. }
  68. var ct = _closeTags[seq]
  69. if (ct) {
  70. // Pop sequence
  71. ansiCodes.pop()
  72. return ct
  73. }
  74. return ''
  75. })
  76. // Make sure tags are closed.
  77. var l = ansiCodes.length
  78. ;(l > 0) && (ret += Array(l + 1).join('</span>'))
  79. return ret
  80. }
  81. /**
  82. * Customize colors.
  83. * @param {Object} colors reference to _defColors
  84. */
  85. ansiHTML.setColors = function (colors) {
  86. if (typeof colors !== 'object') {
  87. throw new Error('`colors` parameter must be an Object.')
  88. }
  89. var _finalColors = {}
  90. for (var key in _defColors) {
  91. var hex = colors.hasOwnProperty(key) ? colors[key] : null
  92. if (!hex) {
  93. _finalColors[key] = _defColors[key]
  94. continue
  95. }
  96. if ('reset' === key) {
  97. if (typeof hex === 'string') {
  98. hex = [hex]
  99. }
  100. if (!Array.isArray(hex) || hex.length === 0 || hex.some(function (h) {
  101. return typeof h !== 'string'
  102. })) {
  103. throw new Error('The value of `' + key + '` property must be an Array and each item could only be a hex string, e.g.: FF0000')
  104. }
  105. var defHexColor = _defColors[key]
  106. if (!hex[0]) {
  107. hex[0] = defHexColor[0]
  108. }
  109. if (hex.length === 1 || !hex[1]) {
  110. hex = [hex[0]]
  111. hex.push(defHexColor[1])
  112. }
  113. hex = hex.slice(0, 2)
  114. } else if (typeof hex !== 'string') {
  115. throw new Error('The value of `' + key + '` property must be a hex string, e.g.: FF0000')
  116. }
  117. _finalColors[key] = hex
  118. }
  119. _setTags(_finalColors)
  120. }
  121. /**
  122. * Reset colors.
  123. */
  124. ansiHTML.reset = function () {
  125. _setTags(_defColors)
  126. }
  127. /**
  128. * Expose tags, including open and close.
  129. * @type {Object}
  130. */
  131. ansiHTML.tags = {}
  132. if (Object.defineProperty) {
  133. Object.defineProperty(ansiHTML.tags, 'open', {
  134. get: function () { return _openTags }
  135. })
  136. Object.defineProperty(ansiHTML.tags, 'close', {
  137. get: function () { return _closeTags }
  138. })
  139. } else {
  140. ansiHTML.tags.open = _openTags
  141. ansiHTML.tags.close = _closeTags
  142. }
  143. function _setTags (colors) {
  144. // reset all
  145. _openTags['0'] = 'font-weight:normal;opacity:1;color:#' + colors.reset[0] + ';background:#' + colors.reset[1]
  146. // inverse
  147. _openTags['7'] = 'color:#' + colors.reset[1] + ';background:#' + colors.reset[0]
  148. // dark grey
  149. _openTags['90'] = 'color:#' + colors.darkgrey
  150. for (var code in _styles) {
  151. var color = _styles[code]
  152. var oriColor = colors[color] || '000'
  153. _openTags[code] = 'color:#' + oriColor
  154. code = parseInt(code)
  155. _openTags[(code + 10).toString()] = 'background:#' + oriColor
  156. }
  157. }
  158. ansiHTML.reset()