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