JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
vanilla ckeditor-3.6.3
[ckeditor.git] / _source / plugins / toolbar / plugin.js
index c1701a4..c21e6f2 100644 (file)
@@ -1,5 +1,5 @@
 /*\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
@@ -36,6 +36,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                toolbarFocus :\r
                {\r
                        modes : { wysiwyg : 1, source : 1 },\r
+                       readOnly : 1,\r
 \r
                        exec : function( editor )\r
                        {\r
@@ -43,8 +44,9 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                {\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
@@ -57,39 +59,95 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
        {\r
                init : function( editor )\r
                {\r
+                       var endFlag;\r
+\r
                        var itemKeystroke = function( item, keystroke )\r
                        {\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 39 :                                       // RIGHT-ARROW\r
                                        case 9 :                                        // TAB\r
-                                               // Look for the next item in the toolbar.\r
-                                               while ( ( item = item.next || ( item.toolbar.next && item.toolbar.next.items[ 0 ] ) ) && !item.focus )\r
-                                               { /*jsl:pass*/ }\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
+                                                       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
+                                                       // Look for the first item that accepts focus.\r
+                                                       if ( toolbar.items.length )\r
+                                                       {\r
+                                                               item = toolbar.items[ endFlag ? ( toolbar.items.length - 1 ) : 0 ];\r
+                                                               while ( item && !item.focus )\r
+                                                               {\r
+                                                                       item = endFlag ? item.previous : item.next;\r
+\r
+                                                                       if ( !item )\r
+                                                                               toolbar = 0;\r
+                                                               }\r
+                                                       }\r
+                                               }\r
 \r
-                                               // If available, just focus it, otherwise focus the\r
-                                               // first one.\r
                                                if ( item )\r
                                                        item.focus();\r
+\r
+                                               return false;\r
+\r
+                                       case rtl ? 37 : 39 :            // RIGHT-ARROW\r
+                                       case 40 :                                       // DOWN-ARROW\r
+                                               next = item;\r
+                                               do\r
+                                               {\r
+                                                       // Look for the next item in the toolbar.\r
+                                                       next = next.next;\r
+\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
+                                               // If available, just focus it, otherwise focus the\r
+                                               // first one.\r
+                                               if ( next )\r
+                                                       next.focus();\r
                                                else\r
-                                                       editor.toolbox.focus();\r
+                                                       // Send a TAB.\r
+                                                       itemKeystroke( item, 9 );\r
 \r
                                                return false;\r
 \r
-                                       case 37 :                                       // LEFT-ARROW\r
-                                       case CKEDITOR.SHIFT + 9 :       // SHIFT + TAB\r
-                                               // Look for the previous item in the toolbar.\r
-                                               while ( ( item = item.previous || ( item.toolbar.previous && item.toolbar.previous.items[ item.toolbar.previous.items.length - 1 ] ) ) && !item.focus )\r
-                                               { /*jsl:pass*/ }\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
+                                                       // 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 ( 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
@@ -112,12 +170,17 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                        {\r
                                                editor.toolbox = new toolbox();\r
 \r
-                                               var output = [ '<div class="cke_toolbox"' ],\r
+                                               var labelId = CKEDITOR.tools.getNextId();\r
+\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.toolbars, '</span>' );\r
+\r
                                                var toolbars = editor.toolbox.toolbars,\r
                                                        toolbar =\r
                                                                        ( editor.config.toolbar instanceof Array ) ?\r
@@ -127,7 +190,11 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
 \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
@@ -137,9 +204,6 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                                        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
@@ -152,37 +216,56 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                                                continue;\r
                                                        }\r
 \r
-                                                       output.push( '<span id="', toolbarId, '" class="cke_toolbar"><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
-                                                                                       output.push( '<span class="cke_toolgroup">' );\r
+                                                                                       output.push( '<span class="cke_toolgroup" role="presentation">' );\r
                                                                                        groupStarted = 1;\r
                                                                                }\r
                                                                        }\r
@@ -222,7 +305,8 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                                                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
@@ -233,22 +317,28 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                                                function()\r
                                                                {\r
                                                                        editor.execCommand( 'toolbarCollapse' );\r
-                                                               } );\r
+                                                               });\r
+\r
+                                                       editor.on( 'destroy', function () {\r
+                                                                       CKEDITOR.tools.removeFunction( collapserFn );\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
-\r
-                                                                               if ( 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
                                                                                        toolbox.hide();\r
                                                                                        collapser.addClass( 'cke_toolbox_collapser_min' );\r
@@ -261,6 +351,11 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                                                                        collapser.setAttribute( 'title', editor.lang.toolbarCollapse );\r
                                                                                }\r
 \r
+                                                                               // Update collapser symbol.\r
+                                                                               collapser.getFirst().setText( collapsed ?\r
+                                                                                       '\u25B2' :              // BLACK UP-POINTING TRIANGLE\r
+                                                                                       '\u25C0' );             // BLACK LEFT-POINTING TRIANGLE\r
+\r
                                                                                var dy = toolboxContainer.$.offsetHeight - previousHeight;\r
                                                                                contents.setStyle( 'height', ( contentHeight - dy ) + 'px' );\r
 \r
@@ -271,36 +366,58 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                                                } );\r
 \r
                                                        output.push( '<a title="' + ( expanded ? editor.lang.toolbarCollapse : editor.lang.toolbarExpand )\r
-                                                                                                         + '" id="' + collapserId + '" class="cke_toolbox_collapser' );\r
+                                                                                                         + '" id="' + collapserId + '" tabIndex="-1" class="cke_toolbox_collapser' );\r
 \r
                                                        if ( !expanded )\r
                                                                output.push( ' cke_toolbox_collapser_min' );\r
 \r
-                                                       output.push( '" onclick="CKEDITOR.tools.callFunction(' + collapserFn + ')"></a>' );\r
+                                                       output.push( '" onclick="CKEDITOR.tools.callFunction(' + collapserFn + ')">',\r
+                                                                               '<span>&#9650;</span>',         // BLACK UP-POINTING TRIANGLE\r
+                                                                               '</a>' );\r
                                                }\r
 \r
                                                event.data.html += output.join( '' );\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"></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
@@ -345,38 +462,36 @@ CKEDITOR.config.toolbar_Basic =
  * // 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'],\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
@@ -415,3 +530,16 @@ CKEDITOR.config.toolbarCanCollapse = true;
  * @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