Files
datascape/assets/editor/tables.js
2026-04-15 09:14:24 +02:00

275 lines
10 KiB
JavaScript

window.EditorTables = (function () {
function repeat(ch, n) {
var s = '';
for (var i = 0; i < n; i++) s += ch;
return s;
}
function padLeft(s, w) {
while (s.length < w) s = ' ' + s;
return s;
}
function padRight(s, w) {
while (s.length < w) s += ' ';
return s;
}
function padCenter(s, w) {
while (s.length < w) {
s = s + ' ';
if (s.length < w) s = ' ' + s;
}
return s;
}
function parseTableRow(line) {
var trimmed = line.trim();
if (trimmed.charAt(0) === '|') trimmed = trimmed.slice(1);
if (trimmed.charAt(trimmed.length - 1) === '|') trimmed = trimmed.slice(0, -1);
return trimmed.split('|').map(function (c) { return c.trim(); });
}
function isSeparatorRow(cells) {
return cells.every(function (c) { return /^:?-+:?$/.test(c); });
}
function parseAlignment(cell) {
var left = cell.charAt(0) === ':';
var right = cell.charAt(cell.length - 1) === ':';
if (left && right) return 'center';
if (left) return 'left';
if (right) return 'right';
return null;
}
function makeSepCell(width, align) {
if (align === 'left') return ':' + repeat('-', width - 1);
if (align === 'center') return ':' + repeat('-', width - 2) + ':';
if (align === 'right') return repeat('-', width - 1) + ':';
return repeat('-', width);
}
function findTableRange(text, cursorPos) {
var lines = text.split('\n');
var charCount = 0;
var cursorLine = 0;
for (var i = 0; i < lines.length; i++) {
if (charCount + lines[i].length >= cursorPos && charCount <= cursorPos) {
cursorLine = i;
}
charCount += lines[i].length + 1;
}
if (!/^\|.*\|$/.test(lines[cursorLine].trim())) return null;
var start = cursorLine;
while (start > 0 && /^\|.*\|$/.test(lines[start - 1].trim())) start--;
var end = cursorLine;
while (end < lines.length - 1 && /^\|.*\|$/.test(lines[end + 1].trim())) end++;
if (end - start < 1) return null;
return { start: start, end: end, lines: lines, cursorLine: cursorLine };
}
function formatTableText(text, cursorPos) {
var range = findTableRange(text, cursorPos);
if (!range) return null;
var rows = [];
var sepIndex = -1;
var alignments = [];
for (var i = range.start; i <= range.end; i++) {
var cells = parseTableRow(range.lines[i]);
if (sepIndex === -1 && isSeparatorRow(cells)) {
sepIndex = rows.length;
alignments = cells.map(function (c) { return parseAlignment(c); });
}
rows.push(cells);
}
if (sepIndex === -1) return null;
var colCount = 0;
rows.forEach(function (r) { if (r.length > colCount) colCount = r.length; });
while (alignments.length < colCount) alignments.push(null);
rows = rows.map(function (r) {
while (r.length < colCount) r.push('');
return r;
});
var widths = [];
for (var c = 0; c < colCount; c++) {
var max = 3;
for (var r = 0; r < rows.length; r++) {
if (r === sepIndex) continue;
if (rows[r][c].length > max) max = rows[r][c].length;
}
widths.push(max);
}
var formatted = rows.map(function (row, ri) {
var cells = row.map(function (cell, ci) {
var w = widths[ci];
if (ri === sepIndex) return makeSepCell(w, alignments[ci]);
var align = alignments[ci];
if (align === 'right') return padLeft(cell, w);
if (align === 'center') return padCenter(cell, w);
return padRight(cell, w);
});
return '| ' + cells.join(' | ') + ' |';
});
var beforeTable = range.lines.slice(0, range.start).join('\n');
var afterTable = range.lines.slice(range.end + 1).join('\n');
var parts = [];
if (beforeTable) parts.push(beforeTable);
parts.push(formatted.join('\n'));
if (afterTable) parts.push(afterTable);
var newText = parts.join('\n');
var oldBeforeLen = 0;
for (var i = 0; i < range.start; i++) oldBeforeLen += range.lines[i].length + 1;
var cursorInTable = cursorPos - oldBeforeLen;
var newTableText = formatted.join('\n');
var newCursor = (beforeTable ? beforeTable.length + 1 : 0) + Math.min(cursorInTable, newTableText.length);
return { text: newText, cursor: newCursor };
}
function getCursorColumn(text, cursorPos) {
var range = findTableRange(text, cursorPos);
if (!range) return null;
var lines = text.split('\n');
var charCount = 0;
for (var i = 0; i < range.cursorLine; i++) charCount += lines[i].length + 1;
var lineOffset = cursorPos - charCount;
var line = lines[range.cursorLine];
var col = -1;
for (var c = 0; c < line.length; c++) {
if (line.charAt(c) === '|') {
col++;
if (c >= lineOffset) return Math.max(col - 1, 0);
}
}
return Math.max(col, 0);
}
function setColumnAlignment(text, cursorPos, align) {
var range = findTableRange(text, cursorPos);
if (!range) return null;
var colIdx = getCursorColumn(text, cursorPos);
if (colIdx === null) return null;
var rows = [];
var sepIndex = -1;
for (var i = range.start; i <= range.end; i++) {
var cells = parseTableRow(range.lines[i]);
if (sepIndex === -1 && isSeparatorRow(cells)) sepIndex = rows.length;
rows.push(cells);
}
if (sepIndex === -1 || colIdx >= rows[sepIndex].length) return null;
var cell = rows[sepIndex][colIdx].replace(/:/g, '-');
if (align === 'left') cell = ':' + cell.slice(1);
else if (align === 'center') cell = ':' + cell.slice(1, -1) + ':';
else if (align === 'right') cell = cell.slice(0, -1) + ':';
rows[sepIndex][colIdx] = cell;
var newLines = range.lines.slice();
for (var i = 0; i < rows.length; i++) {
newLines[range.start + i] = '| ' + rows[i].join(' | ') + ' |';
}
return formatTableText(newLines.join('\n'), cursorPos);
}
function insertColumn(text, cursorPos) {
var range = findTableRange(text, cursorPos);
if (!range) return null;
var colIdx = getCursorColumn(text, cursorPos);
if (colIdx === null) return null;
var newLines = range.lines.slice();
var sepIndex = -1;
for (var i = range.start; i <= range.end; i++) {
var cells = parseTableRow(range.lines[i]);
var isSep = sepIndex === -1 && isSeparatorRow(cells);
if (isSep) sepIndex = i;
cells.splice(colIdx, 0, isSep ? '---' : '');
newLines[i] = '| ' + cells.join(' | ') + ' |';
}
return formatTableText(newLines.join('\n'), cursorPos);
}
function deleteColumn(text, cursorPos) {
var range = findTableRange(text, cursorPos);
if (!range) return null;
var colIdx = getCursorColumn(text, cursorPos);
if (colIdx === null) return null;
var newLines = range.lines.slice();
for (var i = range.start; i <= range.end; i++) {
var cells = parseTableRow(range.lines[i]);
if (cells.length <= 1) return null;
cells.splice(colIdx, 1);
newLines[i] = '| ' + cells.join(' | ') + ' |';
}
return formatTableText(newLines.join('\n'), cursorPos);
}
function insertRow(text, cursorPos) {
var range = findTableRange(text, cursorPos);
if (!range) return null;
var colCount = 0;
for (var i = range.start; i <= range.end; i++) {
var cells = parseTableRow(range.lines[i]);
if (cells.length > colCount) colCount = cells.length;
}
var emptyCells = [];
for (var c = 0; c < colCount; c++) emptyCells.push('');
var newLines = range.lines.slice();
newLines.splice(range.cursorLine + 1, 0, '| ' + emptyCells.join(' | ') + ' |');
return formatTableText(newLines.join('\n'), cursorPos);
}
function insertRowBelow(text, cursorPos) {
var range = findTableRange(text, cursorPos);
if (!range) return null;
var result = insertRow(text, cursorPos);
if (!result) return null;
var lines = result.text.split('\n');
var cursor = 0;
for (var i = 0; i <= range.cursorLine; i++) cursor += lines[i].length + 1;
cursor += 2; // skip leading '| '
return { text: result.text, cursor: cursor };
}
function deleteRow(text, cursorPos) {
var range = findTableRange(text, cursorPos);
if (!range) return null;
var sepIndex = -1;
for (var i = range.start; i <= range.end; i++) {
if (isSeparatorRow(parseTableRow(range.lines[i]))) { sepIndex = i; break; }
}
if (range.cursorLine === range.start || range.cursorLine === sepIndex) return null;
var newLines = range.lines.slice();
newLines.splice(range.cursorLine, 1);
var newCursor = cursorPos;
if (range.cursorLine < newLines.length) {
var charCount = 0;
for (var i = 0; i < range.cursorLine; i++) charCount += newLines[i].length + 1;
newCursor = charCount;
}
return formatTableText(newLines.join('\n'), Math.min(newCursor, newLines.join('\n').length));
}
return {
formatTableText: formatTableText,
setColumnAlignment: setColumnAlignment,
insertColumn: insertColumn,
deleteColumn: deleteColumn,
insertRow: insertRow,
insertRowBelow: insertRowBelow,
deleteRow: deleteRow,
};
})();