]> git.immae.eu Git - perso/Immae/Projets/packagist/ludivine-ckeditor-component.git/blob - sources/plugins/toolbar/plugin.js
Validation initiale
[perso/Immae/Projets/packagist/ludivine-ckeditor-component.git] / sources / plugins / toolbar / plugin.js
1 /**
2 * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
3 * For licensing, see LICENSE.md or http://ckeditor.com/license
4 */
5
6 /**
7 * @fileOverview The "toolbar" plugin. Renders the default toolbar interface in
8 * the editor.
9 */
10
11 ( function() {
12 var toolbox = function() {
13 this.toolbars = [];
14 this.focusCommandExecuted = false;
15 };
16
17 toolbox.prototype.focus = function() {
18 for ( var t = 0, toolbar; toolbar = this.toolbars[ t++ ]; ) {
19 for ( var i = 0, item; item = toolbar.items[ i++ ]; ) {
20 if ( item.focus ) {
21 item.focus();
22 return;
23 }
24 }
25 }
26 };
27
28 var commands = {
29 toolbarFocus: {
30 modes: { wysiwyg: 1, source: 1 },
31 readOnly: 1,
32
33 exec: function( editor ) {
34 if ( editor.toolbox ) {
35 editor.toolbox.focusCommandExecuted = true;
36
37 // Make the first button focus accessible for IE. (#3417)
38 // Adobe AIR instead need while of delay.
39 if ( CKEDITOR.env.ie || CKEDITOR.env.air ) {
40 setTimeout( function() {
41 editor.toolbox.focus();
42 }, 100 );
43 } else {
44 editor.toolbox.focus();
45 }
46 }
47 }
48 }
49 };
50
51 CKEDITOR.plugins.add( 'toolbar', {
52 requires: 'button',
53 // jscs:disable maximumLineLength
54 lang: 'af,ar,az,bg,bn,bs,ca,cs,cy,da,de,de-ch,el,en,en-au,en-ca,en-gb,eo,es,et,eu,fa,fi,fo,fr,fr-ca,gl,gu,he,hi,hr,hu,id,is,it,ja,ka,km,ko,ku,lt,lv,mk,mn,ms,nb,nl,no,oc,pl,pt,pt-br,ro,ru,si,sk,sl,sq,sr,sr-latn,sv,th,tr,tt,ug,uk,vi,zh,zh-cn', // %REMOVE_LINE_CORE%
55 // jscs:enable maximumLineLength
56
57 init: function( editor ) {
58 var endFlag;
59
60 var itemKeystroke = function( item, keystroke ) {
61 var next, toolbar;
62 var rtl = editor.lang.dir == 'rtl',
63 toolbarGroupCycling = editor.config.toolbarGroupCycling,
64 // Picking right/left key codes.
65 rightKeyCode = rtl ? 37 : 39,
66 leftKeyCode = rtl ? 39 : 37;
67
68 toolbarGroupCycling = toolbarGroupCycling === undefined || toolbarGroupCycling;
69
70 switch ( keystroke ) {
71 case 9: // TAB
72 case CKEDITOR.SHIFT + 9: // SHIFT + TAB
73 // Cycle through the toolbars, starting from the one
74 // closest to the current item.
75 while ( !toolbar || !toolbar.items.length ) {
76 if ( keystroke == 9 ) {
77 toolbar = ( ( toolbar ? toolbar.next : item.toolbar.next ) || editor.toolbox.toolbars[ 0 ] );
78 } else {
79 toolbar = ( ( toolbar ? toolbar.previous : item.toolbar.previous ) || editor.toolbox.toolbars[ editor.toolbox.toolbars.length - 1 ] );
80 }
81
82 // Look for the first item that accepts focus.
83 if ( toolbar.items.length ) {
84 item = toolbar.items[ endFlag ? ( toolbar.items.length - 1 ) : 0 ];
85 while ( item && !item.focus ) {
86 item = endFlag ? item.previous : item.next;
87
88 if ( !item )
89 toolbar = 0;
90 }
91 }
92 }
93
94 if ( item )
95 item.focus();
96
97 return false;
98
99 case rightKeyCode:
100 next = item;
101 do {
102 // Look for the next item in the toolbar.
103 next = next.next;
104
105 // If it's the last item, cycle to the first one.
106 if ( !next && toolbarGroupCycling ) next = item.toolbar.items[ 0 ];
107 }
108 while ( next && !next.focus );
109
110 // If available, just focus it, otherwise focus the
111 // first one.
112 if ( next )
113 next.focus();
114 else
115 // Send a TAB.
116 itemKeystroke( item, 9 );
117
118 return false;
119 case 40: // DOWN-ARROW
120 if ( item.button && item.button.hasArrow ) {
121 // Note: code is duplicated in plugins\richcombo\plugin.js in keyDownFn().
122 editor.once( 'panelShow', function( evt ) {
123 evt.data._.panel._.currentBlock.onKeyDown( 40 );
124 } );
125 item.execute();
126 } else {
127 // Send left arrow key.
128 itemKeystroke( item, keystroke == 40 ? rightKeyCode : leftKeyCode );
129 }
130 return false;
131 case leftKeyCode:
132 case 38: // UP-ARROW
133 next = item;
134 do {
135 // Look for the previous item in the toolbar.
136 next = next.previous;
137
138 // If it's the first item, cycle to the last one.
139 if ( !next && toolbarGroupCycling ) next = item.toolbar.items[ item.toolbar.items.length - 1 ];
140 }
141 while ( next && !next.focus );
142
143 // If available, just focus it, otherwise focus the
144 // last one.
145 if ( next )
146 next.focus();
147 else {
148 endFlag = 1;
149 // Send a SHIFT + TAB.
150 itemKeystroke( item, CKEDITOR.SHIFT + 9 );
151 endFlag = 0;
152 }
153
154 return false;
155
156 case 27: // ESC
157 editor.focus();
158 return false;
159
160 case 13: // ENTER
161 case 32: // SPACE
162 item.execute();
163 return false;
164 }
165 return true;
166 };
167
168 editor.on( 'uiSpace', function( event ) {
169 if ( event.data.space != editor.config.toolbarLocation )
170 return;
171
172 // Create toolbar only once.
173 event.removeListener();
174
175 editor.toolbox = new toolbox();
176
177 var labelId = CKEDITOR.tools.getNextId();
178
179 var output = [
180 '<span id="', labelId, '" class="cke_voice_label">', editor.lang.toolbar.toolbars, '</span>',
181 '<span id="' + editor.ui.spaceId( 'toolbox' ) + '" class="cke_toolbox" role="group" aria-labelledby="', labelId, '" onmousedown="return false;">'
182 ];
183
184 var expanded = editor.config.toolbarStartupExpanded !== false,
185 groupStarted, pendingSeparator;
186
187 // If the toolbar collapser will be available, we'll have
188 // an additional container for all toolbars.
189 if ( editor.config.toolbarCanCollapse && editor.elementMode != CKEDITOR.ELEMENT_MODE_INLINE )
190 output.push( '<span class="cke_toolbox_main"' + ( expanded ? '>' : ' style="display:none">' ) );
191
192 var toolbars = editor.toolbox.toolbars,
193 toolbar = getToolbarConfig( editor ),
194 toolbarLength = toolbar.length;
195
196 for ( var r = 0; r < toolbarLength; r++ ) {
197 var toolbarId,
198 toolbarObj = 0,
199 toolbarName,
200 row = toolbar[ r ],
201 lastToolbarInRow = row !== '/' && ( toolbar[ r + 1 ] === '/' || r == toolbarLength - 1 ),
202 items;
203
204 // It's better to check if the row object is really
205 // available because it's a common mistake to leave
206 // an extra comma in the toolbar definition
207 // settings, which leads on the editor not loading
208 // at all in IE. (#3983)
209 if ( !row )
210 continue;
211
212 if ( groupStarted ) {
213 output.push( '</span>' );
214 groupStarted = 0;
215 pendingSeparator = 0;
216 }
217
218 if ( row === '/' ) {
219 output.push( '<span class="cke_toolbar_break"></span>' );
220 continue;
221 }
222
223 items = row.items || row;
224
225 // Create all items defined for this toolbar.
226 for ( var i = 0; i < items.length; i++ ) {
227 var item = items[ i ],
228 canGroup;
229
230 if ( item ) {
231 if ( item.type == CKEDITOR.UI_SEPARATOR ) {
232 // Do not add the separator immediately. Just save
233 // it be included if we already have something in
234 // the toolbar and if a new item is to be added (later).
235 pendingSeparator = groupStarted && item;
236 continue;
237 }
238
239 canGroup = item.canGroup !== false;
240
241 // Initialize the toolbar first, if needed.
242 if ( !toolbarObj ) {
243 // Create the basic toolbar object.
244 toolbarId = CKEDITOR.tools.getNextId();
245 toolbarObj = { id: toolbarId, items: [] };
246 toolbarName = row.name && ( editor.lang.toolbar.toolbarGroups[ row.name ] || row.name );
247
248 // Output the toolbar opener.
249 output.push( '<span id="', toolbarId, '" class="cke_toolbar' + ( lastToolbarInRow ? ' cke_toolbar_last"' : '"' ),
250 ( toolbarName ? ' aria-labelledby="' + toolbarId + '_label"' : '' ), ' role="toolbar">' );
251
252 // If a toolbar name is available, send the voice label.
253 toolbarName && output.push( '<span id="', toolbarId, '_label" class="cke_voice_label">', toolbarName, '</span>' );
254
255 output.push( '<span class="cke_toolbar_start"></span>' );
256
257 // Add the toolbar to the "editor.toolbox.toolbars"
258 // array.
259 var index = toolbars.push( toolbarObj ) - 1;
260
261 // Create the next/previous reference.
262 if ( index > 0 ) {
263 toolbarObj.previous = toolbars[ index - 1 ];
264 toolbarObj.previous.next = toolbarObj;
265 }
266 }
267
268 if ( canGroup ) {
269 if ( !groupStarted ) {
270 output.push( '<span class="cke_toolgroup" role="presentation">' );
271 groupStarted = 1;
272 }
273 } else if ( groupStarted ) {
274 output.push( '</span>' );
275 groupStarted = 0;
276 }
277
278 function addItem( item ) { // jshint ignore:line
279 var itemObj = item.render( editor, output );
280 index = toolbarObj.items.push( itemObj ) - 1;
281
282 if ( index > 0 ) {
283 itemObj.previous = toolbarObj.items[ index - 1 ];
284 itemObj.previous.next = itemObj;
285 }
286
287 itemObj.toolbar = toolbarObj;
288 itemObj.onkey = itemKeystroke;
289
290 // Fix for #3052:
291 // Prevent JAWS from focusing the toolbar after document load.
292 itemObj.onfocus = function() {
293 if ( !editor.toolbox.focusCommandExecuted )
294 editor.focus();
295 };
296 }
297
298 if ( pendingSeparator ) {
299 addItem( pendingSeparator );
300 pendingSeparator = 0;
301 }
302
303 addItem( item );
304 }
305 }
306
307 if ( groupStarted ) {
308 output.push( '</span>' );
309 groupStarted = 0;
310 pendingSeparator = 0;
311 }
312
313 if ( toolbarObj )
314 output.push( '<span class="cke_toolbar_end"></span></span>' );
315 }
316
317 if ( editor.config.toolbarCanCollapse )
318 output.push( '</span>' );
319
320 // Not toolbar collapser for inline mode.
321 if ( editor.config.toolbarCanCollapse && editor.elementMode != CKEDITOR.ELEMENT_MODE_INLINE ) {
322 var collapserFn = CKEDITOR.tools.addFunction( function() {
323 editor.execCommand( 'toolbarCollapse' );
324 } );
325
326 editor.on( 'destroy', function() {
327 CKEDITOR.tools.removeFunction( collapserFn );
328 } );
329
330 editor.addCommand( 'toolbarCollapse', {
331 readOnly: 1,
332 exec: function( editor ) {
333 var collapser = editor.ui.space( 'toolbar_collapser' ),
334 toolbox = collapser.getPrevious(),
335 contents = editor.ui.space( 'contents' ),
336 toolboxContainer = toolbox.getParent(),
337 contentHeight = parseInt( contents.$.style.height, 10 ),
338 previousHeight = toolboxContainer.$.offsetHeight,
339 minClass = 'cke_toolbox_collapser_min',
340 collapsed = collapser.hasClass( minClass );
341
342 if ( !collapsed ) {
343 toolbox.hide();
344 collapser.addClass( minClass );
345 collapser.setAttribute( 'title', editor.lang.toolbar.toolbarExpand );
346 } else {
347 toolbox.show();
348 collapser.removeClass( minClass );
349 collapser.setAttribute( 'title', editor.lang.toolbar.toolbarCollapse );
350 }
351
352 // Update collapser symbol.
353 collapser.getFirst().setText( collapsed ? '\u25B2' : // BLACK UP-POINTING TRIANGLE
354 '\u25C0' ); // BLACK LEFT-POINTING TRIANGLE
355
356 var dy = toolboxContainer.$.offsetHeight - previousHeight;
357 contents.setStyle( 'height', ( contentHeight - dy ) + 'px' );
358
359 editor.fire( 'resize', {
360 outerHeight: editor.container.$.offsetHeight,
361 contentsHeight: contents.$.offsetHeight,
362 outerWidth: editor.container.$.offsetWidth
363 } );
364 },
365
366 modes: { wysiwyg: 1, source: 1 }
367 } );
368
369 editor.setKeystroke( CKEDITOR.ALT + ( CKEDITOR.env.ie || CKEDITOR.env.webkit ? 189 : 109 ) /*-*/, 'toolbarCollapse' );
370
371 output.push( '<a title="' + ( expanded ? editor.lang.toolbar.toolbarCollapse : editor.lang.toolbar.toolbarExpand ) +
372 '" id="' + editor.ui.spaceId( 'toolbar_collapser' ) +
373 '" tabIndex="-1" class="cke_toolbox_collapser' );
374
375 if ( !expanded )
376 output.push( ' cke_toolbox_collapser_min' );
377
378 output.push( '" onclick="CKEDITOR.tools.callFunction(' + collapserFn + ')">', '<span class="cke_arrow">&#9650;</span>', // BLACK UP-POINTING TRIANGLE
379 '</a>' );
380 }
381
382 output.push( '</span>' );
383 event.data.html += output.join( '' );
384 } );
385
386 editor.on( 'destroy', function() {
387 if ( this.toolbox ) {
388 var toolbars,
389 index = 0,
390 i, items, instance;
391 toolbars = this.toolbox.toolbars;
392 for ( ; index < toolbars.length; index++ ) {
393 items = toolbars[ index ].items;
394 for ( i = 0; i < items.length; i++ ) {
395 instance = items[ i ];
396 if ( instance.clickFn )
397 CKEDITOR.tools.removeFunction( instance.clickFn );
398 if ( instance.keyDownFn )
399 CKEDITOR.tools.removeFunction( instance.keyDownFn );
400 }
401 }
402 }
403 } );
404
405 // Manage editor focus when navigating the toolbar.
406 editor.on( 'uiReady', function() {
407 var toolbox = editor.ui.space( 'toolbox' );
408 toolbox && editor.focusManager.add( toolbox, 1 );
409 } );
410
411 editor.addCommand( 'toolbarFocus', commands.toolbarFocus );
412 editor.setKeystroke( CKEDITOR.ALT + 121 /*F10*/, 'toolbarFocus' );
413
414 editor.ui.add( '-', CKEDITOR.UI_SEPARATOR, {} );
415 editor.ui.addHandler( CKEDITOR.UI_SEPARATOR, {
416 create: function() {
417 return {
418 render: function( editor, output ) {
419 output.push( '<span class="cke_toolbar_separator" role="separator"></span>' );
420 return {};
421 }
422 };
423 }
424 } );
425 }
426 } );
427
428 function getToolbarConfig( editor ) {
429 var removeButtons = editor.config.removeButtons;
430
431 removeButtons = removeButtons && removeButtons.split( ',' );
432
433 function buildToolbarConfig() {
434
435 // Object containing all toolbar groups used by ui items.
436 var lookup = getItemDefinedGroups();
437
438 // Take the base for the new toolbar, which is basically a toolbar
439 // definition without items.
440 var toolbar = CKEDITOR.tools.clone( editor.config.toolbarGroups ) || getPrivateToolbarGroups( editor );
441
442 // Fill the toolbar groups with the available ui items.
443 for ( var i = 0; i < toolbar.length; i++ ) {
444 var toolbarGroup = toolbar[ i ];
445
446 // Skip toolbar break.
447 if ( toolbarGroup == '/' )
448 continue;
449 // Handle simply group name item.
450 else if ( typeof toolbarGroup == 'string' )
451 toolbarGroup = toolbar[ i ] = { name: toolbarGroup };
452
453 var items, subGroups = toolbarGroup.groups;
454
455 // Look for items that match sub groups.
456 if ( subGroups ) {
457 for ( var j = 0, sub; j < subGroups.length; j++ ) {
458 sub = subGroups[ j ];
459
460 // If any ui item is registered for this subgroup.
461 items = lookup[ sub ];
462 items && fillGroup( toolbarGroup, items );
463 }
464 }
465
466 // Add the main group items as well.
467 items = lookup[ toolbarGroup.name ];
468 items && fillGroup( toolbarGroup, items );
469 }
470
471 return toolbar;
472 }
473
474 // Returns an object containing all toolbar groups used by ui items.
475 function getItemDefinedGroups() {
476 var groups = {},
477 itemName, item, itemToolbar, group, order;
478
479 for ( itemName in editor.ui.items ) {
480 item = editor.ui.items[ itemName ];
481 itemToolbar = item.toolbar || 'others';
482 if ( itemToolbar ) {
483 // Break the toolbar property into its parts: "group_name[,order]".
484 itemToolbar = itemToolbar.split( ',' );
485 group = itemToolbar[ 0 ];
486 order = parseInt( itemToolbar[ 1 ] || -1, 10 );
487
488 // Initialize the group, if necessary.
489 groups[ group ] || ( groups[ group ] = [] );
490
491 // Push the data used to build the toolbar later.
492 groups[ group ].push( { name: itemName, order: order } );
493 }
494 }
495
496 // Put the items in the right order.
497 for ( group in groups ) {
498 groups[ group ] = groups[ group ].sort( function( a, b ) {
499 return a.order == b.order ? 0 :
500 b.order < 0 ? -1 :
501 a.order < 0 ? 1 :
502 a.order < b.order ? -1 :
503 1;
504 } );
505 }
506
507 return groups;
508 }
509
510 function fillGroup( toolbarGroup, uiItems ) {
511 if ( uiItems.length ) {
512 if ( toolbarGroup.items )
513 toolbarGroup.items.push( editor.ui.create( '-' ) );
514 else
515 toolbarGroup.items = [];
516
517 var item, name;
518 while ( ( item = uiItems.shift() ) ) {
519 name = typeof item == 'string' ? item : item.name;
520
521 // Ignore items that are configured to be removed.
522 if ( !removeButtons || CKEDITOR.tools.indexOf( removeButtons, name ) == -1 ) {
523 item = editor.ui.create( name );
524
525 if ( !item )
526 continue;
527
528 if ( !editor.addFeature( item ) )
529 continue;
530
531 toolbarGroup.items.push( item );
532 }
533 }
534 }
535 }
536
537 function populateToolbarConfig( config ) {
538 var toolbar = [],
539 i, group, newGroup;
540
541 for ( i = 0; i < config.length; ++i ) {
542 group = config[ i ];
543 newGroup = {};
544
545 if ( group == '/' )
546 toolbar.push( group );
547 else if ( CKEDITOR.tools.isArray( group ) ) {
548 fillGroup( newGroup, CKEDITOR.tools.clone( group ) );
549 toolbar.push( newGroup );
550 }
551 else if ( group.items ) {
552 fillGroup( newGroup, CKEDITOR.tools.clone( group.items ) );
553 newGroup.name = group.name;
554 toolbar.push( newGroup );
555 }
556 }
557
558 return toolbar;
559 }
560
561 var toolbar = editor.config.toolbar;
562
563 // If it is a string, return the relative "toolbar_name" config.
564 if ( typeof toolbar == 'string' )
565 toolbar = editor.config[ 'toolbar_' + toolbar ];
566
567 return ( editor.toolbar = toolbar ? populateToolbarConfig( toolbar ) : buildToolbarConfig() );
568 }
569
570 /**
571 * Adds a toolbar group. See {@link CKEDITOR.config#toolbarGroups} for more details.
572 *
573 * **Note:** This method will not modify toolbar groups set explicitly by
574 * {@link CKEDITOR.config#toolbarGroups}. It will only extend the default setting.
575 *
576 * @param {String} name Toolbar group name.
577 * @param {Number/String} previous The name of the toolbar group after which this one
578 * should be added or `0` if this group should be the first one.
579 * @param {String} [subgroupOf] The name of the parent group.
580 * @member CKEDITOR.ui
581 */
582 CKEDITOR.ui.prototype.addToolbarGroup = function( name, previous, subgroupOf ) {
583 // The toolbarGroups from the privates is the one we gonna use for automatic toolbar creation.
584 var toolbarGroups = getPrivateToolbarGroups( this.editor ),
585 atStart = previous === 0,
586 newGroup = { name: name };
587
588 if ( subgroupOf ) {
589 // Transform the subgroupOf name in the real subgroup object.
590 subgroupOf = CKEDITOR.tools.search( toolbarGroups, function( group ) {
591 return group.name == subgroupOf;
592 } );
593
594 if ( subgroupOf ) {
595 !subgroupOf.groups && ( subgroupOf.groups = [] ) ;
596
597 if ( previous ) {
598 // Search the "previous" item and add the new one after it.
599 previous = CKEDITOR.tools.indexOf( subgroupOf.groups, previous );
600 if ( previous >= 0 ) {
601 subgroupOf.groups.splice( previous + 1, 0, name );
602 return;
603 }
604 }
605
606 // If no previous found.
607
608 if ( atStart )
609 subgroupOf.groups.splice( 0, 0, name );
610 else
611 subgroupOf.groups.push( name );
612 return;
613 } else {
614 // Ignore "previous" if subgroupOf has not been found.
615 previous = null;
616 }
617 }
618
619 if ( previous ) {
620 // Transform the "previous" name into its index.
621 previous = CKEDITOR.tools.indexOf( toolbarGroups, function( group ) {
622 return group.name == previous;
623 } );
624 }
625
626 if ( atStart )
627 toolbarGroups.splice( 0, 0, name );
628 else if ( typeof previous == 'number' )
629 toolbarGroups.splice( previous + 1, 0, newGroup );
630 else
631 toolbarGroups.push( name );
632 };
633
634 function getPrivateToolbarGroups( editor ) {
635 return editor._.toolbarGroups || ( editor._.toolbarGroups = [
636 { name: 'document', groups: [ 'mode', 'document', 'doctools' ] },
637 { name: 'clipboard', groups: [ 'clipboard', 'undo' ] },
638 { name: 'editing', groups: [ 'find', 'selection', 'spellchecker' ] },
639 { name: 'forms' },
640 '/',
641 { name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ] },
642 { name: 'paragraph', groups: [ 'list', 'indent', 'blocks', 'align', 'bidi' ] },
643 { name: 'links' },
644 { name: 'insert' },
645 '/',
646 { name: 'styles' },
647 { name: 'colors' },
648 { name: 'tools' },
649 { name: 'others' },
650 { name: 'about' }
651 ] );
652 }
653 } )();
654
655 /**
656 * Separator UI element.
657 *
658 * @readonly
659 * @property {String} [='separator']
660 * @member CKEDITOR
661 */
662 CKEDITOR.UI_SEPARATOR = 'separator';
663
664 /**
665 * The part of the user interface where the toolbar will be rendered. For the default
666 * editor implementation, the recommended options are `'top'` and `'bottom'`.
667 *
668 * Please note that this option is only applicable to [classic](#!/guide/dev_framed)
669 * (`iframe`-based) editor. In case of [inline](#!/guide/dev_inline) editor the toolbar
670 * position is set dynamically depending on the position of the editable element on the screen.
671 *
672 * Read more in the [documentation](#!/guide/dev_toolbarlocation)
673 * and see the [SDK sample](http://sdk.ckeditor.com/samples/toolbarlocation.html).
674 *
675 * config.toolbarLocation = 'bottom';
676 *
677 * @cfg
678 * @member CKEDITOR.config
679 */
680 CKEDITOR.config.toolbarLocation = 'top';
681
682 /**
683 * The toolbox (alias toolbar) definition. It is a toolbar name or an array of
684 * toolbars (strips), each one being also an array, containing a list of UI items.
685 *
686 * If set to `null`, the toolbar will be generated automatically using all available buttons
687 * and {@link #toolbarGroups} as a toolbar groups layout.
688 *
689 * In CKEditor 4.5+ you can generate your toolbar customization code by using the [visual
690 * toolbar configurator](http://docs.ckeditor.com/#!/guide/dev_toolbar).
691 *
692 * // Defines a toolbar with only one strip containing the "Source" button, a
693 * // separator, and the "Bold" and "Italic" buttons.
694 * config.toolbar = [
695 * [ 'Source', '-', 'Bold', 'Italic' ]
696 * ];
697 *
698 * // Similar to the example above, defines a "Basic" toolbar with only one strip containing three buttons.
699 * // Note that this setting is composed by "toolbar_" added to the toolbar name, which in this case is called "Basic".
700 * // This second part of the setting name can be anything. You must use this name in the CKEDITOR.config.toolbar setting
701 * // in order to instruct the editor which `toolbar_(name)` setting should be used.
702 * config.toolbar_Basic = [
703 * [ 'Source', '-', 'Bold', 'Italic' ]
704 * ];
705 * // Load toolbar_Name where Name = Basic.
706 * config.toolbar = 'Basic';
707 *
708 * @cfg {Array/String} [toolbar=null]
709 * @member CKEDITOR.config
710 */
711
712 /**
713 * The toolbar groups definition.
714 *
715 * If the toolbar layout is not explicitly defined by the {@link #toolbar} setting, then
716 * this setting is used to group all defined buttons (see {@link CKEDITOR.ui#addButton}).
717 * Buttons are associated with toolbar groups by the `toolbar` property in their definition objects.
718 *
719 * New groups may be dynamically added during the editor and plugin initialization by
720 * {@link CKEDITOR.ui#addToolbarGroup}. This is only possible if the default setting was used.
721 *
722 * // Default setting.
723 * config.toolbarGroups = [
724 * { name: 'document', groups: [ 'mode', 'document', 'doctools' ] },
725 * { name: 'clipboard', groups: [ 'clipboard', 'undo' ] },
726 * { name: 'editing', groups: [ 'find', 'selection', 'spellchecker' ] },
727 * { name: 'forms' },
728 * '/',
729 * { name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ] },
730 * { name: 'paragraph', groups: [ 'list', 'indent', 'blocks', 'align', 'bidi' ] },
731 * { name: 'links' },
732 * { name: 'insert' },
733 * '/',
734 * { name: 'styles' },
735 * { name: 'colors' },
736 * { name: 'tools' },
737 * { name: 'others' },
738 * { name: 'about' }
739 * ];
740 *
741 * @cfg {Array} [toolbarGroups=see example]
742 * @member CKEDITOR.config
743 */
744
745 /**
746 * Whether the toolbar can be collapsed by the user. If disabled, the Collapse Toolbar
747 * button will not be displayed.
748 *
749 * config.toolbarCanCollapse = true;
750 *
751 * @cfg {Boolean} [toolbarCanCollapse=false]
752 * @member CKEDITOR.config
753 */
754
755 /**
756 * Whether the toolbar must start expanded when the editor is loaded.
757 *
758 * Setting this option to `false` will affect the toolbar only when
759 * {@link #toolbarCanCollapse} is set to `true`:
760 *
761 * config.toolbarCanCollapse = true;
762 * config.toolbarStartupExpanded = false;
763 *
764 * @cfg {Boolean} [toolbarStartupExpanded=true]
765 * @member CKEDITOR.config
766 */
767
768 /**
769 * When enabled, causes the *Arrow* keys navigation to cycle within the current
770 * toolbar group. Otherwise the *Arrow* keys will move through all items available in
771 * the toolbar. The *Tab* key will still be used to quickly jump among the
772 * toolbar groups.
773 *
774 * config.toolbarGroupCycling = false;
775 *
776 * @since 3.6
777 * @cfg {Boolean} [toolbarGroupCycling=true]
778 * @member CKEDITOR.config
779 */
780
781 /**
782 * List of toolbar button names that must not be rendered. This will also work
783 * for non-button toolbar items, like the Font drop-down list.
784 *
785 * config.removeButtons = 'Underline,JustifyCenter';
786 *
787 * This configuration option should not be overused. The recommended way is to use the
788 * {@link CKEDITOR.config#removePlugins} setting to remove features from the editor
789 * or even better, [create a custom editor build](http://ckeditor.com/builder) with
790 * just the features that you will use.
791 * In some cases though, a single plugin may define a set of toolbar buttons and
792 * `removeButtons` may be useful when just a few of them are to be removed.
793 *
794 * @cfg {String} [removeButtons]
795 * @member CKEDITOR.config
796 */
797
798 /**
799 * The toolbar definition used by the editor. It is created from the
800 * {@link CKEDITOR.config#toolbar} option if it is set or automatically
801 * based on {@link CKEDITOR.config#toolbarGroups}.
802 *
803 * @readonly
804 * @property {Object} toolbar
805 * @member CKEDITOR.editor
806 */