Add interactiveness to HTML outputs. #4

Merged
manuel merged 1 commit from feature/interactive-html into master 2023-04-08 23:48:21 +02:00

View file

@ -5,7 +5,109 @@ import ch.fritteli.labyrinth.generator.renderer.Renderer;
import lombok.NonNull; import lombok.NonNull;
public class HTMLRenderer implements Renderer<String> { public class HTMLRenderer implements Renderer<String> {
private static final String POSTAMBLE = "</body></html>";
private static final String POSTAMBLE = "<script>"
+ "let userPath = [];"
+ "const DIR_UNDEF = -1;"
+ "const DIR_SAME = 0;"
+ "const DIR_UP = 1;"
+ "const DIR_RIGHT = 2;"
+ "const DIR_DOWN = 3;"
+ "const DIR_LEFT = 4;"
+ "function getCoords(cell) {"
+ " return {"
+ " x: cell.cellIndex,"
+ " y: cell.parentElement.rowIndex"
+ " };"
+ "}"
+ "function distance(prev, next) {"
+ " return Math.abs(prev.x - next.x) + Math.abs(prev.y - next.y);"
+ "}"
+ "function direction(prev, next) {"
+ " const dist = distance(prev, next);"
+ " if (dist === 0) {"
+ " return DIR_SAME;"
+ " }"
+ " if (dist !== 1) {"
+ " return DIR_UNDEF;"
+ " }"
+ " if (next.x === prev.x) {"
+ " if (next.y === prev.y + 1) {"
+ " return DIR_DOWN;"
+ " }"
+ " return DIR_UP;"
+ " }"
+ " if (next.x === prev.x + 1) {"
+ " return DIR_RIGHT;"
+ " }"
+ " return DIR_LEFT;"
+ "}"
+ "(function () {"
+ " const labyrinthTable = document.getElementById(\"labyrinth\");"
+ " const labyrinthCells = labyrinthTable.getElementsByTagName(\"td\");"
+ " const start = {x: 0, y: 0};"
+ " const end = {"
+ " x: labyrinthTable.getElementsByTagName(\"tr\")[0].getElementsByTagName(\"td\").length - 1,"
+ " y: labyrinthTable.getElementsByTagName(\"tr\").length - 1"
+ " };"
+ " for (let i = 0; i < labyrinthCells.length; i++) {"
+ " let cell = labyrinthCells.item(i);"
+ " cell.onclick = (event) => {"
+ " let target = event.target;"
+ " const coords = getCoords(target);"
+ " if (coords.x === end.x && coords.y === end.y) {"
+ " alert(\"HOORAY! You did it! Congratulations!\")"
+ " }"
+ " if (userPath.length === 0) {"
+ " if (coords.x === start.x && coords.y === start.y) {"
+ " userPath.push(coords);"
+ " target.classList.toggle(\"user\");"
+ " }"
+ " } else {"
+ " const dir = direction(userPath[userPath.length - 1], coords);"
+ " switch (dir) {"
+ " case DIR_UNDEF:"
+ " return;"
+ " case DIR_SAME:"
+ " userPath.pop();"
+ " target.classList.toggle(\"user\");"
+ " return;"
+ " default:"
+ " if (userPath.find(value => value.x === coords.x && value.y === coords.y)) {"
+ " return;"
+ " } else {"
+ " switch (dir) {"
+ " case DIR_UP:"
+ " if (target.classList.contains(\"bottom\")) {"
+ " return;"
+ " }"
+ " break;"
+ " case DIR_RIGHT:"
+ " if (target.classList.contains(\"left\")) {"
+ " return;"
+ " }"
+ " break;"
+ " case DIR_DOWN:"
+ " if (target.classList.contains(\"top\")) {"
+ " return;"
+ " }"
+ " break;"
+ " case DIR_LEFT:"
+ " if (target.classList.contains(\"right\")) {"
+ " return;"
+ " }"
+ " break;"
+ " }"
+ " userPath.push(coords);"
+ " target.classList.toggle(\"user\");"
+ " return;"
+ " }"
+ " }"
+ " }"
+ " };"
+ " }"
+ "})();"
+ "</script></body></html>";
private HTMLRenderer() { private HTMLRenderer() {
} }
@ -23,7 +125,7 @@ public class HTMLRenderer implements Renderer<String> {
} }
final Generator generator = new Generator(labyrinth); final Generator generator = new Generator(labyrinth);
final StringBuilder sb = new StringBuilder(this.getPreamble(labyrinth)); final StringBuilder sb = new StringBuilder(this.getPreamble(labyrinth));
sb.append("<table>"); sb.append("<table id=\"labyrinth\">");
while (generator.hasNext()) { while (generator.hasNext()) {
sb.append(generator.next()); sb.append(generator.next());
} }
@ -39,11 +141,12 @@ public class HTMLRenderer implements Renderer<String> {
"<meta charset=\"utf-8\">" + "<meta charset=\"utf-8\">" +
"<style>" + "<style>" +
"table{border-collapse:collapse;}" + "table{border-collapse:collapse;}" +
"td{border:0 solid black;height:1em;width:1em;}" + "td{border:0 solid black;height:1em;width:1em;cursor:pointer;}" +
"td.top{border-top-width:1px;}" + "td.top{border-top-width:1px;}" +
"td.right{border-right-width:1px;}" + "td.right{border-right-width:1px;}" +
"td.bottom{border-bottom-width:1px;}" + "td.bottom{border-bottom-width:1px;}" +
"td.left{border-left-width:1px;}" + "td.left{border-left-width:1px;}" +
"td.user{background:hotpink;}" +
"</style>" + "</style>" +
"<script>" + "<script>" +
"let solution = false;" + "let solution = false;" +
@ -59,7 +162,6 @@ public class HTMLRenderer implements Renderer<String> {
"</script>" + "</script>" +
"</head>" + "</head>" +
"<body>" + "<body>" +
"<input type=\"checkbox\" onclick=\"toggleSolution()\">show solution</input>"; "<input id=\"solutionbox\" type=\"checkbox\" onclick=\"toggleSolution()\"/><label for=\"solutionbox\">show solution</label>";
} }
} }