diff --git a/.github/workflows/lint-and-format.yml b/.github/workflows/lint-and-format.yml new file mode 100644 index 0000000..dc4365f --- /dev/null +++ b/.github/workflows/lint-and-format.yml @@ -0,0 +1,22 @@ +name: Lint and Format checks +on: [ push, pull_request ] +jobs: + ruff: + name: Python + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: chartboost/ruff-action@v1 + with: + path: './cardie' + + biome: + name: CSS and JS + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup Biome CLI + uses: biomejs/setup-biome@v2 + + - name: Run Biome + run: biome ci diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml deleted file mode 100644 index 326977c..0000000 --- a/.github/workflows/ruff.yml +++ /dev/null @@ -1,10 +0,0 @@ -name: Ruff -on: [ push, pull_request ] -jobs: - ruff: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: chartboost/ruff-action@v1 - with: - path: './cardie' diff --git a/README.md b/README.md index e166104..c9b8766 100644 --- a/README.md +++ b/README.md @@ -113,5 +113,15 @@ ruff check cardie --fix ``` For VS Code users, you can install the `ruff` extension to get linting and formatting on save. +### biome +This project uses [`biome`](https://biomejs.dev/) to lint CSS and JS files. +For simpler installation, it is recommended to use the VS Code add-on or to download the binary +version (go to [docs](https://biomejs.dev/guides/manual-installation/) for more information). + +```bash +biome check +``` +For VS Code users, you can install the `biome` extension to get linting and formatting on save. + ## Contributing Please see [CONTRIBUTING.md](CONTRIBUTING.md) for instructions on how you can contribute to Cardie diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..4b7e1bd --- /dev/null +++ b/biome.json @@ -0,0 +1,33 @@ +{ + "files": { + "include":[ + "static/main/scripts/global/**/*.js" + ] + }, + "formatter": { + "enabled": true, + "formatWithErrors": false, + "ignore": [], + "attributePosition": "auto", + "indentStyle": "space", + "indentWidth": 4, + "lineWidth": 80, + "lineEnding": "lf" + }, + "javascript": { + "formatter": { + "arrowParentheses": "always", + "bracketSameLine": false, + "bracketSpacing": true, + "jsxQuoteStyle": "double", + "quoteProperties": "asNeeded", + "semicolons": "always", + "trailingCommas": "all" + } + }, + "json": { + "formatter": { + "enabled": false + } + } +} diff --git a/cardie/main/templates/base.html b/cardie/main/templates/base.html index 003c530..146a612 100644 --- a/cardie/main/templates/base.html +++ b/cardie/main/templates/base.html @@ -20,6 +20,7 @@ href="{% static '/main/images/favicon_light.ico' %}" /> + Cardie | {% block head_title %}{% endblock %} diff --git a/cardie/main/views.py b/cardie/main/views.py index 8b4100a..f661574 100644 --- a/cardie/main/views.py +++ b/cardie/main/views.py @@ -6,8 +6,9 @@ from django.shortcuts import HttpResponse, render from django.utils import timezone from django.views.decorators.csrf import csrf_exempt + from main.icons import icons -from main.models import Card, Server, TempCard +from main.models import Card, TempCard def index(request): diff --git a/cardie/static/main/scripts/common.js b/cardie/static/main/scripts/common.js new file mode 100644 index 0000000..491c17c --- /dev/null +++ b/cardie/static/main/scripts/common.js @@ -0,0 +1,7 @@ +function get_or_throw(selector) { + const element = document.querySelector(selector); + if (!element) { + throw new Error(`Could not find element with selector "${selector}"`); + } + return element; +} diff --git a/cardie/static/main/scripts/editor/editor_information.js b/cardie/static/main/scripts/editor/editor_information.js index b6432a5..05ec829 100644 --- a/cardie/static/main/scripts/editor/editor_information.js +++ b/cardie/static/main/scripts/editor/editor_information.js @@ -5,147 +5,145 @@ var currently_editing_icon; var items_list; function open_iconselector_foritem(event) { - const item = event.target.closest(".link_item, .text_item"); - currently_editing_icon = item; - show_iconselector(); + const item = event.target.closest(".link_item, .text_item"); + currently_editing_icon = item; + show_iconselector(); } function editor_create_json() { - const card_json = { - uuid: "unknown", - name: "", - author: "unknown", - layout: "left", - details: { - primary: "", - secondary: "", - }, - information: { - items: [], - }, - colors: { - background: "#ffffff", - accent: "#000000", - text: "#000000", - }, - font_style: "Simple", - version: 4, - }; - - card_json["name"] = document.querySelector( - "#editor_header_name_text_cardname", - ).innerText; - card_json["author"] = username; - card_json["layout"] = layout; - card_json["details"]["primary"] = document.querySelector( - "#editor_main_settings_details_primary", - ).value; - card_json["details"]["secondary"] = document.querySelector( - "#editor_main_settings_details_secondary", - ).value; - - card_json["colors"]["background"] = document.querySelector( - "#editor_main_settings_colors_background", - ).value; - card_json["colors"]["accent"] = document.querySelector( - "#editor_main_settings_colors_accent", - ).value; - card_json["colors"]["text"] = document.querySelector( - "#editor_main_settings_colors_text", - ).value; - card_json["font_style"] = font_style; - - for (const item in items_list) { - const item_uuid = items_list[item].id; - const item_icon = items_list[item].icon; - const item_text = items_list[item].text; - const item_url = items_list[item].url; - const item_url_enabled = items_list[item].url_enabled; - const item_position = items_list[item].position; - - const item_json = { - uuid: item_uuid, - icon: item_icon, - text: item_text, - url: item_url, - url_enabled: item_url_enabled, - position: item_position, - }; - - card_json["information"]["items"].push(item_json); - - card_json["information"]["items"].sort( - (a, b) => a.position - b.position, - ); - } - - return card_json; + const card_json = { + uuid: "unknown", + name: "", + author: "unknown", + layout: "left", + details: { + primary: "", + secondary: "", + }, + information: { + items: [], + }, + colors: { + background: "#ffffff", + accent: "#000000", + text: "#000000", + }, + font_style: "Simple", + version: 4, + }; + + card_json["name"] = document.querySelector( + "#editor_header_name_text_cardname", + ).innerText; + card_json["author"] = username; + card_json["layout"] = layout; + card_json["details"]["primary"] = document.querySelector( + "#editor_main_settings_details_primary", + ).value; + card_json["details"]["secondary"] = document.querySelector( + "#editor_main_settings_details_secondary", + ).value; + + card_json["colors"]["background"] = document.querySelector( + "#editor_main_settings_colors_background", + ).value; + card_json["colors"]["accent"] = document.querySelector( + "#editor_main_settings_colors_accent", + ).value; + card_json["colors"]["text"] = document.querySelector( + "#editor_main_settings_colors_text", + ).value; + card_json["font_style"] = font_style; + + for (const item in items_list) { + const item_uuid = items_list[item].id; + const item_icon = items_list[item].icon; + const item_text = items_list[item].text; + const item_url = items_list[item].url; + const item_url_enabled = items_list[item].url_enabled; + const item_position = items_list[item].position; + + const item_json = { + uuid: item_uuid, + icon: item_icon, + text: item_text, + url: item_url, + url_enabled: item_url_enabled, + position: item_position, + }; + + card_json["information"]["items"].push(item_json); + + card_json["information"]["items"].sort((a, b) => a.position - b.position); + } + + return card_json; } function editor_load_from_json(json) { - // Sets up all the buttons based on the json - - json = JSON.parse(json); - - document.querySelector("#editor_header_name_text_cardname").innerText = - json["name"]; - document.querySelector("#editor_main_settings_details_primary").value = - json["details"]["primary"]; - document.querySelector("#editor_main_settings_details_secondary").value = - json["details"]["secondary"]; - - document.querySelector("#editor_main_settings_colors_background").value = - json["colors"]["background"]; - document.querySelector("#editor_main_settings_colors_accent").value = - json["colors"]["accent"]; - document.querySelector("#editor_main_settings_colors_text").value = - json["colors"]["text"]; - font_style = json["font_style"]; - - card_set_font(".card_card", font_style); - window.dispatchEvent( - new CustomEvent("sendLoadedFontFromJson", { - detail: { font_style }, - }), - ); - - card_set_layout(".card_card", json["layout"]); - layout = json["layout"]; - - json["information"]["items"].sort((a, b) => a.position - b.position); - - for (const item in json["information"]["items"]) { - const uuid = json["information"]["items"][item]["uuid"]; - const text = json["information"]["items"][item]["text"]; - const icon = json["information"]["items"][item]["icon"]; - const url = json["information"]["items"][item]["url"]; - const url_enabled = json["information"]["items"][item]["url_enabled"]; - const position = json["information"]["items"][item]["position"]; - - window.dispatchEvent( - new CustomEvent("createItem", { - detail: { uuid, text, icon, url, url_enabled, position }, - }), - ); - } + // Sets up all the buttons based on the json + + json = JSON.parse(json); + + document.querySelector("#editor_header_name_text_cardname").innerText = + json["name"]; + document.querySelector("#editor_main_settings_details_primary").value = + json["details"]["primary"]; + document.querySelector("#editor_main_settings_details_secondary").value = + json["details"]["secondary"]; + + document.querySelector("#editor_main_settings_colors_background").value = + json["colors"]["background"]; + document.querySelector("#editor_main_settings_colors_accent").value = + json["colors"]["accent"]; + document.querySelector("#editor_main_settings_colors_text").value = + json["colors"]["text"]; + font_style = json["font_style"]; + + card_set_font(".card_card", font_style); + window.dispatchEvent( + new CustomEvent("sendLoadedFontFromJson", { + detail: { font_style }, + }), + ); + + card_set_layout(".card_card", json["layout"]); + layout = json["layout"]; + + json["information"]["items"].sort((a, b) => a.position - b.position); + + for (const item in json["information"]["items"]) { + const uuid = json["information"]["items"][item]["uuid"]; + const text = json["information"]["items"][item]["text"]; + const icon = json["information"]["items"][item]["icon"]; + const url = json["information"]["items"][item]["url"]; + const url_enabled = json["information"]["items"][item]["url_enabled"]; + const position = json["information"]["items"][item]["position"]; + + window.dispatchEvent( + new CustomEvent("createItem", { + detail: { uuid, text, icon, url, url_enabled, position }, + }), + ); + } } function status_saved() { - document.querySelector("#editor_status").innerHTML = - '<i class="ph-bold ph-check-circle"></i> Saved'; + document.querySelector("#editor_status").innerHTML = + '<i class="ph-bold ph-check-circle"></i> Saved'; } function status_saving() { - document.querySelector("#editor_status").innerHTML = - '<i class="ph-bold ph-spinner-gap"></i> Saving...'; + document.querySelector("#editor_status").innerHTML = + '<i class="ph-bold ph-spinner-gap"></i> Saving...'; } function status_error() { - document.querySelector("#editor_status").innerHTML = - '<i class="ph-bold ph-warning"></i> Error'; + document.querySelector("#editor_status").innerHTML = + '<i class="ph-bold ph-warning"></i> Error'; } window.addEventListener("itemData", (event) => { - const { items } = event.detail; - items_list = items; + const { items } = event.detail; + items_list = items; }); diff --git a/cardie/static/main/scripts/editor/icons.js b/cardie/static/main/scripts/editor/icons.js index 10b8914..7d83cf2 100644 --- a/cardie/static/main/scripts/editor/icons.js +++ b/cardie/static/main/scripts/editor/icons.js @@ -1,18 +1,18 @@ // TODO: Support {{ server_url }} const icons_url = "http://127.0.0.1:8000/iconlist"; const itemsData = []; // Array to store item data -var icon_selected_item; +let icon_selected_item; function create_icon(icon) { const div_element = document.createElement("div"); div_element.classList.add("editor-iconselector-icon"); div_element.setAttribute("icon", icon); - icon_element = document.createElement("i"); + const icon_element = document.createElement("i"); icon_element.className = "editor-iconselector-icon-icon ph-bold"; - icon_element.classList.add("ph-" + icon); + icon_element.classList.add(`ph-${icon}`); - text_element = document.createElement("p"); + const text_element = document.createElement("p"); text_element.classList.add("editor-iconselector-icon-text"); text_element.innerText = icon; @@ -22,9 +22,7 @@ function create_icon(icon) { itemsData.push(div_element); - document - .querySelector("#editor-iconselector-icons") - .appendChild(div_element); + get_or_throw("#editor-iconselector-icons").appendChild(div_element); } function render_icons(icons) { @@ -35,7 +33,7 @@ function render_icons(icons) { } async function fetch_icon_list() { - response = await fetch(icons_url); + const response = await fetch(icons_url); const text = await response.text(); render_icons(text); } @@ -46,9 +44,10 @@ document.addEventListener("DOMContentLoaded", (event) => { // Function to render items const renderItems = (filteredItems) => { - document.querySelector("#editor-iconselector-icons").innerHTML = ""; + get_or_throw("#editor-iconselector-icons").innerHTML = ""; + // biome-ignore lint/complexity/noForEach: <explanation> filteredItems.forEach((item) => - document.querySelector("#editor-iconselector-icons").appendChild(item), + get_or_throw("#editor-iconselector-icons").appendChild(item), ); }; @@ -68,38 +67,33 @@ const filterItems = (query) => { }; // Event listener for the search box -document - .querySelector("#editor-iconselector-top-search") - .addEventListener("input", (e) => { +get_or_throw("#editor-iconselector-top-search").addEventListener( + "input", + (e) => { const query = e.target.value; const filteredItems = filterItems(query); const sortedItems = sortItems(filteredItems); renderItems(sortedItems); - }); + }, +); function show_iconselector(item) { icon_selected_item = item; show_background_blur(); - document.querySelector("#editor-iconselector").style.display = "flex"; - document - .querySelector("#editor-iconselector") - .classList.remove("hide-iconselector"); - document - .querySelector("#editor-iconselector") - .classList.add("show-iconselector"); + get_or_throw("#editor-iconselector").style.display = "flex"; + get_or_throw("#editor-iconselector").classList.remove("hide-iconselector"); + get_or_throw("#editor-iconselector").classList.add("show-iconselector"); } function hide_iconselector() { - document - .querySelector("#editor-iconselector") - .classList.add("hide-iconselector"); + get_or_throw("#editor-iconselector").classList.add("hide-iconselector"); setTimeout(() => { - document - .querySelector("#editor-iconselector") - .classList.remove("show-iconselector"); - document.querySelector("#editor-iconselector").style.display = "none"; + get_or_throw("#editor-iconselector").classList.remove( + "show-iconselector", + ); + get_or_throw("#editor-iconselector").style.display = "none"; hide_background_blur(); }, 500); } @@ -119,8 +113,9 @@ function icon_clicked(event) { hide_iconselector(); } -document - .querySelector("#editor-iconselector-top-close") - .addEventListener("click", (event) => { +get_or_throw("#editor-iconselector-top-close").addEventListener( + "click", + (event) => { hide_iconselector(); - }); + }, +); diff --git a/cardie/static/main/scripts/editor/print.js b/cardie/static/main/scripts/editor/print.js index 16e9659..c522b49 100644 --- a/cardie/static/main/scripts/editor/print.js +++ b/cardie/static/main/scripts/editor/print.js @@ -1,111 +1,95 @@ -var print_single = false; - -document - .querySelector("#editor_share_print") - .addEventListener("click", (event) => { - document.querySelector("#dialog_print").showModal(); - }); - -document - .querySelector( - "#dialog_print > .ui_dialog_generic_top > .ui_dialog_generic_top_close", - ) - .addEventListener("click", (event) => { - document.querySelector("#dialog_print").close(); - }); - -document - .querySelector("#dialog_print_single") - .addEventListener("click", (event) => { - print_single = true; - }); - -document - .querySelector("#dialog_print_double") - .addEventListener("click", (event) => { - print_single = false; - }); - -document - .querySelector("#dialog_print_print") - .addEventListener("click", (event) => { - log("INFO", "Opening print dialog"); - window.print(); - }); +let print_single = false; + +get_or_throw("#editor_share_print").addEventListener("click", (event) => { + get_or_throw("#dialog_print").showModal(); +}); + +get_or_throw( + "#dialog_print > .ui_dialog_generic_top > .ui_dialog_generic_top_close", +).addEventListener("click", (event) => { + get_or_throw("#dialog_print").close(); +}); + +get_or_throw("#dialog_print_single").addEventListener("click", (event) => { + print_single = true; +}); + +get_or_throw("#dialog_print_double").addEventListener("click", (event) => { + print_single = false; +}); + +get_or_throw("#dialog_print_print").addEventListener("click", (event) => { + log("INFO", "Opening print dialog"); + window.print(); +}); window.addEventListener("beforeprint", (event) => { - log("INFO", "Preparing DOM for printing"); - - if (print_single) { - for (let card = 0; card < 3; card++) { - const new_card = document - .querySelector(".card_card") - .cloneNode(true); - new_card.classList.add("print-card"); - document - .querySelector("#editor_main_preview") - .appendChild(new_card); - } - } else { - const front_div = document.createElement("div"); - front_div.classList.add("print-div"); - front_div.id = "print_front_div"; - - const back_div = document.createElement("div"); - back_div.classList.add("print-div"); - back_div.id = "print_back_div"; - - document.querySelector("#editor_main_preview").appendChild(front_div); - document.querySelector("#editor_main_preview").appendChild(back_div); - - for (let card = 0; card < 8; card++) { - const card_front = document - .querySelector(".card_card > .card_card_front") - .cloneNode(true); - card_front.classList.add("print-card"); - document.querySelector("#print_front_div").appendChild(card_front); - } - - for (let card = 0; card < 8; card++) { - const card_back = document - .querySelector(".card_card > .card_card_back") - .cloneNode(true); - card_back.classList.add("print-card"); - document.querySelector("#print_back_div").appendChild(card_back); - } - - document.querySelector(".card_card").style.display = "none"; - document - .querySelector("#editor_main_preview") - .classList.add("twosided"); - } + log("INFO", "Preparing DOM for printing"); + + if (print_single) { + for (let card = 0; card < 3; card++) { + const new_card = get_or_throw(".card_card").cloneNode(true); + new_card.classList.add("print-card"); + get_or_throw("#editor_main_preview").appendChild(new_card); + } + } else { + const front_div = document.createElement("div"); + front_div.classList.add("print-div"); + front_div.id = "print_front_div"; + + const back_div = document.createElement("div"); + back_div.classList.add("print-div"); + back_div.id = "print_back_div"; + + get_or_throw("#editor_main_preview").appendChild(front_div); + get_or_throw("#editor_main_preview").appendChild(back_div); + + for (let card = 0; card < 8; card++) { + const card_front = get_or_throw( + ".card_card > .card_card_front", + ).cloneNode(true); + card_front.classList.add("print-card"); + get_or_throw("#print_front_div").appendChild(card_front); + } + + for (let card = 0; card < 8; card++) { + const card_back = get_or_throw(".card_card > .card_card_back").cloneNode( + true, + ); + card_back.classList.add("print-card"); + get_or_throw("#print_back_div").appendChild(card_back); + } + + get_or_throw(".card_card").style.display = "none"; + get_or_throw("#editor_main_preview").classList.add("twosided"); + } }); window.addEventListener("afterprint", (event) => { - log("INFO", "Cleaning up DOM after printing"); - - document.querySelector("#editor_main_preview").classList.remove("twosided"); - document.querySelector(".card_card").style.display = "block"; - - const cards = document.querySelectorAll(".print-card"); - - // TODO: Throws an exception but still works - try { - for (const card in cards) { - cards[card].remove(); - } - } catch { - null; - } - - try { - const divs = document.querySelectorAll(".print-div"); - - // TODO: Throws an exception but still works - for (const div in divs) { - divs[div].remove(); - } - } catch { - null; - } + log("INFO", "Cleaning up DOM after printing"); + + get_or_throw("#editor_main_preview").classList.remove("twosided"); + get_or_throw(".card_card").style.display = "block"; + + const cards = document.querySelectorAll(".print-card"); + + // TODO: Throws an exception but still works + try { + for (const card in cards) { + cards[card].remove(); + } + } catch { + null; + } + + try { + const divs = document.querySelectorAll(".print-div"); + + // TODO: Throws an exception but still works + for (const div in divs) { + divs[div].remove(); + } + } catch { + null; + } }); diff --git a/cardie/static/main/scripts/editor/rename.js b/cardie/static/main/scripts/editor/rename.js index a694fe3..5e05871 100644 --- a/cardie/static/main/scripts/editor/rename.js +++ b/cardie/static/main/scripts/editor/rename.js @@ -1,46 +1,35 @@ -function show_rename() { - show_background_blur(); +const editor_rename = get_or_throw("#editor_rename"); +const editor_rename_input = get_or_throw("#editor_rename_input"); - document.querySelector("#editor_rename").style.display = "flex"; - document.querySelector("#editor_rename").classList.remove("hide-rename"); - document.querySelector("#editor_rename").classList.add("show-rename"); +function show_rename() { + show_background_blur(); + editor_rename.style.display = "flex"; + editor_rename.classList.remove("hide-rename"); + editor_rename.classList.add("show-rename"); } function hide_rename() { - document.querySelector("#editor_rename").classList.add("hide-rename"); + editor_rename.classList.add("hide-rename"); - setTimeout(() => { - document - .querySelector("#editor_rename") - .classList.remove("show-rename"); - document.querySelector("#editor_rename").style.display = "none"; + setTimeout(() => { + editor_rename.classList.remove("show-rename"); + editor_rename.style.display = "none"; - hide_background_blur(); - }, 500); + hide_background_blur(); + }, 500); } function rename_card() { - const card_name = document.querySelector("#editor_rename_input").value; + const card_name = editor_rename_input.value; + const card_name_element = get_or_throw("#editor_header_name_text_cardname"); - document.querySelector("#editor_header_name_text_cardname").innerText = - card_name; - hide_rename(); + card_name_element.innerText = card_name; + hide_rename(); } -document - .querySelector("#editor_rename_top_close") - .addEventListener("click", (event) => { - hide_rename(); - }); - -document - .querySelector("#editor_rename_submit") - .addEventListener("click", (event) => { - rename_card(); - }); - -document - .querySelector("#editor_header_name_button") - .addEventListener("click", (event) => { - show_rename(); - }); +get_or_throw("#editor_rename_top_close").addEventListener("click", hide_rename); +get_or_throw("#editor_rename_submit").addEventListener("click", rename_card); +get_or_throw("#editor_header_name_button").addEventListener( + "click", + show_rename, +); diff --git a/cardie/static/main/scripts/global/background_blur.js b/cardie/static/main/scripts/global/background_blur.js index c706081..cfe3fd2 100644 --- a/cardie/static/main/scripts/global/background_blur.js +++ b/cardie/static/main/scripts/global/background_blur.js @@ -1,16 +1,33 @@ +function get_blur_element() { + /** + * @constant + * @type {HTMLElement | null} + */ + const blurElement = document.querySelector(".background_blur"); + if (!blurElement) { + throw new Error("Could not find background blur element"); + } + return blurElement; +} + function show_background_blur() { - document.querySelector(".background_blur").style.display = "flex"; + /** + * @constant + * @type {HTMLElement | null} + */ + const blurElement = get_blur_element(); + blurElement.style.display = "flex"; setTimeout(() => { - document.querySelector(".background_blur").classList.add("show"); + blurElement.classList.add("show"); }, 10); } function hide_background_blur() { // TODO: This doesn't seem to animate correctly - document.querySelector(".background_blur").classList.remove("show"); - + const blurElement = get_blur_element(); + blurElement.classList.remove("show"); setTimeout(() => { - document.querySelector(".background_blur").style.display = "none"; + blurElement.style.display = "none"; }, 500); } diff --git a/cardie/static/main/scripts/global/card.js b/cardie/static/main/scripts/global/card.js index 1488a2d..d8e4073 100644 --- a/cardie/static/main/scripts/global/card.js +++ b/cardie/static/main/scripts/global/card.js @@ -1,7 +1,7 @@ // Makes every .card_card element flip on click -var cards = document.getElementsByClassName("card_card"); -var card_items = []; +const cards = document.getElementsByClassName("card_card"); +let card_items = []; for (let i = 0; i < cards.length; i++) { cards[i].addEventListener("click", () => { @@ -9,19 +9,19 @@ for (let i = 0; i < cards.length; i++) { }); } -var card_i_style = document.createElement("style"); +const card_i_style = document.createElement("style"); card_i_style.type = "text/css"; document.getElementsByTagName("head")[0].appendChild(card_i_style); -var card_p_style = document.createElement("style"); +const card_p_style = document.createElement("style"); card_p_style.type = "text/css"; document.getElementsByTagName("head")[0].appendChild(card_p_style); // Functions for adding elements to the card function card_delete_items(card_selector) { card_items = []; - document.querySelector(`${card_selector} .card_items`).replaceChildren(); - document.querySelector(`#dialog_card_menu_items`).replaceChildren(); + get_or_throw(`${card_selector} .card_items`).replaceChildren(); + get_or_throw("#dialog_card_menu_items").replaceChildren(); } function card_create_text_item(card_selector, uuid, icon, text) { @@ -52,7 +52,7 @@ function card_create_text_item(card_selector, uuid, icon, text) { div.appendChild(text_element); if (existing_selector.items.length <= 7) { - document.querySelector(`${card_selector} .card_items`).appendChild(div); + get_or_throw(`${card_selector} .card_items`).appendChild(div); } else { if (!document.querySelector("#dialog_card_menu_button")) { const menu_button = document.createElement("button"); @@ -61,15 +61,15 @@ function card_create_text_item(card_selector, uuid, icon, text) { menu_button.addEventListener("click", (event) => { event.stopPropagation(); - document.querySelector("#dialog_card_menu").showModal(); + get_or_throw("#dialog_card_menu").showModal(); }); - document - .querySelector(`${card_selector} .card_items`) - .appendChild(menu_button); + get_or_throw(`${card_selector} .card_items`).appendChild( + menu_button, + ); } - document.querySelector(`#dialog_card_menu_items`).appendChild(div); + get_or_throw("#dialog_card_menu_items").appendChild(div); } } @@ -106,7 +106,7 @@ function card_create_link_item(card_selector, uuid, icon, text, url) { div.appendChild(button_element); if (existing_selector.items.length <= 7) { - document.querySelector(`${card_selector} .card_items`).appendChild(div); + get_or_throw(`${card_selector} .card_items`).appendChild(div); } else { if (!document.querySelector("#dialog_card_menu_button")) { const menu_button = document.createElement("button"); @@ -115,25 +115,23 @@ function card_create_link_item(card_selector, uuid, icon, text, url) { menu_button.addEventListener("click", (event) => { event.stopPropagation(); - document.querySelector("#dialog_card_menu").showModal(); + get_or_throw("#dialog_card_menu").showModal(); }); - document - .querySelector(`${card_selector} .card_items`) - .appendChild(menu_button); + get_or_throw(`${card_selector} .card_items`).appendChild( + menu_button, + ); } - document.querySelector(`#dialog_card_menu_items`).appendChild(div); + get_or_throw("#dialog_card_menu_items").appendChild(div); } } function card_set_colors(card_selector, background, accent, text) { - document.querySelector( - `${card_selector} .card_card_front`, - ).style.backgroundColor = background; - document.querySelector( - `${card_selector} .card_card_back`, - ).style.backgroundColor = background; + get_or_throw(`${card_selector} .card_card_front`).style.backgroundColor = + background; + get_or_throw(`${card_selector} .card_card_back`).style.backgroundColor = + background; card_i_style.innerHTML = `${card_selector} i { color: ${accent} }`; card_p_style.innerHTML = `${card_selector} p { color: ${text} }`; @@ -142,115 +140,130 @@ function card_set_colors(card_selector, background, accent, text) { function card_render_from_json(card_selector, json) { // Renders the contents of the card from the json - json = JSON.parse(json); + const parsed_json = JSON.parse(json); card_delete_items(card_selector); - document.querySelector( - `${card_selector} .card_top_text_username`, - ).innerText = json["details"]["primary"]; - document.querySelector( - `${card_selector} .card_top_text_pronouns`, - ).innerText = json["details"]["secondary"]; + get_or_throw(`${card_selector} .card_top_text_username`).innerText = + // biome-ignore lint/complexity/useLiteralKeys: <explanation> + parsed_json["details"]["primary"]; + get_or_throw(`${card_selector} .card_top_text_pronouns`).innerText = + // biome-ignore lint/complexity/useLiteralKeys: <explanation> + parsed_json["details"]["secondary"]; card_set_colors( card_selector, - json["colors"]["background"], - json["colors"]["accent"], - json["colors"]["text"], + // biome-ignore lint/complexity/useLiteralKeys: <explanation> + parsed_json["colors"]["background"], + // biome-ignore lint/complexity/useLiteralKeys: <explanation> + parsed_json["colors"]["accent"], + // biome-ignore lint/complexity/useLiteralKeys: <explanation> + parsed_json["colors"]["text"], ); - card_set_layout(card_selector, json["layout"]); - card_set_font(card_selector, json["font_style"]); - - for (const item in json["information"]["items"]) { - if (json["information"]["items"][item]["url_enabled"]) { + // biome-ignore lint/complexity/useLiteralKeys: <explanation> + card_set_layout(card_selector, parsed_json["layout"]); + // biome-ignore lint/complexity/useLiteralKeys: <explanation> + card_set_font(card_selector, parsed_json["font_style"]); + + // biome-ignore lint/complexity/useLiteralKeys: <explanation> + for (const item in parsed_json["information"]["items"]) { + // biome-ignore lint/complexity/useLiteralKeys: <explanation> + if (parsed_json["information"]["items"][item]["url_enabled"]) { card_create_link_item( card_selector, - json["information"]["items"][item]["uuid"], - json["information"]["items"][item]["icon"], - json["information"]["items"][item]["text"], - json["information"]["items"][item]["url"], + // biome-ignore lint/complexity/useLiteralKeys: <explanation> + parsed_json["information"]["items"][item]["uuid"], + // biome-ignore lint/complexity/useLiteralKeys: <explanation> + parsed_json["information"]["items"][item]["icon"], + // biome-ignore lint/complexity/useLiteralKeys: <explanation> + parsed_json["information"]["items"][item]["text"], + // biome-ignore lint/complexity/useLiteralKeys: <explanation> + parsed_json["information"]["items"][item]["url"], ); } else { card_create_text_item( card_selector, - json["information"]["items"][item]["uuid"], - json["information"]["items"][item]["icon"], - json["information"]["items"][item]["text"], + // biome-ignore lint/complexity/useLiteralKeys: <explanation> + parsed_json["information"]["items"][item]["uuid"], + // biome-ignore lint/complexity/useLiteralKeys: <explanation> + parsed_json["information"]["items"][item]["icon"], + // biome-ignore lint/complexity/useLiteralKeys: <explanation> + parsed_json["information"]["items"][item]["text"], ); } } } +const ALLOWED_LAYOUTS = ["left", "right", "center", ""]; function card_set_layout(card_selector, layout) { // layout can be "left", "right", "center" - const card = document.querySelector(`${card_selector} .card_card_front`); - - if (layout == "left") { - card.setAttribute("card-align", "left"); - } else if (layout == "right") { - card.setAttribute("card-align", "right"); - } else if (layout == "center") { - card.setAttribute("card-align", "center"); - } else if (layout == "") { - // Default value in databases - card.setAttribute("card-align", "left"); - } else { + const card = get_or_throw(`${card_selector} .card_card_front`); + if (!ALLOWED_LAYOUTS.includes(layout)) { log("WARNING", "The card layout parameter is not an accepted value"); + throw new Error("The card layout parameter is not an accepted value"); } + card.setAttribute("card-align", layout === "" ? "left" : layout); } function card_set_font(card_selector, name) { let font_style = get_font_style(name); + if (!font_style) { + throw new Error("The font style does not exist"); + } + // biome-ignore lint/complexity/useLiteralKeys: <explanation> load_font(font_style["header"]["name"], font_style["header"]["url"]); + // biome-ignore lint/complexity/useLiteralKeys: <explanation> load_font(font_style["text"]["name"], font_style["text"]["url"]); - const primary_top = document.querySelector( + const primary_top = get_or_throw( `${card_selector} .card_top_text_username`, ); - const secondary_top = document.querySelector( + const secondary_top = get_or_throw( `${card_selector} .card_top_text_pronouns`, ); - const items = document.querySelector(`${card_selector} .card_items`); + const items = get_or_throw(`${card_selector} .card_items`); + // biome-ignore lint/complexity/useLiteralKeys: <explanation> primary_top.style.fontFamily = font_style["header"]["name"]; + // biome-ignore lint/complexity/useLiteralKeys: <explanation> secondary_top.style.fontFamily = font_style["text"]["name"]; + // biome-ignore lint/complexity/useLiteralKeys: <explanation> items.style.fontFamily = font_style["text"]["name"]; font_style = name; } +// NOTE(@ar4s): I think that behaviour can achieved by using only CSS for (let i = 0; i < cards.length; i++) { - const outer = cards[i], - maxWidth = outer.clientWidth, - maxHeight = outer.clientHeight; + const outer = cards[i]; + const maxWidth = outer.clientWidth; + const maxHeight = outer.clientHeight; window.addEventListener("resize", resize); resize(); function resize() { - let scale, - width = window.innerWidth, - height = window.innerHeight, - isMax = width >= maxWidth && height >= maxHeight; + const width = window.innerWidth; + const height = window.innerHeight; + const isMax = width >= maxWidth && height >= maxHeight; + const scale = Math.min(width / maxWidth, height / maxHeight); - scale = Math.min(width / maxWidth, height / maxHeight); - outer.style.transform = isMax ? "" : "scale(" + scale + ")"; + // @ts-ignore + outer.style.transform = isMax ? "" : `scale(${scale})`; } } window.addEventListener("setFontOnCard", (event) => { // Called when a font item is clicked in the editor event.stopImmediatePropagation(); + // @ts-ignore const { header, text, style_name } = event.detail; card_set_font(".card_card", style_name); font_style = style_name; }); -document - .querySelector( - "#dialog_card_menu > .ui_dialog_generic_top > .ui_dialog_generic_top_close", - ) - .addEventListener("click", (event) => { - document.querySelector("#dialog_card_menu").close(); - }); +get_or_throw( + "#dialog_card_menu > .ui_dialog_generic_top > .ui_dialog_generic_top_close", +).addEventListener("click", (event) => { + get_or_throw("#dialog_card_menu").close(); +}); diff --git a/cardie/static/main/scripts/global/font_styles.js b/cardie/static/main/scripts/global/font_styles.js index 7190643..b55ce3d 100644 --- a/cardie/static/main/scripts/global/font_styles.js +++ b/cardie/static/main/scripts/global/font_styles.js @@ -1,7 +1,7 @@ // This file lists the available font styles to be used for cards // It will primarily contain the array of font style objects which can then be imported by the editor preview or cardview to allow the font to be changed -var font_styles = [ +const font_styles = [ { name: "Simple", header: { name: "Noto Sans", url: "Noto Sans" }, @@ -29,7 +29,7 @@ var font_styles = [ ]; function get_font_style(name) { - return font_styles.find((font) => font.name == name); + return font_styles.find((font) => font.name === name); } async function load_font(name, url) { @@ -67,6 +67,7 @@ document.addEventListener("DOMContentLoaded", (event) => { window.addEventListener("loadFont", (event) => { event.stopImmediatePropagation(); + // @ts-ignore const { font_name, font_url } = event.detail; load_font(font_name, font_url); }); diff --git a/cardie/static/main/scripts/global/logging.js b/cardie/static/main/scripts/global/logging.js index dca0b54..f572a14 100644 --- a/cardie/static/main/scripts/global/logging.js +++ b/cardie/static/main/scripts/global/logging.js @@ -1,22 +1,22 @@ // A simple collection of functions to log data to the JavaScript console -var LOG_LEVEL = "DEBUG"; +const LOG_LEVEL = "DEBUG"; + +const log_to_level_number_map = { + DEBUG: 1, + INFO: 2, + WARNING: 3, + CRITICAL: 4, +}; function log_level_to_number(level) { - if (level == "DEBUG") { - return 1; - } else if (level == "INFO") { - return 2; - } else if (level == "WARNING") { - return 3; - } else if (level == "CRITICAL") { - return 4; - } else { - return 0; - } + return log_to_level_number_map[level] ?? 0; } function log_date() { + /** @constant + * @type {Intl.DateTimeFormatOptions} + */ const options = { hour: "2-digit", minute: "2-digit", @@ -24,8 +24,8 @@ function log_date() { hour12: false, timeZone: "EST", }; - var now = new Date(); - var string = now.toLocaleTimeString("en-us", options); + const now = new Date(); + const string = now.toLocaleTimeString("en-us", options); return string; } @@ -35,21 +35,21 @@ function log(level, text) { const date = log_date(); if (log_level >= allowed_log_level) { - if (log_level == 0) { + if (log_level === 0) { console.log( `%c[${date}] ${text}`, "color: #a1a1a1; font-style: italic;", ); - } else if (log_level == 1) { + } else if (log_level === 1) { console.debug(`%c[${date}] ${text}`, "color: #ffffff;"); - } else if (log_level == 2) { + } else if (log_level === 2) { console.info(`%c[${date}] ${text}`, "color: #7fb5ff;"); - } else if (log_level == 3) { + } else if (log_level === 3) { console.warn( `%c[${date}] ${text}`, "color: #ffa371; font-weight: bold;", ); - } else if (log_level == 4) { + } else if (log_level === 4) { console.error( `%c[${date}] ${text}`, "color: #ff7878; font-weight: bold;", diff --git a/cardie/static/main/scripts/global/notifications.js b/cardie/static/main/scripts/global/notifications.js index 7e3b7de..710462e 100644 --- a/cardie/static/main/scripts/global/notifications.js +++ b/cardie/static/main/scripts/global/notifications.js @@ -26,7 +26,12 @@ function create_notification(header, body, icon) { notification.appendChild(notification_icon); notification.appendChild(notification_text); - document.querySelector(".notifications").appendChild(notification); + const notifications = document.querySelector(".notifications"); + + if (notifications === null) { + throw new Error("Could not find notifications box"); + } + notifications.appendChild(notification); setTimeout(() => { notification.classList.add("show");