plugin.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. /**
  2. * TinyMCE version 6.0.3 (2022-05-25)
  3. */
  4. (function () {
  5. 'use strict';
  6. var global = tinymce.util.Tools.resolve('tinymce.PluginManager');
  7. const link = () => /(?:[A-Za-z][A-Za-z\d.+-]{0,14}:\/\/(?:[-.~*+=!&;:'%@?^${}(),\w]+@)?|www\.|[-;:&=+$,.\w]+@)[A-Za-z\d-]+(?:\.[A-Za-z\d-]+)*(?::\d+)?(?:\/(?:[-+~=.,%()\/\w]*[-+~=%()\/\w])?)?(?:\?(?:[-.~*+=!&;:'%@?^${}(),\/\w]+))?(?:#(?:[-.~*+=!&;:'%@?^${}(),\/\w]+))?/g;
  8. const option = name => editor => editor.options.get(name);
  9. const register = editor => {
  10. const registerOption = editor.options.register;
  11. registerOption('autolink_pattern', {
  12. processor: 'regexp',
  13. default: new RegExp('^' + link().source + '$', 'i')
  14. });
  15. registerOption('link_default_target', { processor: 'string' });
  16. registerOption('link_default_protocol', {
  17. processor: 'string',
  18. default: 'https'
  19. });
  20. };
  21. const getAutoLinkPattern = option('autolink_pattern');
  22. const getDefaultLinkTarget = option('link_default_target');
  23. const getDefaultLinkProtocol = option('link_default_protocol');
  24. const hasProto = (v, constructor, predicate) => {
  25. var _a;
  26. if (predicate(v, constructor.prototype)) {
  27. return true;
  28. } else {
  29. return ((_a = v.constructor) === null || _a === void 0 ? void 0 : _a.name) === constructor.name;
  30. }
  31. };
  32. const typeOf = x => {
  33. const t = typeof x;
  34. if (x === null) {
  35. return 'null';
  36. } else if (t === 'object' && Array.isArray(x)) {
  37. return 'array';
  38. } else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {
  39. return 'string';
  40. } else {
  41. return t;
  42. }
  43. };
  44. const isType = type => value => typeOf(value) === type;
  45. const isString = isType('string');
  46. const checkRange = (str, substr, start) => substr === '' || str.length >= substr.length && str.substr(start, start + substr.length) === substr;
  47. const contains = (str, substr) => {
  48. return str.indexOf(substr) !== -1;
  49. };
  50. const startsWith = (str, prefix) => {
  51. return checkRange(str, prefix, 0);
  52. };
  53. const rangeEqualsBracketOrSpace = rangeString => /^[(\[{ \u00a0]$/.test(rangeString);
  54. const isTextNode = node => node.nodeType === 3;
  55. const isElement = node => node.nodeType === 1;
  56. const handleBracket = editor => parseCurrentLine(editor, -1);
  57. const handleSpacebar = editor => parseCurrentLine(editor, 0);
  58. const handleEnter = editor => parseCurrentLine(editor, -1);
  59. const scopeIndex = (container, index) => {
  60. if (index < 0) {
  61. index = 0;
  62. }
  63. if (isTextNode(container)) {
  64. const len = container.data.length;
  65. if (index > len) {
  66. index = len;
  67. }
  68. }
  69. return index;
  70. };
  71. const setStart = (rng, container, offset) => {
  72. if (!isElement(container) || container.hasChildNodes()) {
  73. rng.setStart(container, scopeIndex(container, offset));
  74. } else {
  75. rng.setStartBefore(container);
  76. }
  77. };
  78. const setEnd = (rng, container, offset) => {
  79. if (!isElement(container) || container.hasChildNodes()) {
  80. rng.setEnd(container, scopeIndex(container, offset));
  81. } else {
  82. rng.setEndAfter(container);
  83. }
  84. };
  85. const hasProtocol = url => /^([A-Za-z][A-Za-z\d.+-]*:\/\/)|mailto:/.test(url);
  86. const isPunctuation = char => /[?!,.;:]/.test(char);
  87. const parseCurrentLine = (editor, endOffset) => {
  88. let end, endContainer, bookmark, text, prev, len, rngText;
  89. const autoLinkPattern = getAutoLinkPattern(editor);
  90. const defaultLinkTarget = getDefaultLinkTarget(editor);
  91. if (editor.dom.getParent(editor.selection.getNode(), 'a[href]') !== null) {
  92. return;
  93. }
  94. const rng = editor.selection.getRng().cloneRange();
  95. if (rng.startOffset < 5) {
  96. prev = rng.endContainer.previousSibling;
  97. if (!prev) {
  98. if (!rng.endContainer.firstChild || !rng.endContainer.firstChild.nextSibling) {
  99. return;
  100. }
  101. prev = rng.endContainer.firstChild.nextSibling;
  102. }
  103. len = prev.length;
  104. setStart(rng, prev, len);
  105. setEnd(rng, prev, len);
  106. if (rng.endOffset < 5) {
  107. return;
  108. }
  109. end = rng.endOffset;
  110. endContainer = prev;
  111. } else {
  112. endContainer = rng.endContainer;
  113. if (!isTextNode(endContainer) && endContainer.firstChild) {
  114. while (!isTextNode(endContainer) && endContainer.firstChild) {
  115. endContainer = endContainer.firstChild;
  116. }
  117. if (isTextNode(endContainer)) {
  118. setStart(rng, endContainer, 0);
  119. setEnd(rng, endContainer, endContainer.nodeValue.length);
  120. }
  121. }
  122. if (rng.endOffset === 1) {
  123. end = 2;
  124. } else {
  125. end = rng.endOffset - 1 - endOffset;
  126. }
  127. }
  128. const start = end;
  129. do {
  130. setStart(rng, endContainer, end >= 2 ? end - 2 : 0);
  131. setEnd(rng, endContainer, end >= 1 ? end - 1 : 0);
  132. end -= 1;
  133. rngText = rng.toString();
  134. } while (!rangeEqualsBracketOrSpace(rngText) && end - 2 >= 0);
  135. if (rangeEqualsBracketOrSpace(rng.toString())) {
  136. setStart(rng, endContainer, end);
  137. setEnd(rng, endContainer, start);
  138. end += 1;
  139. } else if (rng.startOffset === 0) {
  140. setStart(rng, endContainer, 0);
  141. setEnd(rng, endContainer, start);
  142. } else {
  143. setStart(rng, endContainer, end);
  144. setEnd(rng, endContainer, start);
  145. }
  146. text = rng.toString();
  147. if (isPunctuation(text.charAt(text.length - 1))) {
  148. setEnd(rng, endContainer, start - 1);
  149. }
  150. text = rng.toString().trim();
  151. const matches = text.match(autoLinkPattern);
  152. const protocol = getDefaultLinkProtocol(editor);
  153. if (matches) {
  154. let url = matches[0];
  155. if (startsWith(url, 'www.')) {
  156. url = protocol + '://' + url;
  157. } else if (contains(url, '@') && !hasProtocol(url)) {
  158. url = 'mailto:' + url;
  159. }
  160. bookmark = editor.selection.getBookmark();
  161. editor.selection.setRng(rng);
  162. editor.getDoc().execCommand('createlink', false, url);
  163. if (isString(defaultLinkTarget)) {
  164. editor.dom.setAttrib(editor.selection.getNode(), 'target', defaultLinkTarget);
  165. }
  166. editor.selection.moveToBookmark(bookmark);
  167. editor.nodeChanged();
  168. }
  169. };
  170. const setup = editor => {
  171. editor.on('keydown', e => {
  172. if (e.keyCode === 13) {
  173. return handleEnter(editor);
  174. }
  175. });
  176. editor.on('keypress', e => {
  177. if (e.keyCode === 41 || e.keyCode === 93 || e.keyCode === 125) {
  178. return handleBracket(editor);
  179. }
  180. });
  181. editor.on('keyup', e => {
  182. if (e.keyCode === 32) {
  183. return handleSpacebar(editor);
  184. }
  185. });
  186. };
  187. var Plugin = () => {
  188. global.add('autolink', editor => {
  189. register(editor);
  190. setup(editor);
  191. });
  192. };
  193. Plugin();
  194. })();