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 11 var commitValue = function( data ) 12 { 13 var id = this.id; 14 if ( !data.info ) 15 data.info = {}; 16 data.info[id] = this.getValue(); 17 }; 18 19 function tableDialog( editor, command ) 20 { 21 var makeElement = function( name ){ return new CKEDITOR.dom.element( name, editor.document ); }; 22 23 return { 24 title : editor.lang.table.title, 25 minWidth : 310, 26 minHeight : CKEDITOR.env.ie ? 310 : 280, 27 onShow : function() 28 { 29 // Detect if there's a selected table. 30 var selection = editor.getSelection(), 31 ranges = selection.getRanges(), 32 selectedTable = null; 33 34 var rowsInput = this.getContentElement( 'info', 'txtRows' ), 35 colsInput = this.getContentElement( 'info', 'txtCols' ), 36 widthInput = this.getContentElement( 'info', 'txtWidth' ); 37 if ( command == 'tableProperties' ) 38 { 39 if ( ( selectedTable = editor.getSelection().getSelectedElement() ) ) 40 { 41 if ( selectedTable.getName() != 'table' ) 42 selectedTable = null; 43 } 44 else if ( ranges.length > 0 ) 45 { 46 var rangeRoot = ranges[0].getCommonAncestor( true ); 47 selectedTable = rangeRoot.getAscendant( 'table', true ); 48 } 49 50 // Save a reference to the selected table, and push a new set of default values. 51 this._.selectedElement = selectedTable; 52 } 53 54 // Enable, disable and select the row, cols, width fields. 55 if ( selectedTable ) 56 { 57 this.setupContent( selectedTable ); 58 rowsInput && rowsInput.disable(); 59 colsInput && colsInput.disable(); 60 widthInput && widthInput.select(); 61 } 62 else 63 { 64 rowsInput && rowsInput.enable(); 65 colsInput && colsInput.enable(); 66 rowsInput && rowsInput.select(); 67 } 68 }, 69 onOk : function() 70 { 71 var table = this._.selectedElement || makeElement( 'table' ), 72 me = this, 73 data = {}; 74 75 this.commitContent( data, table ); 76 77 if ( data.info ) 78 { 79 var info = data.info; 80 81 // Generate the rows and cols. 82 if ( !this._.selectedElement ) 83 { 84 var tbody = table.append( makeElement( 'tbody' ) ), 85 rows = parseInt( info.txtRows, 10 ) || 0, 86 cols = parseInt( info.txtCols, 10 ) || 0; 87 88 for ( var i = 0 ; i < rows ; i++ ) 89 { 90 var row = tbody.append( makeElement( 'tr' ) ); 91 for ( var j = 0 ; j < cols ; j++ ) 92 { 93 var cell = row.append( makeElement( 'td' ) ); 94 if ( !CKEDITOR.env.ie ) 95 cell.append( makeElement( 'br' ) ); 96 } 97 } 98 } 99 100 // Modify the table headers. Depends on havint rows and cols generated 101 // correctly so it can't be done in commit functions. 102 103 // Should we make a <thead>? 104 var headers = info.selHeaders; 105 if ( !table.$.tHead && ( headers == 'row' || headers == 'both' ) ) 106 { 107 var thead = new CKEDITOR.dom.element( table.$.createTHead() ); 108 tbody = table.getElementsByTag( 'tbody' ).getItem( 0 ); 109 var theRow = tbody.getElementsByTag( 'tr' ).getItem( 0 ); 110 111 // Change TD to TH: 112 for ( i = 0 ; i < theRow.getChildCount() ; i++ ) 113 { 114 var th = theRow.getChild( i ); 115 if ( th.type == CKEDITOR.NODE_ELEMENT ) 116 { 117 th.renameNode( 'th' ); 118 if ( !i ) 119 th.setAttribute( 'scope', 'col' ); 120 } 121 } 122 thead.append( theRow.remove() ); 123 } 124 125 if ( table.$.tHead !== null && !( headers == 'row' || headers == 'both' ) ) 126 { 127 // Move the row out of the THead and put it in the TBody: 128 thead = new CKEDITOR.dom.element( table.$.tHead ); 129 tbody = table.getElementsByTag( 'tbody' ).getItem( 0 ); 130 131 var previousFirstRow = tbody.getFirst(); 132 while ( thead.getChildCount() > 0 ) 133 { 134 theRow = thead.getFirst(); 135 for ( i = 0; i < theRow.getChildCount() ; i++ ) 136 { 137 var newCell = theRow.getChild( i ); 138 if ( newCell.type == CKEDITOR.NODE_ELEMENT ) 139 { 140 newCell.renameNode( 'td' ); 141 newCell.removeAttribute( 'scope' ); 142 } 143 } 144 theRow.insertBefore( previousFirstRow ); 145 } 146 thead.remove(); 147 } 148 149 // Should we make all first cells in a row TH? 150 if ( !this.hasColumnHeaders && ( headers == 'col' || headers == 'both' ) ) 151 { 152 for( row = 0 ; row < table.$.rows.length ; row++ ) 153 { 154 newCell = new CKEDITOR.dom.element( table.$.rows[ row ].cells[ 0 ] ); 155 newCell.renameNode( 'th' ); 156 newCell.setAttribute( 'scope', 'col' ); 157 } 158 } 159 160 // Should we make all first TH-cells in a row make TD? If 'yes' we do it the other way round :-) 161 if ( ( this.hasColumnHeaders ) && !( headers == 'col' || headers == 'both' ) ) 162 { 163 for( i = 0 ; i < table.$.rows.length ; i++ ) 164 { 165 row = new CKEDITOR.dom.element( table.$.rows[i] ); 166 if ( row.getParent().getName() == 'tbody' ) 167 { 168 newCell = new CKEDITOR.dom.element( row.$.cells[0] ); 169 newCell.renameNode( 'td'); 170 newCell.removeAttribute( 'scope' ); 171 } 172 } 173 } 174 175 // Set the width and height. 176 var styles = []; 177 if ( info.txtHeight ) 178 styles.push( 'height:' + info.txtHeight + 'px' ); 179 if ( info.txtWidth ) 180 { 181 var type = info.cmbWidthType || 'pixels'; 182 styles.push( 'width:' + info.txtWidth + ( type == 'pixels' ? 'px' : '%' ) ); 183 } 184 styles = styles.join( ';' ); 185 if ( styles ) 186 table.$.style.cssText = styles; 187 else 188 table.removeAttribute( 'style' ); 189 } 190 191 // Insert the table element if we're creating one. 192 if ( !this._.selectedElement ) 193 editor.insertElement( table ); 194 195 return true; 196 }, 197 contents : [ 198 { 199 id : 'info', 200 label : editor.lang.table.title, 201 elements : 202 [ 203 { 204 type : 'hbox', 205 widths : [ null, null ], 206 styles : [ 'vertical-align:top' ], 207 children : 208 [ 209 { 210 type : 'vbox', 211 padding : 0, 212 children : 213 [ 214 { 215 type : 'text', 216 id : 'txtRows', 217 'default' : 3, 218 label : editor.lang.table.rows, 219 style : 'width:5em', 220 validate : function() 221 { 222 var pass = true, 223 value = this.getValue(); 224 pass = pass && CKEDITOR.dialog.validate.integer()( value ) 225 && value > 0; 226 if ( !pass ) 227 { 228 alert( editor.lang.table.invalidRows ); 229 this.select(); 230 } 231 return pass; 232 }, 233 setup : function( selectedElement ) 234 { 235 this.setValue( selectedElement.$.rows.length ); 236 }, 237 commit : commitValue 238 }, 239 { 240 type : 'text', 241 id : 'txtCols', 242 'default' : 2, 243 label : editor.lang.table.columns, 244 style : 'width:5em', 245 validate : function() 246 { 247 var pass = true, 248 value = this.getValue(); 249 pass = pass && CKEDITOR.dialog.validate.integer()( value ) 250 && value > 0; 251 if ( !pass ) 252 { 253 alert( editor.lang.table.invalidCols ); 254 this.select(); 255 } 256 return pass; 257 }, 258 setup : function( selectedTable ) 259 { 260 this.setValue( selectedTable.$.rows[0].cells.length); 261 }, 262 commit : commitValue 263 }, 264 { 265 type : 'html', 266 html : ' ' 267 }, 268 { 269 type : 'select', 270 id : 'selHeaders', 271 'default' : '', 272 label : editor.lang.table.headers, 273 items : 274 [ 275 [ editor.lang.table.headersNone, '' ], 276 [ editor.lang.table.headersRow, 'row' ], 277 [ editor.lang.table.headersColumn, 'col' ], 278 [ editor.lang.table.headersBoth, 'both' ] 279 ], 280 setup : function( selectedTable ) 281 { 282 // Fill in the headers field. 283 var dialog = this.getDialog(); 284 dialog.hasColumnHeaders = true; 285 286 // Check if all the first cells in every row are TH 287 for ( var row = 0 ; row < selectedTable.$.rows.length ; row++ ) 288 { 289 // If just one cell isn't a TH then it isn't a header column 290 if ( selectedTable.$.rows[row].cells[0].nodeName.toLowerCase() != 'th' ) 291 { 292 dialog.hasColumnHeaders = false; 293 break; 294 } 295 } 296 297 // Check if the table contains <thead>. 298 if ( ( selectedTable.$.tHead !== null) ) 299 this.setValue( dialog.hasColumnHeaders ? 'both' : 'row' ); 300 else 301 this.setValue( dialog.hasColumnHeaders ? 'col' : '' ); 302 }, 303 commit : commitValue 304 }, 305 { 306 type : 'text', 307 id : 'txtBorder', 308 'default' : 1, 309 label : editor.lang.table.border, 310 style : 'width:3em', 311 validate : CKEDITOR.dialog.validate['number']( editor.lang.table.invalidBorder ), 312 setup : function( selectedTable ) 313 { 314 this.setValue( selectedTable.getAttribute( 'border' ) || '' ); 315 }, 316 commit : function( data, selectedTable ) 317 { 318 if ( this.getValue() ) 319 selectedTable.setAttribute( 'border', this.getValue() ); 320 else 321 selectedTable.removeAttribute( 'border' ); 322 } 323 }, 324 { 325 id : 'cmbAlign', 326 type : 'select', 327 'default' : '', 328 label : editor.lang.table.align, 329 items : 330 [ 331 [ editor.lang.table.alignNotSet , ''], 332 [ editor.lang.table.alignLeft , 'left'], 333 [ editor.lang.table.alignCenter , 'center'], 334 [ editor.lang.table.alignRight , 'right'] 335 ], 336 setup : function( selectedTable ) 337 { 338 this.setValue( selectedTable.getAttribute( 'align' ) || '' ); 339 }, 340 commit : function( data, selectedTable ) 341 { 342 if ( this.getValue() ) 343 selectedTable.setAttribute( 'align', this.getValue() ); 344 else 345 selectedTable.removeAttribute( 'align' ); 346 } 347 } 348 ] 349 }, 350 { 351 type : 'vbox', 352 padding : 0, 353 children : 354 [ 355 { 356 type : 'hbox', 357 widths : [ '5em' ], 358 children : 359 [ 360 { 361 type : 'text', 362 id : 'txtWidth', 363 style : 'width:5em', 364 label : editor.lang.table.width, 365 'default' : 200, 366 validate : CKEDITOR.dialog.validate['number']( editor.lang.table.invalidWidth ), 367 setup : function( selectedTable ) 368 { 369 var widthMatch = widthPattern.exec( selectedTable.$.style.width ); 370 if ( widthMatch ) 371 this.setValue( widthMatch[1] ); 372 }, 373 commit : commitValue 374 }, 375 { 376 id : 'cmbWidthType', 377 type : 'select', 378 label : ' ', 379 'default' : 'pixels', 380 items : 381 [ 382 [ editor.lang.table.widthPx , 'pixels'], 383 [ editor.lang.table.widthPc , 'percents'] 384 ], 385 setup : function( selectedTable ) 386 { 387 var widthMatch = widthPattern.exec( selectedTable.$.style.width ); 388 if ( widthMatch ) 389 this.setValue( widthMatch[2] == 'px' ? 'pixels' : 'percents' ); 390 }, 391 commit : commitValue 392 } 393 ] 394 }, 395 { 396 type : 'hbox', 397 widths : [ '5em' ], 398 children : 399 [ 400 { 401 type : 'text', 402 id : 'txtHeight', 403 style : 'width:5em', 404 label : editor.lang.table.height, 405 'default' : '', 406 validate : CKEDITOR.dialog.validate['number']( editor.lang.table.invalidHeight ), 407 setup : function( selectedTable ) 408 { 409 var heightMatch = heightPattern.exec( selectedTable.$.style.height ); 410 if ( heightMatch ) 411 this.setValue( heightMatch[1] ); 412 }, 413 commit : commitValue 414 }, 415 { 416 type : 'html', 417 html : '<br />' + editor.lang.table.widthPx 418 } 419 ] 420 }, 421 { 422 type : 'html', 423 html : ' ' 424 }, 425 { 426 type : 'text', 427 id : 'txtCellSpace', 428 style : 'width:3em', 429 label : editor.lang.table.cellSpace, 430 'default' : 1, 431 validate : CKEDITOR.dialog.validate['number']( editor.lang.table.invalidCellSpacing ), 432 setup : function( selectedTable ) 433 { 434 this.setValue( selectedTable.getAttribute( 'cellSpacing' ) || '' ); 435 }, 436 commit : function( data, selectedTable ) 437 { 438 if ( this.getValue() ) 439 selectedTable.setAttribute( 'cellSpacing', this.getValue() ); 440 else 441 selectedTable.removeAttribute( 'cellSpacing' ); 442 } 443 }, 444 { 445 type : 'text', 446 id : 'txtCellPad', 447 style : 'width:3em', 448 label : editor.lang.table.cellPad, 449 'default' : 1, 450 validate : CKEDITOR.dialog.validate['number']( editor.lang.table.invalidCellPadding ), 451 setup : function( selectedTable ) 452 { 453 this.setValue( selectedTable.getAttribute( 'cellPadding' ) || '' ); 454 }, 455 commit : function( data, selectedTable ) 456 { 457 if ( this.getValue() ) 458 selectedTable.setAttribute( 'cellPadding', this.getValue() ); 459 else 460 selectedTable.removeAttribute( 'cellPadding' ); 461 } 462 } 463 ] 464 } 465 ] 466 }, 467 { 468 type : 'html', 469 align : 'right', 470 html : '' 471 }, 472 { 473 type : 'vbox', 474 padding : 0, 475 children : 476 [ 477 { 478 type : 'text', 479 id : 'txtCaption', 480 label : editor.lang.table.caption, 481 setup : function( selectedTable ) 482 { 483 var nodeList = selectedTable.getElementsByTag( 'caption' ); 484 if ( nodeList.count() > 0 ) 485 { 486 var caption = nodeList.getItem( 0 ); 487 caption = ( caption.getChild( 0 ) && caption.getChild( 0 ).getText() ) || ''; 488 caption = CKEDITOR.tools.trim( caption ); 489 this.setValue( caption ); 490 } 491 }, 492 commit : function( data, table ) 493 { 494 var caption = this.getValue(), 495 captionElement = table.getElementsByTag( 'caption' ); 496 if ( caption ) 497 { 498 if ( captionElement.count() > 0 ) 499 { 500 captionElement = captionElement.getItem( 0 ); 501 captionElement.setHtml( '' ); 502 } 503 else 504 { 505 captionElement = new CKEDITOR.dom.element( 'caption', editor.document ); 506 if ( table.getChildCount() ) 507 captionElement.insertBefore( table.getFirst() ); 508 else 509 captionElement.appendTo( table ); 510 } 511 captionElement.append( new CKEDITOR.dom.text( caption, editor.document ) ); 512 } 513 else if ( captionElement.count() > 0 ) 514 { 515 for ( var i = captionElement.count() - 1 ; i >= 0 ; i-- ) 516 captionElement.getItem( i ).remove(); 517 } 518 } 519 }, 520 { 521 type : 'text', 522 id : 'txtSummary', 523 label : editor.lang.table.summary, 524 setup : function( selectedTable ) 525 { 526 this.setValue( selectedTable.getAttribute( 'summary' ) || '' ); 527 }, 528 commit : function( data, selectedTable ) 529 { 530 if ( this.getValue() ) 531 selectedTable.setAttribute( 'summary', this.getValue() ); 532 } 533 } 534 ] 535 } 536 ] 537 } 538 ] 539 }; 540 } 541 542 CKEDITOR.dialog.add( 'table', function( editor ) 543 { 544 return tableDialog( editor, 'table' ); 545 } ); 546 CKEDITOR.dialog.add( 'tableProperties', function( editor ) 547 { 548 return tableDialog( editor, 'tableProperties' ); 549 } ); 550 })(); 551