| 270 | }, |
| 271 | |
| 272 | focusInput(count) { |
| 273 | // Focus the first input element on the page, and create overlays to highlight all the input |
| 274 | // elements, with the currently-focused element highlighted specially. Tabbing will shift focus |
| 275 | // to the next input element. Pressing any other key will remove the overlays and the special |
| 276 | // tab behavior. |
| 277 | let element, selectedInputIndex; |
| 278 | const resultSet = DomUtils.evaluateXPath( |
| 279 | textInputXPath, |
| 280 | XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, |
| 281 | ); |
| 282 | const visibleInputs = []; |
| 283 | |
| 284 | for (let i = 0, end = resultSet.snapshotLength; i < end; i++) { |
| 285 | element = resultSet.snapshotItem(i); |
| 286 | if (!DomUtils.getVisibleClientRect(element, true)) { |
| 287 | continue; |
| 288 | } |
| 289 | visibleInputs.push({ element, index: i, rect: Rect.copy(element.getBoundingClientRect()) }); |
| 290 | } |
| 291 | |
| 292 | visibleInputs.sort( |
| 293 | function ({ element: element1, index: i1 }, { element: element2, index: i2 }) { |
| 294 | // Put elements with a lower positive tabIndex first, keeping elements in DOM order. |
| 295 | if (element1.tabIndex > 0) { |
| 296 | if (element2.tabIndex > 0) { |
| 297 | const tabDifference = element1.tabIndex - element2.tabIndex; |
| 298 | if (tabDifference !== 0) { |
| 299 | return tabDifference; |
| 300 | } else { |
| 301 | return i1 - i2; |
| 302 | } |
| 303 | } else { |
| 304 | return -1; |
| 305 | } |
| 306 | } else if (element2.tabIndex > 0) { |
| 307 | return 1; |
| 308 | } else { |
| 309 | return i1 - i2; |
| 310 | } |
| 311 | }, |
| 312 | ); |
| 313 | |
| 314 | if (visibleInputs.length === 0) { |
| 315 | HUD.show("There are no inputs to focus.", 1000); |
| 316 | return; |
| 317 | } |
| 318 | |
| 319 | // This is a hack to improve usability on the Vimium options page. We prime the recently-focused |
| 320 | // input to be the key-mappings input. Arguably, this is the input that the user is most likely |
| 321 | // to use. |
| 322 | const recentlyFocusedElement = lastFocusedInput(); |
| 323 | |
| 324 | if (count === 1) { |
| 325 | // As the starting index, we pick that of the most recently focused input element (or 0). |
| 326 | const elements = visibleInputs.map((visibleInput) => visibleInput.element); |
| 327 | selectedInputIndex = Math.max(0, elements.indexOf(recentlyFocusedElement)); |
| 328 | } else { |
| 329 | selectedInputIndex = Math.min(count, visibleInputs.length) - 1; |