From a2f2cca0195bac07c57272cfadd2c138e1847711 Mon Sep 17 00:00:00 2001 From: Ogbemi mene Date: Mon, 3 Aug 2026 09:28:05 +0100 Subject: [PATCH 1/6] debugged/book-library --- debugging/book-library/script.js | 67 +++++++++++++++++--------------- 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/debugging/book-library/script.js b/debugging/book-library/script.js index 75ce6c1d..07740a81 100644 --- a/debugging/book-library/script.js +++ b/debugging/book-library/script.js @@ -7,7 +7,7 @@ window.addEventListener("load", function (e) { function populateStorage() { if (myLibrary.length == 0) { - let book1 = new Book("Robison Crusoe", "Daniel Defoe", "252", true); + let book1 = new Book("Robinson Crusoe", "Daniel Defoe", "252", true); let book2 = new Book( "The Old Man and the Sea", "Ernest Hemingway", @@ -25,20 +25,22 @@ const author = document.getElementById("author"); const pages = document.getElementById("pages"); const check = document.getElementById("check"); -//check the right input from forms and if its ok -> add the new book (object in array) -//via Book function and start render function function submit() { - if ( - title.value == null || - title.value == "" || - pages.value == null || - pages.value == "" - ) { + // Validate all required fields (title, author, and pages) + if (!title.value.trim() || !author.value.trim() || !pages.value.trim()) { alert("Please fill all fields!"); return false; } else { - let book = new Book(title.value, title.value, pages.value, check.checked); - library.push(book); + // Corrected to pass author.value and push to myLibrary + let book = new Book(title.value, author.value, pages.value, check.checked); + myLibrary.push(book); + + // Clear input fields after submission + title.value = ""; + author.value = ""; + pages.value = ""; + check.checked = false; + render(); } } @@ -53,34 +55,34 @@ function Book(title, author, pages, check) { function render() { let table = document.getElementById("display"); let rowsNumber = table.rows.length; - //delete old table - for (let n = rowsNumber - 1; n > 0; n-- { + + // Fixed missing parenthesis in loop + for (let n = rowsNumber - 1; n > 0; n--) { table.deleteRow(n); } - //insert updated row and cells + + // Insert updated rows and cells let length = myLibrary.length; for (let i = 0; i < length; i++) { - let row = table.insertRow(1); + let row = table.insertRow(-1); // Appends at the end of the table let titleCell = row.insertCell(0); let authorCell = row.insertCell(1); let pagesCell = row.insertCell(2); let wasReadCell = row.insertCell(3); let deleteCell = row.insertCell(4); + titleCell.innerHTML = myLibrary[i].title; authorCell.innerHTML = myLibrary[i].author; pagesCell.innerHTML = myLibrary[i].pages; - //add and wait for action for read/unread button + // Toggle Read status button let changeBut = document.createElement("button"); - changeBut.id = i; - changeBut.className = "btn btn-success"; - wasReadCell.appendChild(changeBut); - let readStatus = ""; - if (myLibrary[i].check == false) { - readStatus = "Yes"; - } else { - readStatus = "No"; - } + changeBut.className = myLibrary[i].check + ? "btn btn-success" + : "btn btn-secondary"; + + // Corrected read status logic + let readStatus = myLibrary[i].check ? "Yes" : "No"; changeBut.innerText = readStatus; changeBut.addEventListener("click", function () { @@ -88,16 +90,19 @@ function render() { render(); }); - //add delete button to every row and render again + wasReadCell.appendChild(changeBut); + + // Fixed variable names (delBut -> delButton) and event listener ("clicks" -> "click") let delButton = document.createElement("button"); - delBut.id = i + 5; - deleteCell.appendChild(delBut); - delBut.className = "btn btn-warning"; - delBut.innerHTML = "Delete"; - delBut.addEventListener("clicks", function () { + delButton.className = "btn btn-danger"; + delButton.innerHTML = "Delete"; + + delButton.addEventListener("click", function () { alert(`You've deleted title: ${myLibrary[i].title}`); myLibrary.splice(i, 1); render(); }); + + deleteCell.appendChild(delButton); } } From 9f892071c4bee632cde78daa0d83fa47172046a6 Mon Sep 17 00:00:00 2001 From: Ogbemi mene Date: Wed, 5 Aug 2026 07:54:43 +0100 Subject: [PATCH 2/6] my new library html file --- debugging/book-library/index.html | 146 +++++++++++++++--------------- 1 file changed, 73 insertions(+), 73 deletions(-) diff --git a/debugging/book-library/index.html b/debugging/book-library/index.html index 23acfa71..6c31961a 100644 --- a/debugging/book-library/index.html +++ b/debugging/book-library/index.html @@ -1,15 +1,14 @@ - + - - + Library Books + + + + -
+

Library

Add books to your virtual library

-
+ + +
+ + +
+
+ + + + + + + + - +
+ + +
-
-
- - - - - - - - + type="button" + value="Submit" + class="btn btn-primary" + onclick="submit();" + /> +
-
- - - - - - - - - - - - - - - - - - - -
TitleAuthorNumber of PagesRead
+ + + + + + + + + + + + + +
TitleAuthorNumber of PagesReadActions
+
- + \ No newline at end of file From 58c952df2c064b448ba0f5acebf1e31bf8029e73 Mon Sep 17 00:00:00 2001 From: Ogbemi mene Date: Wed, 5 Aug 2026 14:16:21 +0100 Subject: [PATCH 3/6] my html commit --- debugging/book-library/index.html | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/debugging/book-library/index.html b/debugging/book-library/index.html index 6c31961a..25f1b76a 100644 --- a/debugging/book-library/index.html +++ b/debugging/book-library/index.html @@ -2,18 +2,15 @@ Library Books - - + + - - + + @@ -23,7 +20,7 @@

Library

- @@ -36,7 +33,7 @@

Library

id="title" name="title" required - /> + > Library id="author" name="author" required - /> + > Library id="pages" name="pages" required - /> + >
Library class="form-check-input" id="check" value="read" - /> + >
@@ -71,7 +68,7 @@

Library

value="Submit" class="btn btn-primary" onclick="submit();" - /> + > From 822472f2eea1f941b3ce446cc69e2f43ac9da148 Mon Sep 17 00:00:00 2001 From: Ogbemi mene Date: Wed, 5 Aug 2026 18:04:21 +0100 Subject: [PATCH 4/6] my commit --- debugging/book-library/index.html | 34 ++-- debugging/book-library/script.js | 298 ++++++++++++++++++++++-------- 2 files changed, 237 insertions(+), 95 deletions(-) diff --git a/debugging/book-library/index.html b/debugging/book-library/index.html index 25f1b76a..a04bf2ab 100644 --- a/debugging/book-library/index.html +++ b/debugging/book-library/index.html @@ -4,11 +4,11 @@ Library Books - + - + @@ -20,12 +20,17 @@

Library

-
-
+
Library required > - + Library required > - + Library required > -
+
Library
- -
+ +
@@ -88,6 +90,6 @@

Library

- + - \ No newline at end of file + diff --git a/debugging/book-library/script.js b/debugging/book-library/script.js index 07740a81..effc96fc 100644 --- a/debugging/book-library/script.js +++ b/debugging/book-library/script.js @@ -1,108 +1,248 @@ -let myLibrary = []; +class Book { + constructor(title, author, pages, check) { + this.title = title; + this.author = author; + this.pages = pages; + this.check = check; + } +} -window.addEventListener("load", function (e) { - populateStorage(); - render(); -}); +const myLibrary = []; + +// DOM Node References +const bookForm = document.getElementById("book-form"); +const titleInput = document.getElementById("title"); +const authorInput = document.getElementById("author"); +const pagesInput = document.getElementById("pages"); +const checkInput = document.getElementById("check"); +const displayTableBody = document.querySelector("#display tbody"); + +// ----------------------------------------------------------------------------- +// NON-BLOCKING NOTIFICATION SYSTEM (BOOTSTRAP 4 COMPATIBLE) +// ----------------------------------------------------------------------------- + +function showNotification(message, type = "success") { + const notificationArea = + document.getElementById("notification-area") || + (() => { + const el = document.createElement("div"); + el.id = "notification-area"; + el.style.cssText = + "position: fixed; top: 20px; right: 20px; z-index: 1060; max-width: 350px;"; + document.body.appendChild(el); + return el; + })(); + + const alertEl = document.createElement("div"); + alertEl.className = `alert alert-${type} alert-dismissible fade show shadow-sm mb-2`; + alertEl.role = "alert"; + + // Bootstrap 4 syntax: data-dismiss instead of data-bs-dismiss + alertEl.innerHTML = ` +
${message}
+ + `; + + notificationArea.appendChild(alertEl); + + setTimeout(() => { + alertEl.classList.remove("show"); + setTimeout(() => alertEl.remove(), 200); + }, 3500); +} + +// ----------------------------------------------------------------------------- +// LOCAL STORAGE PERSISTENCE +// ----------------------------------------------------------------------------- -function populateStorage() { - if (myLibrary.length == 0) { - let book1 = new Book("Robinson Crusoe", "Daniel Defoe", "252", true); - let book2 = new Book( +const STORAGE_KEY = "myLibraryData"; + +function saveLibraryToStorage() { + localStorage.setItem(STORAGE_KEY, JSON.stringify(myLibrary)); +} + +function loadLibraryFromStorage() { + const storedData = localStorage.getItem(STORAGE_KEY); + + if (storedData) { + const parsedData = JSON.parse(storedData); + myLibrary.length = 0; + parsedData.forEach((b) => + myLibrary.push(new Book(b.title, b.author, b.pages, b.check)) + ); + } else { + const book1 = new Book("Robinson Crusoe", "Daniel Defoe", 252, true); + const book2 = new Book( "The Old Man and the Sea", "Ernest Hemingway", - "127", + 127, true ); - myLibrary.push(book1); - myLibrary.push(book2); - render(); + myLibrary.push(book1, book2); + saveLibraryToStorage(); } } -const title = document.getElementById("title"); -const author = document.getElementById("author"); -const pages = document.getElementById("pages"); -const check = document.getElementById("check"); +// ----------------------------------------------------------------------------- +// PREPROCESSING PIPELINE (Sanitization, Normalization, & Validation) +// ----------------------------------------------------------------------------- + +function sanitizeString(str) { + if (typeof str !== "string") return ""; + return str + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'") + .replace(/\//g, "/"); +} -function submit() { - // Validate all required fields (title, author, and pages) - if (!title.value.trim() || !author.value.trim() || !pages.value.trim()) { - alert("Please fill all fields!"); - return false; - } else { - // Corrected to pass author.value and push to myLibrary - let book = new Book(title.value, author.value, pages.value, check.checked); - myLibrary.push(book); +function normalizeString(str) { + if (typeof str !== "string") return ""; + return str.trim().replace(/\s+/g, " "); +} + +function preprocessBookInput(rawTitle, rawAuthor, rawPages, rawCheck) { + const cleanTitle = sanitizeString(normalizeString(rawTitle)); + const cleanAuthor = sanitizeString(normalizeString(rawAuthor)); + const cleanPagesNum = Number(rawPages); + const cleanCheck = Boolean(rawCheck); - // Clear input fields after submission - title.value = ""; - author.value = ""; - pages.value = ""; - check.checked = false; + const errors = []; - render(); + if (cleanTitle === "") { + errors.push("Title cannot be empty or contain only space characters."); + } else if (cleanTitle.length > 150) { + errors.push("Title must be 150 characters or less."); } + + if (cleanAuthor === "") { + errors.push("Author cannot be empty or contain only space characters."); + } else if (cleanAuthor.length > 100) { + errors.push("Author must be 100 characters or less."); + } + + if ( + rawPages === "" || + isNaN(cleanPagesNum) || + cleanPagesNum <= 0 || + !Number.isInteger(cleanPagesNum) + ) { + errors.push("Pages must be a whole positive number greater than 0."); + } + + if (errors.length > 0) { + showNotification( + `Validation Error:
${errors.join("
")}`, + "danger" + ); + return null; + } + + return { + title: cleanTitle, + author: cleanAuthor, + pages: cleanPagesNum, + check: cleanCheck, + }; } -function Book(title, author, pages, check) { - this.title = title; - this.author = author; - this.pages = pages; - this.check = check; +// ----------------------------------------------------------------------------- +// EVENT HANDLERS & DYNAMIC RENDERING +// ----------------------------------------------------------------------------- + +function handleFormSubmit(event) { + event.preventDefault(); + + const processedData = preprocessBookInput( + titleInput.value, + authorInput.value, + pagesInput.value, + checkInput.checked + ); + + if (!processedData) return; + + const book = new Book( + processedData.title, + processedData.author, + processedData.pages, + processedData.check + ); + + myLibrary.push(book); + saveLibraryToStorage(); + bookForm.reset(); + render(); + + showNotification(`"${book.title}" added to your library.`, "success"); } function render() { - let table = document.getElementById("display"); - let rowsNumber = table.rows.length; + displayTableBody.innerHTML = ""; - // Fixed missing parenthesis in loop - for (let n = rowsNumber - 1; n > 0; n--) { - table.deleteRow(n); - } + const fragment = document.createDocumentFragment(); + + myLibrary.forEach((book, index) => { + const rowEl = document.createElement("tr"); - // Insert updated rows and cells - let length = myLibrary.length; - for (let i = 0; i < length; i++) { - let row = table.insertRow(-1); // Appends at the end of the table - let titleCell = row.insertCell(0); - let authorCell = row.insertCell(1); - let pagesCell = row.insertCell(2); - let wasReadCell = row.insertCell(3); - let deleteCell = row.insertCell(4); - - titleCell.innerHTML = myLibrary[i].title; - authorCell.innerHTML = myLibrary[i].author; - pagesCell.innerHTML = myLibrary[i].pages; - - // Toggle Read status button - let changeBut = document.createElement("button"); - changeBut.className = myLibrary[i].check - ? "btn btn-success" - : "btn btn-secondary"; - - // Corrected read status logic - let readStatus = myLibrary[i].check ? "Yes" : "No"; - changeBut.innerText = readStatus; - - changeBut.addEventListener("click", function () { - myLibrary[i].check = !myLibrary[i].check; + // Text cells (Title, Author, Pages) + ["title", "author", "pages"].forEach((prop) => { + const cellEl = document.createElement("td"); + cellEl.textContent = book[prop]; + rowEl.appendChild(cellEl); + }); + + // Read status toggle button + const wasReadCellEl = document.createElement("td"); + const toggleBtn = document.createElement("button"); + toggleBtn.type = "button"; + toggleBtn.className = book.check + ? "btn btn-success btn-sm" + : "btn btn-secondary btn-sm"; + toggleBtn.textContent = book.check ? "Yes" : "No"; + + toggleBtn.addEventListener("click", () => { + book.check = !book.check; + saveLibraryToStorage(); render(); }); - wasReadCell.appendChild(changeBut); + wasReadCellEl.appendChild(toggleBtn); + rowEl.appendChild(wasReadCellEl); - // Fixed variable names (delBut -> delButton) and event listener ("clicks" -> "click") - let delButton = document.createElement("button"); - delButton.className = "btn btn-danger"; - delButton.innerHTML = "Delete"; + // Delete button + const deleteCellEl = document.createElement("td"); + const deleteBtn = document.createElement("button"); + deleteBtn.type = "button"; + deleteBtn.className = "btn btn-danger btn-sm"; + deleteBtn.textContent = "Delete"; - delButton.addEventListener("click", function () { - alert(`You've deleted title: ${myLibrary[i].title}`); - myLibrary.splice(i, 1); + deleteBtn.addEventListener("click", () => { + const deletedTitle = book.title; + myLibrary.splice(index, 1); + saveLibraryToStorage(); render(); + showNotification(`"${deletedTitle}" deleted successfully.`, "info"); }); - deleteCell.appendChild(delButton); - } + deleteCellEl.appendChild(deleteBtn); + rowEl.appendChild(deleteCellEl); + + fragment.appendChild(rowEl); + }); + + displayTableBody.appendChild(fragment); +} + +document.addEventListener("DOMContentLoaded", () => { + loadLibraryFromStorage(); + render(); +}); + +if (bookForm) { + bookForm.addEventListener("submit", handleFormSubmit); } From 976813784cd2287772894083a705807f87911fa9 Mon Sep 17 00:00:00 2001 From: Ogbemi mene Date: Thu, 6 Aug 2026 12:36:03 +0100 Subject: [PATCH 5/6] new commit on index.html --- debugging/book-library/index.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/debugging/book-library/index.html b/debugging/book-library/index.html index a04bf2ab..a883b7ba 100644 --- a/debugging/book-library/index.html +++ b/debugging/book-library/index.html @@ -55,6 +55,8 @@

Library

class="form-control" id="pages" name="pages" + min="1" + step="1" required > @@ -92,4 +94,4 @@

Library

- + \ No newline at end of file From eab5d5db1a4fa1ec14e7b7791cf3a5295ad2e70e Mon Sep 17 00:00:00 2001 From: Ogbemi mene Date: Thu, 6 Aug 2026 12:36:38 +0100 Subject: [PATCH 6/6] correction to the js script --- debugging/book-library/script.js | 44 +++++++++++++------------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/debugging/book-library/script.js b/debugging/book-library/script.js index effc96fc..df5ebf65 100644 --- a/debugging/book-library/script.js +++ b/debugging/book-library/script.js @@ -86,28 +86,17 @@ function loadLibraryFromStorage() { } // ----------------------------------------------------------------------------- -// PREPROCESSING PIPELINE (Sanitization, Normalization, & Validation) +// PREPROCESSING PIPELINE (Normalization & Validation) // ----------------------------------------------------------------------------- -function sanitizeString(str) { - if (typeof str !== "string") return ""; - return str - .replace(/&/g, "&") - .replace(//g, ">") - .replace(/"/g, """) - .replace(/'/g, "'") - .replace(/\//g, "/"); -} - function normalizeString(str) { if (typeof str !== "string") return ""; return str.trim().replace(/\s+/g, " "); } function preprocessBookInput(rawTitle, rawAuthor, rawPages, rawCheck) { - const cleanTitle = sanitizeString(normalizeString(rawTitle)); - const cleanAuthor = sanitizeString(normalizeString(rawAuthor)); + const cleanTitle = normalizeString(rawTitle); + const cleanAuthor = normalizeString(rawAuthor); const cleanPagesNum = Number(rawPages); const cleanCheck = Boolean(rawCheck); @@ -125,12 +114,7 @@ function preprocessBookInput(rawTitle, rawAuthor, rawPages, rawCheck) { errors.push("Author must be 100 characters or less."); } - if ( - rawPages === "" || - isNaN(cleanPagesNum) || - cleanPagesNum <= 0 || - !Number.isInteger(cleanPagesNum) - ) { + if (!Number.isInteger(cleanPagesNum) || cleanPagesNum <= 0) { errors.push("Pages must be a whole positive number greater than 0."); } @@ -178,7 +162,9 @@ function handleFormSubmit(event) { bookForm.reset(); render(); - showNotification(`"${book.title}" added to your library.`, "success"); + const tempDiv = document.createElement("div"); + tempDiv.textContent = book.title; + showNotification(`"${tempDiv.innerHTML}" added to your library.`, "success"); } function render() { @@ -226,7 +212,10 @@ function render() { myLibrary.splice(index, 1); saveLibraryToStorage(); render(); - showNotification(`"${deletedTitle}" deleted successfully.`, "info"); + + const tempDiv = document.createElement("div"); + tempDiv.textContent = deletedTitle; + showNotification(`"${tempDiv.innerHTML}" deleted successfully.`, "info"); }); deleteCellEl.appendChild(deleteBtn); @@ -241,8 +230,11 @@ function render() { document.addEventListener("DOMContentLoaded", () => { loadLibraryFromStorage(); render(); -}); -if (bookForm) { - bookForm.addEventListener("submit", handleFormSubmit); -} + // Guard check placed directly within the listener assignment + if (bookForm) { + bookForm.addEventListener("submit", (event) => { + handleFormSubmit(event); + }); + } +});