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