//Variale `list` is imported from JSON with query_handler.php. //var lists = JSON.parse(document.getElementById("lists-json").innerText); var list_id = document.getElementById("query-list").innerText; //Get list id from tag created in query-handler.php. var selected_list = null; if(lists.hasOwnProperty(list_id)){ selected_list = lists[list_id]; } else { selected_list = lists["master"]; //If the list id in the query is invalid, go back to the main list. } function gen_list_html(list){ //This function creates HTML from a list's JSON object by iterativel calling the gen_item_html function to convert individual items. This design paradigm facilitates the construction of nested lists or lists with sections. var html = ""; if(!list.hasOwnProperty("type")){ list.type = "default"; } if(!list.hasOwnProperty("subtype")){ list.subtype = "default"; } var list_type = list.type; if(list.hasOwnProperty("sections")){ list_type = "sectioned"; //Lists that have separate sections require special handling involving recursion. } switch(list_type){ case "master": for(id in lists){ html += gen_item_html(id,type="list-id"); } html = ``; break; case "quotes": html += "
"; for(quote of list.list){ html += gen_item_html(quote,type="quote"); } break; case "key-value": for(pair of list.list){ html += gen_item_html(pair,type="kv-pair"); } html = (list.hasOwnProperty("ordered") && list.ordered) ? `
    ${html}
` : ``; //Create an ordered list if list has the property "ordered". break; case "gallery": document.getElementById("lists").style.textAlign="center"; //Center the rows of exhibits on the page. if(!list.hasOwnProperty("subtype")){ list.subtype="default"; } for(exhibit of list.list){ html += gen_item_html(exhibit,type="exhibit",subtype=list.subtype); } break; case "external": //This branch will only run if someone manually enters the list name into the query; clicking an external-type list from the Master List will just open the link in a new tab. setTimeout(function(){location.assign(list["link"])}, 600); //If the list specified in the query has the external property, then redirect to the external link. I use setTimeout in order to allow the rest of the code to run. list.title = `Redirecting to List of ${list.title}...`; list.list = []; //It takes a second to redirect, so put some filler on the page while the reader waits. break; case "sectioned": if(!list.hasOwnProperty("section-level")||list["section-level"]==null){ list["section-level"] = 1; //Section-level defines the heading level of the section. Top level is 1. } if(!list.hasOwnProperty("dropdown")||list.dropdown==null){ list.dropdown = true; //By default, sections ARE in details/summary tags. } if(!list.hasOwnProperty("dropdown-open")||list["dropdown-open"]==null){ list["dropdown-open"] = false; //By default, sections are collapsed. } for(section of list.sections){ var section_html = ""; var level = list["section-level"] + 1; section["section-level"] = level; //Nested secions are of lower levels. if(!section.hasOwnProperty("type")||section.type==undefined){ section.type = list.type; //This branch transfers the list type down from higher levels. By default, the bottom-level lists will inherit the type of the top-level object unless otherwise specified. } if(!section.hasOwnProperty("subtype")||section.subtype==undefined){ section.subtype = list.subtype; //Sections should also inherit subtypes. } if(!section.hasOwnProperty("dropdown")||section.dropdown==undefined){ section.dropdown = list.dropdown; //Inherit dropdown-ness unless otherwise specified. } if(!section.hasOwnProperty("dropdown-open")||section["dropdown-open"]==undefined){ section["dropdown-open"] = list["dropdown-open"]; //Inherit whether the dropdown should be collapsed or open. } var description = (section.hasOwnProperty("description")) ? `

${section.description}

` : ""; //Sections can have their own descriptions. var title = `${section.title}`; //Wrap the section title in a header tag. if(section.hasOwnProperty("dropdown") && section.dropdown){ //If the section is marked with the "dropdown" attribute, then nest the section's data in a details/summary tag. var open = (section["dropdown-open"]) ? "open":""; section_html = `
${title}${description}${gen_list_html(section)}
`; } else { section_html = `${title}${description}${gen_list_html(section)}`; } //Sectioned html += `
${section_html}
`; //Assigning ids allows for the possibility of fragment linking. } break; default: for(item of list.list){ html += gen_item_html(item); } html = (list.hasOwnProperty("ordered") && list.ordered) ? `
    ${html}
` : ``; //Create an ordered list if list has the property "ordered". } return html; } function gen_item_html(item,type="default",subtype=null){ var item_html = ""; switch(type){ case "list-id": if(lists[item].hasOwnProperty("hidden") && lists[item].hidden){ return ""; //Lists marked with the "hidden" attribute are in-development and should not be displayed on the Master List. } if(!lists[item].hasOwnProperty("title")){ lists[item].title = item; //If a title is not set for the list, then just use its id. } var tooltip = ""; if(lists[item].hasOwnProperty("description")){ tooltip=lists[item]["description"]; //When hovering over a list in the directory, display its description as a tooltip. } var link = `href='index.php?page=lists&list=${item}' target='_self'`; //By default, lists have internal links that change the query value to that list's title. Internal links should open in the same tab. if(lists[id].hasOwnProperty("type") && lists[item].type=="external"){ link = `href='${lists[item]["link"]}' target='_blank'`; //For external lists, use their link instead of an internal link. External links should open in a new tab. } item_html = `
  • ${lists[item].title}
  • `; //The Master List contains a link to every other list in the JSON file. break; case "quote": //Format quotes like they are formatted on Goodreads. if(!item.hasOwnProperty("quote")){ //If the quote is blank, then things are going to get real weird. Most likely, this will be caused by a typo, so this branch is a safeguard against any resulting errors. return ""; } item_html = "

    "; if(item.hasOwnProperty("title")){ item_html += `${item.title}
    `; //Add a title if the quote has one (e.g. "The Litany Against Fear"). } item_html += `“${item.quote}”
    `; //Add the text of the quote. if(item.hasOwnProperty("card") && !item.hasOwnProperty("quotee")){ item.quotee=""; //If a flavor text doesn't have a quotee, don't write "Unknown". } item_html += ` — ${item.hasOwnProperty("quotee") ? item.quotee : "Unknown"}`; //Add the quotee's name, or "Unknown" if one is not specified. if(item.hasOwnProperty("source")){ if(item.hasOwnProperty("quotee") && item.quotee!=""){ item_html += ", "; //Unless there is no quotee, separate the quotee from the source with ", " } item_html += item.source; //Add the source if the quote has one. } if(item.hasOwnProperty("card")){ if(item.quotee!=""||item.hasOwnProperty("source")){ item_html += `, `; //Separate quotee or source from card title with ", " } item_html += `${item.card} (Magic: the Gathering)`; } item_html += "


    "; //Delimit the quotes with a horizontal rule. break; case "kv-pair": if(!(item.hasOwnProperty("k")&&item.hasOwnProperty("v"))||Object.keys(item).length==1){ var key = Object.keys(item)[0]; item["k"] = key; item["v"] = item[key]; //If the item is an object containing a single key-value pair, then use that pair as the k and v for displaying. } if(item.hasOwnProperty("link")){ item_html = `${item["k"]}`; } else { item_html = `${item["k"]}`; } item_html += ` — ${item["v"]}`; if(item.hasOwnProperty("list") && Array.isArray(item.list)){ //Sublist time, baby! var temp = { "title":item_html, "list":item.list, "ordered": (item.hasOwnProperty("ordered")) ? item.ordered:undefined, "dropdown": (item.hasOwnProperty("dropdown")) ? item.dropdown:undefined, "dropdown-open": (item.hasOwnProperty("dropdown-open")) ? item["dropdown-open"]:undefined }; //If a key-value pair has a list key with an array attribute, then reformat it as a sublist whose title is the kv pair and whose list is the array. item_html = gen_item_html(temp,type="sublist") } else { item_html = `
  • ${item_html}
  • `; } break; case "exhibit": var tooltip, alt, text, img_src, image; tooltip = text = img_src = image = ""; classes = {"div":[],"img":[],"a":[]} switch(subtype){ case "album": tooltip = `${item.title} (${item.year}) - ${item.artist}`; alt = tooltip; img_src = item.cover; text = `${item.title}
    ${item.artist}
    (${item.year})`; break; case "movie": tooltip = `${item.title} (${item.year})`; alt = tooltip; img_src = item.poster; text = `${item.title}
    (${item.year})`; break; case "mtg-card": var gatherer_link = "https://gatherer.wizards.com/pages/card/Details.aspx?multiverseid="; var gatherer_image = "https://gatherer.wizards.com/Handlers/Image.ashx?multiverseid="; if(item.hasOwnProperty("multiverseid")&&!item.hasOwnProperty("link")){ if(Array.isArray(item.multiverseid)){ //If the multiverseid is an array, treat it as a transform card and link to the Gatherer page for the front half. item.link = gatherer_link+item.multiverseid[0]; } else { item.link = gatherer_link+item.multiverseid; } //If there's no alternate link specified, then use the multiverseid to generate the Gatherer link. } if(item.hasOwnProperty("multiverseid")&&!item.hasOwnProperty("image")){ if(Array.isArray(item.multiverseid)){ img_src = []; for(id of item.multiverseid){ img_src.push(gatherer_image+id+"&type=card") } //If there are multiple (two) multiverseids, make a link src for each of them for convenience. } else { img_src = gatherer_image+item.multiverseid+"&type=card"; } //If there's no alternate link specified, then use the multiverseid to generate the Gatherer link for the image. } else { if(Array.isArray(item.image)){ img_src=[]; for(src of item.image){ img_src.push(src); } //If multiple (two) image sources are specified, create an array of both. } else { img_src = item.image; } } if(item.hasOwnProperty("name")){ alt = tooltip = item.name; } if(Array.isArray(img_src)){ image = `${tooltip}${tooltip}` //Two srcs are to be treated as those of a transform card, which switches on hover. classes.div.push("card-transform"); } break; default: } if(image==""||!image){ image = image = `${tooltip}`; //If the image variable is not already defined, then generate it. } //Gallery items must have an image. if(item.hasOwnProperty("link")){ image = `${image}`; //If there's a link associated with the exhibit, put it on the image. } item_html = (text=="") ? image : `${image}
    ${text}`; //If there's no text, then there's no need for a line break. item_html = `` break; case "sublist": //Sublists can be any type (most likely default or key-value), so the best way to deal with them is recursion. if(item.hasOwnProperty("dropdown") && item.dropdown){ var open = (item.hasOwnProperty("dropdown-open")&&item["dropdown-open"])?"open":""; item_html = `
  • ${item.title}${gen_list_html(item)}
  • `; } else { item_html = `
  • ${item.title}${gen_list_html(item)}
  • `; } break; default: if(["string","number"].includes(typeof item)){ item_html = `
  • ${item}
  • `; //If the element is a simple string or number, then we don't need to do any special formatting. } else if (typeof item === "object"){ var keys = Object.keys(item); if(keys.length == 1){ if (Array.isArray(item[keys[0]])){ //An item that is a dictionary only containing a list is probably a sublist. Format it as such, and pass it back through this switch statement. var temp = { "title":keys[0], "type":"default", "list":item[keys[0]] }; item_html = gen_item_html(temp,"sublist"); } else { item_html = gen_item_html(item,"kv-pair"); //A item that is dictionary with one key, whose value is not a list, is to be treated as a term-explanation type key-value pair. } } else { item_html = gen_item_html(item,"sublist"); //At this point, if there is no other specification, an item that's an object is probably a sublist. } } } return item_html; } function str2html(md){ //This function replaces escapes commands and Markdown with their HTML equivalents. html = md.replaceAll("\n","
    "); return html; } document.getElementById("list-title").innerHTML = selected_list.title; if(selected_list.hasOwnProperty("description")){ document.getElementById("list-description").innerHTML = selected_list.description; } else { document.getElementById("list-description").style.display = "none"; //If the list has no description, hide the element to remove the awkward space from the padding. } //Generate the article header from the list's title and description. document.getElementById("list-container").innerHTML += str2html(gen_list_html(selected_list)); //Call the gen_list_html function to convert the list from a JSON object to sophisticated HTML. if(list_id != "master"){ document.getElementById("lists").innerHTML += "

    Return to Master List ↩

    "; //Add a return link to the bottom of the article. }