plugin.js 4.7 KB

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