Ticket #2854: 2854.patch
File 2854.patch, 47.2 KB (added by , 12 years ago) |
---|
-
_source/core/config.js
146 146 * @example 147 147 * config.plugins = 'basicstyles,button,htmldataprocessor,toolbar,wysiwygarea'; 148 148 */ 149 plugins : 'basicstyles,button,elementspath,horizontalrule,htmldataprocessor,keystrokes,newpage,removeformat,smiley,sourcearea, specialchar,tab,toolbar,wysiwygarea',149 plugins : 'basicstyles,button,elementspath,horizontalrule,htmldataprocessor,keystrokes,newpage,removeformat,smiley,sourcearea,table,specialchar,tab,toolbar,wysiwygarea', 150 150 151 151 /** 152 152 * The theme to be used to build the UI. -
_source/core/dom/element.js
660 660 661 661 if ( toStart ) 662 662 { 663 while ( ( child = $.lastChild) )663 while ( ( child = $.lastChild ) ) 664 664 target.insertBefore( $.removeChild( child ), target.firstChild ); 665 665 } 666 666 else 667 667 { 668 while ( ( child = $.firstChild) )668 while ( ( child = $.firstChild ) ) 669 669 target.appendChild( $.removeChild( child ) ); 670 670 } 671 671 }, -
_source/plugins/dialog/plugin.js
834 834 { 835 835 var selection = new CKEDITOR.dom.selection( this._.editor.document ); 836 836 this._.selectedRanges = selection.getRanges(); 837 this._.selectedElement = selection.getSelectedElement(); 837 838 } 838 839 }, 839 840 … … 846 847 clearSavedSelection : function() 847 848 { 848 849 delete this._.selectedRanges; 850 delete this._.selectedElement; 849 851 }, 850 852 851 853 /** 854 * Gets the saved control selection. Control selections should be retrieved 855 * with this function instead of from restoreSelection() because 856 * restoreSelection() does not properly restore control selections. 857 * @returns {CKEDITOR.dom.element} The element that was selected. 858 * @example 859 */ 860 getSelectedElement : function() 861 { 862 return this._.selectedElement; 863 }, 864 865 /** 852 866 * Restores the editor's selection from the previously saved position in this 853 867 * dialog. 854 868 * This function is automatically called when a non-nested dialog is closed, -
_source/plugins/table/plugin.js
1 /* 2 Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved. 3 For licensing, see LICENSE.html or http://ckeditor.com/license 4 */ 5 6 CKEDITOR.plugins.add( 'table', 7 { 8 init : function( editor ) 9 { 10 var table = CKEDITOR.plugins.table; 11 12 editor.addCommand( 'table', new CKEDITOR.dialogCommand( 'table' ) ); 13 editor.addCommand( 'tableProperties', new CKEDITOR.dialogCommand( 'tableProperties' ) ); 14 editor.ui.addButton( 'Table', 15 { 16 label : editor.lang.table.toolbar, 17 command : 'table' 18 }); 19 20 CKEDITOR.dialog.add( 'table', this.path + 'dialogs/table.js' ); 21 CKEDITOR.dialog.add( 'tableProperties', this.path + 'dialogs/table.js' ); 22 } 23 } ); 24 25 CKEDITOR.config.table = 26 { 27 defaultValues : 28 { 29 rows : '3', 30 columns : '2', 31 border : '1', 32 align : '', 33 width : '200', 34 widthType : 'pixels', 35 height : '', 36 caption : '', 37 summary : '', 38 cellspacing : '1', 39 cellpadding : '1' 40 } 41 }; -
_source/plugins/table/dialogs/table.js
1 /* 2 Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved. 3 For licensing, see LICENSE.html or http://ckeditor.com/license 4 */ 5 6 (function() 7 { 8 var widthPattern = /^(\d+(?:\.\d+)?)(px|%)$/, 9 heightPattern = /^(\d+(?:\.\d+)?)px$/, 10 setupValue = function( data ) 11 { 12 var id = this.id; 13 if ( data.info && data.info[id] ) 14 this.setValue( data.info[id] ); 15 }, 16 commitValue = function( data ) 17 { 18 var id = this.id; 19 if ( !data.info ) 20 data.info = {}; 21 data.info[id] = this.getValue(); 22 }; 23 24 // Copy all the attributes from one node to the other, kinda like a clone 25 // skipAttributes is an object with the attributes that must NOT be copied 26 function copyAttributes( source, dest, skipAttributes ) 27 { 28 var attributes = source.$.attributes; 29 30 for ( var n = 0 ; n < attributes.length ; n++ ) 31 { 32 var attribute = attributes[n]; 33 34 if ( attribute.specified ) 35 { 36 var attrName = attribute.nodeName; 37 // We can set the type only once, so do it with the proper value, not copying it. 38 if ( attrName in skipAttributes ) 39 continue; 40 41 var attrValue = source.getAttribute( attrName ); 42 if ( attrValue == null ) 43 attrValue = attribute.nodeValue; 44 45 dest.setAttribute( attrName, attrValue ); 46 } 47 } 48 // The style: 49 if ( source.$.style.cssText !== '' ) 50 dest.$.style.cssText = source.$.style.cssText; 51 } 52 53 /** 54 * Replaces a tag with another one, keeping its contents: 55 * for example TD --> TH, and TH --> TD. 56 * input: the original node, and the new tag name 57 * http://www.w3.org/TR/DOM-Level-3-Core/core.html#Document3-renameNode 58 */ 59 function renameNode( node , newTag ) 60 { 61 // Only rename element nodes. 62 if ( node.type != CKEDITOR.NODE_ELEMENT ) 63 return null; 64 65 // If it's already correct exit here. 66 if ( node.getName() == newTag ) 67 return node; 68 69 var doc = node.getDocument(); 70 71 // Create the new node 72 var newNode = new CKEDITOR.dom.element( newTag, doc ); 73 74 // Copy all attributes 75 copyAttributes( node, newNode, {} ); 76 77 // Move children to the new node 78 node.moveChildren( newNode ); 79 80 // Finally replace the node and return the new one 81 node.$.parentNode.replaceChild( newNode.$, node.$ ); 82 83 return newNode; 84 } 85 86 function tableDialog( editor, command ) 87 { 88 var makeElement = function( name ){ return new CKEDITOR.dom.element( name, editor.document ); }; 89 90 return { 91 title : editor.lang.table.title, 92 minWidth : 480, 93 minHeight : 260, 94 onShow : function() 95 { 96 // Detect if there's a selected table. 97 this.restoreSelection(); 98 var selection = editor.getSelection(), 99 ranges = selection.getRanges(), 100 me = this, 101 selectedTable = null, 102 data = {}; 103 104 if ( command == 'tableProperties' ) 105 { 106 if ( ( selectedTable = this.getSelectedElement() ) ) 107 { 108 if ( selectedTable.getName() != 'table' ) 109 selectedTable = null; 110 } 111 else if ( ranges.length > 0 ) 112 { 113 var rangeRoot = ranges[0].getCommonAncestor( true ); 114 selectedTable = rangeRoot.getAscendant( 'table', true ); 115 } 116 117 // Fill in relevant fields if there's a selected table. 118 var rowsInput = this.getContentElement( 'info', 'txtRows' ), 119 colsInput = this.getContentElement( 'info', 'txtCols' ); 120 if ( selectedTable ) 121 { 122 var info = data.info = {}, 123 syncAttr = function( inputName, attrName ) 124 { 125 info[ inputName ] = selectedTable.getAttribute( attrName ); 126 }, 127 widthMatch = widthPattern.exec( selectedTable.$.style.width ), 128 heightMatch = heightPattern.exec( selectedTable.$.style.height ), 129 caption = selectedTable.getElementsByTag( 'caption' ); 130 131 // Fill in the simple properties. 132 syncAttr( 'txtBorder', 'border' ); 133 syncAttr( 'cmbAlign', 'align' ); 134 syncAttr( 'txtCellPad', 'cellPadding' ); 135 syncAttr( 'txtCellSpace', 'cellSpacing' ); 136 syncAttr( 'txtSummary', 'summary' ); 137 138 // Fill in the width and height. 139 if ( widthMatch ) 140 { 141 info.txtWidth = widthMatch[1]; 142 info.cmdWidthType = ( widthMatch[2] == 'px' ? 'pixels' : 'percents' ); 143 } 144 145 if ( heightMatch ) 146 info.txtHeight = heightMatch[1]; 147 148 // Fill in the headers field. 149 this.hasColumnHeaders = true; 150 151 // Check if all the first cells in every row are TH 152 for ( var row = 0 ; row < selectedTable.$.rows.length ; row++ ) 153 { 154 // If just one cell isn't a TH then it isn't a header column 155 if ( selectedTable.$.rows[row].cells[0].nodeName != 'TH' ) 156 { 157 this.hasColumnHeaders = false; 158 break; 159 } 160 } 161 162 // Check if the table contains <thead>. 163 if ( ( selectedTable.$.tHead !== null) ) 164 info.selHeaders = this.hasColumnHeaders ? 'both' : 'row'; 165 else 166 info.selHeaders = this.hasColumnHeaders ? 'col' : ''; 167 168 // Fill in the number of rows and columns. 169 info.txtRows = selectedTable.$.rows.length; 170 info.txtCols = selectedTable.$.rows[0].cells.length; 171 172 // Fill in the caption. 173 if ( caption.count() > 0 ) 174 { 175 caption = caption.getItem( 0 ); 176 caption = ( caption.getChild( 0 ) && caption.getChild( 0 ).getText() ) || ''; 177 caption = CKEDITOR.tools.trim( caption ); 178 info.txtCaption = caption; 179 } 180 181 // Disable the rows and column fields if a table is already selected. 182 rowsInput && rowsInput.disable(); 183 colsInput && colsInput.disable(); 184 185 // Save a reference to the selected table, and push a new set of default values. 186 this._.selectedElement = selectedTable; 187 } 188 else 189 { 190 this._.selectedElement = null; 191 rowsInput && rowsInput.enable(); 192 colsInput && colsInput.enable(); 193 } 194 } 195 196 this.setupContent( data, selectedTable ); 197 this.pushDefault(); 198 }, 199 onHide : function() 200 { 201 this.popDefault(); 202 }, 203 onOk : function() 204 { 205 var table = this._.selectedElement || makeElement( 'table' ), 206 me = this, 207 data = {}; 208 209 this.commitContent( data ); 210 211 if ( data.info ) 212 { 213 // Insert or modify the caption, if any. 214 var info = data.info, 215 caption = info.txtCaption; 216 if ( caption ) 217 { 218 var captionElement = table.getElementsByTag( 'caption' ); 219 if ( captionElement.count() > 0 ) 220 { 221 captionElement = captionElement.getItem( 0 ); 222 captionElement.setHtml( '' ); 223 } 224 else 225 captionElement = new CKEDITOR.dom.element( 'caption', editor.document ); 226 captionElement.append( new CKEDITOR.dom.text( caption, editor.document ) ); 227 if ( table.getChildCount() ) 228 captionElement.insertBefore( table.getFirst() ); 229 else 230 captionElement.appendTo( table ); 231 } 232 233 // Generate the rows and cols. 234 if ( !this._.selectedElement ) 235 { 236 var tbody = table.append( makeElement( 'tbody' ) ), 237 rows = parseInt( info.txtRows, 10 ) || 0; 238 cols = parseInt( info.txtCols, 10 ) || 0; 239 240 for ( var i = 0 ; i < rows ; i++ ) 241 { 242 var row = tbody.append( makeElement( 'tr' ) ); 243 for ( var j = 0 ; j < cols ; j++ ) 244 { 245 var cell = row.append( makeElement( 'td' ) ); 246 if ( !CKEDITOR.env.ie ) 247 cell.append( makeElement( 'br' ) ); 248 } 249 } 250 } 251 252 // Modify the table headers. 253 // Should we make a <thead>? 254 var headers = info.selHeaders; 255 if ( table.$.tHead == null && ( headers == 'row' || headers == 'both' ) ) 256 { 257 var thead = new CKEDITOR.dom.element( table.$.createTHead() ), 258 tbody = table.getElementsByTag( 'tbody' ).getItem( 0 ), 259 theRow = tbody.getElementsByTag( 'tr' ).getItem( 0 ); 260 261 // Change TD to TH: 262 for ( var i = 0 ; i < theRow.getChildCount() ; i++ ) 263 { 264 var th = renameNode( theRow.getChild( i ), 'th' ); 265 if ( th != null ) 266 th.setAttribute( 'scope', 'col' ); 267 } 268 thead.append( theRow.remove() ); 269 } 270 271 if ( table.$.tHead !== null && !( headers == 'row' || headers == 'both' ) ) 272 { 273 // Move the row out of the THead and put it in the TBody: 274 var thead = new CKEDITOR.dom.element( table.$.tHead ), 275 tbody = table.getElementsByTag( 'tbody' ).getItem( 0 ); 276 277 var previousFirstRow = tbody.getFirst(); 278 while ( thead.getChildCount() > 0 ) 279 { 280 var theRow = thead.getFirst(); 281 for ( var i = 0; i < theRow.getChildCount() ; i++ ) 282 { 283 var newCell = renameNode( theRow.getChild( i ), 'td' ); 284 if ( newCell != null ) 285 newCell.removeAttribute( 'scope' ); 286 } 287 theRow.insertBefore( previousFirstRow ); 288 } 289 thead.remove(); 290 } 291 292 // Should we make all first cells in a row TH? 293 if ( !this.hasColumnHeaders && ( headers == 'col' || headers == 'both' ) ) 294 { 295 for( var row = 0 ; row < table.$.rows.length ; row++ ) 296 { 297 var newCell = renameNode( new CKEDITOR.dom.element( table.$.rows[row].cells[0] ), 'th' ); 298 if ( newCell != null ) 299 newCell.setAttribute( 'scope', 'col' ); 300 } 301 } 302 303 // Should we make all first TH-cells in a row make TD? If 'yes' we do it the other way round :-) 304 if ( ( this.hasColumnHeaders ) && !( headers == 'col' || headers == 'both' ) ) 305 { 306 for( var i = 0 ; i < table.$.rows.length ; i++ ) 307 { 308 var row = new CKEDITOR.dom.element( table.$.rows[i] ); 309 if ( row.getParent().getName() == 'tbody' ) 310 { 311 var newCell = renameNode( new CKEDITOR.dom.element( row.$.cells[0] ), 'td' ); 312 if ( newCell != null ) 313 newCell.removeAttribute( 'scope' ); 314 } 315 } 316 } 317 318 // Set the basic attributes. 319 var setAttr = function( inputNameOrFunction, attrName ) 320 { 321 var value = ( typeof( inputNameOrFunction ) == 'function' ) ? inputNameOrFunction() 322 : info[ inputNameOrFunction ]; 323 if ( value ) 324 table.setAttribute( attrName, value ); 325 else 326 table.removeAttribute( attrName ); 327 }; 328 setAttr( 'txtBorder', 'border' ); 329 setAttr( 'cmbAlign', 'align' ); 330 setAttr( 'txtCellPad', 'cellPadding' ); 331 setAttr( 'txtCellSpace', 'cellSpacing' ); 332 setAttr( 'txtSummary', 'summary' ); 333 setAttr( function() 334 { 335 var styles = []; 336 if ( info.txtHeight ) 337 styles.push( 'height:' + info.txtHeight + 'px' ); 338 if ( info.txtWidth ) 339 { 340 var type = info.cmdWidthType || 'pixels'; 341 styles.push( 'width:' + info.txtWidth + ( type == 'pixels' ? 'px' : '%' ) ); 342 } 343 return styles.join( ';' ); 344 }, 'style' ); 345 } 346 347 // Insert the table element if we're creating one. 348 if ( !this._.selectedElement ) 349 { 350 this.restoreSelection(); 351 editor.insertElement( table ); 352 this.clearSavedSelection(); 353 } 354 355 return true; 356 }, 357 contents : [ 358 { 359 id : 'info', 360 label : editor.lang.table.title, 361 accessKey : 'I', 362 elements : 363 [ 364 { 365 type : 'hbox', 366 widths : [ '40%', '10%', '60%' ], 367 children : 368 [ 369 { 370 type : 'vbox', 371 padding : 0, 372 children : 373 [ 374 { 375 type : 'text', 376 id : 'txtRows', 377 labelLayout : 'horizontal', 378 widths : [ '60%','40%' ], 379 style : 'width:105px', 380 'default' : editor.config.table.defaultValues.rows, 381 label : editor.lang.table.rows, 382 validate: function( ) { 383 if ( this.getValue() != '' ) 384 { 385 if ( isNaN( parseInt( this.getValue(), 10 ) ) ) 386 { 387 this.select(); 388 return false; 389 } 390 } 391 return true; 392 }, 393 setup : function( data, selectedElement ) 394 { 395 setupValue.apply( this, arguments); 396 if ( !selectedElement ) 397 this.select(); 398 }, 399 commit : commitValue 400 }, 401 { 402 type : 'text', 403 id : 'txtCols', 404 labelLayout : 'horizontal', 405 widths : [ '60%','40%' ], 406 style : 'width:105px', 407 'default' : editor.config.table.defaultValues.columns, 408 label : editor.lang.table.columns, 409 validate: function( ) { 410 if ( this.getValue() != '' ) 411 { 412 if ( isNaN( parseInt( this.getValue(), 10 ) ) ) 413 { 414 this.select(); 415 return false; 416 } 417 } 418 return true; 419 }, 420 setup : setupValue, 421 commit : commitValue 422 }, 423 { 424 type : 'select', 425 id : 'selHeaders', 426 labelLayout : 'horizontal', 427 'default' : '', 428 widths : [ '40%', '60%' ], 429 label : editor.lang.table.headers, 430 items : 431 [ 432 [ editor.lang.table.headersNone, '' ], 433 [ editor.lang.table.headersRow, 'row' ], 434 [ editor.lang.table.headersColumn, 'col' ], 435 [ editor.lang.table.headersBoth, 'both' ] 436 ], 437 setup : setupValue, 438 commit : commitValue 439 }, 440 { 441 type : 'text', 442 id : 'txtBorder', 443 labelLayout : 'horizontal', 444 widths : [ '60%','40%' ], 445 style : 'width:105px', 446 'default' : editor.config.table.defaultValues.border, 447 label : editor.lang.table.border, 448 validate: function( ) { 449 if ( this.getValue() != '' ) 450 { 451 if ( isNaN( parseInt( this.getValue(), 10 ) ) ) 452 { 453 this.select(); 454 return false; 455 } 456 } 457 return true; 458 }, 459 setup : setupValue, 460 commit : commitValue 461 }, 462 { 463 id : 'cmbAlign', 464 type : 'select', 465 labelLayout : 'horizontal', 466 'default' : editor.config.table.defaultValues.align, 467 widths : [ '40%','60%' ], 468 label : editor.lang.table.align, 469 items : 470 [ 471 [ editor.lang.table.alignNotSet , ''], 472 [ editor.lang.table.alignLeft , 'left'], 473 [ editor.lang.table.alignCenter , 'center'], 474 [ editor.lang.table.alignRight , 'right'] 475 ], 476 setup : setupValue, 477 commit : commitValue 478 } 479 ] 480 }, 481 { 482 type : 'html', 483 align : 'right', 484 html : '' 485 }, 486 { 487 type : 'vbox', 488 align : 'right', 489 padding : 0, 490 children : 491 [ 492 { 493 type : 'hbox', 494 align : 'center', 495 widths : [ '70%', '30%' ], 496 children : 497 [ 498 { 499 type : 'text', 500 id : 'txtWidth', 501 labelLayout : 'horizontal', 502 widths : [ '50%','50%' ], 503 label : editor.lang.table.width, 504 'default' : editor.config.table.defaultValues.width, 505 validate: function( ) { 506 if ( this.getValue() != '' ) 507 { 508 if ( isNaN( parseInt( this.getValue(), 10 ) ) ) 509 { 510 this.select(); 511 return false; 512 } 513 } 514 return true; 515 }, 516 setup : function( data, selectedElement ) 517 { 518 setupValue.apply( this, arguments ); 519 if ( selectedElement ) 520 this.select(); 521 }, 522 commit : commitValue 523 }, 524 { 525 id : 'cmbWidthType', 526 type : 'select', 527 labelLayout : 'horizontal', 528 widths : [ '0%','100%' ], 529 label : '', 530 'default' : editor.config.table.defaultValues.widthType, 531 items : 532 [ 533 [ editor.lang.table.widthPx , 'pixels'], 534 [ editor.lang.table.widthPc , 'percents'] 535 ], 536 setup : setupValue, 537 commit : commitValue 538 } 539 ] 540 }, 541 { 542 type : 'hbox', 543 widths : [ '70%', '30%' ], 544 children : 545 [ 546 { 547 type : 'text', 548 id : 'txtHeight', 549 labelLayout : 'horizontal', 550 widths : [ '50%','50%' ], 551 label : editor.lang.table.height, 552 'default' : editor.config.table.defaultValues.height, 553 validate: function( ) { 554 if ( this.getValue() != '' ) 555 { 556 if ( isNaN( parseInt( this.getValue(), 10 ) ) ) 557 { 558 this.select(); 559 return false; 560 } 561 } 562 return true; 563 }, 564 setup : setupValue, 565 commit : commitValue 566 }, 567 { 568 type : 'html', 569 html : editor.lang.table.widthPx 570 } 571 ] 572 }, 573 { 574 type : 'html', 575 html : ' ' 576 }, 577 { 578 type : 'text', 579 id : 'txtCellSpace', 580 labelLayout : 'horizontal', 581 widths : [ '50%','50%' ], 582 style : 'width:140px', 583 label : editor.lang.table.cellSpace, 584 'default' : editor.config.table.defaultValues.cellspacing, 585 validate: function( ) { 586 if ( this.getValue() != '' ) 587 { 588 if ( isNaN( parseInt( this.getValue(), 10 ) ) ) 589 { 590 this.select(); 591 return false; 592 } 593 } 594 return true; 595 }, 596 setup : setupValue, 597 commit : commitValue 598 }, 599 { 600 type : 'text', 601 id : 'txtCellPad', 602 labelLayout : 'horizontal', 603 widths : [ '50%','50%' ], 604 style : 'width:140px', 605 label : editor.lang.table.cellPad, 606 'default' : editor.config.table.defaultValues.cellpadding, 607 validate: function( ) { 608 if ( this.getValue() != '' ) 609 { 610 if ( isNaN( parseInt( this.getValue(), 10 ) ) ) 611 { 612 this.select(); 613 return false; 614 } 615 } 616 return true; 617 }, 618 setup : setupValue, 619 commit : commitValue 620 } 621 ] 622 } 623 ] 624 }, 625 { 626 type : 'html', 627 align : 'right', 628 html : '' 629 }, 630 { 631 type : 'vbox', 632 padding : 0, 633 children : 634 [ 635 { 636 id : 'txtCaption', 637 type : 'text', 638 label : editor.lang.table.caption, 639 widths : [ '30%','70%' ], 640 labelLayout : 'horizontal', 641 'default' : editor.config.table.defaultValues.caption, 642 style : 'width:400px', 643 setup : setupValue, 644 commit : commitValue 645 }, 646 { 647 id : 'txtSummary', 648 type : 'text', 649 labelLayout : 'horizontal', 650 label : editor.lang.table.summary, 651 'default' : editor.config.table.defaultValues.summary, 652 widths : [ '30%','70%' ], 653 accessKey : 'A', 654 style : 'width:400px', 655 setup : setupValue, 656 commit : commitValue 657 } 658 ] 659 } 660 ] 661 } 662 ] 663 }; 664 } 665 666 CKEDITOR.dialog.add( 'table', function( editor ) 667 { 668 return tableDialog( editor, 'table' ); 669 } ); 670 CKEDITOR.dialog.add( 'tableProperties', function( editor ) 671 { 672 return tableDialog( editor, 'tableProperties' ); 673 } ); 674 })(); -
_source/plugins/table/dialogs/table.js
1 /* 2 Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved. 3 For licensing, see LICENSE.html or http://ckeditor.com/license 4 */ 5 6 (function() 7 { 8 var widthPattern = /^(\d+(?:\.\d+)?)(px|%)$/, 9 heightPattern = /^(\d+(?:\.\d+)?)px$/, 10 setupValue = function( data ) 11 { 12 var id = this.id; 13 if ( data.info && data.info[id] ) 14 this.setValue( data.info[id] ); 15 }, 16 commitValue = function( data ) 17 { 18 var id = this.id; 19 if ( !data.info ) 20 data.info = {}; 21 data.info[id] = this.getValue(); 22 }; 23 24 // Copy all the attributes from one node to the other, kinda like a clone 25 // skipAttributes is an object with the attributes that must NOT be copied 26 function copyAttributes( source, dest, skipAttributes ) 27 { 28 var attributes = source.$.attributes; 29 30 for ( var n = 0 ; n < attributes.length ; n++ ) 31 { 32 var attribute = attributes[n]; 33 34 if ( attribute.specified ) 35 { 36 var attrName = attribute.nodeName; 37 // We can set the type only once, so do it with the proper value, not copying it. 38 if ( attrName in skipAttributes ) 39 continue; 40 41 var attrValue = source.getAttribute( attrName ); 42 if ( attrValue == null ) 43 attrValue = attribute.nodeValue; 44 45 dest.setAttribute( attrName, attrValue ); 46 } 47 } 48 // The style: 49 if ( source.$.style.cssText !== '' ) 50 dest.$.style.cssText = source.$.style.cssText; 51 } 52 53 /** 54 * Replaces a tag with another one, keeping its contents: 55 * for example TD --> TH, and TH --> TD. 56 * input: the original node, and the new tag name 57 * http://www.w3.org/TR/DOM-Level-3-Core/core.html#Document3-renameNode 58 */ 59 function renameNode( node , newTag ) 60 { 61 // Only rename element nodes. 62 if ( node.type != CKEDITOR.NODE_ELEMENT ) 63 return null; 64 65 // If it's already correct exit here. 66 if ( node.getName() == newTag ) 67 return node; 68 69 var doc = node.getDocument(); 70 71 // Create the new node 72 var newNode = new CKEDITOR.dom.element( newTag, doc ); 73 74 // Copy all attributes 75 copyAttributes( node, newNode, {} ); 76 77 // Move children to the new node 78 node.moveChildren( newNode ); 79 80 // Finally replace the node and return the new one 81 node.$.parentNode.replaceChild( newNode.$, node.$ ); 82 83 return newNode; 84 } 85 86 function tableDialog( editor, command ) 87 { 88 var makeElement = function( name ){ return new CKEDITOR.dom.element( name, editor.document ); }; 89 90 return { 91 title : editor.lang.table.title, 92 minWidth : 480, 93 minHeight : 260, 94 onShow : function() 95 { 96 // Detect if there's a selected table. 97 this.restoreSelection(); 98 var selection = editor.getSelection(), 99 ranges = selection.getRanges(), 100 me = this, 101 selectedTable = null, 102 data = {}; 103 104 if ( command == 'tableProperties' ) 105 { 106 if ( ( selectedTable = this.getSelectedElement() ) ) 107 { 108 if ( selectedTable.getName() != 'table' ) 109 selectedTable = null; 110 } 111 else if ( ranges.length > 0 ) 112 { 113 var rangeRoot = ranges[0].getCommonAncestor( true ); 114 selectedTable = rangeRoot.getAscendant( 'table', true ); 115 } 116 117 // Fill in relevant fields if there's a selected table. 118 var rowsInput = this.getContentElement( 'info', 'txtRows' ), 119 colsInput = this.getContentElement( 'info', 'txtCols' ); 120 if ( selectedTable ) 121 { 122 var info = data.info = {}, 123 syncAttr = function( inputName, attrName ) 124 { 125 info[ inputName ] = selectedTable.getAttribute( attrName ); 126 }, 127 widthMatch = widthPattern.exec( selectedTable.$.style.width ), 128 heightMatch = heightPattern.exec( selectedTable.$.style.height ), 129 caption = selectedTable.getElementsByTag( 'caption' ); 130 131 // Fill in the simple properties. 132 syncAttr( 'txtBorder', 'border' ); 133 syncAttr( 'cmbAlign', 'align' ); 134 syncAttr( 'txtCellPad', 'cellPadding' ); 135 syncAttr( 'txtCellSpace', 'cellSpacing' ); 136 syncAttr( 'txtSummary', 'summary' ); 137 138 // Fill in the width and height. 139 if ( widthMatch ) 140 { 141 info.txtWidth = widthMatch[1]; 142 info.cmdWidthType = ( widthMatch[2] == 'px' ? 'pixels' : 'percents' ); 143 } 144 145 if ( heightMatch ) 146 info.txtHeight = heightMatch[1]; 147 148 // Fill in the headers field. 149 this.hasColumnHeaders = true; 150 151 // Check if all the first cells in every row are TH 152 for ( var row = 0 ; row < selectedTable.$.rows.length ; row++ ) 153 { 154 // If just one cell isn't a TH then it isn't a header column 155 if ( selectedTable.$.rows[row].cells[0].nodeName != 'TH' ) 156 { 157 this.hasColumnHeaders = false; 158 break; 159 } 160 } 161 162 // Check if the table contains <thead>. 163 if ( ( selectedTable.$.tHead !== null) ) 164 info.selHeaders = this.hasColumnHeaders ? 'both' : 'row'; 165 else 166 info.selHeaders = this.hasColumnHeaders ? 'col' : ''; 167 168 // Fill in the number of rows and columns. 169 info.txtRows = selectedTable.$.rows.length; 170 info.txtCols = selectedTable.$.rows[0].cells.length; 171 172 // Fill in the caption. 173 if ( caption.count() > 0 ) 174 { 175 caption = caption.getItem( 0 ); 176 caption = ( caption.getChild( 0 ) && caption.getChild( 0 ).getText() ) || ''; 177 caption = CKEDITOR.tools.trim( caption ); 178 info.txtCaption = caption; 179 } 180 181 // Disable the rows and column fields if a table is already selected. 182 rowsInput && rowsInput.disable(); 183 colsInput && colsInput.disable(); 184 185 // Save a reference to the selected table, and push a new set of default values. 186 this._.selectedElement = selectedTable; 187 } 188 else 189 { 190 this._.selectedElement = null; 191 rowsInput && rowsInput.enable(); 192 colsInput && colsInput.enable(); 193 } 194 } 195 196 this.setupContent( data, selectedTable ); 197 this.pushDefault(); 198 }, 199 onHide : function() 200 { 201 this.popDefault(); 202 }, 203 onOk : function() 204 { 205 var table = this._.selectedElement || makeElement( 'table' ), 206 me = this, 207 data = {}; 208 209 this.commitContent( data ); 210 211 if ( data.info ) 212 { 213 // Insert or modify the caption, if any. 214 var info = data.info, 215 caption = info.txtCaption; 216 if ( caption ) 217 { 218 var captionElement = table.getElementsByTag( 'caption' ); 219 if ( captionElement.count() > 0 ) 220 { 221 captionElement = captionElement.getItem( 0 ); 222 captionElement.setHtml( '' ); 223 } 224 else 225 captionElement = new CKEDITOR.dom.element( 'caption', editor.document ); 226 captionElement.append( new CKEDITOR.dom.text( caption, editor.document ) ); 227 if ( table.getChildCount() ) 228 captionElement.insertBefore( table.getFirst() ); 229 else 230 captionElement.appendTo( table ); 231 } 232 233 // Generate the rows and cols. 234 if ( !this._.selectedElement ) 235 { 236 var tbody = table.append( makeElement( 'tbody' ) ), 237 rows = parseInt( info.txtRows, 10 ) || 0; 238 cols = parseInt( info.txtCols, 10 ) || 0; 239 240 for ( var i = 0 ; i < rows ; i++ ) 241 { 242 var row = tbody.append( makeElement( 'tr' ) ); 243 for ( var j = 0 ; j < cols ; j++ ) 244 { 245 var cell = row.append( makeElement( 'td' ) ); 246 if ( !CKEDITOR.env.ie ) 247 cell.append( makeElement( 'br' ) ); 248 } 249 } 250 } 251 252 // Modify the table headers. 253 // Should we make a <thead>? 254 var headers = info.selHeaders; 255 if ( table.$.tHead == null && ( headers == 'row' || headers == 'both' ) ) 256 { 257 var thead = new CKEDITOR.dom.element( table.$.createTHead() ), 258 tbody = table.getElementsByTag( 'tbody' ).getItem( 0 ), 259 theRow = tbody.getElementsByTag( 'tr' ).getItem( 0 ); 260 261 // Change TD to TH: 262 for ( var i = 0 ; i < theRow.getChildCount() ; i++ ) 263 { 264 var th = renameNode( theRow.getChild( i ), 'th' ); 265 if ( th != null ) 266 th.setAttribute( 'scope', 'col' ); 267 } 268 thead.append( theRow.remove() ); 269 } 270 271 if ( table.$.tHead !== null && !( headers == 'row' || headers == 'both' ) ) 272 { 273 // Move the row out of the THead and put it in the TBody: 274 var thead = new CKEDITOR.dom.element( table.$.tHead ), 275 tbody = table.getElementsByTag( 'tbody' ).getItem( 0 ); 276 277 var previousFirstRow = tbody.getFirst(); 278 while ( thead.getChildCount() > 0 ) 279 { 280 var theRow = thead.getFirst(); 281 for ( var i = 0; i < theRow.getChildCount() ; i++ ) 282 { 283 var newCell = renameNode( theRow.getChild( i ), 'td' ); 284 if ( newCell != null ) 285 newCell.removeAttribute( 'scope' ); 286 } 287 theRow.insertBefore( previousFirstRow ); 288 } 289 thead.remove(); 290 } 291 292 // Should we make all first cells in a row TH? 293 if ( !this.hasColumnHeaders && ( headers == 'col' || headers == 'both' ) ) 294 { 295 for( var row = 0 ; row < table.$.rows.length ; row++ ) 296 { 297 var newCell = renameNode( new CKEDITOR.dom.element( table.$.rows[row].cells[0] ), 'th' ); 298 if ( newCell != null ) 299 newCell.setAttribute( 'scope', 'col' ); 300 } 301 } 302 303 // Should we make all first TH-cells in a row make TD? If 'yes' we do it the other way round :-) 304 if ( ( this.hasColumnHeaders ) && !( headers == 'col' || headers == 'both' ) ) 305 { 306 for( var i = 0 ; i < table.$.rows.length ; i++ ) 307 { 308 var row = new CKEDITOR.dom.element( table.$.rows[i] ); 309 if ( row.getParent().getName() == 'tbody' ) 310 { 311 var newCell = renameNode( new CKEDITOR.dom.element( row.$.cells[0] ), 'td' ); 312 if ( newCell != null ) 313 newCell.removeAttribute( 'scope' ); 314 } 315 } 316 } 317 318 // Set the basic attributes. 319 var setAttr = function( inputNameOrFunction, attrName ) 320 { 321 var value = ( typeof( inputNameOrFunction ) == 'function' ) ? inputNameOrFunction() 322 : info[ inputNameOrFunction ]; 323 if ( value ) 324 table.setAttribute( attrName, value ); 325 else 326 table.removeAttribute( attrName ); 327 }; 328 setAttr( 'txtBorder', 'border' ); 329 setAttr( 'cmbAlign', 'align' ); 330 setAttr( 'txtCellPad', 'cellPadding' ); 331 setAttr( 'txtCellSpace', 'cellSpacing' ); 332 setAttr( 'txtSummary', 'summary' ); 333 setAttr( function() 334 { 335 var styles = []; 336 if ( info.txtHeight ) 337 styles.push( 'height:' + info.txtHeight + 'px' ); 338 if ( info.txtWidth ) 339 { 340 var type = info.cmdWidthType || 'pixels'; 341 styles.push( 'width:' + info.txtWidth + ( type == 'pixels' ? 'px' : '%' ) ); 342 } 343 return styles.join( ';' ); 344 }, 'style' ); 345 } 346 347 // Insert the table element if we're creating one. 348 if ( !this._.selectedElement ) 349 { 350 this.restoreSelection(); 351 editor.insertElement( table ); 352 this.clearSavedSelection(); 353 } 354 355 return true; 356 }, 357 contents : [ 358 { 359 id : 'info', 360 label : editor.lang.table.title, 361 accessKey : 'I', 362 elements : 363 [ 364 { 365 type : 'hbox', 366 widths : [ '40%', '10%', '60%' ], 367 children : 368 [ 369 { 370 type : 'vbox', 371 padding : 0, 372 children : 373 [ 374 { 375 type : 'text', 376 id : 'txtRows', 377 labelLayout : 'horizontal', 378 widths : [ '60%','40%' ], 379 style : 'width:105px', 380 'default' : editor.config.table.defaultValues.rows, 381 label : editor.lang.table.rows, 382 validate: function( ) { 383 if ( this.getValue() != '' ) 384 { 385 if ( isNaN( parseInt( this.getValue(), 10 ) ) ) 386 { 387 this.select(); 388 return false; 389 } 390 } 391 return true; 392 }, 393 setup : function( data, selectedElement ) 394 { 395 setupValue.apply( this, arguments); 396 if ( !selectedElement ) 397 this.select(); 398 }, 399 commit : commitValue 400 }, 401 { 402 type : 'text', 403 id : 'txtCols', 404 labelLayout : 'horizontal', 405 widths : [ '60%','40%' ], 406 style : 'width:105px', 407 'default' : editor.config.table.defaultValues.columns, 408 label : editor.lang.table.columns, 409 validate: function( ) { 410 if ( this.getValue() != '' ) 411 { 412 if ( isNaN( parseInt( this.getValue(), 10 ) ) ) 413 { 414 this.select(); 415 return false; 416 } 417 } 418 return true; 419 }, 420 setup : setupValue, 421 commit : commitValue 422 }, 423 { 424 type : 'select', 425 id : 'selHeaders', 426 labelLayout : 'horizontal', 427 'default' : '', 428 widths : [ '40%', '60%' ], 429 label : editor.lang.table.headers, 430 items : 431 [ 432 [ editor.lang.table.headersNone, '' ], 433 [ editor.lang.table.headersRow, 'row' ], 434 [ editor.lang.table.headersColumn, 'col' ], 435 [ editor.lang.table.headersBoth, 'both' ] 436 ], 437 setup : setupValue, 438 commit : commitValue 439 }, 440 { 441 type : 'text', 442 id : 'txtBorder', 443 labelLayout : 'horizontal', 444 widths : [ '60%','40%' ], 445 style : 'width:105px', 446 'default' : editor.config.table.defaultValues.border, 447 label : editor.lang.table.border, 448 validate: function( ) { 449 if ( this.getValue() != '' ) 450 { 451 if ( isNaN( parseInt( this.getValue(), 10 ) ) ) 452 { 453 this.select(); 454 return false; 455 } 456 } 457 return true; 458 }, 459 setup : setupValue, 460 commit : commitValue 461 }, 462 { 463 id : 'cmbAlign', 464 type : 'select', 465 labelLayout : 'horizontal', 466 'default' : editor.config.table.defaultValues.align, 467 widths : [ '40%','60%' ], 468 label : editor.lang.table.align, 469 items : 470 [ 471 [ editor.lang.table.alignNotSet , ''], 472 [ editor.lang.table.alignLeft , 'left'], 473 [ editor.lang.table.alignCenter , 'center'], 474 [ editor.lang.table.alignRight , 'right'] 475 ], 476 setup : setupValue, 477 commit : commitValue 478 } 479 ] 480 }, 481 { 482 type : 'html', 483 align : 'right', 484 html : '' 485 }, 486 { 487 type : 'vbox', 488 align : 'right', 489 padding : 0, 490 children : 491 [ 492 { 493 type : 'hbox', 494 align : 'center', 495 widths : [ '70%', '30%' ], 496 children : 497 [ 498 { 499 type : 'text', 500 id : 'txtWidth', 501 labelLayout : 'horizontal', 502 widths : [ '50%','50%' ], 503 label : editor.lang.table.width, 504 'default' : editor.config.table.defaultValues.width, 505 validate: function( ) { 506 if ( this.getValue() != '' ) 507 { 508 if ( isNaN( parseInt( this.getValue(), 10 ) ) ) 509 { 510 this.select(); 511 return false; 512 } 513 } 514 return true; 515 }, 516 setup : function( data, selectedElement ) 517 { 518 setupValue.apply( this, arguments ); 519 if ( selectedElement ) 520 this.select(); 521 }, 522 commit : commitValue 523 }, 524 { 525 id : 'cmbWidthType', 526 type : 'select', 527 labelLayout : 'horizontal', 528 widths : [ '0%','100%' ], 529 label : '', 530 'default' : editor.config.table.defaultValues.widthType, 531 items : 532 [ 533 [ editor.lang.table.widthPx , 'pixels'], 534 [ editor.lang.table.widthPc , 'percents'] 535 ], 536 setup : setupValue, 537 commit : commitValue 538 } 539 ] 540 }, 541 { 542 type : 'hbox', 543 widths : [ '70%', '30%' ], 544 children : 545 [ 546 { 547 type : 'text', 548 id : 'txtHeight', 549 labelLayout : 'horizontal', 550 widths : [ '50%','50%' ], 551 label : editor.lang.table.height, 552 'default' : editor.config.table.defaultValues.height, 553 validate: function( ) { 554 if ( this.getValue() != '' ) 555 { 556 if ( isNaN( parseInt( this.getValue(), 10 ) ) ) 557 { 558 this.select(); 559 return false; 560 } 561 } 562 return true; 563 }, 564 setup : setupValue, 565 commit : commitValue 566 }, 567 { 568 type : 'html', 569 html : editor.lang.table.widthPx 570 } 571 ] 572 }, 573 { 574 type : 'html', 575 html : ' ' 576 }, 577 { 578 type : 'text', 579 id : 'txtCellSpace', 580 labelLayout : 'horizontal', 581 widths : [ '50%','50%' ], 582 style : 'width:140px', 583 label : editor.lang.table.cellSpace, 584 'default' : editor.config.table.defaultValues.cellspacing, 585 validate: function( ) { 586 if ( this.getValue() != '' ) 587 { 588 if ( isNaN( parseInt( this.getValue(), 10 ) ) ) 589 { 590 this.select(); 591 return false; 592 } 593 } 594 return true; 595 }, 596 setup : setupValue, 597 commit : commitValue 598 }, 599 { 600 type : 'text', 601 id : 'txtCellPad', 602 labelLayout : 'horizontal', 603 widths : [ '50%','50%' ], 604 style : 'width:140px', 605 label : editor.lang.table.cellPad, 606 'default' : editor.config.table.defaultValues.cellpadding, 607 validate: function( ) { 608 if ( this.getValue() != '' ) 609 { 610 if ( isNaN( parseInt( this.getValue(), 10 ) ) ) 611 { 612 this.select(); 613 return false; 614 } 615 } 616 return true; 617 }, 618 setup : setupValue, 619 commit : commitValue 620 } 621 ] 622 } 623 ] 624 }, 625 { 626 type : 'html', 627 align : 'right', 628 html : '' 629 }, 630 { 631 type : 'vbox', 632 padding : 0, 633 children : 634 [ 635 { 636 id : 'txtCaption', 637 type : 'text', 638 label : editor.lang.table.caption, 639 widths : [ '30%','70%' ], 640 labelLayout : 'horizontal', 641 'default' : editor.config.table.defaultValues.caption, 642 style : 'width:400px', 643 setup : setupValue, 644 commit : commitValue 645 }, 646 { 647 id : 'txtSummary', 648 type : 'text', 649 labelLayout : 'horizontal', 650 label : editor.lang.table.summary, 651 'default' : editor.config.table.defaultValues.summary, 652 widths : [ '30%','70%' ], 653 accessKey : 'A', 654 style : 'width:400px', 655 setup : setupValue, 656 commit : commitValue 657 } 658 ] 659 } 660 ] 661 } 662 ] 663 }; 664 } 665 666 CKEDITOR.dialog.add( 'table', function( editor ) 667 { 668 return tableDialog( editor, 'table' ); 669 } ); 670 CKEDITOR.dialog.add( 'tableProperties', function( editor ) 671 { 672 return tableDialog( editor, 'tableProperties' ); 673 } ); 674 })(); -
_source/plugins/table/plugin.js
1 /* 2 Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved. 3 For licensing, see LICENSE.html or http://ckeditor.com/license 4 */ 5 6 CKEDITOR.plugins.add( 'table', 7 { 8 init : function( editor ) 9 { 10 var table = CKEDITOR.plugins.table; 11 12 editor.addCommand( 'table', new CKEDITOR.dialogCommand( 'table' ) ); 13 editor.addCommand( 'tableProperties', new CKEDITOR.dialogCommand( 'tableProperties' ) ); 14 editor.ui.addButton( 'Table', 15 { 16 label : editor.lang.table.toolbar, 17 command : 'table' 18 }); 19 20 CKEDITOR.dialog.add( 'table', this.path + 'dialogs/table.js' ); 21 CKEDITOR.dialog.add( 'tableProperties', this.path + 'dialogs/table.js' ); 22 } 23 } ); 24 25 CKEDITOR.config.table = 26 { 27 defaultValues : 28 { 29 rows : '3', 30 columns : '2', 31 border : '1', 32 align : '', 33 width : '200', 34 widthType : 'pixels', 35 height : '', 36 caption : '', 37 summary : '', 38 cellspacing : '1', 39 cellpadding : '1' 40 } 41 }; -
_source/plugins/toolbar/plugin.js
211 211 'Bold', 'Italic', 'Underline', 'Strike', '-', 212 212 'Subscript', 'Superscript', '-', 213 213 'SelectAll', 'RemoveFormat', '-', 214 ' Smiley', 'HorizontalRule', 'SpecialChar'214 'Table', 'Smiley', 'HorizontalRule', 'SpecialChar' 215 215 ] 216 216 ]; -
_source/plugins/wysiwygarea/plugin.js
100 100 101 101 clone = element.clone( true ); 102 102 103 // If the new node is a block element, split the current block .103 // If the new node is a block element, split the current block (if any). 104 104 if ( this.config.enterMode != 'br' && isBlock ) 105 range.splitBlock(); 105 { 106 var startPath = new CKEDITOR.dom.elementPath( range.startContainer ); 107 if ( startPath.block ) 108 range.splitBlock(); 109 } 106 110 107 111 // Insert the new node. 108 112 range.insertNode( clone );