+ * Merges sibling elements that are identical to this one.<br>\r
+ * <br>\r
+ * Identical child elements are also merged. For example:<br>\r
+ * <b><i></i></b><b><i></i></b> => <b><i></i></b>\r
+ * @function\r
+ * @param {Boolean} [inlineOnly] Allow only inline elements to be merged. Defaults to "true".\r
+ */\r
+ mergeSiblings : ( function()\r
+ {\r
+ function mergeElements( element, sibling, isNext )\r
+ {\r
+ if ( sibling && sibling.type == CKEDITOR.NODE_ELEMENT )\r
+ {\r
+ // Jumping over bookmark nodes and empty inline elements, e.g. <b><i></i></b>,\r
+ // queuing them to be moved later. (#5567)\r
+ var pendingNodes = [];\r
+\r
+ while ( sibling.data( 'cke-bookmark' )\r
+ || sibling.isEmptyInlineRemoveable() )\r
+ {\r
+ pendingNodes.push( sibling );\r
+ sibling = isNext ? sibling.getNext() : sibling.getPrevious();\r
+ if ( !sibling || sibling.type != CKEDITOR.NODE_ELEMENT )\r
+ return;\r
+ }\r
+\r
+ if ( element.isIdentical( sibling ) )\r
+ {\r
+ // Save the last child to be checked too, to merge things like\r
+ // <b><i></i></b><b><i></i></b> => <b><i></i></b>\r
+ var innerSibling = isNext ? element.getLast() : element.getFirst();\r
+\r
+ // Move pending nodes first into the target element.\r
+ while( pendingNodes.length )\r
+ pendingNodes.shift().move( element, !isNext );\r
+\r
+ sibling.moveChildren( element, !isNext );\r
+ sibling.remove();\r
+\r
+ // Now check the last inner child (see two comments above).\r
+ if ( innerSibling && innerSibling.type == CKEDITOR.NODE_ELEMENT )\r
+ innerSibling.mergeSiblings();\r
+ }\r
+ }\r
+ }\r
+\r
+ return function( inlineOnly )\r
+ {\r
+ if ( ! ( inlineOnly === false\r
+ || CKEDITOR.dtd.$removeEmpty[ this.getName() ]\r
+ || this.is( 'a' ) ) ) // Merge empty links and anchors also. (#5567)\r
+ {\r
+ return;\r
+ }\r
+\r
+ mergeElements( this, this.getNext(), true );\r
+ mergeElements( this, this.getPrevious() );\r
+ };\r
+ } )(),\r
+\r
+ /**\r