';
});
}
function attachHotspotListeners(svg) {
var areas = svg.querySelectorAll('[data-hotspot]');
areas.forEach(function(area) {
var callout = parseInt(area.getAttribute('data-hotspot'));
area.style.cursor = 'pointer';
area.addEventListener('click', function(e) {
e.stopPropagation();
selectHotspot(callout);
});
area.addEventListener('mouseenter', function() {
highlightHotspot(callout, true);
});
area.addEventListener('mouseleave', function() {
highlightHotspot(callout, false);
});
});
}
function highlightHotspot(callout, on) {
// Highlight in parts list
var items = partsListEl.querySelectorAll('.part-item');
items.forEach(function(item) {
if (parseInt(item.dataset.callout) === callout) {
item.classList.toggle('is-highlighted', on);
}
});
}
function selectHotspot(callout) {
var hotspot = state.hotspots.find(function(h) { return h.callout_number === callout; });
if (!hotspot) return;
state.selectedHotspot = hotspot;
// Highlight in SVG
var svg = svgWrapper.querySelector('svg');
if (svg) {
svg.querySelectorAll('[data-hotspot]').forEach(function(area) {
var num = parseInt(area.getAttribute('data-hotspot'));
if (num === callout) {
area.style.fill = 'rgba(245, 166, 35, 0.25)';
area.style.stroke = '#F5A623';
area.style.strokeWidth = '3';
} else {
area.style.fill = 'transparent';
area.style.stroke = 'none';
}
});
}
// Highlight in parts list
var items = partsListEl.querySelectorAll('.part-item');
items.forEach(function(item) {
item.classList.toggle('is-active', parseInt(item.dataset.callout) === callout);
});
// Show hotspot detail panel
showHotspotDetail(hotspot);
}
function showHotspotDetail(hotspot) {
hotspotPanel.classList.add('is-open');
var partName = hotspot.part_name_es || hotspot.part_name || 'Parte #' + hotspot.callout_number;
var partNum = hotspot.part_number || '';
var desc = hotspot.description_es || hotspot.description || '';
// Determine search term for catalog link
var searchTerm = partNum || partName;
// Map callout to friendly part names for placeholder diagrams
var placeholderNames = getPlaceholderPartInfo(hotspot.callout_number);
var displayName = hotspot.part_name_es || hotspot.part_name || placeholderNames.name;
var displayDesc = desc || placeholderNames.desc;
var html = '';
html += '
';
html += '
' + hotspot.callout_number + '
';
html += '
' + esc(displayName) + '
';
if (partNum) {
html += '
No. Parte: ' + esc(partNum) + '
';
}
if (displayDesc) {
html += '
' + esc(displayDesc) + '
';
}
// Action buttons
html += '
';
if (hotspot.part_id) {
html += '';
html += '';
} else {
html += '';
}
html += '
';
html += '
';
hotspotBody.innerHTML = html;
}
function getPlaceholderPartInfo(callout) {
// For built-in placeholder diagrams, provide friendly names
var d = state.currentDiagram;
if (!d) return { name: 'Parte ' + callout, desc: '' };
var brakeNames = {
1: { name: 'Disco de freno', desc: 'Disco ventilado de freno delantero. Se recomienda cambiar en pares.' },
2: { name: 'Caliper de freno', desc: 'Caliper con pistones, incluye purga. Verificar compatibilidad con tipo de pastilla.' },
3: { name: 'Pastillas de freno', desc: 'Juego de pastillas con indicador de desgaste. Material ceramico o semi-metalico.' },
4: { name: 'Manguera de freno', desc: 'Manguera flexible de alta presion. Revisar por grietas cada 40,000 km.' },
5: { name: 'Cilindro maestro', desc: 'Cilindro maestro con deposito de liquido de frenos. Incluye empaques.' },
};
var suspNames = {
1: { name: 'Amortiguador', desc: 'Amortiguador delantero de gas. Se recomienda cambiar en pares.' },
2: { name: 'Resorte helicoidal', desc: 'Resorte de suspension delantera. Verificar altura libre.' },
3: { name: 'Brazo de control', desc: 'Brazo inferior de control con bujes. Incluye herraje de montaje.' },
4: { name: 'Rotula', desc: 'Rotula inferior de suspension. Incluye guardapolvo y seguros.' },
5: { name: 'Barra de acoplamiento', desc: 'Barra de acoplamiento de direccion con terminales. Requiere alineacion.' },
};
var engineNames = {
1: { name: 'Filtro de aire', desc: 'Filtro de aire del motor. Cambiar cada 15,000-20,000 km.' },
2: { name: 'Bujias', desc: 'Juego de bujias. Verificar tipo (platino, iridio) segun especificacion del motor.' },
3: { name: 'Banda serpentina', desc: 'Banda de accesorios. Revisar tension y desgaste. Incluye alternador, A/C y direccion.' },
4: { name: 'Junta de culata', desc: 'Junta de cabeza de cilindros. Material MLS multicapa. Requiere torque especifico.' },
5: { name: 'Filtro de aceite', desc: 'Filtro de aceite del motor. Cambiar en cada servicio de aceite.' },
};
var name = (d.name || '').toLowerCase();
if (name.indexOf('brak') !== -1 || name.indexOf('freno') !== -1) return brakeNames[callout] || { name: 'Parte ' + callout, desc: '' };
if (name.indexOf('susp') !== -1) return suspNames[callout] || { name: 'Parte ' + callout, desc: '' };
if (name.indexOf('engine') !== -1 || name.indexOf('motor') !== -1) return engineNames[callout] || { name: 'Parte ' + callout, desc: '' };
return { name: 'Parte ' + callout, desc: '' };
}
function closeHotspotPanel() {
hotspotPanel.classList.remove('is-open');
state.selectedHotspot = null;
// Clear SVG highlights
var svg = svgWrapper.querySelector('svg');
if (svg) {
svg.querySelectorAll('[data-hotspot]').forEach(function(area) {
area.style.fill = 'transparent';
area.style.stroke = 'none';
});
}
// Clear list highlights
if (partsListEl) {
partsListEl.querySelectorAll('.part-item').forEach(function(item) {
item.classList.remove('is-active');
});
}
}
// ---- Parts list sidebar ----
function renderPartsList() {
if (!partsListEl) return;
var html = '
Partes en diagrama
';
state.hotspots.forEach(function(h) {
var info = getPlaceholderPartInfo(h.callout_number);
var name = h.part_name_es || h.part_name || info.name;
var num = h.part_number || '';
html += '
';
html += '' + h.callout_number + '';
html += '
';
html += '
' + esc(name) + '
';
if (num) html += '
' + esc(num) + '
';
html += '
';
html += '
';
});
if (state.hotspots.length === 0) {
html += '
Sin partes definidas
';
}
partsListEl.innerHTML = html;
}
// ---- Zoom / Pan ----
function zoom(factor) {
state.scale = Math.max(0.3, Math.min(5, state.scale * factor));
applyTransform();
}
function resetZoom() {
state.scale = 1;
state.panX = 0;
state.panY = 0;
applyTransform();
}
function applyTransform() {
if (!svgWrapper) return;
svgWrapper.style.transform = 'translate(' + state.panX + 'px, ' + state.panY + 'px) scale(' + state.scale + ')';
}
function startPan(e) {
// Only pan with left button and not on a hotspot
if (e.button && e.button !== 0) return;
state.isPanning = true;
state.lastPointer = { x: e.clientX, y: e.clientY };
}
function doPan(e) {
if (!state.isPanning || !state.lastPointer) return;
var dx = e.clientX - state.lastPointer.x;
var dy = e.clientY - state.lastPointer.y;
state.panX += dx;
state.panY += dy;
state.lastPointer = { x: e.clientX, y: e.clientY };
applyTransform();
}
function endPan() {
state.isPanning = false;
state.lastPointer = null;
}
// ---- Actions ----
function viewPart(partId) {
window.open('/pos/catalog?part=' + partId, '_blank');
}
function searchPart(query) {
window.open('/pos/catalog?search=' + encodeURIComponent(query), '_blank');
}
function addToCart(partId) {
// Reuse the catalog cart logic if available
alert('Funcion disponible desde el catalogo. Busca la parte para agregarla al carrito.');
}
// ---- Helpers ----
function esc(s) {
if (!s) return '';
var d = document.createElement('div');
d.appendChild(document.createTextNode(s));
return d.innerHTML;
}
// ---- Public API ----
window.DiagramsApp = {
openDiagram: loadDiagram,
selectHotspot: selectHotspot,
viewPart: viewPart,
searchPart: searchPart,
addToCart: addToCart,
};
// Init on DOM ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();