let activeViewer = null; let currentHotspots = []; /** * Initializes and shows the Pannellum 360° panorama viewer with security overlays. * @param {string} imageUrl - Authorized URL to fetch the secure image stream * @param {Array} hotspots - List of hotspots from the database */ function initPanoramaViewer(imageUrl, hotspots = []) { currentHotspots = hotspots; const container = document.getElementById('viewer-container'); container.style.display = 'block'; if (activeViewer) { try { activeViewer.destroy(); } catch (e) {} } // Chuyển đổi dữ liệu hotspots từ DB sang định dạng Pannellum const pannellumHotspots = hotspots.map(h => ({ pitch: h.pitch, yaw: h.yaw, type: "info", text: h.text || "Điểm điều hướng", id: h._id, clickHandlerFunc: () => { if (h.targetSceneId) { // Gọi hàm openScene từ main_map.js openScene(h.targetSceneId); } } })); // Initialize Pannellum Equirectangular viewer activeViewer = pannellum.viewer('panorama-viewer', { "type": "equirectangular", "panorama": imageUrl, "autoLoad": true, "showControls": true, "compass": false, "mouseZoom": true, "keyboardZoom": true, "crossOrigin": "anonymous", "hotSpots": pannellumHotspots }); // Security constraints inside the viewer applyViewerSecurity(); } /** * Closes and destroys the active panorama viewer. */ function closeViewer() { document.getElementById('viewer-container').style.display = 'none'; // Xóa trạng thái Scene đang hoạt động khi đóng viewer localStorage.removeItem('activeSceneId'); localStorage.removeItem('activeScenePrivacy'); localStorage.removeItem('activeSceneToken'); if (activeViewer) { try { activeViewer.destroy(); } catch (e) {} activeViewer = null; } } /** * Appends event listeners to block right-clicks and common image saving shortcuts. */ function applyViewerSecurity() { // Target the actual viewer element where Pannellum renders const container = document.getElementById('viewer-container'); const panoramaViewer = document.getElementById('panorama-viewer'); const handleContextMenu = (e) => { e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation(); // Nếu viewer đang hoạt động, lấy tọa độ Pitch/Yaw tại điểm click if (activeViewer) { // Lấy tọa độ cầu (Pitch/Yaw) từ điểm click chuột const coords = activeViewer.mouseEventToCoords(e); if (!coords) return; const pitch = coords[0]; const yaw = coords[1]; // Kiểm tra xem có hotspot nào gần điểm click không (ngưỡng 2 độ) const existing = currentHotspots.find(h => Math.abs(h.pitch - pitch) < 2 && Math.abs(h.yaw - yaw) < 2 ); if (typeof window.handleHotspotCreation === 'function') { window.handleHotspotCreation(pitch, yaw, existing); } else { console.log(`Coordinates captured: Pitch ${pitch}, Yaw ${yaw}`); } } return false; }; // Sử dụng capture phase (true) để bắt sự kiện trước khi nó chạm đến Pannellum container.addEventListener('contextmenu', handleContextMenu, true); panoramaViewer.addEventListener('contextmenu', handleContextMenu, true); // Block drag and drop container.addEventListener('dragstart', (e) => { e.preventDefault(); }); } // Global safety shortcut listeners (F12, Ctrl+S, Ctrl+U, Ctrl+Shift+I) document.addEventListener('keydown', (e) => { // Only enforce when viewer is active if (document.getElementById('viewer-container').style.display === 'block') { const isCtrlS = e.ctrlKey && (e.key === 's' || e.key === 'S'); const isCtrlU = e.ctrlKey && (e.key === 'u' || e.key === 'U'); const isF12 = e.key === 'F12'; const isCtrlShiftI = e.ctrlKey && e.shiftKey && (e.key === 'i' || e.key === 'I'); if (isCtrlS || isCtrlU || isF12 || isCtrlShiftI) { e.preventDefault(); alert('Security Alert: Inspection and saving functions are restricted on this viewer.'); return false; } } });