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 ).getFirst(), 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 					' onmousedown="CKEDITOR.tools.callFunction(', clickFn, ', this);">' +
189 						'<span id="', id, '_text" class=cke_text> </span>' +
190 						'<span class=cke_openbutton></span>' +
191 					'</a>' +
192 				'</span>' +
193 				'</span>' );
194
195 			if ( this.onRender )
196 				this.onRender();
197
198 			return instance;
199 		},
200
201 		createPanel : function( editor )
202 		{
203 			if ( this._.panel )
204 				return;
205
206 			var panelDefinition = this._.panelDefinition,
207 				panelParentElement = panelDefinition.parent || CKEDITOR.document.getBody(),
208 				panel = new CKEDITOR.ui.floatPanel( editor, panelParentElement, panelDefinition ),
209 				list = panel.addListBlock( this.id, this.multiSelect ),
210 				me = this;
211
212 			panel.onShow = function()
213 				{
214 					if ( me.className )
215 						this.element.getFirst().addClass( me.className + '_panel' );
216
217 					me.setState( CKEDITOR.TRISTATE_ON );
218
219 					list.focus( !me.multiSelect && me.getValue() );
220
221 					me._.on = 1;
222
223 					if ( me.onOpen )
224 						me.onOpen();
225 				};
226
227 			panel.onHide = function()
228 				{
229 					if ( me.className )
230 						this.element.getFirst().removeClass( me.className + '_panel' );
231
232 					me.setState( CKEDITOR.TRISTATE_OFF );
233
234 					me._.on = 0;
235
236 					if ( me.onClose )
237 						me.onClose();
238 				};
239
240 			panel.onEscape = function()
241 				{
242 					panel.hide();
243 					me.document.getById( 'cke_' + me.id ).getFirst().getNext().focus();
244 				};
245
246 			list.onClick = function( value, marked )
247 				{
248 					// Move the focus to the main windows, otherwise it will stay
249 					// into the floating panel, even if invisible, and Safari and
250 					// Opera will go a bit crazy.
251 					me.document.getWindow().focus();
252
253 					if ( me.onClick )
254 						me.onClick.call( me, value, marked );
255
256 					if ( marked )
257 						me.setValue( value, me._.items[ value ] );
258 					else
259 						me.setValue( '' );
260
261 					panel.hide();
262 				};
263
264 			this._.panel = panel;
265 			this._.list = list;
266
267 			if ( this.init )
268 				this.init();
269 		},
270
271 		setValue : function( value, text )
272 		{
273 			this._.value = value;
274
275 			var textElement = this.document.getById( 'cke_' + this.id + '_text' );
276 			textElement.setHtml( typeof text != 'undefined' ? text : value );
277 		},
278
279 		getValue : function()
280 		{
281 			return this._.value || '';
282 		},
283
284 		unmarkAll : function()
285 		{
286 			this._.list.unmarkAll();
287 		},
288
289 		mark : function( value )
290 		{
291 			this._.list.mark( value );
292 		},
293
294 		hideItem : function( value )
295 		{
296 			this._.list.hideItem( value );
297 		},
298
299 		hideGroup : function( groupTitle )
300 		{
301 			this._.list.hideGroup( groupTitle );
302 		},
303
304 		showAll : function()
305 		{
306 			this._.list.showAll();
307 		},
308
309 		add : function( value, html, text )
310 		{
311 			this._.items[ value ] = text || value;
312 			this._.list.add( value, html, text );
313 		},
314
315 		startGroup : function( title )
316 		{
317 			this._.list.startGroup( title );
318 		},
319
320 		commit : function()
321 		{
322 			this._.list.commit();
323 		},
324
325 		setState : function( state )
326 		{
327 			if ( this._.state == state )
328 				return;
329
330 			this.document.getById( 'cke_' + this.id ).setState( state );
331
332 			this._.state = state;
333 		}
334 	}
335 });
336
337 CKEDITOR.ui.prototype.addRichCombo = function( name, definition )
338 {
339 	this.add( name, CKEDITOR.UI_RICHCOMBO, definition );
340 };
341