<commit_before>/*
 * table-columns.ts
 *
 * Copyright (C) 2019-20 by RStudio, PBC
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

import { Node as ProsemirrorNode } from 'prosemirror-model';
import { EditorView } from 'prosemirror-view';
import { Transaction } from 'prosemirror-state';
import { findChildrenByType } from 'prosemirror-utils';

import { PandocTokenType } from '../../api/pandoc';

import { CssAlignment } from './table-commands';

export function tableColumnAlignments(node: ProsemirrorNode) {
  // get the first row (schema requires at least 1 row)
  const firstRow = node.firstChild!;

  // determine # of columns
  const cols = firstRow.childCount;

  // return alignments
  const alignments = new Array<PandocTokenType>(cols);
  for (let i = 0; i < cols; i++) {
    const cell = firstRow.child(i);
    const align: CssAlignment = cell.attrs.align;
    switch (align) {
      case CssAlignment.Left:
        alignments[i] = PandocTokenType.AlignLeft;
        break;
      case CssAlignment.Right:
        alignments[i] = PandocTokenType.AlignRight;
        break;
      case CssAlignment.Center:
        alignments[i] = PandocTokenType.AlignCenter;
        break;
      case null:
      default:
        alignments[i] = PandocTokenType.AlignDefault;
        break;
    }
  }
  return alignments;
}

export function tableColumnWidths(node: ProsemirrorNode) {
  // get the first row (schema requires at least 1 row)
  const firstRow = node.firstChild!;

  // determine # of columns
  const cols = firstRow.childCount;

  // width percentages
  let widths = new Array<number>(cols).fill(0);

  // take note of explicitly sized columns
  const totalWidth = node.attrs.width;
  let availableWidth = totalWidth;
  let colsWithWidth = 0;
  for (let i = 0; i < cols; i++) {
    const col = firstRow.child(i);
    if (col.attrs.colwidth && col.attrs.colwidth[0]) {
      widths[i] = col.attrs.colwidth[0];
      colsWithWidth++;
      availableWidth -= widths[i];
    }
  }

  // allocate remaining widths if we have at least 1 sized columns (otherwise
  // we leave all of the widths at 0 which results in no sizing)
  if (colsWithWidth > 0) {
    if (availableWidth > 0) {
      const defaultWidth = availableWidth / (cols - colsWithWidth);
      for (let i = 0; i < cols; i++) {
        const col = firstRow.child(i);
        if (!col.attrs.colwidth || !col.attrs.colwidth[0]) {
          widths[i] = defaultWidth;
        }
      }
    }

    // convert widths to percentages
    widths = widths.map(width => width / totalWidth);
  }

  return widths;
}

export function fixupTableWidths(view: EditorView) {
  return (tr: Transaction) => {
    const schema = tr.doc.type.schema;

    // don't do fixup if we aren't connected to the DOM
    if (!view.dom || !view.dom.isConnected) {
      return tr;
    }

    const tables = findChildrenByType(tr.doc, schema.nodes.table);
    for (const table of tables) {
      // get table width
      const el = view.domAtPos(table.pos).node as HTMLElement;
      const containerWidth = table.node.attrs.width || el.clientWidth;

      // resolve colpercents (read by tokenzier)
      const colpercents = table.node.attrs.colpercents;
      if (colpercents) {
        // for each row
        table.node.forEach((rowNode, rowOffset, r) => {
          // for each cell
          rowNode.forEach((cellNode, cellOffset, c) => {
            const cellPos = table.pos + 1 + rowOffset + 1 + cellOffset;
            const colWidth = [colpercents[c] * containerWidth];
            if (colWidth !== cellNode.attrs.colwidth) {
              tr.setNodeMarkup(cellPos, cellNode.type, {
                ...cellNode.attrs,
                colwidth: colWidth,<commit_msg>don't fixup table widths when containerWidth is 0<commit_after>/*
 * table-columns.ts
 *
 * Copyright (C) 2019-20 by RStudio, PBC
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

import { Node as ProsemirrorNode } from 'prosemirror-model';
import { EditorView } from 'prosemirror-view';
import { Transaction } from 'prosemirror-state';
import { findChildrenByType } from 'prosemirror-utils';

import { PandocTokenType } from '../../api/pandoc';

import { CssAlignment } from './table-commands';

export function tableColumnAlignments(node: ProsemirrorNode) {
  // get the first row (schema requires at least 1 row)
  const firstRow = node.firstChild!;

  // determine # of columns
  const cols = firstRow.childCount;

  // return alignments
  const alignments = new Array<PandocTokenType>(cols);
  for (let i = 0; i < cols; i++) {
    const cell = firstRow.child(i);
    const align: CssAlignment = cell.attrs.align;
    switch (align) {
      case CssAlignment.Left:
        alignments[i] = PandocTokenType.AlignLeft;
        break;
      case CssAlignment.Right:
        alignments[i] = PandocTokenType.AlignRight;
        break;
      case CssAlignment.Center:
        alignments[i] = PandocTokenType.AlignCenter;
        break;
      case null:
      default:
        alignments[i] = PandocTokenType.AlignDefault;
        break;
    }
  }
  return alignments;
}

export function tableColumnWidths(node: ProsemirrorNode) {
  // get the first row (schema requires at least 1 row)
  const firstRow = node.firstChild!;

  // determine # of columns
  const cols = firstRow.childCount;

  // width percentages
  let widths = new Array<number>(cols).fill(0);

  // take note of explicitly sized columns
  const totalWidth = node.attrs.width;
  let availableWidth = totalWidth;
  let colsWithWidth = 0;
  for (let i = 0; i < cols; i++) {
    const col = firstRow.child(i);
    if (col.attrs.colwidth && col.attrs.colwidth[0]) {
      widths[i] = col.attrs.colwidth[0];
      colsWithWidth++;
      availableWidth -= widths[i];
    }
  }

  // allocate remaining widths if we have at least 1 sized columns (otherwise
  // we leave all of the widths at 0 which results in no sizing)
  if (colsWithWidth > 0) {
    if (availableWidth > 0) {
      const defaultWidth = availableWidth / (cols - colsWithWidth);
      for (let i = 0; i < cols; i++) {
        const col = firstRow.child(i);
        if (!col.attrs.colwidth || !col.attrs.colwidth[0]) {
          widths[i] = defaultWidth;
        }
      }
    }

    // convert widths to percentages
    widths = widths.map(width => width / totalWidth);
  }

  return widths;
}

export function fixupTableWidths(view: EditorView) {
  return (tr: Transaction) => {
    const schema = tr.doc.type.schema;

    // don't do fixup if we aren't connected to the DOM
    if (!view.dom || !view.dom.isConnected) {
      return tr;
    }

    const tables = findChildrenByType(tr.doc, schema.nodes.table);
    for (const table of tables) {
      // get table width
      const el = view.domAtPos(table.pos).node as HTMLElement;
      const containerWidth = table.node.attrs.width || el.clientWidth;
      if (containerWidth === 0) {
        continue;
      }

      // resolve colpercents (read by tokenzier)
      const colpercents = table.node.attrs.colpercents;
      if (colpercents) {
        // for each row
        table.node.forEach((rowNode, rowOffset, r) => {
          // for each cell
          rowNode.forEach((cellNode, cellOffset, c) => {
            const cellPos = table.pos + 1 + rowOffset + 1 + cellOffset;
            const colWidth = [colpercents[c] * containerWidth];
            if (colWidth !== cellNode.attrs.colwidth) {
              tr.setNodeMarkup(cellPos, cellNode.type, {
                ...cellNode.attrs,
                colwidth: colWidth,
{'fraction_non_alphanumeric': 0.10767590618336886, 'fraction_numerical': 0.006159677801468846, 'mean_word_length': 2.7441241685144124, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 19, 'https://': 0, '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': '3749079', 'n_tokens_mistral': 2632, 'n_tokens_neox': 2511, 'n_words': 774}