<commit_before>/**
 * Copyright (c) Tiny Technologies, Inc. All rights reserved.
 * Licensed under the LGPL or a commercial license.
 * For LGPL see License.txt in the project root for license information.
 * For commercial licenses see https://www.tiny.cloud/
 */

import { HTMLElement, Element, Range, window } from '@ephox/dom-globals';
import { Fun } from '@ephox/katamari';
import { Spot, SpotPoint } from '@ephox/phoenix';
import { Css, Element as SugarElement, Height, Insert, Location, Node, Position, Remove, Scroll, Text, Traverse } from '@ephox/sugar';
import Editor from '../api/Editor';
import * as OuterPosition from '../frames/OuterPosition';
import Zwsp from '../text/Zwsp';

interface MarkerInfo {
  element: SugarElement;
  bottom: number;
  pos: Position;
  cleanup: () => void;
}

type ScrollFunc = (doc: SugarElement, scrollTop: number, marker: MarkerInfo, alignToTop?: boolean) => void;

const excludeFromDescend = (element: SugarElement) => Node.name(element) === 'textarea';

const descend = (element: SugarElement, offset: number): SpotPoint<SugarElement> => {
  const children = Traverse.children(element);
  if (children.length === 0 || excludeFromDescend(element)) {
    return Spot.point(element, offset);
  } else if (offset < children.length && !excludeFromDescend(children[offset])) {
    return Spot.point(children[offset], 0);
  } else {
    const last = children[children.length - 1];
    if (excludeFromDescend(last)) {
      return Spot.point(element, offset);
    } else {
      if (Node.name(last) === 'img') {
        return Spot.point(last, 1);
      } else if (Node.isText(last)) {
        return Spot.point(last, Text.get(last).length);
      } else {
        return Spot.point(last, Traverse.children(last).length);
      }
    }
  }
};

const markerInfo = (element: SugarElement, cleanupFun: () => void): MarkerInfo => {
  const pos = Location.absolute(element);
  const height = Height.get(element);
  return {
    element,
    bottom: pos.top() + height,
    pos,
    cleanup: cleanupFun
  };
};

const createMarker = (element: SugarElement, offset: number) => {
  const startPoint = descend(element, offset);
  const span = SugarElement.fromHtml('<span data-mce-bogus="all">' + Zwsp.ZWSP + '</span>');
  Insert.before(startPoint.element(), span);

  return markerInfo(span, () => Remove.remove(span));
};

const elementMarker = (element: HTMLElement): MarkerInfo => {
  return markerInfo(SugarElement.fromDom(element), Fun.noop);
};

const withMarker = (editor: Editor, f: ScrollFunc, rng: Range, alignToTop?: boolean) => {
  preserveWith(editor, (_s, _e) => applyWithMarker(editor, f, rng, alignToTop), rng);
};

const applyWithMarker = function (editor: Editor, f: ScrollFunc, rng: Range, alignToTop?: boolean) {
  const body = SugarElement.fromDom(editor.getBody());
  const doc = SugarElement.fromDom(editor.getDoc());

  Css.reflow(body);
  const scrollTop = Scroll.get(doc).top();
  const marker = createMarker(SugarElement.fromDom(rng.startContainer), rng.startOffset);
  f(doc, scrollTop, marker, alignToTop);<commit_msg>TINY-3663: Removed added dependency on Spot from phoenix<commit_after>/**
 * Copyright (c) Tiny Technologies, Inc. All rights reserved.
 * Licensed under the LGPL or a commercial license.
 * For LGPL see License.txt in the project root for license information.
 * For commercial licenses see https://www.tiny.cloud/
 */

import { HTMLElement, Element, Range, window } from '@ephox/dom-globals';
import { Fun } from '@ephox/katamari';
import { Css, Element as SugarElement, Height, Insert, Location, Node, Position, Remove, Scroll, Text, Traverse } from '@ephox/sugar';
import Editor from '../api/Editor';
import * as OuterPosition from '../frames/OuterPosition';
import Zwsp from '../text/Zwsp';

interface MarkerInfo {
  element: SugarElement;
  bottom: number;
  pos: Position;
  cleanup: () => void;
}

type ScrollFunc = (doc: SugarElement, scrollTop: number, marker: MarkerInfo, alignToTop?: boolean) => void;

const excludeFromDescend = (element: SugarElement) => Node.name(element) === 'textarea';

const descend = (element: SugarElement, offset: number): { element: SugarElement, offset: number } => {
  const children = Traverse.children(element);
  if (children.length === 0 || excludeFromDescend(element)) {
    return { element, offset };
  } else if (offset < children.length && !excludeFromDescend(children[offset])) {
    return { element: children[offset], offset: 0 };
  } else {
    const last = children[children.length - 1];
    if (excludeFromDescend(last)) {
      return { element, offset };
    } else {
      if (Node.name(last) === 'img') {
        return { element: last, offset: 1 };
      } else if (Node.isText(last)) {
        return { element: last, offset: Text.get(last).length };
      } else {
        return { element: last, offset: Traverse.children(last).length };
      }
    }
  }
};

const markerInfo = (element: SugarElement, cleanupFun: () => void): MarkerInfo => {
  const pos = Location.absolute(element);
  const height = Height.get(element);
  return {
    element,
    bottom: pos.top() + height,
    pos,
    cleanup: cleanupFun
  };
};

const createMarker = (element: SugarElement, offset: number): MarkerInfo => {
  const startPoint = descend(element, offset);
  const span = SugarElement.fromHtml('<span data-mce-bogus="all">' + Zwsp.ZWSP + '</span>');
  Insert.before(startPoint.element, span);

  return markerInfo(span, () => Remove.remove(span));
};

const elementMarker = (element: HTMLElement): MarkerInfo => {
  return markerInfo(SugarElement.fromDom(element), Fun.noop);
};

const withMarker = (editor: Editor, f: ScrollFunc, rng: Range, alignToTop?: boolean) => {
  preserveWith(editor, (_s, _e) => applyWithMarker(editor, f, rng, alignToTop), rng);
};

const applyWithMarker = function (editor: Editor, f: ScrollFunc, rng: Range, alignToTop?: boolean) {
  const body = SugarElement.fromDom(editor.getBody());
  const doc = SugarElement.fromDom(editor.getDoc());

  Css.reflow(body);
  const scrollTop = Scroll.get(doc).top();
  const marker = createMarker(SugarElement.fromDom(rng.startContainer), rng.startOffset);
  f(doc, scrollTop, marker, alignToTop);
{'fraction_non_alphanumeric': 0.14656981436642452, 'fraction_numerical': 0.001937046004842615, 'mean_word_length': 4.3276010318142735, 'pattern_counts': {'":': 0, '<': 10, '<?xml version=': 0, '>': 30, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4115748', 'n_tokens_mistral': 1914, 'n_tokens_neox': 1890, 'n_words': 521}