plugin.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. /**
  2. * plugin.js
  3. *
  4. * Released under LGPL License.
  5. * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
  6. *
  7. * License: http://www.tinymce.com/license
  8. * Contributing: http://www.tinymce.com/contributing
  9. */
  10. /*global tinymce:true */
  11. tinymce.PluginManager.add('autolink', function(editor) {
  12. var AutoUrlDetectState;
  13. var AutoLinkPattern = /^(https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.|(?:mailto:)?[A-Z0-9._%+\-]+@)(.+)$/i;
  14. if (editor.settings.autolink_pattern) {
  15. AutoLinkPattern = editor.settings.autolink_pattern;
  16. }
  17. editor.on("keydown", function(e) {
  18. if (e.keyCode == 13) {
  19. return handleEnter(editor);
  20. }
  21. });
  22. // Internet Explorer has built-in automatic linking for most cases
  23. if (tinymce.Env.ie) {
  24. editor.on("focus", function() {
  25. if (!AutoUrlDetectState) {
  26. AutoUrlDetectState = true;
  27. try {
  28. editor.execCommand('AutoUrlDetect', false, true);
  29. } catch (ex) {
  30. // Ignore
  31. }
  32. }
  33. });
  34. return;
  35. }
  36. editor.on("keypress", function(e) {
  37. if (e.keyCode == 41) {
  38. return handleEclipse(editor);
  39. }
  40. });
  41. editor.on("keyup", function(e) {
  42. if (e.keyCode == 32) {
  43. return handleSpacebar(editor);
  44. }
  45. });
  46. function handleEclipse(editor) {
  47. parseCurrentLine(editor, -1, '(', true);
  48. }
  49. function handleSpacebar(editor) {
  50. parseCurrentLine(editor, 0, '', true);
  51. }
  52. function handleEnter(editor) {
  53. parseCurrentLine(editor, -1, '', false);
  54. }
  55. function parseCurrentLine(editor, end_offset, delimiter) {
  56. var rng, end, start, endContainer, bookmark, text, matches, prev, len, rngText;
  57. function scopeIndex(container, index) {
  58. if (index < 0) {
  59. index = 0;
  60. }
  61. if (container.nodeType == 3) {
  62. var len = container.data.length;
  63. if (index > len) {
  64. index = len;
  65. }
  66. }
  67. return index;
  68. }
  69. function setStart(container, offset) {
  70. if (container.nodeType != 1 || container.hasChildNodes()) {
  71. rng.setStart(container, scopeIndex(container, offset));
  72. } else {
  73. rng.setStartBefore(container);
  74. }
  75. }
  76. function setEnd(container, offset) {
  77. if (container.nodeType != 1 || container.hasChildNodes()) {
  78. rng.setEnd(container, scopeIndex(container, offset));
  79. } else {
  80. rng.setEndAfter(container);
  81. }
  82. }
  83. // Never create a link when we are inside a link
  84. if (editor.selection.getNode().tagName == 'A') {
  85. return;
  86. }
  87. // We need at least five characters to form a URL,
  88. // hence, at minimum, five characters from the beginning of the line.
  89. rng = editor.selection.getRng(true).cloneRange();
  90. if (rng.startOffset < 5) {
  91. // During testing, the caret is placed between two text nodes.
  92. // The previous text node contains the URL.
  93. prev = rng.endContainer.previousSibling;
  94. if (!prev) {
  95. if (!rng.endContainer.firstChild || !rng.endContainer.firstChild.nextSibling) {
  96. return;
  97. }
  98. prev = rng.endContainer.firstChild.nextSibling;
  99. }
  100. len = prev.length;
  101. setStart(prev, len);
  102. setEnd(prev, len);
  103. if (rng.endOffset < 5) {
  104. return;
  105. }
  106. end = rng.endOffset;
  107. endContainer = prev;
  108. } else {
  109. endContainer = rng.endContainer;
  110. // Get a text node
  111. if (endContainer.nodeType != 3 && endContainer.firstChild) {
  112. while (endContainer.nodeType != 3 && endContainer.firstChild) {
  113. endContainer = endContainer.firstChild;
  114. }
  115. // Move range to text node
  116. if (endContainer.nodeType == 3) {
  117. setStart(endContainer, 0);
  118. setEnd(endContainer, endContainer.nodeValue.length);
  119. }
  120. }
  121. if (rng.endOffset == 1) {
  122. end = 2;
  123. } else {
  124. end = rng.endOffset - 1 - end_offset;
  125. }
  126. }
  127. start = end;
  128. do {
  129. // Move the selection one character backwards.
  130. setStart(endContainer, end >= 2 ? end - 2 : 0);
  131. setEnd(endContainer, end >= 1 ? end - 1 : 0);
  132. end -= 1;
  133. rngText = rng.toString();
  134. // Loop until one of the following is found: a blank space, &nbsp;, delimiter, (end-2) >= 0
  135. } while (rngText != ' ' && rngText !== '' && rngText.charCodeAt(0) != 160 && (end - 2) >= 0 && rngText != delimiter);
  136. if (rng.toString() == delimiter || rng.toString().charCodeAt(0) == 160) {
  137. setStart(endContainer, end);
  138. setEnd(endContainer, start);
  139. end += 1;
  140. } else if (rng.startOffset === 0) {
  141. setStart(endContainer, 0);
  142. setEnd(endContainer, start);
  143. } else {
  144. setStart(endContainer, end);
  145. setEnd(endContainer, start);
  146. }
  147. // Exclude last . from word like "www.site.com."
  148. text = rng.toString();
  149. if (text.charAt(text.length - 1) == '.') {
  150. setEnd(endContainer, start - 1);
  151. }
  152. text = rng.toString();
  153. matches = text.match(AutoLinkPattern);
  154. if (matches) {
  155. if (matches[1] == 'www.') {
  156. matches[1] = 'http://www.';
  157. } else if (/@$/.test(matches[1]) && !/^mailto:/.test(matches[1])) {
  158. matches[1] = 'mailto:' + matches[1];
  159. }
  160. bookmark = editor.selection.getBookmark();
  161. editor.selection.setRng(rng);
  162. editor.execCommand('createlink', false, matches[1] + matches[2]);
  163. editor.selection.moveToBookmark(bookmark);
  164. editor.nodeChanged();
  165. }
  166. }
  167. });