      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