JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
891b450142078fadabc1e75b049daaaa1b6000a4
[ckeditor.git] / _source / plugins / toolbar / 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 /**\r
7  * @fileOverview The "toolbar" plugin. Renders the default toolbar interface in\r
8  * the editor.\r
9  */\r
10 \r
11 (function()\r
12 {\r
13         var toolbox = function()\r
14         {\r
15                 this.toolbars = [];\r
16                 this.focusCommandExecuted = false;\r
17         };\r
18 \r
19         toolbox.prototype.focus = function()\r
20         {\r
21                 for ( var t = 0, toolbar ; toolbar = this.toolbars[ t++ ] ; )\r
22                 {\r
23                         for ( var i = 0, item ; item = toolbar.items[ i++ ] ; )\r
24                         {\r
25                                 if ( item.focus )\r
26                                 {\r
27                                         item.focus();\r
28                                         return;\r
29                                 }\r
30                         }\r
31                 }\r
32         };\r
33 \r
34         var commands =\r
35         {\r
36                 toolbarFocus :\r
37                 {\r
38                         modes : { wysiwyg : 1, source : 1 },\r
39 \r
40                         exec : function( editor )\r
41                         {\r
42                                 if ( editor.toolbox )\r
43                                 {\r
44                                         editor.toolbox.focusCommandExecuted = true;\r
45 \r
46                                         // Make the first button focus accessible for IE. (#3417)\r
47                                         // Adobe AIR instead need while of delay.\r
48                                         if ( CKEDITOR.env.ie || CKEDITOR.env.air )\r
49                                                 setTimeout( function(){ editor.toolbox.focus(); }, 100 );\r
50                                         else\r
51                                                 editor.toolbox.focus();\r
52                                 }\r
53                         }\r
54                 }\r
55         };\r
56 \r
57         CKEDITOR.plugins.add( 'toolbar',\r
58         {\r
59                 init : function( editor )\r
60                 {\r
61                         var itemKeystroke = function( item, keystroke )\r
62                         {\r
63                                 var next, nextToolGroup, groupItemsCount;\r
64                                 var rtl = editor.lang.dir == 'rtl';\r
65 \r
66                                 switch ( keystroke )\r
67                                 {\r
68                                         case rtl ? 37 : 39 :                                    // RIGHT-ARROW\r
69                                         case 9 :                                        // TAB\r
70                                                 do\r
71                                                 {\r
72                                                         // Look for the next item in the toolbar.\r
73                                                         next = item.next;\r
74 \r
75                                                         if ( !next )\r
76                                                         {\r
77                                                                 nextToolGroup = item.toolbar.next;\r
78                                                                 groupItemsCount = nextToolGroup && nextToolGroup.items.length;\r
79 \r
80                                                                 // Bypass the empty toolgroups.\r
81                                                                 while ( groupItemsCount === 0 )\r
82                                                                 {\r
83                                                                         nextToolGroup = nextToolGroup.next;\r
84                                                                         groupItemsCount = nextToolGroup && nextToolGroup.items.length;\r
85                                                                 }\r
86 \r
87                                                                 if ( nextToolGroup )\r
88                                                                         next = nextToolGroup.items[ 0 ];\r
89                                                         }\r
90 \r
91                                                         item = next;\r
92                                                 }\r
93                                                 while ( item && !item.focus )\r
94 \r
95                                                 // If available, just focus it, otherwise focus the\r
96                                                 // first one.\r
97                                                 if ( item )\r
98                                                         item.focus();\r
99                                                 else\r
100                                                         editor.toolbox.focus();\r
101 \r
102                                                 return false;\r
103 \r
104                                         case rtl ? 39 : 37 :                                    // LEFT-ARROW\r
105                                         case CKEDITOR.SHIFT + 9 :       // SHIFT + TAB\r
106                                                 do\r
107                                                 {\r
108                                                         // Look for the previous item in the toolbar.\r
109                                                         next = item.previous;\r
110 \r
111                                                         if ( !next )\r
112                                                         {\r
113                                                                 nextToolGroup = item.toolbar.previous;\r
114                                                                 groupItemsCount = nextToolGroup && nextToolGroup.items.length;\r
115 \r
116                                                                 // Bypass the empty toolgroups.\r
117                                                                 while ( groupItemsCount === 0 )\r
118                                                                 {\r
119                                                                         nextToolGroup = nextToolGroup.previous;\r
120                                                                         groupItemsCount = nextToolGroup && nextToolGroup.items.length;\r
121                                                                 }\r
122 \r
123                                                                 if ( nextToolGroup )\r
124                                                                         next = nextToolGroup.items[ groupItemsCount - 1 ];\r
125                                                         }\r
126 \r
127                                                         item = next;\r
128                                                 }\r
129                                                 while ( item && !item.focus )\r
130 \r
131                                                 // If available, just focus it, otherwise focus the\r
132                                                 // last one.\r
133                                                 if ( item )\r
134                                                         item.focus();\r
135                                                 else\r
136                                                 {\r
137                                                         var lastToolbarItems = editor.toolbox.toolbars[ editor.toolbox.toolbars.length - 1 ].items;\r
138                                                         lastToolbarItems[ lastToolbarItems.length - 1 ].focus();\r
139                                                 }\r
140 \r
141                                                 return false;\r
142 \r
143                                         case 27 :                                       // ESC\r
144                                                 editor.focus();\r
145                                                 return false;\r
146 \r
147                                         case 13 :                                       // ENTER\r
148                                         case 32 :                                       // SPACE\r
149                                                 item.execute();\r
150                                                 return false;\r
151                                 }\r
152                                 return true;\r
153                         };\r
154 \r
155                         editor.on( 'themeSpace', function( event )\r
156                                 {\r
157                                         if ( event.data.space == editor.config.toolbarLocation )\r
158                                         {\r
159                                                 editor.toolbox = new toolbox();\r
160 \r
161                                                 var labelId = CKEDITOR.tools.getNextId();\r
162 \r
163                                                 var output = [ '<div class="cke_toolbox" role="toolbar" aria-labelledby="', labelId, '" onmousedown="return false;"' ],\r
164                                                         expanded =  editor.config.toolbarStartupExpanded !== false,\r
165                                                         groupStarted;\r
166 \r
167                                                 output.push( expanded ? '>' : ' style="display:none">' );\r
168 \r
169                                                 // Sends the ARIA label.\r
170                                                 output.push( '<span id="', labelId, '" class="cke_voice_label">', editor.lang.toolbar, '</span>' );\r
171 \r
172                                                 var toolbars = editor.toolbox.toolbars,\r
173                                                         toolbar =\r
174                                                                         ( editor.config.toolbar instanceof Array ) ?\r
175                                                                                 editor.config.toolbar\r
176                                                                         :\r
177                                                                                 editor.config[ 'toolbar_' + editor.config.toolbar ];\r
178 \r
179                                                 for ( var r = 0 ; r < toolbar.length ; r++ )\r
180                                                 {\r
181                                                         var row = toolbar[ r ];\r
182 \r
183                                                         // It's better to check if the row object is really\r
184                                                         // available because it's a common mistake to leave\r
185                                                         // an extra comma in the toolbar definition\r
186                                                         // settings, which leads on the editor not loading\r
187                                                         // at all in IE. (#3983)\r
188                                                         if ( !row )\r
189                                                                 continue;\r
190 \r
191                                                         var toolbarId = CKEDITOR.tools.getNextId(),\r
192                                                                 toolbarObj = { id : toolbarId, items : [] };\r
193 \r
194                                                         if ( groupStarted )\r
195                                                         {\r
196                                                                 output.push( '</div>' );\r
197                                                                 groupStarted = 0;\r
198                                                         }\r
199 \r
200                                                         if ( row === '/' )\r
201                                                         {\r
202                                                                 output.push( '<div class="cke_break"></div>' );\r
203                                                                 continue;\r
204                                                         }\r
205 \r
206                                                         output.push( '<span id="', toolbarId, '" class="cke_toolbar" role="presentation"><span class="cke_toolbar_start"></span>' );\r
207 \r
208                                                         // Add the toolbar to the "editor.toolbox.toolbars"\r
209                                                         // array.\r
210                                                         var index = toolbars.push( toolbarObj ) - 1;\r
211 \r
212                                                         // Create the next/previous reference.\r
213                                                         if ( index > 0 )\r
214                                                         {\r
215                                                                 toolbarObj.previous = toolbars[ index - 1 ];\r
216                                                                 toolbarObj.previous.next = toolbarObj;\r
217                                                         }\r
218 \r
219                                                         // Create all items defined for this toolbar.\r
220                                                         for ( var i = 0 ; i < row.length ; i++ )\r
221                                                         {\r
222                                                                 var item,\r
223                                                                         itemName = row[ i ];\r
224 \r
225                                                                 if ( itemName == '-' )\r
226                                                                         item = CKEDITOR.ui.separator;\r
227                                                                 else\r
228                                                                         item = editor.ui.create( itemName );\r
229 \r
230                                                                 if ( item )\r
231                                                                 {\r
232                                                                         if ( item.canGroup )\r
233                                                                         {\r
234                                                                                 if ( !groupStarted )\r
235                                                                                 {\r
236                                                                                         output.push( '<span class="cke_toolgroup" role="presentation">' );\r
237                                                                                         groupStarted = 1;\r
238                                                                                 }\r
239                                                                         }\r
240                                                                         else if ( groupStarted )\r
241                                                                         {\r
242                                                                                 output.push( '</span>' );\r
243                                                                                 groupStarted = 0;\r
244                                                                         }\r
245 \r
246                                                                         var itemObj = item.render( editor, output );\r
247                                                                         index = toolbarObj.items.push( itemObj ) - 1;\r
248 \r
249                                                                         if ( index > 0 )\r
250                                                                         {\r
251                                                                                 itemObj.previous = toolbarObj.items[ index - 1 ];\r
252                                                                                 itemObj.previous.next = itemObj;\r
253                                                                         }\r
254 \r
255                                                                         itemObj.toolbar = toolbarObj;\r
256                                                                         itemObj.onkey = itemKeystroke;\r
257 \r
258                                                                         /*\r
259                                                                          * Fix for #3052:\r
260                                                                          * Prevent JAWS from focusing the toolbar after document load.\r
261                                                                          */\r
262                                                                         itemObj.onfocus = function()\r
263                                                                         {\r
264                                                                                 if ( !editor.toolbox.focusCommandExecuted )\r
265                                                                                         editor.focus();\r
266                                                                         };\r
267                                                                 }\r
268                                                         }\r
269 \r
270                                                         if ( groupStarted )\r
271                                                         {\r
272                                                                 output.push( '</span>' );\r
273                                                                 groupStarted = 0;\r
274                                                         }\r
275 \r
276                                                         output.push( '<span class="cke_toolbar_end"></span></span>' );\r
277                                                 }\r
278 \r
279                                                 output.push( '</div>' );\r
280 \r
281                                                 if ( editor.config.toolbarCanCollapse )\r
282                                                 {\r
283                                                         var collapserFn = CKEDITOR.tools.addFunction(\r
284                                                                 function()\r
285                                                                 {\r
286                                                                         editor.execCommand( 'toolbarCollapse' );\r
287                                                                 });\r
288 \r
289                                                         editor.on( 'destroy', function () {\r
290                                                                         CKEDITOR.tools.removeFunction( collapserFn );\r
291                                                                 });\r
292 \r
293                                                         var collapserId = CKEDITOR.tools.getNextId();\r
294 \r
295                                                         editor.addCommand( 'toolbarCollapse',\r
296                                                                 {\r
297                                                                         exec : function( editor )\r
298                                                                         {\r
299                                                                                 var collapser = CKEDITOR.document.getById( collapserId ),\r
300                                                                                         toolbox = collapser.getPrevious(),\r
301                                                                                         contents = editor.getThemeSpace( 'contents' ),\r
302                                                                                         toolboxContainer = toolbox.getParent(),\r
303                                                                                         contentHeight = parseInt( contents.$.style.height, 10 ),\r
304                                                                                         previousHeight = toolboxContainer.$.offsetHeight,\r
305                                                                                         collapsed = !toolbox.isVisible();\r
306 \r
307                                                                                 if ( !collapsed )\r
308                                                                                 {\r
309                                                                                         toolbox.hide();\r
310                                                                                         collapser.addClass( 'cke_toolbox_collapser_min' );\r
311                                                                                         collapser.setAttribute( 'title', editor.lang.toolbarExpand );\r
312                                                                                 }\r
313                                                                                 else\r
314                                                                                 {\r
315                                                                                         toolbox.show();\r
316                                                                                         collapser.removeClass( 'cke_toolbox_collapser_min' );\r
317                                                                                         collapser.setAttribute( 'title', editor.lang.toolbarCollapse );\r
318                                                                                 }\r
319 \r
320                                                                                 // Update collapser symbol.\r
321                                                                                 collapser.getFirst().setText( collapsed ?\r
322                                                                                         '\u25B2' :              // BLACK UP-POINTING TRIANGLE\r
323                                                                                         '\u25C0' );             // BLACK LEFT-POINTING TRIANGLE\r
324 \r
325                                                                                 var dy = toolboxContainer.$.offsetHeight - previousHeight;\r
326                                                                                 contents.setStyle( 'height', ( contentHeight - dy ) + 'px' );\r
327 \r
328                                                                                 editor.fire( 'resize' );\r
329                                                                         },\r
330 \r
331                                                                         modes : { wysiwyg : 1, source : 1 }\r
332                                                                 } );\r
333 \r
334                                                         output.push( '<a title="' + ( expanded ? editor.lang.toolbarCollapse : editor.lang.toolbarExpand )\r
335                                                                                                           + '" id="' + collapserId + '" tabIndex="-1" class="cke_toolbox_collapser' );\r
336 \r
337                                                         if ( !expanded )\r
338                                                                 output.push( ' cke_toolbox_collapser_min' );\r
339 \r
340                                                         output.push( '" onclick="CKEDITOR.tools.callFunction(' + collapserFn + ')">',\r
341                                                                                 '<span>&#9650;</span>',         // BLACK UP-POINTING TRIANGLE\r
342                                                                                 '</a>' );\r
343                                                 }\r
344 \r
345                                                 event.data.html += output.join( '' );\r
346                                         }\r
347                                 });\r
348 \r
349                         editor.on( 'destroy', function()\r
350                         {\r
351                                 var toolbars, index = 0, i,\r
352                                                 items, instance;\r
353                                 toolbars = this.toolbox.toolbars;\r
354                                 for ( ; index < toolbars.length; index++ )\r
355                                 {\r
356                                         items = toolbars[ index ].items;\r
357                                         for ( i = 0; i < items.length; i++ )\r
358                                         {\r
359                                                 instance = items[ i ];\r
360                                                 if ( instance.clickFn ) CKEDITOR.tools.removeFunction( instance.clickFn );\r
361                                                 if ( instance.keyDownFn ) CKEDITOR.tools.removeFunction( instance.keyDownFn );\r
362 \r
363                                                 if ( instance.index ) CKEDITOR.ui.button._.instances[ instance.index ] = null;\r
364                                         }\r
365                                 }\r
366                         });\r
367 \r
368                         editor.addCommand( 'toolbarFocus', commands.toolbarFocus );\r
369                 }\r
370         });\r
371 })();\r
372 \r
373 /**\r
374  * The UI element that renders a toolbar separator.\r
375  * @type Object\r
376  * @example\r
377  */\r
378 CKEDITOR.ui.separator =\r
379 {\r
380         render : function( editor, output )\r
381         {\r
382                 output.push( '<span class="cke_separator" role="separator"></span>' );\r
383                 return {};\r
384         }\r
385 };\r
386 \r
387 /**\r
388  * The "theme space" to which rendering the toolbar. For the default theme,\r
389  * the recommended options are "top" and "bottom".\r
390  * @type String\r
391  * @default 'top'\r
392  * @see CKEDITOR.config.theme\r
393  * @example\r
394  * config.toolbarLocation = 'bottom';\r
395  */\r
396 CKEDITOR.config.toolbarLocation = 'top';\r
397 \r
398 /**\r
399  * The toolbar definition. It is an array of toolbars (strips),\r
400  * each one being also an array, containing a list of UI items.\r
401  * Note that this setting is composed by "toolbar_" added by the toolbar name,\r
402  * which in this case is called "Basic". This second part of the setting name\r
403  * can be anything. You must use this name in the\r
404  * {@link CKEDITOR.config.toolbar} setting, so you instruct the editor which\r
405  * toolbar_(name) setting to you.\r
406  * @type Array\r
407  * @example\r
408  * // Defines a toolbar with only one strip containing the "Source" button, a\r
409  * // separator and the "Bold" and "Italic" buttons.\r
410  * <b>config.toolbar_Basic =\r
411  * [\r
412  *     [ 'Source', '-', 'Bold', 'Italic' ]\r
413  * ]</b>;\r
414  * config.toolbar = 'Basic';\r
415  */\r
416 CKEDITOR.config.toolbar_Basic =\r
417 [\r
418         ['Bold', 'Italic', '-', 'NumberedList', 'BulletedList', '-', 'Link', 'Unlink','-','About']\r
419 ];\r
420 \r
421 /**\r
422  * This is the default toolbar definition used by the editor. It contains all\r
423  * editor features.\r
424  * @type Array\r
425  * @default (see example)\r
426  * @example\r
427  * // This is actually the default value.\r
428  * config.toolbar_Full =\r
429  * [\r
430  *     ['Source','-','Save','NewPage','Preview','-','Templates'],\r
431  *     ['Cut','Copy','Paste','PasteText','PasteFromWord','-','Print', 'SpellChecker', 'Scayt'],\r
432  *     ['Undo','Redo','-','Find','Replace','-','SelectAll','RemoveFormat'],\r
433  *     ['Form', 'Checkbox', 'Radio', 'TextField', 'Textarea', 'Select', 'Button', 'ImageButton', 'HiddenField'],\r
434  *     '/',\r
435  *     ['Bold','Italic','Underline','Strike','-','Subscript','Superscript'],\r
436  *     ['NumberedList','BulletedList','-','Outdent','Indent','Blockquote','CreateDiv'],\r
437  *     ['JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock'],\r
438  *     ['BidiLtr', 'BidiRtl' ],\r
439  *     ['Link','Unlink','Anchor'],\r
440  *     ['Image','Flash','Table','HorizontalRule','Smiley','SpecialChar','PageBreak','Iframe'],\r
441  *     '/',\r
442  *     ['Styles','Format','Font','FontSize'],\r
443  *     ['TextColor','BGColor'],\r
444  *     ['Maximize', 'ShowBlocks','-','About']\r
445  * ];\r
446  */\r
447 CKEDITOR.config.toolbar_Full =\r
448 [\r
449         ['Source','-','Save','NewPage','Preview','-','Templates'],\r
450         ['Cut','Copy','Paste','PasteText','PasteFromWord','-','Print', 'SpellChecker', 'Scayt'],\r
451         ['Undo','Redo','-','Find','Replace','-','SelectAll','RemoveFormat'],\r
452         ['Form', 'Checkbox', 'Radio', 'TextField', 'Textarea', 'Select', 'Button', 'ImageButton', 'HiddenField'],\r
453         '/',\r
454         ['Bold','Italic','Underline','Strike','-','Subscript','Superscript'],\r
455         ['NumberedList','BulletedList','-','Outdent','Indent','Blockquote','CreateDiv'],\r
456         ['JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock'],\r
457         ['BidiLtr', 'BidiRtl' ],\r
458         ['Link','Unlink','Anchor'],\r
459         ['Image','Flash','Table','HorizontalRule','Smiley','SpecialChar','PageBreak','Iframe'],\r
460         '/',\r
461         ['Styles','Format','Font','FontSize'],\r
462         ['TextColor','BGColor'],\r
463         ['Maximize', 'ShowBlocks','-','About']\r
464 ];\r
465 \r
466 /**\r
467  * The toolbox (alias toolbar) definition. It is a toolbar name or an array of\r
468  * toolbars (strips), each one being also an array, containing a list of UI items.\r
469  * @type Array|String\r
470  * @default 'Full'\r
471  * @example\r
472  * // Defines a toolbar with only one strip containing the "Source" button, a\r
473  * // separator and the "Bold" and "Italic" buttons.\r
474  * config.toolbar =\r
475  * [\r
476  *     [ 'Source', '-', 'Bold', 'Italic' ]\r
477  * ];\r
478  * @example\r
479  * // Load toolbar_Name where Name = Basic.\r
480  * config.toolbar = 'Basic';\r
481  */\r
482 CKEDITOR.config.toolbar = 'Full';\r
483 \r
484 /**\r
485  * Whether the toolbar can be collapsed by the user. If disabled, the collapser\r
486  * button will not be displayed.\r
487  * @type Boolean\r
488  * @default true\r
489  * @example\r
490  * config.toolbarCanCollapse = false;\r
491  */\r
492 CKEDITOR.config.toolbarCanCollapse = true;\r
493 \r
494 /**\r
495  * Whether the toolbar must start expanded when the editor is loaded.\r
496  * @name CKEDITOR.config.toolbarStartupExpanded\r
497  * @type Boolean\r
498  * @default true\r
499  * @example\r
500  * config.toolbarStartupExpanded = false;\r
501  */\r