text: ﻿/**
   * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
   * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
   */
  
  CKEDITOR.dialog.add( 'uicolor', function( editor ) {
  
  	var $el = CKEDITOR.dom.element,
  		$doc = CKEDITOR.document,
  		lang = editor.lang.uicolor,
  		colorCellCls = 'cke_colordialog_colorcell',
  		focusedColorLightCls = 'cke_colordialog_focused_light',
  		focusedColorDarkCls = 'cke_colordialog_focused_dark',
  		selectedColorCls = 'cke_colordialog_selected',
  		highlightedColorId = generateId( 'hicolor' ),
  		highlightedColorTextId = generateId( 'hicolortext' ),
  		selectedColorId = generateId( 'selhicolor' ),
  		spacer = { type: 'html', html: '&nbsp;' },
  		dialog,
  		colorTable,
  		selectedColorCell,
  		focusedColorCell,
  		hoveredColorCell;
  
  	// ---Selected color handling.
  	// Updates selected color.
  	function updateSelected( evt ) {
  		var target = evt.data.getTarget(),
  			color;
  
  		if ( target.getName() == 'td' && ( color = target.getChild( 0 ).getHtml() ) ) {
  			deselectActiveColorCell();
  
  			selectColorCell( target );
  			setSelectedColorPreview( color );
  		}
  	}
  
  	// Clears selected color.
  	function clearSelected() {
  		deselectActiveColorCell();
  		setSelectedColorPreview( null );
  	}
  
  	// Selects given color cell in a color table.
  	function selectColorCell( cell ) {
  		if ( cell ) {
  			selectedColorCell = cell;
  			selectedColorCell.setAttribute( 'aria-selected', true );
  			selectedColorCell.addClass( selectedColorCls );
  		}
  	}
  
  	// Removes selection from the currently selected color cell.
  	function deselectActiveColorCell() {
  		if ( selectedColorCell ) {
  			selectedColorCell.removeClass( selectedColorCls );
  			selectedColorCell.removeAttribute( 'aria-selected' ); // Attribute aria-selected should also be removed when selection changes.
  			selectedColorCell = null;
  		}
  	}
  
  	// Sets given color as selected in a preview panel. If no color given the current one is cleared.
  	function setSelectedColorPreview( color ) {
  		dialog.getContentElement( 'picker', 'selectedColor' ).setValue( color );
  
  		if ( !color ) {
  			$doc.getById( selectedColorId ).removeStyle( 'background-color' );
  		}
  	}
  
  
  	// ---Highlighted color handling.
  	// Updates highlighted color.
  	function updateHighlight( event ) {
  		// Convert to event.
  		!event.name && ( event = new CKEDITOR.event( event ) );
  
  		var isFocus = !( /mouse/ ).test( event.name ),
  			target = event.data.getTarget(),
  			color;
  
  		if ( target.getName() == 'td' && ( color = target.getChild( 0 ).getHtml() ) ) {
  			removeHighlight( event );
  
  			isFocus ? focusedColorCell = target : hoveredColorCell = target;
  
  			// Apply CSS class to show focus.
  			if ( isFocus ) {
  				target.addClass( isLightColor( color ) ? focusedColorLightCls : focusedColorDarkCls );
  			}
  			setHighlightedColor( color );
  		}
  	}
  
  	// Clears highlighted color.
  	function clearHighlight() {
  		if ( focusedColorCell ) {
  			focusedColorCell.removeClass( focusedColorLightCls );
  			focusedColorCell.removeClass( focusedColorDarkCls );
  			focusedColorCell = null;
  			setHighlightedColor( null );
  		}
  	}
  
  	// Removes highlight from the given color cell.
  	function removeHighlight( event ) {
  		var isFocus = !( /mouse/ ).test( event.name ),
  			target = isFocus && focusedColorCell;
  
  		if ( target ) {
  			target.removeClass( focusedColorLightCls );
  			target.removeClass( focusedColorDarkCls );
  		}
  
  		if ( !( focusedColorCell || hoveredColorCell ) ) {
  			setHighlightedColor( false );
  		}
  	}
  
  	// Sets given color as highlighted in a preview panel. If no color given the current one is cleared.
  	function setHighlightedColor( color ) {
  		if ( color ) {
  			$doc.getById( highlightedColorId ).setStyle( 'background-color', color );
  			$doc.getById( highlightedColorTextId ).setHtml( color );
  
  		} else {
  			$doc.getById( highlightedColorId ).removeStyle( 'background-color' );
  			$doc.getById( highlightedColorTextId ).setHtml( '&nbsp;' );
  		}
  	}
  
  
  	// Basing black-white decision off of luma scheme using the Rec. 709 version.
  	function isLightColor( color ) {
  		color = color.replace( /^#/, '' );
  		for ( var i = 0, rgb = []; i <= 2; i++ )
  			rgb[ i ] = parseInt( color.substr( i * 2, 2 ), 16 );
  		var luma = ( 0.2126 * rgb[ 0 ] ) + ( 0.7152 * rgb[ 1 ] ) + ( 0.0722 * rgb[ 2 ] );
  		return luma >= 165;
  	}
  
  	// Manages selection/focus and key handling in a dialog.
  	function onKeyStrokes( evt ) {
  		var domEvt = evt.data;
  
  		var element = domEvt.getTarget();
  		var relative, nodeToMove;
  		var keystroke = domEvt.getKeystroke(),
  			rtl = editor.lang.dir == 'rtl';
  
  		switch ( keystroke ) {
  			// UP-ARROW
  			case 38:
  				// relative is TR
  				if ( ( relative = element.getParent().getPrevious() ) ) {
  					nodeToMove = relative.getChild( [ element.getIndex() ] );
  					nodeToMove.focus();
  				}
  				domEvt.preventDefault();
  				break;
  			// DOWN-ARROW
  			case 40:
  				// relative is TR
  				if ( ( relative = element.getParent().getNext() ) ) {
  					nodeToMove = relative.getChild( [ element.getIndex() ] );
  					if ( nodeToMove && nodeToMove.type == 1 )
  						nodeToMove.focus();
  
  				}
  				domEvt.preventDefault();
  				break;
  
  			// SPACE
  			// ENTER
  			case 32:
  			case 13:
  				updateSelected( evt );
  				domEvt.preventDefault();
  				break;
  
  			// RIGHT-ARROW
  			case rtl ? 37 : 39:
  				// relative is TD
  				if ( ( nodeToMove = element.getNext() ) ) {
  					if ( nodeToMove.type == 1 ) {
  						nodeToMove.focus();
  						domEvt.preventDefault( true );
  					}
  				}
  				// relative is TR
  				else if ( ( relative = element.getParent().getNext() ) ) {
  					nodeToMove = relative.getChild( [ 0 ] );
  					if ( nodeToMove && nodeToMove.type == 1 ) {
  						nodeToMove.focus();
  						domEvt.preventDefault( true );
  					}
  				}
  				break;
  
  			// LEFT-ARROW
  			case rtl ? 39 : 37:
  				// relative is TD
  				if ( ( nodeToMove = element.getPrevious() ) ) {
  					nodeToMove.focus();
  					domEvt.preventDefault( true );
  				}
  				// relative is TR
  				else if ( ( relative = element.getParent().getPrevious() ) ) {
  					nodeToMove = relative.getLast();
  					nodeToMove.focus();
  					domEvt.preventDefault( true );
  				}
  				break;
  			default:
  				// Do not stop not handled events.
  				return;
  		}
  	}
  
  	// Creates color palette table and binds event listeners to manage focus inside it.
  	function createColorTable() {
  		var table = CKEDITOR.dom.element.createFromHtml( '<table tabIndex="-1" class="cke_colordialog_table"' +
  			' aria-label="' + lang.options + '" role="grid" style="border-collapse:separate;" cellspacing="0">' +
  			'<caption class="cke_voice_label">' + lang.options + '</caption>' +
  			'<tbody role="presentation"></tbody></table>' );
  
  		table.on( 'mouseover', updateHighlight );
  		table.on( 'mouseout', removeHighlight );
  
  		// Create the base colors array.
  		var aColors = [ '00', '33', '66', '99', 'cc', 'ff' ];
  
  		// This function combines two ranges of three values from the color array into a row.
  		function appendColorRow( rangeA, rangeB ) {
  			for ( var i = rangeA; i < rangeA + 3; i++ ) {
  				var row = new $el( table.$.insertRow( -1 ) );
  				row.setAttribute( 'role', 'row' );
  
  				for ( var j = rangeB; j < rangeB + 3; j++ ) {
  					for ( var n = 0; n < 6; n++ ) {
  						appendColorCell( row.$, '#' + aColors[ j ] + aColors[ n ] + aColors[ i ] );
  					}
  				}
  			}
  		}
  
  		// This function create a single color cell in the color table.
  		function appendColorCell( targetRow, color ) {
  			var cell = new $el( targetRow.insertCell( -1 ) );
  			cell.setAttribute( 'class', 'ColorCell ' + colorCellCls );
  			cell.setAttribute( 'tabIndex', -1 );
  			cell.setAttribute( 'role', 'gridcell' );
  			cell.setAttribute( 'data-color', color );
  
  			cell.on( 'keydown', onKeyStrokes );
  			cell.on( 'click', updateSelected );
  			cell.on( 'focus', updateHighlight );
  			cell.on( 'blur', removeHighlight );
  
  			cell.setStyle( 'background-color', color );
  
  			var colorLabel = generateId( 'color_table_cell' );
  			cell.setAttribute( 'aria-labelledby', colorLabel );
  			cell.append( CKEDITOR.dom.element.createFromHtml( '<span id="' + colorLabel + '" class="cke_voice_label">' + color + '</span>', CKEDITOR.document ) );
  		}
  
  		appendColorRow( 0, 0 );
  		appendColorRow( 3, 0 );
  		appendColorRow( 0, 3 );
  		appendColorRow( 3, 3 );
  
  		// Create the last row.
  		var oRow = new $el( table.$.insertRow( -1 ) );
  		oRow.setAttribute( 'role', 'row' );
  
  		// Create the gray scale colors cells.
  		appendColorCell( oRow.$, '#000000' );
  		for ( var n = 0; n < 16; n++  ) {
  			var c = n.toString( 16 );
  			appendColorCell( oRow.$, '#' + c + c + c + c + c + c );
  		}
  		appendColorCell( oRow.$, '#ffffff' );
  
  		return table;
  	}
  
  	// Generates id from the given string and next number in a sequence.
  	function generateId( id ) {
  		return CKEDITOR.tools.getNextId() + '_' + id;
  	}
  
  	// Finds cell with the given color defined.
  	function findColorCell( color ) {
  		var colorCell = null;
  
  		if ( colorTable && color ) {
  			colorCell = colorTable.findOne( 'td[data-color="' + color + '"]' );
  		}
  		return colorCell;
  	}
  
  	// Sets the editor UI color and update states of color table and color dropdown depending on what triggered the update.
  	function onColorChanged( color, predefinedColor ) {
  		var newColor = color || predefinedColor;
  
  		editor.setUiColor( newColor );
  		dialog.getContentElement( 'picker', 'configBox' ).setValue( newColor );
  
  		if ( color && dialog.getContentElement( 'picker', 'predefined' ).getValue() !== newColor ) {
  			// Color was updated via color palette, update dropdown.
  			dialog.getContentElement( 'picker', 'predefined' ).setValue( newColor );
  
  		} else if ( predefinedColor ) {
  			// Color was updated via dropdown, updated palette.
  			var colorCell = findColorCell( newColor );
  			if ( colorCell ) {
  				selectColorCell( colorCell );
  			} else {
  				deselectActiveColorCell();
  			}
  
  			if ( dialog.getContentElement( 'picker', 'selectedColor' ).getValue() !== newColor ) {
  				setSelectedColorPreview( newColor );
  			}
  		}
  	}
  
  	// Returns normalized uiColor value.
  	function getUiColor( editor ) {
  		return editor.getUiColor() ? CKEDITOR.tools.parseCssText( 'color:' + editor.getUiColor(), true ).color : null;
  	}
  
  
  	// Initialize.
  	colorTable = createColorTable();
  
  	// Load CSS.
  	CKEDITOR.document.appendStyleSheet( CKEDITOR.getUrl( CKEDITOR.plugins.get( 'uicolor' ).path + 'dialogs/uicolor.css' ) );
  
  	return {
  		title: lang.title,
  		minWidth: 360,
  		minHeight: 220,
  		buttons: [ CKEDITOR.dialog.okButton ],
  		onLoad: function() {
  			// Update reference.
  			dialog = this;
  		},
  		onHide: function() {
  			clearSelected();
  			clearHighlight();
  		},
  		contents: [ {
  			id: 'picker',
  			label: lang.title,
  			accessKey: 'I',
  			elements: [ {
  				type: 'hbox',
  				padding: 0,
  				widths: [ '70%', '10%', '30%' ],
  				children: [ {
  					type: 'html',
  					html: '<div></div>',
  					onLoad: function() {
  						CKEDITOR.document.getById( this.domId ).append( colorTable );
  					},
  					focus: function() {
  						var color = getUiColor( editor ),
  							focusCell = color ? findColorCell( color ) : ( focusedColorCell || this.getElement().getElementsByTag( 'td' ).getItem( 0 ) );
  
  						if ( focusCell ) {
  							// Highlights color cell on open.
  							focusCell && focusCell.focus();
  
  							// If UI color is set also set the color cell as selected.
  							if ( color ) {
  								selectColorCell( focusCell );
  								setSelectedColorPreview( color );
  							}
  						}
  					}
  				},
  				spacer,
  				{
  					type: 'vbox',
  					padding: 0,
  					widths: [ '70%', '5%', '25%' ],
  					children: [ {
  						type: 'html',
  						html: '<span>' + lang.highlight + '</span>' +
  							'<div id="' + highlightedColorId + '" style="border: 1px solid; height: 74px; width: 74px;"></div>' +
  							'<div id="' + highlightedColorTextId + '">&nbsp;</div><span>' + lang.selected + '</span>' +
  							'<div id="' + selectedColorId + '" style="border: 1px solid; height: 20px; width: 74px;"></div>'
  					},
  					{
  						type: 'text',
  						label: lang.selected,
  						labelStyle: 'display:none',
  						id: 'selectedColor',
  						style: 'width: 76px;margin-top:4px',
  						onChange: function() {
  							// Try to update color preview with new value. If fails, then set it to none.
  							try {
  								var newColor = this.getValue();
  								if ( newColor ) {
  									$doc.getById( selectedColorId ).setStyle( 'background-color', newColor );
  									onColorChanged( newColor );
  								}
  							} catch ( e ) {
  								clearSelected();
  							}
  						}
  					} ]
  				} ]
  			}, {
  				type: 'vbox',
  				children: [ {
  					type: 'hbox',
  					padding: 0,
  					children: [ {
  						id: 'predefined',
  						type: 'select',
  						'default': '',
  						width: '100%',
  						label: lang.predefined,
  						items: [
  							[ '' ],
  							[ 'Light blue', '#9ab8f3' ],
  							[ 'Sand', '#d2b48c' ],
  							[ 'Metallic', '#949aaa' ],
  							[ 'Purple', '#c2a3c7' ],
  							[ 'Olive', '#a2c980' ],
  							[ 'Happy green', '#9bd446' ],
  							[ 'Jezebel Blue', '#14b8c4' ],
  							[ 'Burn', '#ff89a' ],
  							[ 'Easy red', '#ff6969' ],
  							[ 'Pisces 3', '#48b4f2' ],
  							[ 'Aquarius 5', '#487ed4' ],
  							[ 'Absinthe', '#a8cf76' ],
  							[ 'Scrambled Egg', '#c7a622' ],
  							[ 'Hello monday', '#8e8d80' ],
  							[ 'Lovely sunshine', '#f1e8b1' ],
  							[ 'Recycled air', '#b3c593' ],
  							[ 'Down', '#bcbca4' ],
  							[ 'Mark Twain', '#cfe91d' ],
  							[ 'Specks of dust', '#d1b596' ],
  							[ 'Lollipop', '#f6ce23' ]
  						],
  						onShow: function() {
  							this.setValue( getUiColor( editor ) );
  						},
  						onChange: function() {
  							var color = this.getValue();
  							if ( color ) {
  								onColorChanged( null, color );
  
  								// Set focus (after opening dialog if uiColor is based
  								// on predefined value we want to focus the dropdown).
  								this.focus();
  							}
  						}
  					} ]
  				},
  				{
  					id: 'configBox',
  					type: 'text',
  					label: lang.config,
  					onShow: function() {
  						this.getInputElement().setAttribute( 'readonly', true );
  						this.setValue( getUiColor( editor ) );
  					},
  					onChange: function() {
  						var color = this.getValue();
  
  						// Update the value only if color was passed. If user types something different do not update.
  						if ( color && CKEDITOR.tools.style.parse._findColor( color ).length ) {
  							this.setValue( 'config.uiColor = "' + color.toLowerCase() + '"', true );
  						}
  					}
  				} ]
  			} ]
  		} ]
  	};
  } );
  