From 37cfd0a105d39dadb70343f570f461fe508a3f0c Mon Sep 17 00:00:00 2001
From: Rob Weber 
Date: Sun, 28 Jul 2024 20:25:09 +0000
Subject: [PATCH 01/20] implementation of raid calculator with levels 0, 1, 5,
 6, and 10 need to add descriptions yet
---
 components.d.ts                               |  17 ++-
 src/tools/index.ts                            |   3 +-
 src/tools/raid-calculator/index.ts            |  12 ++
 .../raid-calculator.e2e.spec.ts               |  15 +++
 .../raid-calculator.service.test.ts           |   6 +
 .../raid-calculator.service.ts                |  94 +++++++++++++++
 src/tools/raid-calculator/raid-calculator.vue | 107 ++++++++++++++++++
 7 files changed, 247 insertions(+), 7 deletions(-)
 create mode 100644 src/tools/raid-calculator/index.ts
 create mode 100644 src/tools/raid-calculator/raid-calculator.e2e.spec.ts
 create mode 100644 src/tools/raid-calculator/raid-calculator.service.test.ts
 create mode 100644 src/tools/raid-calculator/raid-calculator.service.ts
 create mode 100644 src/tools/raid-calculator/raid-calculator.vue
diff --git a/components.d.ts b/components.d.ts
index f2c3146f..4164caa8 100644
--- a/components.d.ts
+++ b/components.d.ts
@@ -89,7 +89,9 @@ declare module '@vue/runtime-core' {
     HttpStatusCodes: typeof import('./src/tools/http-status-codes/http-status-codes.vue')['default']
     IbanValidatorAndParser: typeof import('./src/tools/iban-validator-and-parser/iban-validator-and-parser.vue')['default']
     'IconMdi:brushVariant': typeof import('~icons/mdi/brush-variant')['default']
+    'IconMdi:contentCopy': typeof import('~icons/mdi/content-copy')['default']
     'IconMdi:kettleSteamOutline': typeof import('~icons/mdi/kettle-steam-outline')['default']
+    IconMdiArrowRightBottom: typeof import('~icons/mdi/arrow-right-bottom')['default']
     IconMdiChevronDown: typeof import('~icons/mdi/chevron-down')['default']
     IconMdiChevronRight: typeof import('~icons/mdi/chevron-right')['default']
     IconMdiClose: typeof import('~icons/mdi/close')['default']
@@ -126,25 +128,27 @@ declare module '@vue/runtime-core' {
     MenuLayout: typeof import('./src/components/MenuLayout.vue')['default']
     MetaTagGenerator: typeof import('./src/tools/meta-tag-generator/meta-tag-generator.vue')['default']
     MimeTypes: typeof import('./src/tools/mime-types/mime-types.vue')['default']
+    NAlert: typeof import('naive-ui')['NAlert']
     NavbarButtons: typeof import('./src/components/NavbarButtons.vue')['default']
-    NCode: typeof import('naive-ui')['NCode']
     NCollapseTransition: typeof import('naive-ui')['NCollapseTransition']
     NConfigProvider: typeof import('naive-ui')['NConfigProvider']
+    NDatePicker: typeof import('naive-ui')['NDatePicker']
     NDivider: typeof import('naive-ui')['NDivider']
     NEllipsis: typeof import('naive-ui')['NEllipsis']
+    NForm: typeof import('naive-ui')['NForm']
     NFormItem: typeof import('naive-ui')['NFormItem']
-    NGi: typeof import('naive-ui')['NGi']
-    NGrid: typeof import('naive-ui')['NGrid']
     NH1: typeof import('naive-ui')['NH1']
     NH3: typeof import('naive-ui')['NH3']
     NIcon: typeof import('naive-ui')['NIcon']
+    NInputGroup: typeof import('naive-ui')['NInputGroup']
+    NInputGroupLabel: typeof import('naive-ui')['NInputGroupLabel']
     NInputNumber: typeof import('naive-ui')['NInputNumber']
-    NLabel: typeof import('naive-ui')['NLabel']
     NLayout: typeof import('naive-ui')['NLayout']
     NLayoutSider: typeof import('naive-ui')['NLayoutSider']
     NMenu: typeof import('naive-ui')['NMenu']
-    NScrollbar: typeof import('naive-ui')['NScrollbar']
-    NSpin: typeof import('naive-ui')['NSpin']
+    NStatistic: typeof import('naive-ui')['NStatistic']
+    NSwitch: typeof import('naive-ui')['NSwitch']
+    NTable: typeof import('naive-ui')['NTable']
     NumeronymGenerator: typeof import('./src/tools/numeronym-generator/numeronym-generator.vue')['default']
     OtpCodeGeneratorAndValidator: typeof import('./src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue')['default']
     PasswordStrengthAnalyser: typeof import('./src/tools/password-strength-analyser/password-strength-analyser.vue')['default']
@@ -153,6 +157,7 @@ declare module '@vue/runtime-core' {
     PercentageCalculator: typeof import('./src/tools/percentage-calculator/percentage-calculator.vue')['default']
     PhoneParserAndFormatter: typeof import('./src/tools/phone-parser-and-formatter/phone-parser-and-formatter.vue')['default']
     QrCodeGenerator: typeof import('./src/tools/qr-code-generator/qr-code-generator.vue')['default']
+    RaidCalculator: typeof import('./src/tools/raid-calculator/raid-calculator.vue')['default']
     RandomPortGenerator: typeof import('./src/tools/random-port-generator/random-port-generator.vue')['default']
     ResultRow: typeof import('./src/tools/ipv4-range-expander/result-row.vue')['default']
     RomanNumeralConverter: typeof import('./src/tools/roman-numeral-converter/roman-numeral-converter.vue')['default']
diff --git a/src/tools/index.ts b/src/tools/index.ts
index aa861c93..da75b7a8 100644
--- a/src/tools/index.ts
+++ b/src/tools/index.ts
@@ -1,6 +1,7 @@
 import { tool as base64FileConverter } from './base64-file-converter';
 import { tool as base64StringConverter } from './base64-string-converter';
 import { tool as basicAuthGenerator } from './basic-auth-generator';
+import { tool as raidCalculator } from './raid-calculator';
 
 import { tool as asciiTextDrawer } from './ascii-text-drawer';
 
@@ -156,7 +157,7 @@ export const toolsByCategory: ToolCategory[] = [
   },
   {
     name: 'Math',
-    components: [mathEvaluator, etaCalculator, percentageCalculator],
+    components: [mathEvaluator, etaCalculator, percentageCalculator, raidCalculator],
   },
   {
     name: 'Measurement',
diff --git a/src/tools/raid-calculator/index.ts b/src/tools/raid-calculator/index.ts
new file mode 100644
index 00000000..a22dbdab
--- /dev/null
+++ b/src/tools/raid-calculator/index.ts
@@ -0,0 +1,12 @@
+import { Database } from '@vicons/tabler';
+import { defineTool } from '../tool';
+
+export const tool = defineTool({
+  name: 'RAID Calculator',
+  path: '/raid-calculator',
+  description: 'Calculate storage capacity and fault tolerance of an array based on the number of disks, size, and RAID type',
+  keywords: ['raid', 'calculator'],
+  component: () => import('./raid-calculator.vue'),
+  icon: Database,
+  createdAt: new Date('2024-07-27'),
+});
diff --git a/src/tools/raid-calculator/raid-calculator.e2e.spec.ts b/src/tools/raid-calculator/raid-calculator.e2e.spec.ts
new file mode 100644
index 00000000..4838d0e1
--- /dev/null
+++ b/src/tools/raid-calculator/raid-calculator.e2e.spec.ts
@@ -0,0 +1,15 @@
+import { test, expect } from '@playwright/test';
+
+test.describe('Tool - RAID Calculator', () => {
+  test.beforeEach(async ({ page }) => {
+    await page.goto('/raid-calculator');
+  });
+
+  test('Has correct title', async ({ page }) => {
+    await expect(page).toHaveTitle('RAID Calculator - IT Tools');
+  });
+
+  test('', async ({ page }) => {
+
+  });
+});
diff --git a/src/tools/raid-calculator/raid-calculator.service.test.ts b/src/tools/raid-calculator/raid-calculator.service.test.ts
new file mode 100644
index 00000000..46812611
--- /dev/null
+++ b/src/tools/raid-calculator/raid-calculator.service.test.ts
@@ -0,0 +1,6 @@
+import { expect, describe, it } from 'vitest';
+// import { } from './raid-calculator.service';
+//
+// describe('raid-calculator', () => {
+//
+// })
\ No newline at end of file
diff --git a/src/tools/raid-calculator/raid-calculator.service.ts b/src/tools/raid-calculator/raid-calculator.service.ts
new file mode 100644
index 00000000..b57b350d
--- /dev/null
+++ b/src/tools/raid-calculator/raid-calculator.service.ts
@@ -0,0 +1,94 @@
+export { raidCalculations };
+
+const raidCalculations = {
+  raid_0: {
+    about: 'RAID 0 splits data evenly across 2 or more disks with redunancy or fault tolerance. More info: Wikipedia',
+    requirements: 'RAID 0 requires at least 1 disk',
+    validate: function(num, size){
+      return num > 1
+    },
+    capacity: function(num, size, unit){
+      // total disks * size
+      return (num * size) * unit;
+    },
+    speed: function(num, size, unit){
+      return `${num}x read and ${num}x write speed gain`;
+    },
+    fault: function(num, size, unit){
+      return "None";
+    }
+  },
+  raid_1: {
+    about: 'RAID 1 consists of an exact copy of the data across two or moe disks. The array will operate as long as at least one drive is operational.  More info: Wikipedia',
+    requirements: 'RAID 1 requires at least 1 disk',
+    validate: function(num, size){
+      return num > 1
+    },
+    capacity: function(num, size, unit){
+      // total size is size of a single drive
+      return size * unit;
+    },
+    speed: function(num, size, unit){
+      // potential for all drives read at once
+      return `Potential ${num}x read and no write speed gain`;
+    },
+    fault: function(num, size, unit){
+      // FT = total - 1
+      return `${num -1} drive failures`;
+    }
+  },
+  raid_5: {
+    about: 'This is RAID 5',
+    requirements: 'RAID 5 requires at least 3 disks',
+    validate: function(num, size){
+      return num >= 3
+    },
+    capacity: function(num, size, unit){
+      // (N-1) * S (one drive for parity)
+      return ((num - 1) * size) * unit;
+    },
+    speed: function(num, size, unit){
+      return `${num - 1}x read speed gain and write speed penalty (due to parity calculations)`;
+    },
+    fault: function(num, size, unit){
+      // always 1 failure
+      return "1 drive failure";
+    }
+  },
+  raid_6: {
+    about: 'This is RAID 6',
+    requirements: 'RAID 6 requires at least 4 disks',
+    validate: function(num, size){
+      return num >= 4
+    },
+    capacity: function(num, size, unit){
+      // (N-2) * S (2 parity)
+      return ((num - 2) * size) * unit;
+    },
+    speed: function(num, size, unit){
+      return `${num - 2}x read speed gain and write speed penalty (due to parity calculations)`;
+    },
+    fault: function(num, size, unit){
+      // always 2 drive failures
+      return "2 drive failures";
+    }
+  },
+  raid_10: {
+    about: 'RAID 10 is generally recognized as a stripe of mirrors (RAID 1 + RAID 2). Each set of drives is mirrored and striped together so that each drive in the set is fault tolerant within the group. More info: Wikipedia',
+    requirements: 'RAID 10 requires an even number of at least 4 disks',
+    validate: function(num, size){
+      return num >= 4 && num % 2 == 0
+    },
+    capacity: function(num, size, unit){
+      // Total disks (stripe)/2 (mirror)
+      return ((num * size) / 2) * unit;
+    },
+    speed: function(num, size, unit){
+      return `${num - 2}x read speed gain and write speed penalty (due to parity calculations)`;
+    },
+    fault: function(num, size, unit){
+      // always 2 drive failures
+      return "At least 1 drive failure per mirrored set";
+    }
+  }
+}
diff --git a/src/tools/raid-calculator/raid-calculator.vue b/src/tools/raid-calculator/raid-calculator.vue
new file mode 100644
index 00000000..6be056ff
--- /dev/null
+++ b/src/tools/raid-calculator/raid-calculator.vue
@@ -0,0 +1,107 @@
+
+
+
+  
+    
+      
+        
+      
+
+      
+        
+        
+          
+        
+      
+      
+        
+      
+      {{ raidRequirements }}
+      
+    
+    
+      
+        {{ calculatedCapacity }}
+      
+      
+        {{ calculatedFaultTolerance }}
+      
+    
+  
{{ raidRequirements }}
-      
+      
     
     
       
From c5ba4f356fba0adcb5355e1eb528399dfdac97a7 Mon Sep 17 00:00:00 2001
From: Rob Weber 
Date: Mon, 29 Jul 2024 13:32:13 +0000
Subject: [PATCH 06/20] use table for display
---
 src/tools/raid-calculator/raid-calculator.vue | 20 +++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)
diff --git a/src/tools/raid-calculator/raid-calculator.vue b/src/tools/raid-calculator/raid-calculator.vue
index d244074b..ef60166b 100644
--- a/src/tools/raid-calculator/raid-calculator.vue
+++ b/src/tools/raid-calculator/raid-calculator.vue
@@ -91,12 +91,20 @@ function formatBytes(bytes: number, decimals = 2) {
       
     
     
-      
-        {{ calculatedCapacity }}
-      
-      
-        {{ calculatedFaultTolerance }}
-      
+      
+        
+          
+            | Capacity+ | {{ calculatedCapacity }}+ | 
+          
+            | Fault Tolerance+ | +              {{ calculatedFaultTolerance }}
++ | 
+        
+      
     
   
 
From ea6b3d0c3885d5a14349333c74b05ee4b4c5dff3 Mon Sep 17 00:00:00 2001
From: Rob Weber 
Date: Mon, 29 Jul 2024 13:52:05 +0000
Subject: [PATCH 07/20] added enum to use base 2 or base 10 for calcs
---
 src/utils/convert.ts | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/src/utils/convert.ts b/src/utils/convert.ts
index 9eac1921..f834a1c3 100644
--- a/src/utils/convert.ts
+++ b/src/utils/convert.ts
@@ -1,9 +1,14 @@
-export function formatBytes(bytes: number, decimals = 2) {
+export enum UNIT_BASE {
+  BASE_2 = 1024,
+  BASE_10 = 1000
+}
+
+export function formatBytes(bytes: number, decimals = 2, base: UNIT_BASE = UNIT_BASE.BASE_2) {
   if (bytes === 0) {
     return '0 Bytes';
   }
 
-  const k = 1024;
+  const k = base;
   const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
   const i = Math.floor(Math.log(bytes) / Math.log(k));
 
From d2554a7bc34b5bd22a746df3fee774307781b156 Mon Sep 17 00:00:00 2001
From: Rob Weber 
Date: Mon, 29 Jul 2024 13:52:23 +0000
Subject: [PATCH 08/20] use built in converter
---
 src/tools/raid-calculator/raid-calculator.vue | 17 ++---------------
 1 file changed, 2 insertions(+), 15 deletions(-)
diff --git a/src/tools/raid-calculator/raid-calculator.vue b/src/tools/raid-calculator/raid-calculator.vue
index ef60166b..7cdea8ef 100644
--- a/src/tools/raid-calculator/raid-calculator.vue
+++ b/src/tools/raid-calculator/raid-calculator.vue
@@ -1,5 +1,5 @@
 
 
 
From e84ac9e38ccfec3a4d2ad471daef27db5609e3bd Mon Sep 17 00:00:00 2001
From: Rob Weber 
Date: Mon, 29 Jul 2024 19:35:00 +0000
Subject: [PATCH 09/20] no tests for this
---
 src/tools/raid-calculator/raid-calculator.service.test.ts | 6 ------
 1 file changed, 6 deletions(-)
 delete mode 100644 src/tools/raid-calculator/raid-calculator.service.test.ts
diff --git a/src/tools/raid-calculator/raid-calculator.service.test.ts b/src/tools/raid-calculator/raid-calculator.service.test.ts
deleted file mode 100644
index 46812611..00000000
--- a/src/tools/raid-calculator/raid-calculator.service.test.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { expect, describe, it } from 'vitest';
-// import { } from './raid-calculator.service';
-//
-// describe('raid-calculator', () => {
-//
-// })
\ No newline at end of file
From b476b6808ecdf3d5aa142a366b7e2876725e62ff Mon Sep 17 00:00:00 2001
From: Rob Weber 
Date: Mon, 29 Jul 2024 19:50:37 +0000
Subject: [PATCH 10/20] linting
---
 .../raid-calculator.e2e.spec.ts               |  2 +-
 .../raid-calculator.service.ts                | 66 +++++++++----------
 src/tools/raid-calculator/raid-calculator.vue | 40 ++++++-----
 src/utils/convert.ts                          |  2 +-
 4 files changed, 57 insertions(+), 53 deletions(-)
diff --git a/src/tools/raid-calculator/raid-calculator.e2e.spec.ts b/src/tools/raid-calculator/raid-calculator.e2e.spec.ts
index 4838d0e1..f558383c 100644
--- a/src/tools/raid-calculator/raid-calculator.e2e.spec.ts
+++ b/src/tools/raid-calculator/raid-calculator.e2e.spec.ts
@@ -1,4 +1,4 @@
-import { test, expect } from '@playwright/test';
+import { expect, test } from '@playwright/test';
 
 test.describe('Tool - RAID Calculator', () => {
   test.beforeEach(async ({ page }) => {
diff --git a/src/tools/raid-calculator/raid-calculator.service.ts b/src/tools/raid-calculator/raid-calculator.service.ts
index 162029ae..9739b687 100644
--- a/src/tools/raid-calculator/raid-calculator.service.ts
+++ b/src/tools/raid-calculator/raid-calculator.service.ts
@@ -2,77 +2,77 @@ export { raidCalculations };
 
 const raidCalculations = {
   raid_0: {
-    about: 'RAID 0 the splits data evenly across 2 or more disks without redunancy or fault tolerance creating one large storage space. More info: Wikipedia',
+    about: 'RAID 0 splits data evenly across 2 or more disks without redunancy or fault tolerance creating one large storage space. More info: Wikipedia',
     requirements: 'RAID 0 requires at least 1 disk',
-    validate: function(num, size){
-      return num > 1
+    validate(num, size) {
+      return num > 1;
     },
-    capacity: function(num, size, unit){
+    capacity(num, size, unit) {
       // total disks * size
       return (num * size) * unit;
     },
-    fault: function(num, size, unit){
-      return "None";
-    }
+    fault(num, size, unit) {
+      return 'None';
+    },
   },
   raid_1: {
     about: 'RAID 1 consists of an exact copy of the data (mirror) across two or more disks. The array will operate as long as at least one drive is operational.  More info: Wikipedia',
     requirements: 'RAID 1 requires at least 1 disk',
-    validate: function(num, size){
-      return num > 1
+    validate(num, size) {
+      return num > 1;
     },
-    capacity: function(num, size, unit){
+    capacity(num, size, unit) {
       // total size is size of a single drive
       return size * unit;
     },
-    fault: function(num, size, unit){
+    fault(num, size, unit) {
       // FT = total - 1
-      return `${num -1} drive failures`;
-    }
+      return `${num - 1} drive failures`;
+    },
   },
   raid_5: {
     about: 'RAID 5 uses block level striping with parity. This allows for fault tolerance with a storage reduction equal to one drive for the parity information. More info: Wikipedia',
     requirements: 'RAID 5 requires at least 3 disks',
-    validate: function(num, size){
-      return num >= 3
+    validate(num, size) {
+      return num >= 3;
     },
-    capacity: function(num, size, unit){
+    capacity(num, size, unit) {
       // (N-1) * S (one drive for parity)
       return ((num - 1) * size) * unit;
     },
-    fault: function(num, size, unit){
+    fault(num, size, unit) {
       // always 1 failure
-      return "1 drive failure";
-    }
+      return '1 drive failure';
+    },
   },
   raid_6: {
     about: 'RAID 6 is similiar to RAID 5 but with an additional parity block. This allows for an additional disk failure at the cost of storage reduction equal to two drives. More info: Wikipedia',
     requirements: 'RAID 6 requires at least 4 disks',
-    validate: function(num, size){
-      return num >= 4
+    validate(num, size) {
+      return num >= 4;
     },
-    capacity: function(num, size, unit){
+    capacity(num, size, unit) {
       // (N-2) * S (2 parity)
       return ((num - 2) * size) * unit;
     },
-    fault: function(num, size, unit){
+    fault(num, size, unit) {
       // always 2 drive failures
-      return "2 drive failures";
-    }
+      return '2 drive failures';
+    },
   },
   raid_10: {
     about: 'RAID 10 is generally recognized as a stripe of mirrors (RAID 1 + RAID 0). Each set of drives is mirrored and striped together so that each drive in the set is fault tolerant within the group. More info: Wikipedia',
     requirements: 'RAID 10 requires an even number of at least 4 disks',
-    validate: function(num, size){
-      return num >= 4 && num % 2 == 0
+    validate(num, size) {
+      return num >= 4 && num % 2 === 0;
     },
-    capacity: function(num, size, unit){
+    capacity(num, size, unit) {
       // Total disks (stripe)/2 (mirror)
       return ((num * size) / 2) * unit;
     },
-    fault: function(num, size, unit){
+    fault(num, size, unit) {
       // one per mirror
-      return "1 drive failure per mirrored set";
-    }
-  }
-}
+      return '1 drive failure per mirrored set';
+    },
+  },
+};
diff --git a/src/tools/raid-calculator/raid-calculator.vue b/src/tools/raid-calculator/raid-calculator.vue
index 7cdea8ef..d53f6006 100644
--- a/src/tools/raid-calculator/raid-calculator.vue
+++ b/src/tools/raid-calculator/raid-calculator.vue
@@ -1,10 +1,10 @@
 
 
@@ -51,6 +62,10 @@ function validateSetup() {
           />
         
       
+      
+        
+        
+      
       
         
       
From 26e0657edec91d350dec71022bab118d006d98e7 Mon Sep 17 00:00:00 2001
From: Rob Weber 
Date: Tue, 30 Jul 2024 14:46:09 +0000
Subject: [PATCH 14/20] added raid 60
---
 src/tools/raid-calculator/index.ts            |  2 +-
 .../raid-calculator.service.ts                | 28 +++++++++++++++++--
 src/tools/raid-calculator/raid-calculator.vue |  3 +-
 3 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/tools/raid-calculator/index.ts b/src/tools/raid-calculator/index.ts
index a22dbdab..6420deb1 100644
--- a/src/tools/raid-calculator/index.ts
+++ b/src/tools/raid-calculator/index.ts
@@ -4,7 +4,7 @@ import { defineTool } from '../tool';
 export const tool = defineTool({
   name: 'RAID Calculator',
   path: '/raid-calculator',
-  description: 'Calculate storage capacity and fault tolerance of an array based on the number of disks, size, and RAID type',
+  description: 'Calculate storage capacity, fault tolerance and space efficiency of an array based on the number of disks, size, and RAID type',
   keywords: ['raid', 'calculator'],
   component: () => import('./raid-calculator.vue'),
   icon: Database,
diff --git a/src/tools/raid-calculator/raid-calculator.service.ts b/src/tools/raid-calculator/raid-calculator.service.ts
index 69b9c309..0428548c 100644
--- a/src/tools/raid-calculator/raid-calculator.service.ts
+++ b/src/tools/raid-calculator/raid-calculator.service.ts
@@ -97,12 +97,12 @@ const raidCalculations = {
   },
   raid_50: {
     about: 'RAID 50 stripes multiple RAID 5 arrays together (RAID 5 + RAID 0). Each RAID 5 set can sustain a single drive failure. More info: Wikipedia',
-    requirements: 'RAID 50 requires at least 6 disks total with 3 minimum per stripe. Stripes must contain an equal number of disks.',
+    requirements: 'RAID 50 requires at least 6 disks with 3 minimum per stripe. Stripes must contain an equal number of disks.',
     validate(num, size, stripeSize) {
       return num >= 6 && stripeSize >= 3 && num % stripeSize === 0;
     },
     capacity(num, size, stripeSize, unit) {
-      // RAID 5 per strip
+      // RAID 5 per stripe
       const perStripe = ((stripeSize - 1) * size) * unit;
 
       // sum each stripe
@@ -113,8 +113,30 @@ const raidCalculations = {
       return (1 - (1 / stripeSize)) * 100;
     },
     fault(num, size, unit) {
-      // one per mirror
+      // one per set
       return '1 drive failure per RAID 5 set';
     },
   },
+  raid_60: {
+    about: 'RAID 60 stripes multiple RAID 6 arrays together (RAID 6 + RAID 0). Each RAID 6 set can sustain a two drive failures. More info: Wikipedia',
+    requirements: 'RAID 50 requires at least 8 disks with 4 minimum per stripe. Stripes must contain an equal number of disks.',
+    validate(num, size, stripeSize) {
+      return num >= 8 && stripeSize >= 4 && num % stripeSize === 0;
+    },
+    capacity(num, size, stripeSize, unit) {
+      // RAID 6 per stripe
+      const perStripe = ((stripeSize - 2) * size) * unit;
+
+      // sum each stripe
+      return perStripe * (num / stripeSize);
+    },
+    efficiency(num, stripeSize) {
+      // 1 - (2 / strips per stripe)
+      return (1 - (2 / stripeSize)) * 100;
+    },
+    fault(num, size, unit) {
+      // 2 per set
+      return '2 drive failures per RAID 6 set';
+    },
+  },
 };
diff --git a/src/tools/raid-calculator/raid-calculator.vue b/src/tools/raid-calculator/raid-calculator.vue
index 0e607034..acb4b08a 100644
--- a/src/tools/raid-calculator/raid-calculator.vue
+++ b/src/tools/raid-calculator/raid-calculator.vue
@@ -62,7 +62,7 @@ function validateSetup() {
           />
         
       
-      
+      
         
         
       
@@ -77,6 +77,7 @@ function validateSetup() {
             { label: 'RAID 6 (double parity)', value: 'raid_6' },
             { label: 'RAID 10 (mirror + stripe)', value: 'raid_10' },
             { label: 'RAID 50 (parity + stripe)', value: 'raid_50' },
+            { label: 'RAID 60 (double parity + stripe)', value: 'raid_60' },
           ]"
         />
       
From 844d7ac69fa4ab89e4524066b915922336336161 Mon Sep 17 00:00:00 2001
From: Rob Weber 
Date: Tue, 30 Jul 2024 14:48:34 +0000
Subject: [PATCH 15/20] liniting
---
 src/tools/raid-calculator/raid-calculator.vue | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/src/tools/raid-calculator/raid-calculator.vue b/src/tools/raid-calculator/raid-calculator.vue
index acb4b08a..1a6c5604 100644
--- a/src/tools/raid-calculator/raid-calculator.vue
+++ b/src/tools/raid-calculator/raid-calculator.vue
@@ -12,11 +12,10 @@ const raidRequirements = computed(() => raidCalculations[raidType.value].require
 const inputsValid = computed(() => validateSetup());
 
 const totalStripes = computed(() => {
-  if(inputsValid.value){
+  if (inputsValid.value) {
     return `${diskTotal.value / diskPerStripe.value} stripes total`;
   }
-  else
-  {
+  else {
     return '';
   }
 });
From e515041d4e36d2c7e36f969fbec7454a08d9cae1 Mon Sep 17 00:00:00 2001
From: Rob Weber 
Date: Tue, 30 Jul 2024 17:42:26 +0000
Subject: [PATCH 16/20] revert this
---
 components.d.ts | 391 ++++++++++++++++++++++++------------------------
 1 file changed, 193 insertions(+), 198 deletions(-)
diff --git a/components.d.ts b/components.d.ts
index 4164caa8..d10a0fd7 100644
--- a/components.d.ts
+++ b/components.d.ts
@@ -1,198 +1,193 @@
-/* eslint-disable */
-/* prettier-ignore */
-// @ts-nocheck
-// Generated by unplugin-vue-components
-// Read more: https://github.com/vuejs/core/pull/3399
-import '@vue/runtime-core'
-
-export {}
-
-declare module '@vue/runtime-core' {
-  export interface GlobalComponents {
-    '404.page': typeof import('./src/pages/404.page.vue')['default']
-    About: typeof import('./src/pages/About.vue')['default']
-    App: typeof import('./src/App.vue')['default']
-    AsciiTextDrawer: typeof import('./src/tools/ascii-text-drawer/ascii-text-drawer.vue')['default']
-    'Base.layout': typeof import('./src/layouts/base.layout.vue')['default']
-    Base64FileConverter: typeof import('./src/tools/base64-file-converter/base64-file-converter.vue')['default']
-    Base64StringConverter: typeof import('./src/tools/base64-string-converter/base64-string-converter.vue')['default']
-    BasicAuthGenerator: typeof import('./src/tools/basic-auth-generator/basic-auth-generator.vue')['default']
-    Bcrypt: typeof import('./src/tools/bcrypt/bcrypt.vue')['default']
-    BenchmarkBuilder: typeof import('./src/tools/benchmark-builder/benchmark-builder.vue')['default']
-    Bip39Generator: typeof import('./src/tools/bip39-generator/bip39-generator.vue')['default']
-    CAlert: typeof import('./src/ui/c-alert/c-alert.vue')['default']
-    'CAlert.demo': typeof import('./src/ui/c-alert/c-alert.demo.vue')['default']
-    CameraRecorder: typeof import('./src/tools/camera-recorder/camera-recorder.vue')['default']
-    CaseConverter: typeof import('./src/tools/case-converter/case-converter.vue')['default']
-    CButton: typeof import('./src/ui/c-button/c-button.vue')['default']
-    'CButton.demo': typeof import('./src/ui/c-button/c-button.demo.vue')['default']
-    CButtonsSelect: typeof import('./src/ui/c-buttons-select/c-buttons-select.vue')['default']
-    'CButtonsSelect.demo': typeof import('./src/ui/c-buttons-select/c-buttons-select.demo.vue')['default']
-    CCard: typeof import('./src/ui/c-card/c-card.vue')['default']
-    'CCard.demo': typeof import('./src/ui/c-card/c-card.demo.vue')['default']
-    CCollapse: typeof import('./src/ui/c-collapse/c-collapse.vue')['default']
-    'CCollapse.demo': typeof import('./src/ui/c-collapse/c-collapse.demo.vue')['default']
-    CDiffEditor: typeof import('./src/ui/c-diff-editor/c-diff-editor.vue')['default']
-    CFileUpload: typeof import('./src/ui/c-file-upload/c-file-upload.vue')['default']
-    'CFileUpload.demo': typeof import('./src/ui/c-file-upload/c-file-upload.demo.vue')['default']
-    ChmodCalculator: typeof import('./src/tools/chmod-calculator/chmod-calculator.vue')['default']
-    Chronometer: typeof import('./src/tools/chronometer/chronometer.vue')['default']
-    CInputText: typeof import('./src/ui/c-input-text/c-input-text.vue')['default']
-    'CInputText.demo': typeof import('./src/ui/c-input-text/c-input-text.demo.vue')['default']
-    CKeyValueList: typeof import('./src/ui/c-key-value-list/c-key-value-list.vue')['default']
-    CKeyValueListItem: typeof import('./src/ui/c-key-value-list/c-key-value-list-item.vue')['default']
-    CLabel: typeof import('./src/ui/c-label/c-label.vue')['default']
-    CLink: typeof import('./src/ui/c-link/c-link.vue')['default']
-    'CLink.demo': typeof import('./src/ui/c-link/c-link.demo.vue')['default']
-    CMarkdown: typeof import('./src/ui/c-markdown/c-markdown.vue')['default']
-    'CMarkdown.demo': typeof import('./src/ui/c-markdown/c-markdown.demo.vue')['default']
-    CModal: typeof import('./src/ui/c-modal/c-modal.vue')['default']
-    'CModal.demo': typeof import('./src/ui/c-modal/c-modal.demo.vue')['default']
-    CModalValue: typeof import('./src/ui/c-modal-value/c-modal-value.vue')['default']
-    'CModalValue.demo': typeof import('./src/ui/c-modal-value/c-modal-value.demo.vue')['default']
-    CollapsibleToolMenu: typeof import('./src/components/CollapsibleToolMenu.vue')['default']
-    ColorConverter: typeof import('./src/tools/color-converter/color-converter.vue')['default']
-    ColoredCard: typeof import('./src/components/ColoredCard.vue')['default']
-    CommandPalette: typeof import('./src/modules/command-palette/command-palette.vue')['default']
-    CommandPaletteOption: typeof import('./src/modules/command-palette/components/command-palette-option.vue')['default']
-    CrontabGenerator: typeof import('./src/tools/crontab-generator/crontab-generator.vue')['default']
-    CSelect: typeof import('./src/ui/c-select/c-select.vue')['default']
-    'CSelect.demo': typeof import('./src/ui/c-select/c-select.demo.vue')['default']
-    CTable: typeof import('./src/ui/c-table/c-table.vue')['default']
-    'CTable.demo': typeof import('./src/ui/c-table/c-table.demo.vue')['default']
-    CTextCopyable: typeof import('./src/ui/c-text-copyable/c-text-copyable.vue')['default']
-    'CTextCopyable.demo': typeof import('./src/ui/c-text-copyable/c-text-copyable.demo.vue')['default']
-    CTooltip: typeof import('./src/ui/c-tooltip/c-tooltip.vue')['default']
-    'CTooltip.demo': typeof import('./src/ui/c-tooltip/c-tooltip.demo.vue')['default']
-    DateTimeConverter: typeof import('./src/tools/date-time-converter/date-time-converter.vue')['default']
-    'DemoHome.page': typeof import('./src/ui/demo/demo-home.page.vue')['default']
-    DemoWrapper: typeof import('./src/ui/demo/demo-wrapper.vue')['default']
-    DeviceInformation: typeof import('./src/tools/device-information/device-information.vue')['default']
-    DiffViewer: typeof import('./src/tools/json-diff/diff-viewer/diff-viewer.vue')['default']
-    DockerRunToDockerComposeConverter: typeof import('./src/tools/docker-run-to-docker-compose-converter/docker-run-to-docker-compose-converter.vue')['default']
-    DynamicValues: typeof import('./src/tools/benchmark-builder/dynamic-values.vue')['default']
-    Editor: typeof import('./src/tools/html-wysiwyg-editor/editor/editor.vue')['default']
-    EmojiCard: typeof import('./src/tools/emoji-picker/emoji-card.vue')['default']
-    EmojiGrid: typeof import('./src/tools/emoji-picker/emoji-grid.vue')['default']
-    EmojiPicker: typeof import('./src/tools/emoji-picker/emoji-picker.vue')['default']
-    Encryption: typeof import('./src/tools/encryption/encryption.vue')['default']
-    EtaCalculator: typeof import('./src/tools/eta-calculator/eta-calculator.vue')['default']
-    FavoriteButton: typeof import('./src/components/FavoriteButton.vue')['default']
-    FormatTransformer: typeof import('./src/components/FormatTransformer.vue')['default']
-    GitMemo: typeof import('./src/tools/git-memo/git-memo.vue')['default']
-    'GitMemo.content': typeof import('./src/tools/git-memo/git-memo.content.md')['default']
-    HashText: typeof import('./src/tools/hash-text/hash-text.vue')['default']
-    HmacGenerator: typeof import('./src/tools/hmac-generator/hmac-generator.vue')['default']
-    'Home.page': typeof import('./src/pages/Home.page.vue')['default']
-    HtmlEntities: typeof import('./src/tools/html-entities/html-entities.vue')['default']
-    HtmlWysiwygEditor: typeof import('./src/tools/html-wysiwyg-editor/html-wysiwyg-editor.vue')['default']
-    HttpStatusCodes: typeof import('./src/tools/http-status-codes/http-status-codes.vue')['default']
-    IbanValidatorAndParser: typeof import('./src/tools/iban-validator-and-parser/iban-validator-and-parser.vue')['default']
-    'IconMdi:brushVariant': typeof import('~icons/mdi/brush-variant')['default']
-    'IconMdi:contentCopy': typeof import('~icons/mdi/content-copy')['default']
-    'IconMdi:kettleSteamOutline': typeof import('~icons/mdi/kettle-steam-outline')['default']
-    IconMdiArrowRightBottom: typeof import('~icons/mdi/arrow-right-bottom')['default']
-    IconMdiChevronDown: typeof import('~icons/mdi/chevron-down')['default']
-    IconMdiChevronRight: typeof import('~icons/mdi/chevron-right')['default']
-    IconMdiClose: typeof import('~icons/mdi/close')['default']
-    IconMdiContentCopy: typeof import('~icons/mdi/content-copy')['default']
-    IconMdiEye: typeof import('~icons/mdi/eye')['default']
-    IconMdiEyeOff: typeof import('~icons/mdi/eye-off')['default']
-    IconMdiHeart: typeof import('~icons/mdi/heart')['default']
-    IconMdiSearch: typeof import('~icons/mdi/search')['default']
-    IconMdiTranslate: typeof import('~icons/mdi/translate')['default']
-    IconMdiTriangleDown: typeof import('~icons/mdi/triangle-down')['default']
-    InputCopyable: typeof import('./src/components/InputCopyable.vue')['default']
-    IntegerBaseConverter: typeof import('./src/tools/integer-base-converter/integer-base-converter.vue')['default']
-    Ipv4AddressConverter: typeof import('./src/tools/ipv4-address-converter/ipv4-address-converter.vue')['default']
-    Ipv4RangeExpander: typeof import('./src/tools/ipv4-range-expander/ipv4-range-expander.vue')['default']
-    Ipv4SubnetCalculator: typeof import('./src/tools/ipv4-subnet-calculator/ipv4-subnet-calculator.vue')['default']
-    Ipv6UlaGenerator: typeof import('./src/tools/ipv6-ula-generator/ipv6-ula-generator.vue')['default']
-    JsonDiff: typeof import('./src/tools/json-diff/json-diff.vue')['default']
-    JsonMinify: typeof import('./src/tools/json-minify/json-minify.vue')['default']
-    JsonToCsv: typeof import('./src/tools/json-to-csv/json-to-csv.vue')['default']
-    JsonToToml: typeof import('./src/tools/json-to-toml/json-to-toml.vue')['default']
-    JsonToYaml: typeof import('./src/tools/json-to-yaml-converter/json-to-yaml.vue')['default']
-    JsonViewer: typeof import('./src/tools/json-viewer/json-viewer.vue')['default']
-    JwtParser: typeof import('./src/tools/jwt-parser/jwt-parser.vue')['default']
-    KeycodeInfo: typeof import('./src/tools/keycode-info/keycode-info.vue')['default']
-    ListConverter: typeof import('./src/tools/list-converter/list-converter.vue')['default']
-    LocaleSelector: typeof import('./src/modules/i18n/components/locale-selector.vue')['default']
-    LoremIpsumGenerator: typeof import('./src/tools/lorem-ipsum-generator/lorem-ipsum-generator.vue')['default']
-    MacAddressGenerator: typeof import('./src/tools/mac-address-generator/mac-address-generator.vue')['default']
-    MacAddressLookup: typeof import('./src/tools/mac-address-lookup/mac-address-lookup.vue')['default']
-    MathEvaluator: typeof import('./src/tools/math-evaluator/math-evaluator.vue')['default']
-    MenuBar: typeof import('./src/tools/html-wysiwyg-editor/editor/menu-bar.vue')['default']
-    MenuBarItem: typeof import('./src/tools/html-wysiwyg-editor/editor/menu-bar-item.vue')['default']
-    MenuIconItem: typeof import('./src/components/MenuIconItem.vue')['default']
-    MenuLayout: typeof import('./src/components/MenuLayout.vue')['default']
-    MetaTagGenerator: typeof import('./src/tools/meta-tag-generator/meta-tag-generator.vue')['default']
-    MimeTypes: typeof import('./src/tools/mime-types/mime-types.vue')['default']
-    NAlert: typeof import('naive-ui')['NAlert']
-    NavbarButtons: typeof import('./src/components/NavbarButtons.vue')['default']
-    NCollapseTransition: typeof import('naive-ui')['NCollapseTransition']
-    NConfigProvider: typeof import('naive-ui')['NConfigProvider']
-    NDatePicker: typeof import('naive-ui')['NDatePicker']
-    NDivider: typeof import('naive-ui')['NDivider']
-    NEllipsis: typeof import('naive-ui')['NEllipsis']
-    NForm: typeof import('naive-ui')['NForm']
-    NFormItem: typeof import('naive-ui')['NFormItem']
-    NH1: typeof import('naive-ui')['NH1']
-    NH3: typeof import('naive-ui')['NH3']
-    NIcon: typeof import('naive-ui')['NIcon']
-    NInputGroup: typeof import('naive-ui')['NInputGroup']
-    NInputGroupLabel: typeof import('naive-ui')['NInputGroupLabel']
-    NInputNumber: typeof import('naive-ui')['NInputNumber']
-    NLayout: typeof import('naive-ui')['NLayout']
-    NLayoutSider: typeof import('naive-ui')['NLayoutSider']
-    NMenu: typeof import('naive-ui')['NMenu']
-    NStatistic: typeof import('naive-ui')['NStatistic']
-    NSwitch: typeof import('naive-ui')['NSwitch']
-    NTable: typeof import('naive-ui')['NTable']
-    NumeronymGenerator: typeof import('./src/tools/numeronym-generator/numeronym-generator.vue')['default']
-    OtpCodeGeneratorAndValidator: typeof import('./src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue')['default']
-    PasswordStrengthAnalyser: typeof import('./src/tools/password-strength-analyser/password-strength-analyser.vue')['default']
-    PdfSignatureChecker: typeof import('./src/tools/pdf-signature-checker/pdf-signature-checker.vue')['default']
-    PdfSignatureDetails: typeof import('./src/tools/pdf-signature-checker/components/pdf-signature-details.vue')['default']
-    PercentageCalculator: typeof import('./src/tools/percentage-calculator/percentage-calculator.vue')['default']
-    PhoneParserAndFormatter: typeof import('./src/tools/phone-parser-and-formatter/phone-parser-and-formatter.vue')['default']
-    QrCodeGenerator: typeof import('./src/tools/qr-code-generator/qr-code-generator.vue')['default']
-    RaidCalculator: typeof import('./src/tools/raid-calculator/raid-calculator.vue')['default']
-    RandomPortGenerator: typeof import('./src/tools/random-port-generator/random-port-generator.vue')['default']
-    ResultRow: typeof import('./src/tools/ipv4-range-expander/result-row.vue')['default']
-    RomanNumeralConverter: typeof import('./src/tools/roman-numeral-converter/roman-numeral-converter.vue')['default']
-    RouterLink: typeof import('vue-router')['RouterLink']
-    RouterView: typeof import('vue-router')['RouterView']
-    RsaKeyPairGenerator: typeof import('./src/tools/rsa-key-pair-generator/rsa-key-pair-generator.vue')['default']
-    SafelinkDecoder: typeof import('./src/tools/safelink-decoder/safelink-decoder.vue')['default']
-    SlugifyString: typeof import('./src/tools/slugify-string/slugify-string.vue')['default']
-    SpanCopyable: typeof import('./src/components/SpanCopyable.vue')['default']
-    SqlPrettify: typeof import('./src/tools/sql-prettify/sql-prettify.vue')['default']
-    StringObfuscator: typeof import('./src/tools/string-obfuscator/string-obfuscator.vue')['default']
-    SvgPlaceholderGenerator: typeof import('./src/tools/svg-placeholder-generator/svg-placeholder-generator.vue')['default']
-    TemperatureConverter: typeof import('./src/tools/temperature-converter/temperature-converter.vue')['default']
-    TextareaCopyable: typeof import('./src/components/TextareaCopyable.vue')['default']
-    TextDiff: typeof import('./src/tools/text-diff/text-diff.vue')['default']
-    TextStatistics: typeof import('./src/tools/text-statistics/text-statistics.vue')['default']
-    TextToBinary: typeof import('./src/tools/text-to-binary/text-to-binary.vue')['default']
-    TextToNatoAlphabet: typeof import('./src/tools/text-to-nato-alphabet/text-to-nato-alphabet.vue')['default']
-    TextToUnicode: typeof import('./src/tools/text-to-unicode/text-to-unicode.vue')['default']
-    TokenDisplay: typeof import('./src/tools/otp-code-generator-and-validator/token-display.vue')['default']
-    'TokenGenerator.tool': typeof import('./src/tools/token-generator/token-generator.tool.vue')['default']
-    TomlToJson: typeof import('./src/tools/toml-to-json/toml-to-json.vue')['default']
-    TomlToYaml: typeof import('./src/tools/toml-to-yaml/toml-to-yaml.vue')['default']
-    'Tool.layout': typeof import('./src/layouts/tool.layout.vue')['default']
-    ToolCard: typeof import('./src/components/ToolCard.vue')['default']
-    UlidGenerator: typeof import('./src/tools/ulid-generator/ulid-generator.vue')['default']
-    UrlEncoder: typeof import('./src/tools/url-encoder/url-encoder.vue')['default']
-    UrlParser: typeof import('./src/tools/url-parser/url-parser.vue')['default']
-    UserAgentParser: typeof import('./src/tools/user-agent-parser/user-agent-parser.vue')['default']
-    UserAgentResultCards: typeof import('./src/tools/user-agent-parser/user-agent-result-cards.vue')['default']
-    UuidGenerator: typeof import('./src/tools/uuid-generator/uuid-generator.vue')['default']
-    WifiQrCodeGenerator: typeof import('./src/tools/wifi-qr-code-generator/wifi-qr-code-generator.vue')['default']
-    XmlFormatter: typeof import('./src/tools/xml-formatter/xml-formatter.vue')['default']
-    YamlToJson: typeof import('./src/tools/yaml-to-json-converter/yaml-to-json.vue')['default']
-    YamlToToml: typeof import('./src/tools/yaml-to-toml/yaml-to-toml.vue')['default']
-    YamlViewer: typeof import('./src/tools/yaml-viewer/yaml-viewer.vue')['default']
-  }
-}
+/* eslint-disable */
+/* prettier-ignore */
+// @ts-nocheck
+// Generated by unplugin-vue-components
+// Read more: https://github.com/vuejs/core/pull/3399
+import '@vue/runtime-core'
+
+export {}
+
+declare module '@vue/runtime-core' {
+  export interface GlobalComponents {
+    '404.page': typeof import('./src/pages/404.page.vue')['default']
+    About: typeof import('./src/pages/About.vue')['default']
+    App: typeof import('./src/App.vue')['default']
+    AsciiTextDrawer: typeof import('./src/tools/ascii-text-drawer/ascii-text-drawer.vue')['default']
+    'Base.layout': typeof import('./src/layouts/base.layout.vue')['default']
+    Base64FileConverter: typeof import('./src/tools/base64-file-converter/base64-file-converter.vue')['default']
+    Base64StringConverter: typeof import('./src/tools/base64-string-converter/base64-string-converter.vue')['default']
+    BasicAuthGenerator: typeof import('./src/tools/basic-auth-generator/basic-auth-generator.vue')['default']
+    Bcrypt: typeof import('./src/tools/bcrypt/bcrypt.vue')['default']
+    BenchmarkBuilder: typeof import('./src/tools/benchmark-builder/benchmark-builder.vue')['default']
+    Bip39Generator: typeof import('./src/tools/bip39-generator/bip39-generator.vue')['default']
+    CAlert: typeof import('./src/ui/c-alert/c-alert.vue')['default']
+    'CAlert.demo': typeof import('./src/ui/c-alert/c-alert.demo.vue')['default']
+    CameraRecorder: typeof import('./src/tools/camera-recorder/camera-recorder.vue')['default']
+    CaseConverter: typeof import('./src/tools/case-converter/case-converter.vue')['default']
+    CButton: typeof import('./src/ui/c-button/c-button.vue')['default']
+    'CButton.demo': typeof import('./src/ui/c-button/c-button.demo.vue')['default']
+    CButtonsSelect: typeof import('./src/ui/c-buttons-select/c-buttons-select.vue')['default']
+    'CButtonsSelect.demo': typeof import('./src/ui/c-buttons-select/c-buttons-select.demo.vue')['default']
+    CCard: typeof import('./src/ui/c-card/c-card.vue')['default']
+    'CCard.demo': typeof import('./src/ui/c-card/c-card.demo.vue')['default']
+    CCollapse: typeof import('./src/ui/c-collapse/c-collapse.vue')['default']
+    'CCollapse.demo': typeof import('./src/ui/c-collapse/c-collapse.demo.vue')['default']
+    CDiffEditor: typeof import('./src/ui/c-diff-editor/c-diff-editor.vue')['default']
+    CFileUpload: typeof import('./src/ui/c-file-upload/c-file-upload.vue')['default']
+    'CFileUpload.demo': typeof import('./src/ui/c-file-upload/c-file-upload.demo.vue')['default']
+    ChmodCalculator: typeof import('./src/tools/chmod-calculator/chmod-calculator.vue')['default']
+    Chronometer: typeof import('./src/tools/chronometer/chronometer.vue')['default']
+    CInputText: typeof import('./src/ui/c-input-text/c-input-text.vue')['default']
+    'CInputText.demo': typeof import('./src/ui/c-input-text/c-input-text.demo.vue')['default']
+    CKeyValueList: typeof import('./src/ui/c-key-value-list/c-key-value-list.vue')['default']
+    CKeyValueListItem: typeof import('./src/ui/c-key-value-list/c-key-value-list-item.vue')['default']
+    CLabel: typeof import('./src/ui/c-label/c-label.vue')['default']
+    CLink: typeof import('./src/ui/c-link/c-link.vue')['default']
+    'CLink.demo': typeof import('./src/ui/c-link/c-link.demo.vue')['default']
+    CMarkdown: typeof import('./src/ui/c-markdown/c-markdown.vue')['default']
+    'CMarkdown.demo': typeof import('./src/ui/c-markdown/c-markdown.demo.vue')['default']
+    CModal: typeof import('./src/ui/c-modal/c-modal.vue')['default']
+    'CModal.demo': typeof import('./src/ui/c-modal/c-modal.demo.vue')['default']
+    CModalValue: typeof import('./src/ui/c-modal-value/c-modal-value.vue')['default']
+    'CModalValue.demo': typeof import('./src/ui/c-modal-value/c-modal-value.demo.vue')['default']
+    CollapsibleToolMenu: typeof import('./src/components/CollapsibleToolMenu.vue')['default']
+    ColorConverter: typeof import('./src/tools/color-converter/color-converter.vue')['default']
+    ColoredCard: typeof import('./src/components/ColoredCard.vue')['default']
+    CommandPalette: typeof import('./src/modules/command-palette/command-palette.vue')['default']
+    CommandPaletteOption: typeof import('./src/modules/command-palette/components/command-palette-option.vue')['default']
+    CrontabGenerator: typeof import('./src/tools/crontab-generator/crontab-generator.vue')['default']
+    CSelect: typeof import('./src/ui/c-select/c-select.vue')['default']
+    'CSelect.demo': typeof import('./src/ui/c-select/c-select.demo.vue')['default']
+    CTable: typeof import('./src/ui/c-table/c-table.vue')['default']
+    'CTable.demo': typeof import('./src/ui/c-table/c-table.demo.vue')['default']
+    CTextCopyable: typeof import('./src/ui/c-text-copyable/c-text-copyable.vue')['default']
+    'CTextCopyable.demo': typeof import('./src/ui/c-text-copyable/c-text-copyable.demo.vue')['default']
+    CTooltip: typeof import('./src/ui/c-tooltip/c-tooltip.vue')['default']
+    'CTooltip.demo': typeof import('./src/ui/c-tooltip/c-tooltip.demo.vue')['default']
+    DateTimeConverter: typeof import('./src/tools/date-time-converter/date-time-converter.vue')['default']
+    'DemoHome.page': typeof import('./src/ui/demo/demo-home.page.vue')['default']
+    DemoWrapper: typeof import('./src/ui/demo/demo-wrapper.vue')['default']
+    DeviceInformation: typeof import('./src/tools/device-information/device-information.vue')['default']
+    DiffViewer: typeof import('./src/tools/json-diff/diff-viewer/diff-viewer.vue')['default']
+    DockerRunToDockerComposeConverter: typeof import('./src/tools/docker-run-to-docker-compose-converter/docker-run-to-docker-compose-converter.vue')['default']
+    DynamicValues: typeof import('./src/tools/benchmark-builder/dynamic-values.vue')['default']
+    Editor: typeof import('./src/tools/html-wysiwyg-editor/editor/editor.vue')['default']
+    EmojiCard: typeof import('./src/tools/emoji-picker/emoji-card.vue')['default']
+    EmojiGrid: typeof import('./src/tools/emoji-picker/emoji-grid.vue')['default']
+    EmojiPicker: typeof import('./src/tools/emoji-picker/emoji-picker.vue')['default']
+    Encryption: typeof import('./src/tools/encryption/encryption.vue')['default']
+    EtaCalculator: typeof import('./src/tools/eta-calculator/eta-calculator.vue')['default']
+    FavoriteButton: typeof import('./src/components/FavoriteButton.vue')['default']
+    FormatTransformer: typeof import('./src/components/FormatTransformer.vue')['default']
+    GitMemo: typeof import('./src/tools/git-memo/git-memo.vue')['default']
+    'GitMemo.content': typeof import('./src/tools/git-memo/git-memo.content.md')['default']
+    HashText: typeof import('./src/tools/hash-text/hash-text.vue')['default']
+    HmacGenerator: typeof import('./src/tools/hmac-generator/hmac-generator.vue')['default']
+    'Home.page': typeof import('./src/pages/Home.page.vue')['default']
+    HtmlEntities: typeof import('./src/tools/html-entities/html-entities.vue')['default']
+    HtmlWysiwygEditor: typeof import('./src/tools/html-wysiwyg-editor/html-wysiwyg-editor.vue')['default']
+    HttpStatusCodes: typeof import('./src/tools/http-status-codes/http-status-codes.vue')['default']
+    IbanValidatorAndParser: typeof import('./src/tools/iban-validator-and-parser/iban-validator-and-parser.vue')['default']
+    'IconMdi:brushVariant': typeof import('~icons/mdi/brush-variant')['default']
+    'IconMdi:kettleSteamOutline': typeof import('~icons/mdi/kettle-steam-outline')['default']
+    IconMdiChevronDown: typeof import('~icons/mdi/chevron-down')['default']
+    IconMdiChevronRight: typeof import('~icons/mdi/chevron-right')['default']
+    IconMdiClose: typeof import('~icons/mdi/close')['default']
+    IconMdiContentCopy: typeof import('~icons/mdi/content-copy')['default']
+    IconMdiEye: typeof import('~icons/mdi/eye')['default']
+    IconMdiEyeOff: typeof import('~icons/mdi/eye-off')['default']
+    IconMdiHeart: typeof import('~icons/mdi/heart')['default']
+    IconMdiSearch: typeof import('~icons/mdi/search')['default']
+    IconMdiTranslate: typeof import('~icons/mdi/translate')['default']
+    IconMdiTriangleDown: typeof import('~icons/mdi/triangle-down')['default']
+    InputCopyable: typeof import('./src/components/InputCopyable.vue')['default']
+    IntegerBaseConverter: typeof import('./src/tools/integer-base-converter/integer-base-converter.vue')['default']
+    Ipv4AddressConverter: typeof import('./src/tools/ipv4-address-converter/ipv4-address-converter.vue')['default']
+    Ipv4RangeExpander: typeof import('./src/tools/ipv4-range-expander/ipv4-range-expander.vue')['default']
+    Ipv4SubnetCalculator: typeof import('./src/tools/ipv4-subnet-calculator/ipv4-subnet-calculator.vue')['default']
+    Ipv6UlaGenerator: typeof import('./src/tools/ipv6-ula-generator/ipv6-ula-generator.vue')['default']
+    JsonDiff: typeof import('./src/tools/json-diff/json-diff.vue')['default']
+    JsonMinify: typeof import('./src/tools/json-minify/json-minify.vue')['default']
+    JsonToCsv: typeof import('./src/tools/json-to-csv/json-to-csv.vue')['default']
+    JsonToToml: typeof import('./src/tools/json-to-toml/json-to-toml.vue')['default']
+    JsonToYaml: typeof import('./src/tools/json-to-yaml-converter/json-to-yaml.vue')['default']
+    JsonViewer: typeof import('./src/tools/json-viewer/json-viewer.vue')['default']
+    JwtParser: typeof import('./src/tools/jwt-parser/jwt-parser.vue')['default']
+    KeycodeInfo: typeof import('./src/tools/keycode-info/keycode-info.vue')['default']
+    ListConverter: typeof import('./src/tools/list-converter/list-converter.vue')['default']
+    LocaleSelector: typeof import('./src/modules/i18n/components/locale-selector.vue')['default']
+    LoremIpsumGenerator: typeof import('./src/tools/lorem-ipsum-generator/lorem-ipsum-generator.vue')['default']
+    MacAddressGenerator: typeof import('./src/tools/mac-address-generator/mac-address-generator.vue')['default']
+    MacAddressLookup: typeof import('./src/tools/mac-address-lookup/mac-address-lookup.vue')['default']
+    MathEvaluator: typeof import('./src/tools/math-evaluator/math-evaluator.vue')['default']
+    MenuBar: typeof import('./src/tools/html-wysiwyg-editor/editor/menu-bar.vue')['default']
+    MenuBarItem: typeof import('./src/tools/html-wysiwyg-editor/editor/menu-bar-item.vue')['default']
+    MenuIconItem: typeof import('./src/components/MenuIconItem.vue')['default']
+    MenuLayout: typeof import('./src/components/MenuLayout.vue')['default']
+    MetaTagGenerator: typeof import('./src/tools/meta-tag-generator/meta-tag-generator.vue')['default']
+    MimeTypes: typeof import('./src/tools/mime-types/mime-types.vue')['default']
+    NavbarButtons: typeof import('./src/components/NavbarButtons.vue')['default']
+    NCode: typeof import('naive-ui')['NCode']
+    NCollapseTransition: typeof import('naive-ui')['NCollapseTransition']
+    NConfigProvider: typeof import('naive-ui')['NConfigProvider']
+    NDivider: typeof import('naive-ui')['NDivider']
+    NEllipsis: typeof import('naive-ui')['NEllipsis']
+    NFormItem: typeof import('naive-ui')['NFormItem']
+    NGi: typeof import('naive-ui')['NGi']
+    NGrid: typeof import('naive-ui')['NGrid']
+    NH1: typeof import('naive-ui')['NH1']
+    NH3: typeof import('naive-ui')['NH3']
+    NIcon: typeof import('naive-ui')['NIcon']
+    NInputNumber: typeof import('naive-ui')['NInputNumber']
+    NLabel: typeof import('naive-ui')['NLabel']
+    NLayout: typeof import('naive-ui')['NLayout']
+    NLayoutSider: typeof import('naive-ui')['NLayoutSider']
+    NMenu: typeof import('naive-ui')['NMenu']
+    NScrollbar: typeof import('naive-ui')['NScrollbar']
+    NSpin: typeof import('naive-ui')['NSpin']
+    NumeronymGenerator: typeof import('./src/tools/numeronym-generator/numeronym-generator.vue')['default']
+    OtpCodeGeneratorAndValidator: typeof import('./src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue')['default']
+    PasswordStrengthAnalyser: typeof import('./src/tools/password-strength-analyser/password-strength-analyser.vue')['default']
+    PdfSignatureChecker: typeof import('./src/tools/pdf-signature-checker/pdf-signature-checker.vue')['default']
+    PdfSignatureDetails: typeof import('./src/tools/pdf-signature-checker/components/pdf-signature-details.vue')['default']
+    PercentageCalculator: typeof import('./src/tools/percentage-calculator/percentage-calculator.vue')['default']
+    PhoneParserAndFormatter: typeof import('./src/tools/phone-parser-and-formatter/phone-parser-and-formatter.vue')['default']
+    QrCodeGenerator: typeof import('./src/tools/qr-code-generator/qr-code-generator.vue')['default']
+    RandomPortGenerator: typeof import('./src/tools/random-port-generator/random-port-generator.vue')['default']
+    ResultRow: typeof import('./src/tools/ipv4-range-expander/result-row.vue')['default']
+    RomanNumeralConverter: typeof import('./src/tools/roman-numeral-converter/roman-numeral-converter.vue')['default']
+    RouterLink: typeof import('vue-router')['RouterLink']
+    RouterView: typeof import('vue-router')['RouterView']
+    RsaKeyPairGenerator: typeof import('./src/tools/rsa-key-pair-generator/rsa-key-pair-generator.vue')['default']
+    SafelinkDecoder: typeof import('./src/tools/safelink-decoder/safelink-decoder.vue')['default']
+    SlugifyString: typeof import('./src/tools/slugify-string/slugify-string.vue')['default']
+    SpanCopyable: typeof import('./src/components/SpanCopyable.vue')['default']
+    SqlPrettify: typeof import('./src/tools/sql-prettify/sql-prettify.vue')['default']
+    StringObfuscator: typeof import('./src/tools/string-obfuscator/string-obfuscator.vue')['default']
+    SvgPlaceholderGenerator: typeof import('./src/tools/svg-placeholder-generator/svg-placeholder-generator.vue')['default']
+    TemperatureConverter: typeof import('./src/tools/temperature-converter/temperature-converter.vue')['default']
+    TextareaCopyable: typeof import('./src/components/TextareaCopyable.vue')['default']
+    TextDiff: typeof import('./src/tools/text-diff/text-diff.vue')['default']
+    TextStatistics: typeof import('./src/tools/text-statistics/text-statistics.vue')['default']
+    TextToBinary: typeof import('./src/tools/text-to-binary/text-to-binary.vue')['default']
+    TextToNatoAlphabet: typeof import('./src/tools/text-to-nato-alphabet/text-to-nato-alphabet.vue')['default']
+    TextToUnicode: typeof import('./src/tools/text-to-unicode/text-to-unicode.vue')['default']
+    TokenDisplay: typeof import('./src/tools/otp-code-generator-and-validator/token-display.vue')['default']
+    'TokenGenerator.tool': typeof import('./src/tools/token-generator/token-generator.tool.vue')['default']
+    TomlToJson: typeof import('./src/tools/toml-to-json/toml-to-json.vue')['default']
+    TomlToYaml: typeof import('./src/tools/toml-to-yaml/toml-to-yaml.vue')['default']
+    'Tool.layout': typeof import('./src/layouts/tool.layout.vue')['default']
+    ToolCard: typeof import('./src/components/ToolCard.vue')['default']
+    UlidGenerator: typeof import('./src/tools/ulid-generator/ulid-generator.vue')['default']
+    UrlEncoder: typeof import('./src/tools/url-encoder/url-encoder.vue')['default']
+    UrlParser: typeof import('./src/tools/url-parser/url-parser.vue')['default']
+    UserAgentParser: typeof import('./src/tools/user-agent-parser/user-agent-parser.vue')['default']
+    UserAgentResultCards: typeof import('./src/tools/user-agent-parser/user-agent-result-cards.vue')['default']
+    UuidGenerator: typeof import('./src/tools/uuid-generator/uuid-generator.vue')['default']
+    WifiQrCodeGenerator: typeof import('./src/tools/wifi-qr-code-generator/wifi-qr-code-generator.vue')['default']
+    XmlFormatter: typeof import('./src/tools/xml-formatter/xml-formatter.vue')['default']
+    YamlToJson: typeof import('./src/tools/yaml-to-json-converter/yaml-to-json.vue')['default']
+    YamlToToml: typeof import('./src/tools/yaml-to-toml/yaml-to-toml.vue')['default']
+    YamlViewer: typeof import('./src/tools/yaml-viewer/yaml-viewer.vue')['default']
+  }
+}
From 32b2237e19015996bf1d0f7c421021e9be5f1fb6 Mon Sep 17 00:00:00 2001
From: Rob Weber 
Date: Wed, 31 Jul 2024 20:51:34 +0000
Subject: [PATCH 17/20] specify type and interface for map
---
 .../raid-calculator.service.ts                | 67 +++++++++++--------
 1 file changed, 38 insertions(+), 29 deletions(-)
diff --git a/src/tools/raid-calculator/raid-calculator.service.ts b/src/tools/raid-calculator/raid-calculator.service.ts
index 0428548c..f9b5809c 100644
--- a/src/tools/raid-calculator/raid-calculator.service.ts
+++ b/src/tools/raid-calculator/raid-calculator.service.ts
@@ -1,39 +1,48 @@
 export { raidCalculations };
 
-const raidCalculations = {
+interface RaidType {
+  about: string,
+  requirements: string,
+  validate(num: number, size: number, stripeSize: number): boolean,
+  capacity(num: number, size: number, stripeSize: number, unit: number): number,
+  efficiency(num: number, stripeSize: number): number,
+  fault(num: number, size: number, unit: number): string,
+}
+
+const raidCalculations: { [key: string]: RaidType } = {
   raid_0: {
     about: 'RAID 0 splits data evenly across 2 or more disks without redunancy or fault tolerance creating one large storage space. More info: Wikipedia',
     requirements: 'RAID 0 requires at least 1 disk',
-    validate(num, size, stripeSize) {
+    validate(num: number, size: number, stripeSize: number) {
       return num > 1;
     },
-    capacity(num, size, stripeSize, unit) {
+    capacity(num: number, size: number, stripeSize: number, unit: number) {
       // total disks * size
       return (num * size) * unit;
     },
-    efficiency(num, stripeSize) {
+    efficiency(num: number, stripeSize: number) {
       // uses 100% of space
       return 100;
     },
-    fault(num, size, unit) {
+    fault(num: number, size: number, unit: number) {
       return 'None';
     },
   },
   raid_1: {
     about: 'RAID 1 consists of an exact copy of the data (mirror) across two or more disks. The array will operate as long as at least one drive is operational.  More info: Wikipedia',
     requirements: 'RAID 1 requires at least 1 disk',
-    validate(num, size, stripeSize) {
+    validate(num: number, size: number, stripeSize: number) {
       return num > 1;
     },
-    capacity(num, size, stripeSize, unit) {
+    capacity(num: number, size: number, stripeSize: number, unit: number) {
       // total size is size of a single drive
       return size * unit;
     },
-    efficiency(num, stripeSize) {
+    efficiency(num: number, stripeSize: number) {
       // 1/N
       return (1 / num) * 100;
     },
-    fault(num, size, unit) {
+    fault(num: number, size: number, unit: number) {
       // FT = total - 1
       return `${num - 1} drive failures`;
     },
@@ -41,18 +50,18 @@ const raidCalculations = {
   raid_5: {
     about: 'RAID 5 uses block level striping with parity. This allows for fault tolerance with a storage reduction equal to one drive for the parity information. More info: Wikipedia',
     requirements: 'RAID 5 requires at least 3 disks',
-    validate(num, size, stripeSize) {
+    validate(num: number, size: number, stripeSize: number) {
       return num >= 3;
     },
-    capacity(num, size, stripeSize, unit) {
+    capacity(num: number, size: number, stripeSize: number, unit: number) {
       // (N-1) * S (one drive for parity)
       return ((num - 1) * size) * unit;
     },
-    efficiency(num, stripeSize) {
+    efficiency(num: number, stripeSize: number) {
       // 1 - (1/N)
       return (1 - (1 / num)) * 100;
     },
-    fault(num, size, unit) {
+    fault(num: number, size: number, unit: number) {
       // always 1 failure
       return '1 drive failure';
     },
@@ -60,18 +69,18 @@ const raidCalculations = {
   raid_6: {
     about: 'RAID 6 is similiar to RAID 5 but with an additional parity block. This allows for an additional disk failure at the cost of storage reduction equal to two drives. More info: Wikipedia',
     requirements: 'RAID 6 requires at least 4 disks',
-    validate(num, size, stripeSize) {
+    validate(num: number, size: number, stripeSize: number) {
       return num >= 4;
     },
-    capacity(num, size, stripeSize, unit) {
+    capacity(num: number, size: number, stripeSize: number, unit: number) {
       // (N-2) * S (2 parity)
       return ((num - 2) * size) * unit;
     },
-    efficiency(num, stripeSize) {
+    efficiency(num: number, stripeSize: number) {
       // 1 - (2/N)
       return (1 - (2 / num)) * 100;
     },
-    fault(num, size, unit) {
+    fault(num: number, size: number, unit: number) {
       // always 2 drive failures
       return '2 drive failures';
     },
@@ -79,18 +88,18 @@ const raidCalculations = {
   raid_10: {
     about: 'RAID 10 is a stripe of mirrors (RAID 1 + RAID 0). Each set of drives is mirrored and striped together so that each drive in the set is fault tolerant within the group. More info: Wikipedia',
     requirements: 'RAID 10 requires an even number of at least 4 disks',
-    validate(num, size, stripeSize) {
+    validate(num: number, size: number, stripeSize: number) {
       return num >= 4 && num % 2 === 0;
     },
-    capacity(num, size, stripeSize, unit) {
+    capacity(num: number, size: number, stripeSize: number, unit: number) {
       // Total disks (stripe)/2 (mirror)
       return ((num * size) / 2) * unit;
     },
-    efficiency(num, stripeSize) {
+    efficiency(num: number, stripeSize: number) {
       // 1/2 (1/strips per stripe, 2 in this case)
       return 50;
     },
-    fault(num, size, unit) {
+    fault(num: number, size: number, unit: number) {
       // one per mirror
       return '1 drive failure per mirrored set';
     },
@@ -98,21 +107,21 @@ const raidCalculations = {
   raid_50: {
     about: 'RAID 50 stripes multiple RAID 5 arrays together (RAID 5 + RAID 0). Each RAID 5 set can sustain a single drive failure. More info: Wikipedia',
     requirements: 'RAID 50 requires at least 6 disks with 3 minimum per stripe. Stripes must contain an equal number of disks.',
-    validate(num, size, stripeSize) {
+    validate(num: number, size: number, stripeSize: number) {
       return num >= 6 && stripeSize >= 3 && num % stripeSize === 0;
     },
-    capacity(num, size, stripeSize, unit) {
+    capacity(num: number, size: number, stripeSize: number, unit: number) {
       // RAID 5 per stripe
       const perStripe = ((stripeSize - 1) * size) * unit;
 
       // sum each stripe
       return perStripe * (num / stripeSize);
     },
-    efficiency(num, stripeSize) {
+    efficiency(num: number, stripeSize: number) {
       // 1 - (1 / strips per stripe)
       return (1 - (1 / stripeSize)) * 100;
     },
-    fault(num, size, unit) {
+    fault(num: number, size: number, unit: number) {
       // one per set
       return '1 drive failure per RAID 5 set';
     },
@@ -120,21 +129,21 @@ const raidCalculations = {
   raid_60: {
     about: 'RAID 60 stripes multiple RAID 6 arrays together (RAID 6 + RAID 0). Each RAID 6 set can sustain a two drive failures. More info: Wikipedia',
     requirements: 'RAID 50 requires at least 8 disks with 4 minimum per stripe. Stripes must contain an equal number of disks.',
-    validate(num, size, stripeSize) {
+    validate(num: number, size: number, stripeSize: number) {
       return num >= 8 && stripeSize >= 4 && num % stripeSize === 0;
     },
-    capacity(num, size, stripeSize, unit) {
+    capacity(num: number, size: number, stripeSize: number, unit: number) {
       // RAID 6 per stripe
       const perStripe = ((stripeSize - 2) * size) * unit;
 
       // sum each stripe
       return perStripe * (num / stripeSize);
     },
-    efficiency(num, stripeSize) {
+    efficiency(num: number, stripeSize: number) {
       // 1 - (2 / strips per stripe)
       return (1 - (2 / stripeSize)) * 100;
     },
-    fault(num, size, unit) {
+    fault(num: number, size: number, unit: number) {
       // 2 per set
       return '2 drive failures per RAID 6 set';
     },
From 3f9d3995a06b60a09f876b55443d72968212b6a2 Mon Sep 17 00:00:00 2001
From: Rob Weber 
Date: Wed, 31 Jul 2024 20:55:48 +0000
Subject: [PATCH 18/20] removed raw html and set link static
---
 .../raid-calculator/raid-calculator.service.ts   | 16 ++++++++--------
 src/tools/raid-calculator/raid-calculator.vue    |  5 ++++-
 2 files changed, 12 insertions(+), 9 deletions(-)
diff --git a/src/tools/raid-calculator/raid-calculator.service.ts b/src/tools/raid-calculator/raid-calculator.service.ts
index f9b5809c..9cb0d8a5 100644
--- a/src/tools/raid-calculator/raid-calculator.service.ts
+++ b/src/tools/raid-calculator/raid-calculator.service.ts
@@ -11,7 +11,7 @@ interface RaidType {
 
 const raidCalculations: { [key: string]: RaidType } = {
   raid_0: {
-    about: 'RAID 0 splits data evenly across 2 or more disks without redunancy or fault tolerance creating one large storage space. More info: Wikipedia',
+    about: 'RAID 0 splits data evenly across 2 or more disks without redunancy or fault tolerance creating one large storage space.',
     requirements: 'RAID 0 requires at least 1 disk',
     validate(num: number, size: number, stripeSize: number) {
       return num > 1;
@@ -29,7 +29,7 @@ const raidCalculations: { [key: string]: RaidType } = {
     },
   },
   raid_1: {
-    about: 'RAID 1 consists of an exact copy of the data (mirror) across two or more disks. The array will operate as long as at least one drive is operational.  More info: Wikipedia',
+    about: 'RAID 1 consists of an exact copy of the data (mirror) across two or more disks. The array will operate as long as at least one drive is operational.',
     requirements: 'RAID 1 requires at least 1 disk',
     validate(num: number, size: number, stripeSize: number) {
       return num > 1;
@@ -48,7 +48,7 @@ const raidCalculations: { [key: string]: RaidType } = {
     },
   },
   raid_5: {
-    about: 'RAID 5 uses block level striping with parity. This allows for fault tolerance with a storage reduction equal to one drive for the parity information. More info: Wikipedia',
+    about: 'RAID 5 uses block level striping with parity. This allows for fault tolerance with a storage reduction equal to one drive for the parity information.',
     requirements: 'RAID 5 requires at least 3 disks',
     validate(num: number, size: number, stripeSize: number) {
       return num >= 3;
@@ -67,7 +67,7 @@ const raidCalculations: { [key: string]: RaidType } = {
     },
   },
   raid_6: {
-    about: 'RAID 6 is similiar to RAID 5 but with an additional parity block. This allows for an additional disk failure at the cost of storage reduction equal to two drives. More info: Wikipedia',
+    about: 'RAID 6 is similiar to RAID 5 but with an additional parity block. This allows for an additional disk failure at the cost of storage reduction equal to two drives.',
     requirements: 'RAID 6 requires at least 4 disks',
     validate(num: number, size: number, stripeSize: number) {
       return num >= 4;
@@ -86,7 +86,7 @@ const raidCalculations: { [key: string]: RaidType } = {
     },
   },
   raid_10: {
-    about: 'RAID 10 is a stripe of mirrors (RAID 1 + RAID 0). Each set of drives is mirrored and striped together so that each drive in the set is fault tolerant within the group. More info: Wikipedia',
+    about: 'RAID 10 is a stripe of mirrors (RAID 1 + RAID 0). Each set of drives is mirrored and striped together so that each drive in the set is fault tolerant within the group.',
     requirements: 'RAID 10 requires an even number of at least 4 disks',
     validate(num: number, size: number, stripeSize: number) {
       return num >= 4 && num % 2 === 0;
@@ -105,7 +105,7 @@ const raidCalculations: { [key: string]: RaidType } = {
     },
   },
   raid_50: {
-    about: 'RAID 50 stripes multiple RAID 5 arrays together (RAID 5 + RAID 0). Each RAID 5 set can sustain a single drive failure. More info: Wikipedia',
+    about: 'RAID 50 stripes multiple RAID 5 arrays together (RAID 5 + RAID 0). Each RAID 5 set can sustain a single drive failure.',
     requirements: 'RAID 50 requires at least 6 disks with 3 minimum per stripe. Stripes must contain an equal number of disks.',
     validate(num: number, size: number, stripeSize: number) {
       return num >= 6 && stripeSize >= 3 && num % stripeSize === 0;
@@ -127,8 +127,8 @@ const raidCalculations: { [key: string]: RaidType } = {
     },
   },
   raid_60: {
-    about: 'RAID 60 stripes multiple RAID 6 arrays together (RAID 6 + RAID 0). Each RAID 6 set can sustain a two drive failures. More info: Wikipedia',
-    requirements: 'RAID 50 requires at least 8 disks with 4 minimum per stripe. Stripes must contain an equal number of disks.',
+    about: 'RAID 60 stripes multiple RAID 6 arrays together (RAID 6 + RAID 0). Each RAID 6 set can sustain a two drive failures.',
+    requirements: 'RAID 60 requires at least 8 disks with 4 minimum per stripe. Stripes must contain an equal number of disks.',
     validate(num: number, size: number, stripeSize: number) {
       return num >= 8 && stripeSize >= 4 && num % stripeSize === 0;
     },
diff --git a/src/tools/raid-calculator/raid-calculator.vue b/src/tools/raid-calculator/raid-calculator.vue
index 1a6c5604..0c6438a5 100644
--- a/src/tools/raid-calculator/raid-calculator.vue
+++ b/src/tools/raid-calculator/raid-calculator.vue
@@ -83,7 +83,10 @@ function validateSetup() {
       
         {{ raidRequirements }}
       
-      
+      
+        {{ raidInfo }}
+        For more information on RAID types, see Wikipedia.
+      
     
     
       
From 24fca687663519a1c63343358d10f22e2120bb98 Mon Sep 17 00:00:00 2001
From: Rob Weber 
Date: Wed, 31 Jul 2024 20:59:08 +0000
Subject: [PATCH 19/20] syntax fixes
---
 src/tools/raid-calculator/raid-calculator.service.ts | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/tools/raid-calculator/raid-calculator.service.ts b/src/tools/raid-calculator/raid-calculator.service.ts
index 9cb0d8a5..95ea9712 100644
--- a/src/tools/raid-calculator/raid-calculator.service.ts
+++ b/src/tools/raid-calculator/raid-calculator.service.ts
@@ -1,12 +1,12 @@
 export { raidCalculations };
 
 interface RaidType {
-  about: string,
-  requirements: string,
-  validate(num: number, size: number, stripeSize: number): boolean,
-  capacity(num: number, size: number, stripeSize: number, unit: number): number,
-  efficiency(num: number, stripeSize: number): number,
-  fault(num: number, size: number, unit: number): string,
+  about: string
+  requirements: string
+  validate(num: number, size: number, stripeSize: number): boolean
+  capacity(num: number, size: number, stripeSize: number, unit: number): number
+  efficiency(num: number, stripeSize: number): number
+  fault(num: number, size: number, unit: number): string
 }
 
 const raidCalculations: { [key: string]: RaidType } = {
From 89e8184ce731c7ece7f62ab9f6261fd7a69ac79f Mon Sep 17 00:00:00 2001
From: Rob Weber 
Date: Thu, 1 Aug 2024 18:34:17 +0000
Subject: [PATCH 20/20] added noopener per security check
---
 src/tools/raid-calculator/raid-calculator.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/tools/raid-calculator/raid-calculator.vue b/src/tools/raid-calculator/raid-calculator.vue
index 0c6438a5..bf9ee31c 100644
--- a/src/tools/raid-calculator/raid-calculator.vue
+++ b/src/tools/raid-calculator/raid-calculator.vue
@@ -85,7 +85,7 @@ function validateSetup() {
       
       
         {{ raidInfo }}
-        For more information on RAID types, see Wikipedia.
+        For more information on RAID types, see Wikipedia.