diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..17ba301
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,14 @@
+
+
+root = true
+
+
+[*]
+insert_final_newline = true
+end_of_line = lf
+
+
+[*.{md,js,css,html}]
+indent_style = space
+indent_size = 4
+charset = utf-8
diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml
new file mode 100644
index 0000000..f218fe2
--- /dev/null
+++ b/.github/workflows/static.yml
@@ -0,0 +1,53 @@
+
+name : Deploy the visualization page to GitHub Pages
+
+
+# Only rebuild the page if something
+# changes in the /Source/ folder.
+
+on:
+ workflow_dispatch: # Allow manual starting
+ push:
+ branches:
+ - 'main'
+
+ paths:
+ - 'Source/**'
+
+
+permissions:
+ id-token : write
+ contents : read
+ pages : write
+
+
+concurrency:
+ cancel-in-progress : true
+ group : 'pages'
+
+
+jobs:
+ deploy:
+
+ environment:
+ name : github-pages
+ url : ${{ steps.deployment.outputs.page_url }}
+
+ runs-on : ubuntu-latest
+
+ steps:
+
+ - name : Checkout
+ uses : actions/checkout@v3
+
+ - name : Setup Pages
+ uses : actions/configure-pages@v2
+
+ - name : Upload artifact
+ uses : actions/upload-pages-artifact@v1
+ with :
+ path : 'Source/'
+
+ - name : Deploy to GitHub Pages
+ uses : actions/deploy-pages@v1
+ id : deployment
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..8609668
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 David Kobalia
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..8ef8625
--- /dev/null
+++ b/README.md
@@ -0,0 +1,62 @@
+
+
+
+
+
+# Sorting Algorithms
+
+*Visualization of a variety of sorting algorithms.*
+
+
+
+
+[![Button Website]][Website]
+
+
+
+
+
+
+## Algorithms
+
+*List of algorithms used on the page.*
+
+- **[Insertion Sort]**
+
+- **[Selection Sort]**
+
+- **[Counting Sort]**
+
+- **[Cocktail Sort]**
+
+- **[Bubble Sort]**
+
+- **[Gnome Sort]**
+
+- **[Quick Sort]**
+
+- **[Heap Sort]**
+
+- **[Bogo Sort] a.k.a. Monkey Sort**
+
+
+
+
+
+
+[Website]: https://Insuetus.github.io/sorting_algos
+
+[Insertion Sort]: https://en.wikipedia.org/wiki/Insertion_sort
+[Selection Sort]: https://en.wikipedia.org/wiki/Selection_sort
+[Counting Sort]: https://en.wikipedia.org/wiki/Counting_sort
+[Cocktail Sort]: https://en.wikipedia.org/wiki/Cocktail_shaker_sort
+[Bubble Sort]: https://en.wikipedia.org/wiki/Bubble_sort
+[Gnome Sort]: https://en.wikipedia.org/wiki/Gnome_sort
+[Quick Sort]: https://en.wikipedia.org/wiki/Quicksort
+[Heap Sort]: https://en.wikipedia.org/wiki/Heapsort
+[Bogo Sort]: https://en.wikipedia.org/wiki/Bogosort
+
+
+
+
+[Button Website]: https://img.shields.io/badge/Website-7D929E?style=for-the-badge&logoColor=white&logo=ApacheCouchDB
diff --git a/Source/.DS_Store b/Source/.DS_Store
new file mode 100644
index 0000000..d137607
Binary files /dev/null and b/Source/.DS_Store differ
diff --git a/Source/Algorithms/BubbleSort.js b/Source/Algorithms/BubbleSort.js
new file mode 100644
index 0000000..a00b017
--- /dev/null
+++ b/Source/Algorithms/BubbleSort.js
@@ -0,0 +1,30 @@
+
+import { Sorted, Unsorted, Alpha, Beta } from 'Colors'
+
+
+export default function* (size, items) {
+
+ for (let i = 0; i < size - 1; i++) {
+
+ for (let j = 0; j < size - i - 1; j++) {
+
+ yield [Alpha, j]
+ yield [Beta, j + 1]
+
+ if (items[j] > items[j + 1]) {
+
+ [items[j], items[j + 1]] = [items[j + 1], items[j]];
+
+ yield [Beta, j]
+ yield [Alpha, j + 1]
+ }
+
+ yield [Unsorted, j]
+ yield [Unsorted, j + 1]
+ }
+
+ yield [Sorted, size - 1 - i]
+ }
+
+ yield [Sorted, 0]
+}
diff --git a/Source/Algorithms/CocktailSort.js b/Source/Algorithms/CocktailSort.js
new file mode 100644
index 0000000..9cdbf92
--- /dev/null
+++ b/Source/Algorithms/CocktailSort.js
@@ -0,0 +1,66 @@
+
+import { Sorted, Unsorted, Alpha, Beta } from 'Colors'
+
+
+export default function* (size, items) {
+
+ let
+ swapped = true,
+ lsize = size,
+ index = 0;
+
+ while (swapped) {
+
+ swapped = false;
+
+ for (let i = index; i < lsize - 1; ++i) {
+
+ yield [Alpha, i]
+ yield [Beta, i + 1]
+
+ if (items[i] > items[i + 1]) {
+
+ [items[i], items[i + 1]] = [items[i + 1], items[i]];
+
+ yield [Beta, i]
+ yield [Alpha, i + 1]
+
+ swapped = true;
+ }
+
+ yield [Unsorted, i]
+ yield [Unsorted, i + 1]
+ }
+
+ swapped = false;
+ lsize--;
+
+ for (let i = lsize - 1; i >= index; i--) {
+
+ yield [Alpha, i]
+ yield [Beta, i + 1]
+
+ if (items[i] > items[i + 1]) {
+
+ [items[i], items[i + 1]] = [items[i + 1], items[i]];
+
+ yield [Beta, i]
+ yield [Alpha, i + 1]
+
+ swapped = true;
+ }
+
+ yield [Unsorted, i]
+ yield [Unsorted, i + 1]
+ }
+
+ yield [Sorted, index]
+
+ index++
+
+ yield [Sorted, lsize]
+ }
+
+ for (let i = 0; i < size; i++)
+ yield [Sorted, i]
+}
diff --git a/Source/Algorithms/CountingSort.js b/Source/Algorithms/CountingSort.js
new file mode 100644
index 0000000..18a1e91
--- /dev/null
+++ b/Source/Algorithms/CountingSort.js
@@ -0,0 +1,51 @@
+
+import { Sorted, Unsorted, Alpha, Beta } from 'Colors'
+
+
+export default function* (size, items) {
+ const counter = {}
+ let max = -1,
+ max_index = 0,
+ min = 10e6,
+ min_index = 0;
+
+ for (let i = 0; i < size; i++) {
+ yield [Alpha, i];
+ if (items[i] > max) {
+ max = items[i];
+ max_index = i;
+ }
+ if (items[i] < min) {
+ min = items[i];
+ min_index = i;
+ }
+ if (items[i] in counter)
+ counter[items[i]].push(i);
+ else
+ counter[items[i]] = [i];
+ }
+
+ yield [Beta, max_index]
+ yield [Beta, min_index]
+ let c = 0,
+ replaced_index,
+ old_items = [...items];
+ for (let i = min; i <= max; i++) {
+ if (!(i in counter)) continue;
+ while (counter[i].length) {
+ replaced_index = counter[i].pop();
+ yield [Alpha, c];
+ yield [Beta, replaced_index];
+ yield [Unsorted, c];
+ yield [Unsorted, replaced_index];
+ [items[c], old_items[replaced_index]] = [old_items[replaced_index], items[c]];
+ yield [Beta, replaced_index];
+ yield [Sorted, c++];
+ }
+ }
+
+ for (let i = 0; i < size; i++) {
+ yield [Sorted, i];
+ }
+
+}
diff --git a/Source/Algorithms/GnomeSort.js b/Source/Algorithms/GnomeSort.js
new file mode 100644
index 0000000..342a36b
--- /dev/null
+++ b/Source/Algorithms/GnomeSort.js
@@ -0,0 +1,28 @@
+
+import { Sorted, Unsorted, Alpha, Beta } from 'Colors'
+
+
+export default function* (size, items) {
+
+ let index = 0;
+
+ while (index < size) {
+
+ if (items[index] >= items[index - 1] || index == 0) {
+
+ yield [Alpha, index]
+ yield [Sorted, index]
+
+ index++;
+
+ } else {
+
+ [items[index], items[index - 1]] = [items[index - 1], items[index]];
+
+ yield [Beta, index]
+ yield [Unsorted, index + 1]
+
+ index--;
+ }
+ }
+}
diff --git a/Source/Algorithms/HeapSort.js b/Source/Algorithms/HeapSort.js
new file mode 100644
index 0000000..4ce776e
--- /dev/null
+++ b/Source/Algorithms/HeapSort.js
@@ -0,0 +1,70 @@
+
+import { Sorted , Unsorted , Alpha , Beta } from 'Colors'
+
+const { floor } = Math;
+
+
+export default function * ( size , items ){
+
+ for ( let i = 0 ; i < size ; i++ )
+ yield * heap_up(items,i);
+
+ for ( let i = 0 ; i < size - 1 ; i++){
+
+ let last = size - 1 - i;
+
+ [ items[0] , items[last] ] = [ items[last] , items[0] ];
+
+ yield [ Sorted , 0 ]
+ yield [ Sorted , last ]
+
+ yield * heap_down(items,last);
+ }
+}
+
+
+function * heap_up ( items , i ){
+
+ let root = floor((i - 1) / 2);
+
+ while ( i > 0 && items[root] < items[i]){
+
+ [ items[i] , items[root] ] = [ items[root] , items[i] ];
+
+ yield [ Alpha , i ]
+ yield [ Beta , root ]
+ yield [ Unsorted , i ]
+ yield [ Unsorted , root ]
+
+ i = root;
+
+ root = floor((i - 1) / 2);
+ }
+
+ yield [ Unsorted , i ]
+}
+
+function * heap_down ( items , size ){
+
+ let i = 0;
+
+ while ( 2 * i + 1 < size ){
+
+ let child = 2 * i + 1;
+
+ if(2 * i + 2 < size && items[2 * i + 2] >= items[child])
+ child = 2 * i + 2;
+
+ yield [ Alpha , i ]
+ yield [ Beta , child ]
+ yield [ Unsorted , i ]
+ yield [ Unsorted , child ]
+
+ if(items[i] >= items[child])
+ return
+
+ [ items[i] , items[child] ] = [ items[child] , items[i] ];
+
+ i = child;
+ }
+}
diff --git a/Source/Algorithms/InsertionSort.js b/Source/Algorithms/InsertionSort.js
new file mode 100644
index 0000000..b547ff0
--- /dev/null
+++ b/Source/Algorithms/InsertionSort.js
@@ -0,0 +1,32 @@
+
+import { Sorted, Alpha, Beta } from 'Colors'
+
+
+export default function* (size, items) {
+
+ for (let i = 0; i < size; i++) {
+
+ let temp = items[i];
+
+ yield [Beta, i]
+
+ let j = i - 1;
+
+ for (j = i - 1; j >= 0 && items[j] > temp; j--) {
+
+ items[j + 1] = items[j];
+
+ yield [Alpha, j]
+ yield [Beta, j + 1]
+ yield [Sorted, j + 1]
+ yield [Sorted, j]
+ }
+
+ items[j + 1] = temp;
+
+ yield [Beta, i]
+ yield [Sorted, i]
+ yield [Beta, j + 1]
+ yield [Sorted, j + 1]
+ }
+}
diff --git a/Source/Algorithms/MonkeySort.js b/Source/Algorithms/MonkeySort.js
new file mode 100644
index 0000000..05f22cf
--- /dev/null
+++ b/Source/Algorithms/MonkeySort.js
@@ -0,0 +1,26 @@
+
+import { Sorted, Alpha, Beta } from 'Colors'
+const { floor, random } = Math
+
+export default function* (size, items) {
+ while (!items.slice(1).every((item, i) => items[i] <= item)) {
+ yield* shuffle(size, items)
+ }
+ yield* colorize_bars(size, Alpha)
+ yield* colorize_bars(size, Beta)
+ yield* colorize_bars(size, Sorted)
+}
+
+function* shuffle(size, items) {
+ for (let i = 0; i < size; i++) {
+ var random_index = floor(random() * size);
+ [items[i], items[random_index]] = [items[random_index], items[i]];
+ yield [Alpha, i]
+ yield [Beta, random_index]
+ }
+}
+
+function* colorize_bars(size, color) {
+ for (let i = 0; i < size; i++)
+ yield [color, i]
+}
diff --git a/Source/Algorithms/QuickSort.js b/Source/Algorithms/QuickSort.js
new file mode 100644
index 0000000..c7fb1b9
--- /dev/null
+++ b/Source/Algorithms/QuickSort.js
@@ -0,0 +1,51 @@
+
+import { Sorted, Unsorted, Alpha, Beta } from 'Colors'
+
+
+export default function* sort(size, items, start, end) {
+
+ if (start > end) {
+ yield [Sorted, start]
+ return
+ }
+
+ if (start == end) {
+ yield [Sorted, start]
+ return
+ }
+
+ let pivot = items[start],
+ tail = end + 1,
+ head = start;
+
+ while (head < tail) {
+
+ do {
+
+ yield [Alpha, head]
+ yield [Unsorted, head]
+
+ head++;
+
+ } while (items[head] <= pivot);
+
+ do {
+
+ tail--;
+
+ yield [Beta, tail]
+ yield [Unsorted, tail]
+
+ } while (items[tail] > pivot);
+
+ if (head < tail)
+ [items[head], items[tail]] = [items[tail], items[head]];
+ }
+
+ [items[start], items[tail]] = [items[tail], items[start]];
+
+ yield [Sorted, tail]
+
+ yield* sort(size, items, start, tail - 1);
+ yield* sort(size, items, tail + 1, end);
+}
diff --git a/Source/Algorithms/SelectionSort.js b/Source/Algorithms/SelectionSort.js
new file mode 100644
index 0000000..d61fc0e
--- /dev/null
+++ b/Source/Algorithms/SelectionSort.js
@@ -0,0 +1,30 @@
+
+import { Sorted, Unsorted, Alpha } from 'Colors'
+
+
+export default function* (size, items) {
+
+ for (let i = 0; i < size - 1; i++) {
+
+ let min = i;
+
+ for (let j = size - 1; j > i; j--) {
+
+ yield [Alpha, j]
+
+ if (items[j] < items[min])
+ min = j;
+
+ yield [Unsorted, j]
+ }
+
+ [items[i], items[min]] = [items[min], items[i]];
+
+ yield [Sorted, i]
+
+ if (min != i)
+ yield [Unsorted, min]
+ }
+
+ yield [Sorted, size - 1]
+}
diff --git a/Source/Algorithms/mod.js b/Source/Algorithms/mod.js
new file mode 100644
index 0000000..718fbd7
--- /dev/null
+++ b/Source/Algorithms/mod.js
@@ -0,0 +1,10 @@
+
+export { default as 'Selection Sort' } from './SelectionSort.js'
+export { default as 'Insertion Sort' } from './InsertionSort.js'
+export { default as 'Cocktail Sort' } from './CocktailSort.js'
+export { default as 'Bubble Sort' } from './BubbleSort.js'
+export { default as 'Gnome Sort' } from './GnomeSort.js'
+export { default as 'Quick Sort' } from './QuickSort.js'
+export { default as 'Heap Sort' } from './HeapSort.js'
+export { default as 'Counting Sort' } from './CountingSort.js'
+export { default as 'Monkey Sort' } from './MonkeySort.js'
diff --git a/Source/App.js b/Source/App.js
new file mode 100644
index 0000000..4f93f4b
--- /dev/null
+++ b/Source/App.js
@@ -0,0 +1,218 @@
+
+import { queryAll, query, create, byId } from 'Document'
+import progressAnimation from 'Progress'
+import * as Algorithms from 'Algorithms'
+
+
+const { random, floor } = Math;
+
+
+const randomInt = (minimum, maximum) =>
+ floor(random() * (maximum - minimum + 1) + minimum);
+
+const sleep = (millis) =>
+ new Promise((resolve) => setTimeout(resolve, millis));
+
+
+let
+ sorting_progress = 0,
+ sortingProcess,
+ algorithm = 'Bubble Sort',
+ animation,
+ cancel = false,
+ values = [],
+ bars = [],
+ size = 35,
+ time = 0;
+
+
+const delayFrom = (factor) =>
+ 10000 / (floor(size / 10) * factor);
+
+let delay = delayFrom(500);
+
+
+const algorithmSelection =
+ queryAll('.dropdown-menu > li');
+
+const activeSelection =
+ byId('nav-menu');
+
+const button_randomize =
+ query('.random-array');
+
+const slider_speed =
+ byId('speed');
+
+const slider_size =
+ byId('size');
+
+const button_sort =
+ byId('SORT');
+
+const list_bars =
+ query('.BARS');
+
+
+
+function onAlgorithmSelect(event) {
+
+ const { target } = event;
+
+ target.swapTextWith(activeSelection);
+
+ algorithm = activeSelection.innerText;
+}
+
+function onSizeChange(event) {
+ size = event.target.value;
+ randomizeValues();
+}
+
+function onSpeedChange(event) {
+
+ const { value } = event.target;
+
+ delay = delayFrom(value);
+}
+
+
+
+function animateSorting() {
+
+ const steps = progressAnimation();
+
+ const animate = () =>
+ button_sort.innerText = steps.next().value;
+
+ animation = setInterval(animate, 500);
+}
+
+
+async function randomizeValues() {
+
+ cancel = true;
+
+ await sortingProcess;
+
+ clearInterval(animation);
+
+ disableNavigation(false);
+
+ sorting_progress = 0;
+ time = 0;
+
+ list_bars.innerHTML = '';
+ button_sort.innerText = 'Sort';
+
+ values = [];
+ bars = [];
+
+ prepareBars();
+}
+
+
+function prepareBars() {
+
+ for (let i = 0; i < size; i++)
+ generateBar();
+
+ const stable = create('div');
+ stable.classList.add('stable');
+ list_bars.appendChild(stable);
+}
+
+
+function generateBar() {
+
+ const
+ value = randomInt(50, 500),
+ bar = create('div');
+
+ const { style } = bar;
+
+ style.height = `${value}px`;
+ style.width = `${60 / size}%`;
+
+ list_bars.appendChild(bar);
+ values.push(value);
+ bars.push(bar);
+}
+
+async function visualize(index, color) {
+
+ const [value, bar] = [values[index], bars[index]];
+
+ if (bar) {
+
+ await sleep(delay);
+
+ const { style } = bar;
+
+ style.backgroundColor = color;
+ style.height = `${value}px`;
+ }
+}
+
+
+function disableNavigation(state) {
+ activeSelection.disabled = state;
+ button_sort.disabled = state;
+ slider_size.disabled = state;
+}
+
+
+function onStartSorting() {
+ sortingProcess = sort();
+}
+
+async function sort() {
+
+ sorting_progress = 1;
+ cancel = false;
+
+ disableNavigation(true);
+ animateSorting();
+
+ const parameters = [size, values, 0, size - 1]
+
+ const process = Algorithms[algorithm](...parameters);
+
+ for (const [color, index] of process) {
+
+ await visualize(index, color);
+
+ if (cancel)
+ break;
+ }
+
+ disableNavigation(false);
+
+ clearInterval(animation);
+
+ button_sort.innerText = 'Sort';
+
+ sorting_progress = 0;
+ time = 0;
+}
+
+
+
+for (const choice of algorithmSelection)
+ choice.addEventListener('click', onAlgorithmSelect);
+
+button_randomize
+ .addEventListener('click', randomizeValues);
+
+slider_speed
+ .addEventListener('input', onSpeedChange);
+
+button_sort
+ .addEventListener('click', onStartSorting);
+
+slider_size
+ .addEventListener('input', onSizeChange);
+
+
+
+randomizeValues();
diff --git a/Source/Misc/Document.js b/Source/Misc/Document.js
new file mode 100644
index 0000000..6357f18
--- /dev/null
+++ b/Source/Misc/Document.js
@@ -0,0 +1,13 @@
+
+
+export const queryAll = (selector) =>
+ document.querySelectorAll(selector);
+
+export const query = (selector) =>
+ document.querySelector(selector);
+
+export const create = (type) =>
+ document.createElement(type);
+
+export const byId = (id) =>
+ document.getElementById(id);
diff --git a/Source/Misc/Extensions.js b/Source/Misc/Extensions.js
new file mode 100644
index 0000000..9fa65a3
--- /dev/null
+++ b/Source/Misc/Extensions.js
@@ -0,0 +1,31 @@
+
+
+const Element = HTMLElement.prototype;
+
+
+Element.visible = function (state) {
+ this.classList[state ? 'add' : 'remove']('d-none');
+}
+
+Element.show = function () {
+ this.classList.remove('d-none');
+}
+
+Element.hide = function () {
+ this.classList.add('d-none');
+}
+
+
+Element.enable = function () {
+ this.disabled = false;
+}
+
+Element.disable = function () {
+ this.disabled = true;
+}
+
+
+Element.swapTextWith = function (other) {
+ [this.innerText, other.innerText] =
+ [other.innerText, this.innerText];
+}
diff --git a/Source/Styles/Dark.css b/Source/Styles/Dark.css
new file mode 100644
index 0000000..7ed3e31
--- /dev/null
+++ b/Source/Styles/Dark.css
@@ -0,0 +1,12 @@
+:root {
+ --sort-btn-background-color: #212529;
+ --bar-background-color: #f5f5f5;
+ --source-code-color: #00ffff;
+ --sort-btn-color: #f5f5f5;
+ --shadow-color: #888888;
+}
+
+body {
+ background-color: #212529;
+ color: white;
+}
diff --git a/Source/Styles/General.css b/Source/Styles/General.css
new file mode 100644
index 0000000..d8c72a6
--- /dev/null
+++ b/Source/Styles/General.css
@@ -0,0 +1,129 @@
+:root {
+ --bar-background-color: #f5f5f5;
+ --sort-btn-color: #f5f5f5;
+ --sort-btn-background-color: #212529;
+ --source-code-color: #00ffff;
+ --shadow-color: #888888;
+}
+
+body {
+ background-color: #212529;
+ color: #fff;
+ text-align: center;
+ font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif;
+ height: 100vh;
+ transition: 0.5s;
+ -webkit-transition: 0.5s;
+ -moz-transition: 0.5s;
+ -ms-transition: 0.5s;
+ -o-transition: 0.5s;
+}
+
+a,
+a:hover,
+a:focus,
+a:active {
+ text-decoration: none;
+ color: #ffffff;
+}
+
+.range {
+ color: #ffffff;
+}
+
+/* Dark and Light mode */
+.toggle {
+ cursor: pointer;
+ transition: 0.5s;
+ -webkit-transition: 0.70s;
+ -moz-transition: 0.70s;
+ -ms-transition: 0.70s;
+ -o-transition: 0.70s;
+}
+
+#light:hover {
+ color: yellow;
+}
+
+
+.about {
+ font-size: 25px;
+ font-family: 'Courier New', Courier, monospace;
+ margin-left: 20px;
+}
+
+.container-fluid {
+ padding: 1% 2% 0 2%;
+ text-align: left;
+}
+
+.nav-items {
+ margin-left: auto;
+ font-size: 1.5rem;
+ font-weight: bold;
+}
+
+.random-array {
+ margin-right: 1rem;
+}
+
+.nav-dropdown {
+ font-size: 1.5rem;
+ font-weight: bold;
+}
+
+.BARS {
+ margin-top: 5%;
+ height: 500px;
+ width: 100%;
+ text-align: center;
+}
+
+.stable {
+ height: 500px;
+ width: 0.1%;
+ display: inline-block;
+}
+
+.BARS>*:not(:last-child) {
+ background-color: var(--bar-background-color);
+ margin-right: 0.1%;
+ margin-left: 0.3%;
+ border-radius: 50px;
+ box-shadow: 3px 5px var(--shadow-color);
+ bottom: 0px;
+ display: inline-block;
+}
+
+.btn-sort,
+.btn-sort:disabled {
+ background-color: var(--sort-btn-background-color);
+ color: var(--sort-btn-color);
+ font-size: 1.75rem;
+ font-weight: bold;
+ border-radius: 50px;
+ box-shadow: 5px 3px var(--shadow-color);
+ width: 80%;
+ margin-left: 10%;
+ margin-right: 10%;
+}
+
+.dropdown-menu>li {
+ display: block;
+ width: 100%;
+ padding: 0.25rem 1rem;
+ clear: both;
+ font-weight: 400;
+ color: #212529;
+ text-align: inherit;
+ text-decoration: none;
+ white-space: nowrap;
+ background-color: transparent;
+ border: 0;
+ cursor: pointer;
+}
+
+.dropdown-menu>li:hover {
+ color: #1e2125;
+ background-color: #e9ecef
+}
diff --git a/Source/Styles/Light.css b/Source/Styles/Light.css
new file mode 100644
index 0000000..3ef5281
--- /dev/null
+++ b/Source/Styles/Light.css
@@ -0,0 +1,12 @@
+:root {
+ --sort-btn-background-color: #f5f5f5;
+ --bar-background-color: #212529;
+ --source-code-color: #000000;
+ --sort-btn-color: #212529;
+ --shadow-color: #212529;
+}
+
+body {
+ background-color: #f5f5f5;
+ color: black;
+}
diff --git a/Source/Visual/Colors.js b/Source/Visual/Colors.js
new file mode 100644
index 0000000..d07d7e4
--- /dev/null
+++ b/Source/Visual/Colors.js
@@ -0,0 +1,13 @@
+
+
+export const Sorted
+ = '#3CB371';
+
+export const Unsorted
+ = '#DC143C';
+
+export const Alpha
+ = '#FFFF00';
+
+export const Beta
+ = '#0096FF';
diff --git a/Source/Visual/ProgressAnimation.js b/Source/Visual/ProgressAnimation.js
new file mode 100644
index 0000000..8939a64
--- /dev/null
+++ b/Source/Visual/ProgressAnimation.js
@@ -0,0 +1,21 @@
+
+const
+ steps = ['/', '-', '\\', '|'],
+ text = 'Sorting';
+
+
+export default function* animation() {
+
+ let progress = 0;
+
+ while (true) {
+
+ const chars = [...text];
+
+ chars[progress % text.length] = steps[progress % 4];
+
+ yield chars.join('');
+
+ progress++;
+ }
+}
diff --git a/Source/Visual/Theme.js b/Source/Visual/Theme.js
new file mode 100644
index 0000000..7379b27
--- /dev/null
+++ b/Source/Visual/Theme.js
@@ -0,0 +1,35 @@
+
+import { byId } from 'Document'
+
+
+const { values } = Object;
+
+
+const sheets = values(document.styleSheets);
+
+const findSheet = (name) =>
+ sheets.find((sheet) => sheet.href?.endsWith(`${name}.css`));
+
+
+const
+ lightStyle = findSheet('Light'),
+ darkStyle = findSheet('Dark');
+
+const
+ light = byId('light'),
+ dark = byId('dark');
+
+light.addEventListener('click', () => enableDark(false));
+dark.addEventListener('click', () => enableDark(true));
+
+
+function enableDark(state) {
+
+ lightStyle.disabled = state;
+ dark.visible(state);
+
+ darkStyle.disabled = !state;
+ light.visible(!state);
+}
+
+enableDark(true);
diff --git a/Source/index.html b/Source/index.html
new file mode 100644
index 0000000..5a4d236
--- /dev/null
+++ b/Source/index.html
@@ -0,0 +1,98 @@
+
+
+
+
+
+ Sorting Algorithms
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Sort
+
+
+
+
+