X-Git-Url: https://git.josue.xyz/?p=VSoRC%2F.git;a=blobdiff_plain;f=node_modules%2Fxterm%2Fsrc%2FAccessibilityManager.ts;fp=node_modules%2Fxterm%2Fsrc%2FAccessibilityManager.ts;h=0000000000000000000000000000000000000000;hp=75393ea4cea8cd04a839c5f0a2db3cf51d64f631;hb=5e96dd57ddd883604e87f62bdddcb111c63a6e1a;hpb=acb5f682a2b75b972710cabd81658f63071324b0 diff --git a/node_modules/xterm/src/AccessibilityManager.ts b/node_modules/xterm/src/AccessibilityManager.ts deleted file mode 100644 index 75393ea..0000000 --- a/node_modules/xterm/src/AccessibilityManager.ts +++ /dev/null @@ -1,297 +0,0 @@ -/** - * Copyright (c) 2017 The xterm.js authors. All rights reserved. - * @license MIT - */ - -import * as Strings from './browser/LocalizableStrings'; -import { ITerminal } from './Types'; -import { IBuffer } from 'common/buffer/Types'; -import { isMac } from 'common/Platform'; -import { RenderDebouncer } from 'browser/RenderDebouncer'; -import { addDisposableDomListener } from 'browser/Lifecycle'; -import { Disposable } from 'common/Lifecycle'; -import { ScreenDprMonitor } from 'browser/ScreenDprMonitor'; -import { IRenderService } from 'browser/services/Services'; - -const MAX_ROWS_TO_READ = 20; - -const enum BoundaryPosition { - TOP, - BOTTOM -} - -export class AccessibilityManager extends Disposable { - private _accessibilityTreeRoot: HTMLElement; - private _rowContainer: HTMLElement; - private _rowElements: HTMLElement[]; - private _liveRegion: HTMLElement; - private _liveRegionLineCount: number = 0; - - private _renderRowsDebouncer: RenderDebouncer; - private _screenDprMonitor: ScreenDprMonitor; - - private _topBoundaryFocusListener: (e: FocusEvent) => void; - private _bottomBoundaryFocusListener: (e: FocusEvent) => void; - - /** - * This queue has a character pushed to it for keys that are pressed, if the - * next character added to the terminal is equal to the key char then it is - * not announced (added to live region) because it has already been announced - * by the textarea event (which cannot be canceled). There are some race - * condition cases if there is typing while data is streaming, but this covers - * the main case of typing into the prompt and inputting the answer to a - * question (Y/N, etc.). - */ - private _charsToConsume: string[] = []; - - private _charsToAnnounce: string = ''; - - constructor( - private readonly _terminal: ITerminal, - private readonly _renderService: IRenderService - ) { - super(); - this._accessibilityTreeRoot = document.createElement('div'); - this._accessibilityTreeRoot.classList.add('xterm-accessibility'); - - this._rowContainer = document.createElement('div'); - this._rowContainer.classList.add('xterm-accessibility-tree'); - this._rowElements = []; - for (let i = 0; i < this._terminal.rows; i++) { - this._rowElements[i] = this._createAccessibilityTreeNode(); - this._rowContainer.appendChild(this._rowElements[i]); - } - - this._topBoundaryFocusListener = e => this._onBoundaryFocus(e, BoundaryPosition.TOP); - this._bottomBoundaryFocusListener = e => this._onBoundaryFocus(e, BoundaryPosition.BOTTOM); - this._rowElements[0].addEventListener('focus', this._topBoundaryFocusListener); - this._rowElements[this._rowElements.length - 1].addEventListener('focus', this._bottomBoundaryFocusListener); - - this._refreshRowsDimensions(); - this._accessibilityTreeRoot.appendChild(this._rowContainer); - - this._renderRowsDebouncer = new RenderDebouncer(this._renderRows.bind(this)); - this._refreshRows(); - - this._liveRegion = document.createElement('div'); - this._liveRegion.classList.add('live-region'); - this._liveRegion.setAttribute('aria-live', 'assertive'); - this._accessibilityTreeRoot.appendChild(this._liveRegion); - - this._terminal.element.insertAdjacentElement('afterbegin', this._accessibilityTreeRoot); - - this.register(this._renderRowsDebouncer); - this.register(this._terminal.onResize(e => this._onResize(e.rows))); - this.register(this._terminal.onRender(e => this._refreshRows(e.start, e.end))); - this.register(this._terminal.onScroll(() => this._refreshRows())); - // Line feed is an issue as the prompt won't be read out after a command is run - this.register(this._terminal.onA11yChar(char => this._onChar(char))); - this.register(this._terminal.onLineFeed(() => this._onChar('\n'))); - this.register(this._terminal.onA11yTab(spaceCount => this._onTab(spaceCount))); - this.register(this._terminal.onKey(e => this._onKey(e.key))); - this.register(this._terminal.onBlur(() => this._clearLiveRegion())); - this.register(this._renderService.onDimensionsChange(() => this._refreshRowsDimensions())); - - this._screenDprMonitor = new ScreenDprMonitor(); - this.register(this._screenDprMonitor); - this._screenDprMonitor.setListener(() => this._refreshRowsDimensions()); - // This shouldn't be needed on modern browsers but is present in case the - // media query that drives the ScreenDprMonitor isn't supported - this.register(addDisposableDomListener(window, 'resize', () => this._refreshRowsDimensions())); - } - - public dispose(): void { - super.dispose(); - this._terminal.element.removeChild(this._accessibilityTreeRoot); - this._rowElements.length = 0; - } - - private _onBoundaryFocus(e: FocusEvent, position: BoundaryPosition): void { - const boundaryElement = e.target; - const beforeBoundaryElement = this._rowElements[position === BoundaryPosition.TOP ? 1 : this._rowElements.length - 2]; - - // Don't scroll if the buffer top has reached the end in that direction - const posInSet = boundaryElement.getAttribute('aria-posinset'); - const lastRowPos = position === BoundaryPosition.TOP ? '1' : `${this._terminal.buffer.lines.length}`; - if (posInSet === lastRowPos) { - return; - } - - // Don't scroll when the last focused item was not the second row (focus is going the other - // direction) - if (e.relatedTarget !== beforeBoundaryElement) { - return; - } - - // Remove old boundary element from array - let topBoundaryElement: HTMLElement; - let bottomBoundaryElement: HTMLElement; - if (position === BoundaryPosition.TOP) { - topBoundaryElement = boundaryElement; - bottomBoundaryElement = this._rowElements.pop()!; - this._rowContainer.removeChild(bottomBoundaryElement); - } else { - topBoundaryElement = this._rowElements.shift()!; - bottomBoundaryElement = boundaryElement; - this._rowContainer.removeChild(topBoundaryElement); - } - - // Remove listeners from old boundary elements - topBoundaryElement.removeEventListener('focus', this._topBoundaryFocusListener); - bottomBoundaryElement.removeEventListener('focus', this._bottomBoundaryFocusListener); - - // Add new element to array/DOM - if (position === BoundaryPosition.TOP) { - const newElement = this._createAccessibilityTreeNode(); - this._rowElements.unshift(newElement); - this._rowContainer.insertAdjacentElement('afterbegin', newElement); - } else { - const newElement = this._createAccessibilityTreeNode(); - this._rowElements.push(newElement); - this._rowContainer.appendChild(newElement); - } - - // Add listeners to new boundary elements - this._rowElements[0].addEventListener('focus', this._topBoundaryFocusListener); - this._rowElements[this._rowElements.length - 1].addEventListener('focus', this._bottomBoundaryFocusListener); - - // Scroll up - this._terminal.scrollLines(position === BoundaryPosition.TOP ? -1 : 1); - - // Focus new boundary before element - this._rowElements[position === BoundaryPosition.TOP ? 1 : this._rowElements.length - 2].focus(); - - // Prevent the standard behavior - e.preventDefault(); - e.stopImmediatePropagation(); - } - - private _onResize(rows: number): void { - // Remove bottom boundary listener - this._rowElements[this._rowElements.length - 1].removeEventListener('focus', this._bottomBoundaryFocusListener); - - // Grow rows as required - for (let i = this._rowContainer.children.length; i < this._terminal.rows; i++) { - this._rowElements[i] = this._createAccessibilityTreeNode(); - this._rowContainer.appendChild(this._rowElements[i]); - } - // Shrink rows as required - while (this._rowElements.length > rows) { - this._rowContainer.removeChild(this._rowElements.pop()!); - } - - // Add bottom boundary listener - this._rowElements[this._rowElements.length - 1].addEventListener('focus', this._bottomBoundaryFocusListener); - - this._refreshRowsDimensions(); - } - - private _createAccessibilityTreeNode(): HTMLElement { - const element = document.createElement('div'); - element.setAttribute('role', 'listitem'); - element.tabIndex = -1; - this._refreshRowDimensions(element); - return element; - } - - private _onTab(spaceCount: number): void { - for (let i = 0; i < spaceCount; i++) { - this._onChar(' '); - } - } - - private _onChar(char: string): void { - if (this._liveRegionLineCount < MAX_ROWS_TO_READ + 1) { - if (this._charsToConsume.length > 0) { - // Have the screen reader ignore the char if it was just input - const shiftedChar = this._charsToConsume.shift(); - if (shiftedChar !== char) { - this._charsToAnnounce += char; - } - } else { - this._charsToAnnounce += char; - } - - if (char === '\n') { - this._liveRegionLineCount++; - if (this._liveRegionLineCount === MAX_ROWS_TO_READ + 1) { - this._liveRegion.textContent += Strings.tooMuchOutput; - } - } - - // Only detach/attach on mac as otherwise messages can go unaccounced - if (isMac) { - if (this._liveRegion.textContent && this._liveRegion.textContent.length > 0 && !this._liveRegion.parentNode) { - setTimeout(() => { - this._accessibilityTreeRoot.appendChild(this._liveRegion); - }, 0); - } - } - } - } - - private _clearLiveRegion(): void { - this._liveRegion.textContent = ''; - this._liveRegionLineCount = 0; - - // Only detach/attach on mac as otherwise messages can go unaccounced - if (isMac) { - if (this._liveRegion.parentNode) { - this._accessibilityTreeRoot.removeChild(this._liveRegion); - } - } - } - - private _onKey(keyChar: string): void { - this._clearLiveRegion(); - this._charsToConsume.push(keyChar); - } - - private _refreshRows(start?: number, end?: number): void { - this._renderRowsDebouncer.refresh(start, end, this._terminal.rows); - } - - private _renderRows(start: number, end: number): void { - const buffer: IBuffer = this._terminal.buffer; - const setSize = buffer.lines.length.toString(); - for (let i = start; i <= end; i++) { - const lineData = buffer.translateBufferLineToString(buffer.ydisp + i, true); - const posInSet = (buffer.ydisp + i + 1).toString(); - const element = this._rowElements[i]; - if (element) { - if (lineData.length === 0) { - element.innerHTML = ' '; - } else { - element.textContent = lineData; - } - element.setAttribute('aria-posinset', posInSet); - element.setAttribute('aria-setsize', setSize); - } - } - this._announceCharacters(); - } - - private _refreshRowsDimensions(): void { - if (!this._renderService.dimensions.actualCellHeight) { - return; - } - if (this._rowElements.length !== this._terminal.rows) { - this._onResize(this._terminal.rows); - } - for (let i = 0; i < this._terminal.rows; i++) { - this._refreshRowDimensions(this._rowElements[i]); - } - } - - private _refreshRowDimensions(element: HTMLElement): void { - element.style.height = `${this._renderService.dimensions.actualCellHeight}px`; - } - - private _announceCharacters(): void { - if (this._charsToAnnounce.length === 0) { - return; - } - this._liveRegion.textContent += this._charsToAnnounce; - this._charsToAnnounce = ''; - } -}