/*
Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved.
Available via Academic Free License >= 2.1 OR the modified BSD license.
see: http://dojotoolkit.org/license for details
*/
if(!dojo._hasResource["dojox.editor.plugins.AutoUrlLink"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.editor.plugins.AutoUrlLink"] = true;
dojo.provide("dojox.editor.plugins.AutoUrlLink");
dojo.require("dojo.string");
dojo.require("dijit._editor._Plugin");
dojo.require("dijit.form.Button");
dojo.declare("dojox.editor.plugins.AutoUrlLink", [dijit._editor._Plugin], {
//summary:
// This plugin can recognize a URL like string
// (such as http://www.website.com) and turn it into
// a hyperlink that points to that URL.
// _template [private] String
// The link template
_template: "<a _djrealurl='${url}' href='${url}'>${url}</a>",
setEditor: function(/*dijit.Editor*/ editor){
// summary:
// Called by the editor it belongs to.
// editor:
// The editor it belongs to.
this.editor = editor;
if(!dojo.isIE){
// IE will recognize URL as a link automatically
// No need to re-invent the wheel.
dojo.some(editor._plugins, function(plugin){
// Need to detect which enter key mode it is now
if(plugin.isInstanceOf(dijit._editor.plugins.EnterKeyHandling)){
this.blockNodeForEnter = plugin.blockNodeForEnter;
return true;
}
return false;
}, this);
this.connect(editor, "onKeyPress", "_keyPress");
this.connect(editor, "onClick", "_recognize");
this.connect(editor, "onBlur", "_recognize");
}
},
_keyPress: function(evt){
// summary:
// Handle the keypress event and dispatch it to the target handler
// evt:
// The keypress event object.
// tags:
// protected
var ks = dojo.keys, v = 118, V = 86,
kc = evt.keyCode, cc = evt.charCode;
if(cc == ks.SPACE || (evt.ctrlKey && (cc == v || cc == V))){
setTimeout(dojo.hitch(this, "_recognize"), 0);
}else if(kc == ks.ENTER){
// Handle the enter event after EnterKeyHandling finishes its job
setTimeout(dojo.hitch(this, function(){
this._recognize({enter: true});
}), 0);
}else{
// _saved: The previous dom node when the cursor is at a new dom node.
// When we click elsewhere, the previous dom node
// should be examed to see if there is any URL need to be activated
this._saved = this.editor.window.getSelection().anchorNode;
}
},
_recognize: function(args){
// summary:
// Recognize the URL like strings and turn them into a link
// tags:
// private
var template = this._template,
isEnter = args ? args.enter : false,
ed = this.editor,
selection = ed.window.getSelection();
if(selection){
var node = isEnter ? this._findLastEditingNode(selection.anchorNode) :
(this._saved || selection.anchorNode),
bm = this._saved = selection.anchorNode,
bmOff = selection.anchorOffset;
if(node.nodeType == 3 && !this._inLink(node)){
var linked = false, result = this._findUrls(node, bm, bmOff),
range = ed.document.createRange(),
item, cost = 0, isSameNode = (bm == node);
item = result.shift();
while(item){
// Covert a URL to a link.
range.setStart(node, item.start);
range.setEnd(node, item.end);
selection.removeAllRanges();
selection.addRange(range);
ed.execCommand("insertHTML", dojo.string.substitute(template, {url: range.toString()}));
cost += item.end;
item = result.shift();
linked = true;
}
// If bm and node are the some dom node, caculate the actual bookmark offset
// If the position of the cursor is modified (turned into a link, etc.), no
// need to recover the cursor position
if(isSameNode && (bmOff = bmOff - cost) <= 0){ return; }
// We didn't update anything, so don't collapse selections.
if(!linked) { return ; }
try{
// Try to recover the cursor position
range.setStart(bm, 0);
range.setEnd(bm, bmOff);
selection.removeAllRanges();
selection.addRange(range);
dojo.withGlobal(ed.window, "collapse", dijit._editor.selection, []);
}catch(e){}
}
}
},
_inLink: function(/*DomNode*/ node){
// summary:
// Check if the node is already embraced within a <a>...</a> tag.
// node:
// The node to be examed.
// tags:
// private
var editNode = this.editor.editNode,
result = false, tagName;
node = node.parentNode;
while(node && node !== editNode){
tagName = node.tagName ? node.tagName.toLowerCase() : "";
if(tagName == "a"){
result = true;
break;
}
node = node.parentNode;
}
return result;
},
_findLastEditingNode: function(/*DomNode*/ node){
// summary:
// Find the last node that was edited so that we can
// get the last edited text.
// node:
// The current node that the cursor is at.
// tags:
// private
var blockTagNames = dijit.range.BlockTagNames,
editNode = this.editor.editNode, blockNode;
if(!node){ return node; }
if(this.blockNodeForEnter == "BR" &&
(!(blockNode = dijit.range.getBlockAncestor(node, null, editNode).blockNode) ||
blockNode.tagName.toUpperCase() != "LI")){
while((node = node.previousSibling) && node.nodeType != 3){}
}else{
// EnterKeyHandling is under "DIV" or "P" mode or
// it's in a LI element. Find the last editing block
if((blockNode || (blockNode = dijit.range.getBlockAncestor(node, null, editNode).blockNode)) &&
blockNode.tagName.toUpperCase() == "LI"){
node = blockNode;
}else{
node = dijit.range.getBlockAncestor(node, null, editNode).blockNode;
}
// Find the last editing text node
while((node = node.previousSibling) && !(node.tagName && node.tagName.match(blockTagNames))){}
if(node){
node = node.lastChild;
while(node){
if(node.nodeType == 3 && dojo.trim(node.nodeValue) != ""){
break;
}else if(node.nodeType == 1){
node = node.lastChild;
}else{
node = node.previousSibling;
}
}
}
}
return node;
},
_findUrls: function(/*DomNode*/ node, /*DomNode*/ bm, /*Number*/ bmOff){
// summary:
// Find the occurrace of the URL strings.
// FF, Chrome && Safri have a behavior that when insertHTML is executed,
// the orignal referrence to the text node will be the text node next to
// the inserted anchor automatically. So we have to re-caculate the index of
// the following URL occurrence.
// value:
// A text to be scanned.
// tags:
// private
var pattern = /(http|https|ftp):\/\/[^\s]+/ig,
list = [], baseIndex = 0,
value = node.nodeValue, result, ch;
if(node === bm && bmOff < value.length){
// Break the text so that it may not grab extra words.
// Such as if you type:
// foo http://foo.com|bar (And | is where you press enter).
// It will grab the bar word as part of the link. That's annoying/bad.
// Also it prevents recognizing the text after the cursor.
value = value.substr(0, bmOff);
}
while((result = pattern.exec(value)) != null){
if(result.index == 0 || (ch = value.charAt(result.index - 1)) == " " || ch == "\xA0"){
list.push({start: result.index - baseIndex, end: result.index + result[0].length - baseIndex});
baseIndex = result.index + result[0].length;
}
}
return list;
}
});
// Register this plugin.
dojo.subscribe(dijit._scopeName + ".Editor.getPlugin",null,function(o){
if(o.plugin){ return; }
var name = o.args.name.toLowerCase();
if(name === "autourllink"){
o.plugin = new dojox.editor.plugins.AutoUrlLink();
}
});
}