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 ( ) 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