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.