More work on cube html
This commit is contained in:
parent
9953f6c289
commit
e7e7426da1
3 changed files with 182 additions and 23 deletions
|
@ -2,11 +2,23 @@ import * as ejs from "ejs";
|
||||||
import { existsSync } from "fs";
|
import { existsSync } from "fs";
|
||||||
import { createServer } from "http";
|
import { createServer } from "http";
|
||||||
|
|
||||||
import { Article, asyncLoadJSON, CardItem, leanArticle, leanCard, MCMDB, onlyUnique } from "../lib";
|
import {
|
||||||
|
Article,
|
||||||
|
asyncLoadJSON,
|
||||||
|
CardItem,
|
||||||
|
dictMap,
|
||||||
|
filterDict,
|
||||||
|
leanArticle,
|
||||||
|
leanCard,
|
||||||
|
MCMDB,
|
||||||
|
onlyUnique,
|
||||||
|
spanBy,
|
||||||
|
} from "../lib";
|
||||||
|
|
||||||
const colorNames = {
|
const colorNames = {
|
||||||
CL: "Colorless",
|
CL: "Colorless",
|
||||||
MC: "Multicolor",
|
MC: "Multicolor",
|
||||||
|
L: "Land",
|
||||||
W: "White",
|
W: "White",
|
||||||
U: "Blue",
|
U: "Blue",
|
||||||
B: "Black",
|
B: "Black",
|
||||||
|
@ -35,13 +47,14 @@ const colorNames = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const columns: Record<string, (c: CardItem) => boolean> = {
|
const columns: Record<string, (c: CardItem) => boolean> = {
|
||||||
W: c => colorid(c.colorIdentity) == "W",
|
W: c => c.types[0] != "Land" && colorid(c.colorIdentity) == "W",
|
||||||
U: c => colorid(c.colorIdentity) == "U",
|
U: c => c.types[0] != "Land" && colorid(c.colorIdentity) == "U",
|
||||||
B: c => colorid(c.colorIdentity) == "B",
|
B: c => c.types[0] != "Land" && colorid(c.colorIdentity) == "B",
|
||||||
R: c => colorid(c.colorIdentity) == "R",
|
R: c => c.types[0] != "Land" && colorid(c.colorIdentity) == "R",
|
||||||
G: c => colorid(c.colorIdentity) == "G",
|
G: c => c.types[0] != "Land" && colorid(c.colorIdentity) == "G",
|
||||||
MC: c => c.colorIdentity.length > 0,
|
MC: c => c.types[0] != "Land" && c.colorIdentity.length > 1,
|
||||||
CL: c => colorid(c.colorIdentity) == "CL"
|
CL: c => c.types[0] != "Land" && colorid(c.colorIdentity) == "CL",
|
||||||
|
L: c => c.types[0] == "Land"
|
||||||
};
|
};
|
||||||
|
|
||||||
function wubrg(a: string, b: string) {
|
function wubrg(a: string, b: string) {
|
||||||
|
@ -58,6 +71,53 @@ function colorid(colors: string[]): string {
|
||||||
return colors.sort(wubrg).join("");
|
return colors.sort(wubrg).join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function prettyColor(color: string) {
|
||||||
|
if (color in colorNames) {
|
||||||
|
return colorNames[color];
|
||||||
|
}
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
const allTypes = [
|
||||||
|
"Creature",
|
||||||
|
"Planeswalker",
|
||||||
|
"Instant",
|
||||||
|
"Sorcery",
|
||||||
|
"Artifact",
|
||||||
|
"Enchantment"
|
||||||
|
];
|
||||||
|
function typeSort(a: string, b: string): number {
|
||||||
|
const indexA = allTypes.indexOf(a);
|
||||||
|
const indexB = allTypes.indexOf(b);
|
||||||
|
return indexA - indexB;
|
||||||
|
}
|
||||||
|
|
||||||
|
function abcSort(a: string, b: string): number {
|
||||||
|
if (a > b) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (b > a) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This would be properly typed but as of currently TS does not allow circular references for types
|
||||||
|
function deepSum(dict: any): number {
|
||||||
|
if (Array.isArray(dict)) {
|
||||||
|
return dict.length;
|
||||||
|
}
|
||||||
|
let total = 0;
|
||||||
|
for (let key in dict) {
|
||||||
|
if (Array.isArray(dict[key])) {
|
||||||
|
total += dict[key].length;
|
||||||
|
} else {
|
||||||
|
total += deepSum(dict[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
async function run() {
|
async function run() {
|
||||||
if (process.argv.length < 3) {
|
if (process.argv.length < 3) {
|
||||||
console.error("Usage: yarn fetch <uid>");
|
console.error("Usage: yarn fetch <uid>");
|
||||||
|
@ -96,15 +156,27 @@ async function run() {
|
||||||
)
|
)
|
||||||
.filter(onlyUnique);
|
.filter(onlyUnique);
|
||||||
|
|
||||||
|
// Filter by colors and cmc (nested)
|
||||||
|
let colorcards = filterDict(valid, columns);
|
||||||
|
let cmccards = dictMap(colorcards, (cards, col) =>
|
||||||
|
dictMap(
|
||||||
|
spanBy(cards, c =>
|
||||||
|
col == "MC" || col == "L" ? colorid(c.colorIdentity) : c.types[0]
|
||||||
|
),
|
||||||
|
typed => spanBy(typed, c => c.convertedManaCost)
|
||||||
|
)
|
||||||
|
);
|
||||||
const server = createServer(async (req, res) => {
|
const server = createServer(async (req, res) => {
|
||||||
const template = await ejs.renderFile("templates/cube.ejs", {
|
const template = await ejs.renderFile("templates/cube.ejs", {
|
||||||
user: uid,
|
user: uid,
|
||||||
cards: valid,
|
cards: valid,
|
||||||
|
cmccards,
|
||||||
columns,
|
columns,
|
||||||
utils: { wubrg, colorNames, colorid }
|
utils: { wubrg, prettyColor, colorid, deepSum, typeSort, abcSort }
|
||||||
});
|
});
|
||||||
res.end(template);
|
res.end(template);
|
||||||
});
|
});
|
||||||
|
|
||||||
server.on("clientError", (err, socket) => {
|
server.on("clientError", (err, socket) => {
|
||||||
socket.end("HTTP/1.1 400 Bad Request\r\n\r\n");
|
socket.end("HTTP/1.1 400 Bad Request\r\n\r\n");
|
||||||
});
|
});
|
||||||
|
|
38
lib/utils.ts
38
lib/utils.ts
|
@ -61,3 +61,41 @@ export function leanArticle(article: Article): LeanArticle {
|
||||||
export function onlyUnique(value: CardItem, index: number, self: CardItem[]) {
|
export function onlyUnique(value: CardItem, index: number, self: CardItem[]) {
|
||||||
return self.findIndex(c => c.name == value.name) == index;
|
return self.findIndex(c => c.name == value.name) == index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function filterDict<K extends keyof any, T>(
|
||||||
|
arr: T[],
|
||||||
|
list: Record<K, (i: T) => boolean>
|
||||||
|
): Record<K, T[]> {
|
||||||
|
let out: Record<K, T[]> = Object.create(null);
|
||||||
|
for (const key in list) {
|
||||||
|
out[key] = arr.filter(list[key]);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function spanBy<K extends keyof any, T>(
|
||||||
|
arr: T[],
|
||||||
|
spanFn: (i: T) => K
|
||||||
|
): Record<K, T[]> {
|
||||||
|
let out: Record<K, T[]> = Object.create(null);
|
||||||
|
arr.forEach(item => {
|
||||||
|
let key = spanFn(item);
|
||||||
|
if (key in out) {
|
||||||
|
out[key].push(item);
|
||||||
|
} else {
|
||||||
|
out[key] = [item];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function dictMap<K extends keyof any, A, B>(
|
||||||
|
dict: Record<K, A>,
|
||||||
|
mapFn: (i: A, k: K) => B
|
||||||
|
): Record<K, B> {
|
||||||
|
let out: Record<K, B> = Object.create(null);
|
||||||
|
for (let key in dict) {
|
||||||
|
out[key] = mapFn(dict[key], key);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
main {
|
main {
|
||||||
background: white;
|
background: white;
|
||||||
max-width: 920px;
|
max-width: 1220px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 20pt;
|
padding: 20pt;
|
||||||
}
|
}
|
||||||
|
@ -33,58 +33,107 @@
|
||||||
|
|
||||||
.color-section {
|
.color-section {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
margin: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-section header {
|
.color-section header {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-W header {
|
.color-W header.color-header {
|
||||||
background-color: #fff6eb;
|
background-color: #fff6eb;
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-U header {
|
.color-U header.color-header {
|
||||||
background-color: #49b0fd;
|
background-color: #49b0fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-B header {
|
.color-B header.color-header {
|
||||||
background-color: #666;
|
background-color: #666;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-R header {
|
.color-R header.color-header {
|
||||||
background-color: #ff6868;
|
background-color: #ff6868;
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-G header {
|
.color-G header.color-header {
|
||||||
background-color: #78ce59;
|
background-color: #78ce59;
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-MC header {
|
.color-MC header.color-header {
|
||||||
background: linear-gradient(to left, #f3ff6b, #ffb019);
|
background: linear-gradient(to left, #f3ff6b, #ffb019);
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-CL header {
|
.color-CL header.color-header {
|
||||||
background-color: #d2d2d2;
|
background-color: #d2d2d2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.color-L header.color-header {
|
||||||
|
background-color: #eeb445;
|
||||||
|
}
|
||||||
|
|
||||||
|
.type-section {
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-size: 10pt;
|
||||||
|
margin: 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.type-section header.type-header {
|
||||||
|
font-size: 12pt;
|
||||||
|
font-weight: bold;
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style-type: none;
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul li {
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<main>
|
<main>
|
||||||
<header>
|
<header>
|
||||||
<h1>Junk rare cube</h1>
|
<h1>Junk rare cube (<%=cards.length%> cards)</h1>
|
||||||
<h2>Using cards from <a href="https://www.cardmarket.com/en/Magic/Users/<%=user%>"><b><%= user %></b></a>
|
<h2>Using cards from <a href="https://www.cardmarket.com/en/Magic/Users/<%=user%>"><b><%= user %></b></a>
|
||||||
</h2>
|
</h2>
|
||||||
</header>
|
</header>
|
||||||
<section class="columns">
|
<section class="columns">
|
||||||
<% for (const column in columns) { %>
|
<% Object.entries(cmccards).forEach(([color, colorcards]) => { %>
|
||||||
<section class="color-section color-<%=column%>">
|
<section class="color-section color-<%=color%>">
|
||||||
<header>
|
<header class="color-header">
|
||||||
<h3><%=utils.colorNames[column]%></h3>
|
<h3><%=utils.prettyColor(color)%> (<%=utils.deepSum(colorcards)%>)</h3>
|
||||||
</header>
|
</header>
|
||||||
|
<% Object.entries(colorcards).sort((a, b) => color == "MC" || color == "L" ? utils.abcSort(a[0], b[0]) : utils.typeSort(a[0], b[0])).forEach(([type, typecards]) => { %>
|
||||||
|
<article class="type-section">
|
||||||
|
<header class="type-header"><%= color == "MC" || color == "L" ? utils.prettyColor(type) : type %>
|
||||||
|
(<%=utils.deepSum(typecards)%>)</header>
|
||||||
|
<section class="cards">
|
||||||
|
<% Object.entries(typecards).forEach(([cmc, finalcards]) => { %>
|
||||||
|
<ul>
|
||||||
|
<% finalcards.forEach(card => { %>
|
||||||
|
<li><%= card.name %></li>
|
||||||
|
<% }); %>
|
||||||
|
</ul>
|
||||||
|
<% }); %>
|
||||||
|
</section>
|
||||||
|
</article>
|
||||||
|
<% }); %>
|
||||||
</section>
|
</section>
|
||||||
<% } %>
|
<% }); %>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
</body>
|
</body>
|
||||||
|
|
Loading…
Reference in a new issue