| [ Index ] |
PHP Cross Reference of Drupal 6 (gatewave) |
[Summary view] [Print] [Text view]
1 /* 2 Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. 3 For licensing, see LICENSE.html or http://ckeditor.com/license 4 */ 5 6 (function() 7 { 8 function removeRawAttribute( $node, attr ) 9 { 10 if ( CKEDITOR.env.ie ) 11 $node.removeAttribute( attr ); 12 else 13 delete $node[ attr ]; 14 } 15 16 var cellNodeRegex = /^(?:td|th)$/; 17 18 function getSelectedCells( selection ) 19 { 20 // Walker will try to split text nodes, which will make the current selection 21 // invalid. So save bookmarks before doing anything. 22 var bookmarks = selection.createBookmarks(); 23 24 var ranges = selection.getRanges(); 25 var retval = []; 26 var database = {}; 27 28 function moveOutOfCellGuard( node ) 29 { 30 // Apply to the first cell only. 31 if ( retval.length > 0 ) 32 return; 33 34 // If we are exiting from the first </td>, then the td should definitely be 35 // included. 36 if ( node.type == CKEDITOR.NODE_ELEMENT && cellNodeRegex.test( node.getName() ) 37 && !node.getCustomData( 'selected_cell' ) ) 38 { 39 CKEDITOR.dom.element.setMarker( database, node, 'selected_cell', true ); 40 retval.push( node ); 41 } 42 } 43 44 for ( var i = 0 ; i < ranges.length ; i++ ) 45 { 46 var range = ranges[ i ]; 47 48 if ( range.collapsed ) 49 { 50 // Walker does not handle collapsed ranges yet - fall back to old API. 51 var startNode = range.getCommonAncestor(); 52 var nearestCell = startNode.getAscendant( 'td', true ) || startNode.getAscendant( 'th', true ); 53 if ( nearestCell ) 54 retval.push( nearestCell ); 55 } 56 else 57 { 58 var walker = new CKEDITOR.dom.walker( range ); 59 var node; 60 walker.guard = moveOutOfCellGuard; 61 62 while ( ( node = walker.next() ) ) 63 { 64 // If may be possible for us to have a range like this: 65 // <td>^1</td><td>^2</td> 66 // The 2nd td shouldn't be included. 67 // 68 // So we have to take care to include a td we've entered only when we've 69 // walked into its children. 70 71 var parent = node.getParent(); 72 if ( parent && cellNodeRegex.test( parent.getName() ) && !parent.getCustomData( 'selected_cell' ) ) 73 { 74 CKEDITOR.dom.element.setMarker( database, parent, 'selected_cell', true ); 75 retval.push( parent ); 76 } 77 } 78 } 79 } 80 81 CKEDITOR.dom.element.clearAllMarkers( database ); 82 83 // Restore selection position. 84 selection.selectBookmarks( bookmarks ); 85 86 return retval; 87 } 88 89 function getFocusElementAfterDelCells( cellsToDelete ) { 90 var i = 0, 91 last = cellsToDelete.length - 1, 92 database = {}, 93 cell,focusedCell, 94 tr; 95 96 while ( ( cell = cellsToDelete[ i++ ] ) ) 97 CKEDITOR.dom.element.setMarker( database, cell, 'delete_cell', true ); 98 99 // 1.first we check left or right side focusable cell row by row; 100 i = 0; 101 while ( ( cell = cellsToDelete[ i++ ] ) ) 102 { 103 if ( ( focusedCell = cell.getPrevious() ) && !focusedCell.getCustomData( 'delete_cell' ) 104 || ( focusedCell = cell.getNext() ) && !focusedCell.getCustomData( 'delete_cell' ) ) 105 { 106 CKEDITOR.dom.element.clearAllMarkers( database ); 107 return focusedCell; 108 } 109 } 110 111 CKEDITOR.dom.element.clearAllMarkers( database ); 112 113 // 2. then we check the toppest row (outside the selection area square) focusable cell 114 tr = cellsToDelete[ 0 ].getParent(); 115 if ( ( tr = tr.getPrevious() ) ) 116 return tr.getLast(); 117 118 // 3. last we check the lowerest row focusable cell 119 tr = cellsToDelete[ last ].getParent(); 120 if ( ( tr = tr.getNext() ) ) 121 return tr.getChild( 0 ); 122 123 return null; 124 } 125 126 function clearRow( $tr ) 127 { 128 // Get the array of row's cells. 129 var $cells = $tr.cells; 130 131 // Empty all cells. 132 for ( var i = 0 ; i < $cells.length ; i++ ) 133 { 134 $cells[ i ].innerHTML = ''; 135 136 if ( !CKEDITOR.env.ie ) 137 ( new CKEDITOR.dom.element( $cells[ i ] ) ).appendBogus(); 138 } 139 } 140 141 function insertRow( selection, insertBefore ) 142 { 143 // Get the row where the selection is placed in. 144 var row = selection.getStartElement().getAscendant( 'tr' ); 145 if ( !row ) 146 return; 147 148 // Create a clone of the row. 149 var newRow = row.clone( true ); 150 151 // Insert the new row before of it. 152 newRow.insertBefore( row ); 153 154 // Clean one of the rows to produce the illusion of inserting an empty row 155 // before or after. 156 clearRow( insertBefore ? newRow.$ : row.$ ); 157 } 158 159 function deleteRows( selectionOrRow ) 160 { 161 if ( selectionOrRow instanceof CKEDITOR.dom.selection ) 162 { 163 var cells = getSelectedCells( selectionOrRow ), 164 cellsCount = cells.length, 165 rowsToDelete = [], 166 cursorPosition, 167 previousRowIndex, 168 nextRowIndex; 169 170 // Queue up the rows - it's possible and likely that we have duplicates. 171 for ( var i = 0 ; i < cellsCount ; i++ ) 172 { 173 var row = cells[ i ].getParent(), 174 rowIndex = row.$.rowIndex; 175 176 !i && ( previousRowIndex = rowIndex - 1 ); 177 rowsToDelete[ rowIndex ] = row; 178 i == cellsCount - 1 && ( nextRowIndex = rowIndex + 1 ); 179 } 180 181 var table = row.getAscendant( 'table' ), 182 rows = table.$.rows, 183 rowCount = rows.length; 184 185 // Where to put the cursor after rows been deleted? 186 // 1. Into next sibling row if any; 187 // 2. Into previous sibling row if any; 188 // 3. Into table's parent element if it's the very last row. 189 cursorPosition = new CKEDITOR.dom.element( 190 nextRowIndex < rowCount && table.$.rows[ nextRowIndex ] || 191 previousRowIndex > 0 && table.$.rows[ previousRowIndex ] || 192 table.$.parentNode ); 193 194 for ( i = rowsToDelete.length ; i >= 0 ; i-- ) 195 { 196 if ( rowsToDelete[ i ] ) 197 deleteRows( rowsToDelete[ i ] ); 198 } 199 200 return cursorPosition; 201 } 202 else if ( selectionOrRow instanceof CKEDITOR.dom.element ) 203 { 204 table = selectionOrRow.getAscendant( 'table' ); 205 206 if ( table.$.rows.length == 1 ) 207 table.remove(); 208 else 209 selectionOrRow.remove(); 210 } 211 212 return 0; 213 } 214 215 function insertColumn( selection, insertBefore ) 216 { 217 // Get the cell where the selection is placed in. 218 var startElement = selection.getStartElement(); 219 var cell = startElement.getAscendant( 'td', true ) || startElement.getAscendant( 'th', true ); 220 221 if ( !cell ) 222 return; 223 224 // Get the cell's table. 225 var table = cell.getAscendant( 'table' ); 226 var cellIndex = cell.$.cellIndex; 227 228 // Loop through all rows available in the table. 229 for ( var i = 0 ; i < table.$.rows.length ; i++ ) 230 { 231 var $row = table.$.rows[ i ]; 232 233 // If the row doesn't have enough cells, ignore it. 234 if ( $row.cells.length < ( cellIndex + 1 ) ) 235 continue; 236 237 cell = ( new CKEDITOR.dom.element( $row.cells[ cellIndex ] ) ).clone( false ); 238 239 if ( !CKEDITOR.env.ie ) 240 cell.appendBogus(); 241 242 // Get back the currently selected cell. 243 var baseCell = new CKEDITOR.dom.element( $row.cells[ cellIndex ] ); 244 if ( insertBefore ) 245 cell.insertBefore( baseCell ); 246 else 247 cell.insertAfter( baseCell ); 248 } 249 } 250 251 function getFocusElementAfterDelCols( cells ) 252 { 253 var cellIndexList = [], 254 table = cells[ 0 ] && cells[ 0 ].getAscendant( 'table' ), 255 i, length, 256 targetIndex, targetCell; 257 258 // get the cellIndex list of delete cells 259 for ( i = 0, length = cells.length; i < length; i++ ) 260 cellIndexList.push( cells[i].$.cellIndex ); 261 262 // get the focusable column index 263 cellIndexList.sort(); 264 for ( i = 1, length = cellIndexList.length; i < length; i++ ) 265 { 266 if ( cellIndexList[ i ] - cellIndexList[ i - 1 ] > 1 ) 267 { 268 targetIndex = cellIndexList[ i - 1 ] + 1; 269 break; 270 } 271 } 272 273 if ( !targetIndex ) 274 targetIndex = cellIndexList[ 0 ] > 0 ? ( cellIndexList[ 0 ] - 1 ) 275 : ( cellIndexList[ cellIndexList.length - 1 ] + 1 ); 276 277 // scan row by row to get the target cell 278 var rows = table.$.rows; 279 for ( i = 0, length = rows.length; i < length ; i++ ) 280 { 281 targetCell = rows[ i ].cells[ targetIndex ]; 282 if ( targetCell ) 283 break; 284 } 285 286 return targetCell ? new CKEDITOR.dom.element( targetCell ) : table.getPrevious(); 287 } 288 289 function deleteColumns( selectionOrCell ) 290 { 291 if ( selectionOrCell instanceof CKEDITOR.dom.selection ) 292 { 293 var colsToDelete = getSelectedCells( selectionOrCell ), 294 elementToFocus = getFocusElementAfterDelCols( colsToDelete ); 295 296 for ( var i = colsToDelete.length - 1 ; i >= 0 ; i-- ) 297 { 298 if ( colsToDelete[ i ] ) 299 deleteColumns( colsToDelete[ i ] ); 300 } 301 302 return elementToFocus; 303 } 304 else if ( selectionOrCell instanceof CKEDITOR.dom.element ) 305 { 306 // Get the cell's table. 307 var table = selectionOrCell.getAscendant( 'table' ); 308 if ( !table ) 309 return null; 310 311 // Get the cell index. 312 var cellIndex = selectionOrCell.$.cellIndex; 313 314 /* 315 * Loop through all rows from down to up, coz it's possible that some rows 316 * will be deleted. 317 */ 318 for ( i = table.$.rows.length - 1 ; i >= 0 ; i-- ) 319 { 320 // Get the row. 321 var row = new CKEDITOR.dom.element( table.$.rows[ i ] ); 322 323 // If the cell to be removed is the first one and the row has just one cell. 324 if ( !cellIndex && row.$.cells.length == 1 ) 325 { 326 deleteRows( row ); 327 continue; 328 } 329 330 // Else, just delete the cell. 331 if ( row.$.cells[ cellIndex ] ) 332 row.$.removeChild( row.$.cells[ cellIndex ] ); 333 } 334 } 335 336 return null; 337 } 338 339 function insertCell( selection, insertBefore ) 340 { 341 var startElement = selection.getStartElement(); 342 var cell = startElement.getAscendant( 'td', true ) || startElement.getAscendant( 'th', true ); 343 344 if ( !cell ) 345 return; 346 347 // Create the new cell element to be added. 348 var newCell = cell.clone(); 349 if ( !CKEDITOR.env.ie ) 350 newCell.appendBogus(); 351 352 if ( insertBefore ) 353 newCell.insertBefore( cell ); 354 else 355 newCell.insertAfter( cell ); 356 } 357 358 function deleteCells( selectionOrCell ) 359 { 360 if ( selectionOrCell instanceof CKEDITOR.dom.selection ) 361 { 362 var cellsToDelete = getSelectedCells( selectionOrCell ); 363 var table = cellsToDelete[ 0 ] && cellsToDelete[ 0 ].getAscendant( 'table' ); 364 var cellToFocus = getFocusElementAfterDelCells( cellsToDelete ); 365 366 for ( var i = cellsToDelete.length - 1 ; i >= 0 ; i-- ) 367 deleteCells( cellsToDelete[ i ] ); 368 369 if ( cellToFocus ) 370 placeCursorInCell( cellToFocus, true ); 371 else if ( table ) 372 table.remove(); 373 } 374 else if ( selectionOrCell instanceof CKEDITOR.dom.element ) 375 { 376 var tr = selectionOrCell.getParent(); 377 if ( tr.getChildCount() == 1 ) 378 tr.remove(); 379 else 380 selectionOrCell.remove(); 381 } 382 } 383 384 // Remove filler at end and empty spaces around the cell content. 385 function trimCell( cell ) 386 { 387 var bogus = cell.getBogus(); 388 bogus && bogus.remove(); 389 cell.trim(); 390 } 391 392 function placeCursorInCell( cell, placeAtEnd ) 393 { 394 var range = new CKEDITOR.dom.range( cell.getDocument() ); 395 if ( !range[ 'moveToElementEdit' + ( placeAtEnd ? 'End' : 'Start' ) ]( cell ) ) 396 { 397 range.selectNodeContents( cell ); 398 range.collapse( placeAtEnd ? false : true ); 399 } 400 range.select( true ); 401 } 402 403 function cellInRow( tableMap, rowIndex, cell ) 404 { 405 var oRow = tableMap[ rowIndex ]; 406 if ( typeof cell == 'undefined' ) 407 return oRow; 408 409 for ( var c = 0 ; oRow && c < oRow.length ; c++ ) 410 { 411 if ( cell.is && oRow[c] == cell.$ ) 412 return c; 413 else if ( c == cell ) 414 return new CKEDITOR.dom.element( oRow[ c ] ); 415 } 416 return cell.is ? -1 : null; 417 } 418 419 function cellInCol( tableMap, colIndex, cell ) 420 { 421 var oCol = []; 422 for ( var r = 0; r < tableMap.length; r++ ) 423 { 424 var row = tableMap[ r ]; 425 if ( typeof cell == 'undefined' ) 426 oCol.push( row[ colIndex ] ); 427 else if ( cell.is && row[ colIndex ] == cell.$ ) 428 return r; 429 else if ( r == cell ) 430 return new CKEDITOR.dom.element( row[ colIndex ] ); 431 } 432 433 return ( typeof cell == 'undefined' )? oCol : cell.is ? -1 : null; 434 } 435 436 function mergeCells( selection, mergeDirection, isDetect ) 437 { 438 var cells = getSelectedCells( selection ); 439 440 // Invalid merge request if: 441 // 1. In batch mode despite that less than two selected. 442 // 2. In solo mode while not exactly only one selected. 443 // 3. Cells distributed in different table groups (e.g. from both thead and tbody). 444 var commonAncestor; 445 if ( ( mergeDirection ? cells.length != 1 : cells.length < 2 ) 446 || ( commonAncestor = selection.getCommonAncestor() ) 447 && commonAncestor.type == CKEDITOR.NODE_ELEMENT 448 && commonAncestor.is( 'table' ) ) 449 { 450 return false; 451 } 452 453 var cell, 454 firstCell = cells[ 0 ], 455 table = firstCell.getAscendant( 'table' ), 456 map = CKEDITOR.tools.buildTableMap( table ), 457 mapHeight = map.length, 458 mapWidth = map[ 0 ].length, 459 startRow = firstCell.getParent().$.rowIndex, 460 startColumn = cellInRow( map, startRow, firstCell ); 461 462 if ( mergeDirection ) 463 { 464 var targetCell; 465 try 466 { 467 targetCell = 468 map[ mergeDirection == 'up' ? 469 ( startRow - 1 ): 470 mergeDirection == 'down' ? ( startRow + 1 ) : startRow ] [ 471 mergeDirection == 'left' ? 472 ( startColumn - 1 ): 473 mergeDirection == 'right' ? ( startColumn + 1 ) : startColumn ]; 474 475 } 476 catch( er ) 477 { 478 return false; 479 } 480 481 // 1. No cell could be merged. 482 // 2. Same cell actually. 483 if ( !targetCell || firstCell.$ == targetCell ) 484 return false; 485 486 // Sort in map order regardless of the DOM sequence. 487 cells[ ( mergeDirection == 'up' || mergeDirection == 'left' ) ? 488 'unshift' : 'push' ]( new CKEDITOR.dom.element( targetCell ) ); 489 } 490 491 // Start from here are merging way ignorance (merge up/right, batch merge). 492 var doc = firstCell.getDocument(), 493 lastRowIndex = startRow, 494 totalRowSpan = 0, 495 totalColSpan = 0, 496 // Use a documentFragment as buffer when appending cell contents. 497 frag = !isDetect && new CKEDITOR.dom.documentFragment( doc ), 498 dimension = 0; 499 500 for ( var i = 0; i < cells.length; i++ ) 501 { 502 cell = cells[ i ]; 503 504 var tr = cell.getParent(), 505 cellFirstChild = cell.getFirst(), 506 colSpan = cell.$.colSpan, 507 rowSpan = cell.$.rowSpan, 508 rowIndex = tr.$.rowIndex, 509 colIndex = cellInRow( map, rowIndex, cell ); 510 511 // Accumulated the actual places taken by all selected cells. 512 dimension += colSpan * rowSpan; 513 // Accumulated the maximum virtual spans from column and row. 514 totalColSpan = Math.max( totalColSpan, colIndex - startColumn + colSpan ) ; 515 totalRowSpan = Math.max( totalRowSpan, rowIndex - startRow + rowSpan ); 516 517 if ( !isDetect ) 518 { 519 // Trim all cell fillers and check to remove empty cells. 520 if ( trimCell( cell ), cell.getChildren().count() ) 521 { 522 // Merge vertically cells as two separated paragraphs. 523 if ( rowIndex != lastRowIndex 524 && cellFirstChild 525 && !( cellFirstChild.isBlockBoundary 526 && cellFirstChild.isBlockBoundary( { br : 1 } ) ) ) 527 { 528 var last = frag.getLast( CKEDITOR.dom.walker.whitespaces( true ) ); 529 if ( last && !( last.is && last.is( 'br' ) ) ) 530 frag.append( new CKEDITOR.dom.element( 'br' ) ); 531 } 532 533 cell.moveChildren( frag ); 534 } 535 i ? cell.remove() : cell.setHtml( '' ); 536 } 537 lastRowIndex = rowIndex; 538 } 539 540 if ( !isDetect ) 541 { 542 frag.moveChildren( firstCell ); 543 544 if ( !CKEDITOR.env.ie ) 545 firstCell.appendBogus(); 546 547 if ( totalColSpan >= mapWidth ) 548 firstCell.removeAttribute( 'rowSpan' ); 549 else 550 firstCell.$.rowSpan = totalRowSpan; 551 552 if ( totalRowSpan >= mapHeight ) 553 firstCell.removeAttribute( 'colSpan' ); 554 else 555 firstCell.$.colSpan = totalColSpan; 556 557 // Swip empty <tr> left at the end of table due to the merging. 558 var trs = new CKEDITOR.dom.nodeList( table.$.rows ), 559 count = trs.count(); 560 561 for ( i = count - 1; i >= 0; i-- ) 562 { 563 var tailTr = trs.getItem( i ); 564 if ( !tailTr.$.cells.length ) 565 { 566 tailTr.remove(); 567 count++; 568 continue; 569 } 570 } 571 572 return firstCell; 573 } 574 // Be able to merge cells only if actual dimension of selected 575 // cells equals to the caculated rectangle. 576 else 577 return ( totalRowSpan * totalColSpan ) == dimension; 578 } 579 580 function verticalSplitCell ( selection, isDetect ) 581 { 582 var cells = getSelectedCells( selection ); 583 if ( cells.length > 1 ) 584 return false; 585 else if ( isDetect ) 586 return true; 587 588 var cell = cells[ 0 ], 589 tr = cell.getParent(), 590 table = tr.getAscendant( 'table' ), 591 map = CKEDITOR.tools.buildTableMap( table ), 592 rowIndex = tr.$.rowIndex, 593 colIndex = cellInRow( map, rowIndex, cell ), 594 rowSpan = cell.$.rowSpan, 595 newCell, 596 newRowSpan, 597 newCellRowSpan, 598 newRowIndex; 599 600 if ( rowSpan > 1 ) 601 { 602 newRowSpan = Math.ceil( rowSpan / 2 ); 603 newCellRowSpan = Math.floor( rowSpan / 2 ); 604 newRowIndex = rowIndex + newRowSpan; 605 var newCellTr = new CKEDITOR.dom.element( table.$.rows[ newRowIndex ] ), 606 newCellRow = cellInRow( map, newRowIndex ), 607 candidateCell; 608 609 newCell = cell.clone(); 610 611 // Figure out where to insert the new cell by checking the vitual row. 612 for ( var c = 0; c < newCellRow.length; c++ ) 613 { 614 candidateCell = newCellRow[ c ]; 615 // Catch first cell actually following the column. 616 if ( candidateCell.parentNode == newCellTr.$ 617 && c > colIndex ) 618 { 619 newCell.insertBefore( new CKEDITOR.dom.element( candidateCell ) ); 620 break; 621 } 622 else 623 candidateCell = null; 624 } 625 626 // The destination row is empty, append at will. 627 if ( !candidateCell ) 628 newCellTr.append( newCell, true ); 629 } 630 else 631 { 632 newCellRowSpan = newRowSpan = 1; 633 634 newCellTr = tr.clone(); 635 newCellTr.insertAfter( tr ); 636 newCellTr.append( newCell = cell.clone() ); 637 638 var cellsInSameRow = cellInRow( map, rowIndex ); 639 for ( var i = 0; i < cellsInSameRow.length; i++ ) 640 cellsInSameRow[ i ].rowSpan++; 641 } 642 643 if ( !CKEDITOR.env.ie ) 644 newCell.appendBogus(); 645 646 cell.$.rowSpan = newRowSpan; 647 newCell.$.rowSpan = newCellRowSpan; 648 if ( newRowSpan == 1 ) 649 cell.removeAttribute( 'rowSpan' ); 650 if ( newCellRowSpan == 1 ) 651 newCell.removeAttribute( 'rowSpan' ); 652 653 return newCell; 654 } 655 656 function horizontalSplitCell( selection, isDetect ) 657 { 658 var cells = getSelectedCells( selection ); 659 if ( cells.length > 1 ) 660 return false; 661 else if ( isDetect ) 662 return true; 663 664 var cell = cells[ 0 ], 665 tr = cell.getParent(), 666 table = tr.getAscendant( 'table' ), 667 map = CKEDITOR.tools.buildTableMap( table ), 668 rowIndex = tr.$.rowIndex, 669 colIndex = cellInRow( map, rowIndex, cell ), 670 colSpan = cell.$.colSpan, 671 newCell, 672 newColSpan, 673 newCellColSpan; 674 675 if ( colSpan > 1 ) 676 { 677 newColSpan = Math.ceil( colSpan / 2 ); 678 newCellColSpan = Math.floor( colSpan / 2 ); 679 } 680 else 681 { 682 newCellColSpan = newColSpan = 1; 683 var cellsInSameCol = cellInCol( map, colIndex ); 684 for ( var i = 0; i < cellsInSameCol.length; i++ ) 685 cellsInSameCol[ i ].colSpan++; 686 } 687 newCell = cell.clone(); 688 newCell.insertAfter( cell ); 689 if ( !CKEDITOR.env.ie ) 690 newCell.appendBogus(); 691 692 cell.$.colSpan = newColSpan; 693 newCell.$.colSpan = newCellColSpan; 694 if ( newColSpan == 1 ) 695 cell.removeAttribute( 'colSpan' ); 696 if ( newCellColSpan == 1 ) 697 newCell.removeAttribute( 'colSpan' ); 698 699 return newCell; 700 } 701 // Context menu on table caption incorrect (#3834) 702 var contextMenuTags = { thead : 1, tbody : 1, tfoot : 1, td : 1, tr : 1, th : 1 }; 703 704 CKEDITOR.plugins.tabletools = 705 { 706 init : function( editor ) 707 { 708 var lang = editor.lang.table; 709 710 editor.addCommand( 'cellProperties', new CKEDITOR.dialogCommand( 'cellProperties' ) ); 711 CKEDITOR.dialog.add( 'cellProperties', this.path + 'dialogs/tableCell.js' ); 712 713 editor.addCommand( 'tableDelete', 714 { 715 exec : function( editor ) 716 { 717 var selection = editor.getSelection(); 718 var startElement = selection && selection.getStartElement(); 719 var table = startElement && startElement.getAscendant( 'table', true ); 720 721 if ( !table ) 722 return; 723 724 // Maintain the selection point at where the table was deleted. 725 selection.selectElement( table ); 726 var range = selection.getRanges()[0]; 727 range.collapse(); 728 selection.selectRanges( [ range ] ); 729 730 // If the table's parent has only one child, remove it,except body,as well.( #5416 ) 731 var parent = table.getParent(); 732 if ( parent.getChildCount() == 1 && parent.getName() != 'body' ) 733 parent.remove(); 734 else 735 table.remove(); 736 } 737 } ); 738 739 editor.addCommand( 'rowDelete', 740 { 741 exec : function( editor ) 742 { 743 var selection = editor.getSelection(); 744 placeCursorInCell( deleteRows( selection ) ); 745 } 746 } ); 747 748 editor.addCommand( 'rowInsertBefore', 749 { 750 exec : function( editor ) 751 { 752 var selection = editor.getSelection(); 753 insertRow( selection, true ); 754 } 755 } ); 756 757 editor.addCommand( 'rowInsertAfter', 758 { 759 exec : function( editor ) 760 { 761 var selection = editor.getSelection(); 762 insertRow( selection ); 763 } 764 } ); 765 766 editor.addCommand( 'columnDelete', 767 { 768 exec : function( editor ) 769 { 770 var selection = editor.getSelection(); 771 var element = deleteColumns( selection ); 772 element && placeCursorInCell( element, true ); 773 } 774 } ); 775 776 editor.addCommand( 'columnInsertBefore', 777 { 778 exec : function( editor ) 779 { 780 var selection = editor.getSelection(); 781 insertColumn( selection, true ); 782 } 783 } ); 784 785 editor.addCommand( 'columnInsertAfter', 786 { 787 exec : function( editor ) 788 { 789 var selection = editor.getSelection(); 790 insertColumn( selection ); 791 } 792 } ); 793 794 editor.addCommand( 'cellDelete', 795 { 796 exec : function( editor ) 797 { 798 var selection = editor.getSelection(); 799 deleteCells( selection ); 800 } 801 } ); 802 803 editor.addCommand( 'cellMerge', 804 { 805 exec : function( editor ) 806 { 807 placeCursorInCell( mergeCells( editor.getSelection() ), true ); 808 } 809 } ); 810 811 editor.addCommand( 'cellMergeRight', 812 { 813 exec : function( editor ) 814 { 815 placeCursorInCell( mergeCells( editor.getSelection(), 'right' ), true ); 816 } 817 } ); 818 819 editor.addCommand( 'cellMergeDown', 820 { 821 exec : function( editor ) 822 { 823 placeCursorInCell( mergeCells( editor.getSelection(), 'down' ), true ); 824 } 825 } ); 826 827 editor.addCommand( 'cellVerticalSplit', 828 { 829 exec : function( editor ) 830 { 831 placeCursorInCell( verticalSplitCell( editor.getSelection() ) ); 832 } 833 } ); 834 835 editor.addCommand( 'cellHorizontalSplit', 836 { 837 exec : function( editor ) 838 { 839 placeCursorInCell( horizontalSplitCell( editor.getSelection() ) ); 840 } 841 } ); 842 843 editor.addCommand( 'cellInsertBefore', 844 { 845 exec : function( editor ) 846 { 847 var selection = editor.getSelection(); 848 insertCell( selection, true ); 849 } 850 } ); 851 852 editor.addCommand( 'cellInsertAfter', 853 { 854 exec : function( editor ) 855 { 856 var selection = editor.getSelection(); 857 insertCell( selection ); 858 } 859 } ); 860 861 // If the "menu" plugin is loaded, register the menu items. 862 if ( editor.addMenuItems ) 863 { 864 editor.addMenuItems( 865 { 866 tablecell : 867 { 868 label : lang.cell.menu, 869 group : 'tablecell', 870 order : 1, 871 getItems : function() 872 { 873 var selection = editor.getSelection(), 874 cells = getSelectedCells( selection ); 875 return { 876 tablecell_insertBefore : CKEDITOR.TRISTATE_OFF, 877 tablecell_insertAfter : CKEDITOR.TRISTATE_OFF, 878 tablecell_delete : CKEDITOR.TRISTATE_OFF, 879 tablecell_merge : mergeCells( selection, null, true ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED, 880 tablecell_merge_right : mergeCells( selection, 'right', true ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED, 881 tablecell_merge_down : mergeCells( selection, 'down', true ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED, 882 tablecell_split_vertical : verticalSplitCell( selection, true ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED, 883 tablecell_split_horizontal : horizontalSplitCell( selection, true ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED, 884 tablecell_properties : cells.length > 0 ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED 885 }; 886 } 887 }, 888 889 tablecell_insertBefore : 890 { 891 label : lang.cell.insertBefore, 892 group : 'tablecell', 893 command : 'cellInsertBefore', 894 order : 5 895 }, 896 897 tablecell_insertAfter : 898 { 899 label : lang.cell.insertAfter, 900 group : 'tablecell', 901 command : 'cellInsertAfter', 902 order : 10 903 }, 904 905 tablecell_delete : 906 { 907 label : lang.cell.deleteCell, 908 group : 'tablecell', 909 command : 'cellDelete', 910 order : 15 911 }, 912 913 tablecell_merge : 914 { 915 label : lang.cell.merge, 916 group : 'tablecell', 917 command : 'cellMerge', 918 order : 16 919 }, 920 921 tablecell_merge_right : 922 { 923 label : lang.cell.mergeRight, 924 group : 'tablecell', 925 command : 'cellMergeRight', 926 order : 17 927 }, 928 929 tablecell_merge_down : 930 { 931 label : lang.cell.mergeDown, 932 group : 'tablecell', 933 command : 'cellMergeDown', 934 order : 18 935 }, 936 937 tablecell_split_horizontal : 938 { 939 label : lang.cell.splitHorizontal, 940 group : 'tablecell', 941 command : 'cellHorizontalSplit', 942 order : 19 943 }, 944 945 tablecell_split_vertical : 946 { 947 label : lang.cell.splitVertical, 948 group : 'tablecell', 949 command : 'cellVerticalSplit', 950 order : 20 951 }, 952 953 tablecell_properties : 954 { 955 label : lang.cell.title, 956 group : 'tablecellproperties', 957 command : 'cellProperties', 958 order : 21 959 }, 960 961 tablerow : 962 { 963 label : lang.row.menu, 964 group : 'tablerow', 965 order : 1, 966 getItems : function() 967 { 968 return { 969 tablerow_insertBefore : CKEDITOR.TRISTATE_OFF, 970 tablerow_insertAfter : CKEDITOR.TRISTATE_OFF, 971 tablerow_delete : CKEDITOR.TRISTATE_OFF 972 }; 973 } 974 }, 975 976 tablerow_insertBefore : 977 { 978 label : lang.row.insertBefore, 979 group : 'tablerow', 980 command : 'rowInsertBefore', 981 order : 5 982 }, 983 984 tablerow_insertAfter : 985 { 986 label : lang.row.insertAfter, 987 group : 'tablerow', 988 command : 'rowInsertAfter', 989 order : 10 990 }, 991 992 tablerow_delete : 993 { 994 label : lang.row.deleteRow, 995 group : 'tablerow', 996 command : 'rowDelete', 997 order : 15 998 }, 999 1000 tablecolumn : 1001 { 1002 label : lang.column.menu, 1003 group : 'tablecolumn', 1004 order : 1, 1005 getItems : function() 1006 { 1007 return { 1008 tablecolumn_insertBefore : CKEDITOR.TRISTATE_OFF, 1009 tablecolumn_insertAfter : CKEDITOR.TRISTATE_OFF, 1010 tablecolumn_delete : CKEDITOR.TRISTATE_OFF 1011 }; 1012 } 1013 }, 1014 1015 tablecolumn_insertBefore : 1016 { 1017 label : lang.column.insertBefore, 1018 group : 'tablecolumn', 1019 command : 'columnInsertBefore', 1020 order : 5 1021 }, 1022 1023 tablecolumn_insertAfter : 1024 { 1025 label : lang.column.insertAfter, 1026 group : 'tablecolumn', 1027 command : 'columnInsertAfter', 1028 order : 10 1029 }, 1030 1031 tablecolumn_delete : 1032 { 1033 label : lang.column.deleteColumn, 1034 group : 'tablecolumn', 1035 command : 'columnDelete', 1036 order : 15 1037 } 1038 }); 1039 } 1040 1041 // If the "contextmenu" plugin is laoded, register the listeners. 1042 if ( editor.contextMenu ) 1043 { 1044 editor.contextMenu.addListener( function( element, selection ) 1045 { 1046 if ( !element || element.isReadOnly() ) 1047 return null; 1048 1049 while ( element ) 1050 { 1051 if ( element.getName() in contextMenuTags ) 1052 { 1053 return { 1054 tablecell : CKEDITOR.TRISTATE_OFF, 1055 tablerow : CKEDITOR.TRISTATE_OFF, 1056 tablecolumn : CKEDITOR.TRISTATE_OFF 1057 }; 1058 } 1059 element = element.getParent(); 1060 } 1061 1062 return null; 1063 } ); 1064 } 1065 }, 1066 1067 getSelectedCells : getSelectedCells 1068 1069 }; 1070 CKEDITOR.plugins.add( 'tabletools', CKEDITOR.plugins.tabletools ); 1071 })(); 1072 1073 /** 1074 * Create a two-dimension array that reflects the actual layout of table cells, 1075 * with cell spans, with mappings to the original td elements. 1076 * @param table {CKEDITOR.dom.element} 1077 */ 1078 CKEDITOR.tools.buildTableMap = function ( table ) 1079 { 1080 var aRows = table.$.rows ; 1081 1082 // Row and Column counters. 1083 var r = -1 ; 1084 1085 var aMap = []; 1086 1087 for ( var i = 0 ; i < aRows.length ; i++ ) 1088 { 1089 r++ ; 1090 !aMap[r] && ( aMap[r] = [] ); 1091 1092 var c = -1 ; 1093 1094 for ( var j = 0 ; j < aRows[i].cells.length ; j++ ) 1095 { 1096 var oCell = aRows[i].cells[j] ; 1097 1098 c++ ; 1099 while ( aMap[r][c] ) 1100 c++ ; 1101 1102 var iColSpan = isNaN( oCell.colSpan ) ? 1 : oCell.colSpan ; 1103 var iRowSpan = isNaN( oCell.rowSpan ) ? 1 : oCell.rowSpan ; 1104 1105 for ( var rs = 0 ; rs < iRowSpan ; rs++ ) 1106 { 1107 if ( !aMap[r + rs] ) 1108 aMap[r + rs] = []; 1109 1110 for ( var cs = 0 ; cs < iColSpan ; cs++ ) 1111 { 1112 aMap[r + rs][c + cs] = aRows[i].cells[j] ; 1113 } 1114 } 1115 1116 c += iColSpan - 1 ; 1117 } 1118 } 1119 return aMap ; 1120 };
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Thu Mar 24 11:18:33 2011 | Cross-referenced by PHPXref 0.7 |