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