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 blurInternal = function( editor, previous )
  9 	{
 10 		var hasContainer = editor.container;
 11
 12 		if ( hasContainer )
 13 		{
 14 			// We need an empty element after the container, so the focus don't go to a container child.
 15 			var tempSpan = new CKEDITOR.dom.element( 'span' );
 16 			tempSpan.setAttribute( 'tabindex', editor.container.getTabIndex() );
 17 			tempSpan.hide();
 18
 19 			// Insert the temp element and set the focus.
 20 			if ( previous )
 21 			{
 22 				tempSpan.insertBefore( editor.container );
 23 				tempSpan.focusPrevious();
 24 			}
 25 			else
 26 			{
 27 				tempSpan.insertAfter( editor.container );
 28 				tempSpan.focusNext();
 29 			}
 30
 31 			// Remove the temporary node.
 32 			tempSpan.remove();
 33 		}
 34
 35 		return hasContainer;
 36 	};
 37
 38 	var blurCommand =
 39 		{
 40 			exec : function( editor )
 41 			{
 42 				return blurInternal( editor );
 43 			}
 44 		};
 45
 46 	var blurBackCommand =
 47 		{
 48 			exec : function( editor )
 49 			{
 50 				return blurInternal( editor, true );
 51 			}
 52 		};
 53
 54 	CKEDITOR.plugins.add( 'tab',
 55 	{
 56 		requires : [ 'keystrokes' ],
 57
 58 		init : function( editor, pluginPath )
 59 		{
 60 			// Register the keystrokes.
 61 			var keystrokes = editor.keystrokeHandler.keystrokes;
 62 			keystrokes[ 9 /* TAB */ ] = 'tab';
 63 			keystrokes[ CKEDITOR.SHIFT + 9 /* TAB */ ] = 'shiftTab';
 64
 65 			var tabSpaces = editor.config.tabSpaces,
 66 				tabText = '';
 67
 68 			while ( tabSpaces-- )
 69 				tabText += '\xa0';
 70
 71 			// Register the "tab" and "shiftTab" commands.
 72 			editor.addCommand( 'tab',
 73 				{
 74 					exec : function( editor )
 75 					{
 76 						// Fire the "tab" event, making it possible to
 77 						// customize the TAB key behavior on specific cases.
 78 						if ( !editor.fire( 'tab' ) )
 79 						{
 80 							if ( tabText.length > 0 )
 81 							{
 82 								// TODO
 83 								/*jsl:pass*/
 84 							}
 85 							else
 86 							{
 87 								// All browsers jump to the next field on TAB,
 88 								// except Safari, so we have to do that manually
 89 								// here.
 90 								/// https://bugs.webkit.org/show_bug.cgi?id=20597
 91 								return editor.execCommand( 'blur' );
 92 							}
 93 						}
 94
 95 						return true;
 96 					}
 97 				});
 98
 99 			editor.addCommand( 'shiftTab',
100 				{
101 					exec : function( editor )
102 					{
103 						// Fire the "tab" event, making it possible to
104 						// customize the TAB key behavior on specific cases.
105 						if ( !editor.fire( 'shiftTab' ) )
106 							return editor.execCommand( 'blurBack' );
107
108 						return true;
109 					}
110 				});
111
112 			editor.addCommand( 'blur', blurCommand );
113 			editor.addCommand( 'blurBack', blurBackCommand );
114 		}
115 	});
116 })();
117
118 /**
119  * Moves the UI focus to the element following this element in the tabindex
120  * order.
121  * @example
122  * var element = CKEDITOR.document.getById( 'example' );
123  * element.focusNext();
124  */
125 CKEDITOR.dom.element.prototype.focusNext = function()
126 {
127 	var $ = this.$,
128 		curTabIndex = this.getTabIndex(),
129 		passedCurrent = false,
130 		elected,
131 		electedTabIndex;
132
133 	var all = document.body.all || document.body.getElementsByTagName( '*' );
134
135 	if ( curTabIndex <= 0 )
136 	{
137 		for ( var i = 0, element ; element = all[ i ] ; i++ )
138 		{
139 			if ( !passedCurrent )
140 			{
141 				if ( element == $ )
142 					passedCurrent = true;
143 				continue;
144 			}
145
146 			element = new CKEDITOR.dom.element( element );
147
148 			if ( element.getComputedStyle( 'display' ) == 'none' || element.getComputedStyle( 'visibility' ) == 'hidden' )
149 				continue;
150
151 			if ( element.getTabIndex() === 0 )
152 			{
153 				elected = element;
154 				break;
155 			}
156 		}
157 	}
158 	else
159 	{
160 		for ( i = 0, element ; element = all[ i ] ; i++ )
161 		{
162 			if ( !passedCurrent && element == $ )
163 			{
164 				passedCurrent = true;
165 				continue;
166 			}
167
168 			element = new CKEDITOR.dom.element( element );
169
170 			if ( element.getComputedStyle( 'display' ) == 'none' || element.getComputedStyle( 'visibility' ) == 'hidden' )
171 				continue;
172
173 			var elementTabIndex = element.getTabIndex();
174
175 			if ( passedCurrent && elementTabIndex == curTabIndex )
176 			{
177 				elected = element;
178 				break;
179 			}
180 			else if ( elementTabIndex > curTabIndex && ( !elected || electedTabIndex > elementTabIndex || electedTabIndex === 0 ) )
181 			{
182 				elected = element;
183 				electedTabIndex = elementTabIndex;
184 			}
185 			else if ( !elected && elementTabIndex === 0 )
186 			{
187 				elected = element;
188 				electedTabIndex = elementTabIndex;
189 			}
190 		}
191 	}
192
193 	if ( elected )
194 		elected.focus();
195 };
196
197 /**
198  * Moves the UI focus to the element before this element in the tabindex order.
199  * @example
200  * var element = CKEDITOR.document.getById( 'example' );
201  * element.focusPrevious();
202  */
203 CKEDITOR.dom.element.prototype.focusPrevious = function()
204 {
205 	var $ = this.$,
206 		curTabIndex = this.getTabIndex(),
207 		passedCurrent = false,
208 		elected,
209 		electedTabIndex;
210
211 	var all = document.body.all || document.body.getElementsByTagName( '*' );
212
213 	if ( curTabIndex <= 0 )
214 	{
215 		for ( var i = 0, element ; element = all[ i ] ; i++ )
216 		{
217 			if ( !passedCurrent && element == $ )
218 			{
219 				if ( elected && electedTabIndex === 0 )
220 					break;
221
222 				passedCurrent = true;
223 				continue;
224 			}
225
226 			element = new CKEDITOR.dom.element( element );
227
228 			if ( element.getComputedStyle( 'display' ) == 'none' || element.getComputedStyle( 'visibility' ) == 'hidden' )
229 				continue;
230
231 			var elementTabIndex = element.getTabIndex();
232
233 			if ( ( !passedCurrent && elementTabIndex === 0 )
234 				|| ( elementTabIndex > 0 && ( !elected || ( electedTabIndex > 0 && electedTabIndex <= elementTabIndex ) ) ) )
235 			{
236 				elected = element;
237 				electedTabIndex = elementTabIndex;
238 			}
239 		}
240 	}
241 	else
242 	{
243 		for ( i = 0, element ; element = all[ i ] ; i++ )
244 		{
245 			if ( !passedCurrent && element == $ )
246 			{
247 				if ( elected && electedTabIndex == curTabIndex )
248 					break;
249
250 				passedCurrent = true;
251 				continue;
252 			}
253
254 			element = new CKEDITOR.dom.element( element );
255
256 			elementTabIndex = element.getTabIndex();
257
258 			if ( elementTabIndex > 0 )
259 			{
260 				if ( ( !passedCurrent && elementTabIndex == curTabIndex )
261 					|| ( elementTabIndex < curTabIndex && ( !elected || electedTabIndex <= elementTabIndex ) ) )
262 				{
263 					elected = element;
264 					electedTabIndex = elementTabIndex;
265 				}
266 			}
267 		}
268 	}
269
270 	if ( elected )
271 		elected.focus();
272 };
273
274 /**
275  * Intructs the editor to add a number of spaces (&nbsp;) to the text when
276  * hitting the TAB key. If set to zero, the TAB key will have its default
277  * behavior instead (like moving out of the editor).
278  * @type {Number}
279  * @default 0
280  * @example
281  * config.tabSpaces = 4;
282  */
283 CKEDITOR.config.tabSpaces = 0 ;
284