document.addEventListener('DOMContentLoaded', function() { const fileInput = document.getElementById('file-upload'); const fileInfo = document.getElementById('file-info'); const fileName = document.getElementById('file-name'); const fileSize = document.getElementById('file-size'); const removeFileBtn = document.getElementById('remove-file'); const analysisSection = document.getElementById('analysis-section'); const resultsSection = document.getElementById('results-section'); const analyzeBtn = document.getElementById('analyze-btn'); const exportExcelBtn = document.getElementById('export-excel'); const resultsTable = document.getElementById('results-table'); // Drag and drop functionality const dropZone = document.querySelector('.border-dashed'); ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { dropZone.addEventListener(eventName, preventDefaults, false); }); // Helper function to format timber dimensions function formatTimberDimensions(width, height) { return `${(width*1000).toFixed(0)}x${(height*1000).toFixed(0)}`; } function preventDefaults(e) { e.preventDefault(); e.stopPropagation(); } ['dragenter', 'dragover'].forEach(eventName => { dropZone.addEventListener(eventName, highlight, false); }); ['dragleave', 'drop'].forEach(eventName => { dropZone.addEventListener(eventName, unhighlight, false); }); function highlight() { dropZone.classList.add('border-blue-500'); dropZone.classList.add('bg-blue-50'); } function unhighlight() { dropZone.classList.remove('border-blue-500'); dropZone.classList.remove('bg-blue-50'); } dropZone.addEventListener('drop', handleDrop, false); function handleDrop(e) { const dt = e.dataTransfer; const files = dt.files; handleFiles(files); } fileInput.addEventListener('change', function() { handleFiles(this.files); }); function handleFiles(files) { if (files.length > 0) { const file = files[0]; if (file.name.endsWith('.ifc')) { fileName.textContent = file.name; fileSize.textContent = formatFileSize(file.size); fileInfo.classList.remove('hidden'); analysisSection.classList.remove('hidden'); resultsSection.classList.add('hidden'); } else { alert('Please upload a valid .ifc file'); } } } removeFileBtn.addEventListener('click', function() { fileInput.value = ''; fileInfo.classList.add('hidden'); analysisSection.classList.add('hidden'); resultsSection.classList.add('hidden'); }); function formatFileSize(bytes) { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } analyzeBtn.addEventListener('click', function() { // Simulate analysis (in a real app, this would use an IFC parser library) analyzeBtn.innerHTML = '🌀 Analyzing...'; analyzeBtn.disabled = true; setTimeout(() => { // Enhanced mock data with more detailed analysis const mockData = { elements: 156, properties: 412, materials: 24, quantities: 67, totalArea: 0, totalVolume: 0, groups: { 'Walls': [], 'Doors': [], 'Windows': [], 'Structural': [], 'Furniture': [], 'Plumbing': [], 'Electrical': [], 'HVAC': [], 'Other': [], 'Timber': [] }, timberTypes: { 'C24 45x195': [], 'C24 70x195': [], 'C24 95x195': [], 'GL24h 120x120': [], 'GL24h 160x160': [], 'GL24h 200x200': [] }, items: [ { id: '12345', type: 'Wall', name: 'Interior Wall 1', properties: 15, width: 0.2, height: 2.7, depth: 5.8, area: 15.66, volume: 3.132, material: 'Gypsum Board', group: 'Walls', location: 'Level 1', classification: 'Interior Partition', propertiesList: [ {name: 'Fire Rating', value: '60 min'}, {name: 'STC Rating', value: '45'}, {name: 'U-Value', value: '0.35 W/m²K'} ] }, { id: '12346', type: 'Door', name: 'Main Entrance', properties: 12, width: 1.0, height: 2.1, depth: 0.1, area: 2.1, volume: 0.21, material: 'Wood', group: 'Doors', location: 'Level 1', classification: 'Fire Exit', propertiesList: [ {name: 'Fire Rating', value: '90 min'}, {name: 'Material', value: 'Solid Core'}, {name: 'Swing', value: 'Double'}, {name: 'Finish', value: 'Stained Oak'} ] }, { id: '12347', type: 'Window', name: 'Living Room Window', properties: 14, width: 1.5, height: 1.2, depth: 0.2, area: 1.8, volume: 0.36, material: 'Aluminum/Glass', group: 'Windows', location: 'Level 1', classification: 'Fixed Glazing', propertiesList: [ {name: 'U-Value', value: '1.4 W/m²K'}, {name: 'SHGC', value: '0.35'}, {name: 'Glazing', value: 'Double Low-E'}, {name: 'Frame', value: 'Thermal Break'} ] }, { id: '12348', type: 'Slab', name: 'First Floor', properties: 18, width: 12.0, height: 0.3, depth: 8.0, area: 96.0, volume: 28.8, material: 'Concrete', group: 'Structural', location: 'Level 1', classification: 'Composite Deck', propertiesList: [ {name: 'Concrete Strength', value: '4000 psi'}, {name: 'Reinforcement', value: '#4 @ 12" OC'}, {name: 'Fire Rating', value: '2 hr'}, {name: 'Topping', value: '2" Lightweight'} ] }, { id: '12349', type: 'Column', name: 'Structural Column A', properties: 16, width: 0.4, height: 3.0, depth: 0.4, area: 4.8, volume: 0.48, material: 'Steel', group: 'Structural', location: 'Grid A-1', classification: 'HSS Column', propertiesList: [ {name: 'Shape', value: 'HSS6x6x1/4'}, {name: 'Material Grade', value: 'A500 Gr.B'}, {name: 'Fireproofing', value: 'Spray-applied'}, {name: 'Connection Type', value: 'Moment'} ] }, { id: '12350', type: 'Timber', name: 'Roof Beam 1', properties: 14, width: 0.045, height: 0.195, length: 4.8, area: 0.936, volume: 0.042, material: 'C24 45x195', group: 'Timber', location: 'Roof Level', classification: 'Structural Timber', propertiesList: [ {name: 'Grade', value: 'C24'}, {name: 'Section', value: '45x195 mm'}, {name: 'Moisture Content', value: '18%'}, {name: 'Treatment', value: 'Tanalith E'} ] }, { id: '12351', type: 'Timber', name: 'Floor Joist 1', properties: 14, width: 0.07, height: 0.195, length: 3.6, area: 0.702, volume: 0.049, material: 'C24 70x195', group: 'Timber', location: 'First Floor', classification: 'Structural Timber', propertiesList: [ {name: 'Grade', value: 'C24'}, {name: 'Section', value: '70x195 mm'}, {name: 'Moisture Content', value: '18%'}, {name: 'Treatment', value: 'Tanalith E'} ] }, { id: '12352', type: 'Timber', name: 'Wall Stud 1', properties: 14, width: 0.045, height: 0.195, length: 2.7, area: 0.526, volume: 0.024, material: 'C24 45x195', group: 'Timber', location: 'Wall A', classification: 'Structural Timber', propertiesList: [ {name: 'Grade', value: 'C24'}, {name: 'Section', value: '45x195 mm'}, {name: 'Moisture Content', value: '18%'}, {name: 'Treatment', value: 'Tanalith E'} ] }, { id: '12353', type: 'Timber', name: 'Glulam Beam', properties: 14, width: 0.16, height: 0.16, length: 5.4, area: 0.864, volume: 0.138, material: 'GL24h 160x160', group: 'Timber', location: 'Main Hall', classification: 'Structural Timber', propertiesList: [ {name: 'Grade', value: 'GL24h'}, {name: 'Section', value: '160x160 mm'}, {name: 'Moisture Content', value: '12%'}, {name: 'Fire Rating', value: 'R30'} ] }, ] }; // Calculate totals and group items mockData.items.forEach(item => { mockData.totalArea += item.area; mockData.totalVolume += item.volume; mockData.groups[item.group].push(item); // Group timber by type if (item.group === 'Timber' && mockData.timberTypes[item.material]) { mockData.timberTypes[item.material].push(item); } }); // Calculate timber summaries const timberSummaries = {}; for (const [type, items] of Object.entries(mockData.timberTypes)) { if (items.length > 0) { const totalLength = items.reduce((sum, item) => sum + item.length, 0); timberSummaries[type] = { count: items.length, totalLength: totalLength.toFixed(2), totalVolume: items.reduce((sum, item) => sum + item.volume, 0).toFixed(3), section: items[0].width*1000 + 'x' + items[0].height*1000 + ' mm' }; } } // Update counts and totals document.getElementById('elements-count').textContent = mockData.elements; document.getElementById('properties-count').textContent = mockData.properties; document.getElementById('materials-count').textContent = mockData.materials; document.getElementById('quantities-count').textContent = mockData.quantities; document.getElementById('total-volume').textContent = mockData.totalVolume.toFixed(3) + ' m³'; // Add timber summary section if (Object.keys(timberSummaries).length > 0) { const resultsContainer = document.querySelector('#results-section .max-w-4xl'); let timberHTML = `

Timber Summary

`; for (const [type, summary] of Object.entries(timberSummaries)) { timberHTML += ` `; } timberHTML += `
Type Section Count Total Length Total Volume
${type} ${summary.section} ${summary.count} ${summary.totalLength} m ${summary.totalVolume} m³
`; if (resultsContainer.querySelector('#timber-summary')) { resultsContainer.querySelector('#timber-summary').innerHTML = timberHTML; } else { const timberDiv = document.createElement('div'); timberDiv.id = 'timber-summary'; timberDiv.innerHTML = timberHTML; resultsContainer.insertBefore(timberDiv, resultsContainer.querySelector('.overflow-x-auto')); } } // Populate detailed table resultsTable.innerHTML = ''; mockData.items.forEach(item => { const row = document.createElement('tr'); row.className = 'hover:bg-gray-50 cursor-pointer'; row.innerHTML = ` ${item.id} ${item.type} ${item.name}
${item.material} ${item.location}
${item.properties} ${item.group} `; resultsTable.appendChild(row); // Add details row const detailsRow = document.createElement('tr'); detailsRow.id = `details-${item.id}`; detailsRow.className = 'hidden bg-gray-50'; const propertiesHTML = item.propertiesList.map(prop => `
${prop.name}: ${prop.value}
` ).join(''); detailsRow.innerHTML = `
Dimensions
Width: ${item.width.toFixed(2)} m
Height: ${item.height.toFixed(2)} m
Depth: ${item.depth.toFixed(2)} m
Area: ${item.area.toFixed(2)} m²
Volume: ${item.volume.toFixed(2)} m³
Classification
Location: ${item.location}
Type: ${item.classification}
Properties
${propertiesHTML}
`; resultsTable.appendChild(detailsRow); }); // Add event listeners for details toggles document.querySelectorAll('.details-btn').forEach(btn => { btn.addEventListener('click', function() { const id = this.getAttribute('data-id'); const detailsRow = document.getElementById(`details-${id}`); const icon = this.querySelector('i'); if (detailsRow.classList.contains('hidden')) { detailsRow.classList.remove('hidden'); icon.setAttribute('data-feather', 'chevron-up'); } else { detailsRow.classList.add('hidden'); icon.setAttribute('data-feather', 'chevron-down'); } feather.replace(); }); }); analyzeBtn.innerHTML = 'Analyze IFC File'; analyzeBtn.disabled = false; resultsSection.classList.remove('hidden'); feather.replace(); }, 2000); }); exportExcelBtn.addEventListener('click', function() { // In a real app, this would generate an Excel file from the IFC data const btn = this; btn.innerHTML = '🌀 Exporting...'; btn.disabled = true; setTimeout(() => { // Mock Excel data structure with timber summary const excelData = { summary: [ ['IFC Analysis Report', '', '', '', ''], ['Generated on', new Date().toLocaleString(), '', '', ''], ['', '', '', '', ''], ['Summary Metrics', '', '', '', ''], ['Total Elements', mockData.elements, '', '', ''], ['Total Properties', mockData.properties, '', '', ''], ['Total Materials', mockData.materials, '', '', ''], ['Total Volume', mockData.totalVolume.toFixed(3) + ' m³', '', '', ''] ], timberSummary: [ ['Timber Summary', '', '', '', ''], ['Type', 'Section', 'Count', 'Total Length (m)', 'Total Volume (m³)'], ...Object.entries(timberSummaries).map(([type, summary]) => [ type, summary.section, summary.count, summary.totalLength, summary.totalVolume ]) ], elements: [ ['Element Details', '', '', '', '', '', '', ''], ['ID', 'Type', 'Name', 'Material', 'Location', 'Length (m)', 'Volume (m³)', 'Properties'], ...mockData.items.map(item => [ item.id, item.type, item.name, item.material, item.location, item.length ? item.length.toFixed(2) : '', item.volume ? item.volume.toFixed(3) : '', item.properties ]) ] }; console.log('Excel data prepared:', excelData); // For demo purposes // In a real implementation, we would use SheetJS to generate the Excel file alert('Excel export would generate:\n- Summary sheet\n- Timber summary by type\n- Detailed elements list'); btn.innerHTML = 'Export to Excel'; btn.disabled = false; feather.replace(); }, 1500); }); });