]>
git.immae.eu Git - perso/Immae/Projets/packagist/connexionswing-ckeditor-component.git/blob - sources/plugins/tabletools/plugin.js
2 * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
3 * For licensing, see LICENSE.md or http://ckeditor.com/license
7 var cellNodeRegex
= /^(?:td|th)$/;
9 function getSelectedCells( selection
) {
10 var ranges
= selection
.getRanges();
14 function moveOutOfCellGuard( node
) {
15 // Apply to the first cell only.
16 if ( retval
.length
> 0 )
19 // If we are exiting from the first </td>, then the td should definitely be
21 if ( node
.type
== CKEDITOR
.NODE_ELEMENT
&& cellNodeRegex
.test( node
.getName() ) && !node
.getCustomData( 'selected_cell' ) ) {
22 CKEDITOR
.dom
.element
.setMarker( database
, node
, 'selected_cell', true );
27 for ( var i
= 0; i
< ranges
.length
; i
++ ) {
28 var range
= ranges
[ i
];
30 if ( range
.collapsed
) {
31 // Walker does not handle collapsed ranges yet - fall back to old API.
32 var startNode
= range
.getCommonAncestor();
33 var nearestCell
= startNode
.getAscendant( 'td', true ) || startNode
.getAscendant( 'th', true );
35 retval
.push( nearestCell
);
37 var walker
= new CKEDITOR
.dom
.walker( range
);
39 walker
.guard
= moveOutOfCellGuard
;
41 while ( ( node
= walker
.next() ) ) {
42 // If may be possible for us to have a range like this:
43 // <td>^1</td><td>^2</td>
44 // The 2nd td shouldn't be included.
46 // So we have to take care to include a td we've entered only when we've
47 // walked into its children.
49 if ( node
.type
!= CKEDITOR
.NODE_ELEMENT
|| !node
.is( CKEDITOR
.dtd
.table
) ) {
50 var parent
= node
.getAscendant( 'td', true ) || node
.getAscendant( 'th', true );
51 if ( parent
&& !parent
.getCustomData( 'selected_cell' ) ) {
52 CKEDITOR
.dom
.element
.setMarker( database
, parent
, 'selected_cell', true );
53 retval
.push( parent
);
60 CKEDITOR
.dom
.element
.clearAllMarkers( database
);
65 function getFocusElementAfterDelCells( cellsToDelete
) {
67 last
= cellsToDelete
.length
- 1,
69 cell
, focusedCell
, tr
;
71 while ( ( cell
= cellsToDelete
[ i
++ ] ) )
72 CKEDITOR
.dom
.element
.setMarker( database
, cell
, 'delete_cell', true );
74 // 1.first we check left or right side focusable cell row by row;
76 while ( ( cell
= cellsToDelete
[ i
++ ] ) ) {
77 if ( ( focusedCell
= cell
.getPrevious() ) && !focusedCell
.getCustomData( 'delete_cell' ) || ( focusedCell
= cell
.getNext() ) && !focusedCell
.getCustomData( 'delete_cell' ) ) {
78 CKEDITOR
.dom
.element
.clearAllMarkers( database
);
83 CKEDITOR
.dom
.element
.clearAllMarkers( database
);
85 // 2. then we check the toppest row (outside the selection area square) focusable cell
86 tr
= cellsToDelete
[ 0 ].getParent();
87 if ( ( tr
= tr
.getPrevious() ) )
90 // 3. last we check the lowerest row focusable cell
91 tr
= cellsToDelete
[ last
].getParent();
92 if ( ( tr
= tr
.getNext() ) )
93 return tr
.getChild( 0 );
98 function insertRow( selection
, insertBefore
) {
99 var cells
= getSelectedCells( selection
),
100 firstCell
= cells
[ 0 ],
101 table
= firstCell
.getAscendant( 'table' ),
102 doc
= firstCell
.getDocument(),
103 startRow
= cells
[ 0 ].getParent(),
104 startRowIndex
= startRow
.$.rowIndex
,
105 lastCell
= cells
[ cells
.length
- 1 ],
106 endRowIndex
= lastCell
.getParent().$.rowIndex
+ lastCell
.$.rowSpan
- 1,
107 endRow
= new CKEDITOR
.dom
.element( table
.$.rows
[ endRowIndex
] ),
108 rowIndex
= insertBefore
? startRowIndex : endRowIndex
,
109 row
= insertBefore
? startRow : endRow
;
111 var map
= CKEDITOR
.tools
.buildTableMap( table
),
112 cloneRow
= map
[ rowIndex
],
113 nextRow
= insertBefore
? map
[ rowIndex
- 1 ] : map
[ rowIndex
+ 1 ],
114 width
= map
[ 0 ].length
;
116 var newRow
= doc
.createElement( 'tr' );
117 for ( var i
= 0; cloneRow
[ i
] && i
< width
; i
++ ) {
119 // Check whether there's a spanning row here, do not break it.
120 if ( cloneRow
[ i
].rowSpan
> 1 && nextRow
&& cloneRow
[ i
] == nextRow
[ i
] ) {
121 cell
= cloneRow
[ i
];
124 cell
= new CKEDITOR
.dom
.element( cloneRow
[ i
] ).clone();
125 cell
.removeAttribute( 'rowSpan' );
127 newRow
.append( cell
);
131 i
+= cell
.colSpan
- 1;
134 insertBefore
? newRow
.insertBefore( row
) : newRow
.insertAfter( row
);
137 function deleteRows( selectionOrRow
) {
138 if ( selectionOrRow
instanceof CKEDITOR
.dom
.selection
) {
139 var cells
= getSelectedCells( selectionOrRow
),
140 firstCell
= cells
[ 0 ],
141 table
= firstCell
.getAscendant( 'table' ),
142 map
= CKEDITOR
.tools
.buildTableMap( table
),
143 startRow
= cells
[ 0 ].getParent(),
144 startRowIndex
= startRow
.$.rowIndex
,
145 lastCell
= cells
[ cells
.length
- 1 ],
146 endRowIndex
= lastCell
.getParent().$.rowIndex
+ lastCell
.$.rowSpan
- 1,
149 // Delete cell or reduce cell spans by checking through the table map.
150 for ( var i
= startRowIndex
; i
<= endRowIndex
; i
++ ) {
151 var mapRow
= map
[ i
],
152 row
= new CKEDITOR
.dom
.element( table
.$.rows
[ i
] );
154 for ( var j
= 0; j
< mapRow
.length
; j
++ ) {
155 var cell
= new CKEDITOR
.dom
.element( mapRow
[ j
] ),
156 cellRowIndex
= cell
.getParent().$.rowIndex
;
158 if ( cell
.$.rowSpan
== 1 )
162 // Span row of the cell, reduce spanning.
164 // Root row of the cell, root cell to next row.
165 if ( cellRowIndex
== i
) {
166 var nextMapRow
= map
[ i
+ 1 ];
167 nextMapRow
[ j
- 1 ] ? cell
.insertAfter( new CKEDITOR
.dom
.element( nextMapRow
[ j
- 1 ] ) ) : new CKEDITOR
.dom
.element( table
.$.rows
[ i
+ 1 ] ).append( cell
, 1 );
171 j
+= cell
.$.colSpan
- 1;
174 rowsToDelete
.push( row
);
177 var rows
= table
.$.rows
;
179 // Where to put the cursor after rows been deleted?
180 // 1. Into next sibling row if any;
181 // 2. Into previous sibling row if any;
182 // 3. Into table's parent element if it's the very last row.
183 var cursorPosition
= new CKEDITOR
.dom
.element( rows
[ endRowIndex
+ 1 ] || ( startRowIndex
> 0 ? rows
[ startRowIndex
- 1 ] : null ) || table
.$.parentNode
);
185 for ( i
= rowsToDelete
.length
; i
>= 0; i
-- )
186 deleteRows( rowsToDelete
[ i
] );
188 return cursorPosition
;
189 } else if ( selectionOrRow
instanceof CKEDITOR
.dom
.element
) {
190 table
= selectionOrRow
.getAscendant( 'table' );
192 if ( table
.$.rows
.length
== 1 )
195 selectionOrRow
.remove();
201 function getCellColIndex( cell
, isStart
) {
202 var row
= cell
.getParent(),
203 rowCells
= row
.$.cells
;
206 for ( var i
= 0; i
< rowCells
.length
; i
++ ) {
207 var mapCell
= rowCells
[ i
];
208 colIndex
+= isStart
? 1 : mapCell
.colSpan
;
209 if ( mapCell
== cell
.$ )
216 function getColumnsIndices( cells
, isStart
) {
217 var retval
= isStart
? Infinity : 0;
218 for ( var i
= 0; i
< cells
.length
; i
++ ) {
219 var colIndex
= getCellColIndex( cells
[ i
], isStart
);
220 if ( isStart
? colIndex
< retval : colIndex
> retval
)
226 function insertColumn( selection
, insertBefore
) {
227 var cells
= getSelectedCells( selection
),
228 firstCell
= cells
[ 0 ],
229 table
= firstCell
.getAscendant( 'table' ),
230 startCol
= getColumnsIndices( cells
, 1 ),
231 lastCol
= getColumnsIndices( cells
),
232 colIndex
= insertBefore
? startCol : lastCol
;
234 var map
= CKEDITOR
.tools
.buildTableMap( table
),
239 for ( var i
= 0; i
< height
; i
++ ) {
240 cloneCol
.push( map
[ i
][ colIndex
] );
241 var nextCell
= insertBefore
? map
[ i
][ colIndex
- 1 ] : map
[ i
][ colIndex
+ 1 ];
242 nextCol
.push( nextCell
);
245 for ( i
= 0; i
< height
; i
++ ) {
248 if ( !cloneCol
[ i
] )
251 // Check whether there's a spanning column here, do not break it.
252 if ( cloneCol
[ i
].colSpan
> 1 && nextCol
[ i
] == cloneCol
[ i
] ) {
253 cell
= cloneCol
[ i
];
256 cell
= new CKEDITOR
.dom
.element( cloneCol
[ i
] ).clone();
257 cell
.removeAttribute( 'colSpan' );
259 cell
[ insertBefore
? 'insertBefore' : 'insertAfter' ].call( cell
, new CKEDITOR
.dom
.element( cloneCol
[ i
] ) );
263 i
+= cell
.rowSpan
- 1;
267 function deleteColumns( selectionOrCell
) {
268 var cells
= getSelectedCells( selectionOrCell
),
269 firstCell
= cells
[ 0 ],
270 lastCell
= cells
[ cells
.length
- 1 ],
271 table
= firstCell
.getAscendant( 'table' ),
272 map
= CKEDITOR
.tools
.buildTableMap( table
),
273 startColIndex
, endColIndex
,
276 // Figure out selected cells' column indices.
277 for ( var i
= 0, rows
= map
.length
; i
< rows
; i
++ ) {
278 for ( var j
= 0, cols
= map
[ i
].length
; j
< cols
; j
++ ) {
279 if ( map
[ i
][ j
] == firstCell
.$ )
281 if ( map
[ i
][ j
] == lastCell
.$ )
286 // Delete cell or reduce cell spans by checking through the table map.
287 for ( i
= startColIndex
; i
<= endColIndex
; i
++ ) {
288 for ( j
= 0; j
< map
.length
; j
++ ) {
289 var mapRow
= map
[ j
],
290 row
= new CKEDITOR
.dom
.element( table
.$.rows
[ j
] ),
291 cell
= new CKEDITOR
.dom
.element( mapRow
[ i
] );
294 if ( cell
.$.colSpan
== 1 )
296 // Reduce the col spans.
300 j
+= cell
.$.rowSpan
- 1;
302 if ( !row
.$.cells
.length
)
303 rowsToDelete
.push( row
);
308 var firstRowCells
= table
.$.rows
[ 0 ] && table
.$.rows
[ 0 ].cells
;
310 // Where to put the cursor after columns been deleted?
311 // 1. Into next cell of the first row if any;
312 // 2. Into previous cell of the first row if any;
313 // 3. Into table's parent element;
314 var cursorPosition
= new CKEDITOR
.dom
.element( firstRowCells
[ startColIndex
] || ( startColIndex
? firstRowCells
[ startColIndex
- 1 ] : table
.$.parentNode
) );
316 // Delete table rows only if all columns are gone (do not remove empty row).
317 if ( rowsToDelete
.length
== rows
)
320 return cursorPosition
;
323 function insertCell( selection
, insertBefore
) {
324 var startElement
= selection
.getStartElement();
325 var cell
= startElement
.getAscendant( 'td', 1 ) || startElement
.getAscendant( 'th', 1 );
330 // Create the new cell element to be added.
331 var newCell
= cell
.clone();
332 newCell
.appendBogus();
335 newCell
.insertBefore( cell
);
337 newCell
.insertAfter( cell
);
340 function deleteCells( selectionOrCell
) {
341 if ( selectionOrCell
instanceof CKEDITOR
.dom
.selection
) {
342 var cellsToDelete
= getSelectedCells( selectionOrCell
);
343 var table
= cellsToDelete
[ 0 ] && cellsToDelete
[ 0 ].getAscendant( 'table' );
344 var cellToFocus
= getFocusElementAfterDelCells( cellsToDelete
);
346 for ( var i
= cellsToDelete
.length
- 1; i
>= 0; i
-- )
347 deleteCells( cellsToDelete
[ i
] );
350 placeCursorInCell( cellToFocus
, true );
353 } else if ( selectionOrCell
instanceof CKEDITOR
.dom
.element
) {
354 var tr
= selectionOrCell
.getParent();
355 if ( tr
.getChildCount() == 1 )
358 selectionOrCell
.remove();
362 // Remove filler at end and empty spaces around the cell content.
363 function trimCell( cell
) {
364 var bogus
= cell
.getBogus();
365 bogus
&& bogus
.remove();
369 function placeCursorInCell( cell
, placeAtEnd
) {
370 var docInner
= cell
.getDocument(),
371 docOuter
= CKEDITOR
.document
;
373 // Fixing "Unspecified error" thrown in IE10 by resetting
374 // selection the dirty and shameful way (#10308).
375 // We can not apply this hack to IE8 because
376 // it causes error (#11058).
377 if ( CKEDITOR
.env
.ie
&& CKEDITOR
.env
.version
== 10 ) {
382 var range
= new CKEDITOR
.dom
.range( docInner
);
383 if ( !range
[ 'moveToElementEdit' + ( placeAtEnd
? 'End' : 'Start' ) ]( cell
) ) {
384 range
.selectNodeContents( cell
);
385 range
.collapse( placeAtEnd
? false : true );
387 range
.select( true );
390 function cellInRow( tableMap
, rowIndex
, cell
) {
391 var oRow
= tableMap
[ rowIndex
];
392 if ( typeof cell
== 'undefined' )
395 for ( var c
= 0; oRow
&& c
< oRow
.length
; c
++ ) {
396 if ( cell
.is
&& oRow
[ c
] == cell
.$ )
398 else if ( c
== cell
)
399 return new CKEDITOR
.dom
.element( oRow
[ c
] );
401 return cell
.is
? -1 : null;
404 function cellInCol( tableMap
, colIndex
) {
406 for ( var r
= 0; r
< tableMap
.length
; r
++ ) {
407 var row
= tableMap
[ r
];
408 oCol
.push( row
[ colIndex
] );
410 // Avoid adding duplicate cells.
411 if ( row
[ colIndex
].rowSpan
> 1 )
412 r
+= row
[ colIndex
].rowSpan
- 1;
417 function mergeCells( selection
, mergeDirection
, isDetect
) {
418 var cells
= getSelectedCells( selection
);
420 // Invalid merge request if:
421 // 1. In batch mode despite that less than two selected.
422 // 2. In solo mode while not exactly only one selected.
423 // 3. Cells distributed in different table groups (e.g. from both thead and tbody).
425 if ( ( mergeDirection
? cells
.length
!= 1 : cells
.length
< 2 ) || ( commonAncestor
= selection
.getCommonAncestor() ) && commonAncestor
.type
== CKEDITOR
.NODE_ELEMENT
&& commonAncestor
.is( 'table' ) )
429 firstCell
= cells
[ 0 ],
430 table
= firstCell
.getAscendant( 'table' ),
431 map
= CKEDITOR
.tools
.buildTableMap( table
),
432 mapHeight
= map
.length
,
433 mapWidth
= map
[ 0 ].length
,
434 startRow
= firstCell
.getParent().$.rowIndex
,
435 startColumn
= cellInRow( map
, startRow
, firstCell
);
437 if ( mergeDirection
) {
440 var rowspan
= parseInt( firstCell
.getAttribute( 'rowspan' ), 10 ) || 1;
441 var colspan
= parseInt( firstCell
.getAttribute( 'colspan' ), 10 ) || 1;
443 targetCell
= map
[ mergeDirection
== 'up' ? ( startRow
- rowspan
) : mergeDirection
== 'down' ? ( startRow
+ rowspan
) : startRow
][
444 mergeDirection
== 'left' ?
445 ( startColumn
- colspan
) :
446 mergeDirection
== 'right' ? ( startColumn
+ colspan
) : startColumn
];
452 // 1. No cell could be merged.
453 // 2. Same cell actually.
454 if ( !targetCell
|| firstCell
.$ == targetCell
)
457 // Sort in map order regardless of the DOM sequence.
458 cells
[ ( mergeDirection
== 'up' || mergeDirection
== 'left' ) ? 'unshift' : 'push' ]( new CKEDITOR
.dom
.element( targetCell
) );
461 // Start from here are merging way ignorance (merge up/right, batch merge).
462 var doc
= firstCell
.getDocument(),
463 lastRowIndex
= startRow
,
466 // Use a documentFragment as buffer when appending cell contents.
467 frag
= !isDetect
&& new CKEDITOR
.dom
.documentFragment( doc
),
470 for ( var i
= 0; i
< cells
.length
; i
++ ) {
473 var tr
= cell
.getParent(),
474 cellFirstChild
= cell
.getFirst(),
475 colSpan
= cell
.$.colSpan
,
476 rowSpan
= cell
.$.rowSpan
,
477 rowIndex
= tr
.$.rowIndex
,
478 colIndex
= cellInRow( map
, rowIndex
, cell
);
480 // Accumulated the actual places taken by all selected cells.
481 dimension
+= colSpan
* rowSpan
;
482 // Accumulated the maximum virtual spans from column and row.
483 totalColSpan
= Math
.max( totalColSpan
, colIndex
- startColumn
+ colSpan
);
484 totalRowSpan
= Math
.max( totalRowSpan
, rowIndex
- startRow
+ rowSpan
);
487 // Trim all cell fillers and check to remove empty cells.
488 if ( trimCell( cell
), cell
.getChildren().count() ) {
489 // Merge vertically cells as two separated paragraphs.
490 if ( rowIndex
!= lastRowIndex
&& cellFirstChild
&& !( cellFirstChild
.isBlockBoundary
&& cellFirstChild
.isBlockBoundary( { br: 1 } ) ) ) {
491 var last
= frag
.getLast( CKEDITOR
.dom
.walker
.whitespaces( true ) );
492 if ( last
&& !( last
.is
&& last
.is( 'br' ) ) )
496 cell
.moveChildren( frag
);
498 i
? cell
.remove() : cell
.setHtml( '' );
500 lastRowIndex
= rowIndex
;
504 frag
.moveChildren( firstCell
);
506 firstCell
.appendBogus();
508 if ( totalColSpan
>= mapWidth
)
509 firstCell
.removeAttribute( 'rowSpan' );
511 firstCell
.$.rowSpan
= totalRowSpan
;
513 if ( totalRowSpan
>= mapHeight
)
514 firstCell
.removeAttribute( 'colSpan' );
516 firstCell
.$.colSpan
= totalColSpan
;
518 // Swip empty <tr> left at the end of table due to the merging.
519 var trs
= new CKEDITOR
.dom
.nodeList( table
.$.rows
),
522 for ( i
= count
- 1; i
>= 0; i
-- ) {
523 var tailTr
= trs
.getItem( i
);
524 if ( !tailTr
.$.cells
.length
) {
533 // Be able to merge cells only if actual dimension of selected
534 // cells equals to the caculated rectangle.
536 return ( totalRowSpan
* totalColSpan
) == dimension
;
540 function horizontalSplitCell( selection
, isDetect
) {
541 var cells
= getSelectedCells( selection
);
542 if ( cells
.length
> 1 )
547 var cell
= cells
[ 0 ],
548 tr
= cell
.getParent(),
549 table
= tr
.getAscendant( 'table' ),
550 map
= CKEDITOR
.tools
.buildTableMap( table
),
551 rowIndex
= tr
.$.rowIndex
,
552 colIndex
= cellInRow( map
, rowIndex
, cell
),
553 rowSpan
= cell
.$.rowSpan
,
554 newCell
, newRowSpan
, newCellRowSpan
, newRowIndex
;
557 newRowSpan
= Math
.ceil( rowSpan
/ 2 );
558 newCellRowSpan
= Math
.floor( rowSpan
/ 2 );
559 newRowIndex
= rowIndex
+ newRowSpan
;
560 var newCellTr
= new CKEDITOR
.dom
.element( table
.$.rows
[ newRowIndex
] ),
561 newCellRow
= cellInRow( map
, newRowIndex
),
564 newCell
= cell
.clone();
566 // Figure out where to insert the new cell by checking the vitual row.
567 for ( var c
= 0; c
< newCellRow
.length
; c
++ ) {
568 candidateCell
= newCellRow
[ c
];
569 // Catch first cell actually following the column.
570 if ( candidateCell
.parentNode
== newCellTr
.$ && c
> colIndex
) {
571 newCell
.insertBefore( new CKEDITOR
.dom
.element( candidateCell
) );
574 candidateCell
= null;
578 // The destination row is empty, append at will.
579 if ( !candidateCell
)
580 newCellTr
.append( newCell
);
582 newCellRowSpan
= newRowSpan
= 1;
584 newCellTr
= tr
.clone();
585 newCellTr
.insertAfter( tr
);
586 newCellTr
.append( newCell
= cell
.clone() );
588 var cellsInSameRow
= cellInRow( map
, rowIndex
);
589 for ( var i
= 0; i
< cellsInSameRow
.length
; i
++ )
590 cellsInSameRow
[ i
].rowSpan
++;
593 newCell
.appendBogus();
595 cell
.$.rowSpan
= newRowSpan
;
596 newCell
.$.rowSpan
= newCellRowSpan
;
597 if ( newRowSpan
== 1 )
598 cell
.removeAttribute( 'rowSpan' );
599 if ( newCellRowSpan
== 1 )
600 newCell
.removeAttribute( 'rowSpan' );
605 function verticalSplitCell( selection
, isDetect
) {
606 var cells
= getSelectedCells( selection
);
607 if ( cells
.length
> 1 )
612 var cell
= cells
[ 0 ],
613 tr
= cell
.getParent(),
614 table
= tr
.getAscendant( 'table' ),
615 map
= CKEDITOR
.tools
.buildTableMap( table
),
616 rowIndex
= tr
.$.rowIndex
,
617 colIndex
= cellInRow( map
, rowIndex
, cell
),
618 colSpan
= cell
.$.colSpan
,
619 newCell
, newColSpan
, newCellColSpan
;
622 newColSpan
= Math
.ceil( colSpan
/ 2 );
623 newCellColSpan
= Math
.floor( colSpan
/ 2 );
625 newCellColSpan
= newColSpan
= 1;
626 var cellsInSameCol
= cellInCol( map
, colIndex
);
627 for ( var i
= 0; i
< cellsInSameCol
.length
; i
++ )
628 cellsInSameCol
[ i
].colSpan
++;
630 newCell
= cell
.clone();
631 newCell
.insertAfter( cell
);
632 newCell
.appendBogus();
634 cell
.$.colSpan
= newColSpan
;
635 newCell
.$.colSpan
= newCellColSpan
;
636 if ( newColSpan
== 1 )
637 cell
.removeAttribute( 'colSpan' );
638 if ( newCellColSpan
== 1 )
639 newCell
.removeAttribute( 'colSpan' );
644 CKEDITOR
.plugins
.tabletools
= {
645 requires: 'table,dialog,contextmenu',
646 init: function( editor
) {
647 var lang
= editor
.lang
.table
;
649 function createDef( def
) {
650 return CKEDITOR
.tools
.extend( def
|| {}, {
652 refresh: function( editor
, path
) {
653 this.setState( path
.contains( { td: 1, th: 1 }, 1 ) ? CKEDITOR
.TRISTATE_OFF : CKEDITOR
.TRISTATE_DISABLED
);
657 function addCmd( name
, def
) {
658 var cmd
= editor
.addCommand( name
, def
);
659 editor
.addFeature( cmd
);
662 addCmd( 'cellProperties', new CKEDITOR
.dialogCommand( 'cellProperties', createDef( {
663 allowedContent: 'td th{width,height,border-color,background-color,white-space,vertical-align,text-align}[colspan,rowspan]',
664 requiredContent: 'table'
666 CKEDITOR
.dialog
.add( 'cellProperties', this.path
+ 'dialogs/tableCell.js' );
668 addCmd( 'rowDelete', createDef( {
669 requiredContent: 'table',
670 exec: function( editor
) {
671 var selection
= editor
.getSelection();
672 placeCursorInCell( deleteRows( selection
) );
676 addCmd( 'rowInsertBefore', createDef( {
677 requiredContent: 'table',
678 exec: function( editor
) {
679 var selection
= editor
.getSelection();
680 insertRow( selection
, true );
684 addCmd( 'rowInsertAfter', createDef( {
685 requiredContent: 'table',
686 exec: function( editor
) {
687 var selection
= editor
.getSelection();
688 insertRow( selection
);
692 addCmd( 'columnDelete', createDef( {
693 requiredContent: 'table',
694 exec: function( editor
) {
695 var selection
= editor
.getSelection();
696 var element
= deleteColumns( selection
);
697 element
&& placeCursorInCell( element
, true );
701 addCmd( 'columnInsertBefore', createDef( {
702 requiredContent: 'table',
703 exec: function( editor
) {
704 var selection
= editor
.getSelection();
705 insertColumn( selection
, true );
709 addCmd( 'columnInsertAfter', createDef( {
710 requiredContent: 'table',
711 exec: function( editor
) {
712 var selection
= editor
.getSelection();
713 insertColumn( selection
);
717 addCmd( 'cellDelete', createDef( {
718 requiredContent: 'table',
719 exec: function( editor
) {
720 var selection
= editor
.getSelection();
721 deleteCells( selection
);
725 addCmd( 'cellMerge', createDef( {
726 allowedContent: 'td[colspan,rowspan]',
727 requiredContent: 'td[colspan,rowspan]',
728 exec: function( editor
) {
729 placeCursorInCell( mergeCells( editor
.getSelection() ), true );
733 addCmd( 'cellMergeRight', createDef( {
734 allowedContent: 'td[colspan]',
735 requiredContent: 'td[colspan]',
736 exec: function( editor
) {
737 placeCursorInCell( mergeCells( editor
.getSelection(), 'right' ), true );
741 addCmd( 'cellMergeDown', createDef( {
742 allowedContent: 'td[rowspan]',
743 requiredContent: 'td[rowspan]',
744 exec: function( editor
) {
745 placeCursorInCell( mergeCells( editor
.getSelection(), 'down' ), true );
749 addCmd( 'cellVerticalSplit', createDef( {
750 allowedContent: 'td[rowspan]',
751 requiredContent: 'td[rowspan]',
752 exec: function( editor
) {
753 placeCursorInCell( verticalSplitCell( editor
.getSelection() ) );
757 addCmd( 'cellHorizontalSplit', createDef( {
758 allowedContent: 'td[colspan]',
759 requiredContent: 'td[colspan]',
760 exec: function( editor
) {
761 placeCursorInCell( horizontalSplitCell( editor
.getSelection() ) );
765 addCmd( 'cellInsertBefore', createDef( {
766 requiredContent: 'table',
767 exec: function( editor
) {
768 var selection
= editor
.getSelection();
769 insertCell( selection
, true );
773 addCmd( 'cellInsertAfter', createDef( {
774 requiredContent: 'table',
775 exec: function( editor
) {
776 var selection
= editor
.getSelection();
777 insertCell( selection
);
781 // If the "menu" plugin is loaded, register the menu items.
782 if ( editor
.addMenuItems
) {
783 editor
.addMenuItems( {
785 label: lang
.cell
.menu
,
788 getItems: function() {
789 var selection
= editor
.getSelection(),
790 cells
= getSelectedCells( selection
);
792 tablecell_insertBefore: CKEDITOR
.TRISTATE_OFF
,
793 tablecell_insertAfter: CKEDITOR
.TRISTATE_OFF
,
794 tablecell_delete: CKEDITOR
.TRISTATE_OFF
,
795 tablecell_merge: mergeCells( selection
, null, true ) ? CKEDITOR
.TRISTATE_OFF : CKEDITOR
.TRISTATE_DISABLED
,
796 tablecell_merge_right: mergeCells( selection
, 'right', true ) ? CKEDITOR
.TRISTATE_OFF : CKEDITOR
.TRISTATE_DISABLED
,
797 tablecell_merge_down: mergeCells( selection
, 'down', true ) ? CKEDITOR
.TRISTATE_OFF : CKEDITOR
.TRISTATE_DISABLED
,
798 tablecell_split_vertical: verticalSplitCell( selection
, true ) ? CKEDITOR
.TRISTATE_OFF : CKEDITOR
.TRISTATE_DISABLED
,
799 tablecell_split_horizontal: horizontalSplitCell( selection
, true ) ? CKEDITOR
.TRISTATE_OFF : CKEDITOR
.TRISTATE_DISABLED
,
800 tablecell_properties: cells
.length
> 0 ? CKEDITOR
.TRISTATE_OFF : CKEDITOR
.TRISTATE_DISABLED
805 tablecell_insertBefore: {
806 label: lang
.cell
.insertBefore
,
808 command: 'cellInsertBefore',
812 tablecell_insertAfter: {
813 label: lang
.cell
.insertAfter
,
815 command: 'cellInsertAfter',
820 label: lang
.cell
.deleteCell
,
822 command: 'cellDelete',
827 label: lang
.cell
.merge
,
829 command: 'cellMerge',
833 tablecell_merge_right: {
834 label: lang
.cell
.mergeRight
,
836 command: 'cellMergeRight',
840 tablecell_merge_down: {
841 label: lang
.cell
.mergeDown
,
843 command: 'cellMergeDown',
847 tablecell_split_horizontal: {
848 label: lang
.cell
.splitHorizontal
,
850 command: 'cellHorizontalSplit',
854 tablecell_split_vertical: {
855 label: lang
.cell
.splitVertical
,
857 command: 'cellVerticalSplit',
861 tablecell_properties: {
862 label: lang
.cell
.title
,
863 group: 'tablecellproperties',
864 command: 'cellProperties',
869 label: lang
.row
.menu
,
872 getItems: function() {
874 tablerow_insertBefore: CKEDITOR
.TRISTATE_OFF
,
875 tablerow_insertAfter: CKEDITOR
.TRISTATE_OFF
,
876 tablerow_delete: CKEDITOR
.TRISTATE_OFF
881 tablerow_insertBefore: {
882 label: lang
.row
.insertBefore
,
884 command: 'rowInsertBefore',
888 tablerow_insertAfter: {
889 label: lang
.row
.insertAfter
,
891 command: 'rowInsertAfter',
896 label: lang
.row
.deleteRow
,
898 command: 'rowDelete',
903 label: lang
.column
.menu
,
904 group: 'tablecolumn',
906 getItems: function() {
908 tablecolumn_insertBefore: CKEDITOR
.TRISTATE_OFF
,
909 tablecolumn_insertAfter: CKEDITOR
.TRISTATE_OFF
,
910 tablecolumn_delete: CKEDITOR
.TRISTATE_OFF
915 tablecolumn_insertBefore: {
916 label: lang
.column
.insertBefore
,
917 group: 'tablecolumn',
918 command: 'columnInsertBefore',
922 tablecolumn_insertAfter: {
923 label: lang
.column
.insertAfter
,
924 group: 'tablecolumn',
925 command: 'columnInsertAfter',
929 tablecolumn_delete: {
930 label: lang
.column
.deleteColumn
,
931 group: 'tablecolumn',
932 command: 'columnDelete',
938 // If the "contextmenu" plugin is laoded, register the listeners.
939 if ( editor
.contextMenu
) {
940 editor
.contextMenu
.addListener( function( element
, selection
, path
) {
941 var cell
= path
.contains( { 'td': 1, 'th': 1 }, 1 );
942 if ( cell
&& !cell
.isReadOnly() ) {
944 tablecell: CKEDITOR
.TRISTATE_OFF
,
945 tablerow: CKEDITOR
.TRISTATE_OFF
,
946 tablecolumn: CKEDITOR
.TRISTATE_OFF
955 getSelectedCells: getSelectedCells
958 CKEDITOR
.plugins
.add( 'tabletools', CKEDITOR
.plugins
.tabletools
);
962 * Create a two-dimension array that reflects the actual layout of table cells,
963 * with cell spans, with mappings to the original td elements.
965 * @param {CKEDITOR.dom.element} table
966 * @member CKEDITOR.tools
968 CKEDITOR
.tools
.buildTableMap = function( table
) {
969 var aRows
= table
.$.rows
;
971 // Row and Column counters.
976 for ( var i
= 0; i
< aRows
.length
; i
++ ) {
978 !aMap
[ r
] && ( aMap
[ r
] = [] );
982 for ( var j
= 0; j
< aRows
[ i
].cells
.length
; j
++ ) {
983 var oCell
= aRows
[ i
].cells
[ j
];
986 while ( aMap
[ r
][ c
] )
989 var iColSpan
= isNaN( oCell
.colSpan
) ? 1 : oCell
.colSpan
;
990 var iRowSpan
= isNaN( oCell
.rowSpan
) ? 1 : oCell
.rowSpan
;
992 for ( var rs
= 0; rs
< iRowSpan
; rs
++ ) {
993 if ( !aMap
[ r
+ rs
] )
996 for ( var cs
= 0; cs
< iColSpan
; cs
++ ) {
997 aMap
[ r
+ rs
][ c
+ cs
] = aRows
[ i
].cells
[ j
];