model-explorer / js /ui /exportHandler.js
mr4's picture
Upload 71 files
9bd422a verified
/**
* ExportHandler - Handles exporting model information as JSON
* Requirements: 12.1, 12.2, 12.3, 12.4
*/
const ExportHandler = (function () {
class ExportHandler {
/**
* @param {string} [exportBtnId='exportBtn'] - ID of the export button element
*/
constructor(exportBtnId = 'exportBtn') {
this._exportBtn = document.getElementById(exportBtnId);
this._unsubscribe = null;
this._init();
}
// ─── Private ────────────────────────────────────────────────────────────
_init() {
if (!this._exportBtn) {
console.warn('[ExportHandler] Export button not found:', this._exportBtn);
return;
}
// Bind click handler
this._exportBtn.addEventListener('click', () => this._handleExport());
// Subscribe to currentModel changes to enable/disable button
this._unsubscribe = StateManager.subscribe('currentModel', (model) => {
this._setButtonEnabled(!!model);
});
// Set initial state
this._setButtonEnabled(!!StateManager.getCurrentModel());
}
/**
* Enable or disable the export button.
* @param {boolean} enabled
*/
_setButtonEnabled(enabled) {
if (!this._exportBtn) return;
this._exportBtn.disabled = !enabled;
}
/**
* Build the export payload from the current model.
* @param {object} model - ParsedModel from StateManager
* @returns {object}
*/
_buildExportData(model) {
const data = {};
// Metadata
data.metadata = model.metadata ? Object.assign({}, model.metadata) : {};
// Inputs
data.inputs = Array.isArray(model.inputs)
? model.inputs.map((i) => Object.assign({}, i))
: [];
// Outputs
data.outputs = Array.isArray(model.outputs)
? model.outputs.map((o) => Object.assign({}, o))
: [];
// Graph (nodes + edges)
if (model.graph) {
data.graph = {
name: model.graph.name || '',
nodes: Array.isArray(model.graph.nodes)
? model.graph.nodes.map((n) => Object.assign({}, n))
: [],
edges: Array.isArray(model.graph.edges)
? model.graph.edges.map((e) => Object.assign({}, e))
: [],
};
} else {
data.graph = { name: '', nodes: [], edges: [] };
}
// Initializers
data.initializers = Array.isArray(model.initializers)
? model.initializers.map((init) => Object.assign({}, init))
: [];
return data;
}
/**
* Derive the download file name from the model's fileName metadata.
* e.g. "model.onnx" β†’ "model_info.json"
* @param {object} metadata
* @returns {string}
*/
_buildFileName(metadata) {
const fileName = (metadata && metadata.fileName) || 'model';
// Strip directory path, keep base name
const base = fileName.split('/').pop().split('\\').pop();
// Remove extension
const withoutExt = base.replace(/\.[^.]+$/, '');
return `${withoutExt}_info.json`;
}
/**
* Trigger a browser download of the given JSON string.
* @param {string} jsonStr
* @param {string} fileName
*/
_triggerDownload(jsonStr, fileName) {
const blob = new Blob([jsonStr], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const anchor = document.createElement('a');
anchor.href = url;
anchor.download = fileName;
anchor.style.display = 'none';
document.body.appendChild(anchor);
anchor.click();
document.body.removeChild(anchor);
// Release the object URL after a short delay
setTimeout(() => URL.revokeObjectURL(url), 1000);
}
/**
* Show a brief success or error notification.
* Falls back to console if no ErrorDisplay is available.
* @param {string} message
* @param {'success'|'error'} type
*/
_showNotification(message, type) {
if (window.ErrorDisplay && typeof window.ErrorDisplay.show === 'function') {
window.ErrorDisplay.show(message, type === 'error' ? 'error' : 'info');
} else {
if (type === 'error') {
console.error('[ExportHandler]', message);
} else {
console.info('[ExportHandler]', message);
}
}
}
// ─── Export Handler ──────────────────────────────────────────────────────
_handleExport() {
try {
const model = StateManager.getCurrentModel();
if (!model) {
this._showNotification(
CONFIG.ERRORS.EXPORT_ERROR + ' No model is currently loaded.',
'error'
);
return;
}
const exportData = this._buildExportData(model);
const jsonStr = JSON.stringify(exportData, null, 2);
const fileName = this._buildFileName(model.metadata);
this._triggerDownload(jsonStr, fileName);
this._showNotification(CONFIG.SUCCESS.MODEL_EXPORTED, 'success');
} catch (err) {
console.error('[ExportHandler] Export failed:', err);
this._showNotification(CONFIG.ERRORS.EXPORT_ERROR, 'error');
}
}
// ─── Public API ──────────────────────────────────────────────────────────
/**
* Programmatically trigger an export (useful for testing).
*/
export() {
this._handleExport();
}
/**
* Clean up subscriptions.
*/
destroy() {
if (typeof this._unsubscribe === 'function') {
this._unsubscribe();
this._unsubscribe = null;
}
}
}
return ExportHandler;
})();
// Export for global access in vanilla JS context
window.ExportHandler = ExportHandler;