From 236bef169ad8572f9b2b488bd9e48c878d6d4187 Mon Sep 17 00:00:00 2001 From: Dipa-Sarker Date: Sat, 1 Aug 2026 15:11:01 +0100 Subject: [PATCH 01/23] added my name & git username --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index 9a400d1..09d2943 100644 --- a/index.html +++ b/index.html @@ -3,7 +3,7 @@ - TV Show Project | My Name (My GitHub username) + TV Show Project | Dipa Sarker (Dipa-Sarker) From 1b4500601287193acd90193f4a393b1de51e64a6 Mon Sep 17 00:00:00 2001 From: Dipa-Sarker Date: Sun, 2 Aug 2026 14:53:27 +0100 Subject: [PATCH 02/23] added function for multiple card --- index.html | 7 +++++++ script.js | 17 ++++++++++++++++- style.css | 24 +++++++++++++++++++++++- 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/index.html b/index.html index 09d2943..f48040e 100644 --- a/index.html +++ b/index.html @@ -12,6 +12,13 @@
+

+ Data originally provided by + + TVMaze.com + +

+ diff --git a/script.js b/script.js index 87a7de8..e49af72 100644 --- a/script.js +++ b/script.js @@ -1,12 +1,27 @@ //You can edit ALL of the code here function setup() { const allEpisodes = getAllEpisodes(); + console.log(allEpisodes); makePageForEpisodes(allEpisodes); } function makePageForEpisodes(episodeList) { const rootElem = document.getElementById("root"); - rootElem.textContent = `Got ${episodeList.length} episode(s)`; + + for(const episode of episodeList){ + const card = document.createElement("section"); + card.classList.add("episode-card"); + const episodeName = document.createElement("h2"); +episodeName.textContent = episode.name + "-" + "S" + episode.season.toString().padStart(2, "0") + "E" + episode.number.toString().padStart(2, "0"); +card.append(episodeName); +const image = document.createElement("img"); +image.src = episode.image.medium; +card.append(image); +const summary = document.createElement("p"); +summary.innerHTML = episode.summary; +card.append(summary); + rootElem.append(card); + } } window.onload = setup; diff --git a/style.css b/style.css index 77cb8d4..52e65ce 100644 --- a/style.css +++ b/style.css @@ -1,3 +1,25 @@ #root { - color: red; + color: black; + display: flex; + flex-wrap: wrap; +} +.episode-card{ + + width: 30%; + border: 1px solid black; + margin: 10px; +} +.episode-card h2 { + border: 1px solid black; + text-align: center; + margin: 0; +} +.episode-card img { + padding: 10px; + display: block; + margin: 0 auto; +} +.episode-card p { + text-align: justify; + padding: 10px; } From a02bf75fc38645051472e95dee36cbf1e00e4d1b Mon Sep 17 00:00:00 2001 From: Dipa-Sarker Date: Sun, 2 Aug 2026 21:29:42 +0100 Subject: [PATCH 03/23] style with CSS --- script.js | 34 ++++++++++++++++++++-------------- style.css | 8 ++++++-- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/script.js b/script.js index e49af72..47be3cd 100644 --- a/script.js +++ b/script.js @@ -7,20 +7,26 @@ function setup() { function makePageForEpisodes(episodeList) { const rootElem = document.getElementById("root"); - - for(const episode of episodeList){ - const card = document.createElement("section"); - card.classList.add("episode-card"); - const episodeName = document.createElement("h2"); -episodeName.textContent = episode.name + "-" + "S" + episode.season.toString().padStart(2, "0") + "E" + episode.number.toString().padStart(2, "0"); -card.append(episodeName); -const image = document.createElement("img"); -image.src = episode.image.medium; -card.append(image); -const summary = document.createElement("p"); -summary.innerHTML = episode.summary; -card.append(summary); - rootElem.append(card); + + for (const episode of episodeList) { + const card = document.createElement("section"); + card.classList.add("episode-card"); + const episodeName = document.createElement("h2"); + episodeName.textContent = + episode.name + + "-" + + "S" + + episode.season.toString().padStart(2, "0") + + "E" + + episode.number.toString().padStart(2, "0"); + card.append(episodeName); + const image = document.createElement("img"); + image.src = episode.image.medium; + card.append(image); + const summary = document.createElement("p"); + summary.innerHTML = episode.summary; + card.append(summary); + rootElem.append(card); } } diff --git a/style.css b/style.css index 52e65ce..051419e 100644 --- a/style.css +++ b/style.css @@ -2,15 +2,19 @@ color: black; display: flex; flex-wrap: wrap; + background-color: grey; } -.episode-card{ - +.episode-card { width: 30%; border: 1px solid black; + border-radius: 10px; margin: 10px; + background-color: white; + padding: 10px; } .episode-card h2 { border: 1px solid black; + border-radius: 10px; text-align: center; margin: 0; } From d414c4c4a9e4ddf17128af8890d3b9188a199c85 Mon Sep 17 00:00:00 2001 From: Dipa-Sarker Date: Sun, 2 Aug 2026 21:49:36 +0100 Subject: [PATCH 04/23] check and adjust accessibility --- index.html | 4 ++-- script.js | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/index.html b/index.html index f48040e..d24e77b 100644 --- a/index.html +++ b/index.html @@ -9,8 +9,8 @@ -
-
+
+

Data originally provided by diff --git a/script.js b/script.js index 47be3cd..b7a6da4 100644 --- a/script.js +++ b/script.js @@ -22,6 +22,7 @@ function makePageForEpisodes(episodeList) { card.append(episodeName); const image = document.createElement("img"); image.src = episode.image.medium; + image.alt = `Episode image for ${episode.name}`; card.append(image); const summary = document.createElement("p"); summary.innerHTML = episode.summary; From eafbd2f7cb7020755da80e5f75b3fd1519121a9a Mon Sep 17 00:00:00 2001 From: Dipa-Sarker Date: Wed, 5 Aug 2026 11:05:30 +0100 Subject: [PATCH 05/23] add console.log --- script.js | 1 + 1 file changed, 1 insertion(+) diff --git a/script.js b/script.js index b7a6da4..3864bca 100644 --- a/script.js +++ b/script.js @@ -30,5 +30,6 @@ function makePageForEpisodes(episodeList) { rootElem.append(card); } } +console.log("hello"); window.onload = setup; From 37f8aa48990d201a8a690d126891a5d12ae049b8 Mon Sep 17 00:00:00 2001 From: Rizqah Date: Sun, 9 Aug 2026 21:49:54 +0100 Subject: [PATCH 06/23] template literals used for episodename textcontent --- script.js | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/script.js b/script.js index 3864bca..290a031 100644 --- a/script.js +++ b/script.js @@ -12,17 +12,11 @@ function makePageForEpisodes(episodeList) { const card = document.createElement("section"); card.classList.add("episode-card"); const episodeName = document.createElement("h2"); - episodeName.textContent = - episode.name + - "-" + - "S" + - episode.season.toString().padStart(2, "0") + - "E" + - episode.number.toString().padStart(2, "0"); + episodeName.textContent = `${episode.name} - S${String(episode.season).padStart(2, "0")}E${String(episode.season).padStart(2, "0")}`; card.append(episodeName); const image = document.createElement("img"); image.src = episode.image.medium; - image.alt = `Episode image for ${episode.name}`; + image.alt = `Episode image for ${episode.name}`; card.append(image); const summary = document.createElement("p"); summary.innerHTML = episode.summary; From 3760d38e609d1bab17be75c38686a39aa333e151 Mon Sep 17 00:00:00 2001 From: Risikat Popoola <167776872+risikatpopoola@users.noreply.github.com> Date: Sun, 9 Aug 2026 21:55:22 +0100 Subject: [PATCH 07/23] Revert "template literals used for episodename textcontent" --- script.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/script.js b/script.js index 290a031..3864bca 100644 --- a/script.js +++ b/script.js @@ -12,11 +12,17 @@ function makePageForEpisodes(episodeList) { const card = document.createElement("section"); card.classList.add("episode-card"); const episodeName = document.createElement("h2"); - episodeName.textContent = `${episode.name} - S${String(episode.season).padStart(2, "0")}E${String(episode.season).padStart(2, "0")}`; + episodeName.textContent = + episode.name + + "-" + + "S" + + episode.season.toString().padStart(2, "0") + + "E" + + episode.number.toString().padStart(2, "0"); card.append(episodeName); const image = document.createElement("img"); image.src = episode.image.medium; - image.alt = `Episode image for ${episode.name}`; + image.alt = `Episode image for ${episode.name}`; card.append(image); const summary = document.createElement("p"); summary.innerHTML = episode.summary; From 20b569d716a42759abefe8f45e0a9c6525cd8bf2 Mon Sep 17 00:00:00 2001 From: Rizqah Date: Sun, 9 Aug 2026 21:58:23 +0100 Subject: [PATCH 08/23] template literals used and console log hello removed --- script.js | 1 - 1 file changed, 1 deletion(-) diff --git a/script.js b/script.js index 290a031..026b895 100644 --- a/script.js +++ b/script.js @@ -24,6 +24,5 @@ function makePageForEpisodes(episodeList) { rootElem.append(card); } } -console.log("hello"); window.onload = setup; From 0a72146294d7a500683ce64371b6dc2841075ddb Mon Sep 17 00:00:00 2001 From: Rizqah Date: Mon, 10 Aug 2026 15:00:54 +0100 Subject: [PATCH 09/23] level 200 search implemented --- index.html | 23 +++++++++++++---------- script.js | 23 +++++++++++++++++++++++ style.css | 14 ++++++++++++++ 3 files changed, 50 insertions(+), 10 deletions(-) diff --git a/index.html b/index.html index d24e77b..2e67577 100644 --- a/index.html +++ b/index.html @@ -1,4 +1,4 @@ - + @@ -6,19 +6,22 @@ TV Show Project | Dipa Sarker (Dipa-Sarker) + -

-
+
+ + +
+

+
+ Data originally provided by + TVMaze.com -

- Data originally provided by - - TVMaze.com - -

- diff --git a/script.js b/script.js index 026b895..ee2619b 100644 --- a/script.js +++ b/script.js @@ -23,6 +23,29 @@ function makePageForEpisodes(episodeList) { card.append(summary); rootElem.append(card); } + const searchInput = document.getElementById("search-input"); + const episodeCount = document.getElementById("episode-count"); + const episodeCards = document.querySelectorAll(".episode-card"); + + episodeCount.textContent = `${episodeList.length} episodes`; + + searchInput.addEventListener("input", function () { + const searchTerm = searchInput.value.toLowerCase(); + let matchingEpisodes = 0; + + episodeList.forEach(function (episode, index) { + const card = episodeCards[index]; + + if (episode.name.toLowerCase().includes(searchTerm)) { + card.style.display = ""; + matchingEpisodes++; + } else { + card.style.display = "none"; + } + }); + + episodeCount.textContent = `${matchingEpisodes} episodes`; + }); } window.onload = setup; diff --git a/style.css b/style.css index 051419e..0e760c0 100644 --- a/style.css +++ b/style.css @@ -27,3 +27,17 @@ text-align: justify; padding: 10px; } +.search-container { + position: relative; +} + +.search-container i { + position: absolute; + left: 12px; + top: 50%; + transform: translateY(-50%); +} + +#search-input { + padding-left: 40px; +} From a8b2dbd70eb72a8a5c8e9c3cd76112219e226a3f Mon Sep 17 00:00:00 2001 From: Rizqah Date: Mon, 10 Aug 2026 21:41:25 +0100 Subject: [PATCH 10/23] Episode number fixed --- script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script.js b/script.js index ee2619b..b784eb5 100644 --- a/script.js +++ b/script.js @@ -12,7 +12,7 @@ function makePageForEpisodes(episodeList) { const card = document.createElement("section"); card.classList.add("episode-card"); const episodeName = document.createElement("h2"); - episodeName.textContent = `${episode.name} - S${String(episode.season).padStart(2, "0")}E${String(episode.season).padStart(2, "0")}`; + episodeName.textContent = `${episode.name} - S${String(episode.season).padStart(2, "0")}E${String(episode.number).padStart(2, "0")}`; card.append(episodeName); const image = document.createElement("img"); image.src = episode.image.medium; From 7d422e2e81fe377f426e452956efe8937fcdf700 Mon Sep 17 00:00:00 2001 From: Rizqah Date: Mon, 10 Aug 2026 22:28:04 +0100 Subject: [PATCH 11/23] episodes found corrected --- script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script.js b/script.js index b784eb5..47d7670 100644 --- a/script.js +++ b/script.js @@ -44,7 +44,7 @@ function makePageForEpisodes(episodeList) { } }); - episodeCount.textContent = `${matchingEpisodes} episodes`; + episodeCount.textContent = `${matchingEpisodes} episodes found`; }); } From 085017774b00250824e9ecbc355ab3c85ef6d4fb Mon Sep 17 00:00:00 2001 From: Rizqah Date: Tue, 11 Aug 2026 00:42:10 +0100 Subject: [PATCH 12/23] level 200 search selector and back button added --- script.js | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++----- style.css | 40 +++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 5 deletions(-) diff --git a/script.js b/script.js index 47d7670..6b97d7d 100644 --- a/script.js +++ b/script.js @@ -1,4 +1,4 @@ -//You can edit ALL of the code here +// You can edit ALL of the code here function setup() { const allEpisodes = getAllEpisodes(); console.log(allEpisodes); @@ -7,27 +7,55 @@ function setup() { function makePageForEpisodes(episodeList) { const rootElem = document.getElementById("root"); + const episodeCards = []; + // Selector implemented + const selectEpisodeList = document.createElement("select"); + //back button added + const backButton = document.createElement("button"); + backButton.textContent = "Back to all episodes"; + backButton.id = "back-button"; + backButton.style.display = "none"; + for (const episode of episodeList) { + const option = document.createElement("option"); + + option.textContent = `S${String(episode.season).padStart(2, "0")}E${String(episode.number).padStart(2, "0")} - ${episode.name}`; + + option.value = episode.id; + + selectEpisodeList.appendChild(option); + } + + // Adding dropdown menu beside the search box + const searchContainer = document.querySelector(".search-container"); + searchContainer.appendChild(selectEpisodeList); + searchContainer.appendChild(backButton); + + // Creating episode cards for (const episode of episodeList) { const card = document.createElement("section"); card.classList.add("episode-card"); + const episodeName = document.createElement("h2"); episodeName.textContent = `${episode.name} - S${String(episode.season).padStart(2, "0")}E${String(episode.number).padStart(2, "0")}`; card.append(episodeName); + const image = document.createElement("img"); image.src = episode.image.medium; image.alt = `Episode image for ${episode.name}`; card.append(image); + const summary = document.createElement("p"); summary.innerHTML = episode.summary; card.append(summary); + + episodeCards.push(card); rootElem.append(card); } + + // Search implemented const searchInput = document.getElementById("search-input"); const episodeCount = document.getElementById("episode-count"); - const episodeCards = document.querySelectorAll(".episode-card"); - - episodeCount.textContent = `${episodeList.length} episodes`; searchInput.addEventListener("input", function () { const searchTerm = searchInput.value.toLowerCase(); @@ -44,7 +72,29 @@ function makePageForEpisodes(episodeList) { } }); - episodeCount.textContent = `${matchingEpisodes} episodes found`; + episodeCount.textContent = `Displaying ${matchingEpisodes}/${episodeList.length} episodes`; + }); + + // Selector change event + selectEpisodeList.addEventListener("change", function () { + const selectedId = selectEpisodeList.value; + + const selectedEpisode = episodeList.find(function (episode) { + return episode.id === Number(selectedId); + }); + + for (const card of episodeCards) { + card.style.display = "none"; + } + + const index = episodeList.indexOf(selectedEpisode); + const selectedCard = episodeCards[index]; + + selectedCard.style.display = ""; + backButton.style.display = ""; + }); // Back to all episodes + backButton.addEventListener("click", function () { + location.reload(); }); } diff --git a/style.css b/style.css index 0e760c0..aab9167 100644 --- a/style.css +++ b/style.css @@ -29,6 +29,9 @@ } .search-container { position: relative; + display: flex; + align-items: center; + gap: 10px; } .search-container i { @@ -36,8 +39,45 @@ left: 12px; top: 50%; transform: translateY(-50%); + color: #666666; +} + +#search-input, +.search-container select { + background-color: #ffffff; + color: #111111; + border: 1px solid #d9d9d9; + border-radius: 6px; + padding: 10px; } #search-input { padding-left: 40px; } + +.search-container select { + padding-left: 10px; +} + +#search-input::placeholder { + color: #666666; +} + +#search-input:focus, +.search-container select:focus { + outline: none; + border-color: #333333; + box-shadow: 0 0 0 2px #d9d9d9; +} +#back-button { + background-color: #333333; + color: #ffffff; + border: none; + border-radius: 6px; + padding: 10px 14px; + cursor: pointer; +} + +#back-button:hover { + background-color: #111111; +} From 9123bcf4f39fd691d1bd48d529f5289d740712aa Mon Sep 17 00:00:00 2001 From: Dipa-Sarker Date: Tue, 11 Aug 2026 23:34:40 +0100 Subject: [PATCH 13/23] added functionality for API --- .vscode/settings.json | 3 + episodes.js | 1855 ----------------------------------------- index.html | 4 +- script.js | 42 +- 4 files changed, 39 insertions(+), 1865 deletions(-) create mode 100644 .vscode/settings.json delete mode 100644 episodes.js diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..6f3a291 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "liveServer.settings.port": 5501 +} \ No newline at end of file diff --git a/episodes.js b/episodes.js deleted file mode 100644 index 5ef6e9e..0000000 --- a/episodes.js +++ /dev/null @@ -1,1855 +0,0 @@ -//DO NOT EDIT THIS FILE - -//This content is from https://www.tvmaze.com/ -//specifically: https://api.tvmaze.com/shows/82/episodes - -function getOneEpisode() { - return { - id: 4952, - url: - "http://www.tvmaze.com/episodes/4952/game-of-thrones-1x01-winter-is-coming", - name: "Winter is Coming", - season: 1, - number: 1, - airdate: "2011-04-17", - airtime: "21:00", - airstamp: "2011-04-18T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2668.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2668.jpg", - }, - summary: - "

Lord Eddard Stark, ruler of the North, is summoned to court by his old friend, King Robert Baratheon, to serve as the King's Hand. Eddard reluctantly agrees after learning of a possible threat to the King's life. Eddard's bastard son Jon Snow must make a painful decision about his own future, while in the distant east Viserys Targaryen plots to reclaim his father's throne, usurped by Robert, by selling his sister in marriage.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4952", - }, - }, - }; -} - -function getAllEpisodes() { - return [ - { - id: 4952, - url: - "http://www.tvmaze.com/episodes/4952/game-of-thrones-1x01-winter-is-coming", - name: "Winter is Coming", - season: 1, - number: 1, - airdate: "2011-04-17", - airtime: "21:00", - airstamp: "2011-04-18T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2668.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2668.jpg", - }, - summary: - "

Lord Eddard Stark, ruler of the North, is summoned to court by his old friend, King Robert Baratheon, to serve as the King's Hand. Eddard reluctantly agrees after learning of a possible threat to the King's life. Eddard's bastard son Jon Snow must make a painful decision about his own future, while in the distant east Viserys Targaryen plots to reclaim his father's throne, usurped by Robert, by selling his sister in marriage.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4952", - }, - }, - }, - { - id: 4953, - url: - "http://www.tvmaze.com/episodes/4953/game-of-thrones-1x02-the-kingsroad", - name: "The Kingsroad", - season: 1, - number: 2, - airdate: "2011-04-24", - airtime: "21:00", - airstamp: "2011-04-25T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2669.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2669.jpg", - }, - summary: - "

An incident on the Kingsroad threatens Eddard and Robert's friendship. Jon and Tyrion travel to the Wall, where they discover that the reality of the Night's Watch may not match the heroic image of it.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4953", - }, - }, - }, - { - id: 4954, - url: "http://www.tvmaze.com/episodes/4954/game-of-thrones-1x03-lord-snow", - name: "Lord Snow", - season: 1, - number: 3, - airdate: "2011-05-01", - airtime: "21:00", - airstamp: "2011-05-02T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2671.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2671.jpg", - }, - summary: - "

Jon Snow attempts to find his place amongst the Night's Watch. Eddard and his daughters arrive at King's Landing.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4954", - }, - }, - }, - { - id: 4955, - url: - "http://www.tvmaze.com/episodes/4955/game-of-thrones-1x04-cripples-bastards-and-broken-things", - name: "Cripples, Bastards, and Broken Things", - season: 1, - number: 4, - airdate: "2011-05-08", - airtime: "21:00", - airstamp: "2011-05-09T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2673.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2673.jpg", - }, - summary: - "

Tyrion stops at Winterfell on his way home and gets a frosty reception from Robb Stark. Eddard's investigation into the death of his predecessor gets underway.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4955", - }, - }, - }, - { - id: 4956, - url: - "http://www.tvmaze.com/episodes/4956/game-of-thrones-1x05-the-wolf-and-the-lion", - name: "The Wolf and the Lion", - season: 1, - number: 5, - airdate: "2011-05-15", - airtime: "21:00", - airstamp: "2011-05-16T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2674.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2674.jpg", - }, - summary: - "

Catelyn's actions on the road have repercussions for Eddard. Tyrion enjoys the dubious hospitality of the Eyrie.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4956", - }, - }, - }, - { - id: 4957, - url: - "http://www.tvmaze.com/episodes/4957/game-of-thrones-1x06-a-golden-crown", - name: "A Golden Crown", - season: 1, - number: 6, - airdate: "2011-05-22", - airtime: "21:00", - airstamp: "2011-05-23T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2676.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2676.jpg", - }, - summary: - "

Viserys is increasingly frustrated by the lack of progress towards gaining his crown.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4957", - }, - }, - }, - { - id: 4958, - url: - "http://www.tvmaze.com/episodes/4958/game-of-thrones-1x07-you-win-or-you-die", - name: "You Win or You Die", - season: 1, - number: 7, - airdate: "2011-05-29", - airtime: "21:00", - airstamp: "2011-05-30T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2677.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2677.jpg", - }, - summary: - "

Eddard's investigations in King's Landing reach a climax and a dark secret is revealed.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4958", - }, - }, - }, - { - id: 4959, - url: - "http://www.tvmaze.com/episodes/4959/game-of-thrones-1x08-the-pointy-end", - name: "The Pointy End", - season: 1, - number: 8, - airdate: "2011-06-05", - airtime: "21:00", - airstamp: "2011-06-06T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2678.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2678.jpg", - }, - summary: - "

Tyrion joins his father's army with unexpected allies. Events in King's Landing take a turn for the worse as Arya's lessons are put to the test.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4959", - }, - }, - }, - { - id: 4960, - url: "http://www.tvmaze.com/episodes/4960/game-of-thrones-1x09-baelor", - name: "Baelor", - season: 1, - number: 9, - airdate: "2011-06-12", - airtime: "21:00", - airstamp: "2011-06-13T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2679.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2679.jpg", - }, - summary: - "

Catelyn must negotiate with the irascible Lord Walder Frey.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4960", - }, - }, - }, - { - id: 4961, - url: - "http://www.tvmaze.com/episodes/4961/game-of-thrones-1x10-fire-and-blood", - name: "Fire and Blood", - season: 1, - number: 10, - airdate: "2011-06-19", - airtime: "21:00", - airstamp: "2011-06-20T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2681.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2681.jpg", - }, - summary: - "

Daenerys must realize her destiny. Jaime finds himself in an unfamiliar predicament.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4961", - }, - }, - }, - { - id: 4962, - url: - "http://www.tvmaze.com/episodes/4962/game-of-thrones-2x01-the-north-remembers", - name: "The North Remembers", - season: 2, - number: 1, - airdate: "2012-04-01", - airtime: "21:00", - airstamp: "2012-04-02T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/3174.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/3174.jpg", - }, - summary: - "

War grips the continent of Westeros. As Tyrion Lannister tries to take his strong-willed nephew in hand in King's Landing, Stannis Baratheon launches his own campaign to take the Iron Throne with the help of a mysterious priestess. In the east, Daenerys must lead her retinue through a desolate wasteland whilst beyond the Wall the Night's Watch seeks the aid of a wildling.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4962", - }, - }, - }, - { - id: 4963, - url: - "http://www.tvmaze.com/episodes/4963/game-of-thrones-2x02-the-night-lands", - name: "The Night Lands", - season: 2, - number: 2, - airdate: "2012-04-08", - airtime: "21:00", - airstamp: "2012-04-09T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/3175.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/3175.jpg", - }, - summary: - "

Stannis uses Ser Davos to seek out new allies for his war with the Lannisters. On the road north, Arya confides in Gendry. Robb Stark sends Theon Greyjoy to win an alliance with his father and the fierce warriors of the Iron Islands. Cersei and Tyrion clash on how to rule in King's Landing.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4963", - }, - }, - }, - { - id: 4964, - url: - "http://www.tvmaze.com/episodes/4964/game-of-thrones-2x03-what-is-dead-may-never-die", - name: "What is Dead May Never Die", - season: 2, - number: 3, - airdate: "2012-04-15", - airtime: "21:00", - airstamp: "2012-04-16T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/3176.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/3176.jpg", - }, - summary: - "

Catelyn Stark treats with King Renly in the hope of winning an alliance. Tyrion undertakes a complex plan in King's Landing to expose an enemy. At Winterfell, Bran's dreams continue to trouble him.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4964", - }, - }, - }, - { - id: 4965, - url: - "http://www.tvmaze.com/episodes/4965/game-of-thrones-2x04-garden-of-bones", - name: "Garden of Bones", - season: 2, - number: 4, - airdate: "2012-04-22", - airtime: "21:00", - airstamp: "2012-04-23T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/3177.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/3177.jpg", - }, - summary: - "

Tyrion attempts to restrain Joffrey's cruelty. Catelyn attempts to broker a peace between Stannis and Renly. Daenerys and her followers arrive at the great city of Qarth and hope to find refuge there. Arya and Gendry arrive at Harrenhal, a great castle now under Lannister occupation.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4965", - }, - }, - }, - { - id: 4966, - url: - "http://www.tvmaze.com/episodes/4966/game-of-thrones-2x05-the-ghost-of-harrenhal", - name: "The Ghost of Harrenhal", - season: 2, - number: 5, - airdate: "2012-04-29", - airtime: "21:00", - airstamp: "2012-04-30T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/3178.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/3178.jpg", - }, - summary: - "

Confusion rages in the Stormlands in the wake of a devastating reversal. Catelyn must flee with a new ally, whilst Littlefinger sees an opportunity in the chaos. Theon seeks to prove himself to his father in battle. Arya receives a promise from the enigmatic Jaqen H'ghar. The Night's Watch arrives at the Fist of the First Men. Daenerys Targaryen receives a marriage proposal.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4966", - }, - }, - }, - { - id: 4967, - url: - "http://www.tvmaze.com/episodes/4967/game-of-thrones-2x06-the-old-gods-and-the-new", - name: "The Old Gods and the New", - season: 2, - number: 6, - airdate: "2012-05-06", - airtime: "21:00", - airstamp: "2012-05-07T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/3180.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/3180.jpg", - }, - summary: - "

Arya has a surprise visitor; Dany vows to take what is hers; Joffrey meets his subjects; Qhorin gives Jon a chance to prove himself.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4967", - }, - }, - }, - { - id: 4968, - url: - "http://www.tvmaze.com/episodes/4968/game-of-thrones-2x07-a-man-without-honor", - name: "A Man Without Honor", - season: 2, - number: 7, - airdate: "2012-05-13", - airtime: "21:00", - airstamp: "2012-05-14T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/3192.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/3192.jpg", - }, - summary: - "

Jaime meets a relative; Theon hunts; Dany receives an invitation.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4968", - }, - }, - }, - { - id: 4969, - url: - "http://www.tvmaze.com/episodes/4969/game-of-thrones-2x08-the-prince-of-winterfell", - name: "The Prince of Winterfell", - season: 2, - number: 8, - airdate: "2012-05-20", - airtime: "21:00", - airstamp: "2012-05-21T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/3194.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/3194.jpg", - }, - summary: - "

Theon holds the fort; Arya calls in her debt with Jaqen; Robb is betrayed; Stannis and Davos approach their destination.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4969", - }, - }, - }, - { - id: 4970, - url: - "http://www.tvmaze.com/episodes/4970/game-of-thrones-2x09-blackwater", - name: "Blackwater", - season: 2, - number: 9, - airdate: "2012-05-27", - airtime: "21:00", - airstamp: "2012-05-28T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/3196.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/3196.jpg", - }, - summary: - "

A massive battle rages for control of King's Landing and the Iron Throne.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4970", - }, - }, - }, - { - id: 4971, - url: - "http://www.tvmaze.com/episodes/4971/game-of-thrones-2x10-valar-morghulis", - name: "Valar Morghulis", - season: 2, - number: 10, - airdate: "2012-06-03", - airtime: "21:00", - airstamp: "2012-06-04T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/3197.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/3197.jpg", - }, - summary: - "

Tyrion awakens to a changed situation. King Joffrey doles out rewards to his subjects. As Theon stirs his men to action, Luwin offers some final advice. Brienne silences Jaime; Arya receives a gift from Jaqen; Dany goes to a strange place; Jon proves himself to Qhorin.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4971", - }, - }, - }, - { - id: 4972, - url: - "http://www.tvmaze.com/episodes/4972/game-of-thrones-3x01-valar-dohaeris", - name: "Valar Dohaeris", - season: 3, - number: 1, - airdate: "2013-03-31", - airtime: "21:00", - airstamp: "2013-04-01T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2628.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2628.jpg", - }, - summary: - "

Jon is brought before Mance Rayder, the King Beyond the Wall, while the Night's Watch survivors retreat south. In King's Landing, Tyrion asks for his reward. Littlefinger offers Sansa a way out. Cersei hosts a dinner for the royal family. Daenerys sails into Slaver's Bay.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4972", - }, - }, - }, - { - id: 4973, - url: - "http://www.tvmaze.com/episodes/4973/game-of-thrones-3x02-dark-wings-dark-words", - name: "Dark Wings, Dark Words", - season: 3, - number: 2, - airdate: "2013-04-07", - airtime: "21:00", - airstamp: "2013-04-08T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2618.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2618.jpg", - }, - summary: - "

Sansa says too much. Shae asks Tyrion for a favor. Jaime finds a way to pass the time. Arya runs into the Brotherhood Without Banners.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4973", - }, - }, - }, - { - id: 4974, - url: - "http://www.tvmaze.com/episodes/4974/game-of-thrones-3x03-walk-of-punishment", - name: "Walk of Punishment", - season: 3, - number: 3, - airdate: "2013-04-14", - airtime: "21:00", - airstamp: "2013-04-15T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2616.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2616.jpg", - }, - summary: - "

Tyrion shoulders new responsibilities. Jon is taken to the Fist of the First Men. Daenerys meets with the slavers. Jaime strikes a deal with his captors.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4974", - }, - }, - }, - { - id: 4975, - url: - "http://www.tvmaze.com/episodes/4975/game-of-thrones-3x04-and-now-his-watch-is-ended", - name: "And Now His Watch is Ended", - season: 3, - number: 4, - airdate: "2013-04-21", - airtime: "21:00", - airstamp: "2013-04-22T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2615.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2615.jpg", - }, - summary: - "

The Night's Watch takes stock. Varys meets his better. Arya is taken to the commander of the Brotherhood. Daenerys exchanges a chain for a Whip.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4975", - }, - }, - }, - { - id: 4976, - url: - "http://www.tvmaze.com/episodes/4976/game-of-thrones-3x05-kissed-by-fire", - name: "Kissed by Fire", - season: 3, - number: 5, - airdate: "2013-04-28", - airtime: "21:00", - airstamp: "2013-04-29T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2614.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2614.jpg", - }, - summary: - "

The Hound is judged by the gods; Jaime is judged by men. Jon proves himself; Robb is betrayed. Tyrion learns the cost of weddings.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4976", - }, - }, - }, - { - id: 4977, - url: "http://www.tvmaze.com/episodes/4977/game-of-thrones-3x06-the-climb", - name: "The Climb", - season: 3, - number: 6, - airdate: "2013-05-05", - airtime: "21:00", - airstamp: "2013-05-06T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2612.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2612.jpg", - }, - summary: - "

Tywin plans strategic unions for the Lannisters. Melisandre visits the Riverlands. Robb weighs a compromise to repair his alliance with House Frey. Roose Bolton decides what to do with Jaime Lannister. Jon, Ygritte and the Wildlings face a daunting climb.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4977", - }, - }, - }, - { - id: 4978, - url: - "http://www.tvmaze.com/episodes/4978/game-of-thrones-3x07-the-bear-and-the-maiden-fair", - name: "The Bear and the Maiden Fair", - season: 3, - number: 7, - airdate: "2013-05-12", - airtime: "21:00", - airstamp: "2013-05-13T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2611.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2611.jpg", - }, - summary: - "

Daenerys exchanges gifts with a slave lord outside Yunkai. As Sansa frets about her prospects, Shae chafes at Tyrion's new situation. Tywin counsels the king, and Melisandre reveals a secret to Gendry. Brienne faces a formidable foe in Harrenhal.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4978", - }, - }, - }, - { - id: 4979, - url: - "http://www.tvmaze.com/episodes/4979/game-of-thrones-3x08-second-sons", - name: "Second Sons", - season: 3, - number: 8, - airdate: "2013-05-19", - airtime: "21:00", - airstamp: "2013-05-20T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2599.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2599.jpg", - }, - summary: - "

King's Landing hosts a wedding, and Tyrion and Sansa spend the night together. Daenerys meets the Titan's Bastard. Davos demands proof from Melisandre. Sam and Gilly meet an older Gentleman.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4979", - }, - }, - }, - { - id: 4980, - url: - "http://www.tvmaze.com/episodes/4980/game-of-thrones-3x09-the-rains-of-castamere", - name: "The Rains of Castamere", - season: 3, - number: 9, - airdate: "2013-06-02", - airtime: "21:00", - airstamp: "2013-06-03T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2598.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2598.jpg", - }, - summary: - "

Robb presents himself to Walder Frey, and Edmure meets his bride. Jon faces his harshest test yet. Bran discovers a new gift. Daario and Jorah debate how to take Yunkai. House Frey joins with House Tully.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4980", - }, - }, - }, - { - id: 4981, - url: "http://www.tvmaze.com/episodes/4981/game-of-thrones-3x10-mhysa", - name: "Mhysa", - season: 3, - number: 10, - airdate: "2013-06-09", - airtime: "21:00", - airstamp: "2013-06-10T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2597.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2597.jpg", - }, - summary: - "

Joffrey challenges Tywin. Bran tells a ghost story. In Dragonstone, mercy comes from strange quarters. Daenerys waits to see if she is a conqueror or a liberator.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4981", - }, - }, - }, - { - id: 4982, - url: - "http://www.tvmaze.com/episodes/4982/game-of-thrones-4x01-two-swords", - name: "Two Swords", - season: 4, - number: 1, - airdate: "2014-04-06", - airtime: "21:00", - airstamp: "2014-04-07T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2583.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2583.jpg", - }, - summary: - "

Tyrion welcomes a guest to King's Landing. At Castle Black, Jon Snow finds himself unwelcome. Dany is pointed to Meereen, the mother of all slave cities. Arya runs into an old friend.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4982", - }, - }, - }, - { - id: 4983, - url: - "http://www.tvmaze.com/episodes/4983/game-of-thrones-4x02-the-lion-and-the-rose", - name: "The Lion and the Rose", - season: 4, - number: 2, - airdate: "2014-04-13", - airtime: "21:00", - airstamp: "2014-04-14T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2584.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2584.jpg", - }, - summary: - "

Tyrion lends Jaime a hand. Joffrey and Margaery host a breakfast. At Dragonstone, Stannis loses patience with Davos. Ramsay finds a purpose for his pet. North of the Wall, Bran sees where they must go.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4983", - }, - }, - }, - { - id: 4984, - url: - "http://www.tvmaze.com/episodes/4984/game-of-thrones-4x03-breaker-of-chains", - name: "Breaker of Chains", - season: 4, - number: 3, - airdate: "2014-04-20", - airtime: "21:00", - airstamp: "2014-04-21T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2585.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2585.jpg", - }, - summary: - "

Tyrion ponders his options. Tywin extends an olive branch. Sam realizes Castle Black isn't safe, and Jon proposes a bold plan. The Hound teaches Arya the way things are. Dany chooses her Champion.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4984", - }, - }, - }, - { - id: 4985, - url: - "http://www.tvmaze.com/episodes/4985/game-of-thrones-4x04-oathkeeper", - name: "Oathkeeper", - season: 4, - number: 4, - airdate: "2014-04-27", - airtime: "21:00", - airstamp: "2014-04-28T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2586.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2586.jpg", - }, - summary: - "

Dany balances justice and mercy. Jaime tasks Brienne with his honor. Jon secures volunteers while Bran, Jojen, Meera and Hodor stumble on shelter.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4985", - }, - }, - }, - { - id: 4986, - url: - "http://www.tvmaze.com/episodes/4986/game-of-thrones-4x05-first-of-his-name", - name: "First of His Name", - season: 4, - number: 5, - airdate: "2014-05-04", - airtime: "21:00", - airstamp: "2014-05-05T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2587.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2587.jpg", - }, - summary: - "

Cersei and Tywin plot the Crown's next move. Dany discusses future plans. Jon embarks on a new mission.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4986", - }, - }, - }, - { - id: 4987, - url: - "http://www.tvmaze.com/episodes/4987/game-of-thrones-4x06-the-laws-of-gods-and-men", - name: "The Laws of Gods and Men", - season: 4, - number: 6, - airdate: "2014-05-11", - airtime: "21:00", - airstamp: "2014-05-12T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2588.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2588.jpg", - }, - summary: - "

Stannis and Davos set sail with a new strategy. Dany meets with supplicants. Tyrion faces down his father in the throne room.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4987", - }, - }, - }, - { - id: 4988, - url: - "http://www.tvmaze.com/episodes/4988/game-of-thrones-4x07-mockingbird", - name: "Mockingbird", - season: 4, - number: 7, - airdate: "2014-05-18", - airtime: "21:00", - airstamp: "2014-05-19T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2589.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2589.jpg", - }, - summary: - "

Tyrion enlists an unlikely ally. Daario entreats Dany to allow him to do what he does best. Jon's warnings about the Wall's vulnerability fall on deaf ears. Brienne follows a new lead on the road with Pod.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4988", - }, - }, - }, - { - id: 4989, - url: - "http://www.tvmaze.com/episodes/4989/game-of-thrones-4x08-the-mountain-and-the-viper", - name: "The Mountain and the Viper", - season: 4, - number: 8, - airdate: "2014-06-01", - airtime: "21:00", - airstamp: "2014-06-02T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2591.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2591.jpg", - }, - summary: - "

Mole's Town receives unexpected visitors. Littlefinger's motives are questioned. Ramsay attempts to prove himself to his father. Tyrion's fate is decided.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4989", - }, - }, - }, - { - id: 4990, - url: - "http://www.tvmaze.com/episodes/4990/game-of-thrones-4x09-the-watchers-on-the-wall", - name: "The Watchers on the Wall", - season: 4, - number: 9, - airdate: "2014-06-08", - airtime: "21:00", - airstamp: "2014-06-09T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2593.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2593.jpg", - }, - summary: - "

Jon Snow and the rest of the Night's Watch face the biggest challenge to the Wall yet.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4990", - }, - }, - }, - { - id: 4991, - url: - "http://www.tvmaze.com/episodes/4991/game-of-thrones-4x10-the-children", - name: "The Children", - season: 4, - number: 10, - airdate: "2014-06-15", - airtime: "21:00", - airstamp: "2014-06-16T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2594.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2594.jpg", - }, - summary: - "

An unexpected arrival north of the Wall changes circumstances. Dany is forced to face harsh realities. Bran learns more of his destiny. Tyrion sees the truth of his situation.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4991", - }, - }, - }, - { - id: 116522, - url: - "http://www.tvmaze.com/episodes/116522/game-of-thrones-5x01-the-wars-to-come", - name: "The Wars to Come", - season: 5, - number: 1, - airdate: "2015-04-12", - airtime: "21:00", - airstamp: "2015-04-13T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/10/25988.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/10/25988.jpg", - }, - summary: - "

Varys discusses a conspiracy with Tyrion; Daenerys' rule faces a new threat; Jon finds himself between two kings; and Cersei and Jaime try to move on from Tywin's demise.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/116522", - }, - }, - }, - { - id: 144328, - url: - "http://www.tvmaze.com/episodes/144328/game-of-thrones-5x02-the-house-of-black-and-white", - name: "The House of Black and White", - season: 5, - number: 2, - airdate: "2015-04-19", - airtime: "21:00", - airstamp: "2015-04-20T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/10/25989.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/10/25989.jpg", - }, - summary: - "

Arya arrives in Braavos; Brienne and Podrick find danger while traveling; Cersei worries about Myrcella in Dorne when Ellaria Sand seeks revenge for Oberyn's death; Jon is tempted by Stannis.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/144328", - }, - }, - }, - { - id: 144329, - url: - "http://www.tvmaze.com/episodes/144329/game-of-thrones-5x03-high-sparrow", - name: "High Sparrow", - season: 5, - number: 3, - airdate: "2015-04-26", - airtime: "21:00", - airstamp: "2015-04-27T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/10/25990.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/10/25990.jpg", - }, - summary: - "

Cersei meets the High Sparrow after learning of a clergyman's embarrassing tale. Meanwhile, Davos talks to Jon about the future of Winterfell, where Ramsay Snow has just learned the identity of his future bride; Arya grows impatient doing menial tasks in the House of Black and White; and Tyrion searches for more comfortable surroundings on a long trip with Varys.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/144329", - }, - }, - }, - { - id: 144330, - url: - "http://www.tvmaze.com/episodes/144330/game-of-thrones-5x04-sons-of-the-harpy", - name: "Sons of the Harpy", - season: 5, - number: 4, - airdate: "2015-05-03", - airtime: "21:00", - airstamp: "2015-05-04T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/10/26444.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/10/26444.jpg", - }, - summary: - "

Margaery seeks prudent counsel. Jaime Struggles in foreign lands. Dany answers the Harpy's call.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/144330", - }, - }, - }, - { - id: 151777, - url: - "http://www.tvmaze.com/episodes/151777/game-of-thrones-5x05-kill-the-boy", - name: "Kill the Boy", - season: 5, - number: 5, - airdate: "2015-05-10", - airtime: "21:00", - airstamp: "2015-05-11T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/10/26819.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/10/26819.jpg", - }, - summary: - "

Dany makes a difficult decision in Meereen. Jon recruits the help of an unexpected ally. Brienne searches for Sansa. Theon remains under Ramsay's control.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/151777", - }, - }, - }, - { - id: 152766, - url: - "http://www.tvmaze.com/episodes/152766/game-of-thrones-5x06-unbowed-unbent-unbroken", - name: "Unbowed, Unbent, Unbroken", - season: 5, - number: 6, - airdate: "2015-05-17", - airtime: "21:00", - airstamp: "2015-05-18T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/10/27259.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/10/27259.jpg", - }, - summary: - "

Arya trains. Jorah and Tyrion run into slavers. Trystane and Myrcella make plans. Jaime and Bronn reach their destination. The Sand Snakes attack.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/152766", - }, - }, - }, - { - id: 153327, - url: - "http://www.tvmaze.com/episodes/153327/game-of-thrones-5x07-the-gift", - name: "The Gift", - season: 5, - number: 7, - airdate: "2015-05-24", - airtime: "21:00", - airstamp: "2015-05-25T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/11/27535.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/11/27535.jpg", - }, - summary: - "

Jon prepares for conflict. Sansa tries to talk to Theon. Brienne waits for a sign. Stannis remains stubborn. Jaime attempts to reconnect with family.



", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/153327", - }, - }, - }, - { - id: 155299, - url: - "http://www.tvmaze.com/episodes/155299/game-of-thrones-5x08-hardhome", - name: "Hardhome", - season: 5, - number: 8, - airdate: "2015-05-31", - airtime: "21:00", - airstamp: "2015-06-01T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/11/28151.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/11/28151.jpg", - }, - summary: - "

Arya makes progress in her training. Sansa confronts an old friend. Cersei struggles. Jon travels.



", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/155299", - }, - }, - }, - { - id: 160040, - url: - "http://www.tvmaze.com/episodes/160040/game-of-thrones-5x09-the-dance-of-dragons", - name: "The Dance of Dragons", - season: 5, - number: 9, - airdate: "2015-06-07", - airtime: "21:00", - airstamp: "2015-06-08T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/11/29160.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/11/29160.jpg", - }, - summary: - "

Stannis confronts a troubling decision. Jon returns to The Wall. Mace visits the Iron Bank. Arya encounters someone from her past. Dany reluctantly oversees a traditional celebration of athleticism.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/160040", - }, - }, - }, - { - id: 162186, - url: - "http://www.tvmaze.com/episodes/162186/game-of-thrones-5x10-mothers-mercy", - name: "Mother's Mercy", - season: 5, - number: 10, - airdate: "2015-06-14", - airtime: "21:00", - airstamp: "2015-06-15T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/12/30012.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/12/30012.jpg", - }, - summary: - "

Cersei seeks forgiveness; Jon faces a new challenge; Arya plots to cross a name off her list; Tyrion sees a familiar face; and Daenerys finds herself surrounded by strangers.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/162186", - }, - }, - }, - { - id: 560813, - url: - "http://www.tvmaze.com/episodes/560813/game-of-thrones-6x01-the-red-woman", - name: "The Red Woman", - season: 6, - number: 1, - airdate: "2016-04-24", - airtime: "21:00", - airstamp: "2016-04-25T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/56/142371.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/56/142371.jpg", - }, - summary: - "

Jon Snow is dead. Daenerys meets a strong man. Cersei sees her daughter again.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/560813", - }, - }, - }, - { - id: 664672, - url: "http://www.tvmaze.com/episodes/664672/game-of-thrones-6x02-home", - name: "Home", - season: 6, - number: 2, - airdate: "2016-05-01", - airtime: "21:00", - airstamp: "2016-05-02T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/56/142372.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/56/142372.jpg", - }, - summary: - "

Bran trains with the Three-Eyed Raven. In King's Landing, Jaime advises Tommen. Tyrion demands good news, but has to make his own. At Castle Black, the Night's Watch stands behind Thorne. Ramsay Bolton proposes a plan, and Balon Greyjoy entertains other proposals.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/664672", - }, - }, - }, - { - id: 664673, - url: - "http://www.tvmaze.com/episodes/664673/game-of-thrones-6x03-oathbreaker", - name: "Oathbreaker", - season: 6, - number: 3, - airdate: "2016-05-08", - airtime: "21:00", - airstamp: "2016-05-09T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/56/142370.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/56/142370.jpg", - }, - summary: - "

Daenerys meets her future. Bran meets the past. Tommen confronts the High Sparrow. Arya trains to be No One. Varys finds an answer. Ramsay gets a gift.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/664673", - }, - }, - }, - { - id: 664674, - url: - "http://www.tvmaze.com/episodes/664674/game-of-thrones-6x04-book-of-the-stranger", - name: "Book of the Stranger", - season: 6, - number: 4, - airdate: "2016-05-15", - airtime: "21:00", - airstamp: "2016-05-16T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/56/142273.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/56/142273.jpg", - }, - summary: - "

Tyrion strikes a deal. Jorah and Daario undertake a difficult task. Jaime and Cersei try to improve their situation.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/664674", - }, - }, - }, - { - id: 664675, - url: - "http://www.tvmaze.com/episodes/664675/game-of-thrones-6x05-the-door", - name: "The Door", - season: 6, - number: 5, - airdate: "2016-05-22", - airtime: "21:00", - airstamp: "2016-05-23T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/60/150273.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/60/150273.jpg", - }, - summary: - "

Tyrion seeks a strange ally. Bran learns a great deal. Brienne goes on a mission. Arya is given a chance to prove herself.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/664675", - }, - }, - }, - { - id: 664676, - url: - "http://www.tvmaze.com/episodes/664676/game-of-thrones-6x06-blood-of-my-blood", - name: "Blood of My Blood", - season: 6, - number: 6, - airdate: "2016-05-29", - airtime: "21:00", - airstamp: "2016-05-30T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/60/150274.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/60/150274.jpg", - }, - summary: - "

An old foe comes back into the picture. Gilly meets Sam's family. Arya faces a difficult choice. Jaime faces off against the High Sparrow.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/664676", - }, - }, - }, - { - id: 717449, - url: - "http://www.tvmaze.com/episodes/717449/game-of-thrones-6x07-the-broken-man", - name: "The Broken Man", - season: 6, - number: 7, - airdate: "2016-06-05", - airtime: "21:00", - airstamp: "2016-06-06T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/60/150275.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/60/150275.jpg", - }, - summary: - "

The High Sparrow eyes another target. Jaime confronts a hero. Arya makes a plan. The North is reminded.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/717449", - }, - }, - }, - { - id: 729573, - url: "http://www.tvmaze.com/episodes/729573/game-of-thrones-6x08-no-one", - name: "No One", - season: 6, - number: 8, - airdate: "2016-06-12", - airtime: "21:00", - airstamp: "2016-06-13T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/61/153044.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/61/153044.jpg", - }, - summary: - "

Jaime encounters a hero; the High Sparrow fixates on another prey; Arya hatches a new plan; Yara and Theon plot their next move; Olenna and Cersei discuss their families' futures.

While Jaime weighs his options, Cersei answers a request. Tyrion's plans bear fruit. Arya faces a new test.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/729573", - }, - }, - }, - { - id: 729574, - url: - "http://www.tvmaze.com/episodes/729574/game-of-thrones-6x09-battle-of-the-bastards", - name: "Battle of the Bastards", - season: 6, - number: 9, - airdate: "2016-06-19", - airtime: "21:00", - airstamp: "2016-06-20T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/62/155042.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/62/155042.jpg", - }, - summary: - "

Ramsay surprises his audience. Jon retaliates. Dany is true to her word.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/729574", - }, - }, - }, - { - id: 729575, - url: - "http://www.tvmaze.com/episodes/729575/game-of-thrones-6x10-the-winds-of-winter", - name: "The Winds of Winter", - season: 6, - number: 10, - airdate: "2016-06-26", - airtime: "21:00", - airstamp: "2016-06-27T01:00:00+00:00", - runtime: 69, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/63/157920.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/63/157920.jpg", - }, - summary: - "

Alliances are made, the High Sparrow is holding trials at King's Landing, Daenerys is sailing for the Seven Kingdoms and a new King of the North is crowned.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/729575", - }, - }, - }, - { - id: 937256, - url: - "http://www.tvmaze.com/episodes/937256/game-of-thrones-7x01-dragonstone", - name: "Dragonstone", - season: 7, - number: 1, - airdate: "2017-07-16", - airtime: "21:00", - airstamp: "2017-07-17T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/120/302038.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/120/302038.jpg", - }, - summary: - "

Jon organizes the defense of the North. Cersei tries to even the odds. Daenerys comes home.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/937256", - }, - }, - }, - { - id: 1221410, - url: - "http://www.tvmaze.com/episodes/1221410/game-of-thrones-7x02-stormborn", - name: "Stormborn", - season: 7, - number: 2, - airdate: "2017-07-23", - airtime: "21:00", - airstamp: "2017-07-24T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/120/302047.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/120/302047.jpg", - }, - summary: - "

Daenerys receives an unexpected visitor. Jon faces a revolt. Tyrion plans the conquest of Westeros.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/1221410", - }, - }, - }, - { - id: 1221411, - url: - "http://www.tvmaze.com/episodes/1221411/game-of-thrones-7x03-the-queens-justice", - name: "The Queen's Justice", - season: 7, - number: 3, - airdate: "2017-07-30", - airtime: "21:00", - airstamp: "2017-07-31T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/122/306938.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/122/306938.jpg", - }, - summary: - "

Daenerys holds court. Cersei returns a gift. Jaime learns from his mistakes.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/1221411", - }, - }, - }, - { - id: 1221412, - url: - "http://www.tvmaze.com/episodes/1221412/game-of-thrones-7x04-the-spoils-of-war", - name: "The Spoils of War", - season: 7, - number: 4, - airdate: "2017-08-06", - airtime: "21:00", - airstamp: "2017-08-07T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/123/307677.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/123/307677.jpg", - }, - summary: - "

Arya gets to the final destination. Daenerys takes it upon herself to strike back.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/1221412", - }, - }, - }, - { - id: 1221413, - url: - "http://www.tvmaze.com/episodes/1221413/game-of-thrones-7x05-eastwatch", - name: "Eastwatch", - season: 7, - number: 5, - airdate: "2017-08-13", - airtime: "21:00", - airstamp: "2017-08-14T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/124/310839.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/124/310839.jpg", - }, - summary: - "

Daenerys demands loyalty from the surviving Lannister soldiers; Jon heeds Bran's warning about White Walkers on the move; Cersei vows to vanquish anyone or anything that stands in her way.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/1221413", - }, - }, - }, - { - id: 1221414, - url: - "http://www.tvmaze.com/episodes/1221414/game-of-thrones-7x06-beyond-the-wall", - name: "Beyond the Wall", - season: 7, - number: 6, - airdate: "2017-08-20", - airtime: "21:00", - airstamp: "2017-08-21T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/125/312651.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/125/312651.jpg", - }, - summary: - "

Jon's mission continues north of the wall, but the odds against his ragged band of misfits may be greater than he imagined.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/1221414", - }, - }, - }, - { - id: 1221415, - url: - "http://www.tvmaze.com/episodes/1221415/game-of-thrones-7x07-the-dragon-and-the-wolf", - name: "The Dragon and the Wolf", - season: 7, - number: 7, - airdate: "2017-08-27", - airtime: "21:00", - airstamp: "2017-08-28T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/125/314502.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/125/314502.jpg", - }, - summary: - "

Cersei sits on the Iron Throne; Daenerys sails across the Narrow Sea; Jon Snow is King in the North, and winter is finally here.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/1221415", - }, - }, - }, - { - id: 1590943, - url: - "http://www.tvmaze.com/episodes/1590943/game-of-thrones-8x01-winterfell", - name: "Winterfell", - season: 8, - number: 1, - airdate: "2019-04-14", - airtime: "21:00", - airstamp: "2019-04-15T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/191/479477.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/191/479477.jpg", - }, - summary: - "

Arriving at Winterfell, Jon and Daenerys struggle to unite a divided North. Jon gets some big news.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/1590943", - }, - }, - }, - { - id: 1623964, - url: - "http://www.tvmaze.com/episodes/1623964/game-of-thrones-8x02-a-knight-of-the-seven-kingdoms", - name: "A Knight of the Seven Kingdoms", - season: 8, - number: 2, - airdate: "2019-04-21", - airtime: "21:00", - airstamp: "2019-04-22T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/192/482451.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/192/482451.jpg", - }, - summary: - "

Jaime faces judgement and Winterfell prepares for the battle to come.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/1623964", - }, - }, - }, - { - id: 1623965, - url: - "http://www.tvmaze.com/episodes/1623965/game-of-thrones-8x03-the-long-night", - name: "The Long Night", - season: 8, - number: 3, - airdate: "2019-04-28", - airtime: "21:00", - airstamp: "2019-04-29T01:00:00+00:00", - runtime: 90, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/192/482465.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/192/482465.jpg", - }, - summary: "

Winterfell fights the Army of the Dead.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/1623965", - }, - }, - }, - { - id: 1623966, - url: - "http://www.tvmaze.com/episodes/1623966/game-of-thrones-8x04-the-last-of-the-starks", - name: "The Last of the Starks", - season: 8, - number: 4, - airdate: "2019-05-05", - airtime: "21:00", - airstamp: "2019-05-06T01:00:00+00:00", - runtime: 78, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/195/487839.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/195/487839.jpg", - }, - summary: - "

The survivors plan their next steps; Cersei makes a power move.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/1623966", - }, - }, - }, - { - id: 1623967, - url: - "http://www.tvmaze.com/episodes/1623967/game-of-thrones-8x05-the-bells", - name: "The Bells", - season: 8, - number: 5, - airdate: "2019-05-12", - airtime: "21:00", - airstamp: "2019-05-13T01:00:00+00:00", - runtime: 79, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/196/491994.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/196/491994.jpg", - }, - summary: - "

Varys betrays his queen, and Daenerys brings her forces to King's Landing.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/1623967", - }, - }, - }, - { - id: 1623968, - url: - "http://www.tvmaze.com/episodes/1623968/game-of-thrones-8x06-the-iron-throne", - name: "The Iron Throne", - season: 8, - number: 6, - airdate: "2019-05-19", - airtime: "21:00", - airstamp: "2019-05-20T01:00:00+00:00", - runtime: 80, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/198/495648.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/198/495648.jpg", - }, - summary: - "

In the aftermath of the devastating attack on King's Landing, Daenerys must face the survivors.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/1623968", - }, - }, - }, - ]; -} diff --git a/index.html b/index.html index 2e67577..1fa00b1 100644 --- a/index.html +++ b/index.html @@ -19,12 +19,10 @@

+
Data originally provided by TVMaze.com - - - diff --git a/script.js b/script.js index d562ea1..9e1fd37 100644 --- a/script.js +++ b/script.js @@ -1,8 +1,29 @@ // You can edit ALL of the code here + +const episodesLink = "https://api.tvmaze.com/shows/82/episodes"; //this link will use for fetch + +const message = document.getElementById("message"); //for loading message + function setup() { - const allEpisodes = getAllEpisodes(); - console.log(allEpisodes); - makePageForEpisodes(allEpisodes); + message.textContent = "Loading episodes..."; //shows this message when episode data is loading + const fetchEpisodes = async () => { + //store fetchEpisodes function in a variable + const response = await fetch(episodesLink); //wait for the server to respond + return await response.json(); //API sends the data in JSON format + }; + + fetchEpisodes() + .then((episodes) => { + //success handling + + makePageForEpisodes(episodes); //now data is a normal JavaScript array/object + message.textContent = ""; // loading message disappears after episode arrives + console.log(episodes); + }) + .catch(() => { + //error handling + message.textContent = "Error: Data not found. Try again later..."; + }); } function makePageForEpisodes(episodeList) { @@ -19,7 +40,9 @@ function makePageForEpisodes(episodeList) { for (const episode of episodeList) { const option = document.createElement("option"); - option.textContent = `S${String(episode.season).padStart(2, "0")}E${String(episode.number).padStart(2, "0")} - ${episode.name}`; + option.textContent = `S${String(episode.season).padStart(2, "0")}E${String( + episode.number + ).padStart(2, "0")} - ${episode.name}`; option.value = episode.id; @@ -37,12 +60,14 @@ function makePageForEpisodes(episodeList) { card.classList.add("episode-card"); const episodeName = document.createElement("h2"); - episodeName.textContent = `${episode.name} - S${String(episode.season).padStart(2, "0")}E${String(episode.number).padStart(2, "0")}`; + episodeName.textContent = `${episode.name} - S${String( + episode.season + ).padStart(2, "0")}E${String(episode.number).padStart(2, "0")}`; card.append(episodeName); const image = document.createElement("img"); image.src = episode.image.medium; - image.alt = `Episode image for ${episode.name}`; + image.alt = `Episode image for ${episode.name}`; card.append(image); const summary = document.createElement("p"); @@ -94,7 +119,10 @@ function makePageForEpisodes(episodeList) { backButton.style.display = ""; }); // Back to all episodes backButton.addEventListener("click", function () { - location.reload(); + backButton.style.display = "none"; // hides the Back button + for (const card of episodeCards) { + card.style.display = ""; // shows all episode cards again +} }); } From 2d8f400b1aabdfca19163c41a7565b22cf0bef87 Mon Sep 17 00:00:00 2001 From: Dipa-Sarker Date: Wed, 12 Aug 2026 13:26:53 +0100 Subject: [PATCH 14/23] add label for dropdown & change searchbox color --- script.js | 12 +++++++++--- style.css | 3 ++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/script.js b/script.js index 9e1fd37..c4a454c 100644 --- a/script.js +++ b/script.js @@ -32,6 +32,8 @@ function makePageForEpisodes(episodeList) { // Selector implemented const selectEpisodeList = document.createElement("select"); + selectEpisodeList.id = "episode-selector"; + //back button added const backButton = document.createElement("button"); backButton.textContent = "Back to all episodes"; @@ -51,6 +53,10 @@ function makePageForEpisodes(episodeList) { // Adding dropdown menu beside the search box const searchContainer = document.querySelector(".search-container"); + const episodeLabel = document.createElement("label"); //for showing label beside dropdown + episodeLabel.textContent = "Select an episode:"; + episodeLabel.htmlFor = "episode-selector"; + searchContainer.appendChild(episodeLabel); searchContainer.appendChild(selectEpisodeList); searchContainer.appendChild(backButton); @@ -120,9 +126,9 @@ function makePageForEpisodes(episodeList) { }); // Back to all episodes backButton.addEventListener("click", function () { backButton.style.display = "none"; // hides the Back button - for (const card of episodeCards) { - card.style.display = ""; // shows all episode cards again -} + for (const card of episodeCards) { + card.style.display = ""; // shows all episode cards again + } }); } diff --git a/style.css b/style.css index aab9167..e45bc71 100644 --- a/style.css +++ b/style.css @@ -44,7 +44,7 @@ #search-input, .search-container select { - background-color: #ffffff; + background-color: gainsboro; color: #111111; border: 1px solid #d9d9d9; border-radius: 6px; @@ -53,6 +53,7 @@ #search-input { padding-left: 40px; + border: 1px solid black; } .search-container select { From 1bdadaa9ef52c92f99a2003108355d06a567958b Mon Sep 17 00:00:00 2001 From: Rizqah Date: Thu, 13 Aug 2026 13:49:47 +0100 Subject: [PATCH 15/23] Show selector and episode selector implemented --- index.html | 3 +- script.js | 164 +++++++++++++++++++++++++++++++++++------------------ 2 files changed, 111 insertions(+), 56 deletions(-) diff --git a/index.html b/index.html index 1fa00b1..8ac8f2d 100644 --- a/index.html +++ b/index.html @@ -19,7 +19,8 @@

-
+
+
Data originally provided by TVMaze.com diff --git a/script.js b/script.js index c4a454c..66a4d2d 100644 --- a/script.js +++ b/script.js @@ -1,64 +1,42 @@ // You can edit ALL of the code here const episodesLink = "https://api.tvmaze.com/shows/82/episodes"; //this link will use for fetch - -const message = document.getElementById("message"); //for loading message - -function setup() { - message.textContent = "Loading episodes..."; //shows this message when episode data is loading - const fetchEpisodes = async () => { - //store fetchEpisodes function in a variable - const response = await fetch(episodesLink); //wait for the server to respond - return await response.json(); //API sends the data in JSON format - }; - - fetchEpisodes() - .then((episodes) => { - //success handling - - makePageForEpisodes(episodes); //now data is a normal JavaScript array/object - message.textContent = ""; // loading message disappears after episode arrives - console.log(episodes); - }) - .catch(() => { - //error handling - message.textContent = "Error: Data not found. Try again later..."; - }); +const showsLink = "https://api.tvmaze.com/shows"; +const episodesMessage = document.getElementById("message1"); //for loading message +const showsMessage = document.getElementById("message2"); +async function setup() { + episodesMessage.textContent = "Loading episodes..."; //shows this message when episode data is loading + showsMessage.textContent = "Loading shows..."; //shows this message when shows data is loading + try { + const [episodesResponse, showsResponse] = await Promise.all([ + //wait for the server to respond + fetch(episodesLink), + fetch(showsLink), + ]); + + const episodes = await episodesResponse.json(); //API sends the data in JSON format + const shows = await showsResponse.json(); //API sends the data in JSON format + const episodeCards = makePageForEpisodes(episodes); //now data is a normal JavaScript array/object + makeShowSelector(shows); //now the shows data is a a normal Javascript object + makeEpisodeSelector(episodes, episodeCards); + episodesMessage.textContent = ""; // loading message disappears after episode arrives + showsMessage.textContent = ""; // loading message disappears after show arrives + } catch (error) { + //error handling + episodesMessage.textContent = + "Error: Episodes not found. Try again later..."; + showsMessage.textContent = "Error: Show not found. Try again later..."; + } } function makePageForEpisodes(episodeList) { const rootElem = document.getElementById("root"); + rootElem.innerHTML = ""; //This is so that it refreshes everytime we choose a new show and want to see new episodes const episodeCards = []; - // Selector implemented - const selectEpisodeList = document.createElement("select"); - selectEpisodeList.id = "episode-selector"; - - //back button added - const backButton = document.createElement("button"); - backButton.textContent = "Back to all episodes"; - backButton.id = "back-button"; - backButton.style.display = "none"; - for (const episode of episodeList) { - const option = document.createElement("option"); - - option.textContent = `S${String(episode.season).padStart(2, "0")}E${String( - episode.number - ).padStart(2, "0")} - ${episode.name}`; - - option.value = episode.id; - - selectEpisodeList.appendChild(option); - } - - // Adding dropdown menu beside the search box - const searchContainer = document.querySelector(".search-container"); - const episodeLabel = document.createElement("label"); //for showing label beside dropdown - episodeLabel.textContent = "Select an episode:"; - episodeLabel.htmlFor = "episode-selector"; - searchContainer.appendChild(episodeLabel); - searchContainer.appendChild(selectEpisodeList); - searchContainer.appendChild(backButton); + // episodeSelector implemented + // const selectEpisodeList = document.createElement("select"); // we need to move this out of makepageforepisodes because it would append the same episodes each time if we don + // selectEpisodeList.id = "episode-selector"; // Creating episode cards for (const episode of episodeList) { @@ -67,7 +45,7 @@ function makePageForEpisodes(episodeList) { const episodeName = document.createElement("h2"); episodeName.textContent = `${episode.name} - S${String( - episode.season + episode.season, ).padStart(2, "0")}E${String(episode.number).padStart(2, "0")}`; card.append(episodeName); @@ -105,15 +83,60 @@ function makePageForEpisodes(episodeList) { episodeCount.textContent = `Displaying ${matchingEpisodes}/${episodeList.length} episodes`; }); + return episodeCards; +} +//episode selector taken out of makepage for episodes +function makeEpisodeSelector(episodeList, episodeCards) { + const searchContainer = document.querySelector(".search-container"); + const selectEpisodeList = document.createElement("select"); + selectEpisodeList.id = "episode-selector"; + // Create the episode container only once + let episodeContainer = document.getElementById("episode-container"); + + if (!episodeContainer) { + episodeContainer = document.createElement("div"); + episodeContainer.id = "episode-container"; + searchContainer.appendChild(episodeContainer); + } - // Selector change event + // Clear the old episode selector before adding the new one + episodeContainer.innerHTML = ""; + //back button added + const backButton = document.createElement("button"); + backButton.textContent = "Back to all episodes"; + backButton.id = "back-button"; + backButton.style.display = "none"; + // Add episodes as options + for (const episode of episodeList) { + const option = document.createElement("option"); + + option.textContent = `S${String(episode.season).padStart(2, "0")}E${String( + episode.number, + ).padStart(2, "0")} - ${episode.name}`; + + option.value = episode.id; + + selectEpisodeList.appendChild(option); + } + + // Add dropdown to the page + const episodeLabel = document.createElement("label"); + + episodeLabel.textContent = "Select an episode: "; + episodeLabel.htmlFor = "episode-selector"; + + // Add everything to the container + episodeContainer.appendChild(episodeLabel); + episodeContainer.appendChild(selectEpisodeList); + episodeContainer.appendChild(backButton); + + // What happens when an episode is selected selectEpisodeList.addEventListener("change", function () { const selectedId = selectEpisodeList.value; const selectedEpisode = episodeList.find(function (episode) { return episode.id === Number(selectedId); }); - for (const card of episodeCards) { card.style.display = "none"; } @@ -131,5 +154,36 @@ function makePageForEpisodes(episodeList) { } }); } +//showSelector implemented +function makeShowSelector(shows) { + const selectShowList = document.createElement("select"); + selectShowList.id = "show-selector"; + const showLabel = document.createElement("label"); + showLabel.textContent = "Select a show: "; + showLabel.htmlFor = "show-selector"; + shows.forEach((show) => { + const option = document.createElement("option"); + option.textContent = show.name; + option.value = show.id; + selectShowList.appendChild(option); + }); + selectShowList.addEventListener("change", async () => { + const showId = selectShowList.value; + + const response = await fetch( + `https://api.tvmaze.com/shows/${showId}/episodes`, + ); + + const episodes = await response.json(); + + // fill the episode dropdown + const episodeCards = makePageForEpisodes(episodes); + makeEpisodeSelector(episodes, episodeCards); + }); + // Add dropdown to the page + const searchContainer = document.querySelector(".search-container"); + searchContainer.appendChild(showLabel); + searchContainer.appendChild(selectShowList); +} window.onload = setup; From d8f38b16d4874d7a99fbfba9b18736cccb4b3197 Mon Sep 17 00:00:00 2001 From: Rizqah Date: Thu, 13 Aug 2026 17:49:17 +0100 Subject: [PATCH 16/23] saved episodes in cache, and added show all episodes button --- script.js | 106 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 82 insertions(+), 24 deletions(-) diff --git a/script.js b/script.js index 66a4d2d..c450c6d 100644 --- a/script.js +++ b/script.js @@ -1,24 +1,35 @@ // You can edit ALL of the code here -const episodesLink = "https://api.tvmaze.com/shows/82/episodes"; //this link will use for fetch const showsLink = "https://api.tvmaze.com/shows"; const episodesMessage = document.getElementById("message1"); //for loading message const showsMessage = document.getElementById("message2"); + +//stores episodes that we already fetched +let cachedEpisodes = {}; + async function setup() { episodesMessage.textContent = "Loading episodes..."; //shows this message when episode data is loading showsMessage.textContent = "Loading shows..."; //shows this message when shows data is loading + try { - const [episodesResponse, showsResponse] = await Promise.all([ - //wait for the server to respond - fetch(episodesLink), - fetch(showsLink), - ]); + //fetch the shows + const showsResponse = await fetch(showsLink); - const episodes = await episodesResponse.json(); //API sends the data in JSON format const shows = await showsResponse.json(); //API sends the data in JSON format - const episodeCards = makePageForEpisodes(episodes); //now data is a normal JavaScript array/object - makeShowSelector(shows); //now the shows data is a a normal Javascript object - makeEpisodeSelector(episodes, episodeCards); + + // Sort shows alphabetically, ignoring case + shows.sort(function (show1, show2) { + return show1.name.localeCompare(show2.name, undefined, { + sensitivity: "base", + }); + }); + + makeShowSelector(shows); //now the shows data is a normal Javascript object + + // Load the first show's episodes when the page loads + const firstShowId = shows[0].id; + const episodeCards = await loadEpisodes(firstShowId); + episodesMessage.textContent = ""; // loading message disappears after episode arrives showsMessage.textContent = ""; // loading message disappears after show arrives } catch (error) { @@ -29,6 +40,39 @@ async function setup() { } } +// Load episodes for a show +async function loadEpisodes(showId) { + // Check if we already fetched this show's episodes + if (cachedEpisodes[showId]) { + const episodes = cachedEpisodes[showId]; + + const episodeCards = makePageForEpisodes(episodes); + makeEpisodeSelector(episodes, episodeCards); + + return episodeCards; + } + + try { + const response = await fetch( + `https://api.tvmaze.com/shows/${showId}/episodes`, + ); + + const episodes = await response.json(); + + // Store the episodes so we don't fetch the same URL again + cachedEpisodes[showId] = episodes; + + // fill the episode dropdown + const episodeCards = makePageForEpisodes(episodes); + makeEpisodeSelector(episodes, episodeCards); + + return episodeCards; + } catch (error) { + episodesMessage.textContent = + "Error: Episodes not found. Try again later..."; + } +} + function makePageForEpisodes(episodeList) { const rootElem = document.getElementById("root"); rootElem.innerHTML = ""; //This is so that it refreshes everytime we choose a new show and want to see new episodes @@ -83,13 +127,16 @@ function makePageForEpisodes(episodeList) { episodeCount.textContent = `Displaying ${matchingEpisodes}/${episodeList.length} episodes`; }); + return episodeCards; } + //episode selector taken out of makepage for episodes function makeEpisodeSelector(episodeList, episodeCards) { const searchContainer = document.querySelector(".search-container"); const selectEpisodeList = document.createElement("select"); selectEpisodeList.id = "episode-selector"; + // Create the episode container only once let episodeContainer = document.getElementById("episode-container"); @@ -101,11 +148,7 @@ function makeEpisodeSelector(episodeList, episodeCards) { // Clear the old episode selector before adding the new one episodeContainer.innerHTML = ""; - //back button added - const backButton = document.createElement("button"); - backButton.textContent = "Back to all episodes"; - backButton.id = "back-button"; - backButton.style.display = "none"; + // Add episodes as options for (const episode of episodeList) { const option = document.createElement("option"); @@ -119,6 +162,12 @@ function makeEpisodeSelector(episodeList, episodeCards) { selectEpisodeList.appendChild(option); } + //back button added + const backButton = document.createElement("button"); + backButton.textContent = "Show all episodes"; + backButton.id = "back-button"; + backButton.style.display = ""; + // Add dropdown to the page const episodeLabel = document.createElement("label"); @@ -137,6 +186,7 @@ function makeEpisodeSelector(episodeList, episodeCards) { const selectedEpisode = episodeList.find(function (episode) { return episode.id === Number(selectedId); }); + for (const card of episodeCards) { card.style.display = "none"; } @@ -146,44 +196,52 @@ function makeEpisodeSelector(episodeList, episodeCards) { selectedCard.style.display = ""; backButton.style.display = ""; - }); // Back to all episodes + }); + + // Back to all episodes backButton.addEventListener("click", function () { - backButton.style.display = "none"; // hides the Back button + backButton.style.display = ""; + for (const card of episodeCards) { card.style.display = ""; // shows all episode cards again } }); } + //showSelector implemented function makeShowSelector(shows) { const selectShowList = document.createElement("select"); selectShowList.id = "show-selector"; + const showLabel = document.createElement("label"); showLabel.textContent = "Select a show: "; showLabel.htmlFor = "show-selector"; + shows.forEach((show) => { const option = document.createElement("option"); option.textContent = show.name; option.value = show.id; + selectShowList.appendChild(option); }); + selectShowList.addEventListener("change", async () => { const showId = selectShowList.value; - const response = await fetch( - `https://api.tvmaze.com/shows/${showId}/episodes`, - ); + episodesMessage.textContent = "Loading episodes..."; - const episodes = await response.json(); + // Load episodes using the cache + await loadEpisodes(showId); - // fill the episode dropdown - const episodeCards = makePageForEpisodes(episodes); - makeEpisodeSelector(episodes, episodeCards); + episodesMessage.textContent = ""; }); + // Add dropdown to the page const searchContainer = document.querySelector(".search-container"); + searchContainer.appendChild(showLabel); searchContainer.appendChild(selectShowList); } + window.onload = setup; From f910e482b8f8222bb2145ce90f4bae6bf4399730 Mon Sep 17 00:00:00 2001 From: Rizqah Date: Thu, 13 Aug 2026 17:56:40 +0100 Subject: [PATCH 17/23] searching makes use of summary --- script.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/script.js b/script.js index c450c6d..9ebde0b 100644 --- a/script.js +++ b/script.js @@ -117,7 +117,10 @@ function makePageForEpisodes(episodeList) { episodeList.forEach(function (episode, index) { const card = episodeCards[index]; - if (episode.name.toLowerCase().includes(searchTerm)) { + if ( + episode.name.toLowerCase().includes(searchTerm) || + episode.summary.toLowerCase().includes(searchTerm) + ) { card.style.display = ""; matchingEpisodes++; } else { From 6d5c765e8f44b9bdc0fd2273a11af0361738a1f9 Mon Sep 17 00:00:00 2001 From: Dipa-Sarker Date: Fri, 14 Aug 2026 02:32:14 +0100 Subject: [PATCH 18/23] refactored level 400 --- script.js | 132 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 81 insertions(+), 51 deletions(-) diff --git a/script.js b/script.js index 9ebde0b..8d42f32 100644 --- a/script.js +++ b/script.js @@ -1,12 +1,41 @@ // You can edit ALL of the code here const showsLink = "https://api.tvmaze.com/shows"; + const episodesMessage = document.getElementById("message1"); //for loading message const showsMessage = document.getElementById("message2"); +const searchContainer = document.querySelector(".search-container"); +const rootElem = document.getElementById("root"); +const searchInput = document.getElementById("search-input"); +const episodeCount = document.getElementById("episode-count"); //stores episodes that we already fetched let cachedEpisodes = {}; +// Refactoring: store repeated error messages in constants +const EPISODE_ERROR = + "Error: Episodes not found. Try again later..."; +const SHOW_ERROR = + "Error: Show not found. Try again later..."; + +// Refactoring: reusable helper for episode codes +function formatEpisodeCode(episode) { + return `S${String(episode.season).padStart(2, "0")}E${String( + episode.number + ).padStart(2, "0")}`; +} + +// Refactoring: reusable helper for episode URL +function getEpisodeUrl(showId) { + return `https://api.tvmaze.com/shows/${showId}/episodes`; +} + +// Refactoring: reusable helper for clearing messages +function clearMessages() { + episodesMessage.textContent = ""; // loading message disappears after episode arrives + showsMessage.textContent = ""; // loading message disappears after show arrives +} +//==================== SETUP ==================== async function setup() { episodesMessage.textContent = "Loading episodes..."; //shows this message when episode data is loading showsMessage.textContent = "Loading shows..."; //shows this message when shows data is loading @@ -14,7 +43,6 @@ async function setup() { try { //fetch the shows const showsResponse = await fetch(showsLink); - const shows = await showsResponse.json(); //API sends the data in JSON format // Sort shows alphabetically, ignoring case @@ -28,24 +56,24 @@ async function setup() { // Load the first show's episodes when the page loads const firstShowId = shows[0].id; - const episodeCards = await loadEpisodes(firstShowId); + await loadEpisodes(firstShowId); + + clearMessages(); - episodesMessage.textContent = ""; // loading message disappears after episode arrives - showsMessage.textContent = ""; // loading message disappears after show arrives } catch (error) { //error handling - episodesMessage.textContent = - "Error: Episodes not found. Try again later..."; - showsMessage.textContent = "Error: Show not found. Try again later..."; + episodesMessage.textContent = EPISODE_ERROR; + showsMessage.textContent = SHOW_ERROR; } } + +// ==================== LOAD EPISODES ==================== // Load episodes for a show async function loadEpisodes(showId) { // Check if we already fetched this show's episodes if (cachedEpisodes[showId]) { const episodes = cachedEpisodes[showId]; - const episodeCards = makePageForEpisodes(episodes); makeEpisodeSelector(episodes, episodeCards); @@ -53,12 +81,9 @@ async function loadEpisodes(showId) { } try { - const response = await fetch( - `https://api.tvmaze.com/shows/${showId}/episodes`, - ); - + const response = await fetch(getEpisodeUrl(showId)); const episodes = await response.json(); - + // Store the episodes so we don't fetch the same URL again cachedEpisodes[showId] = episodes; @@ -68,13 +93,13 @@ async function loadEpisodes(showId) { return episodeCards; } catch (error) { - episodesMessage.textContent = - "Error: Episodes not found. Try again later..."; + episodesMessage.textContent = EPISODE_ERROR; } } + +// ==================== DISPLAY EPISODES ==================== function makePageForEpisodes(episodeList) { - const rootElem = document.getElementById("root"); rootElem.innerHTML = ""; //This is so that it refreshes everytime we choose a new show and want to see new episodes const episodeCards = []; @@ -88,9 +113,7 @@ function makePageForEpisodes(episodeList) { card.classList.add("episode-card"); const episodeName = document.createElement("h2"); - episodeName.textContent = `${episode.name} - S${String( - episode.season, - ).padStart(2, "0")}E${String(episode.number).padStart(2, "0")}`; + episodeName.textContent = `${episode.name} - ${formatEpisodeCode(episode)}`; card.append(episodeName); const image = document.createElement("img"); @@ -106,11 +129,9 @@ function makePageForEpisodes(episodeList) { rootElem.append(card); } - // Search implemented - const searchInput = document.getElementById("search-input"); - const episodeCount = document.getElementById("episode-count"); - + // ==================== SEARCH ==================== searchInput.addEventListener("input", function () { + const searchTerm = searchInput.value.toLowerCase(); let matchingEpisodes = 0; @@ -134,15 +155,14 @@ function makePageForEpisodes(episodeList) { return episodeCards; } +// ==================== MAKE EPISODE SELECTOR ==================== //episode selector taken out of makepage for episodes function makeEpisodeSelector(episodeList, episodeCards) { - const searchContainer = document.querySelector(".search-container"); - const selectEpisodeList = document.createElement("select"); - selectEpisodeList.id = "episode-selector"; - // Create the episode container only once + // Find the existing episode container let episodeContainer = document.getElementById("episode-container"); + // Create it only if it doesn't exist if (!episodeContainer) { episodeContainer = document.createElement("div"); episodeContainer.id = "episode-container"; @@ -152,42 +172,43 @@ function makeEpisodeSelector(episodeList, episodeCards) { // Clear the old episode selector before adding the new one episodeContainer.innerHTML = ""; - // Add episodes as options + // Create a new episode dropdown + const selectEpisodeList = document.createElement("select"); + selectEpisodeList.id = "episode-selector"; + + // add episodes for the currently selected show for (const episode of episodeList) { const option = document.createElement("option"); - option.textContent = `S${String(episode.season).padStart(2, "0")}E${String( - episode.number, - ).padStart(2, "0")} - ${episode.name}`; + option.textContent = `${formatEpisodeCode(episode)} - ${episode.name}`; option.value = episode.id; selectEpisodeList.appendChild(option); } - //back button added - const backButton = document.createElement("button"); - backButton.textContent = "Show all episodes"; - backButton.id = "back-button"; - backButton.style.display = ""; - - // Add dropdown to the page + // Add dropdown to the page, create label const episodeLabel = document.createElement("label"); - episodeLabel.textContent = "Select an episode: "; episodeLabel.htmlFor = "episode-selector"; - + + //create show all button + const backButton = document.createElement("button"); + backButton.textContent = "Show all episodes"; + backButton.id = "back-button"; + // Add everything to the container episodeContainer.appendChild(episodeLabel); episodeContainer.appendChild(selectEpisodeList); episodeContainer.appendChild(backButton); + // ==================== EPISODE SELECTOR ==================== // What happens when an episode is selected selectEpisodeList.addEventListener("change", function () { - const selectedId = selectEpisodeList.value; + const selectedId = Number(selectEpisodeList.value); const selectedEpisode = episodeList.find(function (episode) { - return episode.id === Number(selectedId); + return episode.id === selectedId; }); for (const card of episodeCards) { @@ -195,22 +216,27 @@ function makeEpisodeSelector(episodeList, episodeCards) { } const index = episodeList.indexOf(selectedEpisode); - const selectedCard = episodeCards[index]; - - selectedCard.style.display = ""; + episodeCards[index].style.display = ""; backButton.style.display = ""; + }); - // Back to all episodes + // shows all episodes again backButton.addEventListener("click", function () { - backButton.style.display = ""; - for (const card of episodeCards) { card.style.display = ""; // shows all episode cards again } + // Clear search as well + searchInput.value = ""; + + episodeCount.textContent = + `Displaying ${episodeList.length}/${episodeList.length} episodes`; + + backButton.style.display = "none"; }); } +// ==================== SHOW SELECTOR ==================== //showSelector implemented function makeShowSelector(shows) { const selectShowList = document.createElement("select"); @@ -230,7 +256,12 @@ function makeShowSelector(shows) { }); selectShowList.addEventListener("change", async () => { - const showId = selectShowList.value; + const showId = Number(selectShowList.value); + + // Remember which show the user currently wants + selectedShowId = showId; + searchInput.value = ""; + episodeCount.textContent = ""; episodesMessage.textContent = "Loading episodes..."; @@ -240,9 +271,8 @@ function makeShowSelector(shows) { episodesMessage.textContent = ""; }); + // Add dropdown to the page - const searchContainer = document.querySelector(".search-container"); - searchContainer.appendChild(showLabel); searchContainer.appendChild(selectShowList); } From a6ebe76a7d1d87f8eff9e0923c2410950d8e5b73 Mon Sep 17 00:00:00 2001 From: Dipa-Sarker Date: Sat, 15 Aug 2026 02:53:10 +0100 Subject: [PATCH 19/23] add show listing --- script.js | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 74 insertions(+), 7 deletions(-) diff --git a/script.js b/script.js index 8d42f32..0c409df 100644 --- a/script.js +++ b/script.js @@ -12,6 +12,9 @@ const episodeCount = document.getElementById("episode-count"); //stores episodes that we already fetched let cachedEpisodes = {}; +// Store all shows so they can be displayed again when the user returns to the shows listing, i.e front page +let allShows = []; + // Refactoring: store repeated error messages in constants const EPISODE_ERROR = "Error: Episodes not found. Try again later..."; @@ -35,6 +38,7 @@ function clearMessages() { episodesMessage.textContent = ""; // loading message disappears after episode arrives showsMessage.textContent = ""; // loading message disappears after show arrives } + //==================== SETUP ==================== async function setup() { episodesMessage.textContent = "Loading episodes..."; //shows this message when episode data is loading @@ -43,7 +47,9 @@ async function setup() { try { //fetch the shows const showsResponse = await fetch(showsLink); - const shows = await showsResponse.json(); //API sends the data in JSON format + const shows = await showsResponse.json(); //API sends the data in JSON format then + // shows data is a normal Javascript object + console.log(shows[0]); // Sort shows alphabetically, ignoring case shows.sort(function (show1, show2) { @@ -52,11 +58,9 @@ async function setup() { }); }); - makeShowSelector(shows); //now the shows data is a normal Javascript object + makeShowSelector(shows); - // Load the first show's episodes when the page loads - const firstShowId = shows[0].id; - await loadEpisodes(firstShowId); + makePageForShows(shows); //Display all TV shows as cards on the front page clearMessages(); @@ -65,8 +69,63 @@ async function setup() { episodesMessage.textContent = EPISODE_ERROR; showsMessage.textContent = SHOW_ERROR; } + + allShows = shows; // Save the fetched shows for later use (e.g. Back to Shows button) } +// ==================== DISPLAY SHOWS ON THE FRONT PAGE ==================== +function makePageForShows(showList) { + rootElem.innerHTML = ""; + + //creating show cards for FRONT PAGE + for (const show of showList) { + const card = document.createElement("section"); + card.classList.add("show-card"); + + const showName = document.createElement("h2"); + showName.textContent = show.name; + + showName.style.cursor = "pointer"; //shows hand cursor when mouse is over the show name; tells the user, you can click this + + showName.addEventListener("click", async function () { //when the user clicks show name, Load that show's episodes + await loadEpisodes(show.id); + }); + + const image = document.createElement("img"); //6 elements according to requirement 1 + image.src = show.image.medium; + image.alt = `Show image for ${show.name}`; + + const genres = document.createElement("p"); + genres.textContent = `Genres: ${show.genres.join(", ")}`; + + const status = document.createElement("p"); + status.textContent = `Status: ${show.status}`; + + const rating = document.createElement("p"); + rating.textContent = `Rating: ${show.rating.average}`; + + const runtime = document.createElement("p"); + runtime.textContent = `Runtime: ${show.runtime} minutes`; + + const summary = document.createElement("div"); + summary.innerHTML = show.summary; + +card.append( //add all contents inside the card + showName, + image, + genres, + status, + rating, + runtime, + summary +); + + rootElem.append(card); //add the completed card inside the page container (root) /display the finished card on the page + } +} + + + // ==================== LOAD EPISODES ==================== // Load episodes for a show @@ -101,7 +160,7 @@ async function loadEpisodes(showId) { // ==================== DISPLAY EPISODES ==================== function makePageForEpisodes(episodeList) { rootElem.innerHTML = ""; //This is so that it refreshes everytime we choose a new show and want to see new episodes - const episodeCards = []; + const episodeCards = []; //this will further use for episode search, episode selector, show all episodes button // episodeSelector implemented // const selectEpisodeList = document.createElement("select"); // we need to move this out of makepageforepisodes because it would append the same episodes each time if we don @@ -126,7 +185,7 @@ function makePageForEpisodes(episodeList) { card.append(summary); episodeCards.push(card); - rootElem.append(card); + rootElem.append(card); //display card on the page } // ==================== SEARCH ==================== @@ -196,11 +255,17 @@ function makeEpisodeSelector(episodeList, episodeCards) { const backButton = document.createElement("button"); backButton.textContent = "Show all episodes"; backButton.id = "back-button"; + + //create back to show button + const backToShowButton = document.createElement("button"); + backToShowButton.textContent = "Show all shows"; + backToShowButton.id = "back-to-shows-button"; // Add everything to the container episodeContainer.appendChild(episodeLabel); episodeContainer.appendChild(selectEpisodeList); episodeContainer.appendChild(backButton); + episodeContainer.appendChild(backToShowButton); //newly added for back to show // ==================== EPISODE SELECTOR ==================== // What happens when an episode is selected @@ -234,6 +299,8 @@ function makeEpisodeSelector(episodeList, episodeCards) { backButton.style.display = "none"; }); + + } // ==================== SHOW SELECTOR ==================== From 280e573ec7c53c7ca6288637d540867d85c785fb Mon Sep 17 00:00:00 2001 From: Dipa-Sarker Date: Sun, 16 Aug 2026 14:47:40 +0100 Subject: [PATCH 20/23] added functionality for level 500 --- index.html | 4 +- script.js | 130 ++++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 121 insertions(+), 13 deletions(-) diff --git a/index.html b/index.html index 8ac8f2d..6b82170 100644 --- a/index.html +++ b/index.html @@ -14,8 +14,10 @@
- + + +

diff --git a/script.js b/script.js index 0c409df..f6ae800 100644 --- a/script.js +++ b/script.js @@ -8,13 +8,21 @@ const searchContainer = document.querySelector(".search-container"); const rootElem = document.getElementById("root"); const searchInput = document.getElementById("search-input"); const episodeCount = document.getElementById("episode-count"); +const searchLabel = document.getElementById("search-label"); +const showCount = document.getElementById("show-count"); //stores episodes that we already fetched let cachedEpisodes = {}; -// Store all shows so they can be displayed again when the user returns to the shows listing, i.e front page + // Store all shows so they can be displayed again when the user returns to the shows listing, i.e front page let allShows = []; +// Tracks whether the user is viewing shows or episodes; on front page or 2nd page +let currentView = "shows"; + +// Store the episodes of the currently selected show +let currentEpisodes = []; + // Refactoring: store repeated error messages in constants const EPISODE_ERROR = "Error: Episodes not found. Try again later..."; @@ -39,6 +47,31 @@ function clearMessages() { showsMessage.textContent = ""; // loading message disappears after show arrives } +// Helper function: Search TV shows by name, genre or summary and return only the matching shows +function searchShows(searchTerm, allShows) { + const matchingShows = []; + searchTerm = searchTerm.toLowerCase(); + for(const show of allShows){ //go through each show from all shows + if(show.name.toLowerCase().includes(searchTerm) || show.genres.toString().toLowerCase().includes(searchTerm) || (show.summary || "").toLowerCase().includes(searchTerm)){ + matchingShows.push(show); + } + } + return matchingShows; // returns an array containing only the matching shows +} + +// Helper function: Search episodes by name or summary and return only the matching episodes +function searchEpisodes(searchTerm, currentEpisodes) { + const matchingEpisodes = []; + searchTerm = searchTerm.toLowerCase(); + for(const episode of currentEpisodes){ //go through each episode from current episodes + if(episode.name.toLowerCase().includes(searchTerm) || (episode.summary || "").toLowerCase().includes(searchTerm)){ + matchingEpisodes.push(episode); + } + } + return matchingEpisodes; // returns an array containing only the matching episodes +} + + //==================== SETUP ==================== async function setup() { episodesMessage.textContent = "Loading episodes..."; //shows this message when episode data is loading @@ -49,7 +82,7 @@ async function setup() { const showsResponse = await fetch(showsLink); const shows = await showsResponse.json(); //API sends the data in JSON format then // shows data is a normal Javascript object - console.log(shows[0]); + allShows = shows; // Save the fetched shows for later use (e.g. Back to Shows button) // Sort shows alphabetically, ignoring case shows.sort(function (show1, show2) { @@ -65,16 +98,31 @@ async function setup() { clearMessages(); } catch (error) { + console.error(error); //error handling episodesMessage.textContent = EPISODE_ERROR; showsMessage.textContent = SHOW_ERROR; } - allShows = shows; // Save the fetched shows for later use (e.g. Back to Shows button) + } // ==================== DISPLAY SHOWS ON THE FRONT PAGE ==================== function makePageForShows(showList) { + + // Show the "Filtering for" label on the shows listing page, i.e front page + searchLabel.style.display = ""; + + // change the search box to search shows on the front page + searchInput.placeholder = "Search shows..."; + + //Shows the text if no. of shows found after filtering on front page + showCount.textContent = `Found ${showList.length} shows`; + + // User is currently viewing the shows listing on front page + currentView = "shows"; + + rootElem.innerHTML = ""; //creating show cards for FRONT PAGE @@ -133,6 +181,7 @@ async function loadEpisodes(showId) { // Check if we already fetched this show's episodes if (cachedEpisodes[showId]) { const episodes = cachedEpisodes[showId]; + currentEpisodes = episodes; // Save the episodes of the selected show for searching and filtering const episodeCards = makePageForEpisodes(episodes); makeEpisodeSelector(episodes, episodeCards); @@ -142,6 +191,7 @@ async function loadEpisodes(showId) { try { const response = await fetch(getEpisodeUrl(showId)); const episodes = await response.json(); + currentEpisodes = episodes; // Save the episodes of the selected show for searching and filtering // Store the episodes so we don't fetch the same URL again cachedEpisodes[showId] = episodes; @@ -159,6 +209,18 @@ async function loadEpisodes(showId) { // ==================== DISPLAY EPISODES ==================== function makePageForEpisodes(episodeList) { + + // Hide the "Filtering for" label while viewing episodes on the 2nd page + searchLabel.style.display = "none"; + //Hide the text "no. of shows found" after found filtered shows, on the 2nd page + showCount.textContent = ""; + + // change the search box to search episodes when viewing episodes on the 2nd page + searchInput.placeholder = "Search episodes..."; + + // user is currently viewing episodes on the 2nd page + currentView = "episodes"; + rootElem.innerHTML = ""; //This is so that it refreshes everytime we choose a new show and want to see new episodes const episodeCards = []; //this will further use for episode search, episode selector, show all episodes button @@ -176,7 +238,7 @@ function makePageForEpisodes(episodeList) { card.append(episodeName); const image = document.createElement("img"); - image.src = episode.image.medium; + image.src = episode.image?.medium || ""; // image.alt = `Episode image for ${episode.name}`; card.append(image); @@ -189,7 +251,7 @@ function makePageForEpisodes(episodeList) { } // ==================== SEARCH ==================== - searchInput.addEventListener("input", function () { + /*searchInput.addEventListener("input", function () { const searchTerm = searchInput.value.toLowerCase(); let matchingEpisodes = 0; @@ -209,10 +271,10 @@ function makePageForEpisodes(episodeList) { }); episodeCount.textContent = `Displaying ${matchingEpisodes}/${episodeList.length} episodes`; - }); + }); */ - return episodeCards; -} + return episodeCards; +} // ==================== MAKE EPISODE SELECTOR ==================== //episode selector taken out of makepage for episodes @@ -251,12 +313,12 @@ function makeEpisodeSelector(episodeList, episodeCards) { episodeLabel.textContent = "Select an episode: "; episodeLabel.htmlFor = "episode-selector"; - //create show all button + //create "show all" button const backButton = document.createElement("button"); backButton.textContent = "Show all episodes"; backButton.id = "back-button"; - //create back to show button + //create "back to show" button const backToShowButton = document.createElement("button"); backToShowButton.textContent = "Show all shows"; backToShowButton.id = "back-to-shows-button"; @@ -300,12 +362,32 @@ function makeEpisodeSelector(episodeList, episodeCards) { backButton.style.display = "none"; }); + // Reset episode controls and return to the shows listing on FRONT PAGE + backToShowButton.addEventListener("click", function () { //user back to show button clicked + searchInput.value = ""; //clear episode search + episodeCount.textContent = ""; //clear episode count + makePageForShows(allShows); //display all TV shows again on front page +}); } // ==================== SHOW SELECTOR ==================== //showSelector implemented function makeShowSelector(shows) { + +// Find the existing show container +let showContainer = document.getElementById("show-container"); + +// Create it only if it doesn't exist +if (!showContainer) { + showContainer = document.createElement("div"); + showContainer.id = "show-container"; + searchContainer.appendChild(showContainer); +} + +// Clear old dropdown before adding a new one +showContainer.innerHTML = ""; + const selectShowList = document.createElement("select"); selectShowList.id = "show-selector"; @@ -340,8 +422,32 @@ function makeShowSelector(shows) { // Add dropdown to the page - searchContainer.appendChild(showLabel); - searchContainer.appendChild(selectShowList); + showContainer.appendChild(showLabel); + showContainer.appendChild(selectShowList); } +// ==================== SEARCH FOR BOTH SHOWS & EPISODES (Globar search listener) ==================== +// Run every time the user types in the search box +searchInput.addEventListener("input", function () { + + const searchTerm = searchInput.value.toLowerCase(); // Get the current search text and convert it to lowercase + + + if (currentView === "shows") { // If the user is on the shows listing page/ 2nd page + const matchingShows = searchShows(searchTerm, allShows); // Find all shows that match the search text + makePageForShows(matchingShows); // Redisplay only the matching shows + makeShowSelector(matchingShows); //display only matching shows in dropdown +} +else if (currentView === "episodes") { // If the user is viewing episodes + const matchingEpisodes = searchEpisodes(searchTerm, currentEpisodes); // Find all episodes that match the search text + + episodeCount.textContent = + `Displaying ${matchingEpisodes.length}/${currentEpisodes.length} episodes`; + + makePageForEpisodes(matchingEpisodes); // Redisplay only the matching episodes +} + +}); + + window.onload = setup; From 170a97e385528d79da1a48efea90fba20fc5bb33 Mon Sep 17 00:00:00 2001 From: Dipa-Sarker Date: Sun, 16 Aug 2026 22:04:54 +0100 Subject: [PATCH 21/23] fix style of front page --- script.js | 75 +++++++++++++++++++++++++++++++++++-------------------- style.css | 51 ++++++++++++++++++++++++++++++++++++- 2 files changed, 98 insertions(+), 28 deletions(-) diff --git a/script.js b/script.js index f6ae800..d071860 100644 --- a/script.js +++ b/script.js @@ -122,7 +122,6 @@ function makePageForShows(showList) { // User is currently viewing the shows listing on front page currentView = "shows"; - rootElem.innerHTML = ""; //creating show cards for FRONT PAGE @@ -157,19 +156,45 @@ function makePageForShows(showList) { const summary = document.createElement("div"); summary.innerHTML = show.summary; + + + +const showInfo = document.createElement("div"); +showInfo.classList.add("show-info"); -card.append( //add all contents inside the card - showName, - image, +showInfo.append( + rating, genres, status, - rating, - runtime, - summary + runtime +); + +summary.classList.add("summary"); + +card.append( + showName, + image, + summary, + showInfo ); rootElem.append(card); //add the completed card inside the page container (root) /display the finished card on the page } + + // Hide episode controls + const episodeContainer = document.getElementById("episode-container"); + + if (episodeContainer) { + episodeContainer.style.display = "none"; + } + + // Show show selector + const showContainer = document.getElementById("show-container"); + + if (showContainer) { + showContainer.style.display = ""; + } + } @@ -249,31 +274,23 @@ function makePageForEpisodes(episodeList) { episodeCards.push(card); rootElem.append(card); //display card on the page } + +// Hide show selector + const showContainer = document.getElementById("show-container"); - // ==================== SEARCH ==================== - /*searchInput.addEventListener("input", function () { - - const searchTerm = searchInput.value.toLowerCase(); - let matchingEpisodes = 0; - - episodeList.forEach(function (episode, index) { - const card = episodeCards[index]; + if (showContainer) { + showContainer.style.display = "none"; + } - if ( - episode.name.toLowerCase().includes(searchTerm) || - episode.summary.toLowerCase().includes(searchTerm) - ) { - card.style.display = ""; - matchingEpisodes++; - } else { - card.style.display = "none"; - } - }); + // Show episode controls + const episodeContainer = document.getElementById("episode-container"); - episodeCount.textContent = `Displaying ${matchingEpisodes}/${episodeList.length} episodes`; - }); */ + if (episodeContainer) { + episodeContainer.style.display = ""; + } return episodeCards; + } // ==================== MAKE EPISODE SELECTOR ==================== @@ -368,6 +385,10 @@ function makeEpisodeSelector(episodeList, episodeCards) { episodeCount.textContent = ""; //clear episode count makePageForShows(allShows); //display all TV shows again on front page + makeShowSelector(allShows); + if (episodeContainer) { + episodeContainer.style.display = "none"; +} }); } diff --git a/style.css b/style.css index e45bc71..cb1507f 100644 --- a/style.css +++ b/style.css @@ -2,7 +2,8 @@ color: black; display: flex; flex-wrap: wrap; - background-color: grey; + width: 100%; + background-color: whitesmoke; } .episode-card { width: 30%; @@ -27,6 +28,13 @@ text-align: justify; padding: 10px; } + +#episode-container { + display: flex; + gap: 20px; + align-items: center; +} + .search-container { position: relative; display: flex; @@ -82,3 +90,44 @@ #back-button:hover { background-color: #111111; } + +.show-card { + display: grid; + grid-template-columns: 200px 1fr 220px; + gap: 1rem; + width: 100%; + background-color: whitesmoke; + border: 1px solid whitesmoke; + box-shadow: 2px 2px 6px grey; + box-sizing: border-box; + padding: 1rem; + margin-bottom: 1rem; +} +.show-card h2 { + grid-column: 1 / 4; + margin-top: 0; + font-size: xxx-large; + font-family: sans-serif; +} + +.show-card img { + grid-column: 1; + width: 100%; +} + +.summary { + grid-column: 2; + line-height: 150%; +} + +.show-info { + grid-column: 3; + padding-top: 1rem; + padding-bottom: 1rem; + padding-left: 1rem; + padding-right: 1rem; + line-height: 3rem; + background-color: whitesmoke; + box-shadow: 2px 2px 6px grey; + +} From 34ed0b74fe23e3cee9bf1094ab77b843968cb89a Mon Sep 17 00:00:00 2001 From: Dipa-Sarker Date: Mon, 17 Aug 2026 01:54:28 +0100 Subject: [PATCH 22/23] refactoring & adding comments --- index.html | 1 - script.js | 103 +++++++++++++++++++++++++++-------------------------- style.css | 2 ++ 3 files changed, 55 insertions(+), 51 deletions(-) diff --git a/index.html b/index.html index 6b82170..a753191 100644 --- a/index.html +++ b/index.html @@ -15,7 +15,6 @@
-

diff --git a/script.js b/script.js index d071860..7e749c0 100644 --- a/script.js +++ b/script.js @@ -1,53 +1,63 @@ // You can edit ALL of the code here -const showsLink = "https://api.tvmaze.com/shows"; - -const episodesMessage = document.getElementById("message1"); //for loading message -const showsMessage = document.getElementById("message2"); -const searchContainer = document.querySelector(".search-container"); -const rootElem = document.getElementById("root"); -const searchInput = document.getElementById("search-input"); -const episodeCount = document.getElementById("episode-count"); -const searchLabel = document.getElementById("search-label"); -const showCount = document.getElementById("show-count"); - -//stores episodes that we already fetched +const showsLink = "https://api.tvmaze.com/shows"; // TVMaze API endpoint that returns all TV shows + +const episodesMessage = document.getElementById("message1"); // Element used to display episode loading/error messages +const showsMessage = document.getElementById("message2"); // Element used to display show loading/error messages +const searchContainer = document.querySelector(".search-container"); // Container that holds search input and dropdowns +const rootElem = document.getElementById("root"); // Main container where show cards or episode cards are displayed +const searchInput = document.getElementById("search-input"); // Search box used for both show search and episode search +const episodeCount = document.getElementById("episode-count"); // Displays episode filtering count +// Example: Displaying 5/73 episodes +const searchLabel = document.getElementById("search-label"); // Label displayed on the shows page +// Example: "Filtering for" +const showCount = document.getElementById("show-count"); // Displays number of matching shows +// Example: Found 12 shows + + +//Stores episodes that we already fetched from the API +// Key = showId +// Value = array of episodes +// Prevents fetching the same show twice let cachedEpisodes = {}; - // Store all shows so they can be displayed again when the user returns to the shows listing, i.e front page +// Store all shows fetched during setup(),so they can be displayed again when the user +// returns to the shows listing, i.e front page let allShows = []; // Tracks whether the user is viewing shows or episodes; on front page or 2nd page let currentView = "shows"; // Store the episodes of the currently selected show +// Used for searching and filtering episodes let currentEpisodes = []; -// Refactoring: store repeated error messages in constants +// Shared error message shown when episode fetch fails const EPISODE_ERROR = "Error: Episodes not found. Try again later..."; +// Shared error message shown when show fetch fails const SHOW_ERROR = "Error: Show not found. Try again later..."; -// Refactoring: reusable helper for episode codes +// Reusable helper function for episode codes, Example: season: 1, episode: 1 -> S01E01 function formatEpisodeCode(episode) { return `S${String(episode.season).padStart(2, "0")}E${String( episode.number ).padStart(2, "0")}`; } -// Refactoring: reusable helper for episode URL +// Reusable helper function which creates the API URL for a specific show's episodes function getEpisodeUrl(showId) { return `https://api.tvmaze.com/shows/${showId}/episodes`; } -// Refactoring: reusable helper for clearing messages +//Reusable helper function for removing loading and error messages from the page function clearMessages() { - episodesMessage.textContent = ""; // loading message disappears after episode arrives - showsMessage.textContent = ""; // loading message disappears after show arrives + episodesMessage.textContent = ""; // loading/error message disappears after episode arrives + showsMessage.textContent = ""; // loading/error message disappears after show arrives } -// Helper function: Search TV shows by name, genre or summary and return only the matching shows +// Reusable helper function: Search TV shows by name, genre or summary and return only the matching shows function searchShows(searchTerm, allShows) { const matchingShows = []; searchTerm = searchTerm.toLowerCase(); @@ -59,7 +69,7 @@ function searchShows(searchTerm, allShows) { return matchingShows; // returns an array containing only the matching shows } -// Helper function: Search episodes by name or summary and return only the matching episodes +// Reusable helper function: Search episodes by name or summary and return only the matching episodes function searchEpisodes(searchTerm, currentEpisodes) { const matchingEpisodes = []; searchTerm = searchTerm.toLowerCase(); @@ -73,38 +83,38 @@ function searchEpisodes(searchTerm, currentEpisodes) { //==================== SETUP ==================== +// Runs once when the page loads and Fetches all TV shows from the API async function setup() { episodesMessage.textContent = "Loading episodes..."; //shows this message when episode data is loading showsMessage.textContent = "Loading shows..."; //shows this message when shows data is loading try { - //fetch the shows - const showsResponse = await fetch(showsLink); - const shows = await showsResponse.json(); //API sends the data in JSON format then - // shows data is a normal Javascript object - allShows = shows; // Save the fetched shows for later use (e.g. Back to Shows button) - - // Sort shows alphabetically, ignoring case + // Try to fetch and display show data. If anything fails, moves to catch() + const showsResponse = await fetch(showsLink); // Send request to TVMaze API for all shows + const shows = await showsResponse.json(); //API sends the data in JSON format then convert + // shows data is a normal Javascript array object + allShows = shows; // Store the fetched all shows globally so they can be + // displayed again later(e.g. when user clicks "Show all shows") + + // Sort shows alphabetically by name, ignoring case shows.sort(function (show1, show2) { - return show1.name.localeCompare(show2.name, undefined, { - sensitivity: "base", - }); + return show1.name.toLowerCase().localeCompare(show2.name.toLowerCase()); // Convert both names to lowercase + // then compare alphabetically }); - makeShowSelector(shows); - - makePageForShows(shows); //Display all TV shows as cards on the front page + makePageForShows(shows); // Display all TV shows as cards on the front page + makeShowSelector(shows); // Create dropdown containing all shows - clearMessages(); + // Data loaded successfully, remove loading messages + clearMessages(); - } catch (error) { + } catch (error) { // Runs if fetching or processing data fails, i.e error handling console.error(error); - //error handling - episodesMessage.textContent = EPISODE_ERROR; + + episodesMessage.textContent = EPISODE_ERROR; //shows error message showsMessage.textContent = SHOW_ERROR; } - } // ==================== DISPLAY SHOWS ON THE FRONT PAGE ==================== @@ -197,9 +207,6 @@ card.append( } - - - // ==================== LOAD EPISODES ==================== // Load episodes for a show async function loadEpisodes(showId) { @@ -249,10 +256,6 @@ function makePageForEpisodes(episodeList) { rootElem.innerHTML = ""; //This is so that it refreshes everytime we choose a new show and want to see new episodes const episodeCards = []; //this will further use for episode search, episode selector, show all episodes button - // episodeSelector implemented - // const selectEpisodeList = document.createElement("select"); // we need to move this out of makepageforepisodes because it would append the same episodes each time if we don - // selectEpisodeList.id = "episode-selector"; - // Creating episode cards for (const episode of episodeList) { const card = document.createElement("section"); @@ -267,22 +270,22 @@ function makePageForEpisodes(episodeList) { image.alt = `Episode image for ${episode.name}`; card.append(image); - const summary = document.createElement("p"); + const summary = document.createElement("div"); summary.innerHTML = episode.summary; card.append(summary); episodeCards.push(card); rootElem.append(card); //display card on the page } - -// Hide show selector + + // Hide show selector const showContainer = document.getElementById("show-container"); if (showContainer) { showContainer.style.display = "none"; } - // Show episode controls + // Show episode controls const episodeContainer = document.getElementById("episode-container"); if (episodeContainer) { diff --git a/style.css b/style.css index cb1507f..7c8554c 100644 --- a/style.css +++ b/style.css @@ -26,7 +26,9 @@ } .episode-card p { text-align: justify; + line-height: 1.5rem; padding: 10px; + margin: 0; } #episode-container { From 22a1b0d1d3142eaa2c7d2769346bc2c15e9b4516 Mon Sep 17 00:00:00 2001 From: Dipa-Sarker Date: Tue, 18 Aug 2026 12:36:26 +0100 Subject: [PATCH 23/23] made correction for some functionality --- script.js | 472 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 246 insertions(+), 226 deletions(-) diff --git a/script.js b/script.js index 7e749c0..c54bacc 100644 --- a/script.js +++ b/script.js @@ -15,7 +15,7 @@ const showCount = document.getElementById("show-count"); // Displays number of m // Example: Found 12 shows -//Stores episodes that we already fetched from the API +//Stores episodes that we already fetched from the API, Level 400 // Key = showId // Value = array of episodes // Prevents fetching the same show twice @@ -25,10 +25,10 @@ let cachedEpisodes = {}; // returns to the shows listing, i.e front page let allShows = []; -// Tracks whether the user is viewing shows or episodes; on front page or 2nd page +// Tracks whether the user is viewing shows or episodes; i.e on front page or 2nd page let currentView = "shows"; -// Store the episodes of the currently selected show +// Store the fetched episodes of the currently selected show // Used for searching and filtering episodes let currentEpisodes = []; @@ -57,7 +57,7 @@ function clearMessages() { showsMessage.textContent = ""; // loading/error message disappears after show arrives } -// Reusable helper function: Search TV shows by name, genre or summary and return only the matching shows +// Level 500: Reusable helper function: Search TV shows by name, genre or summary and return only the matching shows function searchShows(searchTerm, allShows) { const matchingShows = []; searchTerm = searchTerm.toLowerCase(); @@ -69,13 +69,13 @@ function searchShows(searchTerm, allShows) { return matchingShows; // returns an array containing only the matching shows } -// Reusable helper function: Search episodes by name or summary and return only the matching episodes +// Level 200: Reusable helper function: Search episodes by name or summary and return only the matching episodes function searchEpisodes(searchTerm, currentEpisodes) { - const matchingEpisodes = []; - searchTerm = searchTerm.toLowerCase(); + const matchingEpisodes = []; // Store episodes that match the search text + searchTerm = searchTerm.toLowerCase(); //convert search text to lowercase for(const episode of currentEpisodes){ //go through each episode from current episodes if(episode.name.toLowerCase().includes(searchTerm) || (episode.summary || "").toLowerCase().includes(searchTerm)){ - matchingEpisodes.push(episode); + matchingEpisodes.push(episode); // Store matching episode into matchingEpisodes = []; } } return matchingEpisodes; // returns an array containing only the matching episodes @@ -88,7 +88,7 @@ async function setup() { episodesMessage.textContent = "Loading episodes..."; //shows this message when episode data is loading showsMessage.textContent = "Loading shows..."; //shows this message when shows data is loading - try { + try { //Level 400 // Try to fetch and display show data. If anything fails, moves to catch() const showsResponse = await fetch(showsLink); // Send request to TVMaze API for all shows const shows = await showsResponse.json(); //API sends the data in JSON format then convert @@ -96,7 +96,7 @@ async function setup() { allShows = shows; // Store the fetched all shows globally so they can be // displayed again later(e.g. when user clicks "Show all shows") - // Sort shows alphabetically by name, ignoring case + // Sort shows alphabetically by name, ignoring case, Level 400 shows.sort(function (show1, show2) { return show1.name.toLowerCase().localeCompare(show2.name.toLowerCase()); // Convert both names to lowercase // then compare alphabetically @@ -117,129 +117,9 @@ async function setup() { } -// ==================== DISPLAY SHOWS ON THE FRONT PAGE ==================== -function makePageForShows(showList) { - - // Show the "Filtering for" label on the shows listing page, i.e front page - searchLabel.style.display = ""; - - // change the search box to search shows on the front page - searchInput.placeholder = "Search shows..."; - - //Shows the text if no. of shows found after filtering on front page - showCount.textContent = `Found ${showList.length} shows`; - - // User is currently viewing the shows listing on front page - currentView = "shows"; - - rootElem.innerHTML = ""; - - //creating show cards for FRONT PAGE - for (const show of showList) { - const card = document.createElement("section"); - card.classList.add("show-card"); - - const showName = document.createElement("h2"); - showName.textContent = show.name; - - showName.style.cursor = "pointer"; //shows hand cursor when mouse is over the show name; tells the user, you can click this - - showName.addEventListener("click", async function () { //when the user clicks show name, Load that show's episodes - await loadEpisodes(show.id); - }); - - const image = document.createElement("img"); //6 elements according to requirement 1 - image.src = show.image.medium; - image.alt = `Show image for ${show.name}`; - - const genres = document.createElement("p"); - genres.textContent = `Genres: ${show.genres.join(", ")}`; - - const status = document.createElement("p"); - status.textContent = `Status: ${show.status}`; - - const rating = document.createElement("p"); - rating.textContent = `Rating: ${show.rating.average}`; - - const runtime = document.createElement("p"); - runtime.textContent = `Runtime: ${show.runtime} minutes`; - - const summary = document.createElement("div"); - summary.innerHTML = show.summary; - - - -const showInfo = document.createElement("div"); -showInfo.classList.add("show-info"); - -showInfo.append( - rating, - genres, - status, - runtime -); - -summary.classList.add("summary"); - -card.append( - showName, - image, - summary, - showInfo -); - - rootElem.append(card); //add the completed card inside the page container (root) /display the finished card on the page - } - - // Hide episode controls - const episodeContainer = document.getElementById("episode-container"); - - if (episodeContainer) { - episodeContainer.style.display = "none"; - } - - // Show show selector - const showContainer = document.getElementById("show-container"); - - if (showContainer) { - showContainer.style.display = ""; - } - -} - -// ==================== LOAD EPISODES ==================== -// Load episodes for a show -async function loadEpisodes(showId) { - // Check if we already fetched this show's episodes - if (cachedEpisodes[showId]) { - const episodes = cachedEpisodes[showId]; - currentEpisodes = episodes; // Save the episodes of the selected show for searching and filtering - const episodeCards = makePageForEpisodes(episodes); - makeEpisodeSelector(episodes, episodeCards); - - return episodeCards; - } - - try { - const response = await fetch(getEpisodeUrl(showId)); - const episodes = await response.json(); - currentEpisodes = episodes; // Save the episodes of the selected show for searching and filtering - - // Store the episodes so we don't fetch the same URL again - cachedEpisodes[showId] = episodes; - - // fill the episode dropdown - const episodeCards = makePageForEpisodes(episodes); - makeEpisodeSelector(episodes, episodeCards); - - return episodeCards; - } catch (error) { - episodesMessage.textContent = EPISODE_ERROR; - } -} - - -// ==================== DISPLAY EPISODES ==================== +// ==================== Level-100 DISPLAY EPISODES ==================== +// Creates and displays episode cards for every episode +// in the provided episode list function makePageForEpisodes(episodeList) { // Hide the "Filtering for" label while viewing episodes on the 2nd page @@ -253,41 +133,40 @@ function makePageForEpisodes(episodeList) { // user is currently viewing episodes on the 2nd page currentView = "episodes"; - rootElem.innerHTML = ""; //This is so that it refreshes everytime we choose a new show and want to see new episodes + rootElem.innerHTML = ""; //Clear the old episode cards before creating and displaying the new episode cards const episodeCards = []; //this will further use for episode search, episode selector, show all episodes button // Creating episode cards - for (const episode of episodeList) { - const card = document.createElement("section"); - card.classList.add("episode-card"); - - const episodeName = document.createElement("h2"); - episodeName.textContent = `${episode.name} - ${formatEpisodeCode(episode)}`; - card.append(episodeName); - - const image = document.createElement("img"); - image.src = episode.image?.medium || ""; // - image.alt = `Episode image for ${episode.name}`; - card.append(image); - - const summary = document.createElement("div"); - summary.innerHTML = episode.summary; - card.append(summary); - - episodeCards.push(card); - rootElem.append(card); //display card on the page + for (const episode of episodeList) { // Create one card for one episode + const card = document.createElement("section"); // Create a card container for one episode + card.classList.add("episode-card"); // Add CSS class for episode styling + + const episodeName = document.createElement("h2"); // Create heading for episode title + episodeName.textContent = `${episode.name} - ${formatEpisodeCode(episode)}`; // Display episode title and code + // Example: Winter Is Coming - S01E01 + card.append(episodeName); // Add heading to episode card + + const image = document.createElement("img"); // Create episode image element + image.src = episode.image?.medium || ""; // Use episode image if available + image.alt = `Episode image for ${episode.name}`; // Alternative text shown if image cannot load + card.append(image); //Add image to episode card + + const summary = document.createElement("div"); // Create container for episode summary + summary.innerHTML = episode.summary; // Display episode summary returned by API + card.append(summary); // Add summary to episode card + + episodeCards.push(card); // Save card reference for later filtering + rootElem.append(card); //display completes episode card on the page } // Hide show selector const showContainer = document.getElementById("show-container"); - if (showContainer) { showContainer.style.display = "none"; } // Show episode controls const episodeContainer = document.getElementById("episode-container"); - if (episodeContainer) { episodeContainer.style.display = ""; } @@ -296,7 +175,7 @@ function makePageForEpisodes(episodeList) { } -// ==================== MAKE EPISODE SELECTOR ==================== +// ==================== Level-200 MAKE EPISODE SELECTOR ==================== //episode selector taken out of makepage for episodes function makeEpisodeSelector(episodeList, episodeCards) { @@ -307,166 +186,307 @@ function makeEpisodeSelector(episodeList, episodeCards) { if (!episodeContainer) { episodeContainer = document.createElement("div"); episodeContainer.id = "episode-container"; - searchContainer.appendChild(episodeContainer); + searchContainer.append(episodeContainer); } // Clear the old episode selector before adding the new one episodeContainer.innerHTML = ""; - // Create a new episode dropdown + //rootElem.innerHTML = "" deletes old episode cards. + // currentCards is updated to point to the newly created cards. + let currentCards = episodeCards; + + // Create a new episode selector dropdown const selectEpisodeList = document.createElement("select"); selectEpisodeList.id = "episode-selector"; - // add episodes for the currently selected show + // Add episodes to the episode selector dropdown -level 200 /300 + // Add episodes to the episode selector dropdown for the currently selected show for (const episode of episodeList) { const option = document.createElement("option"); - option.textContent = `${formatEpisodeCode(episode)} - ${episode.name}`; - - option.value = episode.id; - - selectEpisodeList.appendChild(option); + option.value = episode.id; //Each option stores its episode ID. i.e 4952 + selectEpisodeList.append(option); //Add curent option to the dropdown } - // Add dropdown to the page, create label + // Add dropdown to the page, create label, Level 200 const episodeLabel = document.createElement("label"); episodeLabel.textContent = "Select an episode: "; episodeLabel.htmlFor = "episode-selector"; - //create "show all" button + //Create "show all episodes" button, Level 200 const backButton = document.createElement("button"); backButton.textContent = "Show all episodes"; backButton.id = "back-button"; - //create "back to show" button + //Create "back to show/Show all shows" button, Level 500 const backToShowButton = document.createElement("button"); backToShowButton.textContent = "Show all shows"; backToShowButton.id = "back-to-shows-button"; - // Add everything to the container - episodeContainer.appendChild(episodeLabel); - episodeContainer.appendChild(selectEpisodeList); - episodeContainer.appendChild(backButton); - episodeContainer.appendChild(backToShowButton); //newly added for back to show - - // ==================== EPISODE SELECTOR ==================== - // What happens when an episode is selected +// Add all episode controls to the episode container + episodeContainer.append( + episodeLabel, + selectEpisodeList, + backButton, + backToShowButton +); + + + // What happens when an episode is selected fron episode selector dropdown- Level 200/300 selectEpisodeList.addEventListener("change", function () { - const selectedId = Number(selectEpisodeList.value); + const selectedId = Number(selectEpisodeList.value); //Store only selected ID from the all episodes + // when the user selected an option from the dropdown - const selectedEpisode = episodeList.find(function (episode) { + const selectedEpisode = episodeList.find(function (episode) { //Look through every episode. Level 200/300 + //If episode.id equals selectedId, return that episode object. return episode.id === selectedId; - }); + }); - for (const card of episodeCards) { - card.style.display = "none"; - } + // Clear any active search filter so we're working with the full list again + searchInput.value = ""; + + // Re-render(create & display) all episode cards fresh, and update currentCards so every + // other handler (like backButton) stays pointed at currentCards + currentCards = makePageForEpisodes(episodeList); - const index = episodeList.indexOf(selectedEpisode); - episodeCards[index].style.display = ""; - backButton.style.display = ""; + + //Hide all episode cards. Level 200/300 + for (const card of currentCards) { + card.style.display = "none"; + } + + //For displaying selected episode card. Level 200/300 + const index = episodeList.indexOf(selectedEpisode); //Store the index of selected card + currentCards[index].style.display = ""; //Display only the selected episode card + }); - // shows all episodes again + // Shows all episodes again (Bonus part , Level 200) backButton.addEventListener("click", function () { - for (const card of episodeCards) { - card.style.display = ""; // shows all episode cards again - } + + //Re-render(create & display) all episode cards fresh, and update currentCards + currentCards = makePageForEpisodes(episodeList); + // Clear search as well searchInput.value = ""; episodeCount.textContent = - `Displaying ${episodeList.length}/${episodeList.length} episodes`; + `Displaying ${episodeList.length}/${episodeList.length} episodes`; // Displaying 73/73 episodes - backButton.style.display = "none"; }); - // Reset episode controls and return to the shows listing on FRONT PAGE + // Reset episode controls and return to the shows listing on FRONT PAGE, Level 500 backToShowButton.addEventListener("click", function () { //user back to show button clicked searchInput.value = ""; //clear episode search episodeCount.textContent = ""; //clear episode count makePageForShows(allShows); //display all TV shows again on front page - makeShowSelector(allShows); - if (episodeContainer) { - episodeContainer.style.display = "none"; + makeShowSelector(allShows); //Show selector becomes full again on front page +}); } -}); + +// ==================== LEVEL 300- LOAD EPISODES BY FETCHING API ==================== +// Fetch episodes for the selected show from TVMaze API +async function loadEpisodes(showId) { + +//Clear input when switching between shows and episodes + searchInput.value = ""; + + // Check if we already fetched this show's episodes, Level 400 + if (cachedEpisodes[showId]) { + const episodes = cachedEpisodes[showId]; + currentEpisodes = episodes; // Save the episodes of the selected show for searching and filtering + const episodeCards = makePageForEpisodes(episodes); + makeEpisodeSelector(episodes, episodeCards); + + return episodeCards; + } + + try { //Level-300 / If not fetched, i.e first time loading + const response = await fetch(getEpisodeUrl(showId)); // Request episode data from TVMaze API + const episodes = await response.json(); // Convert JSON format into JavaScript objects + currentEpisodes = episodes; // Stored the fetched episodes of the selected show for searching and filtering + + // Store the episodes so we don't fetch the same URL again + cachedEpisodes[showId] = episodes; + + // fill the episode dropdown, Level-300 + const episodeCards = makePageForEpisodes(episodes); // Display episode cards on the page + makeEpisodeSelector(episodes, episodeCards); // Create episode selector dropdown + + return episodeCards; //Returned created card + } catch (error) { // Error handling; Display error message if fetch fails. Level 300 + episodesMessage.textContent = EPISODE_ERROR; + } } -// ==================== SHOW SELECTOR ==================== -//showSelector implemented +// ==================== Level 400 SHOW SELECTOR ==================== +// Create dropdown containing all available shows function makeShowSelector(shows) { -// Find the existing show container +// Find the existing show container, show container keeps show selector dropdown and show dropdown label let showContainer = document.getElementById("show-container"); -// Create it only if it doesn't exist +// Create it only if show container doesn't exist, Level 400 if (!showContainer) { showContainer = document.createElement("div"); showContainer.id = "show-container"; - searchContainer.appendChild(showContainer); + searchContainer.append(showContainer); //Add the show container in to the search container } -// Clear old dropdown before adding a new one +// Clear old show from dropdown before showing a new show showContainer.innerHTML = ""; - const selectShowList = document.createElement("select"); +//Create new show dropdown for show list, Level 400 + const selectShowList = document.createElement("select"); selectShowList.id = "show-selector"; - const showLabel = document.createElement("label"); + const showLabel = document.createElement("label"); //Create show dropdown label showLabel.textContent = "Select a show: "; showLabel.htmlFor = "show-selector"; - shows.forEach((show) => { + //Add entries to the dropdown, Level 400 + shows.forEach((show) => { //Create option for each show from all shows in dropdown const option = document.createElement("option"); - option.textContent = show.name; - option.value = show.id; + option.textContent = show.name; //Set each show text + option.value = show.id; //Store each show ID - selectShowList.appendChild(option); + selectShowList.append(option); //Add option to the dropdown }); + //What happens when a user clicks one show in dropdown, Level 400 selectShowList.addEventListener("change", async () => { - const showId = Number(selectShowList.value); + const showId = Number(selectShowList.value); //Get the selected show ID - // Remember which show the user currently wants - selectedShowId = showId; - searchInput.value = ""; - episodeCount.textContent = ""; + searchInput.value = ""; //Clear the old search when switching shows + episodeCount.textContent = ""; //Clear the previous episode count - episodesMessage.textContent = "Loading episodes..."; + episodesMessage.textContent = "Loading episodes..."; //Show this message when episodes data is loading for a + //selected show. Level 300 / 400 - // Load episodes using the cache + // Load episodes using the cache, Level 400 await loadEpisodes(showId); - episodesMessage.textContent = ""; + episodesMessage.textContent = ""; // Remove loading message once data arrives. Level 300 /400 }); + // Add the show label and show selector dropdown inside the show container, Level 400 + showContainer.append(showLabel, selectShowList); +} + +// ==================== LEVEL 500 DISPLAY SHOWS ON THE FRONT PAGE ==================== +// Create and display show cards on the front page +function makePageForShows(showList) { + + // Show the "Filtering for" label on the shows listing page, i.e front page + searchLabel.style.display = ""; + + // change the search box to search shows on the front page + searchInput.placeholder = "Search shows..."; + + //Shows the text if no. of shows found after filtering on front page + showCount.textContent = `Found ${showList.length} shows`; + + // User is currently viewing the shows listing on front page + currentView = "shows"; - // Add dropdown to the page - showContainer.appendChild(showLabel); - showContainer.appendChild(selectShowList); + rootElem.innerHTML = ""; // Clear any existing content before displaying show cards + + //Creating show cards for FRONT PAGE + for (const show of showList) { //Creates one card per show + const card = document.createElement("section"); + card.classList.add("show-card"); + + const showName = document.createElement("h2"); + showName.textContent = show.name; + + showName.style.cursor = "pointer"; //Shows hand cursor when mouse is over the show name; tells the user, you can click this + + //What happens when user clicks on a show name + showName.addEventListener("click", async function () { //when the user clicks show name, Load that show's episodes + await loadEpisodes(show.id); + }); + + const image = document.createElement("img"); //6 show information according to requirement 1 + image.src = show.image.medium; + image.alt = `Show image for ${show.name}`; + + const genres = document.createElement("p"); + genres.textContent = `Genres: ${show.genres.join(", ")}`; + + const status = document.createElement("p"); + status.textContent = `Status: ${show.status}`; + + const rating = document.createElement("p"); + rating.textContent = `Rating: ${show.rating.average}`; + + const runtime = document.createElement("p"); + runtime.textContent = `Runtime: ${show.runtime} minutes`; + + const summary = document.createElement("div"); + summary.innerHTML = show.summary; + + +//For Organizing the show information nicely before displaying it on the front page, according to the +// Level 500 given layout + +// Container for rating, genres, status and runtime +const showInfo = document.createElement("div"); +showInfo.classList.add("show-info"); // Apply CSS styling to the show information section + +showInfo.append( // Add show details inside the showInfo container + rating, + genres, + status, + runtime +); + +summary.classList.add("summary"); // Apply CSS styling to the show summary section + +card.append( // Build the complete show card + showName, + image, + summary, + showInfo +); + + rootElem.append(card); //Add the completed card inside the page container (root). Display the finished card on the page + } + + // Hide episode controls + const episodeContainer = document.getElementById("episode-container"); + if (episodeContainer) { + episodeContainer.style.display = "none"; + } + + // Show show selector + const showContainer = document.getElementById("show-container"); + if (showContainer) { + showContainer.style.display = ""; + } + } -// ==================== SEARCH FOR BOTH SHOWS & EPISODES (Globar search listener) ==================== +// ====================Level 200/ 500 SEARCH FOR BOTH SHOWS & EPISODES (Globar search listener) ==================== // Run every time the user types in the search box -searchInput.addEventListener("input", function () { - - const searchTerm = searchInput.value.toLowerCase(); // Get the current search text and convert it to lowercase + searchInput.addEventListener("input", function () { + const searchTerm = searchInput.value; // Get the current search text - if (currentView === "shows") { // If the user is on the shows listing page/ 2nd page - const matchingShows = searchShows(searchTerm, allShows); // Find all shows that match the search text + if (currentView === "shows") { // If the user is on the shows listing page/ front page, Level 500 + const matchingShows = searchShows(searchTerm, allShows); // Find all shows that match the search text, Level 500 makePageForShows(matchingShows); // Redisplay only the matching shows - makeShowSelector(matchingShows); //display only matching shows in dropdown + makeShowSelector(matchingShows); //Display only matching shows in dropdown } else if (currentView === "episodes") { // If the user is viewing episodes const matchingEpisodes = searchEpisodes(searchTerm, currentEpisodes); // Find all episodes that match the search text episodeCount.textContent = - `Displaying ${matchingEpisodes.length}/${currentEpisodes.length} episodes`; + `Displaying ${matchingEpisodes.length}/${currentEpisodes.length} episodes`; //For displaying text + // i.e Displaying 1/33 episodes makePageForEpisodes(matchingEpisodes); // Redisplay only the matching episodes }