-
-
Notifications
You must be signed in to change notification settings - Fork 124
London | 25-ITP-May | Houssam Lahlah | Sprint 3 | Java script challenges #317
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
240363a
fcddc49
40f6471
5f529e0
95f0aaf
01b4bca
38caffc
94fecf5
cb4209c
8464e8f
9df60a0
b2d41e2
13aedf1
935adda
b4029f0
2f4c247
2c324be
4cef7e5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,30 +1,35 @@ | ||
// ================= | ||
// Stripped down cowsayer CLI, | ||
// no libraries | ||
// https://nodejs.dev/learn/nodejs-accept-arguments-from-the-command-line | ||
// ================= | ||
// cowsay.js | ||
|
||
// 1. Accept arguments | ||
const userInput = process.argv.slice(2).join(' '); | ||
|
||
// 2. Make speech bubble | ||
function makeBubble(text) { | ||
if (!text) text = "Moo!"; | ||
const len = text.length; | ||
const top = ' ' + '_'.repeat(len + 2); | ||
const middle = `| ${text} |`; | ||
const bottom = ' ' + '-'.repeat(len + 2); | ||
return `${top}\n${middle}\n${bottom}`; | ||
} | ||
|
||
// how will you accept arguments? | ||
|
||
// 2. Make supplies for our speech bubble | ||
|
||
let topLine = '_'; | ||
let bottomLine = '-'; | ||
let saying = ''; | ||
|
||
// 3. Make a cow that takes a string | ||
// 3. Make a simple cow | ||
function makeCow() { | ||
return ` | ||
\\ ^__^ | ||
\\ (oo)\\_______ | ||
(__)\\ )\\/\\ | ||
||----w | | ||
|| || | ||
`; | ||
} | ||
|
||
// 4. Combine bubble + cow | ||
function cowsay(saying) { | ||
// how will you make the speech bubble contain the text? | ||
|
||
// where will the cow picture go? | ||
|
||
// how will you account for the parameter being empty? | ||
|
||
const bubble = makeBubble(saying); | ||
const cow = makeCow(); | ||
return bubble + cow; | ||
} | ||
|
||
//4. Pipe argument into cowsay function and return a cow | ||
|
||
// how will you log this to the console? | ||
// 5. Log to console | ||
console.log(cowsay(userInput)); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,32 @@ | ||
// ================= | ||
// Stripped down cowsayer CLI, | ||
// no libraries or arguments | ||
// https://nodejs.dev/learn/accept-input-from-the-command-line-in-nodejs | ||
// ================= | ||
const readline = require('readline'); | ||
|
||
// 1. Make a command line interface. | ||
const rl = readline.createInterface({ | ||
input: process.stdin, | ||
output: process.stdout | ||
}); | ||
|
||
// 2. Make supplies for our speech bubble | ||
|
||
// 3. Make a cow that takes a string | ||
function makeBubble(text) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the implementation of the bubble is clever. |
||
if (!text) text = "Moo!"; | ||
const len = text.length; | ||
const top = ' ' + '_'.repeat(len + 2); | ||
const middle = `| ${text} |`; | ||
const bottom = ' ' + '-'.repeat(len + 2); | ||
return `${top}\n${middle}\n${bottom}`; | ||
} | ||
|
||
const cow = (saying) => { | ||
// how did you make the cow before? | ||
} | ||
const bubble = makeBubble(saying); | ||
const asciiCow = ` | ||
\\ ^__^ | ||
\\ (oo)\\_______ | ||
(__)\\ )\\/\\ | ||
||----w | | ||
|| || | ||
`; | ||
return bubble + asciiCow; | ||
}; | ||
|
||
// 4. Use readline to get a string from the terminal | ||
// (with a prompt so it's clearer what we want) | ||
rl.question("What should the cow say? ", (answer) => { | ||
console.log(cow(answer)); | ||
rl.close(); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
// ==================== | ||
// Mini-weather App JS | ||
// ==================== | ||
|
||
// Your working API keys | ||
const WEATHER_API_KEY = "13940029dbb0ac27fa8e76f770798f71"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it is generally not good practice to commit secrets/keys to source control. I'm not sure if this was the recommended approach but it is something to note moving forward. you should look up environment variables. In a situation where you have environment variables set, I would need to create my own keys to test your application, so leaving your keys here makes testing/iterating quicker. but this is something to always keep in mind as you go. |
||
const UNSPLASH_ACCESS_KEY = "1EkJLQ64exkT3BxWkvtunY6nGsyoUpmmVOIycUfobC4"; | ||
|
||
// DOM elements | ||
const photoEl = document.getElementById("photo"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. while I can tell what this variable name does by just looking at it, you should look to have more descriptive variable names. so, |
||
const thumbsEl = document.getElementById("thumbs"); | ||
const conditionsEl = document.getElementById("conditions"); | ||
const creditUserEl = document.getElementById("credit-user"); | ||
const searchForm = document.getElementById("search"); | ||
const searchInput = document.getElementById("search-tf"); | ||
|
||
// Fetch weather data from OpenWeather | ||
async function getWeather(city = "London") { | ||
const url = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${WEATHER_API_KEY}&units=metric`; | ||
const res = await fetch(url); | ||
if (!res.ok) throw new Error("Invalid API key or city"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good response check here. however, the error description may have nothing to do with why the fetch request has failed. it could be a server timeout, it could be a 404 not found, it could be anything. but your returned description assumes that it is either the api key or city. if you want to match the error with the description, you would need to handle different response status codes. therefore, if you want to use a generic response here, you would be doing something like:
|
||
const data = await res.json(); | ||
return data; | ||
} | ||
|
||
// Fetch images from Unsplash | ||
async function getImages(query) { | ||
const url = `https://api.unsplash.com/search/photos?query=${query}&client_id=${UNSPLASH_ACCESS_KEY}&per_page=5`; | ||
const res = await fetch(url); | ||
const data = await res.json(); | ||
return data.results; | ||
} | ||
|
||
// Display main photo and thumbnails | ||
function displayImages(images) { | ||
if (!images.length) return; | ||
|
||
const mainImage = images[0]; | ||
photoEl.style.backgroundImage = `url(${mainImage.urls.regular})`; | ||
creditUserEl.textContent = mainImage.user.name; | ||
creditUserEl.href = mainImage.user.links.html; | ||
|
||
thumbsEl.innerHTML = ""; | ||
|
||
images.forEach((img, index) => { | ||
const thumb = document.createElement("div"); | ||
thumb.classList.add("thumb"); | ||
if (index === 0) thumb.classList.add("active"); | ||
thumb.style.backgroundImage = `url(${img.urls.thumb})`; | ||
thumb.dataset.full = img.urls.regular; | ||
thumb.dataset.userName = img.user.name; | ||
thumb.dataset.userLink = img.user.links.html; | ||
|
||
thumb.addEventListener("click", () => { | ||
photoEl.style.backgroundImage = `url(${img.urls.regular})`; | ||
creditUserEl.textContent = img.user.name; | ||
creditUserEl.href = img.user.links.html; | ||
|
||
document.querySelectorAll(".thumb").forEach(t => t.classList.remove("active")); | ||
thumb.classList.add("active"); | ||
}); | ||
|
||
thumbsEl.appendChild(thumb); | ||
}); | ||
} | ||
|
||
// Display weather info | ||
function displayWeather(weatherData) { | ||
const description = weatherData.weather[0].description; | ||
const temp = Math.round(weatherData.main.temp); | ||
const cityName = weatherData.name; | ||
conditionsEl.textContent = `${description}, ${temp}°C in ${cityName}`; | ||
} | ||
|
||
// Update city | ||
async function updateCity(city) { | ||
try { | ||
const weather = await getWeather(city); | ||
displayWeather(weather); | ||
const images = await getImages(weather.weather[0].description); | ||
displayImages(images); | ||
} catch (err) { | ||
console.error(err); | ||
conditionsEl.textContent = "Error fetching weather or images!"; | ||
} | ||
} | ||
|
||
// Load default city | ||
updateCity("London"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. your code says |
||
|
||
// Handle search form | ||
searchForm.addEventListener("submit", (e) => { | ||
e.preventDefault(); | ||
const city = searchInput.value.trim(); | ||
if (city) updateCity(city); | ||
searchInput.value = ""; | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,6 +44,7 @@ <h1 class="title"> | |
</main> | ||
|
||
<!-- JS goes here --> | ||
<script src="app.js"></script> | ||
</body> | ||
|
||
</html> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<title>Dog Photo Gallery</title> | ||
<link rel="stylesheet" href="styles.css"> | ||
</head> | ||
<body> | ||
<h1>🐕 Random Dog Photo Gallery</h1> | ||
<button id="add-dog">Add Dog</button> | ||
<button id="clear-gallery">Clear Gallery</button> | ||
<ul id="dog-gallery"></ul> | ||
|
||
<script src="script.js"></script> | ||
</body> | ||
</html> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
const addDogBtn = document.getElementById("add-dog"); | ||
const clearGalleryBtn = document.getElementById("clear-gallery"); | ||
const gallery = document.getElementById("dog-gallery"); | ||
|
||
async function fetchDogImage() { | ||
try { | ||
const response = await fetch("https://dog.ceo/api/breeds/image/random"); | ||
if (!response.ok) { | ||
throw new Error(`HTTP error! Status: ${response.status}`); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. see (from my comments in the weather app), you have handled the |
||
} | ||
const data = await response.json(); | ||
|
||
// Create elements | ||
const li = document.createElement("li"); | ||
const img = document.createElement("img"); | ||
img.src = data.message; | ||
img.alt = "Cute Dog"; | ||
|
||
// Append to list | ||
li.appendChild(img); | ||
gallery.appendChild(li); | ||
|
||
} catch (error) { | ||
console.error("Error fetching dog image:", error); | ||
alert("🐶 Oops! Failed to fetch a dog image. Try again."); | ||
} | ||
} | ||
|
||
// Event listeners | ||
addDogBtn.addEventListener("click", fetchDogImage); | ||
clearGalleryBtn.addEventListener("click", () => { | ||
gallery.innerHTML = ""; | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
body { | ||
font-family: Arial, sans-serif; | ||
text-align: center; | ||
padding: 20px; | ||
} | ||
|
||
button { | ||
margin: 10px; | ||
padding: 10px 20px; | ||
cursor: pointer; | ||
border-radius: 8px; | ||
border: none; | ||
background: #4CAF50; | ||
color: white; | ||
font-size: 16px; | ||
} | ||
|
||
button:hover { | ||
background: #45a049; | ||
} | ||
|
||
ul { | ||
list-style: none; | ||
padding: 0; | ||
display: flex; | ||
flex-wrap: wrap; | ||
justify-content: center; | ||
} | ||
|
||
li { | ||
margin: 10px; | ||
} | ||
|
||
img { | ||
max-width: 200px; | ||
border-radius: 12px; | ||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
export function add(numbers) { | ||
if (numbers === "") return 0; | ||
|
||
const parts = numbers.split(",").map(Number); | ||
const negatives = parts.filter((num) => num < 0); | ||
|
||
if (negatives.length > 0) { | ||
throw new Error(`negatives not allowed: ${negatives.join(",")}`); | ||
} | ||
|
||
return parts | ||
.filter((num) => num <= 1000) // ignore > 1000 | ||
.reduce((sum, num) => sum + num, 0); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { add } from "./calculator"; | ||
|
||
describe("Calculator", () => { | ||
test("should return 0 for empty string", () => { | ||
expect(add("")).toBe(0); | ||
}); | ||
|
||
test("should return the number itself when only one number is given", () => { | ||
expect(add("5")).toBe(5); | ||
}); | ||
|
||
test("should return sum of two numbers", () => { | ||
expect(add("3,6")).toBe(9); | ||
}); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. very good |
||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
export function verify(password) { | ||
if (!password || password.length < 8) { | ||
return "Password rejected"; | ||
} | ||
|
||
if (!/[A-Z]/.test(password)) { | ||
return "Password rejected"; | ||
} | ||
|
||
if (!/[0-9]/.test(password)) { | ||
return "Password rejected"; | ||
} | ||
|
||
return "Password accepted"; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { verify } from "./verify.js"; | ||
|
||
describe("Password Verifier", () => { | ||
|
||
test("rejects password shorter than 8 characters", () => { | ||
expect(verify("Ab1")).toBe("Password rejected"); | ||
}); | ||
|
||
test("rejects null password", () => { | ||
expect(verify(null)).toBe("Password rejected"); | ||
}); | ||
|
||
test("rejects password without uppercase letter", () => { | ||
expect(verify("password1")).toBe("Password rejected"); | ||
}); | ||
|
||
test("rejects password without number", () => { | ||
expect(verify("Password")).toBe("Password rejected"); | ||
}); | ||
|
||
test("accepts valid password", () => { | ||
expect(verify("Password1")).toBe("Password accepted"); | ||
}); | ||
|
||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,20 @@ | ||
function convertToNewRoman(n) {} | ||
function convertToNewRoman(n) { | ||
// Step 1: simple mapping for 1–10 as example | ||
const romanNumerals = { | ||
1: "I", | ||
2: "II", | ||
3: "III", | ||
4: "IV", | ||
5: "V", | ||
6: "VI", | ||
7: "VII", | ||
8: "VIII", | ||
9: "IX", | ||
10: "X", | ||
}; | ||
|
||
return romanNumerals[n] || ""; | ||
} | ||
|
||
module.exports = convertToNewRoman; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
curious. how did you get the image of the cow?