1/**
2 * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
3 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4 */
5
6CKEDITOR.dialog.add( 'cellProperties', function( editor ) {
7	var langTable = editor.lang.table,
8		langCell = langTable.cell,
9		langCommon = editor.lang.common,
10		validate = CKEDITOR.dialog.validate,
11		widthPattern = /^(\d+(?:\.\d+)?)(px|%)$/,
12		spacer = { type: 'html', html: ' ' },
13		hiddenSpacer,
14		rtl = editor.lang.dir == 'rtl',
15		colorDialog = editor.plugins.colordialog;
16
17	// Returns a function, which runs regular "setup" for all selected cells to find out
18	// whether the initial value of the field would be the same for all cells. If so,
19	// the value is displayed just as if a regular "setup" was executed. Otherwise,
20	// i.e. when there are several cells of different value of the property, a field
21	// gets empty value.
22	//
23	// * @param {Function} setup Setup function which returns a value instead of setting it.
24	// * @returns {Function} A function to be used in dialog definition.
25	function setupCells( setup ) {
26		return function( cells ) {
27			var fieldValue = setup( cells[ 0 ] );
28
29			// If one of the cells would have a different value of the
30			// property, set the empty value for a field.
31			for ( var i = 1; i < cells.length; i++ ) {
32				if ( setup( cells[ i ] ) !== fieldValue ) {
33					fieldValue = null;
34					break;
35				}
36			}
37
38			// Setting meaningful or empty value only makes sense
39			// when setup returns some value. Otherwise, a *default* value
40			// is used for that field.
41			if ( typeof fieldValue != 'undefined' ) {
42				this.setValue( fieldValue );
43
44				// The only way to have an empty select value in Firefox is
45				// to set a negative selectedIndex.
46				if ( CKEDITOR.env.gecko && this.type == 'select' && !fieldValue )
47					this.getInputElement().$.selectedIndex = -1;
48			}
49		};
50	}
51
52	// Reads the unit of width property of the table cell.
53	//
54	// * @param {CKEDITOR.dom.element} cell An element representing table cell.
55	// * @returns {String} A unit of width: 'px', '%' or undefined if none.
56	function getCellWidthType( cell ) {
57		var match = widthPattern.exec(
58			cell.getStyle( 'width' ) || cell.getAttribute( 'width' ) );
59
60		if ( match )
61			return match[ 2 ];
62	}
63
64	return {
65		title: langCell.title,
66		minWidth: CKEDITOR.env.ie && CKEDITOR.env.quirks ? 450 : 410,
67		minHeight: CKEDITOR.env.ie && ( CKEDITOR.env.ie7Compat || CKEDITOR.env.quirks ) ? 230 : 220,
68		contents: [ {
69			id: 'info',
70			label: langCell.title,
71			accessKey: 'I',
72			elements: [ {
73				type: 'hbox',
74				widths: [ '40%', '5%', '40%' ],
75				children: [ {
76					type: 'vbox',
77					padding: 0,
78					children: [
79
80
81					hiddenSpacer = {
82						type: 'html',
83						id: 'hiddenSpacer',
84						html: '&nbsp;',
85						style: 'display: none'
86					},
87					spacer,
88					{
89						type: 'select',
90						id: 'hAlign',
91						label: langCell.hAlign,
92						'default': '',
93						items: [
94							[ langCommon.notSet, '' ],
95							[ langCommon.left, 'left' ],
96							[ langCommon.center, 'center' ],
97							[ langCommon.right, 'right' ]
98						],
99						setup: setupCells( function( element ) {
100							var alignAttr = element.getAttribute( 'align' ),
101							textAlignStyle = element.getStyle( 'text-align' );
102
103							return textAlignStyle || alignAttr || '';
104						} ),
105						commit: function( selectedCell ) {
106							var value = this.getValue();
107
108							if ( value ) {
109								selectedCell.setStyle( 'text-align', value );
110                                selectedCell.setAttribute('class', value + 'align');
111                            }
112							else {
113								selectedCell.removeStyle( 'text-align' );
114								selectedCell.removeAttribute( 'align' );
115							}
116						}
117					},
118 ]
119				},
120				spacer,
121				{
122					type: 'vbox',
123					padding: 0,
124					children: [ {
125						type: 'select',
126						id: 'cellType',
127						label: langCell.cellType,
128						'default': 'td',
129						items: [
130							[ langCell.data, 'td' ],
131							[ langCell.header, 'th' ]
132						],
133						setup: setupCells( function( selectedCell ) {
134							return selectedCell.getName();
135						} ),
136						commit: function( selectedCell ) {
137							selectedCell.renameNode( this.getValue() );
138						}
139					},
140					spacer,
141					{
142						type: 'text',
143						id: 'rowSpan',
144						label: langCell.rowSpan,
145						'default': '',
146						validate: validate.integer( langCell.invalidRowSpan ),
147						setup: setupCells( function( selectedCell ) {
148							var attrVal = parseInt( selectedCell.getAttribute( 'rowSpan' ), 10 );
149							if ( attrVal && attrVal != 1 )
150								return attrVal;
151						} ),
152						commit: function( selectedCell ) {
153							var value = parseInt( this.getValue(), 10 );
154							if ( value && value != 1 )
155								selectedCell.setAttribute( 'rowSpan', this.getValue() );
156							else
157								selectedCell.removeAttribute( 'rowSpan' );
158						}
159					},
160					{
161						type: 'text',
162						id: 'colSpan',
163						label: langCell.colSpan,
164						'default': '',
165						validate: validate.integer( langCell.invalidColSpan ),
166						setup: setupCells( function( element ) {
167							var attrVal = parseInt( element.getAttribute( 'colSpan' ), 10 );
168							if ( attrVal && attrVal != 1 )
169								return attrVal;
170						} ),
171						commit: function( selectedCell ) {
172							var value = parseInt( this.getValue(), 10 );
173							if ( value && value != 1 )
174								selectedCell.setAttribute( 'colSpan', this.getValue() );
175							else
176								selectedCell.removeAttribute( 'colSpan' );
177						}
178					},
179           ]
180				} ]
181			} ]
182		} ],
183		onShow: function() {
184			this.cells = CKEDITOR.plugins.tabletools.getSelectedCells( this._.editor.getSelection() );
185			this.setupContent( this.cells );
186		},
187		onOk: function() {
188			var selection = this._.editor.getSelection(),
189				bookmarks = selection.createBookmarks();
190
191			var cells = this.cells;
192			for ( var i = 0; i < cells.length; i++ )
193				this.commitContent( cells[ i ] );
194
195			this._.editor.forceNextSelectionCheck();
196			selection.selectBookmarks( bookmarks );
197			this._.editor.selectionChange();
198		},
199		onLoad: function() {
200			var saved = {};
201
202			// Prevent from changing cell properties when the field's value
203			// remains unaltered, i.e. when selected multiple cells and dialog loaded
204			// only the properties of the first cell (https://dev.ckeditor.com/ticket/11439).
205			this.foreach( function( field ) {
206				if ( !field.setup || !field.commit )
207					return;
208
209				// Save field's value every time after "setup" is called.
210				field.setup = CKEDITOR.tools.override( field.setup, function( orgSetup ) {
211					return function() {
212						orgSetup.apply( this, arguments );
213						saved[ field.id ] = field.getValue();
214					};
215				} );
216
217				// Compare saved value with actual value. Update cell only if value has changed.
218				field.commit = CKEDITOR.tools.override( field.commit, function( orgCommit ) {
219					return function() {
220						if ( saved[ field.id ] !== field.getValue() )
221							orgCommit.apply( this, arguments );
222					};
223				} );
224			} );
225		}
226	};
227} );
228