summaryrefslogtreecommitdiff
path: root/js/dojo-1.6/dojox/editor/plugins/AutoUrlLink.js
blob: a51a1da8fd1465ce21a10a5d6781cc2cd2207219 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
/*
	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();
	}
});

}