|
263 | 263 | // Remove the original contents, merge split nodes.
|
264 | 264 | range.deleteContents( 1 );
|
265 | 265 |
|
| 266 | + // If range is placed in inermediate element (not td or th), we need to do three things: |
| 267 | + // * fill emptied <td/th>s with if browser needs them, |
| 268 | + // * remove empty text nodes so IE8 won't crash (http://dev.ckeditor.com/ticket/11183#comment:8), |
| 269 | + // * fix structure and move range into the <td/th> element. |
| 270 | + if ( range.startContainer.type == CKEDITOR.NODE_ELEMENT && range.startContainer.is( { tr:1,table:1,tbody:1,thead:1,tfoot:1 } ) ) |
| 271 | + fixTableAfterContentsDeletion( range ); |
| 272 | + |
266 | 273 | // If we're inserting a block at dtd-violated position, split
|
267 | 274 | // the parent blocks until we reach blockLimit.
|
268 | 275 | var current, dtd;
|
|
303 | 310 | var editor = this.editor,
|
304 | 311 | enterMode = editor.activeEnterMode,
|
305 | 312 | selection = editor.getSelection(),
|
306 |
| - ranges = selection.getRanges(), |
| 313 | + range = selection.getRanges()[ 0 ], |
307 | 314 | elementName = element.getName(),
|
308 |
| - isBlock = CKEDITOR.dtd.$block[ elementName ], |
309 |
| - clone, lastElement, range; |
| 315 | + isBlock = CKEDITOR.dtd.$block[ elementName ]; |
310 | 316 |
|
311 | 317 | // Prepare for the insertion.
|
312 | 318 | beforeInsert( this );
|
313 | 319 |
|
314 |
| - // Insert the element into all ranges by cloning. |
315 |
| - for ( var i = ranges.length; i--; ) { |
316 |
| - range = ranges[ i ]; |
317 |
| - |
318 |
| - // Clone is an element for the first range. |
319 |
| - clone = !i && element || element.clone( 1 ); |
320 |
| - |
321 |
| - // Put the clone into a particular range. |
322 |
| - // Save the last **successfully inserted** element reference |
323 |
| - // so we can make the selection later. |
324 |
| - if ( this.insertElementIntoRange( clone, range ) && !lastElement ) |
325 |
| - lastElement = clone; |
326 |
| - } |
327 |
| - |
328 |
| - if ( lastElement ) { |
329 |
| - range.moveToPosition( lastElement, CKEDITOR.POSITION_AFTER_END ); |
| 320 | + // Insert element into first range only and ignore the rest (#11183). |
| 321 | + if ( this.insertElementIntoRange( element, range ) ) { |
| 322 | + range.moveToPosition( element, CKEDITOR.POSITION_AFTER_END ); |
330 | 323 |
|
331 | 324 | // If we're inserting a block element, the new cursor position must be
|
332 | 325 | // optimized. (#3100,#5436,#8950)
|
333 | 326 | if ( isBlock ) {
|
334 |
| - |
335 | 327 | // Find next, meaningful element.
|
336 |
| - var next = lastElement.getNext( function( node ) { |
| 328 | + var next = element.getNext( function( node ) { |
337 | 329 | return isNotEmpty( node ) && !isBogus( node );
|
338 | 330 | } );
|
339 | 331 |
|
340 | 332 | if ( next && next.type == CKEDITOR.NODE_ELEMENT && next.is( CKEDITOR.dtd.$block ) ) {
|
341 |
| - |
342 | 333 | // If the next one is a text block, move cursor to the start of it's content.
|
343 | 334 | if ( next.getDtd()[ '#' ] )
|
344 | 335 | range.moveToElementEditStart( next );
|
345 | 336 | // Otherwise move cursor to the before end of the last element.
|
346 | 337 | else
|
347 |
| - range.moveToElementEditEnd( lastElement ); |
| 338 | + range.moveToElementEditEnd( element ); |
348 | 339 | }
|
349 | 340 | // Open a new line if the block is inserted at the end of parent.
|
350 | 341 | else if ( !next && enterMode != CKEDITOR.ENTER_BR ) {
|
|
1810 | 1801 | }, 0 );
|
1811 | 1802 | }
|
1812 | 1803 |
|
| 1804 | + // 1. Fixes a range which is a result of deleteContents() and is placed in an intermediate element (see dtd.$intermediate), |
| 1805 | + // inside a table. A goal is to find a closest <td> or <th> element and when this fails, recreate the structure of the table. |
| 1806 | + // 2. Fixes empty cells by appending bogus <br>s or deleting empty text nodes in IE<=8 case. |
| 1807 | + var fixTableAfterContentsDeletion = (function() { |
| 1808 | + // Creates an element walker which can only "go deeper". It won't |
| 1809 | + // move out from any element. Therefore it can be used to find <td>x</td> in cases like: |
| 1810 | + // <table><tbody><tr><td>x</td></tr></tbody>^<tfoot>... |
| 1811 | + function getFixTableSelectionWalker( testRange ) { |
| 1812 | + var walker = new CKEDITOR.dom.walker( testRange ); |
| 1813 | + walker.guard = function( node, isMovingOut ) { |
| 1814 | + if ( isMovingOut ) |
| 1815 | + return false; |
| 1816 | + if ( node.type == CKEDITOR.NODE_ELEMENT ) |
| 1817 | + return node.is( CKEDITOR.dtd.$tableContent ); |
| 1818 | + }; |
| 1819 | + walker.evaluator = function( node ) { |
| 1820 | + return node.type == CKEDITOR.NODE_ELEMENT; |
| 1821 | + }; |
| 1822 | + |
| 1823 | + return walker; |
| 1824 | + } |
| 1825 | + |
| 1826 | + function fixTableStructure( element, newElementName, appendToStart ) { |
| 1827 | + var temp = element.getDocument().createElement( newElementName ); |
| 1828 | + element.append( temp, appendToStart ); |
| 1829 | + return temp; |
| 1830 | + } |
| 1831 | + |
| 1832 | + // Fix empty cells. This means: |
| 1833 | + // * add bogus <br> if browser needs it |
| 1834 | + // * remove empty text nodes on IE8, because it will crash (http://dev.ckeditor.com/ticket/11183#comment:8). |
| 1835 | + function fixEmptyCells( cells ) { |
| 1836 | + var i = cells.count(), |
| 1837 | + cell; |
| 1838 | + |
| 1839 | + for ( i; i-- > 0; ) { |
| 1840 | + cell = cells.getItem( i ); |
| 1841 | + |
| 1842 | + if ( !CKEDITOR.tools.trim( cell.getHtml() ) ) { |
| 1843 | + cell.appendBogus(); |
| 1844 | + if ( CKEDITOR.env.ie && CKEDITOR.env.version < 9 && cell.getChildCount() ) |
| 1845 | + cell.getFirst().remove(); |
| 1846 | + } |
| 1847 | + } |
| 1848 | + } |
| 1849 | + |
| 1850 | + return function( range ) { |
| 1851 | + var container = range.startContainer, |
| 1852 | + table = container.getAscendant( 'table', 1 ), |
| 1853 | + testRange, |
| 1854 | + walker, |
| 1855 | + deeperSibling, |
| 1856 | + doc = range.document, |
| 1857 | + appendToStart = false; |
| 1858 | + |
| 1859 | + fixEmptyCells( table.getElementsByTag( 'td' ) ); |
| 1860 | + fixEmptyCells( table.getElementsByTag( 'th' ) ); |
| 1861 | + |
| 1862 | + // Look left. |
| 1863 | + testRange = range.clone(); |
| 1864 | + testRange.setStart( container, 0 ); |
| 1865 | + deeperSibling = getFixTableSelectionWalker( testRange ).lastBackward(); |
| 1866 | + |
| 1867 | + // If left is empty, look right. |
| 1868 | + if ( !deeperSibling ) { |
| 1869 | + testRange = range.clone(); |
| 1870 | + testRange.setEndAt( container, CKEDITOR.POSITION_BEFORE_END ); |
| 1871 | + deeperSibling = getFixTableSelectionWalker( testRange ).lastForward(); |
| 1872 | + appendToStart = true; |
| 1873 | + } |
| 1874 | + |
| 1875 | + // If there's no deeper nested element in both direction - container is empty - we'll use it then. |
| 1876 | + if ( !deeperSibling ) |
| 1877 | + deeperSibling = container; |
| 1878 | + |
| 1879 | + // Fix structure... |
| 1880 | + |
| 1881 | + // We found a table what means that it's empty - remove it completely. |
| 1882 | + if ( deeperSibling.is( 'table' ) ) { |
| 1883 | + range.setStartAt( deeperSibling, CKEDITOR.POSITION_BEFORE_START ); |
| 1884 | + range.collapse( true ); |
| 1885 | + deeperSibling.remove(); |
| 1886 | + return; |
| 1887 | + } |
| 1888 | + |
| 1889 | + // Found an empty txxx element - append tr. |
| 1890 | + if ( deeperSibling.is( { tbody:1,thead:1,tfoot:1 } ) ) |
| 1891 | + deeperSibling = fixTableStructure( deeperSibling, 'tr', appendToStart ); |
| 1892 | + |
| 1893 | + // Found an empty tr element - append td/th. |
| 1894 | + if ( deeperSibling.is( 'tr' ) ) |
| 1895 | + deeperSibling = fixTableStructure( deeperSibling, deeperSibling.getParent().is( 'thead' ) ? 'th' : 'td', appendToStart ); |
| 1896 | + |
| 1897 | + // To avoid setting selection after bogus, remove it from the current cell. |
| 1898 | + // We can safely do that, because we'll insert element into that cell. |
| 1899 | + var bogus = deeperSibling.getBogus(); |
| 1900 | + if ( bogus ) |
| 1901 | + bogus.remove(); |
| 1902 | + |
| 1903 | + range.moveToPosition( deeperSibling, appendToStart ? CKEDITOR.POSITION_AFTER_START : CKEDITOR.POSITION_BEFORE_END ); |
| 1904 | + } |
| 1905 | + })(); |
| 1906 | + |
1813 | 1907 | })();
|
1814 | 1908 |
|
1815 | 1909 | /**
|
|
0 commit comments