1 const sleep
= (ms
) => new Promise(resolve
=> setTimeout(resolve
, ms
));
3 const getGenres
= (lStorage
) => {
4 return new Promise((resolve
) => {
5 const genres
= JSON
.parse(lStorage
.getItem("genres"))
7 fetch("http://api.brackets.jacobcasper.com/genre")
8 .then((response
) => response
.text())
10 lStorage
.setItem("genres", text
);
11 resolve(JSON
.parse(text
));
22 const formSubmitPolyfill
= (form
, callback
) => {
23 if (form
.requestSubmit
) {
30 const createGenreListWithClickEvent
= (genreList
, genres
, callback
) => {
31 genreList
.append(...genres
.map((genre
) => {
32 const name
= genre
["name"];
33 const option
= document
.createElement("option");
34 option
.setAttribute("data-name", name
);
35 option
.addEventListener("click", callback
);
36 option
.appendChild(document
.createTextNode(name
));
41 const relativeToCtx
= (x1
, x2
, y1
, y2
, ctx
) => {
44 ctx
.lineTo(newX
, newY
);
49 * Return (x,y) of canvas
51 const getDimensions
= (canvas
) => [canvas
.width
, canvas
.height
];
54 * Return (x,y) midpoint of canvas
56 const getCenter
= (canvas
) => getDimensions(canvas
).map((dim
) => dim
/ 2);
58 const getRectangleDimensionsUnbound
= (canvas
, xScale
, yScale
) => {
59 [width
, height
] = getDimensions(canvas
);
60 return [width
* xScale
, height
* yScale
];
63 const getRectangleDimensions
= (canvas
) => getRectangleDimensionsUnbound(canvas
, .5, .1);
66 * Draw champion box and clear background.
68 const drawWinner
= (canvas
) => {
69 const ctx
= canvas
.getContext("2d");
70 const [width
, height
] = getDimensions(canvas
);
71 const [mid_x
, mid_y
] = getCenter(canvas
);
72 const [rect_width
, rect_height
] = getRectangleDimensions(canvas
);
73 ctx
.strokeRect(mid_x
- rect_width
/ 2, mid_y
- rect_height
/ 2, rect_width
, rect_height
);
74 ctx
.clearRect(mid_x
- rect_width
/ 2, mid_y
- rect_height
/ 2, rect_width
, rect_height
);
78 * Draws a path in context from the current location to a point xDist * left, yDist * up away from it.
79 * @param ctx RenderingContext
80 * @param x, y float current location
81 * @param x, y float distance away
82 * @param up, left (-1|1) directions
83 * @return [newNodeX, newNodeY]
85 const drawBranchFrom
= (ctx
, x
, y
, xDist
, yDist
, left
, up
) => {
86 const newX
= x
+ (xDist
* left
)
87 const newY
= y
+ (yDist
* up
)
89 ctx
.lineTo(newX
, newY
);
94 const drawArtistOnCtx
= (ctx
, artistName
, x
, y
) => {
95 ctx
.font
= "20px sans serif";
96 ctx
.strokeText(artistName
, x
, y
);
100 * Draws paths to the terminal nodes of a round
101 * @param x, y the point representation of the start of the branch
102 * @param baseCallback a callback that needs the terminal x,y context
104 const drawMatchup
= (canvas
, x
, y
, iter
, maxIter
, left
, artists
, baseCallback
) => {
105 if (iter
=== maxIter
) {
106 return baseCallback(x
, y
);
108 const ctx
= canvas
.getContext("2d");
109 ctx
.direction
= left
=== -1 ? "ltr" : "rtl";
110 const [width
, height
] = getDimensions(canvas
);
112 const drawBranchUp
= (xDist
, yDist
) => drawBranchFrom(ctx
, x
, y
, xDist
, yDist
, left
, -1);
113 const drawBranchDown
= (xDist
, yDist
) => drawBranchFrom(ctx
, x
, y
, xDist
, yDist
, left
, 1);
114 const drawArtist
= (artistName
, x
, y
) => drawArtistOnCtx(ctx
, artistName
, x
, y
);
115 const drawArtist1
= (x
, y
) => {
116 const artist1
= artists
.shift();
120 drawArtist(artist1
["name"], x
, y
);
123 const drawArtist2
= (x
, y
) => {
124 const artist2
= artists
.pop();
128 drawArtist(artist2
["name"], x
, y
);
133 const branchDistances
= [width
/ (5 * (iter
+ 1)), height
/ (9 * (iter
+ 1))];
136 ...drawBranchUp(...branchDistances
),
147 ...drawBranchDown(...branchDistances
),
156 const drawBracket
= (canvas
, artists
, genre
) => {
157 const context
= canvas
.getContext("2d");
158 context
.clearRect(0, 0, canvas
.width
, canvas
.height
);
160 const [mid_x
, mid_y
] = getCenter(canvas
);
161 context
.font
= '28px sans serif'
162 context
.textAlign
= "center";
163 context
.strokeText(genre
.toUpperCase(), mid_x
, 40);
164 context
.textAlign
= "start";
165 const [rect_width
, rect_height
] = getRectangleDimensions(canvas
);
167 const rounds
= Math
.max(
168 Math
.floor(Math
.log2(artists
.length
/ groups
)),
171 for (let group
= 1; group
<= groups
; group
++) {
172 if (artists
.length
=== 0) {
177 mid_x
+ (Math
.pow(-1, group
) * (rect_width
/ 6)),
178 mid_y
+ (Math
.pow(-1, Math
.floor(group
/ 2)) * (rect_height
* 2.5)),
183 (x
, y
) => console
.log("hello")
188 window
.onload
= () => {
189 const lStorage
= window
.localStorage
;
190 const genreList
= document
.getElementById("genre-list");
191 const genreInput
= document
.getElementById("genre-input");
192 const genreForm
= document
.getElementById("genre-form");
193 const canvas
= document
.getElementById("bracket");
195 const genreFormSubmitPolyfill
= () => formSubmitPolyfill(genreList
, formSubmitAction
)
197 const createGenreList
= (genreList
, genres
) => {
198 return createGenreListWithClickEvent(genreList
, genres
, (e
) => {
199 genreInput
.value
= e
.target
.innerText
;
200 genreFormSubmitPolyfill()
204 const genres
= getGenres(lStorage
)
205 .then((genres
) => createGenreList(genreList
, genres
));
207 const formSubmitAction
= () => {
208 fetch(encodeURI(`http://api.brackets.jacobcasper.com/artist/genre?genre_name=${genreInput.value}`))
209 .then((response
) => response
.json())
210 .then((data
) => drawBracket(canvas
, data
.slice(0, 33), genreInput
.value
));
213 genreForm
.addEventListener("submit", (e
) => {
218 genreInput
.addEventListener("change", (e
) => {
219 genreFormSubmitPolyfill()
223 canvas
.width
= window
.innerWidth
;
224 canvas
.height
= window
.innerHeight
;
225 drawBracket(canvas
, [], "");
227 const bgImg
= new Image();
228 bgImg
.onload
= () => {
229 const ctx
= canvas
.getContext("2d");
234 (canvas
.width
/ bgImg
.width
) * bgImg
.width
,
235 (canvas
.height
/ bgImg
.height
) * bgImg
.height
239 const upload
= document
.getElementById("image-upload");
240 upload
.addEventListener("change", (e
) => {
241 bgImg
.src
= URL
.createObjectURL(e
.target
.files
[0]);