/*\r
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.\r
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.\r
For licensing, see LICENSE.html or http://ckeditor.com/license\r
*/\r
\r
toolbarFocus :\r
{\r
modes : { wysiwyg : 1, source : 1 },\r
+ readOnly : 1,\r
\r
exec : function( editor )\r
{\r
{\r
editor.toolbox.focusCommandExecuted = true;\r
\r
- // Make the first button focus accessible. (#3417)\r
- if ( CKEDITOR.env.ie )\r
+ // Make the first button focus accessible for IE. (#3417)\r
+ // Adobe AIR instead need while of delay.\r
+ if ( CKEDITOR.env.ie || CKEDITOR.env.air )\r
setTimeout( function(){ editor.toolbox.focus(); }, 100 );\r
else\r
editor.toolbox.focus();\r
\r
CKEDITOR.plugins.add( 'toolbar',\r
{\r
+ requires : [ 'button' ],\r
init : function( editor )\r
{\r
+ var endFlag;\r
+\r
var itemKeystroke = function( item, keystroke )\r
{\r
- var next, nextToolGroup, groupItemsCount;\r
- var rtl = editor.lang.dir == 'rtl';\r
+ var next, toolbar;\r
+ var rtl = editor.lang.dir == 'rtl',\r
+ toolbarGroupCycling = editor.config.toolbarGroupCycling;\r
+\r
+ toolbarGroupCycling = toolbarGroupCycling === undefined || toolbarGroupCycling;\r
\r
switch ( keystroke )\r
{\r
- case rtl ? 37 : 39 : // RIGHT-ARROW\r
case 9 : // TAB\r
- do\r
+ case CKEDITOR.SHIFT + 9 : // SHIFT + TAB\r
+ // Cycle through the toolbars, starting from the one\r
+ // closest to the current item.\r
+ while ( !toolbar || !toolbar.items.length )\r
{\r
- // Look for the next item in the toolbar.\r
- next = item.next;\r
+ toolbar = keystroke == 9 ?\r
+ ( ( toolbar ? toolbar.next : item.toolbar.next ) || editor.toolbox.toolbars[ 0 ] ) :\r
+ ( ( toolbar ? toolbar.previous : item.toolbar.previous ) || editor.toolbox.toolbars[ editor.toolbox.toolbars.length - 1 ] );\r
\r
- if ( !next )\r
+ // Look for the first item that accepts focus.\r
+ if ( toolbar.items.length )\r
{\r
- nextToolGroup = item.toolbar.next;\r
- groupItemsCount = nextToolGroup && nextToolGroup.items.length;\r
-\r
- // Bypass the empty toolgroups.\r
- while ( groupItemsCount === 0 )\r
+ item = toolbar.items[ endFlag ? ( toolbar.items.length - 1 ) : 0 ];\r
+ while ( item && !item.focus )\r
{\r
- nextToolGroup = nextToolGroup.next;\r
- groupItemsCount = nextToolGroup && nextToolGroup.items.length;\r
- }\r
+ item = endFlag ? item.previous : item.next;\r
\r
- if ( nextToolGroup )\r
- next = nextToolGroup.items[ 0 ];\r
+ if ( !item )\r
+ toolbar = 0;\r
+ }\r
}\r
-\r
- item = next;\r
}\r
- while ( item && !item.focus )\r
\r
- // If available, just focus it, otherwise focus the\r
- // first one.\r
if ( item )\r
item.focus();\r
- else\r
- editor.toolbox.focus();\r
\r
return false;\r
\r
- case rtl ? 39 : 37 : // LEFT-ARROW\r
- case CKEDITOR.SHIFT + 9 : // SHIFT + TAB\r
+ case rtl ? 37 : 39 : // RIGHT-ARROW\r
+ case 40 : // DOWN-ARROW\r
+ next = item;\r
do\r
{\r
- // Look for the previous item in the toolbar.\r
- next = item.previous;\r
+ // Look for the next item in the toolbar.\r
+ next = next.next;\r
\r
- if ( !next )\r
- {\r
- nextToolGroup = item.toolbar.previous;\r
- groupItemsCount = nextToolGroup && nextToolGroup.items.length;\r
+ // If it's the last item, cycle to the first one.\r
+ if ( !next && toolbarGroupCycling )\r
+ next = item.toolbar.items[ 0 ];\r
+ }\r
+ while ( next && !next.focus )\r
\r
- // Bypass the empty toolgroups.\r
- while ( groupItemsCount === 0 )\r
- {\r
- nextToolGroup = nextToolGroup.previous;\r
- groupItemsCount = nextToolGroup && nextToolGroup.items.length;\r
- }\r
+ // If available, just focus it, otherwise focus the\r
+ // first one.\r
+ if ( next )\r
+ next.focus();\r
+ else\r
+ // Send a TAB.\r
+ itemKeystroke( item, 9 );\r
\r
- if ( nextToolGroup )\r
- next = nextToolGroup.items[ groupItemsCount - 1 ];\r
- }\r
+ return false;\r
+\r
+ case rtl ? 39 : 37 : // LEFT-ARROW\r
+ case 38 : // UP-ARROW\r
+ next = item;\r
+ do\r
+ {\r
+ // Look for the previous item in the toolbar.\r
+ next = next.previous;\r
\r
- item = next;\r
+ // If it's the first item, cycle to the last one.\r
+ if ( !next && toolbarGroupCycling )\r
+ next = item.toolbar.items[ item.toolbar.items.length - 1 ];\r
}\r
- while ( item && !item.focus )\r
+ while ( next && !next.focus )\r
\r
// If available, just focus it, otherwise focus the\r
// last one.\r
- if ( item )\r
- item.focus();\r
+ if ( next )\r
+ next.focus();\r
else\r
{\r
- var lastToolbarItems = editor.toolbox.toolbars[ editor.toolbox.toolbars.length - 1 ].items;\r
- lastToolbarItems[ lastToolbarItems.length - 1 ].focus();\r
+ endFlag = 1;\r
+ // Send a SHIFT + TAB.\r
+ itemKeystroke( item, CKEDITOR.SHIFT + 9 );\r
+ endFlag = 0;\r
}\r
\r
return false;\r
{\r
editor.toolbox = new toolbox();\r
\r
- var labelId = 'cke_' + CKEDITOR.tools.getNextNumber();\r
+ var labelId = CKEDITOR.tools.getNextId();\r
\r
- var output = [ '<div class="cke_toolbox" role="toolbar" aria-labelledby="', labelId, '"' ],\r
+ var output = [ '<div class="cke_toolbox" role="group" aria-labelledby="', labelId, '" onmousedown="return false;"' ],\r
expanded = editor.config.toolbarStartupExpanded !== false,\r
groupStarted;\r
\r
output.push( expanded ? '>' : ' style="display:none">' );\r
\r
// Sends the ARIA label.\r
- output.push( '<span id="', labelId, '" class="cke_voice_label">', editor.lang.toolbar, '</span>' );\r
+ output.push( '<span id="', labelId, '" class="cke_voice_label">', editor.lang.toolbars, '</span>' );\r
\r
var toolbars = editor.toolbox.toolbars,\r
toolbar =\r
\r
for ( var r = 0 ; r < toolbar.length ; r++ )\r
{\r
- var row = toolbar[ r ];\r
+ var toolbarId,\r
+ toolbarObj = 0,\r
+ toolbarName,\r
+ row = toolbar[ r ],\r
+ items;\r
\r
// It's better to check if the row object is really\r
// available because it's a common mistake to leave\r
if ( !row )\r
continue;\r
\r
- var toolbarId = 'cke_' + CKEDITOR.tools.getNextNumber(),\r
- toolbarObj = { id : toolbarId, items : [] };\r
-\r
if ( groupStarted )\r
{\r
output.push( '</div>' );\r
continue;\r
}\r
\r
- output.push( '<span id="', toolbarId, '" class="cke_toolbar" role="presentation"><span class="cke_toolbar_start"></span>' );\r
-\r
- // Add the toolbar to the "editor.toolbox.toolbars"\r
- // array.\r
- var index = toolbars.push( toolbarObj ) - 1;\r
-\r
- // Create the next/previous reference.\r
- if ( index > 0 )\r
- {\r
- toolbarObj.previous = toolbars[ index - 1 ];\r
- toolbarObj.previous.next = toolbarObj;\r
- }\r
+ items = row.items || row;\r
\r
// Create all items defined for this toolbar.\r
- for ( var i = 0 ; i < row.length ; i++ )\r
+ for ( var i = 0 ; i < items.length ; i++ )\r
{\r
var item,\r
- itemName = row[ i ];\r
+ itemName = items[ i ],\r
+ canGroup;\r
\r
- if ( itemName == '-' )\r
- item = CKEDITOR.ui.separator;\r
- else\r
- item = editor.ui.create( itemName );\r
+ item = editor.ui.create( itemName );\r
\r
if ( item )\r
{\r
- if ( item.canGroup )\r
+ canGroup = item.canGroup !== false;\r
+\r
+ // Initialize the toolbar first, if needed.\r
+ if ( !toolbarObj )\r
+ {\r
+ // Create the basic toolbar object.\r
+ toolbarId = CKEDITOR.tools.getNextId();\r
+ toolbarObj = { id : toolbarId, items : [] };\r
+ toolbarName = row.name && ( editor.lang.toolbarGroups[ row.name ] || row.name );\r
+\r
+ // Output the toolbar opener.\r
+ output.push( '<span id="', toolbarId, '" class="cke_toolbar"',\r
+ ( toolbarName ? ' aria-labelledby="'+ toolbarId + '_label"' : '' ),\r
+ ' role="toolbar">' );\r
+\r
+ // If a toolbar name is available, send the voice label.\r
+ toolbarName && output.push( '<span id="', toolbarId, '_label" class="cke_voice_label">', toolbarName, '</span>' );\r
+\r
+ output.push( '<span class="cke_toolbar_start"></span>' );\r
+\r
+ // Add the toolbar to the "editor.toolbox.toolbars"\r
+ // array.\r
+ var index = toolbars.push( toolbarObj ) - 1;\r
+\r
+ // Create the next/previous reference.\r
+ if ( index > 0 )\r
+ {\r
+ toolbarObj.previous = toolbars[ index - 1 ];\r
+ toolbarObj.previous.next = toolbarObj;\r
+ }\r
+ }\r
+\r
+ if ( canGroup )\r
{\r
if ( !groupStarted )\r
{\r
groupStarted = 0;\r
}\r
\r
- output.push( '<span class="cke_toolbar_end"></span></span>' );\r
+ if ( toolbarObj )\r
+ output.push( '<span class="cke_toolbar_end"></span></span>' );\r
}\r
\r
output.push( '</div>' );\r
function()\r
{\r
editor.execCommand( 'toolbarCollapse' );\r
- } );\r
+ });\r
\r
editor.on( 'destroy', function () {\r
CKEDITOR.tools.removeFunction( collapserFn );\r
- } );\r
+ });\r
\r
- var collapserId = 'cke_' + CKEDITOR.tools.getNextNumber();\r
+ var collapserId = CKEDITOR.tools.getNextId();\r
\r
editor.addCommand( 'toolbarCollapse',\r
{\r
+ readOnly : 1,\r
exec : function( editor )\r
{\r
- var collapser = CKEDITOR.document.getById( collapserId );\r
- var toolbox = collapser.getPrevious();\r
- var contents = editor.getThemeSpace( 'contents' );\r
- var toolboxContainer = toolbox.getParent();\r
- var contentHeight = parseInt( contents.$.style.height, 10 );\r
- var previousHeight = toolboxContainer.$.offsetHeight;\r
- var collapsed = !toolbox.isVisible();\r
+ var collapser = CKEDITOR.document.getById( collapserId ),\r
+ toolbox = collapser.getPrevious(),\r
+ contents = editor.getThemeSpace( 'contents' ),\r
+ toolboxContainer = toolbox.getParent(),\r
+ contentHeight = parseInt( contents.$.style.height, 10 ),\r
+ previousHeight = toolboxContainer.$.offsetHeight,\r
+ collapsed = !toolbox.isVisible();\r
\r
if ( !collapsed )\r
{\r
}\r
});\r
\r
+ editor.on( 'destroy', function()\r
+ {\r
+ var toolbars, index = 0, i,\r
+ items, instance;\r
+ toolbars = this.toolbox.toolbars;\r
+ for ( ; index < toolbars.length; index++ )\r
+ {\r
+ items = toolbars[ index ].items;\r
+ for ( i = 0; i < items.length; i++ )\r
+ {\r
+ instance = items[ i ];\r
+ if ( instance.clickFn ) CKEDITOR.tools.removeFunction( instance.clickFn );\r
+ if ( instance.keyDownFn ) CKEDITOR.tools.removeFunction( instance.keyDownFn );\r
+ }\r
+ }\r
+ });\r
+\r
editor.addCommand( 'toolbarFocus', commands.toolbarFocus );\r
+\r
+ editor.ui.add( '-', CKEDITOR.UI_SEPARATOR, {} );\r
+ editor.ui.addHandler( CKEDITOR.UI_SEPARATOR,\r
+ {\r
+ create: function()\r
+ {\r
+ return {\r
+ render : function( editor, output )\r
+ {\r
+ output.push( '<span class="cke_separator" role="separator"></span>' );\r
+ return {};\r
+ }\r
+ };\r
+ }\r
+ });\r
}\r
});\r
})();\r
\r
-/**\r
- * The UI element that renders a toolbar separator.\r
- * @type Object\r
- * @example\r
- */\r
-CKEDITOR.ui.separator =\r
-{\r
- render : function( editor, output )\r
- {\r
- output.push( '<span class="cke_separator" role="separator"></span>' );\r
- return {};\r
- }\r
-};\r
+CKEDITOR.UI_SEPARATOR = 'separator';\r
\r
/**\r
* The "theme space" to which rendering the toolbar. For the default theme,\r
* // This is actually the default value.\r
* config.toolbar_Full =\r
* [\r
- * ['Source','-','Save','NewPage','Preview','-','Templates'],\r
- * ['Cut','Copy','Paste','PasteText','PasteFromWord','-','Print', 'SpellChecker', 'Scayt'],\r
- * ['Undo','Redo','-','Find','Replace','-','SelectAll','RemoveFormat'],\r
- * ['Form', 'Checkbox', 'Radio', 'TextField', 'Textarea', 'Select', 'Button', 'ImageButton', 'HiddenField'],\r
+ * { name: 'document', items : [ 'Source','-','Save','NewPage','DocProps','Preview','Print','-','Templates' ] },\r
+ * { name: 'clipboard', items : [ 'Cut','Copy','Paste','PasteText','PasteFromWord','-','Undo','Redo' ] },\r
+ * { name: 'editing', items : [ 'Find','Replace','-','SelectAll','-','SpellChecker', 'Scayt' ] },\r
+ * { name: 'forms', items : [ 'Form', 'Checkbox', 'Radio', 'TextField', 'Textarea', 'Select', 'Button', 'ImageButton', 'HiddenField' ] },\r
* '/',\r
- * ['Bold','Italic','Underline','Strike','-','Subscript','Superscript'],\r
- * ['NumberedList','BulletedList','-','Outdent','Indent','Blockquote','CreateDiv'],\r
- * ['JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock'],\r
- * ['Link','Unlink','Anchor'],\r
- * ['Image','Flash','Table','HorizontalRule','Smiley','SpecialChar','PageBreak'],\r
+ * { name: 'basicstyles', items : [ 'Bold','Italic','Underline','Strike','Subscript','Superscript','-','RemoveFormat' ] },\r
+ * { name: 'paragraph', items : [ 'NumberedList','BulletedList','-','Outdent','Indent','-','Blockquote','CreateDiv','-','JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock','-','BidiLtr','BidiRtl' ] },\r
+ * { name: 'links', items : [ 'Link','Unlink','Anchor' ] },\r
+ * { name: 'insert', items : [ 'Image','Flash','Table','HorizontalRule','Smiley','SpecialChar','PageBreak' ] },\r
* '/',\r
- * ['Styles','Format','Font','FontSize'],\r
- * ['TextColor','BGColor'],\r
- * ['Maximize', 'ShowBlocks','-','About']\r
+ * { name: 'styles', items : [ 'Styles','Format','Font','FontSize' ] },\r
+ * { name: 'colors', items : [ 'TextColor','BGColor' ] },\r
+ * { name: 'tools', items : [ 'Maximize', 'ShowBlocks','-','About' ] }\r
* ];\r
*/\r
CKEDITOR.config.toolbar_Full =\r
[\r
- ['Source','-','Save','NewPage','Preview','-','Templates'],\r
- ['Cut','Copy','Paste','PasteText','PasteFromWord','-','Print', 'SpellChecker', 'Scayt'],\r
- ['Undo','Redo','-','Find','Replace','-','SelectAll','RemoveFormat'],\r
- ['Form', 'Checkbox', 'Radio', 'TextField', 'Textarea', 'Select', 'Button', 'ImageButton', 'HiddenField'],\r
+ { name: 'document', items : [ 'Source','-','Save','NewPage','DocProps','Preview','Print','-','Templates' ] },\r
+ { name: 'clipboard', items : [ 'Cut','Copy','Paste','PasteText','PasteFromWord','-','Undo','Redo' ] },\r
+ { name: 'editing', items : [ 'Find','Replace','-','SelectAll','-','SpellChecker', 'Scayt' ] },\r
+ { name: 'forms', items : [ 'Form', 'Checkbox', 'Radio', 'TextField', 'Textarea', 'Select', 'Button', 'ImageButton', 'HiddenField' ] },\r
'/',\r
- ['Bold','Italic','Underline','Strike','-','Subscript','Superscript'],\r
- ['NumberedList','BulletedList','-','Outdent','Indent','Blockquote','CreateDiv'],\r
- ['JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock'],\r
- ['Link','Unlink','Anchor'],\r
- ['Image','Flash','Table','HorizontalRule','Smiley','SpecialChar','PageBreak'],\r
+ { name: 'basicstyles', items : [ 'Bold','Italic','Underline','Strike','Subscript','Superscript','-','RemoveFormat' ] },\r
+ { name: 'paragraph', items : [ 'NumberedList','BulletedList','-','Outdent','Indent','-','Blockquote','CreateDiv','-','JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock','-','BidiLtr','BidiRtl' ] },\r
+ { name: 'links', items : [ 'Link','Unlink','Anchor' ] },\r
+ { name: 'insert', items : [ 'Image','Flash','Table','HorizontalRule','Smiley','SpecialChar','PageBreak','Iframe' ] },\r
'/',\r
- ['Styles','Format','Font','FontSize'],\r
- ['TextColor','BGColor'],\r
- ['Maximize', 'ShowBlocks','-','About']\r
+ { name: 'styles', items : [ 'Styles','Format','Font','FontSize' ] },\r
+ { name: 'colors', items : [ 'TextColor','BGColor' ] },\r
+ { name: 'tools', items : [ 'Maximize', 'ShowBlocks','-','About' ] }\r
];\r
\r
/**\r
* @example\r
* config.toolbarStartupExpanded = false;\r
*/\r
+\r
+/**\r
+ * When enabled, makes the arrow keys navigation cycle within the current\r
+ * toolbar group. Otherwise the arrows will move trought all items available in\r
+ * the toolbar. The TAB key will still be used to quickly jump among the\r
+ * toolbar groups.\r
+ * @name CKEDITOR.config.toolbarGroupCycling\r
+ * @since 3.6\r
+ * @type Boolean\r
+ * @default true\r
+ * @example\r
+ * config.toolbarGroupCycling = false;\r
+ */\r