Linker.js 4.4 KB

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