PageSourceSearch

https://ainiro.io/assets/js/marked-tables.js

js ainiro.io collected 2026-09-24 22:08:35 UTC 7,067 bytes, 195 lines download raw bytes

vendor: 6,491 bytes, lines 1-168
1function extendedTables() {
2  return {
3    extensions: [
4      {
5        name: 'spanTable',
6        level: 'block', // Is this a block-level or inline-level tokenizer?
7        start(src) { return src.match(/^\n *([^\n ].*\|.*)\n/)?.index; }, // Hint to Marked.js to stop and check for a match
8        tokenizer(src, tokens) {
9          // const regex = this.tokenizer.rules.block.table;
10          const regex = new RegExp('^ *([^\\n ].*\\|.*\\n(?: *[^\\s].*\\n)*?)' // Header
11              + ' {0,3}(?:\\| *)?(:?-+:? *(?:\\| *:?-+:? *)*)(?:\\| *)?' // Align
12              + '(?:\\n((?:(?! *\\n| {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})' // Cells
13              + '(?:\\n+|$)| {0,3}#{1,6} | {0,3}>| {4}[^\\n]| {0,3}(?:`{3,}'
14              + '(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n| {0,3}(?:[*+-]|1[.)]) |'
15              + '<\\/?(?:address|article|aside|base|basefont|blockquote|body|'
16              + 'caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul)(?: +|\\n|\\/?>)|<(?:script|pre|style|textarea|!--)).*(?:\\n|$))*)\\n*|$)'); // Cells
17          const cap = regex.exec(src);
18
19          if (cap) {
20            const item = {
21              type: 'spanTable',
22              header: cap[1].replace(/\n$/, '').split('\n'),
23              align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
24              rows: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : []
25            };
26
27            // Get first header row to determine how many columns
28            item.header[0] = splitCells(item.header[0]);
29
30            const colCount = item.header[0].reduce((length, header) => {
31              return length + header.colspan;
32            }, 0);
33
34            if (colCount === item.align.length) {
35              item.raw = cap[0];
36
37              let i, j, k, row;
38
39              // Get alignment row (:---:)
40              let l = item.align.length;
41
42              for (i = 0; i < l; i++) {
43                if (/^ *-+: *$/.test(item.align[i])) {
44                  item.align[i] = 'right';
45                } else if (/^ *:-+: *$/.test(item.align[i])) {
46                  item.align[i] = 'center';
47                } else if (/^ *:-+ *$/.test(item.align[i])) {
48                  item.align[i] = 'left';
49                } else {
50                  item.align[i] = null;
51                }
52              }
53
54              // Get any remaining header rows
55              l = item.header.length;
56              for (i = 1; i < l; i++) {
57                item.header[i] = splitCells(item.header[i], colCount, item.header[i - 1]);
58              }
59
60              // Get main table cells
61              l = item.rows.length;
62              for (i = 0; i < l; i++) {
63                item.rows[i] = splitCells(item.rows[i], colCount, item.rows[i - 1]);
64              }
65
66              // header child tokens
67              l = item.header.length;
68              for (j = 0; j < l; j++) {
69                row = item.header[j];
70                for (k = 0; k < row.length; k++) {
71                  row[k].tokens = [];
72                  this.lexer.inline(row[k].text, row[k].tokens);
73                }
74              }
75
76              // cell child tokens
77              l = item.rows.length;
78              for (j = 0; j < l; j++) {
79                row = item.rows[j];
80                for (k = 0; k < row.length; k++) {
81                  row[k].tokens = [];
82                  this.lexer.inline(row[k].text, row[k].tokens);
83                }
84              }
85              return item;
86            }
87          }
88        },
89        renderer(token) {
90          let i, j, row, cell, col, text;
91          let output = '<table>';
92          output += '<thead>';
93          for (i = 0; i < token.header.length; i++) {
94            row = token.header[i];
95            let col = 0;
96            output += '<tr>';
97            for (j = 0; j < row.length; j++) {
98              cell = row[j];
99              text = this.parser.parseInline(cell.tokens);
100              output += getTableCell(text, cell, 'th', token.align[col]);
101              col += cell.colspan;
102            }
103            output += '</tr>';
104          }
105          output += '</thead>';
106          if (token.rows.length) {
107            output += '<tbody>';
108            for (i = 0; i < token.rows.length; i++) {
109              row = token.rows[i];
110              col = 0;
111              output += '<tr>';
112              for (j = 0; j < row.length; j++) {
113                cell = row[j];
114                text = this.parser.parseInline(cell.tokens);
115                output += getTableCell(text, cell, 'td', token.align[col]);
116                col += cell.colspan;
117              }
118              output += '</tr>';
119            }
120            output += '</tbody>';
121          }
122          output += '</table>';
123          return output;
124        }
125      }
126    ]
127  };
128}
129  
130const getTableCell = (text, cell, type, align) => {
131  if (!cell.rowspan) {
132    return '';
133  }
134  const tag = `<${type}`
135            + `${cell.colspan > 1 ? ` colspan=${cell.colspan}` : ''}`
136            + `${cell.rowspan > 1 ? ` rowspan=${cell.rowspan}` : ''}`
137            + `${align ? ` align=${align}` : ''}>`;
138  return `${tag + text}</${type}>\n`;
139};
140
141const splitCells = (tableRow, count, prevRow = []) => {
142  const cells = [...tableRow.matchAll(/(?:[^|\\]|\\.?)+(?:\|+|$)/g)].map((x) => x[0]);
143
144  // Remove first/last cell in a row if whitespace only and no leading/trailing pipe
145  if (!cells[0]?.trim()) { cells.shift(); }
146  if (!cells[cells.length - 1]?.trim()) { cells.pop(); }
147
148  let numCols = 0;
149  let i, j, trimmedCell, prevCell, prevCols;
150
151  for (i = 0; i < cells.length; i++) {
152    trimmedCell = cells[i].split(/\|+$/)[0];
153    cells[i] = {
154      rowspan: 1,
155      colspan: Math.max(cells[i].length - trimmedCell.length, 1),
156      text: trimmedCell.trim().replace(/\\\|/g, '|')
157      // display escaped pipes as normal character
158    };
159
160    // Handle Rowspan
161    if (trimmedCell.slice(-1) === '^' && prevRow.length) {
162      // Find matching cell in previous row
163      prevCols = 0;
164      for (j = 0; j < prevRow.length; j++) {
165        prevCell = prevRow[j];
166        if ((prevCols === numCols) && (prevCell.colspan === cells[i].colspan)) {
167          // merge into matching cell in previous row (the "target")
168          cells[i].rowSpanTarget = prevCell.rowSpanTarget ?? prevCell;
169          cells[i].rowSpanTarget.text += ` ${cells[i].text.slice(0, -1)}`;
170          cells[i].rowSpanTarget.rowspan += 1;
171          cells[i].rowspan = 0;
172          break;
173        }
174        prevCols += prevCell.colspan;
175        if (prevCols > numCols) { break; }
176      }
177    }
178
179    numCols += cells[i].colspan;
180  }
181
182  // Force main cell rows to match header column count
183  if (numCols > count) {
184    cells.splice(count);
185  } else {
186    while (numCols < count) {
187      cells.push({
188        colspan: 1,
189        text: ''
190      });
191      numCols += 1;
192    }
193  }
194  return cells;
195};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.