kosei-n 2 лет назад
Родитель
Сommit
fdf1c1274d

+ 1 - 0
packages/editor/src/services/table-util/index.d.ts

@@ -0,0 +1 @@
+export * from './markdown-table';

+ 2 - 1
packages/editor/src/services/table-util/insert-new-row-to-table-markdown.ts

@@ -1,5 +1,6 @@
 import { EditorView } from '@codemirror/view';
-import { MarkdownTable } from '@growi/core/dist/models';
+
+import { MarkdownTable } from './markdown-table';
 
 // https://regex101.com/r/7BN2fR/10
 const linePartOfTableRE = /^([^\r\n|]*)\|(([^\r\n|]*\|)+)$/;

+ 24 - 0
packages/editor/src/services/table-util/markdown-table.d.ts

@@ -0,0 +1,24 @@
+export declare class MarkdownTable {
+
+  // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+  static fromHTMLTableTag(str: any): MarkdownTable;
+
+  // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+  static fromDSV(str: any, delimiter: any): MarkdownTable;
+
+  static fromMarkdownString(str: string): MarkdownTable;
+
+  // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+  constructor(table: any, options: any);
+
+  table: any;
+
+  options: any;
+
+  toString(): any;
+
+  clone(): MarkdownTable;
+
+  normalizeCells(): MarkdownTable;
+
+}

+ 147 - 0
packages/editor/src/services/table-util/markdown-table.js

@@ -0,0 +1,147 @@
+import csvToMarkdown from 'csv-to-markdown-table';
+import markdownTable from 'markdown-table';
+import stringWidth from 'string-width';
+
+// https://github.com/markdown-it/markdown-it/blob/d29f421927e93e88daf75f22089a3e732e195bd2/lib/rules_block/table.js#L83
+// https://regex101.com/r/7BN2fR/7
+const tableAlignmentLineRE = /^[-:|][-:|\s]*$/;
+const tableAlignmentLineNegRE = /^[^-:]*$/; // it is need to check to ignore empty row which is matched above RE
+const linePartOfTableRE = /^\|[^\r\n]*|[^\r\n]*\|$|([^|\r\n]+\|[^|\r\n]*)+/; // own idea
+
+const defaultOptions = { stringLength: stringWidth };
+
+/**
+ * markdown table class for markdown-table module
+ *   ref. https://github.com/wooorm/markdown-table
+ */
+export class MarkdownTable {
+
+  constructor(table, options) {
+    this.table = table || [];
+    this.options = Object.assign(options || {}, defaultOptions);
+
+    this.toString = this.toString.bind(this);
+  }
+
+  toString() {
+    return markdownTable(this.table, this.options);
+  }
+
+  /**
+   * returns cloned Markdowntable instance
+   * (This method clones only the table field.)
+   */
+  clone() {
+    const newTable = [];
+    for (let i = 0; i < this.table.length; i++) {
+      newTable.push([].concat(this.table[i]));
+    }
+    return new MarkdownTable(newTable, this.options);
+  }
+
+  /**
+   * normalize all cell data(trim & convert the newline character to space or pad '' if cell data is null)
+   */
+  normalizeCells() {
+    for (let i = 0; i < this.table.length; i++) {
+      for (let j = 0; j < this.table[i].length; j++) {
+        if (this.table[i][j] != null) {
+          this.table[i][j] = this.table[i][j].trim().replace(/\r?\n/g, ' ');
+        }
+        else {
+          this.table[i][j] = '';
+        }
+      }
+    }
+
+    return this;
+  }
+
+  /**
+   * return a MarkdownTable instance made from a string of HTML table tag
+   *
+   * If a parser error occurs, an error object with an error message is thrown.
+   * The error message is a innerHTML, so must not assign it into element.innerHTML because it can lead to Mutation-based XSS
+   */
+  static fromHTMLTableTag(str) {
+    // set up DOMParser
+    const domParser = new (window.DOMParser)();
+
+    // use DOMParser to prevent DOM based XSS (https://developer.mozilla.org/en-US/docs/Web/API/DOMParser)
+    const dom = domParser.parseFromString(str, 'application/xml');
+
+    if (dom.querySelector('parsererror')) {
+      throw new Error(dom.documentElement.innerHTML);
+    }
+
+    const tableElement = dom.querySelector('table');
+    const trElements = tableElement.querySelectorAll('tr');
+
+    const table = [];
+    let maxRowSize = 0;
+    for (let i = 0; i < trElements.length; i++) {
+      const row = [];
+      const cellElements = trElements[i].querySelectorAll('th,td');
+      for (let j = 0; j < cellElements.length; j++) {
+        row.push(cellElements[j].innerHTML);
+      }
+      table.push(row);
+
+      if (maxRowSize < row.length) maxRowSize = row.length;
+    }
+
+    const align = [];
+    for (let i = 0; i < maxRowSize; i++) {
+      align.push('');
+    }
+
+    return new MarkdownTable(table, { align });
+  }
+
+  /**
+   * return a MarkdownTable instance made from a string of delimiter-separated values
+   */
+  static fromDSV(str, delimiter) {
+    return MarkdownTable.fromMarkdownString(csvToMarkdown(str, delimiter, true));
+  }
+
+  /**
+   * return a MarkdownTable instance
+   *   ref. https://github.com/wooorm/markdown-table
+   * @param {string} str markdown string
+   */
+  static fromMarkdownString(str) {
+    const arrMDTableLines = str.split(/(\r\n|\r|\n)/);
+    const contents = [];
+    let aligns = [];
+    for (let n = 0; n < arrMDTableLines.length; n++) {
+      const line = arrMDTableLines[n];
+
+      if (tableAlignmentLineRE.test(line) && !tableAlignmentLineNegRE.test(line)) {
+        // parse line which described alignment
+        const alignRuleRE = [
+          { align: 'c', regex: /^:-+:$/ },
+          { align: 'l', regex: /^:-+$/ },
+          { align: 'r', regex: /^-+:$/ },
+        ];
+        let lineText = '';
+        lineText = line.replace(/^\||\|$/g, ''); // strip off pipe charactor which is placed head of line and last of line.
+        lineText = lineText.replace(/\s*/g, '');
+        aligns = lineText.split(/\|/).map((col) => {
+          const rule = alignRuleRE.find((rule) => { return col.match(rule.regex) });
+          return (rule != null) ? rule.align : '';
+        });
+      }
+      else if (linePartOfTableRE.test(line)) {
+        // parse line whether header or body
+        let lineText = '';
+        lineText = line.replace(/\s*\|\s*/g, '|');
+        lineText = lineText.replace(/^\||\|$/g, ''); // strip off pipe charactor which is placed head of line and last of line.
+        const row = lineText.split(/\|/);
+        contents.push(row);
+      }
+    }
+    return (new MarkdownTable(contents, { align: aligns }));
+  }
+
+}