/* STEP 1: Given a movie object, copy its title into a new field called
* textContent and return the movie.
*
* @param {object} movie A movie object from the JSON data set
*/
function setTitleContent(movie) {
movie.textContent = movie.title;
return movie;
}
/* STEP 2: Given a movie object, append the remaining fields into the
* textContent field. Each function should append only the relevant field.
* Chaining them together would create a textContent string such as the
* following:
* Mad Max: Fury Road (2015) - 120 minutes [8.1]
* Make sure to include the correct spacing and punctuation marks.
*
* @param {object} movie A movie object from the JSON data set with the
* title already copied into the movie.textContent field.
*/
function appendYear(movie) {
movie.textContent = movie.textContent.concat(` (${movie.year})`);
return movie;
}
function appendRuntime(movie) {
movie.textContent = movie.textContent.concat(` - ${movie.runtime} minutes`);
return movie;
}
function appendRating(movie) {
movie.textContent = movie.textContent.concat(` [${movie.rating}]`);
return movie;
}
/* STEP 3: Return a closure (arrow function) that takes a movie argument
* and compares its .rating field with this function's rating argument.
* Return true if the movie's rating satisfies this minimum criterion.
* Note that you need to call parseFloat on the movie.rating field because
* JSON data defaults to a string type.
*
* @param {number} rating The minimum movie rating that will cause the
* closure to return true for a movie.
*/
function setMinRating(rating) {
// If the minimum rating value passed in the query string is NaN,
// the closure should always evaluate to false.
// Whether or not this is desireable is up to the designer
const minRating = Number.parseFloat(rating);
return (movie) => Number.parseFloat(movie.rating) >= minRating;
}
/* STEP 3: Complete the following functions just as you did setMinRating.
* You'll need to use parseInt instead of parseFloat, because these are
* all integer values.
*/
function setMinYear(year) {
const minYear = Number.parseFloat(year);
return (movie) => Number.parseFloat(movie.year) >= minYear;
}
function setMaxYear(year) {
const maxYear = Number.parseFloat(year);
return (movie) => Number.parseFloat(movie.year) <= maxYear;
}
function setMinTime(mins) {
const minMins = Number.parseFloat(mins);
return (movie) => Number.parseFloat(movie.runtime) >= minMins;
}
function setMaxTime(mins) {
const maxMins = Number.parseFloat(mins);
return (movie) => Number.parseFloat(movie.runtime) <= maxMins;
}
/* buildItem - Helper function that creates a DOM
element, adds the
* list-group-item class, and inserts the movie's text content. Do not
* modify.
*
* @param {object} movie The movie object to create as a list item
* @param {object} list The to add the item to
*/
function buildItem(movie, list) {
let item = document.createElement("li");
item.classList.add("list-group-item");
item.textContent = movie.textContent;
list.append(item);
}
(async function () {
// STEP 1: Start your anonymous initialization function here. Be sure to
// end the file with the needed syntax to call the function.
// FIRST CODE IN THE INITIALIZATION FUNCTION
// Get the object from the HTML (do not modify)
let ul = document.querySelector("#target");
// Set default values (do not modify)
let minYear = 0;
let maxYear = 3000;
let minTime = 0;
let maxTime = 500;
let minRating = 0.0;
// Get the query string and split it into an array of distinct queries.
// (do not modify)
// For example, given index.html?minyear=1990&minrating=9.0, the query
// variable is the array [ 'minyear=1990', 'minrating=9.0' ]
let query = window.location.search.substring(1); //.split('&');
if (query !== "") {
query = query.split("&");
} else {
query = undefined;
}
// KEEP THIS HARD-CODED TO AVOID CORS ISSUES
const hostname = "https://w3.cs.jmu.edu/kirkpams/343/labs/lab8";
const url = `${hostname}/data.json`;
// STEP 1: Fetch the JSON data from the URL. If the query string is empty
// (i.e., query is undefined), grab just the first 10 data entries. Given
// this array, use .map to call setTitleContent on each entry and .forEach
// to call buildItem for each. You may NOT use a traditional loop (e.g.,
// for-of, while, etc.) for this lab.
//
// STEP 2: Extend the movie's textContent by using .map with the additional
// functions to append the year, runtime, and rating values.
//
// STEP 3: If the query variable is defined, then the URL contains a query
// string of criteria. Use the criteria specified in the query string to
// filter the data. Each criterion would be of the form minyear=2000, with
// the criteria being minyear, maxyear, mintime, maxtime, or minrating. Use
// the values here to change the default minYear, etc., listed above. Note
// that you will need to use parseInt or parseFloat to convert the query
// string value into a number.
//
// Once you've processed all of the filter criteria, run the full JSON data
// set through these using .filter, then use .map and .forEach as before to
// generate the data.
// END OF THE INITIALIZATION FUNCTION
// ! Important caveat of this implementation, there is no specification for
// ! how to handle situations where the same query param is passed multiple
// ! times, or when query params contradict each other.
// ! This implementation sequentially applies filters for every query param
// ! regardless of the other conditions in list of query params.
let movieData = await fetch(url)
.then((response) => response.json())
.catch(() => null);
if (!movieData) return;
// In order to constrain all current and potential future filter functions
// to a singular interface all filter functions will accept a single string
// parameter (the query value).
// Any parsing of this string will be handled by the individual filter functions.
const filterMap = {
minyear: setMinYear,
maxyear: setMaxYear,
mintime: setMinTime,
maxtime: setMaxTime,
minrating: setMinRating,
};
query?.forEach((queryString) => {
const [name, value] = queryString.split("=");
if (name in filterMap) {
movieData = movieData.filter(filterMap[name](value));
}
});
if (!query) {
movieData = movieData.slice(0, 10);
}
movieData
.map(setTitleContent)
.map(appendYear)
.map(appendRuntime)
.map(appendRating)
.forEach((movie) => buildItem(movie, ul));
})();