Meroar's picture
Make an app that calculates that allows the user to upload a bunch of files, and then analyzes all of those files content and compares them to each other and create unique indentifier tags for each identical largest common similarity among all files and makes a key that states how big the lcs's are (next to the icon or letter ) for example:
257af98 verified
class FileAnalyzer {
constructor() {
this.files = [];
this.results = [];
this.initializeEventListeners();
}
initializeEventListeners() {
const uploadBtn = document.getElementById('uploadBtn');
const fileInput = document.getElementById('fileInput');
const analyzeBtn = document.getElementById('analyzeBtn');
uploadBtn.addEventListener('click', () => fileInput.click());
fileInput.addEventListener('change', (e) => this.handleFileSelection(e));
analyzeBtn.addEventListener('click', () => this.analyzeFiles());
}
handleFileSelection(event) {
const files = Array.from(event.target.files);
if (files.length === 0) return;
this.files = files;
this.displayFileList();
document.getElementById('analyzeBtn').classList.remove('hidden');
}
displayFileList() {
const fileList = document.getElementById('fileList');
const fileItems = document.getElementById('fileItems');
fileItems.innerHTML = '';
this.files.forEach((file, index) => {
const fileItem = document.createElement('div');
fileItem.className = 'file-item flex items-center justify-between bg-cyan-50 rounded-lg p-3 border border-cyan-200';
fileItem.innerHTML = `
<div class="flex items-center">
<i data-feather="file-text" class="w-5 h-5 text-cyan-600 mr-3"></i>
<span class="text-cyan-800 font-medium code-font">${file.name}</span>
</div>
<span class="text-cyan-600 text-sm">${this.formatFileSize(file.size)}</span>
`;
fileItems.appendChild(fileItem);
});
fileList.classList.remove('hidden');
feather.replace();
}
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];
}
async analyzeFiles() {
if (this.files.length < 2) {
this.showNotification('Please select at least 2 files to analyze', 'error');
return;
}
this.showLoading(true);
try {
const fileContents = await this.readAllFiles();
this.results = this.findLCSAcrossFiles(fileContents);
this.displayResults();
} catch (error) {
this.showNotification('Error analyzing files: ' + error.message, 'error');
} finally {
this.showLoading(false);
}
}
readAllFiles() {
const promises = this.files.map(file => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = e => resolve({
name: file.name,
content: e.target.result,
size: file.size
});
reader.onerror = () => reject(new Error(`Failed to read file: ${file.name}`));
reader.readAsText(file);
});
});
return Promise.all(promises);
}
findLCSAcrossFiles(fileContents) {
const results = [];
const lcsMap = new Map();
let tagCounter = 0;
// Generate all possible file pairs
for (let i = 0; i < fileContents.length; i++) {
for (let j = i + 1; j < fileContents.length; j++) {
const file1 = fileContents[i];
const file2 = fileContents[j];
const lcs = this.findLCS(file1.content, file2.content);
if (lcs.length > 10) { // Only consider significant LCS
const key = this.normalizeLCS(lcs);
if (!lcsMap.has(key)) {
const tag = String.fromCharCode(65 + (tagCounter++ % 26)); // A, B, C, ...
lcsMap.set(key, {
tag: tag,
content: lcs,
length: lcs.length,
files: new Set([file1.name, file2.name])
});
} else {
const existing = lcsMap.get(key);
existing.files.add(file1.name);
existing.files.add(file2.name);
}
}
}
// Convert map to array and sort by length
return Array.from(lcsMap.values())
.sort((a, b) => b.length - a.length)
.slice(0, 10); // Limit to top 10 results
}
findLCS(str1, str2) {
const m = str1.length;
const n = str2.length;
const dp = Array(m + 1).fill().map(() => Array(n + 1).fill(0));
let maxLength = 0;
let endIndex = 0;
for (let i = 1; i <= m; i++) {
for (let j = 1; j <= n; j++) {
if (str1[i - 1] === str2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
if (dp[i][j] > maxLength) {
maxLength = dp[i][j];
endIndex = i - 1;
}
} else {
dp[i][j] = 0;
}
}
}
return str1.substring(endIndex - maxLength + 1, endIndex + 1);
}
normalizeLCS(lcs) {
// Remove whitespace and normalize for comparison
return lcs.replace(/\s+/g, '').trim();
}
displayResults() {
const resultsSection = document.getElementById('resultsSection');
const resultsContainer = document.getElementById('resultsContainer');
resultsContainer.innerHTML = '';
if (this.results.length === 0) {
resultsContainer.innerHTML = `
<div class="text-center py-8">
<i data-feather="alert-circle" class="w-16 h-16 text-cyan-500 mx-auto mb-4"></i>
<p class="text-cyan-600 text-lg">No significant common patterns found</p>
</div>
`;
} else {
this.results.forEach((result, index) => {
const resultCard = document.createElement('div');
resultCard.className = 'fade-in gradient-border rounded-xl p-6 bg-white';
resultCard.style.animationDelay = `${index * 0.1}s`;
const fileList = Array.from(result.files).join(', ');
const preview = result.content.length > 200
? result.content.substring(0, 200) + '...'
: result.content;
resultCard.innerHTML = `
<div class="flex items-center justify-between mb-4">
<div class="flex items-center">
<div class="lcs-tag bg-gradient-to-r from-cyan-500 to-cyan-600 text-white rounded-full w-12 h-12 flex items-center justify-center font-bold text-lg shadow-lg">
${result.tag}
</div>
<div class="ml-4">
<h3 class="text-xl font-bold text-cyan-800">Pattern ${result.tag}</h3>
<p class="text-cyan-600">${result.length} characters</p>
</div>
</div>
<span class="text-cyan-500 font-semibold">${result.files.size} files</span>
</div>
<div class="mb-4">
<h4 class="font-semibold text-cyan-700 mb-2">Files:</h4>
<p class="text-cyan-800 code-font text-sm bg-cyan-50 p-3 rounded-lg">
${fileList}
</p>
</div>
<div>
<h4 class="font-semibold text-cyan-700 mb-2">Common Content Preview:</h4>
<pre class="code-font bg-gray-900 text-cyan-100 p-4 rounded-lg overflow-x-auto text-sm">${this.escapeHtml(preview)}</pre>
</div>
`;
resultsContainer.appendChild(resultCard);
});
}
resultsSection.classList.remove('hidden');
feather.replace();
// Scroll to results
resultsSection.scrollIntoView({ behavior: 'smooth' });
}
escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
showLoading(show) {
const loadingState = document.getElementById('loadingState');
if (show) {
loadingState.classList.remove('hidden');
} else {
loadingState.classList.add('hidden');
}
}
showNotification(message, type = 'info') {
// Create notification element
const notification = document.createElement('div');
notification.className = `fixed top-4 right-4 p-4 rounded-xl shadow-lg z-50 fade-in ${
type === 'error' ? 'bg-red-500 text-white' : 'bg-cyan-500 text-white'
}`;
notification.innerHTML = `
<div class="flex items-center">
<i data-feather="${type === 'error' ? 'alert-triangle' : 'info'}" class="w-5 h-5 mr-2"></i>
<span>${message}</span>
</div>
`;
document.body.appendChild(notification);
feather.replace();
// Remove after 5 seconds
setTimeout(() => {
notification.remove();
}, 5000);
}
}
// Initialize the application when DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
new FileAnalyzer();
});