class NintendoWishlistCard extends HTMLElement { set hass(hass) { if (!this.content) { const card = document.createElement("ha-card"); card.header = this.config.title; this.content = document.createElement("div"); this.content.style.padding = "5px 10px"; card.appendChild(this.content); this.appendChild(card); } // The Great Wall of Config & Defaults™ const style = document.createElement("style"); const entity = this.config.entity; if (!hass.states[entity]) { console.log("entity doesn't exist"); return; } const json = hass.states[entity].attributes.on_sale; if (!json || !json.length) { return; } const sortBy = this.config.sort_by const sortDir = this.config.sort_dir ?? 'desc' if (sortBy) { json.sort((a, b) => sortDir === 'desc' ? b[sortBy] - a[sortBy] : a[sortBy] - b[sortBy]) } const view = this.config.image_style || "boxart"; const title_size = "large"; const line1_size = "medium"; const line2_size = "small"; const line3_size = "small"; const line4_size = "small"; const tSize = (size) => size == "large" ? "18" : size == "medium" ? "14" : "12"; const size = [ tSize(title_size), tSize(line1_size), tSize(line2_size), tSize(line3_size), tSize(line4_size), ]; const defaultClr = (boxart, backgroundart) => view == "boxart" ? boxart : backgroundart; const title_color = this.config.title_color || defaultClr("var(--primary-text-color)", "#fff"); const line1_color = this.config.line1_color || this.config.line_color || defaultClr("var(--primary-text-color)", "#fff"); const line2_color = this.config.line2_color || this.config.line_color || defaultClr("var(--primary-text-color)", "#fff"); const line3_color = this.config.line3_color || this.config.line_color || defaultClr("var(--primary-text-color)", "#fff"); const line4_color = this.config.line4_color || this.config.line_color || defaultClr("var(--primary-text-color)", "#fff"); const accent = this.config.accent_color || defaultClr("var(--primary-color)", "#000"); const border = this.config.border_color || defaultClr("#fff", "#000"); const shadows = (conf) => this.config.all_shadows == undefined ? conf == undefined ? true : conf : this.config.all_shadows; const boxshdw = shadows(this.config.box_shadows) ? view == "boxart" ? "5px 5px 10px" : "3px 2px 25px" : ""; const svgshdw = shadows(this.config.box_shadows) ? "url(#grad1)" : accent; const txtshdw = shadows(this.config.text_shadows) ? "1px 1px 3px" : ""; const max = Math.min(json.length, this.config.max || 5); window.cardSize = max; if (view == "boxart") { style.textContent = ` .switchwishlist_${view} { width:100%; margin-left: auto; margin-right: auto; margin-bottom: 10px; position: relative; display: inline-block; overflow: hidden; } .switchwishlist_${view} img { width:100%; visibility:hidden; } .switchwishlist_svg_${view} { width:55%; margin-top:5%; margin-left:0; vertical-align:top; overflow:visible; z-index:1; } .switchwishlist_container_${view} { position:relative; outline: 5px solid #fff; width:30%; outline:5px solid ${border}; box-shadow:${boxshdw} rgba(0,0,0,.8); float:left; background-position: center; background-repeat: no-repeat; background-size: cover; margin:5px 0 15px 5px; } .switchwishlist_line0_${view} { font-weight:600; font-size:${size[0]}px; text-shadow:${txtshdw} rgba(0,0,0,0.9); fill:${title_color}; } .switchwishlist_line1_${view} { font-size:${size[1]}px; text-shadow:${txtshdw} rgba(0,0,0,0.9); fill:${line1_color}; } .switchwishlist_line2_${view} { font-size:${size[2]}px; text-shadow:${txtshdw} rgba(0,0,0,0.9); fill:${line2_color}; } .switchwishlist_line3_${view} { font-size:${size[3]}px; text-shadow:${txtshdw} rgba(0,0,0,0.9); fill:${line3_color}; } .switchwishlist_line4_${view} { font-size:${size[4]}px; text-shadow:${txtshdw} rgba(0,0,0,0.9); fill:${line4_color}; } `; } else { style.textContent = ` .switchwishlist_${view} { width:100%; overflow:hidden; margin-left: auto; margin-right: auto; margin-bottom: 10px; background-repeat:no-repeat; background-size:auto 100%; box-shadow:${boxshdw} rgba(0,0,0,.8); position:relative; } .switchwishlist_svg_${view} { overflow:visible; width:55%; margin-top:1%; margin-left:2.5%; alignment-baseline:text-after-edge; } .switchwishlist_fan_${view} { width:100%; background:linear-gradient(to right, ${accent} 48%, transparent 70%,${accent} 100%); margin:auto; box-shadow:inset 0 0 0 3px ${border}; } .switchwishlist_line0_${view} { font-weight:600; font-size:${size[0]}px; text-shadow:${txtshdw} rgba(0,0,0,0.9); fill:${title_color}; } .switchwishlist_line1_${view} { font-size:${size[1]}px; text-shadow:${txtshdw} rgba(0,0,0,0.9); fill:${line1_color}; } .switchwishlist_line2_${view} { font-size:${size[2]}px; text-shadow:${txtshdw} rgba(0,0,0,0.9); fill:${line2_color}; } .switchwishlist_line3_${view} { font-size:${size[3]}px; text-shadow:${txtshdw} rgba(0,0,0,0.9); fill:${line3_color}; } .switchwishlist_line4_${view} { font-size:${size[4]}px; text-shadow:${txtshdw} rgba(0,0,0,0.9); fill:${line3_color}; } `; } this.content.innerHTML = ""; // Truncate text... function truncate(text, chars) { // When to truncate depending on size chars = chars == "large" ? 23 : chars == "medium" ? 28 : 35; // Remove parentheses & contents: "Shameless (US)" becomes "Shameless". text = text.replace(/ *\([^)]*\) */g, " "); // Truncate only at whole word w/ no punctuation or space before ellipsis. if (text.length > chars) { for (let i = chars; i > 0; i--) { if ( text.charAt(i).match(/( |:|-|;|"|'|,)/) && text.charAt(i - 1).match(/[a-zA-Z0-9_]/) ) { var truncated = `${text.substring(0, i)}...`; return truncated; } } } else { return text; } } for (let count = 0; count < max; count++) { const item = (key) => json[count][key]; let image = item("box_art_url"); // Shifting images for backgroundart view since we use boxart as fallback image. let shiftimg = item("backgroundart") ? "background-position:100% 0;" : "background-size: 54% auto;background-position:100% 35%;"; // First item in card needs no top margin. if (count == "0") var top = "0px"; else view == "boxart" ? "20px" : "10px"; let line = ["title", "sale_price"]; let char = [title_size, line1_size]; // Keyword map for replacement, return null if empty so we can hide empty sections let keywords = /title|sale_price/g; let keys = { title: item("title") || null, sale_price: item("sale_price") || null, }; let title = item("title"); // Replace keywords in lines for (let i = 0; i < line.length; i++) { line[i] = line[i].replace(" - ", "-"); // Split at '-' so we can ignore entire contents if keyword returns null let text = line[i].replace(keywords, (val) => keys[val]).split("-"); //let text = item('title').split('-'); let filtered = []; // Rebuild lines, ignoring null for (let t = 0; t < text.length; t++) { if (text[t].match(null)) continue; else filtered.push(text[t]); } // Replacing twice to get keywords in component generated strings text = filtered.join(" - ").replace(keywords, (val) => keys[val]); // Shifting header text around depending on view & size let svgshift, y; if (i == 0) size[i].match(/18/) ? (y = "-5") : size[i].match(/14/) ? (y = "-2") : (y = "0"); if (view == "backgroundart") svgshift = i == 0 ? `x="0" dy="1em" ` : `x="0" dy="1.3em" `; else svgshift = i == 0 ? `x="15" y="${y}" dy="1.3em" ` : `x="15" dy="1.3em" `; // Build lines HTML or empty line line[i] = line[i].match("empty") ? `.` : `${truncate( text, char[i] )}`; } if (view == "boxart") { this.content.innerHTML += `
${truncate( title, "large" )} . ${item( "normal_price" )} ${item("sale_price")} (${item("percent_off")}% off)
`; } else { this.content.innerHTML += `
${line[0]} . ${item( "normal_price" )} ${item("sale_price")} (${item( "percent_off" )}% off)
`; } this.appendChild(style); } } setConfig(config) { if (!config.entity) throw new Error("Define entity."); this.config = config; } getCardSize() { let view = this.config.image_style || "boxart"; return view == "boxart" ? window.cardSize * 5 : window.cardSize * 3; } configChanged(newConfig) { const event = new Event("config-changed", { bubbles: true, composed: true, }); event.detail = { config: newConfig }; this.dispatchEvent(event); } static getConfigElement() { return document.createElement("nintendo-wishlist-card-editor"); } static getStubConfig() { return {entity: 'sensor.nintendo_wishlist', title: 'Switch Wishlist', image_style: 'backgroundart', max: 5} } } customElements.define("nintendo-wishlist-card", NintendoWishlistCard); window.customCards = window.customCards || []; window.customCards.push({ type: "nintendo-wishlist-card", name: "Nintendo Wishlist Card", description: "A card that displays Nintendo Switch games that are on sale from your wish list when using the nintendo-wishlist custom integration." })