2 Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
3 For licensing, see LICENSE.html or http://ckeditor.com/license
11 modes
: { wysiwyg
:1, source
:1 }
16 exec : function( editor
)
18 editor
.container
.focusNext( true, editor
.tabIndex
);
24 exec : function( editor
)
26 editor
.container
.focusPrevious( true, editor
.tabIndex
);
30 function selectNextCellCommand( backward
)
35 modes
: { wysiwyg
: 1 },
36 exec : function( editor
)
38 if ( editor
.focusManager
.hasFocus
)
40 var sel
= editor
.getSelection(),
41 ancestor
= sel
.getCommonAncestor(),
44 if ( ( cell
= ( ancestor
.getAscendant( 'td', true ) || ancestor
.getAscendant( 'th', true ) ) ) )
46 var resultRange
= new CKEDITOR
.dom
.range( editor
.document
),
47 next
= CKEDITOR
.tools
.tryThese( function()
49 var row
= cell
.getParent(),
50 next
= row
.$.cells
[ cell
.$.cellIndex
+ ( backward
? - 1 : 1 ) ];
52 // Invalid any empty value.
53 next
.parentNode
.parentNode
;
58 var row
= cell
.getParent(),
59 table
= row
.getAscendant( 'table' ),
60 nextRow
= table
.$.rows
[ row
.$.rowIndex
+ ( backward
? - 1 : 1 ) ];
62 return nextRow
.cells
[ backward
? nextRow
.cells
.length
-1 : 0 ];
65 // Clone one more row at the end of table and select the first newly established cell.
66 if ( ! ( next
|| backward
) )
68 var table
= cell
.getAscendant( 'table' ).$,
69 cells
= cell
.getParent().$.cells
;
71 var newRow
= new CKEDITOR
.dom
.element( table
.insertRow( -1 ), editor
.document
);
73 for ( var i
= 0, count
= cells
.length
; i
< count
; i
++ )
75 var newCell
= newRow
.append( new CKEDITOR
.dom
.element(
76 cells
[ i
], editor
.document
).clone( false, false ) );
77 !CKEDITOR
.env
.ie
&& newCell
.appendBogus();
80 resultRange
.moveToElementEditStart( newRow
);
84 next
= new CKEDITOR
.dom
.element( next
);
85 resultRange
.moveToElementEditStart( next
);
86 // Avoid selecting empty block makes the cursor blind.
87 if ( !( resultRange
.checkStartOfBlock() && resultRange
.checkEndOfBlock() ) )
88 resultRange
.selectNodeContents( next
);
93 resultRange
.select( true );
102 CKEDITOR
.plugins
.add( 'tab',
104 requires
: [ 'keystrokes' ],
106 init : function( editor
)
108 var tabTools
= editor
.config
.enableTabKeyTools
!== false,
109 tabSpaces
= editor
.config
.tabSpaces
|| 0,
112 while ( tabSpaces
-- )
117 editor
.on( 'key', function( ev
)
119 if ( ev
.data
.keyCode
== 9 ) // TAB
121 editor
.insertHtml( tabText
);
129 editor
.on( 'key', function( ev
)
131 if ( ev
.data
.keyCode
== 9 && editor
.execCommand( 'selectNextCell' ) || // TAB
132 ev
.data
.keyCode
== ( CKEDITOR
.SHIFT
+ 9 ) && editor
.execCommand( 'selectPreviousCell' ) ) // SHIFT+TAB
137 if ( CKEDITOR
.env
.webkit
|| CKEDITOR
.env
.gecko
)
139 editor
.on( 'key', function( ev
)
141 var keyCode
= ev
.data
.keyCode
;
143 if ( keyCode
== 9 && !tabText
) // TAB
146 editor
.execCommand( 'blur' );
149 if ( keyCode
== ( CKEDITOR
.SHIFT
+ 9 ) ) // SHIFT+TAB
151 editor
.execCommand( 'blurBack' );
157 editor
.addCommand( 'blur', CKEDITOR
.tools
.extend( blurCommand
, meta
) );
158 editor
.addCommand( 'blurBack', CKEDITOR
.tools
.extend( blurBackCommand
, meta
) );
159 editor
.addCommand( 'selectNextCell', selectNextCellCommand() );
160 editor
.addCommand( 'selectPreviousCell', selectNextCellCommand( true ) );
166 * Moves the UI focus to the element following this element in the tabindex
169 * var element = CKEDITOR.document.getById( 'example' );
170 * element.focusNext();
172 CKEDITOR
.dom
.element
.prototype.focusNext = function( ignoreChildren
, indexToUse
)
175 curTabIndex
= ( indexToUse
=== undefined ? this.getTabIndex() : indexToUse
),
176 passedCurrent
, enteredCurrent
,
177 elected
, electedTabIndex
,
178 element
, elementTabIndex
;
180 if ( curTabIndex
<= 0 )
182 // If this element has tabindex <= 0 then we must simply look for any
183 // element following it containing tabindex=0.
185 element
= this.getNextSourceNode( ignoreChildren
, CKEDITOR
.NODE_ELEMENT
);
189 if ( element
.isVisible() && element
.getTabIndex() === 0 )
195 element
= element
.getNextSourceNode( false, CKEDITOR
.NODE_ELEMENT
);
200 // If this element has tabindex > 0 then we must look for:
201 // 1. An element following this element with the same tabindex.
202 // 2. The first element in source other with the lowest tabindex
203 // that is higher than this element tabindex.
204 // 3. The first element with tabindex=0.
206 element
= this.getDocument().getBody().getFirst();
208 while ( ( element
= element
.getNextSourceNode( false, CKEDITOR
.NODE_ELEMENT
) ) )
210 if ( !passedCurrent
)
212 if ( !enteredCurrent
&& element
.equals( this ) )
214 enteredCurrent
= true;
216 // Ignore this element, if required.
217 if ( ignoreChildren
)
219 if ( !( element
= element
.getNextSourceNode( true, CKEDITOR
.NODE_ELEMENT
) ) )
224 else if ( enteredCurrent
&& !this.contains( element
) )
228 if ( !element
.isVisible() || ( elementTabIndex
= element
.getTabIndex() ) < 0 )
231 if ( passedCurrent
&& elementTabIndex
== curTabIndex
)
237 if ( elementTabIndex
> curTabIndex
&& ( !elected
|| !electedTabIndex
|| elementTabIndex
< electedTabIndex
) )
240 electedTabIndex
= elementTabIndex
;
242 else if ( !elected
&& elementTabIndex
=== 0 )
245 electedTabIndex
= elementTabIndex
;
255 * Moves the UI focus to the element before this element in the tabindex order.
257 * var element = CKEDITOR.document.getById( 'example' );
258 * element.focusPrevious();
260 CKEDITOR
.dom
.element
.prototype.focusPrevious = function( ignoreChildren
, indexToUse
)
263 curTabIndex
= ( indexToUse
=== undefined ? this.getTabIndex() : indexToUse
),
264 passedCurrent
, enteredCurrent
,
269 var element
= this.getDocument().getBody().getLast();
271 while ( ( element
= element
.getPreviousSourceNode( false, CKEDITOR
.NODE_ELEMENT
) ) )
273 if ( !passedCurrent
)
275 if ( !enteredCurrent
&& element
.equals( this ) )
277 enteredCurrent
= true;
279 // Ignore this element, if required.
280 if ( ignoreChildren
)
282 if ( !( element
= element
.getPreviousSourceNode( true, CKEDITOR
.NODE_ELEMENT
) ) )
287 else if ( enteredCurrent
&& !this.contains( element
) )
291 if ( !element
.isVisible() || ( elementTabIndex
= element
.getTabIndex() ) < 0 )
294 if ( curTabIndex
<= 0 )
296 // If this element has tabindex <= 0 then we must look for:
297 // 1. An element before this one containing tabindex=0.
298 // 2. The last element with the highest tabindex.
300 if ( passedCurrent
&& elementTabIndex
=== 0 )
306 if ( elementTabIndex
> electedTabIndex
)
309 electedTabIndex
= elementTabIndex
;
314 // If this element has tabindex > 0 we must look for:
315 // 1. An element preceeding this one, with the same tabindex.
316 // 2. The last element in source other with the highest tabindex
317 // that is lower than this element tabindex.
319 if ( passedCurrent
&& elementTabIndex
== curTabIndex
)
325 if ( elementTabIndex
< curTabIndex
&& ( !elected
|| elementTabIndex
> electedTabIndex
) )
328 electedTabIndex
= elementTabIndex
;
338 * Intructs the editor to add a number of spaces (&nbsp;) to the text when
339 * hitting the TAB key. If set to zero, the TAB key will be used to move the
340 * cursor focus to the next element in the page, out of the editor focus.
341 * @name CKEDITOR.config.tabSpaces
345 * config.tabSpaces = 4;
349 * Allow context-sensitive tab key behaviors, including the following scenarios:
350 * <h5>When selection is anchored inside <b>table cells</b>:</h5>
352 * <li>If TAB is pressed, select the contents of the "next" cell. If in the last cell in the table, add a new row to it and focus its first cell.</li>
353 * <li>If SHIFT+TAB is pressed, select the contents of the "previous" cell. Do nothing when it's in the first cell.</li>
355 * @name CKEDITOR.config.enableTabKeyTools
359 * config.enableTabKeyTools = false;
362 // If the TAB key is not supposed to be enabled for navigation, the following
363 // settings could be used alternatively:
364 // config.keystrokes.push(
365 // [ CKEDITOR.ALT + 38 /*Arrow Up*/, 'selectPreviousCell' ],
366 // [ CKEDITOR.ALT + 40 /*Arrow Down*/, 'selectNextCell' ]