AutoUrlLink.js 7.4 KB

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