Index: _source/core/dom/element.js
===================================================================
--- _source/core/dom/element.js (revision 6745)
+++ _source/core/dom/element.js (working copy)
@@ -429,6 +429,9 @@
name = 'className';
break;
+ case 'name':
+ return this.$.name;
+
case 'tabindex':
var tabIndex = standard.call( this, name );
@@ -884,12 +887,29 @@
* @param {String} name The attribute name.
* @example
*/
- hasAttribute : function( name )
+ hasAttribute : (function()
{
- var $attr = this.$.attributes.getNamedItem( name );
- return !!( $attr && $attr.specified );
- },
+ function standard( name )
+ {
+ var $attr = this.$.attributes.getNamedItem( name );
+ return !!( $attr && $attr.specified );
+ }
+
+ return ( CKEDITOR.env.ie && CKEDITOR.env.version < 8 ) ?
+ function( name )
+ {
+ // On IE < 8 the name attribute cannot be retrieved
+ // right after the element creation and setting the
+ // name with setAttribute.
+ if ( name == 'name' )
+ return !!this.$.name;
+ return standard( name );
+ }
+ :
+ standard;
+ })(),
+
/**
* Hides this element (display:none).
* @example
Index: _source/plugins/elementspath/plugin.js
===================================================================
--- _source/plugins/elementspath/plugin.js (revision 6745)
+++ _source/plugins/elementspath/plugin.js (working copy)
@@ -124,24 +124,28 @@
while ( element )
{
- var ignore = 0;
+ var ignore = 0,
+ name;
+
+ if ( element.data( 'cke-real-element-type' ) )
+ name = element.data( 'cke-real-element-type' );
+ else
+ name = element.getName();
+
for ( var i = 0; i < filters.length; i++ )
{
- if ( filters[ i ]( element ) === false )
+ var ret = filters[ i ]( element, name );
+ if ( ret === false )
{
ignore = 1;
break;
}
+ name = ret || name;
}
if ( !ignore )
{
var index = elementsList.push( element ) - 1;
- var name;
- if ( element.data( 'cke-real-element-type' ) )
- name = element.data( 'cke-real-element-type' );
- else
- name = element.getName();
// Use this variable to add conditional stuff to the
// HTML (because we are doing it in reverse order... unshift).
Index: _source/plugins/forms/plugin.js
===================================================================
--- _source/plugins/forms/plugin.js (revision 6745)
+++ _source/plugins/forms/plugin.js (working copy)
@@ -261,24 +261,28 @@
if ( CKEDITOR.env.ie )
{
- CKEDITOR.dom.element.prototype.hasAttribute = function( name )
- {
- var $attr = this.$.attributes.getNamedItem( name );
-
- if ( this.getName() == 'input' )
+ CKEDITOR.dom.element.prototype.hasAttribute = CKEDITOR.tools.override( CKEDITOR.dom.element.prototype.hasAttribute,
+ function( original )
{
- switch ( name )
- {
- case 'class' :
- return this.$.className.length > 0;
- case 'checked' :
- return !!this.$.checked;
- case 'value' :
- var type = this.getAttribute( 'type' );
- return type == 'checkbox' || type == 'radio' ? this.$.value != 'on' : this.$.value;
- }
- }
+ return function( name )
+ {
+ var $attr = this.$.attributes.getNamedItem( name );
- return !!( $attr && $attr.specified );
- };
+ if ( this.getName() == 'input' )
+ {
+ switch ( name )
+ {
+ case 'class' :
+ return this.$.className.length > 0;
+ case 'checked' :
+ return !!this.$.checked;
+ case 'value' :
+ var type = this.getAttribute( 'type' );
+ return type == 'checkbox' || type == 'radio' ? this.$.value != 'on' : this.$.value;
+ }
+ }
+
+ return original.apply( this, arguments );
+ };
+ });
}
Index: _source/plugins/htmldataprocessor/plugin.js
===================================================================
--- _source/plugins/htmldataprocessor/plugin.js (revision 6745)
+++ _source/plugins/htmldataprocessor/plugin.js (working copy)
@@ -95,14 +95,7 @@
delete blockLikeTags.pre;
var defaultDataFilterRules =
{
- elements : {
- a : function( element )
- {
- var attrs = element.attributes;
- if ( attrs && attrs[ 'data-cke-saved-name' ] )
- attrs[ 'class' ] = ( attrs[ 'class' ] ? attrs[ 'class' ] + ' ' : '' ) + 'cke_anchor';
- }
- },
+ elements : {},
attributeNames :
[
// Event attributes (onXYZ) must not be directly set. They can become
Index: _source/plugins/link/dialogs/anchor.js
===================================================================
--- _source/plugins/link/dialogs/anchor.js (revision 6745)
+++ _source/plugins/link/dialogs/anchor.js (working copy)
@@ -6,68 +6,113 @@
CKEDITOR.dialog.add( 'anchor', function( editor )
{
// Function called in onShow to load selected element.
- var loadElements = function( editor, selection, element )
+ var loadElements = function( element )
{
- this.editMode = true;
- this.editObj = element;
+ this._.selectedElement = element;
- var attributeValue = this.editObj.getAttribute( 'name' );
- if ( attributeValue )
- this.setValueOf( 'info','txtName', attributeValue );
- else
- this.setValueOf( 'info','txtName', "" );
+ var attributeValue = element.data( 'cke-saved-name' );
+ this.setValueOf( 'info','txtName', attributeValue || '' );
};
+ function createFakeAnchor( editor, anchor )
+ {
+ return editor.createFakeElement( anchor, 'cke_anchor', 'a' );
+ }
+
return {
title : editor.lang.anchor.title,
minWidth : 300,
minHeight : 60,
onOk : function()
{
- // Always create a new anchor, because of IE BUG.
- var name = this.getValueOf( 'info', 'txtName' ),
- element = CKEDITOR.env.ie && !( CKEDITOR.document.$.documentMode >= 8 ) ?
- editor.document.createElement( '' ) :
- editor.document.createElement( 'a' );
+ var name = this.getValueOf( 'info', 'txtName' );
+ var attributes =
+ {
+ name : name,
+ 'data-cke-saved-name' : name
+ };
- // Move contents and attributes of old anchor to new anchor.
- if ( this.editMode )
+ if ( this._.selectedElement )
{
- this.editObj.copyAttributes( element, { name : 1 } );
- this.editObj.moveChildren( element );
+ if ( this._.selectedElement.data( 'cke-realelement' ) )
+ {
+ var newFake = createFakeAnchor( editor, editor.document.createElement( 'a', { attributes: attributes } ) );
+ newFake.replace( this._.selectedElement );
+ }
+ else
+ this._.selectedElement.setAttributes( attributes );
}
+ else
+ {
+ var sel = editor.getSelection(),
+ range = sel && sel.getRanges()[ 0 ];
- // Set name.
- element.data( 'cke-saved-name', false );
- element.setAttribute( 'name', name );
+ // Empty anchor
+ if ( range.collapsed )
+ {
+ if ( CKEDITOR.env.ie && CKEDITOR.env.version < 9 )
+ attributes['class'] = 'cke_anchor_empty';
- // Insert a new anchor.
- var fakeElement = editor.createFakeElement( element, 'cke_anchor', 'anchor' );
- if ( !this.editMode )
- editor.insertElement( fakeElement );
- else
- {
- fakeElement.replace( this.fakeObj );
- editor.getSelection().selectElement( fakeElement );
+ if ( CKEDITOR.env.ie && CKEDITOR.env.version < 8 )
+ {
+ attributes[ 'contenteditable' ] = 'false';
+ attributes[ 'data-cke-editable' ] = 1;
+ }
+
+ var anchor = editor.document.createElement( 'a', { attributes: attributes } );
+
+ // Transform the anchor into a fake element for browsers that need it.
+ if ( CKEDITOR.plugins.link.fakeAnchor )
+ anchor = createFakeAnchor( editor, anchor );
+
+ range.insertNode( anchor );
+ }
+ else
+ {
+ if ( CKEDITOR.env.ie && CKEDITOR.env.version < 9 )
+ attributes['class'] = 'cke_anchor';
+
+ // Apply style.
+ var style = new CKEDITOR.style( { element : 'a', attributes : attributes } );
+ style.type = CKEDITOR.STYLE_INLINE;
+ style.apply( editor.document );
+ }
}
+ },
- return true;
+ onHide : function()
+ {
+ delete this._.selectedElement;
},
+
onShow : function()
{
- this.editObj = false;
- this.fakeObj = false;
- this.editMode = false;
+ var selection = editor.getSelection(),
+ fullySelected = selection.getSelectedElement(),
+ partialSelected;
- var selection = editor.getSelection();
- var element = selection.getSelectedElement();
- if ( element && element.data( 'cke-real-element-type' ) && element.data( 'cke-real-element-type' ) == 'anchor' )
+ // Detect the anchor under selection.
+ if ( fullySelected )
{
- this.fakeObj = element;
- element = editor.restoreRealElement( this.fakeObj );
- loadElements.apply( this, [ editor, selection, element ] );
- selection.selectElement( this.fakeObj );
+ if ( CKEDITOR.plugins.link.fakeAnchor )
+ {
+ var realElement = CKEDITOR.plugins.link.tryRestoreFakeAnchor( editor, fullySelected );
+ realElement && loadElements.call( this, realElement );
+ this._.selectedElement = fullySelected;
+ }
+ else if ( fullySelected.is( 'a' ) && fullySelected.hasAttribute( 'name' ) )
+ loadElements.call( this, fullySelected );
}
+ else
+ {
+ partialSelected = CKEDITOR.plugins.link.getSelectedLink( editor );
+ if ( partialSelected )
+ {
+ loadElements.call( this, partialSelected );
+ selection.selectElement( partialSelected );
+ }
+ }
+
this.getContentElement( 'info', 'txtName' ).focus();
},
contents : [
Index: _source/plugins/link/dialogs/link.js
===================================================================
--- _source/plugins/link/dialogs/link.js (revision 6745)
+++ _source/plugins/link/dialogs/link.js (working copy)
@@ -235,37 +235,26 @@
advAttr( 'advTabIndex', 'tabindex' );
advAttr( 'advTitle', 'title' );
advAttr( 'advContentType', 'type' );
- advAttr( 'advCSSClasses', 'class' );
advAttr( 'advCharset', 'charset' );
advAttr( 'advStyles', 'style' );
advAttr( 'advRel', 'rel' );
+
+ retval.adv.advCSSClasses = getLinkClass( element ) || '';
}
// Find out whether we have any anchors in the editor.
- // Get all IMG elements in CK document.
- var elements = editor.document.getElementsByTag( 'img' ),
- realAnchors = new CKEDITOR.dom.nodeList( editor.document.$.anchors ),
- anchors = retval.anchors = [];
+ var anchorList = new CKEDITOR.dom.nodeList( editor.document.$.anchors ),
+ anchors = retval.anchors = [],
+ item;
- for ( var i = 0; i < elements.count() ; i++ )
+ for ( var i = 0, count = anchorList.count(); i < count; i++ )
{
- var item = elements.getItem( i );
- if ( item.data( 'cke-realelement' ) && item.data( 'cke-real-element-type' ) == 'anchor' )
- anchors.push( editor.restoreRealElement( item ) );
- }
-
- for ( i = 0 ; i < realAnchors.count() ; i++ )
- anchors.push( realAnchors.getItem( i ) );
-
- for ( i = 0 ; i < anchors.length ; i++ )
- {
- item = anchors[ i ];
+ item = anchorList.getItem( i );
anchors[ i ] = { name : item.getAttribute( 'name' ), id : item.getAttribute( 'id' ) };
}
// Record down the selected element in the dialog.
this._.selectedElement = element;
-
return retval;
};
@@ -369,6 +358,12 @@
return 'String.fromCharCode(' + encodedChars.join( ',' ) + ')';
}
+ function getLinkClass( ele )
+ {
+ var className = ele.getAttribute( 'class' );
+ return className ? className.replace( /\s*(?:cke_anchor_empty|cke_anchor)(?:\s*$)?/g, '' ) : '';
+ }
+
var commonLang = editor.lang.common,
linkLang = editor.lang.link;
@@ -1151,8 +1146,6 @@
],
onShow : function()
{
- this.fakeObj = false;
-
var editor = this.getParentEditor(),
selection = editor.getSelection(),
element = null;
@@ -1160,14 +1153,6 @@
// Fill in all the relevant fields if there's already one link selected.
if ( ( element = plugin.getSelectedLink( editor ) ) && element.hasAttribute( 'href' ) )
selection.selectElement( element );
- else if ( ( element = selection.getSelectedElement() ) && element.is( 'img' )
- && element.data( 'cke-real-element-type' )
- && element.data( 'cke-real-element-type' ) == 'anchor' )
- {
- this.fakeObj = element;
- element = editor.restoreRealElement( this.fakeObj );
- selection.selectElement( this.fakeObj );
- }
else
element = null;
@@ -1302,10 +1287,7 @@
advAttr( 'advAccessKey', 'accessKey' );
if ( data.adv[ 'advName' ] )
- {
attributes[ 'name' ] = attributes[ 'data-cke-saved-name' ] = data.adv[ 'advName' ];
- attributes[ 'class' ] = ( attributes[ 'class' ] ? attributes[ 'class' ] + ' ' : '' ) + 'cke_anchor';
- }
else
removeAttributes = removeAttributes.concat( [ 'data-cke-saved-name', 'name' ] );
@@ -1350,25 +1332,12 @@
href = element.data( 'cke-saved-href' ),
textView = element.getHtml();
- // IE BUG: Setting the name attribute to an existing link doesn't work.
- // Must re-create the link from weired syntax to workaround.
- if ( CKEDITOR.env.ie && !( CKEDITOR.document.$.documentMode >= 8 ) && attributes.name != element.getAttribute( 'name' ) )
- {
- var newElement = new CKEDITOR.dom.element( '',
- editor.document );
-
- selection = editor.getSelection();
-
- element.copyAttributes( newElement, { name : 1 } );
- element.moveChildren( newElement );
- newElement.replace( element );
- element = newElement;
-
- selection.selectElement( element );
- }
-
element.setAttributes( attributes );
element.removeAttributes( removeAttributes );
+
+ if ( data.adv && data.adv.advName )
+ element.addClass( element.getChildCount() ? 'cke_anchor' : 'cke_anchor_empty' );
+
// Update text view when user changes protocol (#4612).
if ( href == textView || data.type == 'email' && textView.indexOf( '@' ) != -1 )
{
@@ -1376,15 +1345,7 @@
element.setHtml( data.type == 'email' ?
data.email.address : attributes[ 'data-cke-saved-href' ] );
}
- // Make the element display as an anchor if a name has been set.
- if ( element.getAttribute( 'name' ) )
- element.addClass( 'cke_anchor' );
- else
- element.removeClass( 'cke_anchor' );
- if ( this.fakeObj )
- editor.createFakeElement( element, 'cke_anchor', 'anchor' ).replace( this.fakeObj );
-
delete this._.selectedElement;
}
},
Index: _source/plugins/link/plugin.js
===================================================================
--- _source/plugins/link/plugin.js (revision 6745)
+++ _source/plugins/link/plugin.js (working copy)
@@ -30,26 +30,39 @@
CKEDITOR.dialog.add( 'anchor', this.path + 'dialogs/anchor.js' );
// Add the CSS styles for anchor placeholders.
- var side = editor.lang.dir == 'rtl' ? 'right' : 'left';
+
+ var side = ( editor.lang.dir == 'rtl' ? 'right' : 'left' );
+ var basicCss =
+ 'background:url(' + CKEDITOR.getUrl( this.path + 'images/anchor.gif' ) + ') no-repeat ' + side + ' center;' +
+ 'border:1px dotted #00f;';
+
editor.addCss(
+ 'a.cke_anchor,a.cke_anchor_empty' +
+ // IE6 breaks with the following selectors.
+ ( ( CKEDITOR.env.ie && CKEDITOR.env.version < 7 ) ? '' :
+ ',a[name],a[data-cke-saved-name]' ) +
+ '{' +
+ basicCss +
+ 'padding-' + side + ':18px;' +
+ // Show the arrow cursor for the anchor image (FF at least).
+ 'cursor:auto;' +
+ '}' +
+ ( ( CKEDITOR.env.ie && CKEDITOR.env.version > 8 ) ? 'a[name]:empty,' : '' ) +
+ 'a.cke_anchor_empty' +
+ '{' +
+ // Make empty anchor selectable on IE.
+ 'display:inline-block;' +
+ '}' +
'img.cke_anchor' +
'{' +
- 'background-image: url(' + CKEDITOR.getUrl( this.path + 'images/anchor.gif' ) + ');' +
- 'background-position: center center;' +
- 'background-repeat: no-repeat;' +
- 'border: 1px solid #a9a9a9;' +
- 'width: 18px !important;' +
- 'height: 18px !important;' +
- '}\n' +
- 'a.cke_anchor' +
- '{' +
- 'background-image: url(' + CKEDITOR.getUrl( this.path + 'images/anchor.gif' ) + ');' +
- 'background-position: ' + side + ' center;' +
- 'background-repeat: no-repeat;' +
- 'border: 1px solid #a9a9a9;' +
- 'padding-' + side + ': 18px;' +
- '}'
- );
+ basicCss +
+ 'width:16px;' +
+ 'min-height:15px;' +
+ // The default line-height on IE.
+ 'height:1.15em;' +
+ // Opera works better with "middle" (even if not perfect)
+ 'vertical-align:' + ( CKEDITOR.env.opera ? 'middle' : 'text-bottom' ) + ';' +
+ '}');
// Register selection change handler for the unlink button.
editor.on( 'selectionChange', function( evt )
@@ -73,8 +86,11 @@
if ( !element.isReadOnly() )
{
if ( element.is( 'a' ) )
- evt.data.dialog = ( element.getAttribute( 'name' ) && !element.getAttribute( 'href' ) ) ? 'anchor' : 'link';
- else if ( element.is( 'img' ) && element.data( 'cke-real-element-type' ) == 'anchor' )
+ {
+ evt.data.dialog = ( element.getAttribute( 'name' ) && !element.getAttribute( 'href' ) ) ? 'anchor' : 'link';
+ editor.getSelection().selectElement( element );
+ }
+ else if ( CKEDITOR.plugins.link.tryRestoreFakeAnchor( editor, element ) )
evt.data.dialog = 'anchor';
}
});
@@ -117,19 +133,20 @@
if ( !element || element.isReadOnly() )
return null;
- var isAnchor = ( element.is( 'img' ) && element.data( 'cke-real-element-type' ) == 'anchor' );
+ var anchor = CKEDITOR.plugins.link.tryRestoreFakeAnchor( editor, element );
- if ( !isAnchor )
- {
- if ( !( element = CKEDITOR.plugins.link.getSelectedLink( editor ) ) )
+ if ( !anchor && !( anchor = CKEDITOR.plugins.link.getSelectedLink( editor ) ) )
return null;
- isAnchor = ( element.getAttribute( 'name' ) && !element.getAttribute( 'href' ) );
- }
+ var menu = {};
- return isAnchor ?
- { anchor : CKEDITOR.TRISTATE_OFF } :
- { link : CKEDITOR.TRISTATE_OFF, unlink : CKEDITOR.TRISTATE_OFF };
+ if ( anchor.getAttribute( 'href' ) )
+ menu = { link : CKEDITOR.TRISTATE_OFF, unlink : CKEDITOR.TRISTATE_OFF };
+
+ if ( anchor && anchor.getAttribute( 'name' ) )
+ menu.anchor = CKEDITOR.TRISTATE_OFF;
+
+ return menu;
});
}
},
@@ -139,7 +156,9 @@
// Register a filter to displaying placeholders after mode change.
var dataProcessor = editor.dataProcessor,
- dataFilter = dataProcessor && dataProcessor.dataFilter;
+ dataFilter = dataProcessor && dataProcessor.dataFilter,
+ htmlFilter = dataProcessor && dataProcessor.htmlFilter,
+ pathFilters = editor._.elementsPath.filters;
if ( dataFilter )
{
@@ -150,12 +169,61 @@
a : function( element )
{
var attributes = element.attributes;
- if ( attributes.name && !attributes.href )
- return editor.createFakeParserElement( element, 'cke_anchor', 'anchor' );
+ if ( !attributes.name )
+ return;
+
+ var isEmpty = !element.children.length;
+
+ if ( CKEDITOR.env.ie && CKEDITOR.env.version < 9 )
+ {
+ // IE needs a specific class name to be applied
+ // to the anchors, for appropriate styling.
+ var ieClass = isEmpty ? 'cke_anchor_empty' : 'cke_anchor';
+ var cls = attributes[ 'class' ];
+ if ( attributes.name && ( !cls || cls.indexOf( ieClass ) < 0 ) )
+ attributes[ 'class' ] = ( cls || '' ) + ' ' + ieClass;
+
+ if ( isEmpty && CKEDITOR.env.version < 8 )
+ {
+ attributes.contenteditable = 'false';
+ attributes[ 'data-cke-editable' ] = 1;
+ }
+ }
+ else if ( CKEDITOR.plugins.link.fakeAnchor && isEmpty )
+ return editor.createFakeParserElement( element, 'cke_anchor', 'a' );
}
}
});
}
+
+ if ( CKEDITOR.env.ie && htmlFilter )
+ {
+ htmlFilter.addRules(
+ {
+ elements :
+ {
+ a : function( element )
+ {
+ delete element.attributes.contenteditable;
+ }
+ }
+ });
+ }
+
+ if ( pathFilters )
+ {
+ pathFilters.push( function( element, name )
+ {
+ if ( name == 'a' )
+ {
+ if ( CKEDITOR.plugins.link.tryRestoreFakeAnchor( editor, element ) ||
+ ( element.getAttribute( 'name' ) && !element.getAttribute( 'href' ) ) )
+ {
+ return 'anchor';
+ }
+ }
+ });
+ }
},
requires : [ 'fakeobjects' ]
@@ -196,6 +264,20 @@
return root.getAscendant( 'a', true );
}
catch( e ) { return null; }
+ },
+
+ // Opera and WebKit don't make it possible to select empty anchors. Fake
+ // elements must be used for them.
+ fakeAnchor : CKEDITOR.env.opera || CKEDITOR.env.webkit,
+
+ tryRestoreFakeAnchor : function( editor, element )
+ {
+ if ( element && element.data( 'cke-real-element-type' ) && element.data( 'cke-real-element-type' ) == 'a' )
+ {
+ var link = editor.restoreRealElement( element );
+ if ( link.data( 'cke-saved-name' ) )
+ return link;
+ }
}
};