gistfile1.txt
                        
                             · 28 KiB · Text
                        
                    
                    
                      
                        Eredeti
                      
                      
                        
                          
                        
                    
                    
                
                
            <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="RAID Capacity Calculator - calculate storage efficiency for RAID 0, 1, 5, 6, 10 and ZFS configurations">
    <meta name="keywords" content="RAID calculator, storage calculator, ZFS calculator, disk array, RAID capacity">
    <meta name="author" content="Karel Wintersky & Deepseek">
    <!-- Favicon SVG -->
    <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'>
    <rect width='100' height='100' fill='%233367D6'/>
    <rect x='20' y='30' width='60' height='40' fill='%23fff' rx='5'/>
    <rect x='25' y='35' width='50' height='10' fill='%233367D6'/>
    <circle cx='40' cy='60' r='5' fill='%233367D6'/>
    <circle cx='60' cy='60' r='5' fill='%233367D6'/>
    <path d='M30 75 L70 75 L80 85 L20 85 Z' fill='%23fff'/>
    </svg>" type="image/svg+xml">
    <!-- Open Graph / Facebook -->
    <meta property="og:type" content="website">
    <meta property="og:url" content="https://blacktower.wintersky.ru/public/raid_calc.html">
    <meta property="og:title" content="RAID Capacity Calculator">
    <meta property="og:description" content="Calculate storage efficiency for various RAID configurations">
    <meta property="og:image" content="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1200 630'>
    <rect width='1200' height='630' fill='%233367D6'/>
    <rect x='100' y='150' width='1000' height='330' fill='%23fff' rx='20'/>
    <rect x='150' y='200' width='900' height='80' fill='%233367D6' rx='10'/>
    <text x='150' y='300' font-family='Arial' font-size='60' fill='%23000'>RAID 0: <tspan fill='%233367D6'>4×1TB = 4TB</tspan></text>
    <text x='150' y='380' font-family='Arial' font-size='60' fill='%23000'>RAID 5: <tspan fill='%233367D6'>4×1TB = 3TB</tspan></text>
    <text x='150' y='460' font-family='Arial' font-size='60' fill='%23000'>RAID 10: <tspan fill='%233367D6'>4×1TB = 2TB</tspan></text>
    <text x='600' y='550' font-family='Arial' font-size='40' fill='%23fff'>RAID Capacity Calculator</text>
    </svg>">
    <meta property="og:image:width" content="1200">
    <meta property="og:image:height" content="630">
    <!-- Twitter -->
    <meta name="twitter:card" content="summary_large_image">
    <meta name="twitter:title" content="RAID Capacity Calculator">
    <meta name="twitter:description" content="Professional tool for RAID storage calculations">
    <meta name="twitter:image" content="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1200 630'>
    <rect width='1200' height='630' fill='%233367D6'/>
    <rect x='100' y='150' width='1000' height='330' fill='%23fff' rx='20'/>
    <rect x='150' y='200' width='900' height='80' fill='%233367D6' rx='10'/>
    <text x='150' y='300' font-family='Arial' font-size='60' fill='%23000'>RAID 0: <tspan fill='%233367D6'>4×1TB = 4TB</tspan></text>
    <text x='150' y='380' font-family='Arial' font-size='60' fill='%23000'>RAID 5: <tspan fill='%233367D6'>4×1TB = 3TB</tspan></text>
    <text x='150' y='460' font-family='Arial' font-size='60' fill='%23000'>RAID 10: <tspan fill='%233367D6'>4×1TB = 2TB</tspan></text>
    <text x='600' y='550' font-family='Arial' font-size='40' fill='%23fff'>RAID Capacity Calculator</text>
    </svg>">
    <title>RAID Capacity Calculator</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 1000px;
            margin: 0 auto;
            padding: 20px;
            line-height: 1.6;
        }
        .language-switcher {
            text-align: right;
            margin-bottom: 20px;
        }
        .language-switcher button {
            background: none;
            border: 1px solid #ccc;
            padding: 5px 10px;
            cursor: pointer;
            margin-left: 5px;
        }
        .language-switcher button.active {
            background: #eee;
        }
        .calculator-container {
            display: flex;
            gap: 20px;
            margin-bottom: 30px;
        }
        .input-section {
            flex: 1;
            padding: 15px;
            border: 1px solid #ddd;
            border-radius: 5px;
        }
        .results-section {
            flex: 1;
            padding: 15px;
            border: 1px solid #ddd;
            border-radius: 5px;
            background-color: #f5f5f5;
        }
        .input-group {
            margin-bottom: 15px;
        }
        label {
            display: block;
            margin-bottom: 5px;
            font-weight: bold;
        }
        input, select {
            padding: 8px;
            width: 100%;
            box-sizing: border-box;
        }
        .result-item {
            margin-bottom: 10px;
            padding: 10px;
            background-color: white;
            border-radius: 3px;
        }
        .result-label {
            font-weight: bold;
            color: #555;
        }
        .hint-section {
            padding: 20px;
            border: 1px solid #ddd;
            border-radius: 5px;
            background-color: #f9f9f9;
            margin-top: 20px;
        }
        .hint-title {
            font-weight: bold;
            margin-bottom: 10px;
            color: #333;
        }
        .hint-content {
            line-height: 1.5;
        }
        /* */
        .language-switcher {
        text-align: right;
        margin-bottom: 20px;
    }
    .language-switcher button {
        background: none;
        border: 1px solid #ccc;
        padding: 5px 10px;
        cursor: pointer;
        margin-left: 5px;
        display: inline-flex;
        align-items: center;
        gap: 5px;
    }
    .language-switcher button:hover {
        background-color: #f0f0f0;
    }
    .language-switcher button.active {
        background: #eee;
        font-weight: bold;
    }
    .language-switcher svg {
        border: 1px solid #ddd;
    }
    </style>
</head>
<body>
    <!-- 
    <div class="language-switcher">
        <button id="lang-en">English</button>
        <button id="lang-ru">Русский</button>
    </div>
-->
    <div class="language-switcher">
    <button id="lang-en" title="English">
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 30" width="20" height="10">
            <clipPath id="s">
                <path d="M0,0 v30 h60 v-30 z"/>
            </clipPath>
            <clipPath id="t">
                <path d="M30,15 h30 v15 z v15 h-30 z h-30 v-15 z v-15 h30 z"/>
            </clipPath>
            <g clip-path="url(#s)">
                <path d="M0,0 v30 h60 v-30 z" fill="#012169"/>
                <path d="M0,0 L60,30 M60,0 L0,30" stroke="#fff" stroke-width="6"/>
                <path d="M0,0 L60,30 M60,0 L0,30" stroke="#C8102E" stroke-width="4"/>
                <path d="M30,0 v30 M0,15 h60" stroke="#fff" stroke-width="10"/>
                <path d="M30,0 v30 M0,15 h60" stroke="#C8102E" stroke-width="6"/>
            </g>
            <g clip-path="url(#t)">
                <path d="M0,0 v30 h60 v-30 z" fill="#012169"/>
                <path d="M0,0 L60,30 M60,0 L0,30" stroke="#fff" stroke-width="6"/>
                <path d="M0,0 L60,30 M60,0 L0,30" stroke="#C8102E" stroke-width="4"/>
                <path d="M30,0 v30 M0,15 h60" stroke="#fff" stroke-width="10"/>
                <path d="M30,0 v30 M0,15 h60" stroke="#C8102E" stroke-width="6"/>
            </g>
        </svg>
        <span>English</span>
    </button>
    <button id="lang-ru" title="Русский">
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 30" width="20" height="10">
            <clipPath id="r">
                <path d="M0,0 v30 h60 v-30 z"/>
            </clipPath>
            <g clip-path="url(#r)">
                <path d="M0,0 h60 v10 h-60 z" fill="#fff"/>
                <path d="M0,10 h60 v10 h-60 z" fill="#0039A6"/>
                <path d="M0,20 h60 v10 h-60 z" fill="#D52B1E"/>
            </g>
        </svg>
        <span>Русский</span>
    </button>
</div>
    <h1 id="title">RAID Capacity Calculator</h1>
    
    <div class="calculator-container">
        <div class="input-section">
            <div class="input-group">
                <label for="disk-count" id="disk-count-label">Number of disks:</label>
                <input type="number" id="disk-count" min="1" value="4">
            </div>
            
            <div class="input-group">
                <label for="disk-size" id="disk-size-label">Disk size (GB):</label>
                <input type="number" id="disk-size" min="1" value="1000">
            </div>
            
            <div class="input-group">
                <label for="raid-type" id="raid-type-label">RAID type:</label>
                <select id="raid-type">
                    <optgroup label="Standard RAID">
                        <option value="raid0">RAID 0</option>
                        <option value="raid1">RAID 1</option>
                        <option value="raid3">RAID 3</option>
                        <option value="raid5">RAID 5</option>
                        <option value="raid6">RAID 6</option>
                        <option value="raid10">RAID 10</option>
                    </optgroup>
                    <optgroup label="ZFS">
                        <option value="zfsStripe">Stripe set</option>
                        <option value="zfsMirror">Mirror</option>
                        <option value="zfsZ1">RAID-Z1</option>
                        <option value="zfsZ2">RAID-Z2</option>
                        <option value="zfsZ3">RAID-Z3</option>
                    </optgroup>
                </select>
            </div>
        </div>
        
        <div class="results-section">
            <h2 id="results-title">Results</h2>
            <div class="result-item">
                <div class="result-label" id="total-space-label">Total space:</div>
                <div id="total-space">0</div>
            </div>
            <div class="result-item">
                <div class="result-label" id="effective-space-label">Effective space:</div>
                <div id="effective-space">0</div>
            </div>
            <div class="result-item">
                <div class="result-label" id="efficiency-label">Efficiency:</div>
                <div id="efficiency">0</div>
            </div>
            <div class="result-item">
                <div class="result-label" id="fault-tolerance-label">Fault tolerance:</div>
                <div id="fault-tolerance">None</div>
            </div>
        </div>
    </div>
    
    <div class="hint-section">
        <div class="hint-title" id="hint-title">About selected RAID type:</div>
        <div class="hint-content" id="hint-content"></div>
    </div>
    <script>
        // Language data
        const translations = {
            en: {
                title: "RAID Capacity Calculator",
                diskCountLabel: "Number of disks:",
                diskSizeLabel: "Disk size (GB):",
                raidTypeLabel: "RAID type:",
                resultsTitle: "Results",
                totalSpaceLabel: "Total space:",
                effectiveSpaceLabel: "Effective space:",
                efficiencyLabel: "Efficiency:",
                faultToleranceLabel: "Fault tolerance:",
                hintTitle: "About selected RAID type:",
                raidTypes: {
                    standard: "Standard RAID",
                    zfs: "ZFS",
                    raid0: "RAID 0",
                    raid1: "RAID 1",
                    raid3: "RAID 3",
                    raid5: "RAID 5",
                    raid6: "RAID 6",
                    raid10: "RAID 10",
                    zfsStripe: "Stripe set",
                    zfsMirror: "Mirror",
                    zfsZ1: "RAID-Z1",
                    zfsZ2: "RAID-Z2",
                    zfsZ3: "RAID-Z3"
                },
                faultTolerance: {
                    none: "None",
                    one: "1 disk failure",
                    two: "2 disk failures",
                    three: "3 disk failures",
                    half: "Up to half of disks (in mirrored pairs)"
                },
                hints: {
                    raid0: "RAID 0 (striping) combines multiple disks into one logical unit with data distributed across all disks. Provides improved performance but no redundancy - failure of any disk results in total data loss.",
                    raid1: "RAID 1 (mirroring) creates an exact copy of data on two or more disks. Provides fault tolerance but at the cost of reduced storage capacity.",
                    raid3: "RAID 3 uses byte-level striping with a dedicated parity disk. Good for sequential data access but poor for random access. Can tolerate one disk failure.",
                    raid5: "RAID 5 uses block-level striping with distributed parity. Provides good balance between performance, capacity and fault tolerance. Can tolerate one disk failure.",
                    raid6: "RAID 6 is similar to RAID 5 but with double distributed parity. Can tolerate two disk failures, making it more reliable for large arrays.",
                    raid10: "RAID 10 (1+0) combines mirroring and striping. Requires even number of disks (minimum 4). Provides good performance and can tolerate multiple disk failures (one per mirrored pair).",
                    zfsStripe: "ZFS stripe is similar to RAID 0 - no redundancy but maximum capacity and performance. Data is distributed across all disks in the pool.",
                    zfsMirror: "ZFS mirror is similar to RAID 1 - data is duplicated on two or more disks. Provides redundancy at the cost of storage capacity.",
                    zfsZ1: "RAID-Z1 is similar to RAID 5 - single parity with distributed striping. Can tolerate one disk failure while providing good capacity and performance.",
                    zfsZ2: "RAID-Z2 is similar to RAID 6 - double parity with distributed striping. Can tolerate two disk failures, recommended for larger pools.",
                    zfsZ3: "RAID-Z3 provides triple parity protection, able to withstand three disk failures. Recommended for very large pools where rebuild times are long."
                }
            },
            ru: {
                title: "Калькулятор ёмкости RAID",
                diskCountLabel: "Число дисков:",
                diskSizeLabel: "Размер диска (ГБ):",
                raidTypeLabel: "Тип RAID:",
                resultsTitle: "Результаты",
                totalSpaceLabel: "Общий объем:",
                effectiveSpaceLabel: "Эффективный объем:",
                efficiencyLabel: "Эффективность использования:",
                faultToleranceLabel: "Отказоустойчивость:",
                hintTitle: "О выбранном типе RAID:",
                raidTypes: {
                    standard: "Обычный RAID",
                    zfs: "ZFS",
                    raid0: "RAID 0",
                    raid1: "RAID 1",
                    raid3: "RAID 3",
                    raid5: "RAID 5",
                    raid6: "RAID 6",
                    raid10: "RAID 10",
                    zfsStripe: "Stripe set",
                    zfsMirror: "Mirror",
                    zfsZ1: "RAID-Z1",
                    zfsZ2: "RAID-Z2",
                    zfsZ3: "RAID-Z3"
                },
                faultTolerance: {
                    none: "Нет",
                    one: "1 отказ диска",
                    two: "2 отказа диска",
                    three: "3 отказа диска",
                    half: "До половины дисков (в зеркальных парах)"
                },
                hints: {
                    raid0: "RAID 0 (чередование) объединяет несколько дисков в один логический блок с распределением данных по всем дискам. Обеспечивает повышенную производительность, но не имеет избыточности - отказ любого диска приводит к полной потере данных.",
                    raid1: "RAID 1 (зеркалирование) создает точную копию данных на двух или более дисках. Обеспечивает отказоустойчивость, но за счет уменьшения полезного объема хранилища.",
                    raid3: "RAID 3 использует чередование на уровне байтов с выделенным диском четности. Хорошо подходит для последовательного доступа к данным, но плохо для случайного. Может выдержать отказ одного диска.",
                    raid5: "RAID 5 использует чередование на уровне блоков с распределенной четностью. Обеспечивает хороший баланс между производительностью, емкостью и отказоустойчивостью. Может выдержать отказ одного диска.",
                    raid6: "RAID 6 аналогичен RAID 5, но с двойной распределенной четностью. Может выдержать отказ двух дисков, что делает его более надежным для больших массивов.",
                    raid10: "RAID 10 (1+0) сочетает зеркалирование и чередование. Требует четного количества дисков (минимум 4). Обеспечивает хорошую производительность и может выдержать отказ нескольких дисков (по одному в каждой зеркальной паре).",
                    zfsStripe: "ZFS stripe аналогичен RAID 0 - нет избыточности, но максимальная емкость и производительность. Данные распределяются по всем дискам в пуле.",
                    zfsMirror: "ZFS mirror аналогичен RAID 1 - данные дублируются на двух или более дисках. Обеспечивает избыточность за счет полезного объема хранилища.",
                    zfsZ1: "RAID-Z1 аналогичен RAID 5 - одинарная четность с распределенным чередованием. Может выдержать отказ одного диска при хорошей емкости и производительности.",
                    zfsZ2: "RAID-Z2 аналогичен RAID 6 - двойная четность с распределенным чередованием. Может выдержать отказ двух дисков, рекомендуется для больших пулов.",
                    zfsZ3: "RAID-Z3 обеспечивает тройную защиту четностью, может выдержать отказ трех дисков. Рекомендуется для очень больших пулов, где время восстановления велико."
                }
            }
        };
        // Current language
        let currentLang = 'en';
        // DOM elements
        const elements = {
            title: document.getElementById('title'),
            diskCountLabel: document.getElementById('disk-count-label'),
            diskSizeLabel: document.getElementById('disk-size-label'),
            raidTypeLabel: document.getElementById('raid-type-label'),
            resultsTitle: document.getElementById('results-title'),
            totalSpaceLabel: document.getElementById('total-space-label'),
            effectiveSpaceLabel: document.getElementById('effective-space-label'),
            efficiencyLabel: document.getElementById('efficiency-label'),
            faultToleranceLabel: document.getElementById('fault-tolerance-label'),
            hintTitle: document.getElementById('hint-title'),
            hintContent: document.getElementById('hint-content'),
            raidTypeSelect: document.getElementById('raid-type'),
            diskCountInput: document.getElementById('disk-count'),
            diskSizeInput: document.getElementById('disk-size'),
            raidTypeOptgroups: document.querySelectorAll('#raid-type optgroup'),
            raidTypeOptions: document.querySelectorAll('#raid-type option')
        };
        // Save state to URL hash
         function saveStateToHash() {
            const params = new URLSearchParams();
            params.set('lang', currentLang);
            params.set('count', elements.diskCountInput.value);
            params.set('size', elements.diskSizeInput.value);
            params.set('raid', elements.raidTypeSelect.value);
            
            window.location.hash = params.toString();
        }
        // Load state from URL hash
        function loadStateFromHash() {
            if (window.location.hash) {
                try {
                    const hash = window.location.hash.substring(1);
                    const params = new URLSearchParams(hash);
                    
                    if (params.has('lang') && translations[params.get('lang')]) {
                        currentLang = params.get('lang');
                    }
                    if (params.has('count')) {
                        elements.diskCountInput.value = params.get('count');
                    }
                    if (params.has('size')) {
                        elements.diskSizeInput.value = params.get('size');
                    }
                    if (params.has('raid')) {
                        elements.raidTypeSelect.value = params.get('raid');
                    }
                    
                    return true;
                } catch (e) {
                    console.error("Error parsing hash:", e);
                }
            }
            return false;
        }
        // Update UI language
        function updateLanguage(lang) {
            currentLang = lang;
            const t = translations[lang];
            
            // Update labels
            elements.title.textContent = t.title;
            elements.diskCountLabel.textContent = t.diskCountLabel;
            elements.diskSizeLabel.textContent = t.diskSizeLabel;
            elements.raidTypeLabel.textContent = t.raidTypeLabel;
            elements.resultsTitle.textContent = t.resultsTitle;
            elements.totalSpaceLabel.textContent = t.totalSpaceLabel;
            elements.effectiveSpaceLabel.textContent = t.effectiveSpaceLabel;
            elements.efficiencyLabel.textContent = t.efficiencyLabel;
            elements.faultToleranceLabel.textContent = t.faultToleranceLabel;
            elements.hintTitle.textContent = t.hintTitle;
            
            // Update RAID type options
            elements.raidTypeOptgroups[0].label = t.raidTypes.standard;
            elements.raidTypeOptgroups[1].label = t.raidTypes.zfs;
            
            const raidTypeOptions = [
                t.raidTypes.raid0, t.raidTypes.raid1, t.raidTypes.raid3, 
                t.raidTypes.raid5, t.raidTypes.raid6, t.raidTypes.raid10,
                t.raidTypes.zfsStripe, t.raidTypes.zfsMirror, 
                t.raidTypes.zfsZ1, t.raidTypes.zfsZ2, t.raidTypes.zfsZ3
            ];
            
            elements.raidTypeOptions.forEach((option, index) => {
                option.textContent = raidTypeOptions[index];
            });
            
            // Recalculate to update fault tolerance text and hint
            calculate();
        }
        // Update hint based on selected RAID type
        function updateHint(raidType) {
            elements.hintContent.textContent = translations[currentLang].hints[raidType] || '';
        }
        // Calculate RAID parameters
        function calculate() {
            const diskCount = parseInt(elements.diskCountInput.value) || 0;
            const diskSize = parseInt(elements.diskSizeInput.value) || 0;
            const raidType = elements.raidTypeSelect.value;
            
            let totalSpace, effectiveSpace, efficiency, faultTolerance;
            const t = translations[currentLang].faultTolerance;
            
            switch(raidType) {
                case 'raid0':
                case 'zfsStripe':
                    totalSpace = diskCount * diskSize;
                    effectiveSpace = totalSpace;
                    efficiency = 100;
                    faultTolerance = t.none;
                    break;
                    
                case 'raid1':
                case 'zfsMirror':
                    totalSpace = diskCount * diskSize;
                    effectiveSpace = diskSize;
                    efficiency = (100 / diskCount).toFixed(2);
                    faultTolerance = diskCount > 2 ? t.half : t.one;
                    break;
                    
                case 'raid3':
                case 'raid5':
                case 'zfsZ1':
                    totalSpace = diskCount * diskSize;
                    effectiveSpace = (diskCount - 1) * diskSize;
                    efficiency = ((diskCount - 1) / diskCount * 100).toFixed(2);
                    faultTolerance = t.one;
                    break;
                    
                case 'raid6':
                case 'zfsZ2':
                    totalSpace = diskCount * diskSize;
                    effectiveSpace = (diskCount - 2) * diskSize;
                    efficiency = ((diskCount - 2) / diskCount * 100).toFixed(2);
                    faultTolerance = t.two;
                    break;
                    
                case 'zfsZ3':
                    totalSpace = diskCount * diskSize;
                    effectiveSpace = (diskCount - 3) * diskSize;
                    efficiency = ((diskCount - 3) / diskCount * 100).toFixed(2);
                    faultTolerance = t.three;
                    break;
                    
                case 'raid10':
                    if (diskCount < 2 || diskCount % 2 !== 0) {
                        effectiveSpace = 0;
                        efficiency = 0;
                        faultTolerance = t.none;
                    } else {
                        totalSpace = diskCount * diskSize;
                        effectiveSpace = (diskCount / 2) * diskSize;
                        efficiency = 50;
                        faultTolerance = t.half;
                    }
                    break;
                    
                default:
                    totalSpace = effectiveSpace = efficiency = 0;
                    faultTolerance = t.none;
            }
            
            // Update results
            document.getElementById('total-space').textContent = totalSpace.toLocaleString() + ' Gb';
            document.getElementById('effective-space').textContent = effectiveSpace.toLocaleString() + ' Gb';
            document.getElementById('efficiency').textContent = efficiency + ' %';
            document.getElementById('fault-tolerance').textContent = faultTolerance;
            
            // Update hint
            updateHint(raidType);
            
            // Save state
            saveStateToHash();
        }
        // Event listeners
        function setupEventListeners() {
            elements.diskCountInput.addEventListener('input', calculate);
            elements.diskSizeInput.addEventListener('input', calculate);
            elements.raidTypeSelect.addEventListener('change', calculate);
            
            document.getElementById('lang-en').addEventListener('click', () => {
                currentLang = 'en';
                updateLanguage('en');
                saveStateToHash();
            });
            document.getElementById('lang-ru').addEventListener('click', () => {
                currentLang = 'ru';
                updateLanguage('ru');
                saveStateToHash();
            });
        }
        // Initialize
        function init() {
            // Try to load state from hash
            const stateLoaded = loadStateFromHash();
            
            // Setup event listeners
            setupEventListeners();
            
            // Update UI with current or default values
            updateLanguage(currentLang);
            
            // If no state was loaded, calculate with defaults
            if (!stateLoaded) {
                calculate();
            }
        }
        // Start the application
        init();
    </script>
</body>
</html>
                | 1 | <!DOCTYPE html> | 
| 2 | <html lang="en"> | 
| 3 | <head> | 
| 4 | <meta charset="UTF-8"> | 
| 5 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> | 
| 6 | <meta name="description" content="RAID Capacity Calculator - calculate storage efficiency for RAID 0, 1, 5, 6, 10 and ZFS configurations"> | 
| 7 | <meta name="keywords" content="RAID calculator, storage calculator, ZFS calculator, disk array, RAID capacity"> | 
| 8 | <meta name="author" content="Karel Wintersky & Deepseek"> | 
| 9 | |
| 10 | <!-- Favicon SVG --> | 
| 11 | <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'> | 
| 12 | <rect width='100' height='100' fill='%233367D6'/> | 
| 13 | <rect x='20' y='30' width='60' height='40' fill='%23fff' rx='5'/> | 
| 14 | <rect x='25' y='35' width='50' height='10' fill='%233367D6'/> | 
| 15 | <circle cx='40' cy='60' r='5' fill='%233367D6'/> | 
| 16 | <circle cx='60' cy='60' r='5' fill='%233367D6'/> | 
| 17 | <path d='M30 75 L70 75 L80 85 L20 85 Z' fill='%23fff'/> | 
| 18 | </svg>" type="image/svg+xml"> | 
| 19 | |
| 20 | <!-- Open Graph / Facebook --> | 
| 21 | <meta property="og:type" content="website"> | 
| 22 | <meta property="og:url" content="https://blacktower.wintersky.ru/public/raid_calc.html"> | 
| 23 | <meta property="og:title" content="RAID Capacity Calculator"> | 
| 24 | <meta property="og:description" content="Calculate storage efficiency for various RAID configurations"> | 
| 25 | <meta property="og:image" content="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1200 630'> | 
| 26 | <rect width='1200' height='630' fill='%233367D6'/> | 
| 27 | <rect x='100' y='150' width='1000' height='330' fill='%23fff' rx='20'/> | 
| 28 | <rect x='150' y='200' width='900' height='80' fill='%233367D6' rx='10'/> | 
| 29 | <text x='150' y='300' font-family='Arial' font-size='60' fill='%23000'>RAID 0: <tspan fill='%233367D6'>4×1TB = 4TB</tspan></text> | 
| 30 | <text x='150' y='380' font-family='Arial' font-size='60' fill='%23000'>RAID 5: <tspan fill='%233367D6'>4×1TB = 3TB</tspan></text> | 
| 31 | <text x='150' y='460' font-family='Arial' font-size='60' fill='%23000'>RAID 10: <tspan fill='%233367D6'>4×1TB = 2TB</tspan></text> | 
| 32 | <text x='600' y='550' font-family='Arial' font-size='40' fill='%23fff'>RAID Capacity Calculator</text> | 
| 33 | </svg>"> | 
| 34 | <meta property="og:image:width" content="1200"> | 
| 35 | <meta property="og:image:height" content="630"> | 
| 36 | |
| 37 | <!-- Twitter --> | 
| 38 | <meta name="twitter:card" content="summary_large_image"> | 
| 39 | <meta name="twitter:title" content="RAID Capacity Calculator"> | 
| 40 | <meta name="twitter:description" content="Professional tool for RAID storage calculations"> | 
| 41 | <meta name="twitter:image" content="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1200 630'> | 
| 42 | <rect width='1200' height='630' fill='%233367D6'/> | 
| 43 | <rect x='100' y='150' width='1000' height='330' fill='%23fff' rx='20'/> | 
| 44 | <rect x='150' y='200' width='900' height='80' fill='%233367D6' rx='10'/> | 
| 45 | <text x='150' y='300' font-family='Arial' font-size='60' fill='%23000'>RAID 0: <tspan fill='%233367D6'>4×1TB = 4TB</tspan></text> | 
| 46 | <text x='150' y='380' font-family='Arial' font-size='60' fill='%23000'>RAID 5: <tspan fill='%233367D6'>4×1TB = 3TB</tspan></text> | 
| 47 | <text x='150' y='460' font-family='Arial' font-size='60' fill='%23000'>RAID 10: <tspan fill='%233367D6'>4×1TB = 2TB</tspan></text> | 
| 48 | <text x='600' y='550' font-family='Arial' font-size='40' fill='%23fff'>RAID Capacity Calculator</text> | 
| 49 | </svg>"> | 
| 50 | |
| 51 | <title>RAID Capacity Calculator</title> | 
| 52 | <style> | 
| 53 | body { | 
| 54 | font-family: Arial, sans-serif; | 
| 55 | max-width: 1000px; | 
| 56 | margin: 0 auto; | 
| 57 | padding: 20px; | 
| 58 | line-height: 1.6; | 
| 59 | } | 
| 60 | .language-switcher { | 
| 61 | text-align: right; | 
| 62 | margin-bottom: 20px; | 
| 63 | } | 
| 64 | .language-switcher button { | 
| 65 | background: none; | 
| 66 | border: 1px solid #ccc; | 
| 67 | padding: 5px 10px; | 
| 68 | cursor: pointer; | 
| 69 | margin-left: 5px; | 
| 70 | } | 
| 71 | .language-switcher button.active { | 
| 72 | background: #eee; | 
| 73 | } | 
| 74 | .calculator-container { | 
| 75 | display: flex; | 
| 76 | gap: 20px; | 
| 77 | margin-bottom: 30px; | 
| 78 | } | 
| 79 | .input-section { | 
| 80 | flex: 1; | 
| 81 | padding: 15px; | 
| 82 | border: 1px solid #ddd; | 
| 83 | border-radius: 5px; | 
| 84 | } | 
| 85 | .results-section { | 
| 86 | flex: 1; | 
| 87 | padding: 15px; | 
| 88 | border: 1px solid #ddd; | 
| 89 | border-radius: 5px; | 
| 90 | background-color: #f5f5f5; | 
| 91 | } | 
| 92 | .input-group { | 
| 93 | margin-bottom: 15px; | 
| 94 | } | 
| 95 | label { | 
| 96 | display: block; | 
| 97 | margin-bottom: 5px; | 
| 98 | font-weight: bold; | 
| 99 | } | 
| 100 | input, select { | 
| 101 | padding: 8px; | 
| 102 | width: 100%; | 
| 103 | box-sizing: border-box; | 
| 104 | } | 
| 105 | .result-item { | 
| 106 | margin-bottom: 10px; | 
| 107 | padding: 10px; | 
| 108 | background-color: white; | 
| 109 | border-radius: 3px; | 
| 110 | } | 
| 111 | .result-label { | 
| 112 | font-weight: bold; | 
| 113 | color: #555; | 
| 114 | } | 
| 115 | .hint-section { | 
| 116 | padding: 20px; | 
| 117 | border: 1px solid #ddd; | 
| 118 | border-radius: 5px; | 
| 119 | background-color: #f9f9f9; | 
| 120 | margin-top: 20px; | 
| 121 | } | 
| 122 | .hint-title { | 
| 123 | font-weight: bold; | 
| 124 | margin-bottom: 10px; | 
| 125 | color: #333; | 
| 126 | } | 
| 127 | .hint-content { | 
| 128 | line-height: 1.5; | 
| 129 | } | 
| 130 | |
| 131 | /* */ | 
| 132 | .language-switcher { | 
| 133 | text-align: right; | 
| 134 | margin-bottom: 20px; | 
| 135 | } | 
| 136 | .language-switcher button { | 
| 137 | background: none; | 
| 138 | border: 1px solid #ccc; | 
| 139 | padding: 5px 10px; | 
| 140 | cursor: pointer; | 
| 141 | margin-left: 5px; | 
| 142 | display: inline-flex; | 
| 143 | align-items: center; | 
| 144 | gap: 5px; | 
| 145 | } | 
| 146 | .language-switcher button:hover { | 
| 147 | background-color: #f0f0f0; | 
| 148 | } | 
| 149 | .language-switcher button.active { | 
| 150 | background: #eee; | 
| 151 | font-weight: bold; | 
| 152 | } | 
| 153 | .language-switcher svg { | 
| 154 | border: 1px solid #ddd; | 
| 155 | } | 
| 156 | |
| 157 | </style> | 
| 158 | </head> | 
| 159 | <body> | 
| 160 | <!-- | 
| 161 | <div class="language-switcher"> | 
| 162 | <button id="lang-en">English</button> | 
| 163 | <button id="lang-ru">Русский</button> | 
| 164 | </div> | 
| 165 | --> | 
| 166 | <div class="language-switcher"> | 
| 167 | <button id="lang-en" title="English"> | 
| 168 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 30" width="20" height="10"> | 
| 169 | <clipPath id="s"> | 
| 170 | <path d="M0,0 v30 h60 v-30 z"/> | 
| 171 | </clipPath> | 
| 172 | <clipPath id="t"> | 
| 173 | <path d="M30,15 h30 v15 z v15 h-30 z h-30 v-15 z v-15 h30 z"/> | 
| 174 | </clipPath> | 
| 175 | <g clip-path="url(#s)"> | 
| 176 | <path d="M0,0 v30 h60 v-30 z" fill="#012169"/> | 
| 177 | <path d="M0,0 L60,30 M60,0 L0,30" stroke="#fff" stroke-width="6"/> | 
| 178 | <path d="M0,0 L60,30 M60,0 L0,30" stroke="#C8102E" stroke-width="4"/> | 
| 179 | <path d="M30,0 v30 M0,15 h60" stroke="#fff" stroke-width="10"/> | 
| 180 | <path d="M30,0 v30 M0,15 h60" stroke="#C8102E" stroke-width="6"/> | 
| 181 | </g> | 
| 182 | <g clip-path="url(#t)"> | 
| 183 | <path d="M0,0 v30 h60 v-30 z" fill="#012169"/> | 
| 184 | <path d="M0,0 L60,30 M60,0 L0,30" stroke="#fff" stroke-width="6"/> | 
| 185 | <path d="M0,0 L60,30 M60,0 L0,30" stroke="#C8102E" stroke-width="4"/> | 
| 186 | <path d="M30,0 v30 M0,15 h60" stroke="#fff" stroke-width="10"/> | 
| 187 | <path d="M30,0 v30 M0,15 h60" stroke="#C8102E" stroke-width="6"/> | 
| 188 | </g> | 
| 189 | </svg> | 
| 190 | <span>English</span> | 
| 191 | </button> | 
| 192 | <button id="lang-ru" title="Русский"> | 
| 193 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 30" width="20" height="10"> | 
| 194 | <clipPath id="r"> | 
| 195 | <path d="M0,0 v30 h60 v-30 z"/> | 
| 196 | </clipPath> | 
| 197 | <g clip-path="url(#r)"> | 
| 198 | <path d="M0,0 h60 v10 h-60 z" fill="#fff"/> | 
| 199 | <path d="M0,10 h60 v10 h-60 z" fill="#0039A6"/> | 
| 200 | <path d="M0,20 h60 v10 h-60 z" fill="#D52B1E"/> | 
| 201 | </g> | 
| 202 | </svg> | 
| 203 | <span>Русский</span> | 
| 204 | </button> | 
| 205 | </div> | 
| 206 | |
| 207 | <h1 id="title">RAID Capacity Calculator</h1> | 
| 208 | |
| 209 | <div class="calculator-container"> | 
| 210 | <div class="input-section"> | 
| 211 | <div class="input-group"> | 
| 212 | <label for="disk-count" id="disk-count-label">Number of disks:</label> | 
| 213 | <input type="number" id="disk-count" min="1" value="4"> | 
| 214 | </div> | 
| 215 | |
| 216 | <div class="input-group"> | 
| 217 | <label for="disk-size" id="disk-size-label">Disk size (GB):</label> | 
| 218 | <input type="number" id="disk-size" min="1" value="1000"> | 
| 219 | </div> | 
| 220 | |
| 221 | <div class="input-group"> | 
| 222 | <label for="raid-type" id="raid-type-label">RAID type:</label> | 
| 223 | <select id="raid-type"> | 
| 224 | <optgroup label="Standard RAID"> | 
| 225 | <option value="raid0">RAID 0</option> | 
| 226 | <option value="raid1">RAID 1</option> | 
| 227 | <option value="raid3">RAID 3</option> | 
| 228 | <option value="raid5">RAID 5</option> | 
| 229 | <option value="raid6">RAID 6</option> | 
| 230 | <option value="raid10">RAID 10</option> | 
| 231 | </optgroup> | 
| 232 | <optgroup label="ZFS"> | 
| 233 | <option value="zfsStripe">Stripe set</option> | 
| 234 | <option value="zfsMirror">Mirror</option> | 
| 235 | <option value="zfsZ1">RAID-Z1</option> | 
| 236 | <option value="zfsZ2">RAID-Z2</option> | 
| 237 | <option value="zfsZ3">RAID-Z3</option> | 
| 238 | </optgroup> | 
| 239 | </select> | 
| 240 | </div> | 
| 241 | </div> | 
| 242 | |
| 243 | <div class="results-section"> | 
| 244 | <h2 id="results-title">Results</h2> | 
| 245 | <div class="result-item"> | 
| 246 | <div class="result-label" id="total-space-label">Total space:</div> | 
| 247 | <div id="total-space">0</div> | 
| 248 | </div> | 
| 249 | <div class="result-item"> | 
| 250 | <div class="result-label" id="effective-space-label">Effective space:</div> | 
| 251 | <div id="effective-space">0</div> | 
| 252 | </div> | 
| 253 | <div class="result-item"> | 
| 254 | <div class="result-label" id="efficiency-label">Efficiency:</div> | 
| 255 | <div id="efficiency">0</div> | 
| 256 | </div> | 
| 257 | <div class="result-item"> | 
| 258 | <div class="result-label" id="fault-tolerance-label">Fault tolerance:</div> | 
| 259 | <div id="fault-tolerance">None</div> | 
| 260 | </div> | 
| 261 | </div> | 
| 262 | </div> | 
| 263 | |
| 264 | <div class="hint-section"> | 
| 265 | <div class="hint-title" id="hint-title">About selected RAID type:</div> | 
| 266 | <div class="hint-content" id="hint-content"></div> | 
| 267 | </div> | 
| 268 | |
| 269 | <script> | 
| 270 | // Language data | 
| 271 | const translations = { | 
| 272 | en: { | 
| 273 | title: "RAID Capacity Calculator", | 
| 274 | diskCountLabel: "Number of disks:", | 
| 275 | diskSizeLabel: "Disk size (GB):", | 
| 276 | raidTypeLabel: "RAID type:", | 
| 277 | resultsTitle: "Results", | 
| 278 | totalSpaceLabel: "Total space:", | 
| 279 | effectiveSpaceLabel: "Effective space:", | 
| 280 | efficiencyLabel: "Efficiency:", | 
| 281 | faultToleranceLabel: "Fault tolerance:", | 
| 282 | hintTitle: "About selected RAID type:", | 
| 283 | raidTypes: { | 
| 284 | standard: "Standard RAID", | 
| 285 | zfs: "ZFS", | 
| 286 | raid0: "RAID 0", | 
| 287 | raid1: "RAID 1", | 
| 288 | raid3: "RAID 3", | 
| 289 | raid5: "RAID 5", | 
| 290 | raid6: "RAID 6", | 
| 291 | raid10: "RAID 10", | 
| 292 | zfsStripe: "Stripe set", | 
| 293 | zfsMirror: "Mirror", | 
| 294 | zfsZ1: "RAID-Z1", | 
| 295 | zfsZ2: "RAID-Z2", | 
| 296 | zfsZ3: "RAID-Z3" | 
| 297 | }, | 
| 298 | faultTolerance: { | 
| 299 | none: "None", | 
| 300 | one: "1 disk failure", | 
| 301 | two: "2 disk failures", | 
| 302 | three: "3 disk failures", | 
| 303 | half: "Up to half of disks (in mirrored pairs)" | 
| 304 | }, | 
| 305 | hints: { | 
| 306 | raid0: "RAID 0 (striping) combines multiple disks into one logical unit with data distributed across all disks. Provides improved performance but no redundancy - failure of any disk results in total data loss.", | 
| 307 | raid1: "RAID 1 (mirroring) creates an exact copy of data on two or more disks. Provides fault tolerance but at the cost of reduced storage capacity.", | 
| 308 | raid3: "RAID 3 uses byte-level striping with a dedicated parity disk. Good for sequential data access but poor for random access. Can tolerate one disk failure.", | 
| 309 | raid5: "RAID 5 uses block-level striping with distributed parity. Provides good balance between performance, capacity and fault tolerance. Can tolerate one disk failure.", | 
| 310 | raid6: "RAID 6 is similar to RAID 5 but with double distributed parity. Can tolerate two disk failures, making it more reliable for large arrays.", | 
| 311 | raid10: "RAID 10 (1+0) combines mirroring and striping. Requires even number of disks (minimum 4). Provides good performance and can tolerate multiple disk failures (one per mirrored pair).", | 
| 312 | zfsStripe: "ZFS stripe is similar to RAID 0 - no redundancy but maximum capacity and performance. Data is distributed across all disks in the pool.", | 
| 313 | zfsMirror: "ZFS mirror is similar to RAID 1 - data is duplicated on two or more disks. Provides redundancy at the cost of storage capacity.", | 
| 314 | zfsZ1: "RAID-Z1 is similar to RAID 5 - single parity with distributed striping. Can tolerate one disk failure while providing good capacity and performance.", | 
| 315 | zfsZ2: "RAID-Z2 is similar to RAID 6 - double parity with distributed striping. Can tolerate two disk failures, recommended for larger pools.", | 
| 316 | zfsZ3: "RAID-Z3 provides triple parity protection, able to withstand three disk failures. Recommended for very large pools where rebuild times are long." | 
| 317 | } | 
| 318 | }, | 
| 319 | ru: { | 
| 320 | title: "Калькулятор ёмкости RAID", | 
| 321 | diskCountLabel: "Число дисков:", | 
| 322 | diskSizeLabel: "Размер диска (ГБ):", | 
| 323 | raidTypeLabel: "Тип RAID:", | 
| 324 | resultsTitle: "Результаты", | 
| 325 | totalSpaceLabel: "Общий объем:", | 
| 326 | effectiveSpaceLabel: "Эффективный объем:", | 
| 327 | efficiencyLabel: "Эффективность использования:", | 
| 328 | faultToleranceLabel: "Отказоустойчивость:", | 
| 329 | hintTitle: "О выбранном типе RAID:", | 
| 330 | raidTypes: { | 
| 331 | standard: "Обычный RAID", | 
| 332 | zfs: "ZFS", | 
| 333 | raid0: "RAID 0", | 
| 334 | raid1: "RAID 1", | 
| 335 | raid3: "RAID 3", | 
| 336 | raid5: "RAID 5", | 
| 337 | raid6: "RAID 6", | 
| 338 | raid10: "RAID 10", | 
| 339 | zfsStripe: "Stripe set", | 
| 340 | zfsMirror: "Mirror", | 
| 341 | zfsZ1: "RAID-Z1", | 
| 342 | zfsZ2: "RAID-Z2", | 
| 343 | zfsZ3: "RAID-Z3" | 
| 344 | }, | 
| 345 | faultTolerance: { | 
| 346 | none: "Нет", | 
| 347 | one: "1 отказ диска", | 
| 348 | two: "2 отказа диска", | 
| 349 | three: "3 отказа диска", | 
| 350 | half: "До половины дисков (в зеркальных парах)" | 
| 351 | }, | 
| 352 | hints: { | 
| 353 | raid0: "RAID 0 (чередование) объединяет несколько дисков в один логический блок с распределением данных по всем дискам. Обеспечивает повышенную производительность, но не имеет избыточности - отказ любого диска приводит к полной потере данных.", | 
| 354 | raid1: "RAID 1 (зеркалирование) создает точную копию данных на двух или более дисках. Обеспечивает отказоустойчивость, но за счет уменьшения полезного объема хранилища.", | 
| 355 | raid3: "RAID 3 использует чередование на уровне байтов с выделенным диском четности. Хорошо подходит для последовательного доступа к данным, но плохо для случайного. Может выдержать отказ одного диска.", | 
| 356 | raid5: "RAID 5 использует чередование на уровне блоков с распределенной четностью. Обеспечивает хороший баланс между производительностью, емкостью и отказоустойчивостью. Может выдержать отказ одного диска.", | 
| 357 | raid6: "RAID 6 аналогичен RAID 5, но с двойной распределенной четностью. Может выдержать отказ двух дисков, что делает его более надежным для больших массивов.", | 
| 358 | raid10: "RAID 10 (1+0) сочетает зеркалирование и чередование. Требует четного количества дисков (минимум 4). Обеспечивает хорошую производительность и может выдержать отказ нескольких дисков (по одному в каждой зеркальной паре).", | 
| 359 | zfsStripe: "ZFS stripe аналогичен RAID 0 - нет избыточности, но максимальная емкость и производительность. Данные распределяются по всем дискам в пуле.", | 
| 360 | zfsMirror: "ZFS mirror аналогичен RAID 1 - данные дублируются на двух или более дисках. Обеспечивает избыточность за счет полезного объема хранилища.", | 
| 361 | zfsZ1: "RAID-Z1 аналогичен RAID 5 - одинарная четность с распределенным чередованием. Может выдержать отказ одного диска при хорошей емкости и производительности.", | 
| 362 | zfsZ2: "RAID-Z2 аналогичен RAID 6 - двойная четность с распределенным чередованием. Может выдержать отказ двух дисков, рекомендуется для больших пулов.", | 
| 363 | zfsZ3: "RAID-Z3 обеспечивает тройную защиту четностью, может выдержать отказ трех дисков. Рекомендуется для очень больших пулов, где время восстановления велико." | 
| 364 | } | 
| 365 | } | 
| 366 | }; | 
| 367 | |
| 368 | // Current language | 
| 369 | let currentLang = 'en'; | 
| 370 | |
| 371 | // DOM elements | 
| 372 | const elements = { | 
| 373 | title: document.getElementById('title'), | 
| 374 | diskCountLabel: document.getElementById('disk-count-label'), | 
| 375 | diskSizeLabel: document.getElementById('disk-size-label'), | 
| 376 | raidTypeLabel: document.getElementById('raid-type-label'), | 
| 377 | resultsTitle: document.getElementById('results-title'), | 
| 378 | totalSpaceLabel: document.getElementById('total-space-label'), | 
| 379 | effectiveSpaceLabel: document.getElementById('effective-space-label'), | 
| 380 | efficiencyLabel: document.getElementById('efficiency-label'), | 
| 381 | faultToleranceLabel: document.getElementById('fault-tolerance-label'), | 
| 382 | hintTitle: document.getElementById('hint-title'), | 
| 383 | hintContent: document.getElementById('hint-content'), | 
| 384 | raidTypeSelect: document.getElementById('raid-type'), | 
| 385 | diskCountInput: document.getElementById('disk-count'), | 
| 386 | diskSizeInput: document.getElementById('disk-size'), | 
| 387 | raidTypeOptgroups: document.querySelectorAll('#raid-type optgroup'), | 
| 388 | raidTypeOptions: document.querySelectorAll('#raid-type option') | 
| 389 | }; | 
| 390 | |
| 391 | // Save state to URL hash | 
| 392 | function saveStateToHash() { | 
| 393 | const params = new URLSearchParams(); | 
| 394 | params.set('lang', currentLang); | 
| 395 | params.set('count', elements.diskCountInput.value); | 
| 396 | params.set('size', elements.diskSizeInput.value); | 
| 397 | params.set('raid', elements.raidTypeSelect.value); | 
| 398 | |
| 399 | window.location.hash = params.toString(); | 
| 400 | } | 
| 401 | |
| 402 | |
| 403 | // Load state from URL hash | 
| 404 | function loadStateFromHash() { | 
| 405 | if (window.location.hash) { | 
| 406 | try { | 
| 407 | const hash = window.location.hash.substring(1); | 
| 408 | const params = new URLSearchParams(hash); | 
| 409 | |
| 410 | if (params.has('lang') && translations[params.get('lang')]) { | 
| 411 | currentLang = params.get('lang'); | 
| 412 | } | 
| 413 | if (params.has('count')) { | 
| 414 | elements.diskCountInput.value = params.get('count'); | 
| 415 | } | 
| 416 | if (params.has('size')) { | 
| 417 | elements.diskSizeInput.value = params.get('size'); | 
| 418 | } | 
| 419 | if (params.has('raid')) { | 
| 420 | elements.raidTypeSelect.value = params.get('raid'); | 
| 421 | } | 
| 422 | |
| 423 | return true; | 
| 424 | } catch (e) { | 
| 425 | console.error("Error parsing hash:", e); | 
| 426 | } | 
| 427 | } | 
| 428 | return false; | 
| 429 | } | 
| 430 | |
| 431 | // Update UI language | 
| 432 | function updateLanguage(lang) { | 
| 433 | currentLang = lang; | 
| 434 | const t = translations[lang]; | 
| 435 | |
| 436 | // Update labels | 
| 437 | elements.title.textContent = t.title; | 
| 438 | elements.diskCountLabel.textContent = t.diskCountLabel; | 
| 439 | elements.diskSizeLabel.textContent = t.diskSizeLabel; | 
| 440 | elements.raidTypeLabel.textContent = t.raidTypeLabel; | 
| 441 | elements.resultsTitle.textContent = t.resultsTitle; | 
| 442 | elements.totalSpaceLabel.textContent = t.totalSpaceLabel; | 
| 443 | elements.effectiveSpaceLabel.textContent = t.effectiveSpaceLabel; | 
| 444 | elements.efficiencyLabel.textContent = t.efficiencyLabel; | 
| 445 | elements.faultToleranceLabel.textContent = t.faultToleranceLabel; | 
| 446 | elements.hintTitle.textContent = t.hintTitle; | 
| 447 | |
| 448 | // Update RAID type options | 
| 449 | elements.raidTypeOptgroups[0].label = t.raidTypes.standard; | 
| 450 | elements.raidTypeOptgroups[1].label = t.raidTypes.zfs; | 
| 451 | |
| 452 | const raidTypeOptions = [ | 
| 453 | t.raidTypes.raid0, t.raidTypes.raid1, t.raidTypes.raid3, | 
| 454 | t.raidTypes.raid5, t.raidTypes.raid6, t.raidTypes.raid10, | 
| 455 | t.raidTypes.zfsStripe, t.raidTypes.zfsMirror, | 
| 456 | t.raidTypes.zfsZ1, t.raidTypes.zfsZ2, t.raidTypes.zfsZ3 | 
| 457 | ]; | 
| 458 | |
| 459 | elements.raidTypeOptions.forEach((option, index) => { | 
| 460 | option.textContent = raidTypeOptions[index]; | 
| 461 | }); | 
| 462 | |
| 463 | // Recalculate to update fault tolerance text and hint | 
| 464 | calculate(); | 
| 465 | } | 
| 466 | |
| 467 | // Update hint based on selected RAID type | 
| 468 | function updateHint(raidType) { | 
| 469 | elements.hintContent.textContent = translations[currentLang].hints[raidType] || ''; | 
| 470 | } | 
| 471 | |
| 472 | // Calculate RAID parameters | 
| 473 | function calculate() { | 
| 474 | const diskCount = parseInt(elements.diskCountInput.value) || 0; | 
| 475 | const diskSize = parseInt(elements.diskSizeInput.value) || 0; | 
| 476 | const raidType = elements.raidTypeSelect.value; | 
| 477 | |
| 478 | let totalSpace, effectiveSpace, efficiency, faultTolerance; | 
| 479 | const t = translations[currentLang].faultTolerance; | 
| 480 | |
| 481 | switch(raidType) { | 
| 482 | case 'raid0': | 
| 483 | case 'zfsStripe': | 
| 484 | totalSpace = diskCount * diskSize; | 
| 485 | effectiveSpace = totalSpace; | 
| 486 | efficiency = 100; | 
| 487 | faultTolerance = t.none; | 
| 488 | break; | 
| 489 | |
| 490 | case 'raid1': | 
| 491 | case 'zfsMirror': | 
| 492 | totalSpace = diskCount * diskSize; | 
| 493 | effectiveSpace = diskSize; | 
| 494 | efficiency = (100 / diskCount).toFixed(2); | 
| 495 | faultTolerance = diskCount > 2 ? t.half : t.one; | 
| 496 | break; | 
| 497 | |
| 498 | case 'raid3': | 
| 499 | case 'raid5': | 
| 500 | case 'zfsZ1': | 
| 501 | totalSpace = diskCount * diskSize; | 
| 502 | effectiveSpace = (diskCount - 1) * diskSize; | 
| 503 | efficiency = ((diskCount - 1) / diskCount * 100).toFixed(2); | 
| 504 | faultTolerance = t.one; | 
| 505 | break; | 
| 506 | |
| 507 | case 'raid6': | 
| 508 | case 'zfsZ2': | 
| 509 | totalSpace = diskCount * diskSize; | 
| 510 | effectiveSpace = (diskCount - 2) * diskSize; | 
| 511 | efficiency = ((diskCount - 2) / diskCount * 100).toFixed(2); | 
| 512 | faultTolerance = t.two; | 
| 513 | break; | 
| 514 | |
| 515 | case 'zfsZ3': | 
| 516 | totalSpace = diskCount * diskSize; | 
| 517 | effectiveSpace = (diskCount - 3) * diskSize; | 
| 518 | efficiency = ((diskCount - 3) / diskCount * 100).toFixed(2); | 
| 519 | faultTolerance = t.three; | 
| 520 | break; | 
| 521 | |
| 522 | case 'raid10': | 
| 523 | if (diskCount < 2 || diskCount % 2 !== 0) { | 
| 524 | effectiveSpace = 0; | 
| 525 | efficiency = 0; | 
| 526 | faultTolerance = t.none; | 
| 527 | } else { | 
| 528 | totalSpace = diskCount * diskSize; | 
| 529 | effectiveSpace = (diskCount / 2) * diskSize; | 
| 530 | efficiency = 50; | 
| 531 | faultTolerance = t.half; | 
| 532 | } | 
| 533 | break; | 
| 534 | |
| 535 | default: | 
| 536 | totalSpace = effectiveSpace = efficiency = 0; | 
| 537 | faultTolerance = t.none; | 
| 538 | } | 
| 539 | |
| 540 | // Update results | 
| 541 | document.getElementById('total-space').textContent = totalSpace.toLocaleString() + ' Gb'; | 
| 542 | document.getElementById('effective-space').textContent = effectiveSpace.toLocaleString() + ' Gb'; | 
| 543 | document.getElementById('efficiency').textContent = efficiency + ' %'; | 
| 544 | document.getElementById('fault-tolerance').textContent = faultTolerance; | 
| 545 | |
| 546 | // Update hint | 
| 547 | updateHint(raidType); | 
| 548 | |
| 549 | // Save state | 
| 550 | saveStateToHash(); | 
| 551 | } | 
| 552 | |
| 553 | // Event listeners | 
| 554 | function setupEventListeners() { | 
| 555 | elements.diskCountInput.addEventListener('input', calculate); | 
| 556 | elements.diskSizeInput.addEventListener('input', calculate); | 
| 557 | elements.raidTypeSelect.addEventListener('change', calculate); | 
| 558 | |
| 559 | document.getElementById('lang-en').addEventListener('click', () => { | 
| 560 | currentLang = 'en'; | 
| 561 | updateLanguage('en'); | 
| 562 | saveStateToHash(); | 
| 563 | }); | 
| 564 | document.getElementById('lang-ru').addEventListener('click', () => { | 
| 565 | currentLang = 'ru'; | 
| 566 | updateLanguage('ru'); | 
| 567 | saveStateToHash(); | 
| 568 | }); | 
| 569 | } | 
| 570 | |
| 571 | // Initialize | 
| 572 | function init() { | 
| 573 | // Try to load state from hash | 
| 574 | const stateLoaded = loadStateFromHash(); | 
| 575 | |
| 576 | // Setup event listeners | 
| 577 | setupEventListeners(); | 
| 578 | |
| 579 | // Update UI with current or default values | 
| 580 | updateLanguage(currentLang); | 
| 581 | |
| 582 | // If no state was loaded, calculate with defaults | 
| 583 | if (!stateLoaded) { | 
| 584 | calculate(); | 
| 585 | } | 
| 586 | } | 
| 587 | |
| 588 | // Start the application | 
| 589 | init(); | 
| 590 | </script> | 
| 591 | </body> | 
| 592 | </html> |