Linker.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. export default class Linker {
  2. constructor(
  3. type,
  4. label,
  5. link,
  6. isUsePermanentLink = false,
  7. permalink = '',
  8. ) {
  9. this.type = type;
  10. this.label = label;
  11. this.link = link;
  12. this.isUsePermanentLink = isUsePermanentLink;
  13. this.permalink = permalink;
  14. this.generateMarkdownText = this.generateMarkdownText.bind(this);
  15. }
  16. static types = {
  17. markdownLink: 'mdLink',
  18. growiLink: 'growiLink',
  19. pukiwikiLink: 'pukiwikiLink',
  20. }
  21. static patterns = {
  22. pukiwikiLinkWithLabel: /^\[\[(?<label>.+)>(?<link>.+)\]\]$/, // https://regex101.com/r/2fNmUN/2
  23. pukiwikiLinkWithoutLabel: /^\[\[(?<label>.+)\]\]$/, // https://regex101.com/r/S7w5Xu/1
  24. growiLink: /^\[(?<label>\/.+)\]$/, // https://regex101.com/r/DJfkYf/3
  25. markdownLink: /^\[(?<label>.*)\]\((?<link>.*)\)$/, // https://regex101.com/r/DZCKP3/2
  26. }
  27. generateMarkdownText() {
  28. let reshapedLink = this.link;
  29. if (this.isUsePermanentLink && this.permalink != null) {
  30. reshapedLink = this.permalink;
  31. }
  32. if (this.label === '') {
  33. this.label = reshapedLink;
  34. }
  35. if (this.type === Linker.types.pukiwikiLink) {
  36. if (this.label === reshapedLink) return `[[${reshapedLink}]]`;
  37. return `[[${this.label}>${reshapedLink}]]`;
  38. }
  39. if (this.type === Linker.types.growiLink) {
  40. return `[${reshapedLink}]`;
  41. }
  42. if (this.type === Linker.types.markdownLink) {
  43. return `[${this.label}](${reshapedLink})`;
  44. }
  45. }
  46. // create an instance of Linker from string
  47. static fromMarkdownString(str) {
  48. // if str doesn't mean a linker, create a link whose label is str
  49. let label = str;
  50. let link = '';
  51. let type = this.types.markdownLink;
  52. // pukiwiki with separator ">".
  53. if (str.match(this.patterns.pukiwikiLinkWithLabel)) {
  54. type = this.types.pukiwikiLink;
  55. ({ label, link } = str.match(this.patterns.pukiwikiLinkWithLabel).groups);
  56. }
  57. // pukiwiki without separator ">".
  58. else if (str.match(this.patterns.pukiwikiLinkWithoutLabel)) {
  59. type = this.types.pukiwikiLink;
  60. ({ label } = str.match(this.patterns.pukiwikiLinkWithoutLabel).groups);
  61. link = label;
  62. }
  63. // markdown
  64. else if (str.match(this.patterns.markdownLink)) {
  65. type = this.types.markdownLink;
  66. ({ label, link } = str.match(this.patterns.markdownLink).groups);
  67. }
  68. // growi
  69. else if (str.match(this.patterns.growiLink)) {
  70. type = this.types.growiLink;
  71. ({ label } = str.match(this.patterns.growiLink).groups);
  72. link = label;
  73. }
  74. const isUsePermanentLink = false;
  75. const permalink = '';
  76. return new Linker(
  77. type,
  78. label,
  79. link,
  80. isUsePermanentLink,
  81. permalink,
  82. );
  83. }
  84. // create an instance of Linker from text with index
  85. static fromLineWithIndex(line, index) {
  86. const { beginningOfLink, endOfLink } = this.getBeginningAndEndIndexOfLink(line, index);
  87. // if index is in a link, extract it from line
  88. let linkStr = '';
  89. if (beginningOfLink >= 0 && endOfLink >= 0) {
  90. linkStr = line.substring(beginningOfLink, endOfLink);
  91. }
  92. return this.fromMarkdownString(linkStr);
  93. }
  94. // return beginning and end indexies of link
  95. // if index is not in a link, return { beginningOfLink: -1, endOfLink: -1 }
  96. static getBeginningAndEndIndexOfLink(line, index) {
  97. let beginningOfLink;
  98. let endOfLink;
  99. // pukiwiki link ('[[link]]')
  100. [beginningOfLink, endOfLink] = this.getBeginningAndEndIndexWithPrefixAndSuffix(line, index, '[[', ']]');
  101. // markdown link ('[label](link)')
  102. if (beginningOfLink < 0 || endOfLink < 0 || beginningOfLink > index || endOfLink < index) {
  103. [beginningOfLink, endOfLink] = this.getBeginningAndEndIndexWithPrefixAndSuffix(line, index, '[', ')', '](');
  104. }
  105. // growi link ('[/link]')
  106. if (beginningOfLink < 0 || endOfLink < 0 || beginningOfLink > index || endOfLink < index) {
  107. [beginningOfLink, endOfLink] = this.getBeginningAndEndIndexWithPrefixAndSuffix(line, index, '[/', ']');
  108. }
  109. // return { beginningOfLink: -1, endOfLink: -1 }
  110. if (beginningOfLink < 0 || endOfLink < 0 || beginningOfLink > index || endOfLink < index) {
  111. [beginningOfLink, endOfLink] = [-1, -1];
  112. }
  113. return { beginningOfLink, endOfLink };
  114. }
  115. // return begin and end indexies as array only when index is between prefix and suffix and link contains containText.
  116. static getBeginningAndEndIndexWithPrefixAndSuffix(line, index, prefix, suffix, containText = '') {
  117. const beginningIndex = line.lastIndexOf(prefix, index);
  118. const IndexOfContainText = line.indexOf(containText, beginningIndex + prefix.length);
  119. const endIndex = line.indexOf(suffix, IndexOfContainText + containText.length);
  120. if (beginningIndex < 0 || IndexOfContainText < 0 || endIndex < 0) {
  121. return [-1, -1];
  122. }
  123. return [beginningIndex, endIndex + suffix.length];
  124. }
  125. }