1 const sleep
= (ms
) => new Promise(resolve
=> setTimeout(resolve
, ms
));
3 const createGenreListWithClickEvent
= (genreList
, genres
, callback
) => {
4 genreList
.append(...genres
.map((genre
) => {
5 const name
= genre
["name"];
6 const li
= document
.createElement("li");
7 li
.setAttribute("data-name", name
);
8 li
.addEventListener("click", callback
);
9 li
.appendChild(document
.createTextNode(name
));
14 const relativeToCtx
= (x1
, x2
, y1
, y2
, ctx
) => {
17 ctx
.lineTo(newX
, newY
);
22 * Return (x,y) of canvas
24 const getDimensions
= (canvas
) => [canvas
.width
, canvas
.height
];
27 * Return (x,y) midpoint of canvas
29 const getCenter
= (canvas
) => getDimensions(canvas
).map((dim
) => dim
/ 2);
32 * Draw champion box and clear background.
34 const drawWinner
= (canvas
) => {
35 const ctx
= canvas
.getContext("2d");
36 const [width
, height
] = getDimensions(canvas
);
37 const [mid_x
, mid_y
] = getCenter(canvas
);
38 const rect_width
= width
* .5;
39 const rect_height
= height
* .1;
40 ctx
.strokeRect(mid_x
- rect_width
/ 2, mid_y
- rect_height
/ 2, rect_width
, rect_height
);
41 ctx
.clearRect(mid_x
- rect_width
/ 2, mid_y
- rect_height
/ 2, rect_width
, rect_height
);
45 * Draws a path in context from the current location to a point xDist * left, yDist * up away from it.
46 * @param ctx RenderingContext
47 * @param x, y float current location
48 * @param x, y float distance away
49 * @param up, left (-1|1) directions
50 * @return [newNodeX, newNodeY]
52 const drawBranchFrom
= (ctx
, x
, y
, xDist
, yDist
, left
, up
) => {
53 const newX
= x
+ (xDist
* left
)
54 const newY
= y
+ (yDist
* up
)
56 ctx
.lineTo(newX
, newY
);
60 const drawArtistOnCtx
= (ctx
, artistName
, x
, y
) => ctx
.strokeText(artistName
, x
, y
);
63 * Draws paths to the terminal nodes of a round
64 * @param x, y the point representation of the start of the branch
66 const drawMatchup
= (canvas
, x
, y
, iter
, left
, artist1
, artist2
) => {
67 const ctx
= canvas
.getContext("2d");
68 const [width
, height
] = getDimensions(canvas
);
70 const drawBranchUp
= (xDist
, yDist
) => drawBranchFrom(ctx
, x
, y
, xDist
, yDist
, left
, 1);
71 const drawBranchDown
= (xDist
, yDist
) => drawBranchFrom(ctx
, x
, y
, xDist
, yDist
, left
, -1);
72 const drawArtist
= (artistName
, x
, y
) => drawArtistOnCtx(ctx
, artistName
, x
, y
);
78 ...drawBranchUp(width
/ iter
, height
/ iter
)
83 ...drawBranchDown(width
/ iter
, height
/ iter
)
88 const drawBracket
= (canvas
, artists
) => {
90 const [mid_x
, mid_y
] = getCenter(canvas
);
92 const rounds
= Math
.log2(artists
.length
/ groups
);
93 for (let group
= 1; group
<= groups
; group
++) {
94 for (let round
= 0; round
< rounds
; round
++) {
95 const matchup
= [artists
.shift(), artists
.pop()];
96 drawMatchup(canvas
, mid_x
, mid_y
, 2 * round
, Math
.pow(-1, group
), ...matchup
);
101 window
.onload
= () => {
102 const lStorage
= window
.localStorage
;
103 const genreList
= document
.getElementById("genre-list");
104 const genreInput
= document
.getElementById("genre-input");
105 const genreForm
= document
.getElementById("genre-form");
106 const canvas
= document
.getElementById("bracket");
108 const createGenreList
= (genreList
, genres
) => {
109 return createGenreListWithClickEvent(genreList
, genres
, (e
) => {
110 genreInput
.value
= e
.target
.innerText
;
111 genreForm
.requestSubmit();
114 let genres
= JSON
.parse(lStorage
.getItem("genres"))
115 if (genres
=== null) {
116 fetch("http://localhost:8080/genre")
117 .then((response
) => response
.text())
119 window
.localStorage
.setItem("genres", text
);
120 genres
= JSON
.parse(text
);
121 createGenreList(genreList
, genres
);
124 createGenreList(genreList
, genres
);
127 genreForm
.addEventListener("submit", (e
) => {
129 fetch(encodeURI(`http://localhost:8080/artist/genre?genre_name=${genreInput.value}`))
130 .then((response
) => response
.json())
131 .then((data
) => drawBracket(canvas
, data
.slice(0, 33)));
134 genreInput
.addEventListener("input", (e
) => {
135 const input
= e
.target
;
136 for (const item
of genreList
.children
) {
137 item
.style
.display
= item
.dataset
.name
.includes(input
.value
.toLowerCase()) ? "block" : "none";
140 genreInput
.addEventListener("focus", (e
) => {
141 genreList
.style
.display
= "block";
143 genreInput
.addEventListener("blur", (e
) => {
144 sleep(150).then(() => genreList
.style
.display
= "none");
148 canvas
.width
= window
.innerWidth
;
149 canvas
.height
= window
.innerHeight
;
150 drawBracket(canvas
, []);
152 const bgImg
= new Image();
153 bgImg
.onload
= () => {
154 const ctx
= canvas
.getContext("2d");
155 //ctx.mozImageSmoothingEnabled = false;
156 //ctx.webkitImageSmoothingEnabled = false;
157 //ctx.msImageSmoothingEnabled = false;
158 //ctx.imageSmoothingEnabled = false;
163 (canvas
.width
/ bgImg
.width
) * bgImg
.width
,
164 (canvas
.height
/ bgImg
.height
) * bgImg
.height
168 const upload
= document
.getElementById("image-upload");
169 upload
.addEventListener("change", (e
) => {
170 bgImg
.src
= URL
.createObjectURL(e
.target
.files
[0]);