Support lists in issue description (PAN-OS 11.1.0-h2)
This commit is contained in:
+114
-30
@@ -9,6 +9,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
let activeDownloadUrl = null;
|
let activeDownloadUrl = null;
|
||||||
const PROCESS_FORM_STATE_KEY = 'bugmedley.process.formState.v1';
|
const PROCESS_FORM_STATE_KEY = 'bugmedley.process.formState.v1';
|
||||||
const ISSUE_ID_PATTERN = /\b((?:[A-Z]{2,6}|WF500)-\d{4,8})\b/i;
|
const ISSUE_ID_PATTERN = /\b((?:[A-Z]{2,6}|WF500)-\d{4,8})\b/i;
|
||||||
|
const BLOCK_CONTAINER_TAGS = new Set(['ARTICLE', 'ASIDE', 'BLOCKQUOTE', 'DIV', 'P', 'PRE', 'SECTION']);
|
||||||
|
|
||||||
function loadProducts() {
|
function loadProducts() {
|
||||||
fetch('data/products.json')
|
fetch('data/products.json')
|
||||||
@@ -171,47 +172,130 @@ function extractIssueDetails(cell) {
|
|||||||
|
|
||||||
function extractCellText(cell) {
|
function extractCellText(cell) {
|
||||||
const clone = cell.cloneNode(true);
|
const clone = cell.cloneNode(true);
|
||||||
const nestedTables = Array.from(clone.querySelectorAll('table'));
|
return collectMarkdownBlocks(clone)
|
||||||
const markdownTables = nestedTables
|
.join('\n\n')
|
||||||
.map(table => convertHtmlTableToMarkdown(table))
|
.replace(/\n{3,}/g, '\n\n')
|
||||||
.filter(Boolean);
|
.trim();
|
||||||
|
|
||||||
nestedTables.forEach(table => table.remove());
|
|
||||||
|
|
||||||
const blockLikeNodes = clone.querySelectorAll('div, p, li, pre, code');
|
|
||||||
let mainText = '';
|
|
||||||
|
|
||||||
if (blockLikeNodes.length > 0) {
|
|
||||||
const parts = [];
|
|
||||||
blockLikeNodes.forEach(node => {
|
|
||||||
const text = normalizeWhitespace(node.textContent || '');
|
|
||||||
if (text) {
|
|
||||||
parts.push(text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function collectMarkdownBlocks(root, nestedListDepth = 0) {
|
||||||
|
const blocks = [];
|
||||||
|
let paragraphBuffer = '';
|
||||||
|
|
||||||
|
const flushParagraph = () => {
|
||||||
|
const text = normalizeInlineMarkdown(paragraphBuffer);
|
||||||
|
if (text) {
|
||||||
|
blocks.push(text);
|
||||||
|
}
|
||||||
|
paragraphBuffer = '';
|
||||||
|
};
|
||||||
|
|
||||||
|
Array.from(root.childNodes).forEach(node => {
|
||||||
|
if (node.nodeType === Node.TEXT_NODE) {
|
||||||
|
paragraphBuffer += node.textContent || '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.nodeType !== Node.ELEMENT_NODE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tagName = node.tagName.toUpperCase();
|
||||||
|
|
||||||
|
if (tagName === 'BR') {
|
||||||
|
paragraphBuffer += '\n';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tagName === 'TABLE') {
|
||||||
|
flushParagraph();
|
||||||
|
const tableMarkdown = convertHtmlTableToMarkdown(node);
|
||||||
|
if (tableMarkdown) {
|
||||||
|
blocks.push(tableMarkdown);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tagName === 'UL' || tagName === 'OL') {
|
||||||
|
flushParagraph();
|
||||||
|
const listMarkdown = convertHtmlListToMarkdown(node, nestedListDepth);
|
||||||
|
if (listMarkdown) {
|
||||||
|
blocks.push(listMarkdown);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BLOCK_CONTAINER_TAGS.has(tagName)) {
|
||||||
|
flushParagraph();
|
||||||
|
blocks.push(...collectMarkdownBlocks(node, nestedListDepth));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
paragraphBuffer += node.textContent || '';
|
||||||
});
|
});
|
||||||
|
|
||||||
if (parts.length > 0) {
|
flushParagraph();
|
||||||
mainText = parts.join('\n\n');
|
return blocks;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mainText) {
|
function convertHtmlListToMarkdown(list, depth = 0) {
|
||||||
const withLineBreaks = (clone.innerHTML || '')
|
const items = Array.from(list.children).filter(child => child.tagName && child.tagName.toUpperCase() === 'LI');
|
||||||
.replace(/<br\s*\/?>/gi, '\n')
|
if (items.length === 0) {
|
||||||
.replace(/<[^>]+>/g, ' ');
|
return '';
|
||||||
|
|
||||||
mainText = normalizeWhitespace(withLineBreaks).replace(/\s*\n\s*/g, '\n').trim();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (markdownTables.length === 0) {
|
const isOrdered = list.tagName.toUpperCase() === 'OL';
|
||||||
return mainText;
|
return items
|
||||||
|
.map((item, index) => convertListItemToMarkdown(item, isOrdered ? `${index + 1}.` : '-', depth))
|
||||||
|
.filter(Boolean)
|
||||||
|
.join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mainText) {
|
function convertListItemToMarkdown(item, marker, depth) {
|
||||||
return markdownTables.join('\n\n');
|
const blocks = collectMarkdownBlocks(item, depth + 1);
|
||||||
|
const indent = ' '.repeat(depth);
|
||||||
|
const continuationIndent = `${indent} `;
|
||||||
|
|
||||||
|
if (blocks.length === 0) {
|
||||||
|
return `${indent}${marker}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return [mainText, ...markdownTables].join('\n\n');
|
const [firstBlock, ...remainingBlocks] = blocks;
|
||||||
|
const firstLines = String(firstBlock || '').split('\n');
|
||||||
|
let output = `${indent}${marker} ${firstLines[0] || ''}`;
|
||||||
|
|
||||||
|
if (firstLines.length > 1) {
|
||||||
|
output += `\n${firstLines.slice(1).map(line => line ? `${continuationIndent}${line}` : continuationIndent.trimEnd()).join('\n')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
remainingBlocks.forEach(block => {
|
||||||
|
if (!block) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isIndentedListBlock(block, depth + 1)) {
|
||||||
|
output += `\n${block}`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
output += `\n${block.split('\n').map(line => line ? `${continuationIndent}${line}` : '').join('\n')}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
return output.trimEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
function isIndentedListBlock(block, depth) {
|
||||||
|
const indent = ' '.repeat(depth);
|
||||||
|
const lines = String(block || '').split('\n').filter(Boolean);
|
||||||
|
return lines.length > 0 && lines.every(line => line.startsWith(indent) && /^\s*(?:-|\d+\.)\s/.test(line));
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeInlineMarkdown(value) {
|
||||||
|
return String(value || '')
|
||||||
|
.replace(/\u00a0/g, ' ')
|
||||||
|
.replace(/[\t\f\v\r ]+/g, ' ')
|
||||||
|
.replace(/ *\n+ */g, '\n')
|
||||||
|
.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertHtmlTableToMarkdown(table) {
|
function convertHtmlTableToMarkdown(table) {
|
||||||
|
|||||||
@@ -248,6 +248,32 @@ ul {
|
|||||||
width: 180px;
|
width: 180px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.issues-table td ul,
|
||||||
|
.issues-table td ol {
|
||||||
|
margin: 8px 0 0;
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.issues-table td ul {
|
||||||
|
list-style-type: disc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.issues-table td ol {
|
||||||
|
list-style-type: decimal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.issues-table td li {
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
margin: 4px 0 0;
|
||||||
|
border-radius: 0;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.issues-table td li:hover {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
li {
|
li {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border: 1px solid #ddd;
|
border: 1px solid #ddd;
|
||||||
|
|||||||
Reference in New Issue
Block a user