All files SelectionText.ts

100% Statements 50/50
100% Branches 24/24
100% Functions 12/12
100% Lines 49/49

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97            22x 22x 22x 22x 22x     31x 31x 31x 31x 31x 31x       17x 17x 17x 17x 17x     31x 31x           6x 6x 27x 27x 3x 3x     6x       2x 2x 2x 2x 2x     3x 1x 1x 1x 1x       2x         3x     5x 3x 3x 3x 3x 3x 3x 1x   3x       6x               19x 19x      
export class SelectionText {
  elm: HTMLTextAreaElement;
  start: number;
  end: number;
  value: string;
  constructor(elm: HTMLTextAreaElement) {
    const { selectionStart, selectionEnd } = elm;
    this.elm = elm;
    this.start = selectionStart;
    this.end = selectionEnd;
    this.value = this.elm.value;
  }
  position(start?: number, end?: number) {
    const { selectionStart, selectionEnd } = this.elm;
    this.start = typeof start === 'number' && !isNaN(start) ? start : selectionStart;
    this.end = typeof end === 'number' && !isNaN(end) ? end : selectionEnd;
    this.elm.selectionStart = this.start;
    this.elm.selectionEnd = this.end;
    return this;
  }
  insertText(text: string) {
    // Most of the used APIs only work with the field selected
    this.elm.focus();
    this.elm.setRangeText(text);
    this.value = this.elm.value;
    this.position();
    return this;
  }
  getSelectedValue(start?: number, end?: number) {
    const { selectionStart, selectionEnd } = this.elm;
    return this.value.slice(
      typeof start === 'number' && !isNaN(start) ? start : selectionStart,
      typeof end === 'number' && !isNaN(end) ? start : selectionEnd,
    );
  }
  getLineStartNumber() {
    let start = this.start;
    while (start > 0) {
      start--;
      if (this.value.charAt(start) === '\n') {
        start++;
        break;
      }
    }
    return start;
  }
  /** Indent on new lines */
  getIndentText() {
    const start = this.getLineStartNumber();
    const str = this.getSelectedValue(start);
    let indent = '';
    str.replace(/(^(\s)+)/, (str, old) => (indent = old));
    return indent;
  }
  lineStarInstert(text: string) {
    if (text) {
      const oldStart = this.start;
      const start = this.getLineStartNumber();
      const str = this.getSelectedValue(start);
      this.position(start, this.end)
        .insertText(
          str
            .split('\n')
            .map((txt) => text + txt)
            .join('\n'),
        )
        .position(oldStart + text.length, this.end);
    }
    return this;
  }
  lineStarRemove(text: string) {
    if (text) {
      const oldStart = this.start;
      const start = this.getLineStartNumber();
      const str = this.getSelectedValue(start);
      const reg = new RegExp(`^${text}`, 'g');
      let newStart = oldStart - text.length;
      if (!reg.test(str)) {
        newStart = oldStart;
      }
      this.position(start, this.end)
        .insertText(
          str
            .split('\n')
            .map((txt) => txt.replace(reg, ''))
            .join('\n'),
        )
        .position(newStart, this.end);
    }
  }
  /** Notify any possible listeners of the change */
  notifyChange() {
    const event = new Event('input', { bubbles: true, cancelable: false });
    this.elm.dispatchEvent(event);
  }
}