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