Regex.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. import XRegExp from "xregexp";
  2. import Utils from "../Utils.js";
  3. /**
  4. * Regex operations.
  5. *
  6. * @author n1474335 [n1474335@gmail.com]
  7. * @copyright Crown Copyright 2018
  8. * @license Apache-2.0
  9. *
  10. * @namespace
  11. */
  12. const Regex = {
  13. /**
  14. * @constant
  15. * @default
  16. */
  17. REGEX_PRE_POPULATE: [
  18. {
  19. name: "User defined",
  20. value: ""
  21. },
  22. {
  23. name: "IPv4 address",
  24. value: "(?:(?:\\d|[01]?\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d|\\d)(?:\\/\\d{1,2})?"
  25. },
  26. {
  27. name: "IPv6 address",
  28. value: "((?=.*::)(?!.*::.+::)(::)?([\\dA-Fa-f]{1,4}:(:|\\b)|){5}|([\\dA-Fa-f]{1,4}:){6})((([\\dA-Fa-f]{1,4}((?!\\3)::|:\\b|(?![\\dA-Fa-f])))|(?!\\2\\3)){2}|(((2[0-4]|1\\d|[1-9])?\\d|25[0-5])\\.?\\b){4})"
  29. },
  30. {
  31. name: "Email address",
  32. value: "(\\w[-.\\w]*)@([-\\w]+(?:\\.[-\\w]+)*)\\.([A-Za-z]{2,4})"
  33. },
  34. {
  35. name: "URL",
  36. value: "([A-Za-z]+://)([-\\w]+(?:\\.\\w[-\\w]*)+)(:\\d+)?(/[^.!,?\"<>\\[\\]{}\\s\\x7F-\\xFF]*(?:[.!,?]+[^.!,?\"<>\\[\\]{}\\s\\x7F-\\xFF]+)*)?"
  37. },
  38. {
  39. name: "Domain",
  40. value: "\\b((?=[a-z0-9-]{1,63}\\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\\.)+[a-z]{2,63}\\b"
  41. },
  42. {
  43. name: "Windows file path",
  44. value: "([A-Za-z]):\\\\((?:[A-Za-z\\d][A-Za-z\\d\\- \\x27_\\(\\)]{0,61}\\\\?)*[A-Za-z\\d][A-Za-z\\d\\- \\x27_\\(\\)]{0,61})(\\.[A-Za-z\\d]{1,6})?"
  45. },
  46. {
  47. name: "UNIX file path",
  48. value: "(?:/[A-Za-z\\d.][A-Za-z\\d\\-.]{0,61})+"
  49. },
  50. {
  51. name: "MAC address",
  52. value: "[A-Fa-f\\d]{2}(?:[:-][A-Fa-f\\d]{2}){5}"
  53. },
  54. {
  55. name: "Date (yyyy-mm-dd)",
  56. value: "((?:19|20)\\d\\d)[- /.](0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])"
  57. },
  58. {
  59. name: "Date (dd/mm/yyyy)",
  60. value: "(0[1-9]|[12][0-9]|3[01])[- /.](0[1-9]|1[012])[- /.]((?:19|20)\\d\\d)"
  61. },
  62. {
  63. name: "Date (mm/dd/yyyy)",
  64. value: "(0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])[- /.]((?:19|20)\\d\\d)"
  65. },
  66. {
  67. name: "Strings",
  68. value: "[A-Za-z\\d/\\-:.,_$%\\x27\"()<>= !\\[\\]{}@]{4,}"
  69. },
  70. ],
  71. /**
  72. * @constant
  73. * @default
  74. */
  75. REGEX_CASE_INSENSITIVE: true,
  76. /**
  77. * @constant
  78. * @default
  79. */
  80. REGEX_MULTILINE_MATCHING: true,
  81. /**
  82. * @constant
  83. * @default
  84. */
  85. OUTPUT_FORMAT: ["Highlight matches", "List matches", "List capture groups", "List matches with capture groups"],
  86. /**
  87. * @constant
  88. * @default
  89. */
  90. DISPLAY_TOTAL: false,
  91. /**
  92. * Regular expression operation.
  93. *
  94. * @param {string} input
  95. * @param {Object[]} args
  96. * @returns {html}
  97. */
  98. runRegex: function(input, args) {
  99. let userRegex = args[1],
  100. i = args[2],
  101. m = args[3],
  102. displayTotal = args[4],
  103. outputFormat = args[5],
  104. modifiers = "g";
  105. if (i) modifiers += "i";
  106. if (m) modifiers += "m";
  107. if (userRegex && userRegex !== "^" && userRegex !== "$") {
  108. try {
  109. const regex = new XRegExp(userRegex, modifiers);
  110. switch (outputFormat) {
  111. case "Highlight matches":
  112. return Regex._regexHighlight(input, regex, displayTotal);
  113. case "List matches":
  114. return Utils.escapeHtml(Regex._regexList(input, regex, displayTotal, true, false));
  115. case "List capture groups":
  116. return Utils.escapeHtml(Regex._regexList(input, regex, displayTotal, false, true));
  117. case "List matches with capture groups":
  118. return Utils.escapeHtml(Regex._regexList(input, regex, displayTotal, true, true));
  119. default:
  120. return "Error: Invalid output format";
  121. }
  122. } catch (err) {
  123. return "Invalid regex. Details: " + err.message;
  124. }
  125. } else {
  126. return Utils.escapeHtml(input);
  127. }
  128. },
  129. /**
  130. * @constant
  131. * @default
  132. */
  133. SEARCH_TYPE: ["Regex", "Extended (\\n, \\t, \\x...)", "Simple string"],
  134. /**
  135. * @constant
  136. * @default
  137. */
  138. FIND_REPLACE_GLOBAL: true,
  139. /**
  140. * @constant
  141. * @default
  142. */
  143. FIND_REPLACE_CASE: false,
  144. /**
  145. * @constant
  146. * @default
  147. */
  148. FIND_REPLACE_MULTILINE: true,
  149. /**
  150. * Find / Replace operation.
  151. *
  152. * @param {string} input
  153. * @param {Object[]} args
  154. * @returns {string}
  155. */
  156. runFindReplace: function(input, args) {
  157. let find = args[0].string,
  158. type = args[0].option,
  159. replace = args[1],
  160. g = args[2],
  161. i = args[3],
  162. m = args[4],
  163. modifiers = "";
  164. if (g) modifiers += "g";
  165. if (i) modifiers += "i";
  166. if (m) modifiers += "m";
  167. if (type === "Regex") {
  168. find = new RegExp(find, modifiers);
  169. return input.replace(find, replace);
  170. }
  171. if (type.indexOf("Extended") === 0) {
  172. find = Utils.parseEscapedChars(find);
  173. }
  174. find = new RegExp(Utils.escapeRegex(find), modifiers);
  175. return input.replace(find, replace);
  176. },
  177. /**
  178. * Adds HTML highlights to matches within a string.
  179. *
  180. * @private
  181. * @param {string} input
  182. * @param {RegExp} regex
  183. * @param {boolean} displayTotal
  184. * @returns {string}
  185. */
  186. _regexHighlight: function(input, regex, displayTotal) {
  187. let output = "",
  188. m,
  189. hl = 1,
  190. i = 0,
  191. total = 0;
  192. while ((m = regex.exec(input))) {
  193. // Moves pointer when an empty string is matched (prevents infinite loop)
  194. if (m.index === regex.lastIndex) {
  195. regex.lastIndex++;
  196. }
  197. // Add up to match
  198. output += Utils.escapeHtml(input.slice(i, m.index));
  199. // Add match with highlighting
  200. output += "<span class='hl"+hl+"'>" + Utils.escapeHtml(m[0]) + "</span>";
  201. // Switch highlight
  202. hl = hl === 1 ? 2 : 1;
  203. i = regex.lastIndex;
  204. total++;
  205. }
  206. // Add all after final match
  207. output += Utils.escapeHtml(input.slice(i, input.length));
  208. if (displayTotal)
  209. output = "Total found: " + total + "\n\n" + output;
  210. return output;
  211. },
  212. /**
  213. * Creates a string listing the matches within a string.
  214. *
  215. * @private
  216. * @param {string} input
  217. * @param {RegExp} regex
  218. * @param {boolean} displayTotal
  219. * @param {boolean} matches - Display full match
  220. * @param {boolean} captureGroups - Display each of the capture groups separately
  221. * @returns {string}
  222. */
  223. _regexList: function(input, regex, displayTotal, matches, captureGroups) {
  224. let output = "",
  225. total = 0,
  226. match;
  227. while ((match = regex.exec(input))) {
  228. // Moves pointer when an empty string is matched (prevents infinite loop)
  229. if (match.index === regex.lastIndex) {
  230. regex.lastIndex++;
  231. }
  232. total++;
  233. if (matches) {
  234. output += match[0] + "\n";
  235. }
  236. if (captureGroups) {
  237. for (let i = 1; i < match.length; i++) {
  238. if (matches) {
  239. output += " Group " + i + ": ";
  240. }
  241. output += match[i] + "\n";
  242. }
  243. }
  244. }
  245. if (displayTotal)
  246. output = "Total found: " + total + "\n\n" + output;
  247. return output;
  248. },
  249. };
  250. export default Regex;