Source: expandedShowCard.js

/**
 * File Header: expandedShowCard.js
 * 
 * Creates the class for the expanded show card
 */

/**
 * Class Header: expandedShowCard
 * 
 * A expanded show card that previews information about show on the user's watch list
 * Appears when you click on the small show card
 * Displays the show title, an image, rating, comments, and the progress the user made in the show,
 * along with the current season the user is on
 * 
 * data() - sets the show card data with the information provided from the data object
 * update() - updates page to show selected season
 * CreateActionListeners() - Generates listeners to listen for user switching seasons and checking/unchecking episodes
 * generatedInnerHTML() - Generates string representing innerHTML of expandedShowCard element
 * 
 */

//importing functions from tools.js

import {getShowsFromStorage} from './tools.js';
import {saveShowsToStorage} from './tools.js';

//this variable is used to get the current card's id
var currentInd;

export default class expandedShowCard extends HTMLElement {
    /**
     * Construct a expandedShowCard element
     * @constructor
     */
    constructor() {
        super();

        let shadow = this.attachShadow({ mode: "open" });
        let article = document.createElement('article');
        let style = document.createElement('style');

        style.innerHTML = `

        #outerbox {
            background-color: rgb(1, 107, 112);
            padding: 0em 2em 1em 2em;
            margin: auto;
            resize: none;
            width: 96.65vw;
            height: 98.3vh;
        }

        #homebutton {
            border: 0;
            background-color: rgb(1, 107, 112);
            float: right;
            margin-right: 0.5em;
        }

        .toptvshowheader {
            background-color: rgb(1, 107, 112);
            display: flex;
            justify-content: space-between;
            width: 100%;
            font-family: Arial;
            color: white;
            height: 3em;
        }

        .half{
            display: flex;
	        align-items: center;
	        justify-content: space-between;
        }

        #tvshowheader {
            color: white;
            margin-left: 1em;
        }

        #innerbox {
            background-color: rgb(17, 151, 157);
            padding: 0.7em 0.7em 0.7em 0.7em;
            
        }

        #showandinfo {
            display: flex;
        }

        #info {
            margin-left: 0.7em;
            width: 100%;
        }
        
        #titleandbuttons {
            display: flex;
            align-items: center;
            justify-content: space-between;
            height: 2.5em;
            width: 100%;
        }

        #titleandbuttons p {
            display: inline;
            height: 2.5em;
            line-height: 2.5em; 
            font-family: 'Oswald', sans-serif;
            color: white;
            font-size: 1.75em;
            margin: 0;
        }

        #titleandbuttons button {
            display: inline;
            margin-left: 0.6em;
            border: 0;
            background-color: rgb(17, 151, 157);
        }

        .rating {
            height: 2.5em;
            line-height: 2.5em; 
            font-family: 'Oswald', sans-serif;
            color: white;
            font-size: 1.75em;
        }

        .comments {
            height: 2.4em;
            line-height: 1.6em; 
            font-family: 'Oswald', sans-serif;
            color: white;
            font-size: 1.75em;
        }

        #progressheader {
            font-family: 'Oswald', sans-serif;
            margin-top: 0.3em;
            margin-bottom: 0em;
            color: white;
            font-weight: normal;
            font-size: 1.75em;
        }

        #seasonbuttons {
            padding: 0;
            height: 100%;
        }

        .currentSeasonButton{
            width: 13em;
            height: 3em;
            background-color: rgb(5, 94, 98);
            color: white;
            border: none;
            padding: 0;
            outline: none;
            vertical-align: text-bottom;
        }

        .seasonButton{
            width: 6.5em;
            height: 2.5em;
            background-color: rgb(1, 107, 112);
            color: white;
            border: none;
            padding: 0;
            outline: none;
            vertical-align: text-bottom;
        }

        #episodesDiv{
            background-color: rgb(219, 253, 255);
            padding-top: 1em;
            padding-right: 1em;
            padding-bottom: 3em;
            padding-left: 1em;
        }

        #commentArea {
            width: 100%;
            height: 20%;
            resize: vertical;
        }

        .showBoxChecked{
            background-color: rgb(12, 167, 137);
            width: 6.5em;
            height: 2.5em;
            border-style: solid;
            padding: 0;
        }

        .showBoxUnchecked{
            background-color: white;
            width: 6.5em;
            height: 2.5em;
            border-style: solid;
            padding: 0;
        }

        #checkAllButton{
            border: 0;
            background-color: rgb(219, 253, 255);
        }

        #checkAllDiv{
            height: 100%;
            display: flex;
            align-items: baseline;
            margin-top: 0.7em;
            width: 6.5em;
        }

        #checkAllDiv p{
            font-family: Arial;
            color: black;
            height: 100%;
            font-weight: bold;
            margin: 0;
        }
        `;

        shadow.append(article);
        shadow.append(style);
    }

    /**
     * Called when the .data property is set on this element
     * 
     * For example:
     * let expandedShowCard = document.createElement('small-show-card'); // Calls constructor()
     * expandedShowCard.data = { foo: 'bar' } // Calls set data({ foo: 'bar' })
     * 
     * expandedShowCard and smallShowCard get data from same data object, smallShowCard just uses part of the data
     * 
     * @function
     * @param {Object} data - The data to pass into the <expanded-show-card>, must be of the following format:
     *                        {
     *                            "episodeArray": 2D array of booleans representing episodes per season and whether they have been watched
     *                            "id" : num representing place in local storage
     *                            "imgSrc" : "string"
     *                            "imgAlt" : "string"
     *                            "movie" : boolean representing whether or not item is a move
     *                            "rating" : number
     *                            "review" : "string"
     *                            "showTitle" : "string"
     *                        }
     */
    set data(data) {
        // if no data, return
        if (!data) return;
        this.json = data;
        const shadowDom = this.shadowRoot;
        update(data,  1, shadowDom);

    }
    get data(){
        return this.json
    }


    get data(){
        return this.json;
    }

}

/**
 * This function gets the article associated with the shadow and then calls innerHTML on it
 * The generation will create the page with season buttons and checkboxes representing true or false on if the
 * episode is watched or not
 * 
 * Then, createActionListeners is called to generate listeners for the buttons and checkboxes created
 * 
 * This function is called everytime a season button is selected
 * 
 * @function
 * @param {Object} data - data passed in to expandedShowCard
 * @param {number} seasonNumber - the current selected season 
 * @param {*} shadowDom - shadowDOM associated with current object
 */

export function update(data, seasonNumber, shadowDom){
    let article = shadowDom.querySelector('article');
    article.innerHTML = generatedInnerHTML(data, seasonNumber);
    CreateActionListeners(data, seasonNumber, shadowDom);
    currentInd = data.id;
}

/**
 * This function takes in the current selected season (chosen by clicking a button) and then generates event listeners to 
 * switch seasons, if a season is clicked it will take update the current html to reflect the checkbox array for that season
 * 
 * For the checkboxes which are displayed depending on the season selected, event listeners are generated so that data.episodeArray
 * booleans are updated depending on if the checkboxes are checked or unchecked
 * 
 * @function
 * @param {Object} data - data passed in to expandedShowCard
 * @param {number} seasonNumber - current selected season 
 * @param {*} shadowDom - shadowDOM associated with current object 
 * 
 */
function CreateActionListeners(data, seasonNumber, shadowDom){
    for(let i = 0; i < data.episodeArray.length; i++){
        let currNum = i + 1;
        let seasonObject = shadowDom.getElementById(`season_` + (currNum) + `_button`);
        seasonObject.addEventListener('click', () => {
            update(data, currNum, shadowDom);
        });
    }

    // get local storge
    let cards = getShowsFromStorage();

    for(let i = 0; i < data.episodeArray[seasonNumber - 1].length; i++){
        let checkboxObject = shadowDom.getElementById(`season_${seasonNumber}_episode_${i+1}_checkbox`);
        checkboxObject.addEventListener('click', () => {
            data.episodeArray[seasonNumber - 1][i] =  !data.episodeArray[seasonNumber - 1][i];
            
            //toggle the display class
            if(data.episodeArray[seasonNumber - 1][i]){ //checked
                checkboxObject.classList.remove("showBoxUnchecked");
                checkboxObject.classList.add("showBoxChecked");
            }else{ //unchecked
                checkboxObject.classList.remove("showBoxChecked");
                checkboxObject.classList.add("showBoxUnchecked");
            }
            cards[data.id] = data;
            // update local storge
            saveShowsToStorage(cards);
        })
    };

    let editbutton = shadowDom.getElementById("editbutton");
    editbutton.addEventListener('click', () => {
        window.location.href = `./add-content.html?ind=${data['id']}`;
    });

    /**
     * Sets action listener for the trash button, which lets you delete only
     * the current entry
     * 
     * When you confirm the deletion, you are immediately redirected to the home page
     * The entry is deleted from localStorage, and all ids for the remaining cards are updated
     * to be continuous
     * Then they are saved to localStorage again
     */
    let trashButton = shadowDom.getElementById("trashbutton");
    trashButton.addEventListener("click", function() {
        if(true/*confirm("Are you sure you want to delete this entry?")*/) {
            let ind = currentInd;
            let cards = getShowsFromStorage();

            //edge case if there's only one show right now, deletes localStorage entirely
            if(cards.length == 1) {
                localStorage.removeItem('shows');
                window.location.href = '../../index.html';
                return;
            }

            //if there's more than one entry, removes current entry
            cards.splice(ind, 1);

            //after you splice the cards, you must update the ids for each
            for(let i = 0; i < cards.length; i++) {
                cards[i].id = i;
            }

            //saves the new array (without the deleted entry) to localStorage
            saveShowsToStorage(cards);

            //redirects you to the home page
            window.location.href = '../../index.html';
        }
        else {
            return;
        }
    ;});
    
    /**
     * Sets action listener for the check all button, which sets every episode in the current
     * season as "watched." Saves the resulting data to localStorage.
     */
    let checkAllButton = shadowDom.getElementById("checkAllButton");
    checkAllButton.addEventListener("click", function() {
        let episodesBox = shadowDom.getElementById('episodesDiv');
        let episodesChildren = episodesBox.childNodes;
        
        //Loops through all children of episodeDiv and sets them as Checked.
        for (let i = 0; i < episodesChildren.length; i++) {
            /**
             * This if statement guarantees that every element that is accessed is a button
             * AND is a checkbox.
             */
            if(episodesChildren[i].nodeName == "BUTTON" && 
                episodesChildren[i].classList.contains("showBoxUnchecked")) {
                episodesChildren[i].classList.remove("showBoxUnchecked");
                episodesChildren[i].classList.add("showBoxChecked");
                //console.log("Episode " + i + " was checked in HTML.")
            }
        }

        //Sets all entries in the array for the current season to true
        for(let j = 0; j < data.episodeArray[seasonNumber - 1].length; j++) {
            data.episodeArray[seasonNumber - 1][j] = true;
            //console.log("Episode " + j + " was checked in data.")
        }
        cards[data.id] = data;
        saveShowsToStorage(cards);
        //console.log(data);
    })
}

/**
 * Generates string representing innerHTML of expandedShowCard element
 * 
 * @function
 * @param {Object} data - data passed in to expandedShowCard
 * @param {number} seasonNumber - the current selected season 
 * @returns {string} string representing the innerHTML of the expandedShowCard
 */

export function generatedInnerHTML(data, seasonNumber){
    if(data.imgSrc == "./assets/img/icons/bingetracker_logo.png"){
        data.imgSrc = "../img/icons/bingetracker_logo.png";
    }
  
    let innerHTML =
                `<div id="outerbox">
                    <div class="toptvshowheader"> 
                        <div class="half"><h4 id="tvshowheader">TV Show</h4></div>
                        <div class="half"><a id="testhomebutton" href="../../index.html"><button id="homebutton">
                            <img height="35em" src="../img/icons/home.png"></img>
                        </button></a></div>
                    </div>
                    <div id="innerbox">
                        <div id="showandinfo">
                            <img id="showimage" src=${data.imgSrc} alt=${data.imgAlt} height="240 em">
                            <div id="info">
                                <div id="titleandbuttons"> 
                                    <p class="title">Name: ${data.showTitle}</p>
                                    <div id="buttons">
                                    <button id="editbutton">
                                        <img height="27em" src="../img/icons/edit.png"></img>
                                    </button>
                                        <button id="trashbutton">
                                            <img height="27em" src="../img/icons/trash.png">
                                            </img>
                                        </button>
                                    </div>
                                </div>
                                <div class="rating">Rating: ${data.rating}/5</div>
                                <div class="comments">Comments:</div>
                                <textarea disabled id="commentArea"> ${data.review}</textarea>
                            </div>
                        </div>
                        <h2 id="progressheader">Progress: </h2>` +
                        generateSeasonsHTML(data.episodeArray, seasonNumber) + 
                        generateEpisodesForSeason(data.episodeArray[seasonNumber-1] ,seasonNumber) +
                        `</div>`
                return innerHTML;

}
//<div class="comments">Comments: ${data.review}</div>


/**
 * Generates a HTML string for the episodes section
 * 
 * @function
 * @param {Array} episodes - an array of booleans representing which episodes are watched in a season
 * @param {number} seasonNumber - the current selected season
 * @returns a HTML string used to generate the episodes section
 */
 export function generateEpisodesForSeason(episodes, seasonNumber){
    let s = `<div id="episodesDiv">`;
    for(let i = 0; i < episodes.length; i++){
        let checked;
        episodes[i] ? checked= "showBoxChecked" : checked = "showBoxUnchecked";
        s += `<button id="season_${seasonNumber}_episode_${i+1}_checkbox" class="${checked}"> ${i+1} </button>`;
    }
    s += `
    <div id="checkAllDiv">
        <p>Check All</p>
        <button id="checkAllButton">
            <img height="16em" src="../img/icons/checkboxicon.png">
            </img>
        </button>
    </div>
    </div>`;
    return s;
}


// Example of an "episode array"
// Season 1: we watched epsiodes 1, 2, 4. Four total episodes.
// Season 2: we watched episodes 1, 3. Three total episodes. 
// episodes = [[true, true, false, true],[true, false, true]]

/**
 * Returns a string of HTML that generates multiple "progress bars" for each season
 * You can click through the top labels, each labeled "Season X", to switch between progress bars
 * Each bit of progress is marked by an image.
 * 
 * @function
 * @param {Array} episodes - an array of booleans representing the episodes in each season and whether or not they have been watched
 * @param {number} seasonNumber - the current selected season
 * @return a string representing the HTML for progress bars for each season
 */
export function generateSeasonsHTML(episodes, seasonNumber){
    let s = `<div id="seasonbuttons">`;
    for(let i = 0; i < episodes.length; i++){
        s += `<button id="season_` + (i+1) + `_button" class="${seasonNumber == (i+1) ? "currentSeasonButton" : "seasonButton"}">Season ` + (i+1) + `</button>`;
    }
    s += `</div>`;
    return s;
}


customElements.define("expanded-show-card", expandedShowCard);