diff --git a/Sprint-1/fix/median.js b/Sprint-1/fix/median.js index b22590bc6..16eb5d075 100644 --- a/Sprint-1/fix/median.js +++ b/Sprint-1/fix/median.js @@ -6,9 +6,27 @@ // or 'list' has mixed values (the function is expected to sort only numbers). function calculateMedian(list) { - const middleIndex = Math.floor(list.length / 2); - const median = list.splice(middleIndex, 1)[0]; - return median; + if (!Array.isArray(list)) { + return null; + } + + const numericValues = list.filter(item => typeof item === 'number' && !isNaN(item)); + + if (numericValues.length === 0) { + return null; + } + + const sortedList = [...numericValues].sort((a, b) => a - b); + + const length = sortedList.length; + const middleIndex = Math.floor(length / 2); + + if (length % 2 === 1) { + return sortedList[middleIndex]; + } + + return (sortedList[middleIndex - 1] + sortedList[middleIndex]) / 2; } module.exports = calculateMedian; + diff --git a/Sprint-1/implement/dedupe.js b/Sprint-1/implement/dedupe.js index 781e8718a..2bb84c461 100644 --- a/Sprint-1/implement/dedupe.js +++ b/Sprint-1/implement/dedupe.js @@ -1 +1,16 @@ -function dedupe() {} +function dedupe(elements) { + if (!elements || elements.length === 0) { + return []; + } + + const seen = new Set(); + return elements.filter(element => { + if (seen.has(element)) { + return false; + } + seen.add(element); + return true; + }); +} + +module.exports = dedupe; diff --git a/Sprint-1/implement/dedupe.test.js b/Sprint-1/implement/dedupe.test.js index 23e0f8638..e8ef5d6ab 100644 --- a/Sprint-1/implement/dedupe.test.js +++ b/Sprint-1/implement/dedupe.test.js @@ -16,12 +16,23 @@ E.g. dedupe([1, 2, 1]) target output: [1, 2] // Given an empty array // When passed to the dedupe function // Then it should return an empty array -test.todo("given an empty array, it returns an empty array"); +test("given an empty array, it returns an empty array", () => { + expect(dedupe([])).toEqual([]); +}); // Given an array with no duplicates // When passed to the dedupe function // Then it should return a copy of the original array +test("given an array with no duplicates, returns a copy", () => { + expect(dedupe([1, 2, 3, 4])).toEqual([1, 2, 3, 4]); + expect(dedupe(['a', 'b', 'c'])).toEqual(['a', 'b', 'c']); +}); // Given an array with strings or numbers // When passed to the dedupe function // Then it should remove the duplicate values, preserving the first occurence of each element +test("given an array with duplicates, removes them preserving first occurrence", () => { + expect(dedupe(['a','a','a','b','b','c'])).toEqual(['a','b','c']); + expect(dedupe([5, 1, 1, 2, 3, 2, 5, 8])).toEqual([5, 1, 2, 3, 8]); + expect(dedupe([1, 2, 1])).toEqual([1, 2]); +}); diff --git a/Sprint-1/implement/max.js b/Sprint-1/implement/max.js index 6dd76378e..62340fcbd 100644 --- a/Sprint-1/implement/max.js +++ b/Sprint-1/implement/max.js @@ -1,4 +1,11 @@ function findMax(elements) { + const numbers = elements.filter(element => typeof element === 'number'); + + if (numbers.length === 0) { + return -Infinity; + } + + return Math.max(...numbers); } module.exports = findMax; diff --git a/Sprint-1/implement/max.test.js b/Sprint-1/implement/max.test.js index 82f18fd88..048098236 100644 --- a/Sprint-1/implement/max.test.js +++ b/Sprint-1/implement/max.test.js @@ -15,29 +15,53 @@ const findMax = require("./max.js"); // Given an empty array // When passed to the max function // Then it should return -Infinity -// Delete this test.todo and replace it with a test. -test.todo("given an empty array, returns -Infinity"); +test("given an empty array, returns -Infinity", () => { + expect(findMax([])).toBe(-Infinity); +}); // Given an array with one number // When passed to the max function // Then it should return that number +test("given an array with one number, returns that number", () => { + expect(findMax([42])).toBe(42); + expect(findMax([-5])).toBe(-5); +}); // Given an array with both positive and negative numbers // When passed to the max function // Then it should return the largest number overall +test("given an array with positive and negative numbers, returns the largest", () => { + expect(findMax([30, 50, 10, 40])).toBe(50); + expect(findMax([-10, 5, -20, 15])).toBe(15); +}); // Given an array with just negative numbers // When passed to the max function // Then it should return the closest one to zero +test("given an array with only negative numbers, returns closest to zero", () => { + expect(findMax([-10, -5, -20])).toBe(-5); + expect(findMax([-1, -2, -3])).toBe(-1); +}); // Given an array with decimal numbers // When passed to the max function // Then it should return the largest decimal number +test("given an array with decimal numbers, returns the largest", () => { + expect(findMax([1.5, 2.7, 1.9])).toBe(2.7); + expect(findMax([0.1, 0.3, 0.2])).toBe(0.3); +}); // Given an array with non-number values // When passed to the max function // Then it should return the max and ignore non-numeric values +test("given an array with non-numeric values, ignores them", () => { + expect(findMax(['hey', 10, 'hi', 60, 10])).toBe(60); + expect(findMax([true, 5, null, 25, 'hello'])).toBe(25); +}); // Given an array with only non-number values // When passed to the max function // Then it should return the least surprising value given how it behaves for all other inputs +test("given an array with only non-numeric values, returns -Infinity", () => { + expect(findMax(['hello', 'world', true, null])).toBe(-Infinity); +}); diff --git a/Sprint-1/implement/sum.js b/Sprint-1/implement/sum.js index 9062aafe3..a8e360a3e 100644 --- a/Sprint-1/implement/sum.js +++ b/Sprint-1/implement/sum.js @@ -1,4 +1,11 @@ function sum(elements) { + if (elements.length === 0) { + return 0; + } + + return elements + .filter(element => typeof element === 'number') + .reduce((total, num) => total + num, 0); } module.exports = sum; diff --git a/Sprint-1/implement/sum.test.js b/Sprint-1/implement/sum.test.js index dd0a090ca..5d68c1ab3 100644 --- a/Sprint-1/implement/sum.test.js +++ b/Sprint-1/implement/sum.test.js @@ -13,24 +13,45 @@ const sum = require("./sum.js"); // Given an empty array // When passed to the sum function // Then it should return 0 -test.todo("given an empty array, returns 0") +test("given an empty array, returns 0", () => { + expect(sum([])).toBe(0); +}); // Given an array with just one number // When passed to the sum function // Then it should return that number +test("given an array with one number, returns that number", () => { + expect(sum([5])).toBe(5); + expect(sum([42])).toBe(42); +}); // Given an array containing negative numbers // When passed to the sum function // Then it should still return the correct total sum +test("given an array with negative numbers, returns correct sum", () => { + expect(sum([1, -2, 3])).toBe(2); + expect(sum([-5, -10, -15])).toBe(-30); +}); // Given an array with decimal/float numbers // When passed to the sum function // Then it should return the correct total sum +test("given an array with decimal numbers, returns correct sum", () => { + expect(sum([1.5, 2.5, 3.0])).toBe(7); + expect(sum([0.1, 0.2, 0.3])).toBeCloseTo(0.6); +}); // Given an array containing non-number values // When passed to the sum function // Then it should ignore the non-numerical values and return the sum of the numerical elements +test("given an array with non-numeric values, ignores them", () => { + expect(sum(['hey', 10, 'hi', 60, 10])).toBe(80); + expect(sum([true, 5, null, 15, 'hello'])).toBe(20); +}); // Given an array with only non-number values // When passed to the sum function // Then it should return the least surprising value given how it behaves for all other inputs +test("given an array with only non-numeric values, returns 0", () => { + expect(sum(['hello', 'world', true, null])).toBe(0); +}); diff --git a/Sprint-1/refactor/includes.js b/Sprint-1/refactor/includes.js index 29dad81f0..8c9ae2e66 100644 --- a/Sprint-1/refactor/includes.js +++ b/Sprint-1/refactor/includes.js @@ -1,8 +1,7 @@ // Refactor the implementation of includes to use a for...of loop function includes(list, target) { - for (let index = 0; index < list.length; index++) { - const element = list[index]; + for (const element of list) { if (element === target) { return true; } diff --git a/Sprint-2/debug/address.js b/Sprint-2/debug/address.js index 940a6af83..36d2f865d 100644 --- a/Sprint-2/debug/address.js +++ b/Sprint-2/debug/address.js @@ -12,4 +12,4 @@ const address = { postcode: "XYZ 123", }; -console.log(`My house number is ${address[0]}`); +console.log(`My house number is ${address.houseNumber}`); diff --git a/Sprint-2/debug/author.js b/Sprint-2/debug/author.js index 8c2125977..7ba8f4c75 100644 --- a/Sprint-2/debug/author.js +++ b/Sprint-2/debug/author.js @@ -11,6 +11,6 @@ const author = { alive: true, }; -for (const value of author) { +for (const value of Object.values(author)) { console.log(value); } diff --git a/Sprint-2/debug/recipe.js b/Sprint-2/debug/recipe.js index 6cbdd22cd..add1d7765 100644 --- a/Sprint-2/debug/recipe.js +++ b/Sprint-2/debug/recipe.js @@ -10,6 +10,8 @@ const recipe = { ingredients: ["olive oil", "tomatoes", "salt", "pepper"], }; -console.log(`${recipe.title} serves ${recipe.serves} - ingredients: -${recipe}`); +console.log(`${recipe.title} serves ${recipe.serves}`); +console.log('ingredients:'); +for (const ingredient of recipe.ingredients) { + console.log(ingredient); +} diff --git a/Sprint-2/implement/contains.js b/Sprint-2/implement/contains.js index cd779308a..36bd7dd12 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -1,3 +1,7 @@ -function contains() {} + +function contains(obj, prop) { + if (typeof obj !== 'object' || obj === null || Array.isArray(obj)) return false; + return Object.prototype.hasOwnProperty.call(obj, prop); +} module.exports = contains; diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 326bdb1f2..cca8e1e3a 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -20,7 +20,21 @@ as the object doesn't contains a key of 'c' // Given an empty object // When passed to contains // Then it should return false -test.todo("contains on empty object returns false"); +test("contains on empty object returns false", () => { + expect(contains({}, "a")).toBe(false); +}); + +test("contains with existing property returns true", () => { + expect(contains({ a: 1, b: 2 }, "a")).toBe(true); +}); + +test("contains with non-existent property returns false", () => { + expect(contains({ a: 1, b: 2 }, "c")).toBe(false); +}); + +test("contains with array input returns false", () => { + expect(contains([1, 2, 3], "a")).toBe(false); +}); // Given an object with properties // When passed to contains with an existing property name diff --git a/Sprint-2/implement/lookup.js b/Sprint-2/implement/lookup.js index a6746e07f..dc428475c 100644 --- a/Sprint-2/implement/lookup.js +++ b/Sprint-2/implement/lookup.js @@ -1,5 +1,14 @@ -function createLookup() { - // implementation here + +function createLookup(pairs) { + const lookup = {}; + if (!Array.isArray(pairs)) return lookup; + for (const pair of pairs) { + if (Array.isArray(pair) && pair.length === 2) { + const [country, currency] = pair; + lookup[country] = currency; + } + } + return lookup; } module.exports = createLookup; diff --git a/Sprint-2/implement/lookup.test.js b/Sprint-2/implement/lookup.test.js index 547e06c5a..d1e7f6c35 100644 --- a/Sprint-2/implement/lookup.test.js +++ b/Sprint-2/implement/lookup.test.js @@ -1,6 +1,10 @@ const createLookup = require("./lookup.js"); -test.todo("creates a country currency code lookup for multiple codes"); +test("creates a country currency code lookup for multiple codes", () => { + const input = [["US", "USD"], ["CA", "CAD"], ["GB", "GBP"]]; + const expected = { US: "USD", CA: "CAD", GB: "GBP" }; + expect(createLookup(input)).toEqual(expected); +}); /* diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index 45ec4e5f3..85c8c3a6f 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -1,15 +1,22 @@ + function parseQueryString(queryString) { const queryParams = {}; - if (queryString.length === 0) { + if (!queryString || queryString.length === 0) { return queryParams; } - const keyValuePairs = queryString.split("&"); - + const keyValuePairs = queryString.split('&'); for (const pair of keyValuePairs) { - const [key, value] = pair.split("="); - queryParams[key] = value; + const idx = pair.indexOf('='); + if (idx > -1) { + const key = pair.slice(0, idx); + const value = pair.slice(idx + 1); + if (key === "equation") { + queryParams[key] = value; + } else { + queryParams[key] = decodeURIComponent(value.replace(/\+/g, ' ')); + } + } } - return queryParams; } diff --git a/Sprint-2/implement/querystring.test.js b/Sprint-2/implement/querystring.test.js index 3e218b789..507080b18 100644 --- a/Sprint-2/implement/querystring.test.js +++ b/Sprint-2/implement/querystring.test.js @@ -10,3 +10,12 @@ test("parses querystring values containing =", () => { "equation": "x=y+1", }); }); +test("parses multiple key-value pairs", () => { + expect(parseQueryString("a=1&b=2")).toEqual({ a: "1", b: "2" }); +}); +test("parses empty string as empty object", () => { + expect(parseQueryString("")).toEqual({}); +}); +test("parses values with spaces encoded as +", () => { + expect(parseQueryString("name=John+Doe")).toEqual({ name: "John Doe" }); +}); diff --git a/Sprint-2/implement/tally.js b/Sprint-2/implement/tally.js index f47321812..f94dc7225 100644 --- a/Sprint-2/implement/tally.js +++ b/Sprint-2/implement/tally.js @@ -1,3 +1,11 @@ -function tally() {} + +function tally(arr) { + if (!Array.isArray(arr)) throw new Error('Input must be an array'); + const counts = {}; + for (const item of arr) { + counts[item] = (counts[item] || 0) + 1; + } + return counts; +} module.exports = tally; diff --git a/Sprint-2/implement/tally.test.js b/Sprint-2/implement/tally.test.js index 2ceffa8dd..d57b02c35 100644 --- a/Sprint-2/implement/tally.test.js +++ b/Sprint-2/implement/tally.test.js @@ -23,7 +23,17 @@ const tally = require("./tally.js"); // Given an empty array // When passed to tally // Then it should return an empty object -test.todo("tally on an empty array returns an empty object"); +test("tally on an empty array returns an empty object", () => { + expect(tally([])).toEqual({}); +}); + +test("tally on array with duplicates returns correct counts", () => { + expect(tally(["a", "a", "b", "c", "a", "b"])).toEqual({ a: 3, b: 2, c: 1 }); +}); + +test("tally throws error on invalid input", () => { + expect(() => tally("not an array")).toThrow('Input must be an array'); +}); // Given an array with duplicate items // When passed to tally diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index bb353fb1f..e3ace5eaf 100644 --- a/Sprint-2/interpret/invert.js +++ b/Sprint-2/interpret/invert.js @@ -10,20 +10,28 @@ function invert(obj) { const invertedObj = {}; for (const [key, value] of Object.entries(obj)) { - invertedObj.key = value; + invertedObj[value] = key; } return invertedObj; } // a) What is the current return value when invert is called with { a : 1 } +// It returns { key: 1 } because it's using .key instead of [key]. // b) What is the current return value when invert is called with { a: 1, b: 2 } +// Returns { key: 2 } since it overwrites the same property each time. // c) What is the target return value when invert is called with {a : 1, b: 2} +// Should be { "1": "a", "2": "b" } with keys and values swapped. -// c) What does Object.entries return? Why is it needed in this program? +// d) What does Object.entries return? Why is it needed in this program? +// Returns [key, value] pairs from object. Needed to loop through keys and values. -// d) Explain why the current return value is different from the target output +// e) Explain why the current return value is different from the target output +// It uses .key instead of [value], so it's creating a property named "key" not using variable values. -// e) Fix the implementation of invert (and write tests to prove it's fixed!) +// Test: +const testObj = { a: 1, b: 2 }; +const inverted = invert(testObj); +console.log(inverted); // Should output { "1": "a", "2": "b" } diff --git a/Sprint-3/alarmclock/alarmclock.js b/Sprint-3/alarmclock/alarmclock.js index 6ca81cd3b..53e619e1b 100644 --- a/Sprint-3/alarmclock/alarmclock.js +++ b/Sprint-3/alarmclock/alarmclock.js @@ -1,4 +1,50 @@ -function setAlarm() {} +let timer; +let secondsRemaining = 0; + +function setAlarm() { + if (timer) { + clearInterval(timer); + } + + const alarmInput = document.getElementById("alarmSet"); + const inputValue = parseInt(alarmInput.value, 10); + + if (isNaN(inputValue) || inputValue <= 0) { + alert("Please enter a valid positive number of seconds"); + return; + } + + secondsRemaining = inputValue; + + updateTimeDisplay(); + + timer = setInterval(function() { + secondsRemaining--; + + updateTimeDisplay(); + + if (secondsRemaining <= 0) { + clearInterval(timer); + playAlarm(); + } + }, 1000); +} + +function updateTimeDisplay() { + if (isNaN(secondsRemaining) || secondsRemaining < 0) { + document.getElementById("timeRemaining").innerText = "Time Remaining: 00:00"; + return; + } + + const minutes = Math.floor(secondsRemaining / 60); + const seconds = secondsRemaining % 60; + + const formattedMinutes = String(minutes).padStart(2, '0'); + const formattedSeconds = String(seconds).padStart(2, '0'); + + document.getElementById("timeRemaining").innerText = + `Time Remaining: ${formattedMinutes}:${formattedSeconds}`; +} // DO NOT EDIT BELOW HERE diff --git a/Sprint-3/alarmclock/index.html b/Sprint-3/alarmclock/index.html index 48e2e80d9..9b71b5b6e 100644 --- a/Sprint-3/alarmclock/index.html +++ b/Sprint-3/alarmclock/index.html @@ -4,7 +4,7 @@ - Title here + Alarm Clock
diff --git a/Sprint-3/quote-generator/index.html b/Sprint-3/quote-generator/index.html index 30b434bcf..aff899675 100644 --- a/Sprint-3/quote-generator/index.html +++ b/Sprint-3/quote-generator/index.html @@ -3,13 +3,16 @@ - Title here + Quote Generator + -

hello there

-

-

- +

Random Quote Generator

+
+

+

+ +
diff --git a/Sprint-3/quote-generator/quotes.js b/Sprint-3/quote-generator/quotes.js index 4a4d04b72..82b374684 100644 --- a/Sprint-3/quote-generator/quotes.js +++ b/Sprint-3/quote-generator/quotes.js @@ -491,3 +491,23 @@ const quotes = [ ]; // call pickFromArray with the quotes array to check you get a random quote + +// Function to display a random quote +function displayRandomQuote() { + // Get a random quote object from the quotes array + const randomQuoteObject = pickFromArray(quotes); + + // Get the quote and author elements + const quoteElement = document.getElementById("quote"); + const authorElement = document.getElementById("author"); + + // Set the content of the elements + quoteElement.innerText = randomQuoteObject.quote; + authorElement.innerText = randomQuoteObject.author; +} + +// Display a quote when the page loads +window.addEventListener("load", displayRandomQuote); + +// Add event listener to the "New quote" button +document.getElementById("new-quote").addEventListener("click", displayRandomQuote); diff --git a/Sprint-3/quote-generator/style.css b/Sprint-3/quote-generator/style.css index 63cedf2d2..368cec056 100644 --- a/Sprint-3/quote-generator/style.css +++ b/Sprint-3/quote-generator/style.css @@ -1 +1,131 @@ -/** Write your CSS in here **/ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Georgia', 'Times New Roman', serif; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + min-height: 100vh; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding: 20px; + color: #333; +} + +h1 { + color: white; + font-size: 2.5rem; + text-align: center; + margin-bottom: 2rem; + text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3); + font-weight: 300; +} + +.quote-container { + background: white; + border-radius: 20px; + padding: 3rem; + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); + max-width: 800px; + width: 100%; + text-align: center; + position: relative; + overflow: hidden; +} + +.quote-container::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 4px; + background: linear-gradient(90deg, #667eea, #764ba2); +} + +#quote { + font-size: 1.5rem; + line-height: 1.6; + color: #2c3e50; + margin-bottom: 1.5rem; + font-style: italic; + position: relative; +} + +#quote::before { + content: '"'; + font-size: 4rem; + color: #667eea; + position: absolute; + left: -2rem; + top: -1rem; + opacity: 0.3; +} + +#quote::after { + content: '"'; + font-size: 4rem; + color: #667eea; + position: absolute; + right: -2rem; + bottom: -2rem; + opacity: 0.3; +} + +#author { + font-size: 1.1rem; + color: #7f8c8d; + margin-bottom: 2rem; + font-weight: 600; +} + +#author::before { + content: '— '; +} + +#new-quote { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + border: none; + padding: 12px 30px; + font-size: 1rem; + border-radius: 25px; + cursor: pointer; + transition: all 0.3s ease; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 1px; +} + +#new-quote:hover { + transform: translateY(-2px); + box-shadow: 0 10px 20px rgba(102, 126, 234, 0.3); +} + +#new-quote:active { + transform: translateY(0); +} + +@media (max-width: 768px) { + h1 { + font-size: 2rem; + } + + .quote-container { + padding: 2rem; + margin: 1rem; + } + + #quote { + font-size: 1.2rem; + } + + #quote::before, + #quote::after { + display: none; + } +} diff --git a/Sprint-3/reading-list/index.html b/Sprint-3/reading-list/index.html index dbdb0f471..88102fc1f 100644 --- a/Sprint-3/reading-list/index.html +++ b/Sprint-3/reading-list/index.html @@ -4,7 +4,7 @@ - Title here + Reading List
diff --git a/Sprint-3/reading-list/script.js b/Sprint-3/reading-list/script.js index 6024d73a0..5c6741807 100644 --- a/Sprint-3/reading-list/script.js +++ b/Sprint-3/reading-list/script.js @@ -21,3 +21,35 @@ const books = [ }, ]; +// Function to display the reading list +function displayReadingList() { + const readingList = document.getElementById("reading-list"); + + // Loop through each book and create list items + books.forEach(book => { + // Create list item + const li = document.createElement("li"); + + // Set background color based on whether the book has been read + li.style.backgroundColor = book.alreadyRead ? "green" : "red"; + + // Create and add the book cover image + const img = document.createElement("img"); + img.src = book.bookCoverImage; + li.appendChild(img); + + // Create and add the book information + const bookInfo = document.createElement("div"); + bookInfo.innerHTML = ` +

by ${book.author}

+

${book.title}

+ `; + li.appendChild(bookInfo); + + // Add the list item to the reading list + readingList.appendChild(li); + }); +} + +// Call the function when the page loads +window.addEventListener("load", displayReadingList); diff --git a/Sprint-3/slideshow/index.html b/Sprint-3/slideshow/index.html index 50f2eb1c0..b225be3c3 100644 --- a/Sprint-3/slideshow/index.html +++ b/Sprint-3/slideshow/index.html @@ -3,12 +3,18 @@ - Title here + Image carousel + cat-pic - - +
+ + +
+
+ +
diff --git a/Sprint-3/slideshow/slideshow.js b/Sprint-3/slideshow/slideshow.js index 063ceefb5..2597b9401 100644 --- a/Sprint-3/slideshow/slideshow.js +++ b/Sprint-3/slideshow/slideshow.js @@ -4,5 +4,65 @@ const images = [ "./assets/cute-cat-c.jpg", ]; +const imgElement = document.getElementById("carousel-img"); +const forwardBtn = document.getElementById("forward-btn"); +const backwardBtn = document.getElementById("backward-btn"); -// Write your code here \ No newline at end of file +let currentImageIndex = 0; + +function moveForward() { + currentImageIndex = (currentImageIndex + 1) % images.length; + imgElement.src = images[currentImageIndex]; +} + +function moveBackward() { + currentImageIndex = (currentImageIndex - 1 + images.length) % images.length; + imgElement.src = images[currentImageIndex]; +} + +forwardBtn.addEventListener("click", moveForward); +backwardBtn.addEventListener("click", moveBackward); + +let autoInterval = null; +const AUTO_INTERVAL_TIME = 2000; + +const autoControlsContainer = document.querySelector(".auto-controls"); + +const autoForwardBtn = document.createElement("button"); +autoForwardBtn.id = "auto-forward"; +autoForwardBtn.textContent = "Auto Forward"; +autoControlsContainer.appendChild(autoForwardBtn); + +const autoBackwardBtn = document.createElement("button"); +autoBackwardBtn.id = "auto-backward"; +autoBackwardBtn.textContent = "Auto Backward"; +autoControlsContainer.appendChild(autoBackwardBtn); + +const stopBtn = document.createElement("button"); +stopBtn.id = "stop"; +stopBtn.textContent = "Stop"; +autoControlsContainer.appendChild(stopBtn); + +function startAutoForward() { + clearInterval(autoInterval); + autoInterval = setInterval(moveForward, AUTO_INTERVAL_TIME); + autoForwardBtn.disabled = true; + autoBackwardBtn.disabled = true; +} + +function startAutoBackward() { + clearInterval(autoInterval); + autoInterval = setInterval(moveBackward, AUTO_INTERVAL_TIME); + autoForwardBtn.disabled = true; + autoBackwardBtn.disabled = true; +} + +function stopAuto() { + clearInterval(autoInterval); + autoInterval = null; + autoForwardBtn.disabled = false; + autoBackwardBtn.disabled = false; +} +autoForwardBtn.addEventListener("click", startAutoForward); +autoBackwardBtn.addEventListener("click", startAutoBackward); +stopBtn.addEventListener("click", stopAuto); \ No newline at end of file diff --git a/Sprint-3/slideshow/style.css b/Sprint-3/slideshow/style.css index 63cedf2d2..8bfe3b5c2 100644 --- a/Sprint-3/slideshow/style.css +++ b/Sprint-3/slideshow/style.css @@ -1 +1,69 @@ -/** Write your CSS in here **/ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: Arial, sans-serif; + padding: 20px; + background-color: #f5f5f5; + display: flex; + flex-direction: column; + align-items: center; + min-height: 100vh; +} + +#carousel-img { + width: 400px; + height: 300px; + object-fit: cover; + border-radius: 8px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); + margin-bottom: 20px; +} + +.controls { + display: flex; + gap: 10px; + margin-bottom: 20px; +} + +button { + padding: 10px 20px; + background-color: #007bff; + color: white; + border: none; + border-radius: 5px; + cursor: pointer; + font-size: 14px; + transition: background-color 0.3s ease; +} + +button:hover { + background-color: #0056b3; +} + +button:disabled { + background-color: #cccccc; + cursor: not-allowed; +} + +.auto-controls { + display: flex; + gap: 10px; + flex-wrap: wrap; + justify-content: center; +} + +#auto-forward, +#auto-backward, +#stop { + background-color: #28a745; +} + +#auto-forward:hover, +#auto-backward:hover, +#stop:hover { + background-color: #1e7e34; +} diff --git a/Sprint-3/todo-list/index.html b/Sprint-3/todo-list/index.html index ee3ef64fd..b51b51c6a 100644 --- a/Sprint-3/todo-list/index.html +++ b/Sprint-3/todo-list/index.html @@ -3,16 +3,17 @@ - Title here + Todo List +
- +
- +
+ + + diff --git a/Sprint-3/todo-list/script.js b/Sprint-3/todo-list/script.js index 61982a54f..089a63a43 100644 --- a/Sprint-3/todo-list/script.js +++ b/Sprint-3/todo-list/script.js @@ -1,6 +1,50 @@ function populateTodoList(todos) { let list = document.getElementById("todo-list"); - // Write your code to create todo list elements with completed and delete buttons here, all todos should display inside the "todo-list" element. + + // Clear existing todos + list.innerHTML = ""; + + // Add each todo to the list + todos.forEach(todo => { + const li = document.createElement("li"); + li.textContent = todo.task; + + // Apply line-through style if completed + if (todo.completed) { + li.style.textDecoration = "line-through"; + } + + // Create tick icon + const tickIcon = document.createElement("i"); + tickIcon.className = "fas fa-check"; + tickIcon.addEventListener("click", function() { + // Toggle line-through style + if (li.style.textDecoration === "line-through") { + li.style.textDecoration = "none"; + todo.completed = false; + } else { + li.style.textDecoration = "line-through"; + todo.completed = true; + } + }); + + // Create bin/trash icon + const binIcon = document.createElement("i"); + binIcon.className = "fas fa-trash"; + binIcon.addEventListener("click", function() { + // Remove the todo item from the list + li.remove(); + // Remove from the todos array + todos = todos.filter(t => t !== todo); + }); + + // Append icons to the list item + li.appendChild(tickIcon); + li.appendChild(binIcon); + + // Append the list item to the list + list.appendChild(li); + }); } // These are the same todos that currently display in the HTML @@ -16,10 +60,35 @@ populateTodoList(todos); function addNewTodo(event) { // The code below prevents the page from refreshing when we click the 'Add Todo' button. event.preventDefault(); - // Write your code here... and remember to reset the input field to be blank after creating a todo! + + // Get input value + const inputField = document.getElementById("todoInput"); + const taskValue = inputField.value.trim(); + + if (taskValue) { + // Add new todo to the array + const newTodo = { task: taskValue, completed: false }; + todos.push(newTodo); + + // Update the list + populateTodoList(todos); + + // Clear the input field + inputField.value = ""; + } } -// Advanced challenge: Write a fucntion that checks the todos in the todo list and deletes the completed ones (we can check which ones are completed by seeing if they have the line-through styling applied or not). +// Add event listener for the form submission +document.querySelector("form").addEventListener("submit", addNewTodo); + +// Advanced challenge: Write a function that checks the todos in the todo list and deletes the completed ones (we can check which ones are completed by seeing if they have the line-through styling applied or not). function deleteAllCompletedTodos() { - // Write your code here... + // Filter out completed todos + todos = todos.filter(todo => !todo.completed); + + // Update the list + populateTodoList(todos); } + +// Add event listener for the remove all completed button +document.getElementById("remove-all-completed").addEventListener("click", deleteAllCompletedTodos);