Update private/admin/admin.js
Browse files- private/admin/admin.js +54 -131
private/admin/admin.js
CHANGED
|
@@ -2103,30 +2103,23 @@ class AdminPanel {
|
|
| 2103 |
return `<span class="math-inline math-fallback">${this.escapeHtmlDisplay(fallback)}</span>`;
|
| 2104 |
}
|
| 2105 |
|
| 2106 |
-
/**
|
| 2107 |
-
* Parse markdown tables into HTML
|
| 2108 |
-
* Optimized for performance with array building
|
| 2109 |
-
* @private
|
| 2110 |
-
*/
|
| 2111 |
_parseMarkdownTables(content, placeholders) {
|
| 2112 |
if (!content) return content;
|
| 2113 |
|
| 2114 |
const lines = content.split('\n');
|
| 2115 |
const result = [];
|
| 2116 |
-
|
| 2117 |
|
| 2118 |
-
|
| 2119 |
const line = lines[i];
|
| 2120 |
-
const trimmed = line.trim();
|
| 2121 |
|
| 2122 |
-
if (
|
| 2123 |
const nextLine = lines[i + 1];
|
| 2124 |
-
|
| 2125 |
if (nextLine && /^\|[\s:|-]+\|$/.test(nextLine.trim())) {
|
| 2126 |
const tableLines = [line];
|
| 2127 |
let j = i + 1;
|
| 2128 |
|
| 2129 |
-
while (j <
|
| 2130 |
tableLines.push(lines[j]);
|
| 2131 |
j++;
|
| 2132 |
}
|
|
@@ -2136,76 +2129,52 @@ class AdminPanel {
|
|
| 2136 |
const placeholder = `__TABLE_BLOCK_${placeholders.length}__`;
|
| 2137 |
placeholders.push(tableHtml);
|
| 2138 |
result.push(placeholder);
|
| 2139 |
-
i = j
|
| 2140 |
continue;
|
| 2141 |
}
|
| 2142 |
}
|
| 2143 |
}
|
| 2144 |
|
| 2145 |
result.push(line);
|
|
|
|
| 2146 |
}
|
| 2147 |
|
| 2148 |
return result.join('\n');
|
| 2149 |
}
|
| 2150 |
|
| 2151 |
-
/**
|
| 2152 |
-
* Convert table lines to HTML
|
| 2153 |
-
* Optimized with array building instead of string concatenation
|
| 2154 |
-
* @private
|
| 2155 |
-
*/
|
| 2156 |
_convertTableToHtml(lines) {
|
| 2157 |
if (lines.length < 2) return null;
|
| 2158 |
|
| 2159 |
-
|
| 2160 |
-
const
|
| 2161 |
-
const parts = line.split('|');
|
| 2162 |
-
return parts.slice(1, -1).map(c => c.trim());
|
| 2163 |
-
};
|
| 2164 |
-
|
| 2165 |
-
const headerCells = extractCells(lines[0].trim());
|
| 2166 |
-
if (headerCells.length === 0) return null;
|
| 2167 |
-
|
| 2168 |
-
const headerCount = headerCells.length;
|
| 2169 |
|
| 2170 |
-
|
| 2171 |
-
const
|
| 2172 |
-
|
| 2173 |
if (cell.startsWith(':') && cell.endsWith(':')) return 'center';
|
| 2174 |
if (cell.endsWith(':')) return 'right';
|
| 2175 |
return 'left';
|
| 2176 |
});
|
| 2177 |
|
| 2178 |
-
|
| 2179 |
-
|
| 2180 |
-
|
| 2181 |
-
|
| 2182 |
-
|
| 2183 |
-
const align = aligns[i] || 'left';
|
| 2184 |
-
const content = this._formatInlineContent(headerCells[i]);
|
| 2185 |
-
htmlParts.push(`<th style="text-align:${align}">${content}</th>`);
|
| 2186 |
-
}
|
| 2187 |
-
|
| 2188 |
-
htmlParts.push('</tr></thead><tbody>');
|
| 2189 |
|
| 2190 |
-
// Data rows (skip header and separator)
|
| 2191 |
for (let i = 2; i < lines.length; i++) {
|
| 2192 |
-
const cells =
|
| 2193 |
-
|
| 2194 |
-
|
| 2195 |
-
|
| 2196 |
-
|
| 2197 |
-
|
| 2198 |
-
|
| 2199 |
-
|
| 2200 |
-
const content = this._formatInlineContent(cellContent);
|
| 2201 |
-
htmlParts.push(`<td style="text-align:${align}">${content}</td>`);
|
| 2202 |
-
}
|
| 2203 |
-
|
| 2204 |
-
htmlParts.push('</tr>');
|
| 2205 |
}
|
| 2206 |
|
| 2207 |
-
|
| 2208 |
-
return
|
| 2209 |
}
|
| 2210 |
|
| 2211 |
_initCodeCopyButtons() {
|
|
@@ -3155,110 +3124,64 @@ class AdminPanel {
|
|
| 3155 |
return `<span style="padding:2px 6px;background:rgba(102, 126, 234, 0.1);border-radius:4px;font-family:'Times New Roman',Georgia,serif;font-style:italic;">${this.escapeHtml(rendered)}</span>`;
|
| 3156 |
}
|
| 3157 |
|
| 3158 |
-
/**
|
| 3159 |
-
* Parse markdown tables for PDF
|
| 3160 |
-
* Optimized with array building and cleaner logic
|
| 3161 |
-
* @private
|
| 3162 |
-
*/
|
| 3163 |
_parseMarkdownTablesForPdf(content) {
|
| 3164 |
if (!content) return content;
|
| 3165 |
-
|
| 3166 |
const lines = content.split('\n');
|
| 3167 |
const result = [];
|
| 3168 |
-
|
| 3169 |
-
const separatorRegex = /^\|[\s:|-]+\|$/;
|
| 3170 |
|
| 3171 |
-
|
| 3172 |
const line = lines[i];
|
| 3173 |
-
|
| 3174 |
-
|
| 3175 |
-
if (trimmed.startsWith('|') && trimmed.endsWith('|')) {
|
| 3176 |
const nextLine = lines[i + 1];
|
| 3177 |
-
|
| 3178 |
-
if (nextLine && separatorRegex.test(nextLine.trim())) {
|
| 3179 |
const tableLines = [line];
|
| 3180 |
let j = i + 1;
|
| 3181 |
-
|
| 3182 |
-
while (j < lineCount && lines[j].trim().startsWith('|')) {
|
| 3183 |
tableLines.push(lines[j]);
|
| 3184 |
j++;
|
| 3185 |
}
|
| 3186 |
-
|
| 3187 |
result.push(this._convertTableToHtmlForPdf(tableLines));
|
| 3188 |
-
i = j
|
| 3189 |
continue;
|
| 3190 |
}
|
| 3191 |
}
|
| 3192 |
-
|
| 3193 |
result.push(line);
|
|
|
|
| 3194 |
}
|
| 3195 |
-
|
| 3196 |
return result.join('\n');
|
| 3197 |
}
|
| 3198 |
|
| 3199 |
-
/**
|
| 3200 |
-
* Convert table lines to HTML for PDF
|
| 3201 |
-
* Optimized with array building instead of string concatenation
|
| 3202 |
-
* @private
|
| 3203 |
-
*/
|
| 3204 |
_convertTableToHtmlForPdf(lines) {
|
| 3205 |
if (lines.length < 2) return lines.join('\n');
|
| 3206 |
|
| 3207 |
-
|
| 3208 |
-
const
|
| 3209 |
-
const
|
| 3210 |
-
|
| 3211 |
-
|
| 3212 |
-
|
| 3213 |
-
// Parse alignments from separator
|
| 3214 |
-
const sepCells = extractCells(lines[1]);
|
| 3215 |
-
const aligns = sepCells.map(cell => {
|
| 3216 |
-
if (cell.startsWith(':') && cell.endsWith(':')) return 'center';
|
| 3217 |
-
if (cell.endsWith(':')) return 'right';
|
| 3218 |
return 'left';
|
| 3219 |
});
|
| 3220 |
|
| 3221 |
-
|
| 3222 |
-
|
| 3223 |
-
|
| 3224 |
-
|
| 3225 |
-
|
| 3226 |
-
|
| 3227 |
-
'<table style="width:100%;border-collapse:collapse;margin:20px 0;border-radius:12px;overflow:hidden;box-shadow:0 4px 12px rgba(0,0,0,0.1);">',
|
| 3228 |
-
'<thead><tr style="background:linear-gradient(135deg, #667eea 0%, #764ba2 100%);">'
|
| 3229 |
-
];
|
| 3230 |
-
|
| 3231 |
-
// Header row
|
| 3232 |
-
for (let i = 0; i < headerCount; i++) {
|
| 3233 |
-
const align = aligns[i] || 'left';
|
| 3234 |
-
const content = this._formatInlineContentForPdf(headerCells[i]);
|
| 3235 |
-
htmlParts.push(
|
| 3236 |
-
`<th style="text-align:${align};padding:14px 16px;color:#ffffff;font-weight:600;font-size:12px;text-transform:uppercase;letter-spacing:0.5px;border:none;">${content}</th>`
|
| 3237 |
-
);
|
| 3238 |
-
}
|
| 3239 |
-
|
| 3240 |
-
htmlParts.push('</tr></thead><tbody>');
|
| 3241 |
-
|
| 3242 |
-
// Data rows (skip header and separator)
|
| 3243 |
for (let i = 2; i < lines.length; i++) {
|
| 3244 |
-
const cells =
|
| 3245 |
const rowBg = (i - 2) % 2 === 0 ? '#ffffff' : '#f8fafc';
|
| 3246 |
-
|
| 3247 |
-
|
| 3248 |
-
|
| 3249 |
-
|
| 3250 |
-
|
| 3251 |
-
|
| 3252 |
-
|
| 3253 |
-
`<td style="text-align:${align};padding:14px 16px;border-bottom:1px solid #e2e8f0;color:#2d3748;font-size:14px;">${content}</td>`
|
| 3254 |
-
);
|
| 3255 |
-
}
|
| 3256 |
-
|
| 3257 |
-
htmlParts.push('</tr>');
|
| 3258 |
}
|
| 3259 |
-
|
| 3260 |
-
|
| 3261 |
-
return htmlParts.join('');
|
| 3262 |
}
|
| 3263 |
|
| 3264 |
// Format inline content for PDF (bold, italic, code, underline)
|
|
|
|
| 2103 |
return `<span class="math-inline math-fallback">${this.escapeHtmlDisplay(fallback)}</span>`;
|
| 2104 |
}
|
| 2105 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2106 |
_parseMarkdownTables(content, placeholders) {
|
| 2107 |
if (!content) return content;
|
| 2108 |
|
| 2109 |
const lines = content.split('\n');
|
| 2110 |
const result = [];
|
| 2111 |
+
let i = 0;
|
| 2112 |
|
| 2113 |
+
while (i < lines.length) {
|
| 2114 |
const line = lines[i];
|
|
|
|
| 2115 |
|
| 2116 |
+
if (line.trim().startsWith('|') && line.trim().endsWith('|')) {
|
| 2117 |
const nextLine = lines[i + 1];
|
|
|
|
| 2118 |
if (nextLine && /^\|[\s:|-]+\|$/.test(nextLine.trim())) {
|
| 2119 |
const tableLines = [line];
|
| 2120 |
let j = i + 1;
|
| 2121 |
|
| 2122 |
+
while (j < lines.length && lines[j].trim().startsWith('|')) {
|
| 2123 |
tableLines.push(lines[j]);
|
| 2124 |
j++;
|
| 2125 |
}
|
|
|
|
| 2129 |
const placeholder = `__TABLE_BLOCK_${placeholders.length}__`;
|
| 2130 |
placeholders.push(tableHtml);
|
| 2131 |
result.push(placeholder);
|
| 2132 |
+
i = j;
|
| 2133 |
continue;
|
| 2134 |
}
|
| 2135 |
}
|
| 2136 |
}
|
| 2137 |
|
| 2138 |
result.push(line);
|
| 2139 |
+
i++;
|
| 2140 |
}
|
| 2141 |
|
| 2142 |
return result.join('\n');
|
| 2143 |
}
|
| 2144 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2145 |
_convertTableToHtml(lines) {
|
| 2146 |
if (lines.length < 2) return null;
|
| 2147 |
|
| 2148 |
+
const headerLine = lines[0].trim();
|
| 2149 |
+
const headerCells = headerLine.split('|').filter(c => c.trim()).map(c => c.trim());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2150 |
|
| 2151 |
+
const separatorLine = lines[1].trim();
|
| 2152 |
+
const aligns = separatorLine.split('|').filter(c => c.trim()).map(c => {
|
| 2153 |
+
const cell = c.trim();
|
| 2154 |
if (cell.startsWith(':') && cell.endsWith(':')) return 'center';
|
| 2155 |
if (cell.endsWith(':')) return 'right';
|
| 2156 |
return 'left';
|
| 2157 |
});
|
| 2158 |
|
| 2159 |
+
let html = '<div class="table-wrapper"><table><thead><tr>';
|
| 2160 |
+
headerCells.forEach((h, i) => {
|
| 2161 |
+
html += `<th style="text-align:${aligns[i] || 'left'}">${this._formatInlineContent(h)}</th>`;
|
| 2162 |
+
});
|
| 2163 |
+
html += '</tr></thead><tbody>';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2164 |
|
|
|
|
| 2165 |
for (let i = 2; i < lines.length; i++) {
|
| 2166 |
+
const cells = lines[i].split('|').filter(c => c !== '').map(c => c.trim());
|
| 2167 |
+
html += '<tr>';
|
| 2168 |
+
cells.forEach((c, j) => {
|
| 2169 |
+
if (c !== undefined) {
|
| 2170 |
+
html += `<td style="text-align:${aligns[j] || 'left'}">${this._formatInlineContent(c)}</td>`;
|
| 2171 |
+
}
|
| 2172 |
+
});
|
| 2173 |
+
html += '</tr>';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2174 |
}
|
| 2175 |
|
| 2176 |
+
html += '</tbody></table></div>';
|
| 2177 |
+
return html;
|
| 2178 |
}
|
| 2179 |
|
| 2180 |
_initCodeCopyButtons() {
|
|
|
|
| 3124 |
return `<span style="padding:2px 6px;background:rgba(102, 126, 234, 0.1);border-radius:4px;font-family:'Times New Roman',Georgia,serif;font-style:italic;">${this.escapeHtml(rendered)}</span>`;
|
| 3125 |
}
|
| 3126 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3127 |
_parseMarkdownTablesForPdf(content) {
|
| 3128 |
if (!content) return content;
|
|
|
|
| 3129 |
const lines = content.split('\n');
|
| 3130 |
const result = [];
|
| 3131 |
+
let i = 0;
|
|
|
|
| 3132 |
|
| 3133 |
+
while (i < lines.length) {
|
| 3134 |
const line = lines[i];
|
| 3135 |
+
if (line.trim().startsWith('|') && line.trim().endsWith('|')) {
|
|
|
|
|
|
|
| 3136 |
const nextLine = lines[i + 1];
|
| 3137 |
+
if (nextLine && /^\|[\s:|-]+\|$/.test(nextLine.trim())) {
|
|
|
|
| 3138 |
const tableLines = [line];
|
| 3139 |
let j = i + 1;
|
| 3140 |
+
while (j < lines.length && lines[j].trim().startsWith('|')) {
|
|
|
|
| 3141 |
tableLines.push(lines[j]);
|
| 3142 |
j++;
|
| 3143 |
}
|
|
|
|
| 3144 |
result.push(this._convertTableToHtmlForPdf(tableLines));
|
| 3145 |
+
i = j;
|
| 3146 |
continue;
|
| 3147 |
}
|
| 3148 |
}
|
|
|
|
| 3149 |
result.push(line);
|
| 3150 |
+
i++;
|
| 3151 |
}
|
|
|
|
| 3152 |
return result.join('\n');
|
| 3153 |
}
|
| 3154 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3155 |
_convertTableToHtmlForPdf(lines) {
|
| 3156 |
if (lines.length < 2) return lines.join('\n');
|
| 3157 |
|
| 3158 |
+
const sep = lines[1];
|
| 3159 |
+
const aligns = sep.split('|').filter(c => c.trim()).map(c => {
|
| 3160 |
+
const t = c.trim();
|
| 3161 |
+
if (t.startsWith(':') && t.endsWith(':')) return 'center';
|
| 3162 |
+
if (t.endsWith(':')) return 'right';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3163 |
return 'left';
|
| 3164 |
});
|
| 3165 |
|
| 3166 |
+
const headerCells = lines[0].split('|').filter(c => c.trim()).map(c => c.trim());
|
| 3167 |
+
let html = '<table style="width:100%;border-collapse:collapse;margin:20px 0;border-radius:12px;overflow:hidden;box-shadow:0 4px 12px rgba(0,0,0,0.1);"><thead><tr style="background:linear-gradient(135deg, #667eea 0%, #764ba2 100%);">';
|
| 3168 |
+
headerCells.forEach((h, i) => {
|
| 3169 |
+
html += `<th style="text-align:${aligns[i] || 'left'};padding:14px 16px;color:#ffffff;font-weight:600;font-size:12px;text-transform:uppercase;letter-spacing:0.5px;border:none;">${this._formatInlineContentForPdf(h)}</th>`;
|
| 3170 |
+
});
|
| 3171 |
+
html += '</tr></thead><tbody>';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3172 |
for (let i = 2; i < lines.length; i++) {
|
| 3173 |
+
const cells = lines[i].match(/\|([^|]*)/g)?.map(c => c.slice(1).trim()) || [];
|
| 3174 |
const rowBg = (i - 2) % 2 === 0 ? '#ffffff' : '#f8fafc';
|
| 3175 |
+
html += `<tr style="background:${rowBg};">`;
|
| 3176 |
+
cells.forEach((c, j) => {
|
| 3177 |
+
if (c !== undefined) {
|
| 3178 |
+
html += `<td style="text-align:${aligns[j] || 'left'};padding:14px 16px;border-bottom:1px solid #e2e8f0;color:#2d3748;font-size:14px;">${this._formatInlineContentForPdf(c)}</td>`;
|
| 3179 |
+
}
|
| 3180 |
+
});
|
| 3181 |
+
html += '</tr>';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3182 |
}
|
| 3183 |
+
html += '</tbody></table>';
|
| 3184 |
+
return html;
|
|
|
|
| 3185 |
}
|
| 3186 |
|
| 3187 |
// Format inline content for PDF (bold, italic, code, underline)
|