refactor: centralize context-menu attribute and element helpers

Replace scattered inline encodeURIComponent(JSON.stringify(...)) and
manual oncontextmenu/dataset assignments with unified helpers added to
renderFormatMethods:

  - encodeContextMenuModel(context): safe JSON serialization for data-model
  - getContextMenuAttrs(context, tooltip): HTML attribute string for
    template literals (used in 5 files)
  - getContextMenuProps(context, tooltip): props object for imperative
    element assignment (used in missingBrowserMethods)
  - setContextMenuElement(element, context, tooltip): sets or clears
    data-model, data-tooltip, and oncontextmenu on a DOM element

Reduces duplication across 5 modules, adds try/catch around JSON
serialization, and removes 14 lines of repeated oncontextmenu wiring.
This commit is contained in:
Dariusz L
2026-06-24 23:19:02 +02:00
parent b5d10fa5cc
commit fb8a65758b
6 changed files with 73 additions and 52 deletions
+55 -7
View File
@@ -3,6 +3,59 @@ import { api } from "../../../../../scripts/api.js";
import { $el } from "../../../../../scripts/ui.js";
import { getSvgIcon } from "../../utils/icon_utils.js";
export const renderFormatMethods = {
encodeContextMenuModel(context = null) {
if (!context) return '';
try {
return encodeURIComponent(JSON.stringify(context));
} catch (error) {
console.warn('Model Resolver: failed to encode context menu model', error);
return '';
}
},
getContextMenuAttrs(context = null, tooltip = '') {
const data = this.encodeContextMenuModel(context);
if (!data) return '';
const tooltipAttr = tooltip ? ` data-tooltip="${this.escapeHtml(tooltip)}"` : '';
return ` data-model="${this.escapeHtml(data)}" oncontextmenu="window.MLOpenContextMenu(event, this)"${tooltipAttr}`;
},
getContextMenuProps(context = null, tooltip = '') {
const data = this.encodeContextMenuModel(context);
if (!data) return {};
const props = {
"data-model": data,
oncontextmenu: (event) => {
window.MLOpenContextMenu?.(event, event.currentTarget);
}
};
if (tooltip) {
props["data-tooltip"] = tooltip;
}
return props;
},
setContextMenuElement(element, context = null, tooltip = '') {
if (!element) return;
const data = this.encodeContextMenuModel(context);
if (!data) {
element.removeAttribute('data-model');
element.removeAttribute('data-tooltip');
element.oncontextmenu = null;
return;
}
element.dataset.model = data;
if (tooltip) {
element.dataset.tooltip = tooltip;
} else {
element.removeAttribute('data-tooltip');
}
element.oncontextmenu = (event) => {
window.MLOpenContextMenu?.(event, event.currentTarget);
};
},
/**
* Get a colored confidence badge HTML
* @param {number} confidence - Confidence percentage (0-100)
@@ -110,13 +163,8 @@ export const renderFormatMethods = {
const icon = icons[type] || icons.info;
const contextMenuModel = options?.contextMenuModel || null;
const contextMenuTooltip = options?.contextMenuTooltip || 'Right-click to open download folder';
const contextMenuData = contextMenuModel
? this.escapeHtml(encodeURIComponent(JSON.stringify(contextMenuModel)))
: '';
const contextMenuAttrs = contextMenuData
? ` data-model="${contextMenuData}" oncontextmenu="window.MLOpenContextMenu(event, this)" data-tooltip="${this.escapeHtml(contextMenuTooltip)}"`
: '';
const className = contextMenuData
const contextMenuAttrs = this.getContextMenuAttrs(contextMenuModel, contextMenuTooltip);
const className = contextMenuAttrs
? `mr-status mr-status-${type} mr-download-folder-context`
: `mr-status mr-status-${type}`;