1 /*
  2 Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
  3 For licensing, see LICENSE.html or http://ckeditor.com/license
  4 */
  5 
  6 CKEDITOR.dialog.add( 'colordialog', function( editor )
  7 	{
  8 		// Define some shorthands.
  9 		var $el = CKEDITOR.dom.element,
 10 			$doc = CKEDITOR.document,
 11 			lang = editor.lang.colordialog;
 12 
 13 		// Reference the dialog.
 14 		var dialog;
 15 
 16 		var spacer =
 17 		{
 18 			type : 'html',
 19 			html : ' '
 20 		};
 21 
 22 		var selected;
 23 
 24 		function clearSelected()
 25 		{
 26 			$doc.getById( selHiColorId ).removeStyle( 'background-color' );
 27 			dialog.getContentElement( 'picker', 'selectedColor' ).setValue( '' );
 28 			selected && selected.removeAttribute( 'aria-selected' );
 29 			selected = null;
 30 		}
 31 
 32 		function updateSelected( evt )
 33 		{
 34 			var target = evt.data.getTarget(),
 35 				color;
 36 
 37 			if ( target.getName() == 'td' &&
 38 				 ( color = target.getChild( 0 ).getHtml() ) )
 39 			{
 40 				selected = target;
 41 				selected.setAttribute( 'aria-selected', true );
 42 				dialog.getContentElement( 'picker', 'selectedColor' ).setValue( color );
 43 			}
 44 		}
 45 
 46 		// Basing black-white decision off of luma scheme using the Rec. 709 version
 47 		function whiteOrBlack( color )
 48 		{
 49 			color = color.replace( /^#/, '' );
 50 			for ( var i = 0, rgb = []; i <= 2; i++ )
 51 				rgb[i] = parseInt( color.substr( i * 2, 2 ), 16 );
 52 			var luma = (0.2126 * rgb[0]) + (0.7152 * rgb[1]) + (0.0722 * rgb[2]);
 53 			return '#' + ( luma >= 165 ? '000' : 'fff' );
 54 		}
 55 
 56 		// Distinguish focused and hover states.
 57 		var focused, hovered;
 58 
 59 		// Apply highlight style.
 60 		function updateHighlight( event )
 61 		{
 62 			// Convert to event.
 63 			!event.name && ( event = new CKEDITOR.event( event ) );
 64 
 65 			var isFocus = !(/mouse/).test( event.name ),
 66 				target = event.data.getTarget(),
 67 				color;
 68 
 69 			if ( target.getName() == 'td' && ( color = target.getChild( 0 ).getHtml() ) )
 70 			{
 71 				removeHighlight( event );
 72 
 73 				isFocus ? focused = target : hovered = target;
 74 
 75 				// Apply outline style to show focus.
 76 				if ( isFocus )
 77 				{
 78 					target.setStyle( 'border-color', whiteOrBlack( color ) );
 79 					target.setStyle( 'border-style', 'dotted' );
 80 				}
 81 
 82 				$doc.getById( hicolorId ).setStyle( 'background-color', color );
 83 				$doc.getById( hicolorTextId ).setHtml( color );
 84 			}
 85 		}
 86 
 87 		function clearHighlight()
 88 		{
 89 			var color = focused.getChild( 0 ).getHtml();
 90 			focused.setStyle( 'border-color', color );
 91 			focused.setStyle( 'border-style', 'solid' );
 92 			$doc.getById( hicolorId ).removeStyle( 'background-color' );
 93 			$doc.getById( hicolorTextId ).setHtml( ' ' );
 94 			focused = null;
 95 		}
 96 
 97 		// Remove previously focused style.
 98 		function removeHighlight( event )
 99 		{
100 			var isFocus = !(/mouse/).test( event.name ),
101 				target = isFocus && focused;
102 
103 			if ( target )
104 			{
105 				var color = target.getChild( 0 ).getHtml();
106 				target.setStyle( 'border-color', color );
107 				target.setStyle( 'border-style', 'solid' );
108 			}
109 
110 			if ( ! ( focused || hovered ) )
111 			{
112 				$doc.getById( hicolorId ).removeStyle( 'background-color' );
113 				$doc.getById( hicolorTextId ).setHtml( ' ' );
114 			}
115 		}
116 
117 		function onKeyStrokes( evt )
118 		{
119 			var domEvt = evt.data;
120 
121 			var element = domEvt.getTarget();
122 			var relative, nodeToMove;
123 			var keystroke = domEvt.getKeystroke(),
124 				rtl = editor.lang.dir == 'rtl';
125 
126 			switch ( keystroke )
127 			{
128 				// UP-ARROW
129 				case 38 :
130 					// relative is TR
131 					if ( ( relative = element.getParent().getPrevious() ) )
132 					{
133 						nodeToMove = relative.getChild( [ element.getIndex() ] );
134 						nodeToMove.focus();
135 					}
136 					domEvt.preventDefault();
137 					break;
138 				// DOWN-ARROW
139 				case 40 :
140 					// relative is TR
141 					if ( ( relative = element.getParent().getNext() ) )
142 					{
143 						nodeToMove = relative.getChild( [ element.getIndex() ] );
144 						if ( nodeToMove && nodeToMove.type == 1 )
145 						{
146 							nodeToMove.focus();
147 						}
148 					}
149 					domEvt.preventDefault();
150 					break;
151 
152 				// SPACE
153 				// ENTER
154 				case 32 :
155 				case 13 :
156 					updateSelected( evt );
157 					domEvt.preventDefault();
158 					break;
159 
160 				// RIGHT-ARROW
161 				case rtl ? 37 : 39 :
162 					// relative is TD
163 					if ( ( nodeToMove = element.getNext() ) )
164 					{
165 						if ( nodeToMove.type == 1 )
166 						{
167 							nodeToMove.focus();
168 							domEvt.preventDefault( true );
169 						}
170 					}
171 					// relative is TR
172 					else if ( ( relative = element.getParent().getNext() ) )
173 					{
174 						nodeToMove = relative.getChild( [ 0 ] );
175 						if ( nodeToMove && nodeToMove.type == 1 )
176 						{
177 							nodeToMove.focus();
178 							domEvt.preventDefault( true );
179 						}
180 					}
181 					break;
182 
183 				// LEFT-ARROW
184 				case rtl ? 39 : 37 :
185 					// relative is TD
186 					if ( ( nodeToMove = element.getPrevious() ) )
187 					{
188 						nodeToMove.focus();
189 						domEvt.preventDefault( true );
190 					}
191 					// relative is TR
192 					else if ( ( relative = element.getParent().getPrevious() ) )
193 					{
194 						nodeToMove = relative.getLast();
195 						nodeToMove.focus();
196 						domEvt.preventDefault( true );
197 					}
198 					break;
199 				default :
200 					// Do not stop not handled events.
201 					return;
202 			}
203 		}
204 
205 		function createColorTable()
206 		{
207 			table = CKEDITOR.dom.element.createFromHtml
208 			(
209 				'<table tabIndex="-1" aria-label="' + lang.options + '"' +
210 				' role="grid" style="border-collapse:separate;" cellspacing="0">' +
211 				'<caption class="cke_voice_label">' + lang.options + '</caption>' +
212 				'<tbody role="presentation"></tbody></table>'
213 			);
214 
215 			table.on( 'mouseover', updateHighlight );
216 			table.on( 'mouseout', removeHighlight );
217 
218 			// Create the base colors array.
219 			var aColors = [ '00', '33', '66', '99', 'cc', 'ff' ];
220 
221 			// This function combines two ranges of three values from the color array into a row.
222 			function appendColorRow( rangeA, rangeB )
223 			{
224 				for ( var i = rangeA ; i < rangeA + 3 ; i++ )
225 				{
226 					var row = new $el( table.$.insertRow( -1 ) );
227 					row.setAttribute( 'role', 'row' );
228 
229 					for ( var j = rangeB ; j < rangeB + 3 ; j++ )
230 					{
231 						for ( var n = 0 ; n < 6 ; n++ )
232 						{
233 							appendColorCell( row.$, '#' + aColors[j] + aColors[n] + aColors[i] );
234 						}
235 					}
236 				}
237 			}
238 
239 			// This function create a single color cell in the color table.
240 			function appendColorCell( targetRow, color )
241 			{
242 				var cell = new $el( targetRow.insertCell( -1 ) );
243 				cell.setAttribute( 'class', 'ColorCell' );
244 				cell.setAttribute( 'tabIndex', -1 );
245 				cell.setAttribute( 'role', 'gridcell' );
246 
247 				cell.on( 'keydown', onKeyStrokes );
248 				cell.on( 'click', updateSelected );
249 				cell.on( 'focus', updateHighlight );
250 				cell.on( 'blur', removeHighlight );
251 
252 				cell.setStyle( 'background-color', color );
253 				cell.setStyle( 'border', '1px solid ' + color );
254 
255 				cell.setStyle( 'width', '14px' );
256 				cell.setStyle( 'height', '14px' );
257 
258 				var colorLabel = numbering( 'color_table_cell' );
259 				cell.setAttribute( 'aria-labelledby',colorLabel );
260 				cell.append( CKEDITOR.dom.element.createFromHtml( '<span id="' + colorLabel + '" class="cke_voice_label">' + color + '</span>', CKEDITOR.document ) );
261 			}
262 
263 			appendColorRow( 0, 0 );
264 			appendColorRow( 3, 0 );
265 			appendColorRow( 0, 3 );
266 			appendColorRow( 3, 3 );
267 
268 			// Create the last row.
269 			var oRow = new $el( table.$.insertRow( -1 ) ) ;
270 			oRow.setAttribute( 'role', 'row' );
271 
272 			// Create the gray scale colors cells.
273 			for ( var n = 0 ; n < 6 ; n++ )
274 			{
275 				appendColorCell( oRow.$, '#' + aColors[n] + aColors[n] + aColors[n] ) ;
276 			}
277 
278 			// Fill the row with black cells.
279 			for ( var i = 0 ; i < 12 ; i++ )
280 			{
281 				appendColorCell( oRow.$, '#000000' ) ;
282 			}
283 		}
284 
285 		var numbering = function( id )
286 			{
287 				return CKEDITOR.tools.getNextId() + '_' + id;
288 			},
289 			hicolorId = numbering( 'hicolor' ),
290 			hicolorTextId = numbering( 'hicolortext' ),
291 			selHiColorId = numbering( 'selhicolor' ),
292 			table;
293 
294 		createColorTable();
295 
296 		return {
297 			title : lang.title,
298 			minWidth : 360,
299 			minHeight : 220,
300 			onLoad : function()
301 			{
302 				// Update reference.
303 				dialog = this;
304 			},
305 			onHide : function()
306 			{
307 				clearSelected();
308 				clearHighlight();
309 			},
310 			contents : [
311 				{
312 					id : 'picker',
313 					label : lang.title,
314 					accessKey : 'I',
315 					elements :
316 					[
317 						{
318 							type : 'hbox',
319 							padding : 0,
320 							widths : [ '70%', '10%', '30%' ],
321 							children :
322 							[
323 								{
324 									type : 'html',
325 									html :	'<div></div>',
326 									onLoad : function()
327 									{
328 										CKEDITOR.document.getById( this.domId ).append( table );
329 									},
330 									focus : function()
331 									{
332 										// Restore the previously focused cell,
333 										// otherwise put the initial focus on the first table cell.
334 										( focused || this.getElement().getElementsByTag( 'td' ).getItem( 0 ) ).focus();
335 									}
336 								},
337 								spacer,
338 								{
339 									type : 'vbox',
340 									padding : 0,
341 									widths : [ '70%', '5%', '25%' ],
342 									children :
343 									[
344 										{
345 											type : 'html',
346 											html : '<span>' + lang.highlight +'</span>\
347 												<div id="' + hicolorId + '" style="border: 1px solid; height: 74px; width: 74px;"></div>\
348 												<div id="' + hicolorTextId + '"> </div><span>' + lang.selected + '</span>\
349 												<div id="' + selHiColorId + '" style="border: 1px solid; height: 20px; width: 74px;"></div>'
350 										},
351 										{
352 											type : 'text',
353 											label : lang.selected,
354 											labelStyle: 'display:none',
355 											id : 'selectedColor',
356 											style : 'width: 74px',
357 											onChange : function()
358 											{
359 												// Try to update color preview with new value. If fails, then set it no none.
360 												try
361 												{
362 													$doc.getById( selHiColorId ).setStyle( 'background-color', this.getValue() );
363 												}
364 												catch ( e )
365 												{
366 													clearSelected();
367 												}
368 											}
369 										},
370 										spacer,
371 										{
372 											type : 'button',
373 											id : 'clear',
374 											style : 'margin-top: 5px',
375 											label : lang.clear,
376 											onClick : clearSelected
377 										}
378 									]
379 								}
380 							]
381 						}
382 					]
383 				}
384 			]
385 		};
386 	}
387 	);
388