initial commit
[namibia] / public / scripts / ckeditor / _source / plugins / tab / plugin.js
1 /*
2 Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
3 For licensing, see LICENSE.html or http://ckeditor.com/license
4 */
5
6 (function()
7 {
8         var meta =
9         {
10                 editorFocus : false,
11                 modes : { wysiwyg:1, source:1 }
12         };
13
14         var blurCommand =
15                 {
16                         exec : function( editor )
17                         {
18                                 editor.container.focusNext( true, editor.tabIndex );
19                         }
20                 };
21
22         var blurBackCommand =
23                 {
24                         exec : function( editor )
25                         {
26                                 editor.container.focusPrevious( true, editor.tabIndex );
27                         }
28                 };
29
30         function selectNextCellCommand( backward )
31         {
32                 return {
33                         editorFocus : false,
34                         canUndo : false,
35                         modes : { wysiwyg : 1 },
36                         exec : function( editor )
37                         {
38                                 if ( editor.focusManager.hasFocus )
39                                 {
40                                         var sel = editor.getSelection(),
41                                                 ancestor = sel.getCommonAncestor(),
42                                                 cell;
43
44                                         if ( ( cell = ( ancestor.getAscendant( 'td', true ) || ancestor.getAscendant( 'th', true ) ) ) )
45                                         {
46                                                 var resultRange = new CKEDITOR.dom.range( editor.document ),
47                                                                 next = CKEDITOR.tools.tryThese( function()
48                                                                 {
49                                                                         var row = cell.getParent(),
50                                                                                         next = row.$.cells[ cell.$.cellIndex + ( backward ? - 1 : 1 ) ];
51
52                                                                         // Invalid any empty value.
53                                                                         next.parentNode.parentNode;
54                                                                         return next;
55                                                                 },
56                                                                 function()
57                                                                 {
58                                                                         var row = cell.getParent(),
59                                                                                         table = row.getAscendant( 'table' ),
60                                                                                         nextRow = table.$.rows[ row.$.rowIndex + ( backward ? - 1 : 1 ) ];
61
62                                                                         return nextRow.cells[ backward? nextRow.cells.length -1 : 0 ];
63                                                                 });
64
65                                                 // Clone one more row at the end of table and select the first newly established cell.
66                                                 if ( ! ( next || backward ) )
67                                                 {
68                                                         var table = cell.getAscendant( 'table' ).$,
69                                                                         cells = cell.getParent().$.cells;
70
71                                                         var newRow = new CKEDITOR.dom.element( table.insertRow( -1 ), editor.document );
72
73                                                         for ( var i = 0, count = cells.length ; i < count; i++ )
74                                                         {
75                                                                 var newCell = newRow.append( new CKEDITOR.dom.element(
76                                                                                 cells[ i ], editor.document ).clone( false, false ) );
77                                                                 !CKEDITOR.env.ie && newCell.appendBogus();
78                                                         }
79
80                                                         resultRange.moveToElementEditStart( newRow );
81                                                 }
82                                                 else if ( next )
83                                                 {
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 );
89                                                 }
90                                                 else
91                                                         return true;
92
93                                                 resultRange.select( true );
94                                                 return true;
95                                         }
96                                 }
97                                 return false;
98                         }
99                 };
100         }
101
102         CKEDITOR.plugins.add( 'tab',
103         {
104                 requires : [ 'keystrokes' ],
105
106                 init : function( editor )
107                 {
108                         var tabTools = editor.config.enableTabKeyTools !== false,
109                                 tabSpaces = editor.config.tabSpaces || 0,
110                                 tabText = '';
111
112                         while ( tabSpaces-- )
113                                 tabText += '\xa0';
114
115                         if ( tabText )
116                         {
117                                 editor.on( 'key', function( ev )
118                                         {
119                                                 if ( ev.data.keyCode == 9 )     // TAB
120                                                 {
121                                                         editor.insertHtml( tabText );
122                                                         ev.cancel();
123                                                 }
124                                         });
125                         }
126
127                         if ( tabTools )
128                         {
129                                 editor.on( 'key', function( ev )
130                                 {
131                                         if ( ev.data.keyCode == 9 && editor.execCommand( 'selectNextCell' ) ||  // TAB
132                                                         ev.data.keyCode == ( CKEDITOR.SHIFT + 9 ) && editor.execCommand( 'selectPreviousCell' ) )       // SHIFT+TAB
133                                                 ev.cancel();
134                                 });
135                         }
136
137                         if ( CKEDITOR.env.webkit || CKEDITOR.env.gecko )
138                         {
139                                 editor.on( 'key', function( ev )
140                                         {
141                                                 var keyCode = ev.data.keyCode;
142
143                                                 if ( keyCode == 9 && !tabText )                         // TAB
144                                                 {
145                                                         ev.cancel();
146                                                         editor.execCommand( 'blur' );
147                                                 }
148
149                                                 if ( keyCode == ( CKEDITOR.SHIFT + 9 ) )        // SHIFT+TAB
150                                                 {
151                                                         editor.execCommand( 'blurBack' );
152                                                         ev.cancel();
153                                                 }
154                                         });
155                         }
156
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 ) );
161                 }
162         });
163 })();
164
165 /**
166  * Moves the UI focus to the element following this element in the tabindex
167  * order.
168  * @example
169  * var element = CKEDITOR.document.getById( 'example' );
170  * element.focusNext();
171  */
172 CKEDITOR.dom.element.prototype.focusNext = function( ignoreChildren, indexToUse )
173 {
174         var $ = this.$,
175                 curTabIndex = ( indexToUse === undefined ? this.getTabIndex() : indexToUse ),
176                 passedCurrent, enteredCurrent,
177                 elected, electedTabIndex,
178                 element, elementTabIndex;
179
180         if ( curTabIndex <= 0 )
181         {
182                 // If this element has tabindex <= 0 then we must simply look for any
183                 // element following it containing tabindex=0.
184
185                 element = this.getNextSourceNode( ignoreChildren, CKEDITOR.NODE_ELEMENT );
186
187                 while ( element )
188                 {
189                         if ( element.isVisible() && element.getTabIndex() === 0 )
190                         {
191                                 elected = element;
192                                 break;
193                         }
194
195                         element = element.getNextSourceNode( false, CKEDITOR.NODE_ELEMENT );
196                 }
197         }
198         else
199         {
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.
205
206                 element = this.getDocument().getBody().getFirst();
207
208                 while ( ( element = element.getNextSourceNode( false, CKEDITOR.NODE_ELEMENT ) ) )
209                 {
210                         if ( !passedCurrent )
211                         {
212                                 if ( !enteredCurrent && element.equals( this ) )
213                                 {
214                                         enteredCurrent = true;
215
216                                         // Ignore this element, if required.
217                                         if ( ignoreChildren )
218                                         {
219                                                 if ( !( element = element.getNextSourceNode( true, CKEDITOR.NODE_ELEMENT ) ) )
220                                                         break;
221                                                 passedCurrent = 1;
222                                         }
223                                 }
224                                 else if ( enteredCurrent && !this.contains( element ) )
225                                         passedCurrent = 1;
226                         }
227
228                         if ( !element.isVisible() || ( elementTabIndex = element.getTabIndex() ) < 0 )
229                                 continue;
230
231                         if ( passedCurrent && elementTabIndex == curTabIndex )
232                         {
233                                 elected = element;
234                                 break;
235                         }
236
237                         if ( elementTabIndex > curTabIndex && ( !elected || !electedTabIndex || elementTabIndex < electedTabIndex ) )
238                         {
239                                 elected = element;
240                                 electedTabIndex = elementTabIndex;
241                         }
242                         else if ( !elected && elementTabIndex === 0 )
243                         {
244                                 elected = element;
245                                 electedTabIndex = elementTabIndex;
246                         }
247                 }
248         }
249
250         if ( elected )
251                 elected.focus();
252 };
253
254 /**
255  * Moves the UI focus to the element before this element in the tabindex order.
256  * @example
257  * var element = CKEDITOR.document.getById( 'example' );
258  * element.focusPrevious();
259  */
260 CKEDITOR.dom.element.prototype.focusPrevious = function( ignoreChildren, indexToUse )
261 {
262         var $ = this.$,
263                 curTabIndex = ( indexToUse === undefined ? this.getTabIndex() : indexToUse ),
264                 passedCurrent, enteredCurrent,
265                 elected,
266                 electedTabIndex = 0,
267                 elementTabIndex;
268
269         var element = this.getDocument().getBody().getLast();
270
271         while ( ( element = element.getPreviousSourceNode( false, CKEDITOR.NODE_ELEMENT ) ) )
272         {
273                 if ( !passedCurrent )
274                 {
275                         if ( !enteredCurrent && element.equals( this ) )
276                         {
277                                 enteredCurrent = true;
278
279                                 // Ignore this element, if required.
280                                 if ( ignoreChildren )
281                                 {
282                                         if ( !( element = element.getPreviousSourceNode( true, CKEDITOR.NODE_ELEMENT ) ) )
283                                                 break;
284                                         passedCurrent = 1;
285                                 }
286                         }
287                         else if ( enteredCurrent && !this.contains( element ) )
288                                 passedCurrent = 1;
289                 }
290
291                 if ( !element.isVisible() || ( elementTabIndex = element.getTabIndex() ) < 0 )
292                         continue;
293
294                 if ( curTabIndex <= 0 )
295                 {
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.
299
300                         if ( passedCurrent && elementTabIndex === 0 )
301                         {
302                                 elected = element;
303                                 break;
304                         }
305
306                         if ( elementTabIndex > electedTabIndex )
307                         {
308                                 elected = element;
309                                 electedTabIndex = elementTabIndex;
310                         }
311                 }
312                 else
313                 {
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.
318
319                         if ( passedCurrent && elementTabIndex == curTabIndex )
320                         {
321                                 elected = element;
322                                 break;
323                         }
324
325                         if ( elementTabIndex < curTabIndex && ( !elected || elementTabIndex > electedTabIndex ) )
326                         {
327                                 elected = element;
328                                 electedTabIndex = elementTabIndex;
329                         }
330                 }
331         }
332
333         if ( elected )
334                 elected.focus();
335 };
336
337 /**
338  * Intructs the editor to add a number of spaces (&amp;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
342  * @type Number
343  * @default 0
344  * @example
345  * config.tabSpaces = 4;
346  */
347
348 /**
349  * Allow context-sensitive tab key behaviors, including the following scenarios:
350  * <h5>When selection is anchored inside <b>table cells</b>:</h5>
351  * <ul>
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>
354  * </ul>
355  * @name CKEDITOR.config.enableTabKeyTools
356  * @type Boolean
357  * @default true
358  * @example
359  * config.enableTabKeyTools = false;
360  */
361
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' ]
367 // );