<commit_before>//@ts-check
import { isFirefox } from './tts/utils';

const BookReader = /** @type {typeof import('../BookReader').default} */(window.BookReader);

export class TextSelectionPlugin {

  constructor(avoidTspans = isFirefox()) {
    /**@type {PromiseLike<JQuery<HTMLElement>>} */
    this.djvuPagesPromise = null;
    // Using text elements insted of tspans for words because Firefox does not allow svg tspan strech.
    // Tspans are necessary on Chrome because they prevent newline character after every word when copying
    this.svgParagraphElement = "text";
    this.svgWordElement = "tspan";
    this.insertNewlines = avoidTspans
    if(avoidTspans) {
      this.svgParagraphElement = "g";
      this.svgWordElement = "text";
    }
  }

  /**
   * @param {string} ocaid
   */
  init(ocaid) {
    this.djvuPagesPromise = $.ajax({
      type: "GET",
      url: `https://cors.archive.org/cors/${ocaid}/${ocaid}_djvu.xml`,
      dataType: "xml",

      error: function (e) {
        return undefined;
      }
    }).then(xmlMap  => xmlMap && $(xmlMap).find("OBJECT"));
  }

  /**
   * @param {number} index
   * @returns {Promise<HTMLElement>}
   */
  async getPageText(index) {
    return (await this.djvuPagesPromise)[index];
  }

  /**
   * Intercept copied text to remove any styling applied to it
   * @param {JQuery} $container
   */
  interceptCopy($container) {
    $container.each((i, pageContainer) => {
      pageContainer.addEventListener('copy', (event) => {
        const selection = document.getSelection();
        event.clipboardData.setData('text/plain', selection.toString());
        event.preventDefault();
      });
    });
  }

  /**
   * Deselect all text after clicking elswhere in the BRcontainer
   * @param {JQuery} $container
   */
  deselectOnClick($container) {
    $container.one("mousedown", (event) => {
      if (window.getSelection) window.getSelection().removeAllRanges();
    })
  }

  /**
   * Stops page flipping on short click, allows text selction on longer mousedown
   * @param {JQuery} $container
   */
  stopPageFlip($container) {
    const $svg = $container.find('svg');
    let longPressTimer;
    this.interceptCopy($container);

    $svg.on("mousedown", (event) => {
      if ($(event.target).is(this.svgWordElement)) {
        event.stopPropagation();
        longPressTimer = window.setTimeout(() => {
          $svg.one("mouseup", (event) => {
            event.stopPropagation();
            clearTimeout(longPressTimer);
            this.deselectOnClick($container);
          });
        }, 250);
      }
    });
  }

  /**
   * @param {number} pageIndex
   * @param {JQuery} $container
   */
  async createTextLayer(pageIndex, $container) {
    const $svgLayers = $container.find('.textSelectionSVG');
    if ($svgLayers.length) return;
    const XMLpage = await this.getPageText(pageIndex);
    const XMLwidth = $(XMLpage).attr("width");
    const XMLheight = $(XMLpage).attr("height");

    const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
    svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
    svg.setAttribute("viewBox", `0 0 ${XMLwidth} ${XMLheight}`);
    $container.append(svg);
    svg.setAttribute('class', 'textSelectionSVG');
    svg.setAttribute('preserveAspectRatio', 'none');
    $(svg).css({
      "width": "100%",
      "position": "absolute",
      "height": "100%",
      "top": "0",
      "left": "0",
    });

    $(XMLpage).find("PARAGRAPH").each((i, paragraph) => {
      // Adding text element for each paragraph in the page
      const paragSvg = document.createElementNS("http://www.w3.org/2000/svg", this.svgParagraphElement);<commit_msg>Fix testcafe failing<commit_after>//@ts-check
import { isFirefox } from './tts/utils';

const BookReader = /** @type {typeof import('../BookReader').default} */(window.BookReader);

export class TextSelectionPlugin {

  constructor(avoidTspans = isFirefox()) {
    /**@type {PromiseLike<JQuery<HTMLElement>|undefined>} */
    this.djvuPagesPromise = null;
    // Using text elements insted of tspans for words because Firefox does not allow svg tspan strech.
    // Tspans are necessary on Chrome because they prevent newline character after every word when copying
    this.svgParagraphElement = "text";
    this.svgWordElement = "tspan";
    this.insertNewlines = avoidTspans
    if(avoidTspans) {
      this.svgParagraphElement = "g";
      this.svgWordElement = "text";
    }
  }

  /**
   * @param {string} ocaid
   */
  init(ocaid) {
    this.djvuPagesPromise = $.ajax({
      type: "GET",
      url: `https://cors.archive.org/cors/${ocaid}/${ocaid}_djvu.xml`,
      dataType: "xml",

      error: function (e) {
        return undefined;
      }
    }).then(xmlMap  => xmlMap && $(xmlMap).find("OBJECT"));
  }

  /**
   * @param {number} index
   * @returns {Promise<HTMLElement|undefined>}
   */
  async getPageText(index) {
    const XMLpagesArr = await this.djvuPagesPromise;
    if (XMLpagesArr) return XMLpagesArr[index];
  }

  /**
   * Intercept copied text to remove any styling applied to it
   * @param {JQuery} $container
   */
  interceptCopy($container) {
    $container.each((i, pageContainer) => {
      pageContainer.addEventListener('copy', (event) => {
        const selection = document.getSelection();
        event.clipboardData.setData('text/plain', selection.toString());
        event.preventDefault();
      });
    });
  }

  /**
   * Deselect all text after clicking elswhere in the BRcontainer
   * @param {JQuery} $container
   */
  deselectOnClick($container) {
    $container.one("mousedown", (event) => {
      if (window.getSelection) window.getSelection().removeAllRanges();
    })
  }

  /**
   * Stops page flipping on short click, allows text selction on longer mousedown
   * @param {JQuery} $container
   */
  stopPageFlip($container) {
    const $svg = $container.find('svg');
    let longPressTimer;
    this.interceptCopy($container);

    $svg.on("mousedown", (event) => {
      if ($(event.target).is(this.svgWordElement)) {
        event.stopPropagation();
        longPressTimer = window.setTimeout(() => {
          $svg.one("mouseup", (event) => {
            event.stopPropagation();
            clearTimeout(longPressTimer);
            this.deselectOnClick($container);
          });
        }, 250);
      }
    });
  }

  /**
   * @param {number} pageIndex
   * @param {JQuery} $container
   */
  async createTextLayer(pageIndex, $container) {
    const $svgLayers = $container.find('.textSelectionSVG');
    if ($svgLayers.length) return;
    const XMLpage = await this.getPageText(pageIndex);
    if(!XMLpage) return;
    const XMLwidth = $(XMLpage).attr("width");
    const XMLheight = $(XMLpage).attr("height");

    const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
    svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
    svg.setAttribute("viewBox", `0 0 ${XMLwidth} ${XMLheight}`);
    $container.append(svg);
    svg.setAttribute('class', 'textSelectionSVG');
    svg.setAttribute('preserveAspectRatio', 'none');
    $(svg).css({
      "width": "100%",
      "position": "absolute",
      "height": "100%",
      "top": "0",
      "left": "0",
    });

    $(XMLpage).find("PARAGRAPH").each((i, paragraph) => {
      // Adding text element for each paragraph in the page
      const paragSvg = document.createElementNS("http://www.w3.org/2000/svg", this.svgParagraphElement);
{'fraction_non_alphanumeric': 0.15977443609022557, 'fraction_numerical': 0.007518796992481203, 'mean_word_length': 3.0682687056253415, 'pattern_counts': {'":': 10, '<': 9, '<?xml version=': 0, '>': 25, 'https://': 2, 'lorem ipsum': 0, 'www.': 6, 'xml': 12}, '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': '918281', 'n_tokens_mistral': 2403, 'n_tokens_neox': 2317, 'n_words': 480}