]>
Commit | Line | Data |
---|---|---|
7adcb81e IB |
1 | /**\r |
2 | * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.\r | |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license\r | |
4 | */\r | |
5 | \r | |
6 | CKEDITOR.dialog.add( 'cellProperties', function( editor ) {\r | |
7 | var langTable = editor.lang.table,\r | |
8 | langCell = langTable.cell,\r | |
9 | langCommon = editor.lang.common,\r | |
10 | validate = CKEDITOR.dialog.validate,\r | |
11 | widthPattern = /^(\d+(?:\.\d+)?)(px|%)$/,\r | |
12 | spacer = { type: 'html', html: ' ' },\r | |
13 | rtl = editor.lang.dir == 'rtl',\r | |
14 | colorDialog = editor.plugins.colordialog;\r | |
15 | \r | |
16 | // Returns a function, which runs regular "setup" for all selected cells to find out\r | |
17 | // whether the initial value of the field would be the same for all cells. If so,\r | |
18 | // the value is displayed just as if a regular "setup" was executed. Otherwise,\r | |
19 | // i.e. when there are several cells of different value of the property, a field\r | |
20 | // gets empty value.\r | |
21 | //\r | |
22 | // * @param {Function} setup Setup function which returns a value instead of setting it.\r | |
23 | // * @returns {Function} A function to be used in dialog definition.\r | |
24 | function setupCells( setup ) {\r | |
25 | return function( cells ) {\r | |
26 | var fieldValue = setup( cells[ 0 ] );\r | |
27 | \r | |
28 | // If one of the cells would have a different value of the\r | |
29 | // property, set the empty value for a field.\r | |
30 | for ( var i = 1; i < cells.length; i++ ) {\r | |
31 | if ( setup( cells[ i ] ) !== fieldValue ) {\r | |
32 | fieldValue = null;\r | |
33 | break;\r | |
34 | }\r | |
35 | }\r | |
36 | \r | |
37 | // Setting meaningful or empty value only makes sense\r | |
38 | // when setup returns some value. Otherwise, a *default* value\r | |
39 | // is used for that field.\r | |
40 | if ( typeof fieldValue != 'undefined' ) {\r | |
41 | this.setValue( fieldValue );\r | |
42 | \r | |
43 | // The only way to have an empty select value in Firefox is\r | |
44 | // to set a negative selectedIndex.\r | |
45 | if ( CKEDITOR.env.gecko && this.type == 'select' && !fieldValue )\r | |
46 | this.getInputElement().$.selectedIndex = -1;\r | |
47 | }\r | |
48 | };\r | |
49 | }\r | |
50 | \r | |
51 | // Reads the unit of width property of the table cell.\r | |
52 | //\r | |
53 | // * @param {CKEDITOR.dom.element} cell An element representing table cell.\r | |
54 | // * @returns {String} A unit of width: 'px', '%' or undefined if none.\r | |
55 | function getCellWidthType( cell ) {\r | |
56 | var match = widthPattern.exec(\r | |
57 | cell.getStyle( 'width' ) || cell.getAttribute( 'width' ) );\r | |
58 | \r | |
59 | if ( match )\r | |
60 | return match[ 2 ];\r | |
61 | }\r | |
62 | \r | |
63 | return {\r | |
64 | title: langCell.title,\r | |
65 | minWidth: CKEDITOR.env.ie && CKEDITOR.env.quirks ? 450 : 410,\r | |
66 | minHeight: CKEDITOR.env.ie && ( CKEDITOR.env.ie7Compat || CKEDITOR.env.quirks ) ? 230 : 220,\r | |
67 | contents: [ {\r | |
68 | id: 'info',\r | |
69 | label: langCell.title,\r | |
70 | accessKey: 'I',\r | |
71 | elements: [ {\r | |
72 | type: 'hbox',\r | |
73 | widths: [ '40%', '5%', '40%' ],\r | |
74 | children: [ {\r | |
75 | type: 'vbox',\r | |
76 | padding: 0,\r | |
77 | children: [ {\r | |
78 | type: 'hbox',\r | |
79 | widths: [ '70%', '30%' ],\r | |
80 | children: [ {\r | |
81 | type: 'text',\r | |
82 | id: 'width',\r | |
83 | width: '100px',\r | |
84 | label: langCommon.width,\r | |
85 | validate: validate.number( langCell.invalidWidth ),\r | |
86 | \r | |
87 | // Extra labelling of width unit type.\r | |
88 | onLoad: function() {\r | |
89 | var widthType = this.getDialog().getContentElement( 'info', 'widthType' ),\r | |
90 | labelElement = widthType.getElement(),\r | |
91 | inputElement = this.getInputElement(),\r | |
92 | ariaLabelledByAttr = inputElement.getAttribute( 'aria-labelledby' );\r | |
93 | \r | |
94 | inputElement.setAttribute( 'aria-labelledby', [ ariaLabelledByAttr, labelElement.$.id ].join( ' ' ) );\r | |
95 | },\r | |
96 | \r | |
97 | setup: setupCells( function( element ) {\r | |
98 | var widthAttr = parseInt( element.getAttribute( 'width' ), 10 ),\r | |
99 | widthStyle = parseInt( element.getStyle( 'width' ), 10 );\r | |
100 | \r | |
101 | return !isNaN( widthStyle ) ? widthStyle :\r | |
102 | !isNaN( widthAttr ) ? widthAttr : '';\r | |
103 | } ),\r | |
104 | commit: function( element ) {\r | |
105 | var value = parseInt( this.getValue(), 10 ),\r | |
106 | \r | |
107 | // There might be no widthType value, i.e. when multiple cells are\r | |
108 | // selected but some of them have width expressed in pixels and some\r | |
109 | // of them in percent. Try to re-read the unit from the cell in such\r | |
110 | // case (#11439).\r | |
111 | unit = this.getDialog().getValueOf( 'info', 'widthType' ) || getCellWidthType( element );\r | |
112 | \r | |
113 | if ( !isNaN( value ) )\r | |
114 | element.setStyle( 'width', value + unit );\r | |
115 | else\r | |
116 | element.removeStyle( 'width' );\r | |
117 | \r | |
118 | element.removeAttribute( 'width' );\r | |
119 | },\r | |
120 | 'default': ''\r | |
121 | },\r | |
122 | {\r | |
123 | type: 'select',\r | |
124 | id: 'widthType',\r | |
125 | label: editor.lang.table.widthUnit,\r | |
126 | labelStyle: 'visibility:hidden',\r | |
127 | 'default': 'px',\r | |
128 | items: [\r | |
129 | [ langTable.widthPx, 'px' ],\r | |
130 | [ langTable.widthPc, '%' ]\r | |
131 | ],\r | |
132 | setup: setupCells( getCellWidthType )\r | |
133 | } ]\r | |
134 | },\r | |
135 | {\r | |
136 | type: 'hbox',\r | |
137 | widths: [ '70%', '30%' ],\r | |
138 | children: [ {\r | |
139 | type: 'text',\r | |
140 | id: 'height',\r | |
141 | label: langCommon.height,\r | |
142 | width: '100px',\r | |
143 | 'default': '',\r | |
144 | validate: validate.number( langCell.invalidHeight ),\r | |
145 | \r | |
146 | // Extra labelling of height unit type.\r | |
147 | onLoad: function() {\r | |
148 | var heightType = this.getDialog().getContentElement( 'info', 'htmlHeightType' ),\r | |
149 | labelElement = heightType.getElement(),\r | |
150 | inputElement = this.getInputElement(),\r | |
151 | ariaLabelledByAttr = inputElement.getAttribute( 'aria-labelledby' );\r | |
152 | \r | |
153 | inputElement.setAttribute( 'aria-labelledby', [ ariaLabelledByAttr, labelElement.$.id ].join( ' ' ) );\r | |
154 | },\r | |
155 | \r | |
156 | setup: setupCells( function( element ) {\r | |
157 | var heightAttr = parseInt( element.getAttribute( 'height' ), 10 ),\r | |
158 | heightStyle = parseInt( element.getStyle( 'height' ), 10 );\r | |
159 | \r | |
160 | return !isNaN( heightStyle ) ? heightStyle :\r | |
161 | !isNaN( heightAttr ) ? heightAttr : '';\r | |
162 | } ),\r | |
163 | commit: function( element ) {\r | |
164 | var value = parseInt( this.getValue(), 10 );\r | |
165 | \r | |
166 | if ( !isNaN( value ) )\r | |
167 | element.setStyle( 'height', CKEDITOR.tools.cssLength( value ) );\r | |
168 | else\r | |
169 | element.removeStyle( 'height' );\r | |
170 | \r | |
171 | element.removeAttribute( 'height' );\r | |
172 | }\r | |
173 | },\r | |
174 | {\r | |
175 | id: 'htmlHeightType',\r | |
176 | type: 'html',\r | |
177 | html: '<br />' + langTable.widthPx\r | |
178 | } ]\r | |
179 | },\r | |
180 | spacer,\r | |
181 | {\r | |
182 | type: 'select',\r | |
183 | id: 'wordWrap',\r | |
184 | label: langCell.wordWrap,\r | |
185 | 'default': 'yes',\r | |
186 | items: [\r | |
187 | [ langCell.yes, 'yes' ],\r | |
188 | [ langCell.no, 'no' ]\r | |
189 | ],\r | |
190 | setup: setupCells( function( element ) {\r | |
191 | var wordWrapAttr = element.getAttribute( 'noWrap' ),\r | |
192 | wordWrapStyle = element.getStyle( 'white-space' );\r | |
193 | \r | |
194 | if ( wordWrapStyle == 'nowrap' || wordWrapAttr )\r | |
195 | return 'no';\r | |
196 | } ),\r | |
197 | commit: function( element ) {\r | |
198 | if ( this.getValue() == 'no' )\r | |
199 | element.setStyle( 'white-space', 'nowrap' );\r | |
200 | else\r | |
201 | element.removeStyle( 'white-space' );\r | |
202 | \r | |
203 | element.removeAttribute( 'noWrap' );\r | |
204 | }\r | |
205 | },\r | |
206 | spacer,\r | |
207 | {\r | |
208 | type: 'select',\r | |
209 | id: 'hAlign',\r | |
210 | label: langCell.hAlign,\r | |
211 | 'default': '',\r | |
212 | items: [\r | |
213 | [ langCommon.notSet, '' ],\r | |
214 | [ langCommon.alignLeft, 'left' ],\r | |
215 | [ langCommon.alignCenter, 'center' ],\r | |
216 | [ langCommon.alignRight, 'right' ],\r | |
217 | [ langCommon.alignJustify, 'justify' ]\r | |
218 | ],\r | |
219 | setup: setupCells( function( element ) {\r | |
220 | var alignAttr = element.getAttribute( 'align' ),\r | |
221 | textAlignStyle = element.getStyle( 'text-align' );\r | |
222 | \r | |
223 | return textAlignStyle || alignAttr || '';\r | |
224 | } ),\r | |
225 | commit: function( selectedCell ) {\r | |
226 | var value = this.getValue();\r | |
227 | \r | |
228 | if ( value )\r | |
229 | selectedCell.setStyle( 'text-align', value );\r | |
230 | else\r | |
231 | selectedCell.removeStyle( 'text-align' );\r | |
232 | \r | |
233 | selectedCell.removeAttribute( 'align' );\r | |
234 | }\r | |
235 | },\r | |
236 | {\r | |
237 | type: 'select',\r | |
238 | id: 'vAlign',\r | |
239 | label: langCell.vAlign,\r | |
240 | 'default': '',\r | |
241 | items: [\r | |
242 | [ langCommon.notSet, '' ],\r | |
243 | [ langCommon.alignTop, 'top' ],\r | |
244 | [ langCommon.alignMiddle, 'middle' ],\r | |
245 | [ langCommon.alignBottom, 'bottom' ],\r | |
246 | [ langCell.alignBaseline, 'baseline' ]\r | |
247 | ],\r | |
248 | setup: setupCells( function( element ) {\r | |
249 | var vAlignAttr = element.getAttribute( 'vAlign' ),\r | |
250 | vAlignStyle = element.getStyle( 'vertical-align' );\r | |
251 | \r | |
252 | switch ( vAlignStyle ) {\r | |
253 | // Ignore all other unrelated style values..\r | |
254 | case 'top':\r | |
255 | case 'middle':\r | |
256 | case 'bottom':\r | |
257 | case 'baseline':\r | |
258 | break;\r | |
259 | default:\r | |
260 | vAlignStyle = '';\r | |
261 | }\r | |
262 | \r | |
263 | return vAlignStyle || vAlignAttr || '';\r | |
264 | } ),\r | |
265 | commit: function( element ) {\r | |
266 | var value = this.getValue();\r | |
267 | \r | |
268 | if ( value )\r | |
269 | element.setStyle( 'vertical-align', value );\r | |
270 | else\r | |
271 | element.removeStyle( 'vertical-align' );\r | |
272 | \r | |
273 | element.removeAttribute( 'vAlign' );\r | |
274 | }\r | |
275 | } ]\r | |
276 | },\r | |
277 | spacer,\r | |
278 | {\r | |
279 | type: 'vbox',\r | |
280 | padding: 0,\r | |
281 | children: [ {\r | |
282 | type: 'select',\r | |
283 | id: 'cellType',\r | |
284 | label: langCell.cellType,\r | |
285 | 'default': 'td',\r | |
286 | items: [\r | |
287 | [ langCell.data, 'td' ],\r | |
288 | [ langCell.header, 'th' ]\r | |
289 | ],\r | |
290 | setup: setupCells( function( selectedCell ) {\r | |
291 | return selectedCell.getName();\r | |
292 | } ),\r | |
293 | commit: function( selectedCell ) {\r | |
294 | selectedCell.renameNode( this.getValue() );\r | |
295 | }\r | |
296 | },\r | |
297 | spacer,\r | |
298 | {\r | |
299 | type: 'text',\r | |
300 | id: 'rowSpan',\r | |
301 | label: langCell.rowSpan,\r | |
302 | 'default': '',\r | |
303 | validate: validate.integer( langCell.invalidRowSpan ),\r | |
304 | setup: setupCells( function( selectedCell ) {\r | |
305 | var attrVal = parseInt( selectedCell.getAttribute( 'rowSpan' ), 10 );\r | |
306 | if ( attrVal && attrVal != 1 )\r | |
307 | return attrVal;\r | |
308 | } ),\r | |
309 | commit: function( selectedCell ) {\r | |
310 | var value = parseInt( this.getValue(), 10 );\r | |
311 | if ( value && value != 1 )\r | |
312 | selectedCell.setAttribute( 'rowSpan', this.getValue() );\r | |
313 | else\r | |
314 | selectedCell.removeAttribute( 'rowSpan' );\r | |
315 | }\r | |
316 | },\r | |
317 | {\r | |
318 | type: 'text',\r | |
319 | id: 'colSpan',\r | |
320 | label: langCell.colSpan,\r | |
321 | 'default': '',\r | |
322 | validate: validate.integer( langCell.invalidColSpan ),\r | |
323 | setup: setupCells( function( element ) {\r | |
324 | var attrVal = parseInt( element.getAttribute( 'colSpan' ), 10 );\r | |
325 | if ( attrVal && attrVal != 1 )\r | |
326 | return attrVal;\r | |
327 | } ),\r | |
328 | commit: function( selectedCell ) {\r | |
329 | var value = parseInt( this.getValue(), 10 );\r | |
330 | if ( value && value != 1 )\r | |
331 | selectedCell.setAttribute( 'colSpan', this.getValue() );\r | |
332 | else\r | |
333 | selectedCell.removeAttribute( 'colSpan' );\r | |
334 | }\r | |
335 | },\r | |
336 | spacer,\r | |
337 | {\r | |
338 | type: 'hbox',\r | |
339 | padding: 0,\r | |
340 | widths: [ '60%', '40%' ],\r | |
341 | children: [ {\r | |
342 | type: 'text',\r | |
343 | id: 'bgColor',\r | |
344 | label: langCell.bgColor,\r | |
345 | 'default': '',\r | |
346 | setup: setupCells( function( element ) {\r | |
347 | var bgColorAttr = element.getAttribute( 'bgColor' ),\r | |
348 | bgColorStyle = element.getStyle( 'background-color' );\r | |
349 | \r | |
350 | return bgColorStyle || bgColorAttr;\r | |
351 | } ),\r | |
352 | commit: function( selectedCell ) {\r | |
353 | var value = this.getValue();\r | |
354 | \r | |
355 | if ( value )\r | |
356 | selectedCell.setStyle( 'background-color', this.getValue() );\r | |
357 | else\r | |
358 | selectedCell.removeStyle( 'background-color' );\r | |
359 | \r | |
360 | selectedCell.removeAttribute( 'bgColor' );\r | |
361 | }\r | |
362 | },\r | |
363 | colorDialog ? {\r | |
364 | type: 'button',\r | |
365 | id: 'bgColorChoose',\r | |
366 | 'class': 'colorChooser', // jshint ignore:line\r | |
367 | label: langCell.chooseColor,\r | |
368 | onLoad: function() {\r | |
369 | // Stick the element to the bottom (#5587)\r | |
370 | this.getElement().getParent().setStyle( 'vertical-align', 'bottom' );\r | |
371 | },\r | |
372 | onClick: function() {\r | |
373 | editor.getColorFromDialog( function( color ) {\r | |
374 | if ( color )\r | |
375 | this.getDialog().getContentElement( 'info', 'bgColor' ).setValue( color );\r | |
376 | this.focus();\r | |
377 | }, this );\r | |
378 | }\r | |
379 | } : spacer ]\r | |
380 | },\r | |
381 | spacer,\r | |
382 | {\r | |
383 | type: 'hbox',\r | |
384 | padding: 0,\r | |
385 | widths: [ '60%', '40%' ],\r | |
386 | children: [ {\r | |
387 | type: 'text',\r | |
388 | id: 'borderColor',\r | |
389 | label: langCell.borderColor,\r | |
390 | 'default': '',\r | |
391 | setup: setupCells( function( element ) {\r | |
392 | var borderColorAttr = element.getAttribute( 'borderColor' ),\r | |
393 | borderColorStyle = element.getStyle( 'border-color' );\r | |
394 | \r | |
395 | return borderColorStyle || borderColorAttr;\r | |
396 | } ),\r | |
397 | commit: function( selectedCell ) {\r | |
398 | var value = this.getValue();\r | |
399 | if ( value )\r | |
400 | selectedCell.setStyle( 'border-color', this.getValue() );\r | |
401 | else\r | |
402 | selectedCell.removeStyle( 'border-color' );\r | |
403 | \r | |
404 | selectedCell.removeAttribute( 'borderColor' );\r | |
405 | }\r | |
406 | },\r | |
407 | \r | |
408 | colorDialog ? {\r | |
409 | type: 'button',\r | |
410 | id: 'borderColorChoose',\r | |
411 | 'class': 'colorChooser', // jshint ignore:line\r | |
412 | label: langCell.chooseColor,\r | |
413 | style: ( rtl ? 'margin-right' : 'margin-left' ) + ': 10px',\r | |
414 | onLoad: function() {\r | |
415 | // Stick the element to the bottom (#5587)\r | |
416 | this.getElement().getParent().setStyle( 'vertical-align', 'bottom' );\r | |
417 | },\r | |
418 | onClick: function() {\r | |
419 | editor.getColorFromDialog( function( color ) {\r | |
420 | if ( color )\r | |
421 | this.getDialog().getContentElement( 'info', 'borderColor' ).setValue( color );\r | |
422 | this.focus();\r | |
423 | }, this );\r | |
424 | }\r | |
425 | } : spacer ]\r | |
426 | } ]\r | |
427 | } ]\r | |
428 | } ]\r | |
429 | } ],\r | |
430 | onShow: function() {\r | |
431 | this.cells = CKEDITOR.plugins.tabletools.getSelectedCells( this._.editor.getSelection() );\r | |
432 | this.setupContent( this.cells );\r | |
433 | },\r | |
434 | onOk: function() {\r | |
435 | var selection = this._.editor.getSelection(),\r | |
436 | bookmarks = selection.createBookmarks();\r | |
437 | \r | |
438 | var cells = this.cells;\r | |
439 | for ( var i = 0; i < cells.length; i++ )\r | |
440 | this.commitContent( cells[ i ] );\r | |
441 | \r | |
442 | this._.editor.forceNextSelectionCheck();\r | |
443 | selection.selectBookmarks( bookmarks );\r | |
444 | this._.editor.selectionChange();\r | |
445 | },\r | |
446 | onLoad: function() {\r | |
447 | var saved = {};\r | |
448 | \r | |
449 | // Prevent from changing cell properties when the field's value\r | |
450 | // remains unaltered, i.e. when selected multiple cells and dialog loaded\r | |
451 | // only the properties of the first cell (#11439).\r | |
452 | this.foreach( function( field ) {\r | |
453 | if ( !field.setup || !field.commit )\r | |
454 | return;\r | |
455 | \r | |
456 | // Save field's value every time after "setup" is called.\r | |
457 | field.setup = CKEDITOR.tools.override( field.setup, function( orgSetup ) {\r | |
458 | return function() {\r | |
459 | orgSetup.apply( this, arguments );\r | |
460 | saved[ field.id ] = field.getValue();\r | |
461 | };\r | |
462 | } );\r | |
463 | \r | |
464 | // Compare saved value with actual value. Update cell only if value has changed.\r | |
465 | field.commit = CKEDITOR.tools.override( field.commit, function( orgCommit ) {\r | |
466 | return function() {\r | |
467 | if ( saved[ field.id ] !== field.getValue() )\r | |
468 | orgCommit.apply( this, arguments );\r | |
469 | };\r | |
470 | } );\r | |
471 | } );\r | |
472 | }\r | |
473 | };\r | |
474 | } );\r |