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 =
- ' Saved';
+ document.querySelector("#editor_status").innerHTML =
+ ' Saved';
}
function status_saving() {
- document.querySelector("#editor_status").innerHTML =
- ' Saving...';
+ document.querySelector("#editor_status").innerHTML =
+ ' Saving...';
}
function status_error() {
- document.querySelector("#editor_status").innerHTML =
- ' Error';
+ document.querySelector("#editor_status").innerHTML =
+ ' 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:
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:
+ parsed_json["details"]["primary"];
+ get_or_throw(`${card_selector} .card_top_text_pronouns`).innerText =
+ // biome-ignore lint/complexity/useLiteralKeys:
+ parsed_json["details"]["secondary"];
card_set_colors(
card_selector,
- json["colors"]["background"],
- json["colors"]["accent"],
- json["colors"]["text"],
+ // biome-ignore lint/complexity/useLiteralKeys:
+ parsed_json["colors"]["background"],
+ // biome-ignore lint/complexity/useLiteralKeys:
+ parsed_json["colors"]["accent"],
+ // biome-ignore lint/complexity/useLiteralKeys:
+ 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:
+ card_set_layout(card_selector, parsed_json["layout"]);
+ // biome-ignore lint/complexity/useLiteralKeys:
+ card_set_font(card_selector, parsed_json["font_style"]);
+
+ // biome-ignore lint/complexity/useLiteralKeys:
+ for (const item in parsed_json["information"]["items"]) {
+ // biome-ignore lint/complexity/useLiteralKeys:
+ 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:
+ parsed_json["information"]["items"][item]["uuid"],
+ // biome-ignore lint/complexity/useLiteralKeys:
+ parsed_json["information"]["items"][item]["icon"],
+ // biome-ignore lint/complexity/useLiteralKeys:
+ parsed_json["information"]["items"][item]["text"],
+ // biome-ignore lint/complexity/useLiteralKeys:
+ 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:
+ parsed_json["information"]["items"][item]["uuid"],
+ // biome-ignore lint/complexity/useLiteralKeys:
+ parsed_json["information"]["items"][item]["icon"],
+ // biome-ignore lint/complexity/useLiteralKeys:
+ 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:
load_font(font_style["header"]["name"], font_style["header"]["url"]);
+ // biome-ignore lint/complexity/useLiteralKeys:
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:
primary_top.style.fontFamily = font_style["header"]["name"];
+ // biome-ignore lint/complexity/useLiteralKeys:
secondary_top.style.fontFamily = font_style["text"]["name"];
+ // biome-ignore lint/complexity/useLiteralKeys:
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");