"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const editorDom = require("../editorDom.js");
const viewPart = require("../view/viewPart.js");
const viewLine = require("../viewParts/lines/viewLine.js");
const position = require("../../common/core/position.js");
const range = require("../../common/core/range.js");
const cursorColumns = require("../../common/core/cursorColumns.js");
const dom = require("../../../base/browser/dom.js");
const cursorAtomicMoveOperations = require("../../common/cursor/cursorAtomicMoveOperations.js");
class UnknownHitTestResult {
  constructor(hitTarget = null) {
    this.hitTarget = hitTarget;
    this.type = 0;
  }
}
class ContentHitTestResult {
  constructor(position2, spanNode, injectedText) {
    this.position = position2;
    this.spanNode = spanNode;
    this.injectedText = injectedText;
    this.type = 1;
  }
}
var HitTestResult;
(function(HitTestResult2) {
  function createFromDOMInfo(ctx, spanNode, offset) {
    const position2 = ctx.getPositionFromDOMInfo(spanNode, offset);
    if (position2) {
      return new ContentHitTestResult(position2, spanNode, null);
    }
    return new UnknownHitTestResult(spanNode);
  }
  HitTestResult2.createFromDOMInfo = createFromDOMInfo;
})(HitTestResult || (HitTestResult = {}));
class PointerHandlerLastRenderData {
  constructor(lastViewCursorsRenderData, lastTextareaPosition) {
    this.lastViewCursorsRenderData = lastViewCursorsRenderData;
    this.lastTextareaPosition = lastTextareaPosition;
  }
}
class MouseTarget {
  static _deduceRage(position2, range$1 = null) {
    if (!range$1 && position2) {
      return new range.Range(position2.lineNumber, position2.column, position2.lineNumber, position2.column);
    }
    return range$1 !== null && range$1 !== void 0 ? range$1 : null;
  }
  static createUnknown(element, mouseColumn, position2) {
    return { type: 0, element, mouseColumn, position: position2, range: this._deduceRage(position2) };
  }
  static createTextarea(element, mouseColumn) {
    return { type: 1, element, mouseColumn, position: null, range: null };
  }
  static createMargin(type, element, mouseColumn, position2, range2, detail) {
    return { type, element, mouseColumn, position: position2, range: range2, detail };
  }
  static createViewZone(type, element, mouseColumn, position2, detail) {
    return { type, element, mouseColumn, position: position2, range: this._deduceRage(position2), detail };
  }
  static createContentText(element, mouseColumn, position2, range2, detail) {
    return { type: 6, element, mouseColumn, position: position2, range: this._deduceRage(position2, range2), detail };
  }
  static createContentEmpty(element, mouseColumn, position2, detail) {
    return { type: 7, element, mouseColumn, position: position2, range: this._deduceRage(position2), detail };
  }
  static createContentWidget(element, mouseColumn, detail) {
    return { type: 9, element, mouseColumn, position: null, range: null, detail };
  }
  static createScrollbar(element, mouseColumn, position2) {
    return { type: 11, element, mouseColumn, position: position2, range: this._deduceRage(position2) };
  }
  static createOverlayWidget(element, mouseColumn, detail) {
    return { type: 12, element, mouseColumn, position: null, range: null, detail };
  }
  static createOutsideEditor(mouseColumn, position2) {
    return { type: 13, element: null, mouseColumn, position: position2, range: this._deduceRage(position2) };
  }
  static _typeToString(type) {
    if (type === 1) {
      return "TEXTAREA";
    }
    if (type === 2) {
      return "GUTTER_GLYPH_MARGIN";
    }
    if (type === 3) {
      return "GUTTER_LINE_NUMBERS";
    }
    if (type === 4) {
      return "GUTTER_LINE_DECORATIONS";
    }
    if (type === 5) {
      return "GUTTER_VIEW_ZONE";
    }
    if (type === 6) {
      return "CONTENT_TEXT";
    }
    if (type === 7) {
      return "CONTENT_EMPTY";
    }
    if (type === 8) {
      return "CONTENT_VIEW_ZONE";
    }
    if (type === 9) {
      return "CONTENT_WIDGET";
    }
    if (type === 10) {
      return "OVERVIEW_RULER";
    }
    if (type === 11) {
      return "SCROLLBAR";
    }
    if (type === 12) {
      return "OVERLAY_WIDGET";
    }
    return "UNKNOWN";
  }
  static toString(target) {
    return this._typeToString(target.type) + ": " + target.position + " - " + target.range + " - " + JSON.stringify(target.detail);
  }
}
class ElementPath {
  static isTextArea(path) {
    return path.length === 2 && path[0] === 3 && path[1] === 6;
  }
  static isChildOfViewLines(path) {
    return path.length >= 4 && path[0] === 3 && path[3] === 7;
  }
  static isStrictChildOfViewLines(path) {
    return path.length > 4 && path[0] === 3 && path[3] === 7;
  }
  static isChildOfScrollableElement(path) {
    return path.length >= 2 && path[0] === 3 && path[1] === 5;
  }
  static isChildOfMinimap(path) {
    return path.length >= 2 && path[0] === 3 && path[1] === 8;
  }
  static isChildOfContentWidgets(path) {
    return path.length >= 4 && path[0] === 3 && path[3] === 1;
  }
  static isChildOfOverflowingContentWidgets(path) {
    return path.length >= 1 && path[0] === 2;
  }
  static isChildOfOverlayWidgets(path) {
    return path.length >= 2 && path[0] === 3 && path[1] === 4;
  }
}
class HitTestContext {
  constructor(context, viewHelper, lastRenderData) {
    this.viewModel = context.viewModel;
    const options = context.configuration.options;
    this.layoutInfo = options.get(
      131
      /* layoutInfo */
    );
    this.viewDomNode = viewHelper.viewDomNode;
    this.lineHeight = options.get(
      59
      /* lineHeight */
    );
    this.stickyTabStops = options.get(
      104
      /* stickyTabStops */
    );
    this.typicalHalfwidthCharacterWidth = options.get(
      44
      /* fontInfo */
    ).typicalHalfwidthCharacterWidth;
    this.lastRenderData = lastRenderData;
    this._context = context;
    this._viewHelper = viewHelper;
  }
  getZoneAtCoord(mouseVerticalOffset) {
    return HitTestContext.getZoneAtCoord(this._context, mouseVerticalOffset);
  }
  static getZoneAtCoord(context, mouseVerticalOffset) {
    const viewZoneWhitespace = context.viewLayout.getWhitespaceAtVerticalOffset(mouseVerticalOffset);
    if (viewZoneWhitespace) {
      const viewZoneMiddle = viewZoneWhitespace.verticalOffset + viewZoneWhitespace.height / 2;
      const lineCount = context.viewModel.getLineCount();
      let positionBefore = null;
      let position$1;
      let positionAfter = null;
      if (viewZoneWhitespace.afterLineNumber !== lineCount) {
        positionAfter = new position.Position(viewZoneWhitespace.afterLineNumber + 1, 1);
      }
      if (viewZoneWhitespace.afterLineNumber > 0) {
        positionBefore = new position.Position(viewZoneWhitespace.afterLineNumber, context.viewModel.getLineMaxColumn(viewZoneWhitespace.afterLineNumber));
      }
      if (positionAfter === null) {
        position$1 = positionBefore;
      } else if (positionBefore === null) {
        position$1 = positionAfter;
      } else if (mouseVerticalOffset < viewZoneMiddle) {
        position$1 = positionBefore;
      } else {
        position$1 = positionAfter;
      }
      return {
        viewZoneId: viewZoneWhitespace.id,
        afterLineNumber: viewZoneWhitespace.afterLineNumber,
        positionBefore,
        positionAfter,
        position: position$1
      };
    }
    return null;
  }
  getFullLineRangeAtCoord(mouseVerticalOffset) {
    if (this._context.viewLayout.isAfterLines(mouseVerticalOffset)) {
      const lineNumber2 = this._context.viewModel.getLineCount();
      const maxLineColumn2 = this._context.viewModel.getLineMaxColumn(lineNumber2);
      return {
        range: new range.Range(lineNumber2, maxLineColumn2, lineNumber2, maxLineColumn2),
        isAfterLines: true
      };
    }
    const lineNumber = this._context.viewLayout.getLineNumberAtVerticalOffset(mouseVerticalOffset);
    const maxLineColumn = this._context.viewModel.getLineMaxColumn(lineNumber);
    return {
      range: new range.Range(lineNumber, 1, lineNumber, maxLineColumn),
      isAfterLines: false
    };
  }
  getLineNumberAtVerticalOffset(mouseVerticalOffset) {
    return this._context.viewLayout.getLineNumberAtVerticalOffset(mouseVerticalOffset);
  }
  isAfterLines(mouseVerticalOffset) {
    return this._context.viewLayout.isAfterLines(mouseVerticalOffset);
  }
  isInTopPadding(mouseVerticalOffset) {
    return this._context.viewLayout.isInTopPadding(mouseVerticalOffset);
  }
  isInBottomPadding(mouseVerticalOffset) {
    return this._context.viewLayout.isInBottomPadding(mouseVerticalOffset);
  }
  getVerticalOffsetForLineNumber(lineNumber) {
    return this._context.viewLayout.getVerticalOffsetForLineNumber(lineNumber);
  }
  findAttribute(element, attr) {
    return HitTestContext._findAttribute(element, attr, this._viewHelper.viewDomNode);
  }
  static _findAttribute(element, attr, stopAt) {
    while (element && element !== document.body) {
      if (element.hasAttribute && element.hasAttribute(attr)) {
        return element.getAttribute(attr);
      }
      if (element === stopAt) {
        return null;
      }
      element = element.parentNode;
    }
    return null;
  }
  getLineWidth(lineNumber) {
    return this._viewHelper.getLineWidth(lineNumber);
  }
  visibleRangeForPosition(lineNumber, column) {
    return this._viewHelper.visibleRangeForPosition(lineNumber, column);
  }
  getPositionFromDOMInfo(spanNode, offset) {
    return this._viewHelper.getPositionFromDOMInfo(spanNode, offset);
  }
  getCurrentScrollTop() {
    return this._context.viewLayout.getCurrentScrollTop();
  }
  getCurrentScrollLeft() {
    return this._context.viewLayout.getCurrentScrollLeft();
  }
}
class BareHitTestRequest {
  constructor(ctx, editorPos, pos, relativePos) {
    this.editorPos = editorPos;
    this.pos = pos;
    this.relativePos = relativePos;
    this.mouseVerticalOffset = Math.max(0, ctx.getCurrentScrollTop() + this.relativePos.y);
    this.mouseContentHorizontalOffset = ctx.getCurrentScrollLeft() + this.relativePos.x - ctx.layoutInfo.contentLeft;
    this.isInMarginArea = this.relativePos.x < ctx.layoutInfo.contentLeft && this.relativePos.x >= ctx.layoutInfo.glyphMarginLeft;
    this.isInContentArea = !this.isInMarginArea;
    this.mouseColumn = Math.max(0, MouseTargetFactory._getMouseColumn(this.mouseContentHorizontalOffset, ctx.typicalHalfwidthCharacterWidth));
  }
}
class HitTestRequest extends BareHitTestRequest {
  constructor(ctx, editorPos, pos, relativePos, target) {
    super(ctx, editorPos, pos, relativePos);
    this._ctx = ctx;
    if (target) {
      this.target = target;
      this.targetPath = viewPart.PartFingerprints.collect(target, ctx.viewDomNode);
    } else {
      this.target = null;
      this.targetPath = new Uint8Array(0);
    }
  }
  toString() {
    return `pos(${this.pos.x},${this.pos.y}), editorPos(${this.editorPos.x},${this.editorPos.y}), relativePos(${this.relativePos.x},${this.relativePos.y}), mouseVerticalOffset: ${this.mouseVerticalOffset}, mouseContentHorizontalOffset: ${this.mouseContentHorizontalOffset}
	target: ${this.target ? this.target.outerHTML : null}`;
  }
  _getMouseColumn(position2 = null) {
    if (position2 && position2.column < this._ctx.viewModel.getLineMaxColumn(position2.lineNumber)) {
      return cursorColumns.CursorColumns.visibleColumnFromColumn(this._ctx.viewModel.getLineContent(position2.lineNumber), position2.column, this._ctx.viewModel.model.getOptions().tabSize) + 1;
    }
    return this.mouseColumn;
  }
  fulfillUnknown(position2 = null) {
    return MouseTarget.createUnknown(this.target, this._getMouseColumn(position2), position2);
  }
  fulfillTextarea() {
    return MouseTarget.createTextarea(this.target, this._getMouseColumn());
  }
  fulfillMargin(type, position2, range2, detail) {
    return MouseTarget.createMargin(type, this.target, this._getMouseColumn(position2), position2, range2, detail);
  }
  fulfillViewZone(type, position2, detail) {
    return MouseTarget.createViewZone(type, this.target, this._getMouseColumn(position2), position2, detail);
  }
  fulfillContentText(position2, range2, detail) {
    return MouseTarget.createContentText(this.target, this._getMouseColumn(position2), position2, range2, detail);
  }
  fulfillContentEmpty(position2, detail) {
    return MouseTarget.createContentEmpty(this.target, this._getMouseColumn(position2), position2, detail);
  }
  fulfillContentWidget(detail) {
    return MouseTarget.createContentWidget(this.target, this._getMouseColumn(), detail);
  }
  fulfillScrollbar(position2) {
    return MouseTarget.createScrollbar(this.target, this._getMouseColumn(position2), position2);
  }
  fulfillOverlayWidget(detail) {
    return MouseTarget.createOverlayWidget(this.target, this._getMouseColumn(), detail);
  }
  withTarget(target) {
    return new HitTestRequest(this._ctx, this.editorPos, this.pos, this.relativePos, target);
  }
}
const EMPTY_CONTENT_AFTER_LINES = { isAfterLines: true };
function createEmptyContentDataInLines(horizontalDistanceToText) {
  return {
    isAfterLines: false,
    horizontalDistanceToText
  };
}
class MouseTargetFactory {
  constructor(context, viewHelper) {
    this._context = context;
    this._viewHelper = viewHelper;
  }
  mouseTargetIsWidget(e) {
    const t = e.target;
    const path = viewPart.PartFingerprints.collect(t, this._viewHelper.viewDomNode);
    if (ElementPath.isChildOfContentWidgets(path) || ElementPath.isChildOfOverflowingContentWidgets(path)) {
      return true;
    }
    if (ElementPath.isChildOfOverlayWidgets(path)) {
      return true;
    }
    return false;
  }
  createMouseTarget(lastRenderData, editorPos, pos, relativePos, target) {
    const ctx = new HitTestContext(this._context, this._viewHelper, lastRenderData);
    const request = new HitTestRequest(ctx, editorPos, pos, relativePos, target);
    try {
      const r = MouseTargetFactory._createMouseTarget(ctx, request, false);
      return r;
    } catch (err) {
      return request.fulfillUnknown();
    }
  }
  static _createMouseTarget(ctx, request, domHitTestExecuted) {
    if (request.target === null) {
      if (domHitTestExecuted) {
        return request.fulfillUnknown();
      }
      const hitTestResult = MouseTargetFactory._doHitTest(ctx, request);
      if (hitTestResult.type === 1) {
        return MouseTargetFactory.createMouseTargetFromHitTestPosition(ctx, request, hitTestResult.spanNode, hitTestResult.position, hitTestResult.injectedText);
      }
      return this._createMouseTarget(ctx, request.withTarget(hitTestResult.hitTarget), true);
    }
    const resolvedRequest = request;
    let result = null;
    result = result || MouseTargetFactory._hitTestContentWidget(ctx, resolvedRequest);
    result = result || MouseTargetFactory._hitTestOverlayWidget(ctx, resolvedRequest);
    result = result || MouseTargetFactory._hitTestMinimap(ctx, resolvedRequest);
    result = result || MouseTargetFactory._hitTestScrollbarSlider(ctx, resolvedRequest);
    result = result || MouseTargetFactory._hitTestViewZone(ctx, resolvedRequest);
    result = result || MouseTargetFactory._hitTestMargin(ctx, resolvedRequest);
    result = result || MouseTargetFactory._hitTestViewCursor(ctx, resolvedRequest);
    result = result || MouseTargetFactory._hitTestTextArea(ctx, resolvedRequest);
    result = result || MouseTargetFactory._hitTestViewLines(ctx, resolvedRequest, domHitTestExecuted);
    result = result || MouseTargetFactory._hitTestScrollbar(ctx, resolvedRequest);
    return result || request.fulfillUnknown();
  }
  static _hitTestContentWidget(ctx, request) {
    if (ElementPath.isChildOfContentWidgets(request.targetPath) || ElementPath.isChildOfOverflowingContentWidgets(request.targetPath)) {
      const widgetId = ctx.findAttribute(request.target, "widgetId");
      if (widgetId) {
        return request.fulfillContentWidget(widgetId);
      } else {
        return request.fulfillUnknown();
      }
    }
    return null;
  }
  static _hitTestOverlayWidget(ctx, request) {
    if (ElementPath.isChildOfOverlayWidgets(request.targetPath)) {
      const widgetId = ctx.findAttribute(request.target, "widgetId");
      if (widgetId) {
        return request.fulfillOverlayWidget(widgetId);
      } else {
        return request.fulfillUnknown();
      }
    }
    return null;
  }
  static _hitTestViewCursor(ctx, request) {
    if (request.target) {
      const lastViewCursorsRenderData = ctx.lastRenderData.lastViewCursorsRenderData;
      for (const d of lastViewCursorsRenderData) {
        if (request.target === d.domNode) {
          return request.fulfillContentText(d.position, null, { mightBeForeignElement: false, injectedText: null });
        }
      }
    }
    if (request.isInContentArea) {
      const lastViewCursorsRenderData = ctx.lastRenderData.lastViewCursorsRenderData;
      const mouseContentHorizontalOffset = request.mouseContentHorizontalOffset;
      const mouseVerticalOffset = request.mouseVerticalOffset;
      for (const d of lastViewCursorsRenderData) {
        if (mouseContentHorizontalOffset < d.contentLeft) {
          continue;
        }
        if (mouseContentHorizontalOffset > d.contentLeft + d.width) {
          continue;
        }
        const cursorVerticalOffset = ctx.getVerticalOffsetForLineNumber(d.position.lineNumber);
        if (cursorVerticalOffset <= mouseVerticalOffset && mouseVerticalOffset <= cursorVerticalOffset + d.height) {
          return request.fulfillContentText(d.position, null, { mightBeForeignElement: false, injectedText: null });
        }
      }
    }
    return null;
  }
  static _hitTestViewZone(ctx, request) {
    const viewZoneData = ctx.getZoneAtCoord(request.mouseVerticalOffset);
    if (viewZoneData) {
      const mouseTargetType = request.isInContentArea ? 8 : 5;
      return request.fulfillViewZone(mouseTargetType, viewZoneData.position, viewZoneData);
    }
    return null;
  }
  static _hitTestTextArea(ctx, request) {
    if (ElementPath.isTextArea(request.targetPath)) {
      if (ctx.lastRenderData.lastTextareaPosition) {
        return request.fulfillContentText(ctx.lastRenderData.lastTextareaPosition, null, { mightBeForeignElement: false, injectedText: null });
      }
      return request.fulfillTextarea();
    }
    return null;
  }
  static _hitTestMargin(ctx, request) {
    if (request.isInMarginArea) {
      const res = ctx.getFullLineRangeAtCoord(request.mouseVerticalOffset);
      const pos = res.range.getStartPosition();
      let offset = Math.abs(request.relativePos.x);
      const detail = {
        isAfterLines: res.isAfterLines,
        glyphMarginLeft: ctx.layoutInfo.glyphMarginLeft,
        glyphMarginWidth: ctx.layoutInfo.glyphMarginWidth,
        lineNumbersWidth: ctx.layoutInfo.lineNumbersWidth,
        offsetX: offset
      };
      offset -= ctx.layoutInfo.glyphMarginLeft;
      if (offset <= ctx.layoutInfo.glyphMarginWidth) {
        return request.fulfillMargin(2, pos, res.range, detail);
      }
      offset -= ctx.layoutInfo.glyphMarginWidth;
      if (offset <= ctx.layoutInfo.lineNumbersWidth) {
        return request.fulfillMargin(3, pos, res.range, detail);
      }
      offset -= ctx.layoutInfo.lineNumbersWidth;
      return request.fulfillMargin(4, pos, res.range, detail);
    }
    return null;
  }
  static _hitTestViewLines(ctx, request, domHitTestExecuted) {
    if (!ElementPath.isChildOfViewLines(request.targetPath)) {
      return null;
    }
    if (ctx.isInTopPadding(request.mouseVerticalOffset)) {
      return request.fulfillContentEmpty(new position.Position(1, 1), EMPTY_CONTENT_AFTER_LINES);
    }
    if (ctx.isAfterLines(request.mouseVerticalOffset) || ctx.isInBottomPadding(request.mouseVerticalOffset)) {
      const lineCount = ctx.viewModel.getLineCount();
      const maxLineColumn = ctx.viewModel.getLineMaxColumn(lineCount);
      return request.fulfillContentEmpty(new position.Position(lineCount, maxLineColumn), EMPTY_CONTENT_AFTER_LINES);
    }
    if (domHitTestExecuted) {
      if (ElementPath.isStrictChildOfViewLines(request.targetPath)) {
        const lineNumber = ctx.getLineNumberAtVerticalOffset(request.mouseVerticalOffset);
        if (ctx.viewModel.getLineLength(lineNumber) === 0) {
          const lineWidth2 = ctx.getLineWidth(lineNumber);
          const detail = createEmptyContentDataInLines(request.mouseContentHorizontalOffset - lineWidth2);
          return request.fulfillContentEmpty(new position.Position(lineNumber, 1), detail);
        }
        const lineWidth = ctx.getLineWidth(lineNumber);
        if (request.mouseContentHorizontalOffset >= lineWidth) {
          const detail = createEmptyContentDataInLines(request.mouseContentHorizontalOffset - lineWidth);
          const pos = new position.Position(lineNumber, ctx.viewModel.getLineMaxColumn(lineNumber));
          return request.fulfillContentEmpty(pos, detail);
        }
      }
      return request.fulfillUnknown();
    }
    const hitTestResult = MouseTargetFactory._doHitTest(ctx, request);
    if (hitTestResult.type === 1) {
      return MouseTargetFactory.createMouseTargetFromHitTestPosition(ctx, request, hitTestResult.spanNode, hitTestResult.position, hitTestResult.injectedText);
    }
    return this._createMouseTarget(ctx, request.withTarget(hitTestResult.hitTarget), true);
  }
  static _hitTestMinimap(ctx, request) {
    if (ElementPath.isChildOfMinimap(request.targetPath)) {
      const possibleLineNumber = ctx.getLineNumberAtVerticalOffset(request.mouseVerticalOffset);
      const maxColumn = ctx.viewModel.getLineMaxColumn(possibleLineNumber);
      return request.fulfillScrollbar(new position.Position(possibleLineNumber, maxColumn));
    }
    return null;
  }
  static _hitTestScrollbarSlider(ctx, request) {
    if (ElementPath.isChildOfScrollableElement(request.targetPath)) {
      if (request.target && request.target.nodeType === 1) {
        const className = request.target.className;
        if (className && /\b(slider|scrollbar)\b/.test(className)) {
          const possibleLineNumber = ctx.getLineNumberAtVerticalOffset(request.mouseVerticalOffset);
          const maxColumn = ctx.viewModel.getLineMaxColumn(possibleLineNumber);
          return request.fulfillScrollbar(new position.Position(possibleLineNumber, maxColumn));
        }
      }
    }
    return null;
  }
  static _hitTestScrollbar(ctx, request) {
    if (ElementPath.isChildOfScrollableElement(request.targetPath)) {
      const possibleLineNumber = ctx.getLineNumberAtVerticalOffset(request.mouseVerticalOffset);
      const maxColumn = ctx.viewModel.getLineMaxColumn(possibleLineNumber);
      return request.fulfillScrollbar(new position.Position(possibleLineNumber, maxColumn));
    }
    return null;
  }
  getMouseColumn(relativePos) {
    const options = this._context.configuration.options;
    const layoutInfo = options.get(
      131
      /* layoutInfo */
    );
    const mouseContentHorizontalOffset = this._context.viewLayout.getCurrentScrollLeft() + relativePos.x - layoutInfo.contentLeft;
    return MouseTargetFactory._getMouseColumn(mouseContentHorizontalOffset, options.get(
      44
      /* fontInfo */
    ).typicalHalfwidthCharacterWidth);
  }
  static _getMouseColumn(mouseContentHorizontalOffset, typicalHalfwidthCharacterWidth) {
    if (mouseContentHorizontalOffset < 0) {
      return 1;
    }
    const chars = Math.round(mouseContentHorizontalOffset / typicalHalfwidthCharacterWidth);
    return chars + 1;
  }
  static createMouseTargetFromHitTestPosition(ctx, request, spanNode, pos, injectedText) {
    const lineNumber = pos.lineNumber;
    const column = pos.column;
    const lineWidth = ctx.getLineWidth(lineNumber);
    if (request.mouseContentHorizontalOffset > lineWidth) {
      const detail = createEmptyContentDataInLines(request.mouseContentHorizontalOffset - lineWidth);
      return request.fulfillContentEmpty(pos, detail);
    }
    const visibleRange = ctx.visibleRangeForPosition(lineNumber, column);
    if (!visibleRange) {
      return request.fulfillUnknown(pos);
    }
    const columnHorizontalOffset = visibleRange.left;
    if (request.mouseContentHorizontalOffset === columnHorizontalOffset) {
      return request.fulfillContentText(pos, null, { mightBeForeignElement: !!injectedText, injectedText });
    }
    const points = [];
    points.push({ offset: visibleRange.left, column });
    if (column > 1) {
      const visibleRange2 = ctx.visibleRangeForPosition(lineNumber, column - 1);
      if (visibleRange2) {
        points.push({ offset: visibleRange2.left, column: column - 1 });
      }
    }
    const lineMaxColumn = ctx.viewModel.getLineMaxColumn(lineNumber);
    if (column < lineMaxColumn) {
      const visibleRange2 = ctx.visibleRangeForPosition(lineNumber, column + 1);
      if (visibleRange2) {
        points.push({ offset: visibleRange2.left, column: column + 1 });
      }
    }
    points.sort((a, b) => a.offset - b.offset);
    const mouseCoordinates = request.pos.toClientCoordinates();
    const spanNodeClientRect = spanNode.getBoundingClientRect();
    const mouseIsOverSpanNode = spanNodeClientRect.left <= mouseCoordinates.clientX && mouseCoordinates.clientX <= spanNodeClientRect.right;
    for (let i = 1; i < points.length; i++) {
      const prev = points[i - 1];
      const curr = points[i];
      if (prev.offset <= request.mouseContentHorizontalOffset && request.mouseContentHorizontalOffset <= curr.offset) {
        const rng = new range.Range(lineNumber, prev.column, lineNumber, curr.column);
        return request.fulfillContentText(pos, rng, { mightBeForeignElement: !mouseIsOverSpanNode || !!injectedText, injectedText });
      }
    }
    return request.fulfillContentText(pos, null, { mightBeForeignElement: !mouseIsOverSpanNode || !!injectedText, injectedText });
  }
  /**
   * Most probably WebKit browsers and Edge
   */
  static _doHitTestWithCaretRangeFromPoint(ctx, request) {
    const lineNumber = ctx.getLineNumberAtVerticalOffset(request.mouseVerticalOffset);
    const lineVerticalOffset = ctx.getVerticalOffsetForLineNumber(lineNumber);
    const lineCenteredVerticalOffset = lineVerticalOffset + Math.floor(ctx.lineHeight / 2);
    let adjustedPageY = request.pos.y + (lineCenteredVerticalOffset - request.mouseVerticalOffset);
    if (adjustedPageY <= request.editorPos.y) {
      adjustedPageY = request.editorPos.y + 1;
    }
    if (adjustedPageY >= request.editorPos.y + request.editorPos.height) {
      adjustedPageY = request.editorPos.y + request.editorPos.height - 1;
    }
    const adjustedPage = new editorDom.PageCoordinates(request.pos.x, adjustedPageY);
    const r = this._actualDoHitTestWithCaretRangeFromPoint(ctx, adjustedPage.toClientCoordinates());
    if (r.type === 1) {
      return r;
    }
    return this._actualDoHitTestWithCaretRangeFromPoint(ctx, request.pos.toClientCoordinates());
  }
  static _actualDoHitTestWithCaretRangeFromPoint(ctx, coords) {
    const shadowRoot = dom.getShadowRoot(ctx.viewDomNode);
    let range2;
    if (shadowRoot) {
      if (typeof shadowRoot.caretRangeFromPoint === "undefined") {
        range2 = shadowCaretRangeFromPoint(shadowRoot, coords.clientX, coords.clientY);
      } else {
        range2 = shadowRoot.caretRangeFromPoint(coords.clientX, coords.clientY);
      }
    } else {
      range2 = document.caretRangeFromPoint(coords.clientX, coords.clientY);
    }
    if (!range2 || !range2.startContainer) {
      return new UnknownHitTestResult();
    }
    const startContainer = range2.startContainer;
    if (startContainer.nodeType === startContainer.TEXT_NODE) {
      const parent1 = startContainer.parentNode;
      const parent2 = parent1 ? parent1.parentNode : null;
      const parent3 = parent2 ? parent2.parentNode : null;
      const parent3ClassName = parent3 && parent3.nodeType === parent3.ELEMENT_NODE ? parent3.className : null;
      if (parent3ClassName === viewLine.ViewLine.CLASS_NAME) {
        return HitTestResult.createFromDOMInfo(ctx, parent1, range2.startOffset);
      } else {
        return new UnknownHitTestResult(startContainer.parentNode);
      }
    } else if (startContainer.nodeType === startContainer.ELEMENT_NODE) {
      const parent1 = startContainer.parentNode;
      const parent2 = parent1 ? parent1.parentNode : null;
      const parent2ClassName = parent2 && parent2.nodeType === parent2.ELEMENT_NODE ? parent2.className : null;
      if (parent2ClassName === viewLine.ViewLine.CLASS_NAME) {
        return HitTestResult.createFromDOMInfo(ctx, startContainer, startContainer.textContent.length);
      } else {
        return new UnknownHitTestResult(startContainer);
      }
    }
    return new UnknownHitTestResult();
  }
  /**
   * Most probably Gecko
   */
  static _doHitTestWithCaretPositionFromPoint(ctx, coords) {
    const hitResult = document.caretPositionFromPoint(coords.clientX, coords.clientY);
    if (hitResult.offsetNode.nodeType === hitResult.offsetNode.TEXT_NODE) {
      const parent1 = hitResult.offsetNode.parentNode;
      const parent2 = parent1 ? parent1.parentNode : null;
      const parent3 = parent2 ? parent2.parentNode : null;
      const parent3ClassName = parent3 && parent3.nodeType === parent3.ELEMENT_NODE ? parent3.className : null;
      if (parent3ClassName === viewLine.ViewLine.CLASS_NAME) {
        return HitTestResult.createFromDOMInfo(ctx, hitResult.offsetNode.parentNode, hitResult.offset);
      } else {
        return new UnknownHitTestResult(hitResult.offsetNode.parentNode);
      }
    }
    if (hitResult.offsetNode.nodeType === hitResult.offsetNode.ELEMENT_NODE) {
      const parent1 = hitResult.offsetNode.parentNode;
      const parent1ClassName = parent1 && parent1.nodeType === parent1.ELEMENT_NODE ? parent1.className : null;
      const parent2 = parent1 ? parent1.parentNode : null;
      const parent2ClassName = parent2 && parent2.nodeType === parent2.ELEMENT_NODE ? parent2.className : null;
      if (parent1ClassName === viewLine.ViewLine.CLASS_NAME) {
        const tokenSpan = hitResult.offsetNode.childNodes[Math.min(hitResult.offset, hitResult.offsetNode.childNodes.length - 1)];
        if (tokenSpan) {
          return HitTestResult.createFromDOMInfo(ctx, tokenSpan, 0);
        }
      } else if (parent2ClassName === viewLine.ViewLine.CLASS_NAME) {
        return HitTestResult.createFromDOMInfo(ctx, hitResult.offsetNode, 0);
      }
    }
    return new UnknownHitTestResult(hitResult.offsetNode);
  }
  static _snapToSoftTabBoundary(position$1, viewModel) {
    const lineContent = viewModel.getLineContent(position$1.lineNumber);
    const { tabSize } = viewModel.model.getOptions();
    const newPosition = cursorAtomicMoveOperations.AtomicTabMoveOperations.atomicPosition(
      lineContent,
      position$1.column - 1,
      tabSize,
      2
      /* Nearest */
    );
    if (newPosition !== -1) {
      return new position.Position(position$1.lineNumber, newPosition + 1);
    }
    return position$1;
  }
  static _doHitTest(ctx, request) {
    let result = new UnknownHitTestResult();
    if (typeof document.caretRangeFromPoint === "function") {
      result = this._doHitTestWithCaretRangeFromPoint(ctx, request);
    } else if (document.caretPositionFromPoint) {
      result = this._doHitTestWithCaretPositionFromPoint(ctx, request.pos.toClientCoordinates());
    }
    if (result.type === 1) {
      const injectedText = ctx.viewModel.getInjectedTextAt(result.position);
      const normalizedPosition = ctx.viewModel.normalizePosition(
        result.position,
        2
        /* None */
      );
      if (injectedText || !normalizedPosition.equals(result.position)) {
        result = new ContentHitTestResult(normalizedPosition, result.spanNode, injectedText);
      }
    }
    if (result.type === 1 && ctx.stickyTabStops) {
      result = new ContentHitTestResult(this._snapToSoftTabBoundary(result.position, ctx.viewModel), result.spanNode, result.injectedText);
    }
    return result;
  }
}
function shadowCaretRangeFromPoint(shadowRoot, x, y) {
  const range2 = document.createRange();
  let el = shadowRoot.elementFromPoint(x, y);
  if (el !== null) {
    while (el && el.firstChild && el.firstChild.nodeType !== el.firstChild.TEXT_NODE && el.lastChild && el.lastChild.firstChild) {
      el = el.lastChild;
    }
    const rect = el.getBoundingClientRect();
    const font = window.getComputedStyle(el, null).getPropertyValue("font");
    const text = el.innerText;
    let pixelCursor = rect.left;
    let offset = 0;
    let step;
    if (x > rect.left + rect.width) {
      offset = text.length;
    } else {
      const charWidthReader = CharWidthReader.getInstance();
      for (let i = 0; i < text.length + 1; i++) {
        step = charWidthReader.getCharWidth(text.charAt(i), font) / 2;
        pixelCursor += step;
        if (x < pixelCursor) {
          offset = i;
          break;
        }
        pixelCursor += step;
      }
    }
    range2.setStart(el.firstChild, offset);
    range2.setEnd(el.firstChild, offset);
  }
  return range2;
}
class CharWidthReader {
  constructor() {
    this._cache = {};
    this._canvas = document.createElement("canvas");
  }
  static getInstance() {
    if (!CharWidthReader._INSTANCE) {
      CharWidthReader._INSTANCE = new CharWidthReader();
    }
    return CharWidthReader._INSTANCE;
  }
  getCharWidth(char, font) {
    const cacheKey = char + font;
    if (this._cache[cacheKey]) {
      return this._cache[cacheKey];
    }
    const context = this._canvas.getContext("2d");
    context.font = font;
    const metrics = context.measureText(char);
    const width = metrics.width;
    this._cache[cacheKey] = width;
    return width;
  }
}
CharWidthReader._INSTANCE = null;
exports.HitTestContext = HitTestContext;
exports.MouseTarget = MouseTarget;
exports.MouseTargetFactory = MouseTargetFactory;
exports.PointerHandlerLastRenderData = PointerHandlerLastRenderData;
exports.shadowCaretRangeFromPoint = shadowCaretRangeFromPoint;
