diff --git a/webroot/js/calculator.js b/webroot/js/calculator.js
index 510fdfb..7d95c85 100644
--- a/webroot/js/calculator.js
+++ b/webroot/js/calculator.js
@@ -140,11 +140,17 @@ export class Calculator {
}
setupEventListeners() {
- // Event delegation for calculator buttons
+ // Event delegation for calculator buttons - only handle from active tab's calculator
document.addEventListener('click', (e) => {
// Only handle clicks on calculator buttons
if (!e.target.classList.contains('calc-btn')) return;
+ // Verify the button is in the active tab's calculator (if app is initialized)
+ const activeContent = document.querySelector('.tab-content.active');
+ if (activeContent && !activeContent.contains(e.target)) {
+ return; // Button is not in active tab's calculator
+ }
+
const button = e.target;
if (button.classList.contains('number')) {
diff --git a/webroot/js/filesystems/base.js b/webroot/js/filesystems/base.js
index 21dbc8d..6d65363 100644
--- a/webroot/js/filesystems/base.js
+++ b/webroot/js/filesystems/base.js
@@ -22,8 +22,8 @@ export class BaseFilesystem {
// Helper function to format labels with offset information
const formatLabel = (label) => {
- // Match patterns like "(Boot-Offset 0x00)", "(MFT-Header-Offset 0x14)", "(Offset 0x00)" and wrap offset info
- const offsetPattern = /(\((Boot-Offset|MFT-Header-Offset|Offset) [^)]+\))/g;
+ // Match patterns like "(Boot-Offset 0x00)", "(MFT-Header-Offset 0x14)", "(Superblock-Offset 0x00)", "(GDT-Offset 0x08)", "(Offset 0x00)" and wrap offset info
+ const offsetPattern = /(\((Boot-Offset|MFT-Header-Offset|Superblock-Offset|GDT-Offset|Offset) [^)]+\))/g;
return label.replace(offsetPattern, '$1');
};
@@ -57,8 +57,8 @@ export class BaseFilesystem {
// Helper function to format labels with offset information
const formatLabel = (label) => {
- // Match patterns like "(Boot-Offset 0x00)", "(MFT-Header-Offset 0x14)", "(Offset 0x00)" and wrap offset info
- const offsetPattern = /(\((Boot-Offset|MFT-Header-Offset|Offset) [^)]+\))/g;
+ // Match patterns like "(Boot-Offset 0x00)", "(MFT-Header-Offset 0x14)", "(Superblock-Offset 0x00)", "(GDT-Offset 0x08)", "(Offset 0x00)" and wrap offset info
+ const offsetPattern = /(\((Boot-Offset|MFT-Header-Offset|Superblock-Offset|GDT-Offset|Offset) [^)]+\))/g;
return label.replace(offsetPattern, '$1');
};
@@ -164,6 +164,35 @@ export class BaseFilesystem {
throw new Error('calculate method must be implemented by subclass');
}
+ // Validate that input values are within reasonable ranges
+ validateInputRanges(variantId, values) {
+ const variant = this.variants.find(v => v.id === variantId);
+ if (!variant) return { valid: true, errors: [] };
+
+ const errors = [];
+
+ // Basic sanity checks that can be applied to all filesystems
+ // Check for extremely large values that would cause issues
+ const maxSafeValue = Number.MAX_SAFE_INTEGER / 1024; // Allow some headroom
+
+ Object.entries(values).forEach(([key, value]) => {
+ if (typeof value === 'number' && value > maxSafeValue) {
+ errors.push(`${key}: Wert zu groß (${value})`);
+ }
+ });
+
+ return {
+ valid: errors.length === 0,
+ errors: errors
+ };
+ }
+
+ // Abstract method for subclasses to override with specific validation
+ validateFilesystemSpecific(variantId, values) {
+ // Subclasses can override this for filesystem-specific validation
+ return { valid: true, errors: [] };
+ }
+
// Setup input event listeners for this filesystem
setupInputListeners(variantId) {
const variant = this.variants.find(v => v.id === variantId);
@@ -196,7 +225,8 @@ export class BaseFilesystem {
variant.constants.forEach(constant => {
const element = document.getElementById(constant.id);
if (element) {
- values[constant.id] = parseHex(element.value) || 0;
+ const parsedValue = parseHex(element.value);
+ values[constant.id] = parsedValue !== null ? parsedValue : 0;
}
});
@@ -204,7 +234,8 @@ export class BaseFilesystem {
variant.inputs.forEach(input => {
const element = document.getElementById(input.id);
if (element) {
- values[input.id] = parseHex(element.value);
+ const parsedValue = parseHex(element.value);
+ values[input.id] = parsedValue !== null ? parsedValue : 0;
}
});
diff --git a/webroot/js/filesystems/exfat.js b/webroot/js/filesystems/exfat.js
index 549e998..7c3b1e9 100644
--- a/webroot/js/filesystems/exfat.js
+++ b/webroot/js/filesystems/exfat.js
@@ -64,11 +64,47 @@ export class ExFATFilesystem extends BaseFilesystem {
if (variantId !== 'exfat') return;
const values = this.getInputValues(variantId);
+
+ // Validate input ranges
+ const validation = this.validateFilesystemSpecific(variantId, values);
+ if (!validation.valid) {
+ console.warn('exFAT validation errors:', validation.errors);
+ }
+
const results = {};
this.calculateExFAT(values, results);
}
+ validateFilesystemSpecific(variantId, values) {
+ if (variantId !== 'exfat') return { valid: true, errors: [] };
+
+ const errors = [];
+
+ // Validate cluster number is reasonable
+ if (values.clusterNumberExfat !== undefined && values.clusterNumberExfat < 2) {
+ errors.push('clusterNumberExfat: Cluster-Nummer muss >= 2 sein');
+ }
+
+ // Validate sector and cluster size exponents
+ if (values.sectorSizeExfat !== undefined) {
+ if (values.sectorSizeExfat < 0 || values.sectorSizeExfat > 15) {
+ errors.push('sectorSizeExfat: Exponent muss zwischen 0 und 15 liegen');
+ }
+ }
+
+ if (values.clusterSizeSectorsExfat !== undefined) {
+ if (values.clusterSizeSectorsExfat < 0 || values.clusterSizeSectorsExfat > 25) {
+ errors.push('clusterSizeSectorsExfat: Exponent muss zwischen 0 und 25 liegen');
+ }
+ }
+
+ return {
+ valid: errors.length === 0,
+ errors: errors
+ };
+ }
+
calculateExFAT(values, results) {
// Calculate actual sector size from 2^n
const sectorSize = Math.pow(2, values.sectorSizeExfat);
diff --git a/webroot/js/filesystems/ext.js b/webroot/js/filesystems/ext.js
index 1a13b1f..72b8d3f 100644
--- a/webroot/js/filesystems/ext.js
+++ b/webroot/js/filesystems/ext.js
@@ -97,26 +97,71 @@ export class EXTFilesystem extends BaseFilesystem {
if (variantId !== 'ext') return;
const values = this.getInputValues(variantId);
+
+ // Validate input ranges specific to EXT filesystem
+ const validation = this.validateFilesystemSpecific(variantId, values);
+ if (!validation.valid) {
+ console.warn('EXT validation errors:', validation.errors);
+ // Continue with calculation but log warnings
+ }
+
const results = {};
this.calculateEXT(values, results);
}
+ validateFilesystemSpecific(variantId, values) {
+ if (variantId !== 'ext') return { valid: true, errors: [] };
+
+ const errors = [];
+
+ // Validate blockgroup number doesn't exceed total blockgroups
+ if (values.blockgroupNumberEXT !== undefined && values.totalBlocksEXT !== undefined && values.blocksPerGroupEXT !== undefined && values.blocksPerGroupEXT > 0) {
+ const totalBlockgroups = Math.ceil(values.totalBlocksEXT / values.blocksPerGroupEXT);
+ if (values.blockgroupNumberEXT >= totalBlockgroups) {
+ errors.push(`blockgroupNumberEXT: ${values.blockgroupNumberEXT} >= Gesamtblockgruppen: ${totalBlockgroups}`);
+ }
+ }
+
+ // Validate inode number isn't 0 (no inode 0 exists)
+ if (values.targetInodeEXT !== undefined && values.targetInodeEXT === 0) {
+ errors.push('targetInodeEXT: Inode 0 existiert nicht');
+ }
+
+ // Validate block and inode sizes are reasonable
+ if (values.blockSizeExpEXT !== undefined) {
+ const blockSize = Math.pow(2, 10 + values.blockSizeExpEXT);
+ if (blockSize < 1024 || blockSize > 65536) {
+ errors.push(`Block-Größe: ${blockSize} außerhalb akzeptabeler Grenzen (1024-65536)`);
+ }
+ }
+
+ if (values.inodeSizeEXT !== undefined) {
+ if (values.inodeSizeEXT < 128 || values.inodeSizeEXT > 4096) {
+ errors.push(`Inode-Größe: ${values.inodeSizeEXT} außerhalb akzeptabeler Grenzen (128-4096)`);
+ }
+ }
+
+ return {
+ valid: errors.length === 0,
+ errors: errors
+ };
+ }
+
calculateEXT(values, results) {
+ try {
// Superblock Position
if (checkDependencies(['partitionStartEXT', 'superblockOffsetEXT'])) {
results.superblockPos = values.partitionStartEXT + values.superblockOffsetEXT;
- updateResultItem('superblockPosEXT', results.superblockPos, true,
- `0x${values.partitionStartEXT.toString(16)} + 0x${values.superblockOffsetEXT.toString(16)} = 0x${results.superblockPos.toString(16)}`);
+ updateResultItem('superblockPosEXT', { bytes: results.superblockPos, other: `Block 1 (bei 1024-Byte Blöcken)` }, true,
+ `0x${Math.floor(values.partitionStartEXT).toString(16).toUpperCase()} + 0x${Math.floor(values.superblockOffsetEXT).toString(16).toUpperCase()} = 0x${Math.floor(results.superblockPos).toString(16).toUpperCase()}`);
} else {
updateResultItem('superblockPosEXT', 0, false);
- }
-
- // Block Size (2^(10+n))
+ } // Block Size (2^(10+n))
if (checkDependencies(['blockSizeExpEXT'])) {
results.blockSize = Math.pow(2, 10 + values.blockSizeExpEXT);
- updateResultItem('blockSizeBytesEXT', results.blockSize, true,
- `2^(10 + ${values.blockSizeExpEXT}) = 2^${10 + values.blockSizeExpEXT} = 0x${results.blockSize.toString(16)} (${results.blockSize} Bytes)`);
+ updateResultItem('blockSizeBytesEXT', { bytes: results.blockSize, other: `2^${10 + values.blockSizeExpEXT}` }, true,
+ `2^(10 + ${values.blockSizeExpEXT}) = 2^${10 + values.blockSizeExpEXT} = 0x${Math.floor(results.blockSize).toString(16).toUpperCase()} (${results.blockSize} Bytes)`);
} else {
updateResultItem('blockSizeBytesEXT', 0, false);
}
@@ -132,13 +177,18 @@ export class EXTFilesystem extends BaseFilesystem {
// GDT Start (block after superblock)
if (checkDependencies(['partitionStartEXT', 'superblockOffsetEXT', 'blockSizeExpEXT']) && results.blockSize !== undefined) {
- // If block size is 1024 (0x400), superblock is in block 1, GDT starts at block 2
- // Otherwise, superblock is in block 0, GDT starts at block 1
+ // Special handling for 1024-byte block size:
+ // - Superblock is in block 1 (not block 0) because it starts at offset 0x400
+ // - GDT follows in block 2
+ // For other block sizes (2048, 4096, ...):
+ // - Superblock is in block 0 (starts at offset 0)
+ // - GDT follows in block 1
const superblockBlock = (results.blockSize === 1024) ? 1 : 0;
const gdtBlock = superblockBlock + 1;
results.gdtStart = values.partitionStartEXT + (gdtBlock * results.blockSize);
- updateResultItem('gdtStartEXT', results.gdtStart, true,
- `0x${values.partitionStartEXT.toString(16)} + (Block ${gdtBlock} × 0x${results.blockSize.toString(16)}) = 0x${results.gdtStart.toString(16)}`);
+ const gdtBlockNum = Math.floor(results.gdtStart / results.blockSize) - Math.floor(values.partitionStartEXT / results.blockSize);
+ updateResultItem('gdtStartEXT', { bytes: results.gdtStart, other: `Block ${gdtBlock}` }, true,
+ `0x${Math.floor(values.partitionStartEXT).toString(16).toUpperCase()} + (Block ${gdtBlock} × 0x${Math.floor(results.blockSize).toString(16).toUpperCase()}) = 0x${Math.floor(results.gdtStart).toString(16).toUpperCase()}`);
} else {
updateResultItem('gdtStartEXT', 0, false);
}
@@ -146,8 +196,8 @@ export class EXTFilesystem extends BaseFilesystem {
// GDT Entry for specific Blockgroup
if (checkDependencies(['blockgroupNumberEXT', 'groupDescSizeEXT']) && results.gdtStart !== undefined) {
results.gdtBlockgroupOffset = results.gdtStart + (values.blockgroupNumberEXT * values.groupDescSizeEXT);
- updateResultItem('gdtBlockgroupOffsetEXT', results.gdtBlockgroupOffset, true,
- `0x${results.gdtStart.toString(16)} + (0x${values.blockgroupNumberEXT.toString(16)} × 0x${values.groupDescSizeEXT.toString(16)}) = 0x${results.gdtBlockgroupOffset.toString(16)}`);
+ updateResultItem('gdtBlockgroupOffsetEXT', { bytes: results.gdtBlockgroupOffset, other: `Eintrag ${values.blockgroupNumberEXT}` }, true,
+ `0x${Math.floor(results.gdtStart).toString(16).toUpperCase()} + (0x${Math.floor(values.blockgroupNumberEXT).toString(16).toUpperCase()} × 0x${Math.floor(values.groupDescSizeEXT).toString(16).toUpperCase()}) = 0x${Math.floor(results.gdtBlockgroupOffset).toString(16).toUpperCase()}`);
} else {
updateResultItem('gdtBlockgroupOffsetEXT', 0, false);
}
@@ -173,8 +223,8 @@ export class EXTFilesystem extends BaseFilesystem {
// Inode Table Start
if (checkDependencies(['partitionStartEXT', 'inodeTableBlockGroupEXT', 'blockSizeExpEXT']) && results.blockSize !== undefined) {
results.inodeTableStart = values.partitionStartEXT + (values.inodeTableBlockGroupEXT * results.blockSize);
- updateResultItem('inodeTableStartEXT', results.inodeTableStart, true,
- `0x${values.partitionStartEXT.toString(16)} + (0x${values.inodeTableBlockGroupEXT.toString(16)} × 0x${results.blockSize.toString(16)}) = 0x${results.inodeTableStart.toString(16)}`);
+ updateResultItem('inodeTableStartEXT', { bytes: results.inodeTableStart, other: `Block ${Math.floor(values.inodeTableBlockGroupEXT)}` }, true,
+ `0x${Math.floor(values.partitionStartEXT).toString(16).toUpperCase()} + (0x${Math.floor(values.inodeTableBlockGroupEXT).toString(16).toUpperCase()} × 0x${Math.floor(results.blockSize).toString(16).toUpperCase()}) = 0x${Math.floor(results.inodeTableStart).toString(16).toUpperCase()}`);
} else {
updateResultItem('inodeTableStartEXT', 0, false);
}
@@ -182,8 +232,8 @@ export class EXTFilesystem extends BaseFilesystem {
// Inode Offset
if (checkDependencies(['targetInodeEXT', 'inodesPerGroupEXT', 'inodeSizeEXT']) && results.inodeTableStart !== undefined && results.inodeRelativeIndex !== undefined) {
results.inodeOffset = results.inodeTableStart + (results.inodeRelativeIndex * values.inodeSizeEXT);
- updateResultItem('inodeOffsetEXT', results.inodeOffset, true,
- `0x${results.inodeTableStart.toString(16)} + (0x${results.inodeRelativeIndex.toString(16)} × 0x${values.inodeSizeEXT.toString(16)}) = 0x${results.inodeOffset.toString(16)}`);
+ updateResultItem('inodeOffsetEXT', { bytes: results.inodeOffset, other: `Inode-Größe: 0x${Math.floor(values.inodeSizeEXT).toString(16).toUpperCase()}` }, true,
+ `0x${Math.floor(results.inodeTableStart).toString(16).toUpperCase()} + (0x${Math.floor(results.inodeRelativeIndex).toString(16).toUpperCase()} × 0x${Math.floor(values.inodeSizeEXT).toString(16).toUpperCase()}) = 0x${Math.floor(results.inodeOffset).toString(16).toUpperCase()}`);
} else {
updateResultItem('inodeOffsetEXT', 0, false);
}
@@ -191,8 +241,8 @@ export class EXTFilesystem extends BaseFilesystem {
// Block Offset
if (checkDependencies(['partitionStartEXT', 'targetBlockEXT', 'blockSizeExpEXT']) && results.blockSize !== undefined) {
results.blockOffset = values.partitionStartEXT + (values.targetBlockEXT * results.blockSize);
- updateResultItem('blockOffsetEXT', results.blockOffset, true,
- `0x${values.partitionStartEXT.toString(16)} + (0x${values.targetBlockEXT.toString(16)} × 0x${results.blockSize.toString(16)}) = 0x${results.blockOffset.toString(16)}`);
+ updateResultItem('blockOffsetEXT', { bytes: results.blockOffset, other: `Block 0x${Math.floor(values.targetBlockEXT).toString(16).toUpperCase()}` }, true,
+ `0x${Math.floor(values.partitionStartEXT).toString(16).toUpperCase()} + (0x${Math.floor(values.targetBlockEXT).toString(16).toUpperCase()} × 0x${Math.floor(results.blockSize).toString(16).toUpperCase()}) = 0x${Math.floor(results.blockOffset).toString(16).toUpperCase()}`);
} else {
updateResultItem('blockOffsetEXT', 0, false);
}
@@ -201,33 +251,33 @@ export class EXTFilesystem extends BaseFilesystem {
if (results.inodeTableStart !== undefined && checkDependencies(['inodeSizeEXT'])) {
// Inode 1 (Bad Blocks) - Index 0
results.badBlocksInode = results.inodeTableStart + (0 * values.inodeSizeEXT);
- updateResultItem('badBlocksInodeEXT', results.badBlocksInode, true,
- `0x${results.inodeTableStart.toString(16)} + (0 × 0x${values.inodeSizeEXT.toString(16)}) = 0x${results.badBlocksInode.toString(16)}`);
+ updateResultItem('badBlocksInodeEXT', { bytes: results.badBlocksInode, other: 'Inode 1 (Bad Blocks)' }, true,
+ `0x${Math.floor(results.inodeTableStart).toString(16).toUpperCase()} + (0 × 0x${Math.floor(values.inodeSizeEXT).toString(16).toUpperCase()}) = 0x${Math.floor(results.badBlocksInode).toString(16).toUpperCase()}`);
// Inode 2 (Root Directory) - Index 1
results.rootDirInode = results.inodeTableStart + (1 * values.inodeSizeEXT);
- updateResultItem('rootDirInodeEXT', results.rootDirInode, true,
- `0x${results.inodeTableStart.toString(16)} + (1 × 0x${values.inodeSizeEXT.toString(16)}) = 0x${results.rootDirInode.toString(16)}`);
+ updateResultItem('rootDirInodeEXT', { bytes: results.rootDirInode, other: 'Inode 2 (Root Dir)' }, true,
+ `0x${Math.floor(results.inodeTableStart).toString(16).toUpperCase()} + (1 × 0x${Math.floor(values.inodeSizeEXT).toString(16).toUpperCase()}) = 0x${Math.floor(results.rootDirInode).toString(16).toUpperCase()}`);
// Inode 3 (User Quotas) - Index 2
results.userQuotaInode = results.inodeTableStart + (2 * values.inodeSizeEXT);
- updateResultItem('userQuotaInodeEXT', results.userQuotaInode, true,
- `0x${results.inodeTableStart.toString(16)} + (2 × 0x${values.inodeSizeEXT.toString(16)}) = 0x${results.userQuotaInode.toString(16)}`);
+ updateResultItem('userQuotaInodeEXT', { bytes: results.userQuotaInode, other: 'Inode 3 (User Quota)' }, true,
+ `0x${Math.floor(results.inodeTableStart).toString(16).toUpperCase()} + (2 × 0x${Math.floor(values.inodeSizeEXT).toString(16).toUpperCase()}) = 0x${Math.floor(results.userQuotaInode).toString(16).toUpperCase()}`);
// Inode 4 (Group Quotas) - Index 3
results.groupQuotaInode = results.inodeTableStart + (3 * values.inodeSizeEXT);
- updateResultItem('groupQuotaInodeEXT', results.groupQuotaInode, true,
- `0x${results.inodeTableStart.toString(16)} + (3 × 0x${values.inodeSizeEXT.toString(16)}) = 0x${results.groupQuotaInode.toString(16)}`);
+ updateResultItem('groupQuotaInodeEXT', { bytes: results.groupQuotaInode, other: 'Inode 4 (Group Quota)' }, true,
+ `0x${Math.floor(results.inodeTableStart).toString(16).toUpperCase()} + (3 × 0x${Math.floor(values.inodeSizeEXT).toString(16).toUpperCase()}) = 0x${Math.floor(results.groupQuotaInode).toString(16).toUpperCase()}`);
// Inode 5 (Bootloader) - Index 4
results.bootloaderInode = results.inodeTableStart + (4 * values.inodeSizeEXT);
- updateResultItem('bootloaderInodeEXT', results.bootloaderInode, true,
- `0x${results.inodeTableStart.toString(16)} + (4 × 0x${values.inodeSizeEXT.toString(16)}) = 0x${results.bootloaderInode.toString(16)}`);
+ updateResultItem('bootloaderInodeEXT', { bytes: results.bootloaderInode, other: 'Inode 5 (Bootloader)' }, true,
+ `0x${Math.floor(results.inodeTableStart).toString(16).toUpperCase()} + (4 × 0x${Math.floor(values.inodeSizeEXT).toString(16).toUpperCase()}) = 0x${Math.floor(results.bootloaderInode).toString(16).toUpperCase()}`);
// Inode 8 (Journal) - Index 7
results.journalInode = results.inodeTableStart + (7 * values.inodeSizeEXT);
- updateResultItem('journalInodeEXT', results.journalInode, true,
- `0x${results.inodeTableStart.toString(16)} + (7 × 0x${values.inodeSizeEXT.toString(16)}) = 0x${results.journalInode.toString(16)}`);
+ updateResultItem('journalInodeEXT', { bytes: results.journalInode, other: 'Inode 8 (Journal)' }, true,
+ `0x${Math.floor(results.inodeTableStart).toString(16).toUpperCase()} + (7 × 0x${Math.floor(values.inodeSizeEXT).toString(16).toUpperCase()}) = 0x${Math.floor(results.journalInode).toString(16).toUpperCase()}`);
} else {
updateResultItem('badBlocksInodeEXT', 0, false);
updateResultItem('rootDirInodeEXT', 0, false);
@@ -240,8 +290,8 @@ export class EXTFilesystem extends BaseFilesystem {
// Inode Table Size (Bytes)
if (checkDependencies(['inodesPerGroupEXT', 'inodeSizeEXT'])) {
results.inodeTableSize = values.inodesPerGroupEXT * values.inodeSizeEXT;
- updateResultItem('inodeTableSizeEXT', results.inodeTableSize, true,
- `0x${values.inodesPerGroupEXT.toString(16)} × 0x${values.inodeSizeEXT.toString(16)} = 0x${results.inodeTableSize.toString(16)} (${results.inodeTableSize} Bytes)`);
+ updateResultItem('inodeTableSizeEXT', { bytes: results.inodeTableSize, other: `für 0x${Math.floor(values.inodesPerGroupEXT).toString(16).toUpperCase()} Inodes` }, true,
+ `0x${Math.floor(values.inodesPerGroupEXT).toString(16).toUpperCase()} × 0x${Math.floor(values.inodeSizeEXT).toString(16).toUpperCase()} = 0x${Math.floor(results.inodeTableSize).toString(16).toUpperCase()} (${Math.floor(results.inodeTableSize)} Bytes)`);
} else {
updateResultItem('inodeTableSizeEXT', 0, false);
}
@@ -254,5 +304,12 @@ export class EXTFilesystem extends BaseFilesystem {
} else {
updateResultItem('inodeTableSizeBlocksEXT', 0, false);
}
+ } catch (error) {
+ console.error('Fehler in EXT-Berechnung:', error);
+ // Mark all results as unavailable on error
+ updateResultItem('superblockPosEXT', 0, false);
+ updateResultItem('blockSizeBytesEXT', 0, false);
+ updateResultItem('totalBlockgroupsEXT', 0, false);
+ }
}
}
diff --git a/webroot/js/filesystems/fat.js b/webroot/js/filesystems/fat.js
index b0e6db8..19e5879 100644
--- a/webroot/js/filesystems/fat.js
+++ b/webroot/js/filesystems/fat.js
@@ -10,10 +10,42 @@ export class FAT12_16Filesystem extends BaseFilesystem {
if (variantId !== 'fat1216') return;
const values = this.getInputValues(variantId);
+
+ // Validate input ranges
+ const validation = this.validateFilesystemSpecific(variantId, values);
+ if (!validation.valid) {
+ console.warn('FAT12/16 validation errors:', validation.errors);
+ }
+
const results = {};
this.calculateFAT1216(values, results);
}
+
+ validateFilesystemSpecific(variantId, values) {
+ if (variantId !== 'fat1216') return { valid: true, errors: [] };
+
+ const errors = [];
+
+ // Validate cluster number doesn't exceed total clusters
+ if (values.clusterNumber1216 !== undefined && values.clusterNumber1216 < 2) {
+ errors.push('clusterNumber1216: Cluster-Nummer muss >= 2 sein');
+ }
+
+ // Validate partition size is greater than reserved + FAT areas
+ if (values.partitionSizeInSectors1216 !== undefined && values.reservedSektoren1216 !== undefined && values.numFATs1216 !== undefined && values.fatSizeSectors1216 !== undefined) {
+ const minPartitionSize = values.reservedSektoren1216 + (values.numFATs1216 * values.fatSizeSectors1216) + 1;
+ if (values.partitionSizeInSectors1216 < minPartitionSize) {
+ errors.push(`partitionSizeInSectors1216: ${values.partitionSizeInSectors1216} < minimum ${minPartitionSize}`);
+ }
+ }
+
+ return {
+ valid: errors.length === 0,
+ errors: errors
+ };
+ }
+
constructor() {
super('FAT12/16', [
{
@@ -201,10 +233,42 @@ export class FAT32Filesystem extends BaseFilesystem {
if (variantId !== 'fat32') return;
const values = this.getInputValues(variantId);
+
+ // Validate input ranges
+ const validation = this.validateFilesystemSpecific(variantId, values);
+ if (!validation.valid) {
+ console.warn('FAT32 validation errors:', validation.errors);
+ }
+
const results = {};
this.calculateFAT32(values, results);
}
+
+ validateFilesystemSpecific(variantId, values) {
+ if (variantId !== 'fat32') return { valid: true, errors: [] };
+
+ const errors = [];
+
+ // Validate cluster number doesn't exceed total clusters
+ if (values.clusterNumber32 !== undefined && values.clusterNumber32 < 2) {
+ errors.push('clusterNumber32: Cluster-Nummer muss >= 2 sein');
+ }
+
+ // Validate partition size is greater than reserved + FAT areas
+ if (values.partitionSizeInSectors32 !== undefined && values.reservedSectors32 !== undefined && values.numFATs32 !== undefined && values.fatSizeSectors32 !== undefined) {
+ const minPartitionSize = values.reservedSectors32 + (values.numFATs32 * values.fatSizeSectors32) + 1;
+ if (values.partitionSizeInSectors32 < minPartitionSize) {
+ errors.push(`partitionSizeInSectors32: ${values.partitionSizeInSectors32} < minimum ${minPartitionSize}`);
+ }
+ }
+
+ return {
+ valid: errors.length === 0,
+ errors: errors
+ };
+ }
+
constructor() {
super('FAT32', [
{
diff --git a/webroot/js/filesystems/ntfs.js b/webroot/js/filesystems/ntfs.js
index 5e1b959..25880a1 100644
--- a/webroot/js/filesystems/ntfs.js
+++ b/webroot/js/filesystems/ntfs.js
@@ -103,6 +103,13 @@ export class NTFSFilesystem extends BaseFilesystem {
calculate(variantId) {
const values = this.getInputValues(variantId);
+
+ // Validate input ranges
+ const validation = this.validateFilesystemSpecific(variantId, values);
+ if (!validation.valid) {
+ console.warn('NTFS validation errors:', validation.errors);
+ }
+
const results = {};
if (variantId === 'ntfs') {
@@ -110,6 +117,32 @@ export class NTFSFilesystem extends BaseFilesystem {
}
}
+ validateFilesystemSpecific(variantId, values) {
+ if (variantId !== 'ntfs') return { valid: true, errors: [] };
+
+ const errors = [];
+
+ // Validate cluster number is reasonable
+ if (values.clusterNumberNTFS !== undefined && values.partitionSizeSectorsNTFS !== undefined && values.sectorSizeNTFS !== undefined && values.clusterSizeSectorsNTFS !== undefined) {
+ const clusterSizeBytes = Math.pow(2, values.sectorSizeNTFS) * Math.pow(2, values.clusterSizeSectorsNTFS);
+ const partitionSizeBytes = values.partitionSizeSectorsNTFS * Math.pow(2, values.sectorSizeNTFS);
+ const maxClusters = partitionSizeBytes / clusterSizeBytes;
+ if (values.clusterNumberNTFS > maxClusters) {
+ errors.push(`clusterNumberNTFS: ${values.clusterNumberNTFS} > max ${Math.floor(maxClusters)}`);
+ }
+ }
+
+ // Validate MFT entry number is reasonable
+ if (values.mftEntryNumberNTFS !== undefined && values.mftEntryNumberNTFS < 0) {
+ errors.push('mftEntryNumberNTFS: MFT-Eintrag muss >= 0 sein');
+ }
+
+ return {
+ valid: errors.length === 0,
+ errors: errors
+ };
+ }
+
calculateMFTEntrySize(rawValue, clusterSizeBytes) {
// Special encoding from offset 0x40 and 0x44
// If positive (0x00-0x7F): size = value * cluster size
diff --git a/webroot/js/main.js b/webroot/js/main.js
index f12257a..ef65751 100644
--- a/webroot/js/main.js
+++ b/webroot/js/main.js
@@ -1,6 +1,6 @@
// Main application file that coordinates all modules
-import { setupTooltips, setupCopyButtons } from './utils.js';
+import { setupTooltips, setupCopyButtons, copyToClipboard } from './utils.js';
import { Calculator } from './calculator.js';
import { FAT12_16Filesystem, FAT32Filesystem } from './filesystems/fat.js';
import { NTFSFilesystem } from './filesystems/ntfs.js';
@@ -34,6 +34,19 @@ class FilesystemCalculator {
}
}
+ // Get active tab's filesystem for context-aware operations
+ getActiveFilesystem() {
+ return this.findFilesystemForTab(this.activeTab);
+ }
+
+ // Get active tab's variant
+ getActiveVariant() {
+ if (!this.activeTab) return null;
+ const filesystem = this.getActiveFilesystem();
+ if (!filesystem) return null;
+ return filesystem.variants.find(v => v.id === this.activeTab);
+ }
+
renderTabs() {
const tabsContainer = document.getElementById('filesystem-tabs');
const tabs = [];
@@ -93,10 +106,9 @@ class FilesystemCalculator {
// Initialize UX enhancements
initializeUXEnhancements();
+ // Bind copyToClipboard at module scope - no dynamic import needed
window.copyToClipboard = (elementId) => {
- import('./utils.js').then(module => {
- module.copyToClipboard(elementId);
- });
+ copyToClipboard(elementId);
};
}
diff --git a/webroot/js/utils.js b/webroot/js/utils.js
index 4b18e88..3cf26ea 100644
--- a/webroot/js/utils.js
+++ b/webroot/js/utils.js
@@ -186,12 +186,6 @@ export function setupTooltips() {
}
});
- document.addEventListener('mouseout', function (e) {
- if (e.target.classList.contains('result-label') || e.target.classList.contains('result-value')) {
- hideTooltip();
- }
- });
-
document.addEventListener('scroll', hideTooltip);
}