Index: _source/plugins/link/dialogs/link.js
===================================================================
--- _source/plugins/link/dialogs/link.js	(revision 5237)
+++ _source/plugins/link/dialogs/link.js	Wed Mar 17 00:14:22 CST 2010
@@ -1,10 +1,11 @@
-/*
+/*
 Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
 For licensing, see LICENSE.html or http://ckeditor.com/license
 */
 
 CKEDITOR.dialog.add( 'link', function( editor )
 {
+	var plugin = CKEDITOR.plugins.link;
 	// Handles the event when the "Target" selection box is changed.
 	var targetChanged = function()
 	{
@@ -93,7 +94,7 @@
 
 	var parseLink = function( editor, element )
 	{
-		var href = element ? ( element.getAttribute( '_cke_saved_href' ) || element.getAttribute( 'href' ) ) : '',
+		var href = ( element  && ( element.getAttribute( '_cke_saved_href' ) || element.getAttribute( 'href' ) ) ) || '',
 		 	javascriptMatch,
 			emailMatch,
 			anchorMatch,
@@ -1136,30 +1137,21 @@
 
 			var editor = this.getParentEditor(),
 				selection = editor.getSelection(),
-				ranges = selection.getRanges(),
-				element = null,
-				me = this;
-			// Fill in all the relevant fields if there's already one link selected.
-			if ( ranges.length == 1 )
-			{
+				element = null;
 
-				var rangeRoot = ranges[0].getCommonAncestor( true );
-				element = rangeRoot.getAscendant( 'a', true );
-				if ( element && element.getAttribute( 'href' ) )
-				{
+			// Fill in all the relevant fields if there's already one link selected.
+			if ( ( element = plugin.getSelectedLink( editor ) ) && element.hasAttribute( 'href' ) )
-					selection.selectElement( element );
+				selection.selectElement( element );
-				}
-				else if ( ( element = rangeRoot.getAscendant( 'img', true ) ) &&
-						 element.getAttribute( '_cke_real_element_type' ) &&
-						 element.getAttribute( '_cke_real_element_type' ) == 'anchor' )
+			else if ( ( element = selection.getSelectedElement() ) && element.is( 'img' )
+					&& element.getAttribute( '_cke_real_element_type' )
+					&& element.getAttribute( '_cke_real_element_type' ) == 'anchor' )
-				{
-					this.fakeObj = element;
-					element = editor.restoreRealElement( this.fakeObj );
-					selection.selectElement( this.fakeObj );
-				}
-				else
-					element = null;
+			{
+				this.fakeObj = element;
+				element = editor.restoreRealElement( this.fakeObj );
+				selection.selectElement( this.fakeObj );
+			}
+			else
+				element = null;
-			}
 
 			this.setupContent( parseLink.apply( this, [ editor, element ] ) );
 		},
Index: _source/plugins/link/plugin.js
===================================================================
--- _source/plugins/link/plugin.js	(revision 5206)
+++ _source/plugins/link/plugin.js	Wed Mar 17 00:06:59 CST 2010
@@ -107,7 +107,7 @@
 
 					if ( !isAnchor )
 					{
-						if ( !( element = element.getAscendant( 'a', true ) ) )
+						if ( !( element = CKEDITOR.plugins.link.getSelectedLink( editor ) ) )
 							return null;
 
 						isAnchor = ( element.getAttribute( 'name' ) && !element.getAttribute( 'href' ) );
@@ -147,6 +147,35 @@
 	requires : [ 'fakeobjects' ]
 } );
 
+CKEDITOR.plugins.link =
+{
+	/**
+	 *  Get the surrounding link element of current selection.
+	 * @param editor
+	 * @example CKEDITOR.plugins.link.getSelectedLink( editor );
+	 * The following selection will all return the link element.   
+	 *	 <pre>
+	 *  <a href="#">li^nk</a>
+	 *  <a href="#">[link]</a>
+	 *  text[<a href="#">link]</a>
+	 *  <a href="#">li[nk</a>]
+	 *  [<b><a href="#">li]nk</a></b>]
+	 *  [<a href="#"><b>li]nk</b></a>
+	 * </pre>
+	 */
+	getSelectedLink : function( editor )
+	{
+		var range;
+		try { range  = editor.getSelection().getRanges()[ 0 ]; }
+		catch( e ) { return null; }
+
+		range.shrink();
+		var root = range.getCommonAncestor();
+		return root.getAscendant( 'a', true );
+	}
+
+};
+
 CKEDITOR.unlinkCommand = function(){};
 CKEDITOR.unlinkCommand.prototype =
 {
Index: _source/core/dom/range.js
===================================================================
--- _source/core/dom/range.js	(revision 5220)
+++ _source/core/dom/range.js	Wed Mar 17 00:14:10 CST 2010
@@ -1236,6 +1236,88 @@
 		},
 
 		/**
+		 *  Descrease the range to make sure that boundaries
+		 *  always anchor beside text nodes.
+		 */
+		shrink : function()
+		{
+			// Unable to shrink a collapsed range.
+			if( !this.collapsed )
+			{
+				var walkerRange = this.clone();
+
+				var startContainer = this.startContainer,
+					endContainer = this.endContainer,
+					startOffset = this.startOffset,
+					endOffset = this.endOffset;
+
+				// Whether the start/end boundary is moveable.
+				var moveStart = 1,
+						moveEnd = 1;
+
+				if ( startContainer && startContainer.type == CKEDITOR.NODE_TEXT )
+				{
+					if ( !startOffset )
+						walkerRange.setStartBefore( startContainer );
+					else if ( startOffset >= startContainer.getLength() )
+						walkerRange.setStartAfter( startContainer );
+					else
+					{
+						// Enlarge the range properly to avoid walker making
+						// DOM changes caused by triming the text nodes later.
+						walkerRange.setStartBefore( startContainer );
+						moveStart = 0;
+					}
+				}
+				
+				if ( endContainer && endContainer.type == CKEDITOR.NODE_TEXT )
+				{
+					if ( !endOffset )
+						walkerRange.setEndBefore( endContainer );
+					else if ( endOffset >= endContainer.getLength() )
+						walkerRange.setEndAfter( endContainer );
+					else
+					{
+						walkerRange.setEndAfter( endContainer );
+						moveEnd = 0;
+					}
+				}
+
+				var walker = new CKEDITOR.dom.walker( walkerRange );
+				walker.evaluator = function( node )
+				{
+					return node.type == CKEDITOR.NODE_TEXT;
+				};
+
+				var currentElement;
+				walker.guard = function( node, movingOut )
+				{
+					// Stop when we've walked "through" an element.
+					if ( movingOut && node.equals( currentElement ) )
+						return false;
+
+					if ( !movingOut && node.type == CKEDITOR.NODE_ELEMENT )
+						currentElement = node;
+				};
+
+				if( moveStart )
+				{
+					var textStart = walker.next();
+					textStart && this.setStartBefore( textStart );
+				}
+
+				if ( moveEnd )
+				{
+					walker.reset();
+					var textEnd = walker.previous();
+					textEnd && this.setEndAfter( textEnd );
+				}
+				
+				return !!( moveStart || moveEnd );
+			}
+		},
+
+		/**
 		 * Inserts a node at the start of the range. The range will be expanded
 		 * the contain the node.
 		 */
Index: _source/core/dom/walker.js
===================================================================
--- _source/core/dom/walker.js	(revision 5206)
+++ _source/core/dom/walker.js	Wed Mar 17 00:07:02 CST 2010
@@ -101,7 +101,7 @@
 						node = null;
 				}
 				else
-					node = ( guard ( node ) === false ) ?
+					node = ( guard ( node, true ) === false ) ?
 						null : node.getPreviousSourceNode( true, type, guard );
 			}
 			else
@@ -115,7 +115,7 @@
 						node = null;
 				}
 				else
-					node = ( guard ( range.startContainer ) === false ) ?
+					node = ( guard ( range.startContainer, true ) === false ) ?
 						null : range.startContainer.getNextSourceNode( true, type, guard ) ;
 			}
 		}
