Linker.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. export default class Linker {
  2. constructor(type, label, link) {
  3. this.type = type;
  4. this.label = label;
  5. this.link = link;
  6. // TODO GW-3074 相対パスを利用しているかの情報も持つようにする
  7. }
  8. static types = {
  9. markdownLink: 'mdLink',
  10. growiLink: 'growiLink',
  11. pukiwikiLink: 'pukiwikiLink',
  12. }
  13. static patterns = {
  14. pukiwikiLinkWithLabel: /^\[\[(?<label>.+)>(?<link>.+)\]\]$/, // https://regex101.com/r/2fNmUN/2
  15. pukiwikiLinkWithoutLabel: /^\[\[(?<label>.+)\]\]$/, // https://regex101.com/r/S7w5Xu/1
  16. growiLink: /^\[(?<label>\/.+)\]$/, // https://regex101.com/r/DJfkYf/3
  17. markdownLink: /^\[(?<label>.*)\]\((?<link>.*)\)$/, // https://regex101.com/r/DZCKP3/2
  18. }
  19. // create an instance of Linker from string
  20. static fromMarkdownString(str) {
  21. // if str doesn't mean a linker, create a link whose label is str
  22. let label = str;
  23. let link = '';
  24. let type = this.types.markdownLink;
  25. // pukiwiki with separator ">".
  26. if (str.match(this.patterns.pukiwikiLinkWithLabel)) {
  27. type = this.types.pukiwikiLink;
  28. ({ label, link } = str.match(this.patterns.pukiwikiLinkWithLabel).groups);
  29. }
  30. // pukiwiki without separator ">".
  31. else if (str.match(this.patterns.pukiwikiLinkWithoutLabel)) {
  32. type = this.types.pukiwikiLink;
  33. ({ label } = str.match(this.patterns.pukiwikiLinkWithoutLabel).groups);
  34. link = label;
  35. }
  36. // growi
  37. else if (str.match(this.patterns.growiLink)) {
  38. type = this.types.growiLink;
  39. ({ label } = str.match(this.patterns.growiLink).groups);
  40. link = label;
  41. }
  42. // markdown
  43. else if (str.match(this.patterns.markdownLink)) {
  44. type = this.types.markdownLink;
  45. ({ label, link } = str.match(this.patterns.markdownLink).groups);
  46. }
  47. return new Linker(type, label, link);
  48. }
  49. // create an instance of Linker from text with index
  50. static fromLineWithIndex(line, index) {
  51. const { beginningOfLink, endOfLink } = this.getBeginningAndEndIndexOfLink(line, index);
  52. // if index is in a link, extract it from line
  53. let linkStr = '';
  54. if (beginningOfLink >= 0 && endOfLink >= 0) {
  55. linkStr = line.substring(beginningOfLink, endOfLink);
  56. }
  57. return this.fromMarkdownString(linkStr);
  58. }
  59. // return beginning and end indexies of link
  60. // if index is not in a link, return { beginningOfLink: -1, endOfLink: -1 }
  61. static getBeginningAndEndIndexOfLink(line, index) {
  62. let beginningOfLink;
  63. let endOfLink;
  64. // pukiwiki link ('[[link]]')
  65. [beginningOfLink, endOfLink] = this.getBeginningAndEndIndexWithPrefixAndSuffix(line, index, '[[', ']]');
  66. // if index is not in a pukiwiki link
  67. // growi link ('[/link]')
  68. if (beginningOfLink < 0 || endOfLink < 0 || beginningOfLink > index || endOfLink < index) {
  69. [beginningOfLink, endOfLink] = this.getBeginningAndEndIndexWithPrefixAndSuffix(line, index, '[/', ']');
  70. }
  71. // and if index is not in a growi link
  72. // markdown link ('[label](link)')
  73. if (beginningOfLink < 0 || endOfLink < 0 || beginningOfLink > index || endOfLink < index) {
  74. [beginningOfLink, endOfLink] = this.getBeginningAndEndIndexWithPrefixAndSuffix(line, index, '[', ')', '](');
  75. }
  76. // and if index is not in a markdown link
  77. // return { beginningOfLink: -1, endOfLink: -1 }
  78. if (beginningOfLink < 0 || endOfLink < 0 || beginningOfLink > index || endOfLink < index) {
  79. [beginningOfLink, endOfLink] = [-1, -1];
  80. }
  81. return { beginningOfLink, endOfLink };
  82. }
  83. // return begin and end indexies as array only when index is between prefix and suffix and link contains containText.
  84. static getBeginningAndEndIndexWithPrefixAndSuffix(line, index, prefix, suffix, containText = '') {
  85. const beginningIndex = line.lastIndexOf(prefix, index);
  86. const IndexOfContainText = line.indexOf(containText, beginningIndex + prefix.length);
  87. const endIndex = line.indexOf(suffix, IndexOfContainText + containText.length);
  88. if (beginningIndex < 0 || IndexOfContainText < 0 || endIndex < 0) {
  89. return [-1, -1];
  90. }
  91. return [beginningIndex, endIndex + suffix.length];
  92. }
  93. }