AutoUrlLink.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. define("dojox/editor/plugins/AutoUrlLink", [
  2. "dojo",
  3. "dijit",
  4. "dojox",
  5. "dijit/_editor/range",
  6. "dijit/_editor/selection",
  7. "dijit/_editor/_Plugin",
  8. "dijit/form/Button",
  9. "dojo/_base/connect",
  10. "dojo/_base/declare",
  11. "dojo/string"
  12. ], function(dojo, dijit, dojox) {
  13. dojo.declare("dojox.editor.plugins.AutoUrlLink", [dijit._editor._Plugin], {
  14. //summary:
  15. // This plugin can recognize a URL like string
  16. // (such as http://www.website.com) and turn it into
  17. // a hyperlink that points to that URL.
  18. // _template [private] String
  19. // The link template
  20. _template: "<a _djrealurl='${url}' href='${url}'>${url}</a>",
  21. setEditor: function(/*dijit.Editor*/ editor){
  22. // summary:
  23. // Called by the editor it belongs to.
  24. // editor:
  25. // The editor it belongs to.
  26. this.editor = editor;
  27. if(!dojo.isIE){
  28. // IE will recognize URL as a link automatically
  29. // No need to re-invent the wheel.
  30. dojo.some(editor._plugins, function(plugin){
  31. // Need to detect which enter key mode it is now
  32. if(plugin.isInstanceOf(dijit._editor.plugins.EnterKeyHandling)){
  33. this.blockNodeForEnter = plugin.blockNodeForEnter;
  34. return true;
  35. }
  36. return false;
  37. }, this);
  38. this.connect(editor, "onKeyPress", "_keyPress");
  39. this.connect(editor, "onClick", "_recognize");
  40. this.connect(editor, "onBlur", "_recognize");
  41. }
  42. },
  43. _keyPress: function(evt){
  44. // summary:
  45. // Handle the keypress event and dispatch it to the target handler
  46. // evt:
  47. // The keypress event object.
  48. // tags:
  49. // protected
  50. var ks = dojo.keys, v = 118, V = 86,
  51. kc = evt.keyCode, cc = evt.charCode;
  52. if(cc == ks.SPACE || (evt.ctrlKey && (cc == v || cc == V))){
  53. setTimeout(dojo.hitch(this, "_recognize"), 0);
  54. }else if(kc == ks.ENTER){
  55. // Handle the enter event after EnterKeyHandling finishes its job
  56. setTimeout(dojo.hitch(this, function(){
  57. this._recognize({enter: true});
  58. }), 0);
  59. }else{
  60. // _saved: The previous dom node when the cursor is at a new dom node.
  61. // When we click elsewhere, the previous dom node
  62. // should be examed to see if there is any URL need to be activated
  63. this._saved = this.editor.window.getSelection().anchorNode;
  64. }
  65. },
  66. _recognize: function(args){
  67. // summary:
  68. // Recognize the URL like strings and turn them into a link
  69. // tags:
  70. // private
  71. var template = this._template,
  72. isEnter = args ? args.enter : false,
  73. ed = this.editor,
  74. selection = ed.window.getSelection();
  75. if(selection){
  76. var node = isEnter ? this._findLastEditingNode(selection.anchorNode) :
  77. (this._saved || selection.anchorNode),
  78. bm = this._saved = selection.anchorNode,
  79. bmOff = selection.anchorOffset;
  80. if(node.nodeType == 3 && !this._inLink(node)){
  81. var linked = false, result = this._findUrls(node, bm, bmOff),
  82. range = ed.document.createRange(),
  83. item, cost = 0, isSameNode = (bm == node);
  84. item = result.shift();
  85. while(item){
  86. // Covert a URL to a link.
  87. range.setStart(node, item.start);
  88. range.setEnd(node, item.end);
  89. selection.removeAllRanges();
  90. selection.addRange(range);
  91. ed.execCommand("insertHTML", dojo.string.substitute(template, {url: range.toString()}));
  92. cost += item.end;
  93. item = result.shift();
  94. linked = true;
  95. }
  96. // If bm and node are the some dom node, caculate the actual bookmark offset
  97. // If the position of the cursor is modified (turned into a link, etc.), no
  98. // need to recover the cursor position
  99. if(isSameNode && (bmOff = bmOff - cost) <= 0){ return; }
  100. // We didn't update anything, so don't collapse selections.
  101. if(!linked) { return ; }
  102. try{
  103. // Try to recover the cursor position
  104. range.setStart(bm, 0);
  105. range.setEnd(bm, bmOff);
  106. selection.removeAllRanges();
  107. selection.addRange(range);
  108. dojo.withGlobal(ed.window, "collapse", dijit._editor.selection, []);
  109. }catch(e){}
  110. }
  111. }
  112. },
  113. _inLink: function(/*DomNode*/ node){
  114. // summary:
  115. // Check if the node is already embraced within a <a>...</a> tag.
  116. // node:
  117. // The node to be examed.
  118. // tags:
  119. // private
  120. var editNode = this.editor.editNode,
  121. result = false, tagName;
  122. node = node.parentNode;
  123. while(node && node !== editNode){
  124. tagName = node.tagName ? node.tagName.toLowerCase() : "";
  125. if(tagName == "a"){
  126. result = true;
  127. break;
  128. }
  129. node = node.parentNode;
  130. }
  131. return result;
  132. },
  133. _findLastEditingNode: function(/*DomNode*/ node){
  134. // summary:
  135. // Find the last node that was edited so that we can
  136. // get the last edited text.
  137. // node:
  138. // The current node that the cursor is at.
  139. // tags:
  140. // private
  141. var blockTagNames = dijit.range.BlockTagNames,
  142. editNode = this.editor.editNode, blockNode;
  143. if(!node){ return node; }
  144. if(this.blockNodeForEnter == "BR" &&
  145. (!(blockNode = dijit.range.getBlockAncestor(node, null, editNode).blockNode) ||
  146. blockNode.tagName.toUpperCase() != "LI")){
  147. while((node = node.previousSibling) && node.nodeType != 3){}
  148. }else{
  149. // EnterKeyHandling is under "DIV" or "P" mode or
  150. // it's in a LI element. Find the last editing block
  151. if((blockNode || (blockNode = dijit.range.getBlockAncestor(node, null, editNode).blockNode)) &&
  152. blockNode.tagName.toUpperCase() == "LI"){
  153. node = blockNode;
  154. }else{
  155. node = dijit.range.getBlockAncestor(node, null, editNode).blockNode;
  156. }
  157. // Find the last editing text node
  158. while((node = node.previousSibling) && !(node.tagName && node.tagName.match(blockTagNames))){}
  159. if(node){
  160. node = node.lastChild;
  161. while(node){
  162. if(node.nodeType == 3 && dojo.trim(node.nodeValue) != ""){
  163. break;
  164. }else if(node.nodeType == 1){
  165. node = node.lastChild;
  166. }else{
  167. node = node.previousSibling;
  168. }
  169. }
  170. }
  171. }
  172. return node;
  173. },
  174. _findUrls: function(/*DomNode*/ node, /*DomNode*/ bm, /*Number*/ bmOff){
  175. // summary:
  176. // Find the occurrace of the URL strings.
  177. // FF, Chrome && Safri have a behavior that when insertHTML is executed,
  178. // the orignal referrence to the text node will be the text node next to
  179. // the inserted anchor automatically. So we have to re-caculate the index of
  180. // the following URL occurrence.
  181. // value:
  182. // A text to be scanned.
  183. // tags:
  184. // private
  185. var pattern = /(http|https|ftp):\/\/[^\s]+/ig,
  186. list = [], baseIndex = 0,
  187. value = node.nodeValue, result, ch;
  188. if(node === bm && bmOff < value.length){
  189. // Break the text so that it may not grab extra words.
  190. // Such as if you type:
  191. // foo http://foo.com|bar (And | is where you press enter).
  192. // It will grab the bar word as part of the link. That's annoying/bad.
  193. // Also it prevents recognizing the text after the cursor.
  194. value = value.substr(0, bmOff);
  195. }
  196. while((result = pattern.exec(value)) != null){
  197. if(result.index == 0 || (ch = value.charAt(result.index - 1)) == " " || ch == "\xA0"){
  198. list.push({start: result.index - baseIndex, end: result.index + result[0].length - baseIndex});
  199. baseIndex = result.index + result[0].length;
  200. }
  201. }
  202. return list;
  203. }
  204. });
  205. // Register this plugin.
  206. dojo.subscribe(dijit._scopeName + ".Editor.getPlugin",null,function(o){
  207. if(o.plugin){ return; }
  208. var name = o.args.name.toLowerCase();
  209. if(name === "autourllink"){
  210. o.plugin = new dojox.editor.plugins.AutoUrlLink();
  211. }
  212. });
  213. return dojox.editor.plugins.AutoUrlLink;
  214. });