Ticket #3582: 3582_14.patch
File 3582_14.patch, 22.4 KB (added by , 14 years ago) |
---|
-
_source/core/dom/element.js
429 429 name = 'className'; 430 430 break; 431 431 432 case 'name': 433 return this.$.name; 434 432 435 case 'tabindex': 433 436 var tabIndex = standard.call( this, name ); 434 437 … … 884 887 * @param {String} name The attribute name. 885 888 * @example 886 889 */ 887 hasAttribute : function( name)890 hasAttribute : (function() 888 891 { 889 var $attr = this.$.attributes.getNamedItem( name ); 890 return !!( $attr && $attr.specified ); 891 }, 892 892 function standard( name ) 893 { 894 var $attr = this.$.attributes.getNamedItem( name ); 895 return !!( $attr && $attr.specified ); 896 } 897 898 return ( CKEDITOR.env.ie && CKEDITOR.env.version < 8 ) ? 899 function( name ) 900 { 901 // On IE < 8 the name attribute cannot be retrieved 902 // right after the element creation and setting the 903 // name with setAttribute. 904 if ( name == 'name' ) 905 return !!this.$.name; 906 907 return standard.call( this, name ); 908 } 909 : 910 standard; 911 })(), 912 893 913 /** 894 914 * Hides this element (display:none). 895 915 * @example -
_source/plugins/forms/plugin.js
261 261 262 262 if ( CKEDITOR.env.ie ) 263 263 { 264 CKEDITOR.dom.element.prototype.hasAttribute = function( name ) 265 { 266 var $attr = this.$.attributes.getNamedItem( name ); 264 CKEDITOR.dom.element.prototype.hasAttribute = CKEDITOR.tools.override( CKEDITOR.dom.element.prototype.hasAttribute, 265 function( original ) 266 { 267 return function( name ) 268 { 269 var $attr = this.$.attributes.getNamedItem( name ); 267 270 268 if ( this.getName() == 'input' )269 {270 switch ( name )271 {272 case 'class' :273 return this.$.className.length > 0;274 case 'checked' :275 return !!this.$.checked;276 case 'value' :277 var type = this.getAttribute( 'type' );278 return type == 'checkbox' || type == 'radio' ? this.$.value != 'on' : this.$.value;279 }280 }271 if ( this.getName() == 'input' ) 272 { 273 switch ( name ) 274 { 275 case 'class' : 276 return this.$.className.length > 0; 277 case 'checked' : 278 return !!this.$.checked; 279 case 'value' : 280 var type = this.getAttribute( 'type' ); 281 return type == 'checkbox' || type == 'radio' ? this.$.value != 'on' : this.$.value; 282 } 283 } 281 284 282 return !!( $attr && $attr.specified ); 283 }; 285 return original.apply( this, arguments ); 286 }; 287 }); 284 288 } -
_source/plugins/link/dialogs/anchor.js
6 6 CKEDITOR.dialog.add( 'anchor', function( editor ) 7 7 { 8 8 // Function called in onShow to load selected element. 9 var loadElements = function( e ditor, selection, element )9 var loadElements = function( element ) 10 10 { 11 this.editMode = true; 12 this.editObj = element; 11 this._.selectedElement = element; 13 12 14 var attributeValue = this.editObj.getAttribute( 'name' ); 15 if ( attributeValue ) 16 this.setValueOf( 'info','txtName', attributeValue ); 17 else 18 this.setValueOf( 'info','txtName', "" ); 13 var attributeValue = element.data( 'cke-saved-name' ); 14 this.setValueOf( 'info','txtName', attributeValue || '' ); 19 15 }; 20 16 17 function createFakeAnchor( editor, anchor ) 18 { 19 return editor.createFakeElement( anchor, 'cke_anchor', 'anchor' ); 20 } 21 21 22 return { 22 23 title : editor.lang.anchor.title, 23 24 minWidth : 300, 24 25 minHeight : 60, 25 26 onOk : function() 26 27 { 27 // Always create a new anchor, because of IE BUG. 28 var name = this.getValueOf( 'info', 'txtName' ), 29 element = CKEDITOR.env.ie && !( CKEDITOR.document.$.documentMode >= 8 ) ? 30 editor.document.createElement( '<a name="' + CKEDITOR.tools.htmlEncode( name ) + '">' ) : 31 editor.document.createElement( 'a' ); 28 var name = this.getValueOf( 'info', 'txtName' ); 29 var attributes = 30 { 31 name : name, 32 'data-cke-saved-name' : name 33 }; 32 34 33 // Move contents and attributes of old anchor to new anchor. 34 if ( this.editMode ) 35 if ( this._.selectedElement ) 35 36 { 36 this.editObj.copyAttributes( element, { name : 1 } ); 37 this.editObj.moveChildren( element ); 38 } 37 if ( this._.selectedElement.data( 'cke-realelement' ) ) 38 { 39 var newFake = createFakeAnchor( editor, editor.document.createElement( 'a', { attributes: attributes } ) ); 40 newFake.replace( this._.selectedElement ); 41 } 42 else 43 this._.selectedElement.setAttributes( attributes ); 44 } 45 else 46 { 47 var sel = editor.getSelection(), 48 range = sel && sel.getRanges()[ 0 ]; 39 49 40 // Set name. 41 element.data( 'cke-saved-name', false ); 42 element.setAttribute( 'name', name ); 50 // Empty anchor 51 if ( range.collapsed ) 52 { 53 if ( CKEDITOR.env.ie && CKEDITOR.env.version < 9 ) 54 attributes['class'] = 'cke_anchor_empty'; 43 55 44 // Insert a new anchor. 45 var fakeElement = editor.createFakeElement( element, 'cke_anchor', 'anchor' ); 46 if ( !this.editMode ) 47 editor.insertElement( fakeElement ); 48 else 49 { 50 fakeElement.replace( this.fakeObj ); 51 editor.getSelection().selectElement( fakeElement ); 52 } 56 if ( CKEDITOR.env.ie && CKEDITOR.env.version < 8 ) 57 { 58 attributes[ 'contenteditable' ] = 'false'; 59 attributes[ 'data-cke-editable' ] = 1; 60 } 61 62 var anchor = editor.document.createElement( 'a', { attributes: attributes } ); 63 64 // Transform the anchor into a fake element for browsers that need it. 65 if ( CKEDITOR.plugins.link.fakeAnchor ) 66 anchor = createFakeAnchor( editor, anchor ); 67 68 range.insertNode( anchor ); 69 } 70 else 71 { 72 if ( CKEDITOR.env.ie && CKEDITOR.env.version < 9 ) 73 attributes['class'] = 'cke_anchor'; 74 75 // Apply style. 76 var style = new CKEDITOR.style( { element : 'a', attributes : attributes } ); 77 style.type = CKEDITOR.STYLE_INLINE; 78 style.apply( editor.document ); 79 } 80 } 81 }, 53 82 54 return true; 83 onHide : function() 84 { 85 delete this._.selectedElement; 55 86 }, 87 56 88 onShow : function() 57 89 { 58 this.editObj = false;59 this.fakeObj = false;60 this.editMode = false;90 var selection = editor.getSelection(), 91 fullySelected = selection.getSelectedElement(), 92 partialSelected; 61 93 62 var selection = editor.getSelection(); 63 var element = selection.getSelectedElement(); 64 if ( element && element.data( 'cke-real-element-type' ) && element.data( 'cke-real-element-type' ) == 'anchor' ) 94 // Detect the anchor under selection. 95 if ( fullySelected ) 65 96 { 66 this.fakeObj = element; 67 element = editor.restoreRealElement( this.fakeObj ); 68 loadElements.apply( this, [ editor, selection, element ] ); 69 selection.selectElement( this.fakeObj ); 70 } 97 if ( CKEDITOR.plugins.link.fakeAnchor ) 98 { 99 var realElement = CKEDITOR.plugins.link.tryRestoreFakeAnchor( editor, fullySelected ); 100 realElement && loadElements.call( this, realElement ); 101 this._.selectedElement = fullySelected; 102 } 103 else if ( fullySelected.is( 'a' ) && fullySelected.hasAttribute( 'name' ) ) 104 loadElements.call( this, fullySelected ); 105 } 106 else 107 { 108 partialSelected = CKEDITOR.plugins.link.getSelectedLink( editor ); 109 if ( partialSelected ) 110 { 111 loadElements.call( this, partialSelected ); 112 selection.selectElement( partialSelected ); 113 } 114 } 115 71 116 this.getContentElement( 'info', 'txtName' ).focus(); 72 117 }, 73 118 contents : [ -
_source/plugins/htmldataprocessor/plugin.js
95 95 delete blockLikeTags.pre; 96 96 var defaultDataFilterRules = 97 97 { 98 elements : { 99 a : function( element ) 100 { 101 var attrs = element.attributes; 102 if ( attrs && attrs[ 'data-cke-saved-name' ] ) 103 attrs[ 'class' ] = ( attrs[ 'class' ] ? attrs[ 'class' ] + ' ' : '' ) + 'cke_anchor'; 104 } 105 }, 98 elements : {}, 106 99 attributeNames : 107 100 [ 108 101 // Event attributes (onXYZ) must not be directly set. They can become -
_source/plugins/link/dialogs/link.js
235 235 advAttr( 'advTabIndex', 'tabindex' ); 236 236 advAttr( 'advTitle', 'title' ); 237 237 advAttr( 'advContentType', 'type' ); 238 advAttr( 'advCSSClasses', 'class' ); 238 CKEDITOR.plugins.link.synAnchorSelector ? 239 retval.adv.advCSSClasses = getLinkClass( element ) 240 : advAttr( 'advCSSClasses', 'class' ); 239 241 advAttr( 'advCharset', 'charset' ); 240 242 advAttr( 'advStyles', 'style' ); 241 243 advAttr( 'advRel', 'rel' ); 242 244 } 243 245 244 246 // Find out whether we have any anchors in the editor. 245 // Get all IMG elements in CK document. 246 var elements = editor.document.getElementsByTag( 'img' ), 247 realAnchors = new CKEDITOR.dom.nodeList( editor.document.$.anchors ), 248 anchors = retval.anchors = []; 247 var anchorList = new CKEDITOR.dom.nodeList( editor.document.$.anchors ), 248 anchors = retval.anchors = [], 249 item; 249 250 250 for ( var i = 0 ; i < elements.count(); i++ )251 for ( var i = 0, count = anchorList.count(); i < count; i++ ) 251 252 { 252 var item = elements.getItem( i ); 253 if ( item.data( 'cke-realelement' ) && item.data( 'cke-real-element-type' ) == 'anchor' ) 254 anchors.push( editor.restoreRealElement( item ) ); 255 } 256 257 for ( i = 0 ; i < realAnchors.count() ; i++ ) 258 anchors.push( realAnchors.getItem( i ) ); 259 260 for ( i = 0 ; i < anchors.length ; i++ ) 261 { 262 item = anchors[ i ]; 253 item = anchorList.getItem( i ); 263 254 anchors[ i ] = { name : item.getAttribute( 'name' ), id : item.getAttribute( 'id' ) }; 264 255 } 265 256 266 257 // Record down the selected element in the dialog. 267 258 this._.selectedElement = element; 268 269 259 return retval; 270 260 }; 271 261 … … 369 359 return 'String.fromCharCode(' + encodedChars.join( ',' ) + ')'; 370 360 } 371 361 362 function getLinkClass( ele ) 363 { 364 var className = ele.getAttribute( 'class' ); 365 return className ? className.replace( /\s*(?:cke_anchor_empty|cke_anchor)(?:\s*$)?/g, '' ) : ''; 366 } 367 372 368 var commonLang = editor.lang.common, 373 369 linkLang = editor.lang.link; 374 370 … … 1151 1147 ], 1152 1148 onShow : function() 1153 1149 { 1154 this.fakeObj = false;1155 1156 1150 var editor = this.getParentEditor(), 1157 1151 selection = editor.getSelection(), 1158 1152 element = null; … … 1160 1154 // Fill in all the relevant fields if there's already one link selected. 1161 1155 if ( ( element = plugin.getSelectedLink( editor ) ) && element.hasAttribute( 'href' ) ) 1162 1156 selection.selectElement( element ); 1163 else if ( ( element = selection.getSelectedElement() ) && element.is( 'img' )1164 && element.data( 'cke-real-element-type' )1165 && element.data( 'cke-real-element-type' ) == 'anchor' )1166 {1167 this.fakeObj = element;1168 element = editor.restoreRealElement( this.fakeObj );1169 selection.selectElement( this.fakeObj );1170 }1171 1157 else 1172 1158 element = null; 1173 1159 … … 1302 1288 advAttr( 'advAccessKey', 'accessKey' ); 1303 1289 1304 1290 if ( data.adv[ 'advName' ] ) 1305 {1306 1291 attributes[ 'name' ] = attributes[ 'data-cke-saved-name' ] = data.adv[ 'advName' ]; 1307 attributes[ 'class' ] = ( attributes[ 'class' ] ? attributes[ 'class' ] + ' ' : '' ) + 'cke_anchor';1308 }1309 1292 else 1310 1293 removeAttributes = removeAttributes.concat( [ 'data-cke-saved-name', 'name' ] ); 1311 1294 … … 1350 1333 href = element.data( 'cke-saved-href' ), 1351 1334 textView = element.getHtml(); 1352 1335 1353 // IE BUG: Setting the name attribute to an existing link doesn't work.1354 // Must re-create the link from weired syntax to workaround.1355 if ( CKEDITOR.env.ie && !( CKEDITOR.document.$.documentMode >= 8 ) && attributes.name != element.getAttribute( 'name' ) )1356 {1357 var newElement = new CKEDITOR.dom.element( '<a name="' + CKEDITOR.tools.htmlEncode( attributes.name ) + '">',1358 editor.document );1359 1360 selection = editor.getSelection();1361 1362 element.copyAttributes( newElement, { name : 1 } );1363 element.moveChildren( newElement );1364 newElement.replace( element );1365 element = newElement;1366 1367 selection.selectElement( element );1368 }1369 1370 1336 element.setAttributes( attributes ); 1371 1337 element.removeAttributes( removeAttributes ); 1338 1339 if ( data.adv && data.adv.advName && CKEDITOR.plugins.link.synAnchorSelector ) 1340 element.addClass( element.getChildCount() ? 'cke_anchor' : 'cke_anchor_empty' ); 1341 1372 1342 // Update text view when user changes protocol (#4612). 1373 1343 if ( href == textView || data.type == 'email' && textView.indexOf( '@' ) != -1 ) 1374 1344 { … … 1376 1346 element.setHtml( data.type == 'email' ? 1377 1347 data.email.address : attributes[ 'data-cke-saved-href' ] ); 1378 1348 } 1379 // Make the element display as an anchor if a name has been set.1380 if ( element.getAttribute( 'name' ) )1381 element.addClass( 'cke_anchor' );1382 else1383 element.removeClass( 'cke_anchor' );1384 1349 1385 if ( this.fakeObj )1386 editor.createFakeElement( element, 'cke_anchor', 'anchor' ).replace( this.fakeObj );1387 1388 1350 delete this._.selectedElement; 1389 1351 } 1390 1352 }, -
_source/plugins/elementspath/plugin.js
124 124 125 125 while ( element ) 126 126 { 127 var ignore = 0; 127 var ignore = 0, 128 name; 129 130 if ( element.data( 'cke-real-element-type' ) ) 131 name = element.data( 'cke-real-element-type' ); 132 else 133 name = element.getName(); 134 128 135 for ( var i = 0; i < filters.length; i++ ) 129 136 { 130 if ( filters[ i ]( element ) === false ) 137 var ret = filters[ i ]( element, name ); 138 if ( ret === false ) 131 139 { 132 140 ignore = 1; 133 141 break; 134 142 } 143 name = ret || name; 135 144 } 136 145 137 146 if ( !ignore ) 138 147 { 139 148 var index = elementsList.push( element ) - 1; 140 var name;141 if ( element.data( 'cke-real-element-type' ) )142 name = element.data( 'cke-real-element-type' );143 else144 name = element.getName();145 149 146 150 // Use this variable to add conditional stuff to the 147 151 // HTML (because we are doing it in reverse order... unshift). -
_source/plugins/link/plugin.js
30 30 CKEDITOR.dialog.add( 'anchor', this.path + 'dialogs/anchor.js' ); 31 31 32 32 // Add the CSS styles for anchor placeholders. 33 var side = editor.lang.dir == 'rtl' ? 'right' : 'left'; 33 34 var side = ( editor.lang.dir == 'rtl' ? 'right' : 'left' ); 35 var basicCss = 36 'background:url(' + CKEDITOR.getUrl( this.path + 'images/anchor.gif' ) + ') no-repeat ' + side + ' center;' + 37 'border:1px dotted #00f;'; 38 34 39 editor.addCss( 35 'img.cke_anchor' + 40 'a.cke_anchor,a.cke_anchor_empty' + 41 // IE6 breaks with the following selectors. 42 ( ( CKEDITOR.env.ie && CKEDITOR.env.version < 7 ) ? '' : 43 ',a[name],a[data-cke-saved-name]' ) + 36 44 '{' + 37 'background-image: url(' + CKEDITOR.getUrl( this.path + 'images/anchor.gif' ) + ');' + 38 'background-position: center center;' + 39 'background-repeat: no-repeat;' + 40 'border: 1px solid #a9a9a9;' + 41 'width: 18px !important;' + 42 'height: 18px !important;' + 43 '}\n' + 44 'a.cke_anchor' + 45 '{' + 46 'background-image: url(' + CKEDITOR.getUrl( this.path + 'images/anchor.gif' ) + ');' + 47 'background-position: ' + side + ' center;' + 48 'background-repeat: no-repeat;' + 49 'border: 1px solid #a9a9a9;' + 50 'padding-' + side + ': 18px;' + 51 '}' 52 ); 45 basicCss + 46 'padding-' + side + ':18px;' + 47 // Show the arrow cursor for the anchor image (FF at least). 48 'cursor:auto;' + 49 '}' + 50 ( ( CKEDITOR.env.ie && CKEDITOR.env.version > 8 ) ? 'a[name]:empty,' : '' ) + 51 'a.cke_anchor_empty' + 52 '{' + 53 // Make empty anchor selectable on IE. 54 'display:inline-block;' + 55 '}' + 56 'img.cke_anchor' + 57 '{' + 58 basicCss + 59 'width:16px;' + 60 'min-height:15px;' + 61 // The default line-height on IE. 62 'height:1.15em;' + 63 // Opera works better with "middle" (even if not perfect) 64 'vertical-align:' + ( CKEDITOR.env.opera ? 'middle' : 'text-bottom' ) + ';' + 65 '}'); 53 66 54 67 // Register selection change handler for the unlink button. 55 68 editor.on( 'selectionChange', function( evt ) … … 60 73 */ 61 74 var command = editor.getCommand( 'unlink' ), 62 75 element = evt.data.path.lastElement && evt.data.path.lastElement.getAscendant( 'a', true ); 63 if ( element && element.getName() == 'a' && element.getAttribute( 'href' ) )76 if ( element && element.getName() == 'a' && element.getAttribute( 'href' ) && element.getChildCount() ) 64 77 command.setState( CKEDITOR.TRISTATE_OFF ); 65 78 else 66 79 command.setState( CKEDITOR.TRISTATE_DISABLED ); … … 73 86 if ( !element.isReadOnly() ) 74 87 { 75 88 if ( element.is( 'a' ) ) 76 evt.data.dialog = ( element.getAttribute( 'name' ) && !element.getAttribute( 'href' ) ) ? 'anchor' : 'link'; 77 else if ( element.is( 'img' ) && element.data( 'cke-real-element-type' ) == 'anchor' ) 89 { 90 evt.data.dialog = ( element.getAttribute( 'name' ) && ( !element.getAttribute( 'href' ) || !element.getChildCount() ) ) ? 'anchor' : 'link'; 91 editor.getSelection().selectElement( element ); 92 } 93 else if ( CKEDITOR.plugins.link.tryRestoreFakeAnchor( editor, element ) ) 78 94 evt.data.dialog = 'anchor'; 79 95 } 80 96 }); … … 117 133 if ( !element || element.isReadOnly() ) 118 134 return null; 119 135 120 var isAnchor = ( element.is( 'img' ) && element.data( 'cke-real-element-type' ) == 'anchor');136 var anchor = CKEDITOR.plugins.link.tryRestoreFakeAnchor( editor, element ); 121 137 122 if ( !isAnchor ) 123 { 124 if ( !( element = CKEDITOR.plugins.link.getSelectedLink( editor ) ) ) 138 if ( !anchor && !( anchor = CKEDITOR.plugins.link.getSelectedLink( editor ) ) ) 125 139 return null; 126 140 127 isAnchor = ( element.getAttribute( 'name' ) && !element.getAttribute( 'href' ) ); 128 } 141 var menu = {}; 129 142 130 return isAnchor ? 131 { anchor : CKEDITOR.TRISTATE_OFF } : 132 { link : CKEDITOR.TRISTATE_OFF, unlink : CKEDITOR.TRISTATE_OFF }; 143 if ( anchor.getAttribute( 'href' ) && anchor.getChildCount() ) 144 menu = { link : CKEDITOR.TRISTATE_OFF, unlink : CKEDITOR.TRISTATE_OFF }; 145 146 if ( anchor && anchor.hasAttribute( 'name' ) ) 147 menu.anchor = CKEDITOR.TRISTATE_OFF; 148 149 return menu; 133 150 }); 134 151 } 135 152 }, … … 139 156 // Register a filter to displaying placeholders after mode change. 140 157 141 158 var dataProcessor = editor.dataProcessor, 142 dataFilter = dataProcessor && dataProcessor.dataFilter; 159 dataFilter = dataProcessor && dataProcessor.dataFilter, 160 htmlFilter = dataProcessor && dataProcessor.htmlFilter, 161 pathFilters = editor._.elementsPath.filters; 143 162 144 163 if ( dataFilter ) 145 164 { … … 150 169 a : function( element ) 151 170 { 152 171 var attributes = element.attributes; 153 if ( attributes.name && !attributes.href ) 172 if ( !attributes.name ) 173 return; 174 175 var isEmpty = !element.children.length; 176 177 if ( CKEDITOR.plugins.link.synAnchorSelector ) 178 { 179 // IE needs a specific class name to be applied 180 // to the anchors, for appropriate styling. 181 var ieClass = isEmpty ? 'cke_anchor_empty' : 'cke_anchor'; 182 var cls = attributes[ 'class' ]; 183 if ( attributes.name && ( !cls || cls.indexOf( ieClass ) < 0 ) ) 184 attributes[ 'class' ] = ( cls || '' ) + ' ' + ieClass; 185 186 if ( isEmpty && CKEDITOR.plugins.link.emptyAnchorFix ) 187 { 188 attributes.contenteditable = 'false'; 189 attributes[ 'data-cke-editable' ] = 1; 190 } 191 } 192 else if ( CKEDITOR.plugins.link.fakeAnchor && isEmpty ) 154 193 return editor.createFakeParserElement( element, 'cke_anchor', 'anchor' ); 155 194 } 156 195 } 157 196 }); 158 197 } 198 199 if ( CKEDITOR.plugins.link.emptyAnchorFix && htmlFilter ) 200 { 201 htmlFilter.addRules( 202 { 203 elements : 204 { 205 a : function( element ) 206 { 207 delete element.attributes.contenteditable; 208 } 209 } 210 }); 211 } 212 213 if ( pathFilters ) 214 { 215 pathFilters.push( function( element, name ) 216 { 217 if ( name == 'a' ) 218 { 219 if ( CKEDITOR.plugins.link.tryRestoreFakeAnchor( editor, element ) || 220 ( element.getAttribute( 'name' ) && ( !element.getAttribute( 'href' ) || !element.getChildCount() ) ) ) 221 { 222 return 'anchor'; 223 } 224 } 225 }); 226 } 159 227 }, 160 228 161 229 requires : [ 'fakeobjects' ] … … 196 264 return root.getAscendant( 'a', true ); 197 265 } 198 266 catch( e ) { return null; } 199 } 267 }, 268 269 // Opera and WebKit don't make it possible to select empty anchors. Fake 270 // elements must be used for them. 271 fakeAnchor : CKEDITOR.env.opera || CKEDITOR.env.webkit, 272 273 // For browsers that don't support CSS3 a[name]:empty(). 274 synAnchorSelector : CKEDITOR.env.ie && CKEDITOR.env.version < 9, 275 276 // For browsers that have editing issue with empty anchor. 277 emptyAnchorFix : CKEDITOR.env.ie && CKEDITOR.env.version < 8, 278 279 tryRestoreFakeAnchor : function( editor, element ) 280 { 281 if ( element && element.data( 'cke-real-element-type' ) && element.data( 'cke-real-element-type' ) == 'anchor' ) 282 { 283 var link = editor.restoreRealElement( element ); 284 if ( link.data( 'cke-saved-name' ) ) 285 return link; 286 } 287 } 200 288 }; 201 289 202 290 CKEDITOR.unlinkCommand = function(){};