]> git.immae.eu Git - perso/Immae/Projets/packagist/piedsjaloux-ckeditor-component.git/blob - sources/plugins/tab/plugin.js
Initial commit
[perso/Immae/Projets/packagist/piedsjaloux-ckeditor-component.git] / sources / plugins / tab / plugin.js
1 /**
2 * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
3 * For licensing, see LICENSE.md or http://ckeditor.com/license
4 */
5
6 ( function() {
7 var meta = {
8 editorFocus: false,
9 modes: { wysiwyg: 1, source: 1 }
10 };
11
12 var blurCommand = {
13 exec: function( editor ) {
14 editor.container.focusNext( true, editor.tabIndex );
15 }
16 };
17
18 var blurBackCommand = {
19 exec: function( editor ) {
20 editor.container.focusPrevious( true, editor.tabIndex );
21 }
22 };
23
24 function selectNextCellCommand( backward ) {
25 return {
26 editorFocus: false,
27 canUndo: false,
28 modes: { wysiwyg: 1 },
29 exec: function( editor ) {
30 if ( editor.editable().hasFocus ) {
31 var sel = editor.getSelection(),
32 path = new CKEDITOR.dom.elementPath( sel.getCommonAncestor(), sel.root ),
33 cell;
34
35 if ( ( cell = path.contains( { td: 1, th: 1 }, 1 ) ) ) {
36 var resultRange = editor.createRange(),
37 next = CKEDITOR.tools.tryThese( function() {
38 var row = cell.getParent(),
39 next = row.$.cells[ cell.$.cellIndex + ( backward ? -1 : 1 ) ];
40
41 // Invalid any empty value.
42 next.parentNode.parentNode;
43 return next;
44 }, function() {
45 var row = cell.getParent(),
46 table = row.getAscendant( 'table' ),
47 nextRow = table.$.rows[ row.$.rowIndex + ( backward ? -1 : 1 ) ];
48
49 return nextRow.cells[ backward ? nextRow.cells.length - 1 : 0 ];
50 } );
51
52 // Clone one more row at the end of table and select the first newly established cell.
53 if ( !( next || backward ) ) {
54 var table = cell.getAscendant( 'table' ).$,
55 cells = cell.getParent().$.cells;
56
57 var newRow = new CKEDITOR.dom.element( table.insertRow( -1 ), editor.document );
58
59 for ( var i = 0, count = cells.length; i < count; i++ ) {
60 var newCell = newRow.append( new CKEDITOR.dom.element( cells[ i ], editor.document ).clone( false, false ) );
61 newCell.appendBogus();
62 }
63
64 resultRange.moveToElementEditStart( newRow );
65 } else if ( next ) {
66 next = new CKEDITOR.dom.element( next );
67 resultRange.moveToElementEditStart( next );
68 // Avoid selecting empty block makes the cursor blind.
69 if ( !( resultRange.checkStartOfBlock() && resultRange.checkEndOfBlock() ) )
70 resultRange.selectNodeContents( next );
71 } else {
72 return true;
73 }
74
75 resultRange.select( true );
76 return true;
77 }
78 }
79
80 return false;
81 }
82 };
83 }
84
85 CKEDITOR.plugins.add( 'tab', {
86 init: function( editor ) {
87 var tabTools = editor.config.enableTabKeyTools !== false,
88 tabSpaces = editor.config.tabSpaces || 0,
89 tabText = '';
90
91 while ( tabSpaces-- )
92 tabText += '\xa0';
93
94 if ( tabText ) {
95 editor.on( 'key', function( ev ) {
96 // TAB.
97 if ( ev.data.keyCode == 9 ) {
98 editor.insertText( tabText );
99 ev.cancel();
100 }
101 } );
102 }
103
104 if ( tabTools ) {
105 editor.on( 'key', function( ev ) {
106 if ( ev.data.keyCode == 9 && editor.execCommand( 'selectNextCell' ) || // TAB
107 ev.data.keyCode == ( CKEDITOR.SHIFT + 9 ) && editor.execCommand( 'selectPreviousCell' ) ) // SHIFT+TAB
108 ev.cancel();
109 } );
110 }
111
112 editor.addCommand( 'blur', CKEDITOR.tools.extend( blurCommand, meta ) );
113 editor.addCommand( 'blurBack', CKEDITOR.tools.extend( blurBackCommand, meta ) );
114 editor.addCommand( 'selectNextCell', selectNextCellCommand() );
115 editor.addCommand( 'selectPreviousCell', selectNextCellCommand( true ) );
116 }
117 } );
118 } )();
119
120 /**
121 * Moves the UI focus to the element following this element in the tabindex order.
122 *
123 * var element = CKEDITOR.document.getById( 'example' );
124 * element.focusNext();
125 *
126 * @param {Boolean} [ignoreChildren=false]
127 * @param {Number} [indexToUse]
128 * @member CKEDITOR.dom.element
129 */
130 CKEDITOR.dom.element.prototype.focusNext = function( ignoreChildren, indexToUse ) {
131 var curTabIndex = ( indexToUse === undefined ? this.getTabIndex() : indexToUse ),
132 passedCurrent, enteredCurrent, elected, electedTabIndex, element, elementTabIndex;
133
134 if ( curTabIndex <= 0 ) {
135 // If this element has tabindex <= 0 then we must simply look for any
136 // element following it containing tabindex=0.
137
138 element = this.getNextSourceNode( ignoreChildren, CKEDITOR.NODE_ELEMENT );
139
140 while ( element ) {
141 if ( element.isVisible() && element.getTabIndex() === 0 ) {
142 elected = element;
143 break;
144 }
145
146 element = element.getNextSourceNode( false, CKEDITOR.NODE_ELEMENT );
147 }
148 } else {
149 // If this element has tabindex > 0 then we must look for:
150 // 1. An element following this element with the same tabindex.
151 // 2. The first element in source other with the lowest tabindex
152 // that is higher than this element tabindex.
153 // 3. The first element with tabindex=0.
154
155 element = this.getDocument().getBody().getFirst();
156
157 while ( ( element = element.getNextSourceNode( false, CKEDITOR.NODE_ELEMENT ) ) ) {
158 if ( !passedCurrent ) {
159 if ( !enteredCurrent && element.equals( this ) ) {
160 enteredCurrent = true;
161
162 // Ignore this element, if required.
163 if ( ignoreChildren ) {
164 if ( !( element = element.getNextSourceNode( true, CKEDITOR.NODE_ELEMENT ) ) )
165 break;
166 passedCurrent = 1;
167 }
168 } else if ( enteredCurrent && !this.contains( element ) ) {
169 passedCurrent = 1;
170 }
171 }
172
173 if ( !element.isVisible() || ( elementTabIndex = element.getTabIndex() ) < 0 )
174 continue;
175
176 if ( passedCurrent && elementTabIndex == curTabIndex ) {
177 elected = element;
178 break;
179 }
180
181 if ( elementTabIndex > curTabIndex && ( !elected || !electedTabIndex || elementTabIndex < electedTabIndex ) ) {
182 elected = element;
183 electedTabIndex = elementTabIndex;
184 } else if ( !elected && elementTabIndex === 0 ) {
185 elected = element;
186 electedTabIndex = elementTabIndex;
187 }
188 }
189 }
190
191 if ( elected )
192 elected.focus();
193 };
194
195 /**
196 * Moves the UI focus to the element before this element in the tabindex order.
197 *
198 * var element = CKEDITOR.document.getById( 'example' );
199 * element.focusPrevious();
200 *
201 * @param {Boolean} [ignoreChildren=false]
202 * @param {Number} [indexToUse]
203 * @member CKEDITOR.dom.element
204 */
205 CKEDITOR.dom.element.prototype.focusPrevious = function( ignoreChildren, indexToUse ) {
206 var curTabIndex = ( indexToUse === undefined ? this.getTabIndex() : indexToUse ),
207 passedCurrent, enteredCurrent, elected,
208 electedTabIndex = 0,
209 elementTabIndex;
210
211 var element = this.getDocument().getBody().getLast();
212
213 while ( ( element = element.getPreviousSourceNode( false, CKEDITOR.NODE_ELEMENT ) ) ) {
214 if ( !passedCurrent ) {
215 if ( !enteredCurrent && element.equals( this ) ) {
216 enteredCurrent = true;
217
218 // Ignore this element, if required.
219 if ( ignoreChildren ) {
220 if ( !( element = element.getPreviousSourceNode( true, CKEDITOR.NODE_ELEMENT ) ) )
221 break;
222 passedCurrent = 1;
223 }
224 } else if ( enteredCurrent && !this.contains( element ) ) {
225 passedCurrent = 1;
226 }
227 }
228
229 if ( !element.isVisible() || ( elementTabIndex = element.getTabIndex() ) < 0 )
230 continue;
231
232 if ( curTabIndex <= 0 ) {
233 // If this element has tabindex <= 0 then we must look for:
234 // 1. An element before this one containing tabindex=0.
235 // 2. The last element with the highest tabindex.
236
237 if ( passedCurrent && elementTabIndex === 0 ) {
238 elected = element;
239 break;
240 }
241
242 if ( elementTabIndex > electedTabIndex ) {
243 elected = element;
244 electedTabIndex = elementTabIndex;
245 }
246 } else {
247 // If this element has tabindex > 0 we must look for:
248 // 1. An element preceeding this one, with the same tabindex.
249 // 2. The last element in source other with the highest tabindex
250 // that is lower than this element tabindex.
251
252 if ( passedCurrent && elementTabIndex == curTabIndex ) {
253 elected = element;
254 break;
255 }
256
257 if ( elementTabIndex < curTabIndex && ( !elected || elementTabIndex > electedTabIndex ) ) {
258 elected = element;
259 electedTabIndex = elementTabIndex;
260 }
261 }
262 }
263
264 if ( elected )
265 elected.focus();
266 };
267
268 /**
269 * Intructs the editor to add a number of spaces (`&nbsp;`) to the text when
270 * hitting the <kbd>Tab</kbd> key. If set to zero, the <kbd>Tab</kbd> key will be used to move the
271 * cursor focus to the next element in the page, out of the editor focus.
272 *
273 * config.tabSpaces = 4;
274 *
275 * @cfg {Number} [tabSpaces=0]
276 * @member CKEDITOR.config
277 */
278
279 /**
280 * Allow context-sensitive <kbd>Tab</kbd> key behaviors, including the following scenarios:
281 *
282 * When selection is anchored inside **table cells**:
283 *
284 * * If <kbd>Tab</kbd> is pressed, select the content of the "next" cell. If in the last
285 * cell in the table, add a new row to it and focus its first cell.
286 * * If <kbd>Shift+Tab</kbd> is pressed, select the content of the "previous" cell.
287 * Do nothing when it is in the first cell.
288 *
289 * Example:
290 *
291 * config.enableTabKeyTools = false;
292 *
293 * @cfg {Boolean} [enableTabKeyTools=true]
294 * @member CKEDITOR.config
295 */
296
297 // If the <kbd>Tab</kbd> key is not supposed to be enabled for navigation, the following
298 // settings could be used alternatively:
299 // config.keystrokes.push(
300 // [ CKEDITOR.ALT + 38 /*Arrow Up*/, 'selectPreviousCell' ],
301 // [ CKEDITOR.ALT + 40 /*Arrow Down*/, 'selectNextCell' ]
302 // );