      1 ﻿/*
      2 Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
      3 For licensing, see LICENSE.html or http://ckeditor.com/license
      4 */
      5
      6 (function()
      7 {
      8 	var blurCommand =
      9 		{
     10 			exec : function( editor )
     11 			{
     12 				editor.container.focusNext( true );
     13 			}
     14 		};
     15
     16 	var blurBackCommand =
     17 		{
     18 			exec : function( editor )
     19 			{
     20 				editor.container.focusPrevious( true );
     21 			}
     22 		};
     23
     24 	CKEDITOR.plugins.add( 'tab',
     25 	{
     26 		requires : [ 'keystrokes' ],
     27
     28 		init : function( editor )
     29 		{
     30 			// Register the keystrokes.
     31 			var keystrokes = editor.keystrokeHandler.keystrokes;
     32 			keystrokes[ 9 /* TAB */ ] = 'tab';
     33 			keystrokes[ CKEDITOR.SHIFT + 9 /* TAB */ ] = 'shiftTab';
     34
     35 			var tabSpaces = editor.config.tabSpaces,
     36 				tabText = '';
     37
     38 			while ( tabSpaces-- )
     39 				tabText += '\xa0';
     40
     41 			// Register the "tab" and "shiftTab" commands.
     42 			editor.addCommand( 'tab',
     43 				{
     44 					exec : function( editor )
     45 					{
     46 						// Fire the "tab" event, making it possible to
     47 						// customize the TAB key behavior on specific cases.
     48 						if ( !editor.fire( 'tab' ) )
     49 						{
     50 							if ( tabText.length > 0 )
     51 								editor.insertHtml( tabText );
     52 							else
     53 							{
     54 								// All browsers jump to the next field on TAB,
     55 								// except Safari, so we have to do that manually
     56 								// here.
     57 								/// https://bugs.webkit.org/show_bug.cgi?id=20597
     58 								return editor.execCommand( 'blur' );
     59 							}
     60 						}
     61
     62 						return true;
     63 					}
     64 				});
     65
     66 			editor.addCommand( 'shiftTab',
     67 				{
     68 					exec : function( editor )
     69 					{
     70 						// Fire the "tab" event, making it possible to
     71 						// customize the TAB key behavior on specific cases.
     72 						if ( !editor.fire( 'shiftTab' ) )
     73 							return editor.execCommand( 'blurBack' );
     74
     75 						return true;
     76 					}
     77 				});
     78
     79 			editor.addCommand( 'blur', blurCommand );
     80 			editor.addCommand( 'blurBack', blurBackCommand );
     81 		}
     82 	});
     83 })();
     84
     85 /**
     86  * Moves the UI focus to the element following this element in the tabindex
     87  * order.
     88  * @example
     89  * var element = CKEDITOR.document.getById( 'example' );
     90  * element.focusNext();
     91  */
     92 CKEDITOR.dom.element.prototype.focusNext = function( ignoreChildren )
     93 {
     94 	var $ = this.$,
     95 		curTabIndex = this.getTabIndex(),
     96 		passedCurrent, enteredCurrent,
     97 		elected, electedTabIndex,
     98 		element, elementTabIndex;
     99
    100 	if ( curTabIndex <= 0 )
    101 	{
    102 		// If this element has tabindex <= 0 then we must simply look for any
    103 		// element following it containing tabindex=0.
    104
    105 		element = this.getNextSourceNode( ignoreChildren, CKEDITOR.NODE_ELEMENT );
    106
    107 		while( element )
    108 		{
    109 			if ( element.isVisible() && element.getTabIndex() === 0 )
    110 			{
    111 				elected = element;
    112 				break;
    113 			}
    114
    115 			element = element.getNextSourceNode( false, CKEDITOR.NODE_ELEMENT );
    116 		}
    117 	}
    118 	else
    119 	{
    120 		// If this element has tabindex > 0 then we must look for:
    121 		//		1. An element following this element with the same tabindex.
    122 		//		2. The first element in source other with the lowest tabindex
    123 		//		   that is higher than this element tabindex.
    124 		//		3. The first element with tabindex=0.
    125
    126 		element = this.getDocument().getBody().getFirst();
    127
    128 		while( ( element = element.getNextSourceNode( false, CKEDITOR.NODE_ELEMENT ) ) )
    129 		{
    130 			if ( !passedCurrent )
    131 			{
    132 				if ( !enteredCurrent && element.equals( this ) )
    133 				{
    134 					enteredCurrent = true;
    135
    136 					// Ignore this element, if required.
    137 					if ( ignoreChildren )
    138 					{
    139 						if ( !( element = element.getNextSourceNode( true, CKEDITOR.NODE_ELEMENT ) ) )
    140 							break;
    141 						passedCurrent = 1;
    142 					}
    143 				}
    144 				else if ( enteredCurrent && !this.contains( element ) )
    145 					passedCurrent = 1;
    146 			}
    147
    148 			if ( !element.isVisible() || ( elementTabIndex = element.getTabIndex() ) < 0 )
    149 				continue;
    150
    151 			if ( passedCurrent && elementTabIndex == curTabIndex )
    152 			{
    153 				elected = element;
    154 				break;
    155 			}
    156
    157 			if ( elementTabIndex > curTabIndex && ( !elected || !electedTabIndex || elementTabIndex < electedTabIndex ) )
    158 			{
    159 				elected = element;
    160 				electedTabIndex = elementTabIndex;
    161 			}
    162 			else if ( !elected && elementTabIndex === 0 )
    163 			{
    164 				elected = element;
    165 				electedTabIndex = elementTabIndex;
    166 			}
    167 		}
    168 	}
    169
    170 	if ( elected )
    171 		elected.focus();
    172 };
    173
    174 /**
    175  * Moves the UI focus to the element before this element in the tabindex order.
    176  * @example
    177  * var element = CKEDITOR.document.getById( 'example' );
    178  * element.focusPrevious();
    179  */
    180 CKEDITOR.dom.element.prototype.focusPrevious = function( ignoreChildren )
    181 {
    182 	var $ = this.$,
    183 		curTabIndex = this.getTabIndex(),
    184 		passedCurrent, enteredCurrent,
    185 		elected,
    186 		electedTabIndex = 0,
    187 		elementTabIndex;
    188
    189 	var element = this.getDocument().getBody().getLast();
    190
    191 	while( ( element = element.getPreviousSourceNode( false, CKEDITOR.NODE_ELEMENT ) ) )
    192 	{
    193 		if ( !passedCurrent )
    194 		{
    195 			if ( !enteredCurrent && element.equals( this ) )
    196 			{
    197 				enteredCurrent = true;
    198
    199 				// Ignore this element, if required.
    200 				if ( ignoreChildren )
    201 				{
    202 					if ( !( element = element.getPreviousSourceNode( true, CKEDITOR.NODE_ELEMENT ) ) )
    203 						break;
    204 					passedCurrent = 1;
    205 				}
    206 			}
    207 			else if ( enteredCurrent && !this.contains( element ) )
    208 				passedCurrent = 1;
    209 		}
    210
    211 		if ( !element.isVisible() || ( elementTabIndex = element.getTabIndex() ) < 0 )
    212 			continue;
    213
    214 		if ( curTabIndex <= 0 )
    215 		{
    216 			// If this element has tabindex <= 0 then we must look for:
    217 			//		1. An element before this one containing tabindex=0.
    218 			//		2. The last element with the highest tabindex.
    219
    220 			if ( passedCurrent && elementTabIndex === 0 )
    221 			{
    222 				elected = element;
    223 				break;
    224 			}
    225
    226 			if ( elementTabIndex > electedTabIndex )
    227 			{
    228 				elected = element;
    229 				electedTabIndex = elementTabIndex;
    230 			}
    231 		}
    232 		else
    233 		{
    234 			// If this element has tabindex > 0 we must look for:
    235 			//		1. An element preceeding this one, with the same tabindex.
    236 			//		2. The last element in source other with the highest tabindex
    237 			//		   that is lower than this element tabindex.
    238
    239 			if ( passedCurrent && elementTabIndex == curTabIndex )
    240 			{
    241 				elected = element;
    242 				break;
    243 			}
    244
    245 			if ( elementTabIndex < curTabIndex && ( !elected || elementTabIndex > electedTabIndex ) )
    246 			{
    247 				elected = element;
    248 				electedTabIndex = elementTabIndex;
    249 			}
    250 		}
    251 	}
    252
    253 	if ( elected )
    254 		elected.focus();
    255 };
    256
    257 /**
    258  * Intructs the editor to add a number of spaces (&nbsp;) to the text when
    259  * hitting the TAB key. If set to zero, the TAB key will have its default
    260  * behavior instead (like moving out of the editor).
    261  * @type {Number}
    262  * @default 0
    263  * @example
    264  * config.tabSpaces = 4;
    265  */
    266 CKEDITOR.config.tabSpaces = 0 ;
    267