      1 ﻿/*
      2 Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
      3 For licensing, see LICENSE.html or http://ckeditor.com/license
      4 */
      5
      6 CKEDITOR.plugins.add( 'richcombo',
      7 {
      8 	requires : [ 'floatpanel', 'listblock', 'button' ],
      9
     10 	beforeInit : function( editor )
     11 	{
     12 		editor.ui.addHandler( CKEDITOR.UI_RICHCOMBO, CKEDITOR.ui.richCombo.handler );
     13 	}
     14 });
     15
     16 /**
     17  * Button UI element.
     18  * @constant
     19  * @example
     20  */
     21 CKEDITOR.UI_RICHCOMBO = 3;
     22
     23 CKEDITOR.ui.richCombo = CKEDITOR.tools.createClass(
     24 {
     25 	$ : function( definition )
     26 	{
     27 		// Copy all definition properties to this object.
     28 		CKEDITOR.tools.extend( this, definition,
     29 			// Set defaults.
     30 			{
     31 				title : definition.label,
     32 				modes : { wysiwyg : 1 }
     33 			});
     34
     35 		// We don't want the panel definition in this object.
     36 		var panelDefinition = this.panel || {};
     37 		delete this.panel;
     38
     39 		this.id = CKEDITOR.tools.getNextNumber();
     40
     41 		this.document = ( panelDefinition
     42 							&& panelDefinition.parent
     43 							&& panelDefinition.parent.getDocument() )
     44 						|| CKEDITOR.document;
     45
     46 		panelDefinition.className = ( panelDefinition.className || '' ) + ' cke_rcombopanel';
     47
     48 		this._ =
     49 		{
     50 			panelDefinition : panelDefinition,
     51 			items : {},
     52 			state : CKEDITOR.TRISTATE_OFF
     53 		};
     54 	},
     55
     56 	statics :
     57 	{
     58 		handler :
     59 		{
     60 			create : function( definition )
     61 			{
     62 				return new CKEDITOR.ui.richCombo( definition );
     63 			}
     64 		}
     65 	},
     66
     67 	proto :
     68 	{
     69 		renderHtml : function( editor )
     70 		{
     71 			var output = [];
     72 			this.render( editor, output );
     73 			return output.join( '' );
     74 		},
     75
     76 		/**
     77 		 * Renders the combo.
     78 		 * @param {CKEDITOR.editor} editor The editor instance which this button is
     79 		 *		to be used by.
     80 		 * @param {Array} output The output array to which append the HTML relative
     81 		 *		to this button.
     82 		 * @example
     83 		 */
     84 		render : function( editor, output )
     85 		{
     86 			var id = 'cke_' + this.id;
     87 			var clickFn = CKEDITOR.tools.addFunction( function( $element )
     88 				{
     89 					var _ = this._;
     90
     91 					if ( _.state == CKEDITOR.TRISTATE_DISABLED )
     92 						return;
     93
     94 					this.createPanel( editor );
     95
     96 					if ( _.on )
     97 					{
     98 						_.panel.hide();
     99 						return;
    100 					}
    101
    102 					if ( !_.committed )
    103 					{
    104 						_.list.commit();
    105 						_.committed = 1;
    106 					}
    107
    108 					var value = this.getValue();
    109 					if ( value )
    110 						_.list.mark( value );
    111 					else
    112 						_.list.unmarkAll();
    113
    114 					_.panel.showBlock( this.id, new CKEDITOR.dom.element( $element ), 4 );
    115 				},
    116 				this );
    117
    118 			var instance = {
    119 				id : id,
    120 				combo : this,
    121 				focus : function()
    122 				{
    123 					var element = CKEDITOR.document.getById( id ).getChild( 1 );
    124 					element.focus();
    125 				},
    126 				execute : clickFn
    127 			};
    128
    129 			editor.on( 'mode', function()
    130 				{
    131 					this.setState( this.modes[ editor.mode ] ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED );
    132 				},
    133 				this );
    134
    135 			var keyDownFn = CKEDITOR.tools.addFunction( function( ev, element )
    136 				{
    137 					ev = new CKEDITOR.dom.event( ev );
    138
    139 					var keystroke = ev.getKeystroke();
    140 					switch ( keystroke )
    141 					{
    142 						case 13 :	// ENTER
    143 						case 32 :	// SPACE
    144 						case 40 :	// ARROW-DOWN
    145 							// Show panel
    146 							CKEDITOR.tools.callFunction( clickFn, element );
    147 							break;
    148 						default :
    149 							// Delegate the default behavior to toolbar button key handling.
    150 							instance.onkey( instance,  keystroke );
    151 					}
    152
    153 					// Avoid subsequent focus grab on editor document.
    154 					ev.preventDefault();
    155 				});
    156
    157 			output.push(
    158 				'<span class="cke_rcombo">',
    159 				'<span id=', id );
    160
    161 			if ( this.className )
    162 				output.push( ' class="', this.className, ' cke_off"');
    163
    164 			output.push(
    165 				'>' +
    166 					'<span class=cke_label>', this.label, '</span>' +
    167 					'<a hidefocus=true title="', this.title, '" tabindex="-1" href="javascript:void(\'', this.label, '\')"' );
    168
    169 			// Some browsers don't cancel key events in the keydown but in the
    170 			// keypress.
    171 			// TODO: Check if really needed for Gecko+Mac.
    172 			if ( CKEDITOR.env.opera || ( CKEDITOR.env.gecko && CKEDITOR.env.mac ) )
    173 			{
    174 				output.push(
    175 					' onkeypress="return false;"' );
    176 			}
    177
    178 			// With Firefox, we need to force it to redraw, otherwise it
    179 			// will remain in the focus state.
    180 			if ( CKEDITOR.env.gecko )
    181 			{
    182 				output.push(
    183 					' onblur="this.style.cssText = this.style.cssText;"' );
    184 			}
    185
    186 			output.push(
    187 					' onkeydown="CKEDITOR.tools.callFunction( ', keyDownFn, ', event, this );"' +
    188 					' onclick="CKEDITOR.tools.callFunction(', clickFn, ', this); return false;">' +
    189 						'<span>' +
    190 							'<span class="cke_accessibility">' + ( this.voiceLabel ? this.voiceLabel + ' ' : '' ) + '</span>' +
    191 							'<span id="' + id + '_text" class="cke_text cke_inline_label">' + this.label + '</span>' +
    192 						'</span>' +
    193 						'<span class=cke_openbutton></span>' +
    194 					'</a>' +
    195 				'</span>' +
    196 				'</span>' );
    197
    198 			if ( this.onRender )
    199 				this.onRender();
    200
    201 			return instance;
    202 		},
    203
    204 		createPanel : function( editor )
    205 		{
    206 			if ( this._.panel )
    207 				return;
    208
    209 			var panelDefinition = this._.panelDefinition,
    210 				panelParentElement = panelDefinition.parent || CKEDITOR.document.getBody(),
    211 				panel = new CKEDITOR.ui.floatPanel( editor, panelParentElement, panelDefinition ),
    212 				list = panel.addListBlock( this.id, this.multiSelect ),
    213 				me = this;
    214
    215 			panel.onShow = function()
    216 				{
    217 					if ( me.className )
    218 						this.element.getFirst().addClass( me.className + '_panel' );
    219
    220 					me.setState( CKEDITOR.TRISTATE_ON );
    221
    222 					list.focus( !me.multiSelect && me.getValue() );
    223
    224 					me._.on = 1;
    225
    226 					if ( me.onOpen )
    227 						me.onOpen();
    228 				};
    229
    230 			panel.onHide = function()
    231 				{
    232 					if ( me.className )
    233 						this.element.getFirst().removeClass( me.className + '_panel' );
    234
    235 					me.setState( CKEDITOR.TRISTATE_OFF );
    236
    237 					me._.on = 0;
    238
    239 					if ( me.onClose )
    240 						me.onClose();
    241 				};
    242
    243 			panel.onEscape = function()
    244 				{
    245 					panel.hide();
    246 					me.document.getById( 'cke_' + me.id ).getFirst().getNext().focus();
    247 				};
    248
    249 			list.onClick = function( value, marked )
    250 				{
    251 					// Move the focus to the main windows, otherwise it will stay
    252 					// into the floating panel, even if invisible, and Safari and
    253 					// Opera will go a bit crazy.
    254 					me.document.getWindow().focus();
    255
    256 					if ( me.onClick )
    257 						me.onClick.call( me, value, marked );
    258
    259 					if ( marked )
    260 						me.setValue( value, me._.items[ value ] );
    261 					else
    262 						me.setValue( '' );
    263
    264 					panel.hide();
    265 				};
    266
    267 			this._.panel = panel;
    268 			this._.list = list;
    269
    270 			panel.getBlock( this.id ).onHide = function()
    271 				{
    272 					me._.on = 0;
    273 					me.setState( CKEDITOR.TRISTATE_OFF );
    274 				};
    275
    276 			if ( this.init )
    277 				this.init();
    278 		},
    279
    280 		setValue : function( value, text )
    281 		{
    282 			this._.value = value;
    283
    284 			var textElement = this.document.getById( 'cke_' + this.id + '_text' );
    285
    286 			if ( !value )
    287 			{
    288 				text = this.label;
    289 				textElement.addClass( 'cke_inline_label' );
    290 			}
    291 			else
    292 				textElement.removeClass( 'cke_inline_label' );
    293 			textElement.setHtml( typeof text != 'undefined' ? text : value );
    294 		},
    295
    296 		getValue : function()
    297 		{
    298 			return this._.value || '';
    299 		},
    300
    301 		unmarkAll : function()
    302 		{
    303 			this._.list.unmarkAll();
    304 		},
    305
    306 		mark : function( value )
    307 		{
    308 			this._.list.mark( value );
    309 		},
    310
    311 		hideItem : function( value )
    312 		{
    313 			this._.list.hideItem( value );
    314 		},
    315
    316 		hideGroup : function( groupTitle )
    317 		{
    318 			this._.list.hideGroup( groupTitle );
    319 		},
    320
    321 		showAll : function()
    322 		{
    323 			this._.list.showAll();
    324 		},
    325
    326 		add : function( value, html, text )
    327 		{
    328 			this._.items[ value ] = text || value;
    329 			this._.list.add( value, html, text );
    330 		},
    331
    332 		startGroup : function( title )
    333 		{
    334 			this._.list.startGroup( title );
    335 		},
    336
    337 		commit : function()
    338 		{
    339 			this._.list.commit();
    340 		},
    341
    342 		setState : function( state )
    343 		{
    344 			if ( this._.state == state )
    345 				return;
    346
    347 			this.document.getById( 'cke_' + this.id ).setState( state );
    348
    349 			this._.state = state;
    350 		}
    351 	}
    352 });
    353
    354 CKEDITOR.ui.prototype.addRichCombo = function( name, definition )
    355 {
    356 	this.add( name, CKEDITOR.UI_RICHCOMBO, definition );
    357 };
    358