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