diff --git a/web/debugger.css b/web/debugger.css index 2f88c0c77..c66160dd7 100644 --- a/web/debugger.css +++ b/web/debugger.css @@ -13,6 +13,10 @@ * limitations under the License. */ +:root { + --panel-width: 300px; +} + #PDFBug, #PDFBug :is(input, button, select) { font: message-box; @@ -26,7 +30,7 @@ bottom: 0; font-size: 10px; padding: 0; - width: 300px; + width: var(--panel-width); } #PDFBug .controls { background: rgba(238, 238, 238, 1); diff --git a/web/debugger.js b/web/debugger.js index 3d4cb883e..9160f840f 100644 --- a/web/debugger.js +++ b/web/debugger.js @@ -225,9 +225,9 @@ const StepperManager = (function StepperManagerClosure() { })(); // The stepper for each page's operatorList. -const Stepper = (function StepperClosure() { +class Stepper { // Shorter way to create element and optionally set textContent. - function c(tag, textContent) { + #c(tag, textContent) { const d = document.createElement(tag); if (textContent) { d.textContent = textContent; @@ -235,7 +235,7 @@ const Stepper = (function StepperClosure() { return d; } - function simplifyArgs(args) { + #simplifyArgs(args) { if (typeof args === "string") { const MAX_STRING_LENGTH = 75; return args.length <= MAX_STRING_LENGTH @@ -251,7 +251,7 @@ const Stepper = (function StepperClosure() { simpleArgs = []; let i, ii; for (i = 0, ii = Math.min(MAX_ITEMS, args.length); i < ii; i++) { - simpleArgs.push(simplifyArgs(args[i])); + simpleArgs.push(this.#simplifyArgs(args[i])); } if (i < args.length) { simpleArgs.push("..."); @@ -260,183 +260,179 @@ const Stepper = (function StepperClosure() { } const simpleObj = {}; for (const key in args) { - simpleObj[key] = simplifyArgs(args[key]); + simpleObj[key] = this.#simplifyArgs(args[key]); } return simpleObj; } - // eslint-disable-next-line no-shadow - class Stepper { - constructor(panel, pageIndex, initialBreakPoints) { - this.panel = panel; - this.breakPoint = 0; - this.nextBreakPoint = null; - this.pageIndex = pageIndex; - this.breakPoints = initialBreakPoints; - this.currentIdx = -1; - this.operatorListIdx = 0; - this.indentLevel = 0; + constructor(panel, pageIndex, initialBreakPoints) { + this.panel = panel; + this.breakPoint = 0; + this.nextBreakPoint = null; + this.pageIndex = pageIndex; + this.breakPoints = initialBreakPoints; + this.currentIdx = -1; + this.operatorListIdx = 0; + this.indentLevel = 0; + } + + init(operatorList) { + const panel = this.panel; + const content = this.#c("div", "c=continue, s=step"); + const table = this.#c("table"); + content.append(table); + table.cellSpacing = 0; + const headerRow = this.#c("tr"); + table.append(headerRow); + headerRow.append( + this.#c("th", "Break"), + this.#c("th", "Idx"), + this.#c("th", "fn"), + this.#c("th", "args") + ); + panel.append(content); + this.table = table; + this.updateOperatorList(operatorList); + } + + updateOperatorList(operatorList) { + const self = this; + + function cboxOnClick() { + const x = +this.dataset.idx; + if (this.checked) { + self.breakPoints.push(x); + } else { + self.breakPoints.splice(self.breakPoints.indexOf(x), 1); + } + StepperManager.saveBreakPoints(self.pageIndex, self.breakPoints); } - init(operatorList) { - const panel = this.panel; - const content = c("div", "c=continue, s=step"); - const table = c("table"); - content.append(table); - table.cellSpacing = 0; - const headerRow = c("tr"); - table.append(headerRow); - headerRow.append( - c("th", "Break"), - c("th", "Idx"), - c("th", "fn"), - c("th", "args") - ); - panel.append(content); - this.table = table; - this.updateOperatorList(operatorList); + const MAX_OPERATORS_COUNT = 15000; + if (this.operatorListIdx > MAX_OPERATORS_COUNT) { + return; } - updateOperatorList(operatorList) { - const self = this; + const chunk = document.createDocumentFragment(); + const operatorsToDisplay = Math.min( + MAX_OPERATORS_COUNT, + operatorList.fnArray.length + ); + for (let i = this.operatorListIdx; i < operatorsToDisplay; i++) { + const line = this.#c("tr"); + line.className = "line"; + line.dataset.idx = i; + chunk.append(line); + const checked = this.breakPoints.includes(i); + const args = operatorList.argsArray[i] || []; - function cboxOnClick() { - const x = +this.dataset.idx; - if (this.checked) { - self.breakPoints.push(x); - } else { - self.breakPoints.splice(self.breakPoints.indexOf(x), 1); - } - StepperManager.saveBreakPoints(self.pageIndex, self.breakPoints); - } + const breakCell = this.#c("td"); + const cbox = this.#c("input"); + cbox.type = "checkbox"; + cbox.className = "points"; + cbox.checked = checked; + cbox.dataset.idx = i; + cbox.onclick = cboxOnClick; - const MAX_OPERATORS_COUNT = 15000; - if (this.operatorListIdx > MAX_OPERATORS_COUNT) { - return; - } - - const chunk = document.createDocumentFragment(); - const operatorsToDisplay = Math.min( - MAX_OPERATORS_COUNT, - operatorList.fnArray.length - ); - for (let i = this.operatorListIdx; i < operatorsToDisplay; i++) { - const line = c("tr"); - line.className = "line"; - line.dataset.idx = i; - chunk.append(line); - const checked = this.breakPoints.includes(i); - const args = operatorList.argsArray[i] || []; - - const breakCell = c("td"); - const cbox = c("input"); - cbox.type = "checkbox"; - cbox.className = "points"; - cbox.checked = checked; - cbox.dataset.idx = i; - cbox.onclick = cboxOnClick; - - breakCell.append(cbox); - line.append(breakCell, c("td", i.toString())); - const fn = opMap[operatorList.fnArray[i]]; - let decArgs = args; - if (fn === "showText") { - const glyphs = args[0]; - const charCodeRow = c("tr"); - const fontCharRow = c("tr"); - const unicodeRow = c("tr"); - for (const glyph of glyphs) { - if (typeof glyph === "object" && glyph !== null) { - charCodeRow.append(c("td", glyph.originalCharCode)); - fontCharRow.append(c("td", glyph.fontChar)); - unicodeRow.append(c("td", glyph.unicode)); - } else { - // null or number - const advanceEl = c("td", glyph); - advanceEl.classList.add("advance"); - charCodeRow.append(advanceEl); - fontCharRow.append(c("td")); - unicodeRow.append(c("td")); - } + breakCell.append(cbox); + line.append(breakCell, this.#c("td", i.toString())); + const fn = opMap[operatorList.fnArray[i]]; + let decArgs = args; + if (fn === "showText") { + const glyphs = args[0]; + const charCodeRow = this.#c("tr"); + const fontCharRow = this.#c("tr"); + const unicodeRow = this.#c("tr"); + for (const glyph of glyphs) { + if (typeof glyph === "object" && glyph !== null) { + charCodeRow.append(this.#c("td", glyph.originalCharCode)); + fontCharRow.append(this.#c("td", glyph.fontChar)); + unicodeRow.append(this.#c("td", glyph.unicode)); + } else { + // null or number + const advanceEl = this.#c("td", glyph); + advanceEl.classList.add("advance"); + charCodeRow.append(advanceEl); + fontCharRow.append(this.#c("td")); + unicodeRow.append(this.#c("td")); } - decArgs = c("td"); - const table = c("table"); - table.classList.add("showText"); - decArgs.append(table); - table.append(charCodeRow, fontCharRow, unicodeRow); - } else if (fn === "restore" && this.indentLevel > 0) { - this.indentLevel--; - } - line.append(c("td", " ".repeat(this.indentLevel * 2) + fn)); - if (fn === "save") { - this.indentLevel++; - } - - if (decArgs instanceof HTMLElement) { - line.append(decArgs); - } else { - line.append(c("td", JSON.stringify(simplifyArgs(decArgs)))); } + decArgs = this.#c("td"); + const table = this.#c("table"); + table.classList.add("showText"); + decArgs.append(table); + table.append(charCodeRow, fontCharRow, unicodeRow); + } else if (fn === "restore" && this.indentLevel > 0) { + this.indentLevel--; } - if (operatorsToDisplay < operatorList.fnArray.length) { - const lastCell = c("td", "..."); - lastCell.colspan = 4; - chunk.append(lastCell); + line.append(this.#c("td", " ".repeat(this.indentLevel * 2) + fn)); + if (fn === "save") { + this.indentLevel++; } - this.operatorListIdx = operatorList.fnArray.length; - this.table.append(chunk); - } - getNextBreakPoint() { - this.breakPoints.sort(function (a, b) { - return a - b; - }); - for (const breakPoint of this.breakPoints) { - if (breakPoint > this.currentIdx) { - return breakPoint; - } + if (decArgs instanceof HTMLElement) { + line.append(decArgs); + } else { + line.append(this.#c("td", JSON.stringify(this.#simplifyArgs(decArgs)))); } - return null; } - - breakIt(idx, callback) { - StepperManager.selectStepper(this.pageIndex, true); - this.currentIdx = idx; - - const listener = evt => { - switch (evt.keyCode) { - case 83: // step - document.removeEventListener("keydown", listener); - this.nextBreakPoint = this.currentIdx + 1; - this.goTo(-1); - callback(); - break; - case 67: // continue - document.removeEventListener("keydown", listener); - this.nextBreakPoint = this.getNextBreakPoint(); - this.goTo(-1); - callback(); - break; - } - }; - document.addEventListener("keydown", listener); - this.goTo(idx); + if (operatorsToDisplay < operatorList.fnArray.length) { + const lastCell = this.#c("td", "..."); + lastCell.colspan = 4; + chunk.append(lastCell); } + this.operatorListIdx = operatorList.fnArray.length; + this.table.append(chunk); + } - goTo(idx) { - const allRows = this.panel.getElementsByClassName("line"); - for (const row of allRows) { - if ((row.dataset.idx | 0) === idx) { - row.style.backgroundColor = "rgb(251,250,207)"; - row.scrollIntoView(); - } else { - row.style.backgroundColor = null; - } + getNextBreakPoint() { + this.breakPoints.sort(function (a, b) { + return a - b; + }); + for (const breakPoint of this.breakPoints) { + if (breakPoint > this.currentIdx) { + return breakPoint; + } + } + return null; + } + + breakIt(idx, callback) { + StepperManager.selectStepper(this.pageIndex, true); + this.currentIdx = idx; + + const listener = evt => { + switch (evt.keyCode) { + case 83: // step + document.removeEventListener("keydown", listener); + this.nextBreakPoint = this.currentIdx + 1; + this.goTo(-1); + callback(); + break; + case 67: // continue + document.removeEventListener("keydown", listener); + this.nextBreakPoint = this.getNextBreakPoint(); + this.goTo(-1); + callback(); + break; + } + }; + document.addEventListener("keydown", listener); + this.goTo(idx); + } + + goTo(idx) { + const allRows = this.panel.getElementsByClassName("line"); + for (const row of allRows) { + if ((row.dataset.idx | 0) === idx) { + row.style.backgroundColor = "rgb(251,250,207)"; + row.scrollIntoView(); + } else { + row.style.backgroundColor = null; } } } - return Stepper; -})(); +} const Stats = (function Stats() { let stats = []; @@ -495,115 +491,118 @@ const Stats = (function Stats() { })(); // Manages all the debugging tools. -const PDFBug = (function PDFBugClosure() { - const panelWidth = 300; - const buttons = []; - let activePanel = null; +class PDFBug { + static #buttons = []; - return { - tools: [FontInspector, StepperManager, Stats], - enable(ids) { - const all = ids.length === 1 && ids[0] === "all"; - const tools = this.tools; - for (const tool of tools) { - if (all || ids.includes(tool.id)) { - tool.enabled = true; - } - } - if (!all) { - // Sort the tools by the order they are enabled. - tools.sort(function (a, b) { - let indexA = ids.indexOf(a.id); - indexA = indexA < 0 ? tools.length : indexA; - let indexB = ids.indexOf(b.id); - indexB = indexB < 0 ? tools.length : indexB; - return indexA - indexB; - }); - } - }, - init(container, ids) { - this.loadCSS(); - this.enable(ids); - /* - * Basic Layout: - * PDFBug - * Controls - * Panels - * Panel - * Panel - * ... - */ - const ui = document.createElement("div"); - ui.id = "PDFBug"; + static #activePanel = null; - const controls = document.createElement("div"); - controls.setAttribute("class", "controls"); - ui.append(controls); + static tools = [FontInspector, StepperManager, Stats]; - const panels = document.createElement("div"); - panels.setAttribute("class", "panels"); - ui.append(panels); + static enable(ids) { + const all = ids.length === 1 && ids[0] === "all"; + const tools = this.tools; + for (const tool of tools) { + if (all || ids.includes(tool.id)) { + tool.enabled = true; + } + } + if (!all) { + // Sort the tools by the order they are enabled. + tools.sort(function (a, b) { + let indexA = ids.indexOf(a.id); + indexA = indexA < 0 ? tools.length : indexA; + let indexB = ids.indexOf(b.id); + indexB = indexB < 0 ? tools.length : indexB; + return indexA - indexB; + }); + } + } - container.append(ui); - container.style.right = panelWidth + "px"; + static init(container, ids) { + this.loadCSS(); + this.enable(ids); + /* + * Basic Layout: + * PDFBug + * Controls + * Panels + * Panel + * Panel + * ... + */ + const ui = document.createElement("div"); + ui.id = "PDFBug"; - // Initialize all the debugging tools. - for (const tool of this.tools) { - const panel = document.createElement("div"); - const panelButton = document.createElement("button"); - panelButton.textContent = tool.name; - panelButton.addEventListener("click", event => { - event.preventDefault(); - this.selectPanel(tool); - }); - controls.append(panelButton); - panels.append(panel); - tool.panel = panel; - tool.manager = this; - if (tool.enabled) { - tool.init(); - } else { - panel.textContent = - `${tool.name} is disabled. To enable add "${tool.id}" to ` + - "the pdfBug parameter and refresh (separate multiple by commas)."; - } - buttons.push(panelButton); - } - this.selectPanel(0); - }, - loadCSS() { - const { url } = import.meta; + const controls = document.createElement("div"); + controls.setAttribute("class", "controls"); + ui.append(controls); - const link = document.createElement("link"); - link.rel = "stylesheet"; - link.href = url.replace(/.js$/, ".css"); + const panels = document.createElement("div"); + panels.setAttribute("class", "panels"); + ui.append(panels); - document.head.append(link); - }, - cleanup() { - for (const tool of this.tools) { - if (tool.enabled) { - tool.cleanup(); - } + container.append(ui); + container.style.right = "var(--panel-width)"; + + // Initialize all the debugging tools. + for (const tool of this.tools) { + const panel = document.createElement("div"); + const panelButton = document.createElement("button"); + panelButton.textContent = tool.name; + panelButton.addEventListener("click", event => { + event.preventDefault(); + this.selectPanel(tool); + }); + controls.append(panelButton); + panels.append(panel); + tool.panel = panel; + tool.manager = this; + if (tool.enabled) { + tool.init(); + } else { + panel.textContent = + `${tool.name} is disabled. To enable add "${tool.id}" to ` + + "the pdfBug parameter and refresh (separate multiple by commas)."; } - }, - selectPanel(index) { - if (typeof index !== "number") { - index = this.tools.indexOf(index); + this.#buttons.push(panelButton); + } + this.selectPanel(0); + } + + static loadCSS() { + const { url } = import.meta; + + const link = document.createElement("link"); + link.rel = "stylesheet"; + link.href = url.replace(/.js$/, ".css"); + + document.head.append(link); + } + + static cleanup() { + for (const tool of this.tools) { + if (tool.enabled) { + tool.cleanup(); } - if (index === activePanel) { - return; - } - activePanel = index; - for (const [j, tool] of this.tools.entries()) { - const isActive = j === index; - buttons[j].classList.toggle("active", isActive); - tool.active = isActive; - tool.panel.hidden = !isActive; - } - }, - }; -})(); + } + } + + static selectPanel(index) { + if (typeof index !== "number") { + index = this.tools.indexOf(index); + } + if (index === this.#activePanel) { + return; + } + this.#activePanel = index; + for (const [j, tool] of this.tools.entries()) { + const isActive = j === index; + this.#buttons[j].classList.toggle("active", isActive); + tool.active = isActive; + tool.panel.hidden = !isActive; + } + } +} globalThis.FontInspector = FontInspector; globalThis.StepperManager = StepperManager;