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 + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + + + + +