/*\r
-Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.\r
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.\r
For licensing, see LICENSE.html or http://ckeditor.com/license\r
*/\r
\r
CKEDITOR.plugins.add( 'link',\r
{\r
+ requires : [ 'fakeobjects', 'dialog' ],\r
init : function( editor )\r
{\r
// Add the link and unlink buttons.\r
editor.addCommand( 'link', new CKEDITOR.dialogCommand( 'link' ) );\r
editor.addCommand( 'anchor', new CKEDITOR.dialogCommand( 'anchor' ) );\r
editor.addCommand( 'unlink', new CKEDITOR.unlinkCommand() );\r
+ editor.addCommand( 'removeAnchor', new CKEDITOR.removeAnchorCommand() );\r
editor.ui.addButton( 'Link',\r
{\r
label : editor.lang.link.toolbar,\r
CKEDITOR.dialog.add( 'anchor', this.path + 'dialogs/anchor.js' );\r
\r
// Add the CSS styles for anchor placeholders.\r
- var side = editor.lang.dir == 'rtl' ? 'right' : 'left';\r
+\r
+ var side = ( editor.lang.dir == 'rtl' ? 'right' : 'left' );\r
+ var basicCss =\r
+ 'background:url(' + CKEDITOR.getUrl( this.path + 'images/anchor.gif' ) + ') no-repeat ' + side + ' center;' +\r
+ 'border:1px dotted #00f;';\r
+\r
editor.addCss(\r
- 'img.cke_anchor' +\r
+ 'a.cke_anchor,a.cke_anchor_empty' +\r
+ // IE6 breaks with the following selectors.\r
+ ( ( CKEDITOR.env.ie && CKEDITOR.env.version < 7 ) ? '' :\r
+ ',a[name],a[data-cke-saved-name]' ) +\r
'{' +\r
- 'background-image: url(' + CKEDITOR.getUrl( this.path + 'images/anchor.gif' ) + ');' +\r
- 'background-position: center center;' +\r
- 'background-repeat: no-repeat;' +\r
- 'border: 1px solid #a9a9a9;' +\r
- 'width: 18px !important;' +\r
- 'height: 18px !important;' +\r
- '}\n' +\r
- 'a.cke_anchor' +\r
+ basicCss +\r
+ 'padding-' + side + ':18px;' +\r
+ // Show the arrow cursor for the anchor image (FF at least).\r
+ 'cursor:auto;' +\r
+ '}' +\r
+ ( CKEDITOR.env.ie ? (\r
+ 'a.cke_anchor_empty' +\r
+ '{' +\r
+ // Make empty anchor selectable on IE.\r
+ 'display:inline-block;' +\r
+ '}'\r
+ ) : '' ) +\r
+ 'img.cke_anchor' +\r
'{' +\r
- 'background-image: url(' + CKEDITOR.getUrl( this.path + 'images/anchor.gif' ) + ');' +\r
- 'background-position: ' + side + ' center;' +\r
- 'background-repeat: no-repeat;' +\r
- 'border: 1px solid #a9a9a9;' +\r
- 'padding-' + side + ': 18px;' +\r
- '}'\r
- );\r
+ basicCss +\r
+ 'width:16px;' +\r
+ 'min-height:15px;' +\r
+ // The default line-height on IE.\r
+ 'height:1.15em;' +\r
+ // Opera works better with "middle" (even if not perfect)\r
+ 'vertical-align:' + ( CKEDITOR.env.opera ? 'middle' : 'text-bottom' ) + ';' +\r
+ '}');\r
\r
// Register selection change handler for the unlink button.\r
editor.on( 'selectionChange', function( evt )\r
{\r
+ if ( editor.readOnly )\r
+ return;\r
+\r
/*\r
* Despite our initial hope, document.queryCommandEnabled() does not work\r
* for this in Firefox. So we must detect the state by element paths.\r
*/\r
var command = editor.getCommand( 'unlink' ),\r
element = evt.data.path.lastElement && evt.data.path.lastElement.getAscendant( 'a', true );\r
- if ( element && element.getName() == 'a' && element.getAttribute( 'href' ) )\r
+ if ( element && element.getName() == 'a' && element.getAttribute( 'href' ) && element.getChildCount() )\r
command.setState( CKEDITOR.TRISTATE_OFF );\r
else\r
command.setState( CKEDITOR.TRISTATE_DISABLED );\r
if ( !element.isReadOnly() )\r
{\r
if ( element.is( 'a' ) )\r
- evt.data.dialog = ( element.getAttribute( 'name' ) && !element.getAttribute( 'href' ) ) ? 'anchor' : 'link';\r
- else if ( element.is( 'img' ) && element.data( 'cke-real-element-type' ) == 'anchor' )\r
+ {\r
+ evt.data.dialog = ( element.getAttribute( 'name' ) && ( !element.getAttribute( 'href' ) || !element.getChildCount() ) ) ? 'anchor' : 'link';\r
+ editor.getSelection().selectElement( element );\r
+ }\r
+ else if ( CKEDITOR.plugins.link.tryRestoreFakeAnchor( editor, element ) )\r
evt.data.dialog = 'anchor';\r
}\r
});\r
{\r
label : editor.lang.anchor.menu,\r
command : 'anchor',\r
- group : 'anchor'\r
+ group : 'anchor',\r
+ order : 1\r
+ },\r
+\r
+ removeAnchor :\r
+ {\r
+ label : editor.lang.anchor.remove,\r
+ command : 'removeAnchor',\r
+ group : 'anchor',\r
+ order : 5\r
},\r
\r
link :\r
if ( !element || element.isReadOnly() )\r
return null;\r
\r
- var isAnchor = ( element.is( 'img' ) && element.data( 'cke-real-element-type' ) == 'anchor' );\r
+ var anchor = CKEDITOR.plugins.link.tryRestoreFakeAnchor( editor, element );\r
\r
- if ( !isAnchor )\r
- {\r
- if ( !( element = CKEDITOR.plugins.link.getSelectedLink( editor ) ) )\r
+ if ( !anchor && !( anchor = CKEDITOR.plugins.link.getSelectedLink( editor ) ) )\r
return null;\r
\r
- isAnchor = ( element.getAttribute( 'name' ) && !element.getAttribute( 'href' ) );\r
- }\r
+ var menu = {};\r
+\r
+ if ( anchor.getAttribute( 'href' ) && anchor.getChildCount() )\r
+ menu = { link : CKEDITOR.TRISTATE_OFF, unlink : CKEDITOR.TRISTATE_OFF };\r
+\r
+ if ( anchor && anchor.hasAttribute( 'name' ) )\r
+ menu.anchor = menu.removeAnchor = CKEDITOR.TRISTATE_OFF;\r
\r
- return isAnchor ?\r
- { anchor : CKEDITOR.TRISTATE_OFF } :\r
- { link : CKEDITOR.TRISTATE_OFF, unlink : CKEDITOR.TRISTATE_OFF };\r
+ return menu;\r
});\r
}\r
},\r
// Register a filter to displaying placeholders after mode change.\r
\r
var dataProcessor = editor.dataProcessor,\r
- dataFilter = dataProcessor && dataProcessor.dataFilter;\r
+ dataFilter = dataProcessor && dataProcessor.dataFilter,\r
+ htmlFilter = dataProcessor && dataProcessor.htmlFilter,\r
+ pathFilters = editor._.elementsPath && editor._.elementsPath.filters;\r
\r
if ( dataFilter )\r
{\r
a : function( element )\r
{\r
var attributes = element.attributes;\r
- if ( attributes.name && !attributes.href )\r
+ if ( !attributes.name )\r
+ return null;\r
+\r
+ var isEmpty = !element.children.length;\r
+\r
+ if ( CKEDITOR.plugins.link.synAnchorSelector )\r
+ {\r
+ // IE needs a specific class name to be applied\r
+ // to the anchors, for appropriate styling.\r
+ var ieClass = isEmpty ? 'cke_anchor_empty' : 'cke_anchor';\r
+ var cls = attributes[ 'class' ];\r
+ if ( attributes.name && ( !cls || cls.indexOf( ieClass ) < 0 ) )\r
+ attributes[ 'class' ] = ( cls || '' ) + ' ' + ieClass;\r
+\r
+ if ( isEmpty && CKEDITOR.plugins.link.emptyAnchorFix )\r
+ {\r
+ attributes.contenteditable = 'false';\r
+ attributes[ 'data-cke-editable' ] = 1;\r
+ }\r
+ }\r
+ else if ( CKEDITOR.plugins.link.fakeAnchor && isEmpty )\r
return editor.createFakeParserElement( element, 'cke_anchor', 'anchor' );\r
+\r
+ return null;\r
+ }\r
+ }\r
+ });\r
+ }\r
+\r
+ if ( CKEDITOR.plugins.link.emptyAnchorFix && htmlFilter )\r
+ {\r
+ htmlFilter.addRules(\r
+ {\r
+ elements :\r
+ {\r
+ a : function( element )\r
+ {\r
+ delete element.attributes.contenteditable;\r
}\r
}\r
});\r
}\r
- },\r
\r
- requires : [ 'fakeobjects' ]\r
+ if ( pathFilters )\r
+ {\r
+ pathFilters.push( function( element, name )\r
+ {\r
+ if ( name == 'a' )\r
+ {\r
+ if ( CKEDITOR.plugins.link.tryRestoreFakeAnchor( editor, element ) ||\r
+ ( element.getAttribute( 'name' ) && ( !element.getAttribute( 'href' ) || !element.getChildCount() ) ) )\r
+ {\r
+ return 'anchor';\r
+ }\r
+ }\r
+ });\r
+ }\r
+ }\r
} );\r
\r
CKEDITOR.plugins.link =\r
return root.getAscendant( 'a', true );\r
}\r
catch( e ) { return null; }\r
+ },\r
+\r
+ // Opera and WebKit don't make it possible to select empty anchors. Fake\r
+ // elements must be used for them.\r
+ fakeAnchor : CKEDITOR.env.opera || CKEDITOR.env.webkit,\r
+\r
+ // For browsers that don't support CSS3 a[name]:empty(), note IE9 is included because of #7783.\r
+ synAnchorSelector : CKEDITOR.env.ie,\r
+\r
+ // For browsers that have editing issue with empty anchor.\r
+ emptyAnchorFix : CKEDITOR.env.ie && CKEDITOR.env.version < 8,\r
+\r
+ tryRestoreFakeAnchor : function( editor, element )\r
+ {\r
+ if ( element && element.data( 'cke-real-element-type' ) && element.data( 'cke-real-element-type' ) == 'anchor' )\r
+ {\r
+ var link = editor.restoreRealElement( element );\r
+ if ( link.data( 'cke-saved-name' ) )\r
+ return link;\r
+ }\r
}\r
};\r
\r
startDisabled : true\r
};\r
\r
+CKEDITOR.removeAnchorCommand = function(){};\r
+CKEDITOR.removeAnchorCommand.prototype =\r
+{\r
+ /** @ignore */\r
+ exec : function( editor )\r
+ {\r
+ var sel = editor.getSelection(),\r
+ bms = sel.createBookmarks(),\r
+ anchor;\r
+ if ( sel && ( anchor = sel.getSelectedElement() ) && ( CKEDITOR.plugins.link.fakeAnchor && !anchor.getChildCount() ? CKEDITOR.plugins.link.tryRestoreFakeAnchor( editor, anchor ) : anchor.is( 'a' ) ) )\r
+ anchor.remove( 1 );\r
+ else\r
+ {\r
+ if ( ( anchor = CKEDITOR.plugins.link.getSelectedLink( editor ) ) )\r
+ {\r
+ if ( anchor.hasAttribute( 'href' ) )\r
+ {\r
+ anchor.removeAttributes( { name : 1, 'data-cke-saved-name' : 1 } );\r
+ anchor.removeClass( 'cke_anchor' );\r
+ }\r
+ else\r
+ anchor.remove( 1 );\r
+ }\r
+ }\r
+ sel.selectBookmarks( bms );\r
+ }\r
+};\r
+\r
CKEDITOR.tools.extend( CKEDITOR.config,\r
{\r
linkShowAdvancedTab : true,\r