Initial commit
This commit is contained in:
6
README.md
Normal file
6
README.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# THIS WILL NOT WORK IF YOU RUN IT YOURSELF!
|
||||||
|
|
||||||
|
The generator expects `template.zip` with the full template included, it just injects its own `thesis.typ` into it.
|
||||||
|
|
||||||
|
I'll make the script for generating this archive when I get back from work today lol.
|
||||||
|
|
167
data.js
Normal file
167
data.js
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
let steps = {
|
||||||
|
faculty: {
|
||||||
|
title: "Pro kterou fakultu budete svou práci tvořit?",
|
||||||
|
layout: faculty_layout,
|
||||||
|
result: {
|
||||||
|
name: ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
thesis_type: {
|
||||||
|
title: "Jakou práci budete tvořit?",
|
||||||
|
layout: thesis_layout,
|
||||||
|
result: {
|
||||||
|
type: ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
author_info: {
|
||||||
|
title: "Vaše údaje",
|
||||||
|
layout: author_info_layout,
|
||||||
|
result: {
|
||||||
|
name: "",
|
||||||
|
gender: ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
collaborators: {
|
||||||
|
title: "Vedoucí práce",
|
||||||
|
layout: collaborators_layout,
|
||||||
|
result: {
|
||||||
|
has_supervisor: true,
|
||||||
|
supervisor_name: "",
|
||||||
|
has_consultant: false,
|
||||||
|
consultant_name: ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const faculties = {
|
||||||
|
"fs": {
|
||||||
|
name: "Fakulta strojní"
|
||||||
|
},
|
||||||
|
"ft": {
|
||||||
|
name: "Fakulta textilní"
|
||||||
|
},
|
||||||
|
"fp": {
|
||||||
|
name: "Fakulta přirodovědně-humanitní a pedagogická"
|
||||||
|
},
|
||||||
|
"ef": {
|
||||||
|
name: "Ekonomická fakulta"
|
||||||
|
},
|
||||||
|
"fua": {
|
||||||
|
name: "Fakulta umění a architektury"
|
||||||
|
},
|
||||||
|
"fm": {
|
||||||
|
name: "Fakulta mechatroniky, informatiky a mezioborových studií"
|
||||||
|
},
|
||||||
|
"fzs": {
|
||||||
|
name: "Fakulta zdravotnických studií"
|
||||||
|
},
|
||||||
|
"cxi": {
|
||||||
|
name: "Ústav pro nanomateriály, pokročilé technologie a inovace"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const theses = {
|
||||||
|
"bp": {
|
||||||
|
name: "Bakalářská práce"
|
||||||
|
},
|
||||||
|
"dp": {
|
||||||
|
name: "Diplomová práce"
|
||||||
|
},
|
||||||
|
"dis": {
|
||||||
|
name: "Disertační práce"
|
||||||
|
},
|
||||||
|
"hab": {
|
||||||
|
name: "Habilitační práce"
|
||||||
|
},
|
||||||
|
"teze": {
|
||||||
|
name: "Teze disertační práce"
|
||||||
|
},
|
||||||
|
"autoref": {
|
||||||
|
name: "Autoreferát disertační práce"
|
||||||
|
},
|
||||||
|
"proj": {
|
||||||
|
name: "Projekt"
|
||||||
|
},
|
||||||
|
"sp": {
|
||||||
|
name: "Semestrální práce"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const genders = {
|
||||||
|
"masculine": {
|
||||||
|
name: "Mužský rod"
|
||||||
|
},
|
||||||
|
"feminine": {
|
||||||
|
name: "Ženský rod"
|
||||||
|
},
|
||||||
|
"we": {
|
||||||
|
name: "Množné číslo"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const typst_header = `#import "template/template.typ": *
|
||||||
|
|
||||||
|
#show: tultemplate2.with(
|
||||||
|
faculty: "{faculty.name}",
|
||||||
|
document: "{thesis_type.type}",
|
||||||
|
title: (cs: "Návod na použití Typst TUL šablony"),
|
||||||
|
author: "{author_info.name}",
|
||||||
|
author_gender: "{author_info.gender}",
|
||||||
|
<collaborators.has_supervisor: supervisor: "{collaborators.supervisor_name}",
|
||||||
|
><collaborators.has_consultant: consultant: "{collaborators.consultant_name}",
|
||||||
|
> citations: "citations.bib",
|
||||||
|
assignment: "zadani.pdf"
|
||||||
|
)
|
||||||
|
|
||||||
|
= Nadpis
|
||||||
|
|
||||||
|
skibidi
|
||||||
|
`;
|
||||||
|
|
||||||
|
async function generate_zip() {
|
||||||
|
let assets_zip = await fetch("template.zip");
|
||||||
|
let out_zip = await new JSZip().loadAsync(await assets_zip.blob());
|
||||||
|
|
||||||
|
let out = typst_header;
|
||||||
|
|
||||||
|
for(const step_key in steps) {
|
||||||
|
console.log(step_key);
|
||||||
|
|
||||||
|
for(const key in steps[step_key].result) {
|
||||||
|
console.log("- " + key);
|
||||||
|
|
||||||
|
const search_condition = "<" + step_key + "." + key + ":";
|
||||||
|
const condition_pos = out.search(search_condition);
|
||||||
|
|
||||||
|
if(condition_pos >= 0) {
|
||||||
|
const condition_end_pos = out.slice(condition_pos).search(">");
|
||||||
|
|
||||||
|
if(condition_end_pos >= 0) {
|
||||||
|
const head = out.slice(0, condition_pos);
|
||||||
|
const meat = steps[step_key].result[key] ? out.slice(condition_pos + search_condition.length, condition_pos + condition_end_pos) : "";
|
||||||
|
const tail = out.slice(condition_pos + condition_end_pos + 1);
|
||||||
|
|
||||||
|
out = head + meat + tail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const search_replacement = "{" + step_key + "." + key + "}";
|
||||||
|
const replacement_pos = out.search(search_replacement);
|
||||||
|
|
||||||
|
if(replacement_pos >= 0) {
|
||||||
|
const head = out.slice(0, replacement_pos);
|
||||||
|
const meat = steps[step_key].result[key];
|
||||||
|
const tail = out.slice(replacement_pos + search_replacement.length);
|
||||||
|
|
||||||
|
out = head + meat + tail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(out);
|
||||||
|
|
||||||
|
out_zip.file("thesis.typ", out);
|
||||||
|
|
||||||
|
return await out_zip.generateAsync({ type: "blob" });
|
||||||
|
}
|
||||||
|
|
39
index.html
Normal file
39
index.html
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="stylesheet" href="styles.css">
|
||||||
|
<script type="text/javascript" src="lib/jszip.min.js"></script>
|
||||||
|
<script type="text/javascript" src="ui.js"></script>
|
||||||
|
<script type="text/javascript" src="utils.js"></script>
|
||||||
|
<script type="text/javascript" src="data.js"></script>
|
||||||
|
<title>TUL Typst template generator</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="main-container">
|
||||||
|
<div class="main-content">
|
||||||
|
<h1>Generátor TUL Typst šablony</h1>
|
||||||
|
|
||||||
|
<img style="height: 100px; width: 100px;" src="tul_prdel.svg">
|
||||||
|
|
||||||
|
<div class="paragraphs">
|
||||||
|
<p>
|
||||||
|
Vítejte v generátoru šablony pro tvorbu prací v jazyce Typst.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Zeptáme se Vás na několik základních otázek ohledně Vaší práce
|
||||||
|
a na konci poskytneme funkční práci v Typstu s Vámi zadanými údaji.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="horizontal-list">
|
||||||
|
<button class="accent" id="startbutton" onclick="run();">Jdeme na to!</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<img class="zmp-logo" src="zumepro_dark_full.svg">
|
||||||
|
</body>
|
||||||
|
</html>
|
13
lib/jszip.min.js
vendored
Normal file
13
lib/jszip.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
300
styles.css
Normal file
300
styles.css
Normal file
@@ -0,0 +1,300 @@
|
|||||||
|
:root {
|
||||||
|
--tul-color: #5948AD;
|
||||||
|
--fs-color: #888B95;
|
||||||
|
--ft-color: #924C14;
|
||||||
|
--fp-color: #0076D5;
|
||||||
|
--ef-color: #65A812;
|
||||||
|
--fua-color: #006443;
|
||||||
|
--fm-color: #EA7603;
|
||||||
|
--fzs-color: #00B0BE;
|
||||||
|
--cxi-color: #C20019;
|
||||||
|
|
||||||
|
--accent-color: var(--tul-color);
|
||||||
|
|
||||||
|
--background-color: color-mix(in srgb, var(--accent-color) 5%, white);
|
||||||
|
--shadow-color: color-mix(in srgb, var(--accent-color), gray);
|
||||||
|
|
||||||
|
--button-color: var(--background-color);
|
||||||
|
--button-hover-color: white;
|
||||||
|
--button-border-color: color-mix(in srgb, var(--accent-color), white);
|
||||||
|
|
||||||
|
--button-accent-color: var(--accent-color);
|
||||||
|
--button-accent-hover-color: color-mix(in srgb, var(--accent-color), white 25%);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-faculty= "fs"] { --accent-color: var( --fs-color); }
|
||||||
|
[data-faculty= "ft"] { --accent-color: var( --ft-color); }
|
||||||
|
[data-faculty= "fp"] { --accent-color: var( --fp-color); }
|
||||||
|
[data-faculty= "ef"] { --accent-color: var( --ef-color); }
|
||||||
|
[data-faculty="fua"] { --accent-color: var(--fua-color); }
|
||||||
|
[data-faculty= "fm"] { --accent-color: var( --fm-color); }
|
||||||
|
[data-faculty="fzs"] { --accent-color: var(--fzs-color); }
|
||||||
|
[data-faculty="cxi"] { --accent-color: var(--cxi-color); }
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
font-family: "Noto Sans", sans-serif;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: var(--background-color);
|
||||||
|
|
||||||
|
transition: background-color 0.25s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-container {
|
||||||
|
padding: 24px;
|
||||||
|
width: 600px;
|
||||||
|
height: 500px;
|
||||||
|
|
||||||
|
box-shadow: 0 0 20px var(--shadow-color);
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 16px;
|
||||||
|
|
||||||
|
transition: box-shadow 0.25s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
opacity: 1;
|
||||||
|
transition: opacity 0.25s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content > * {
|
||||||
|
flex-grow: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.paragraphs {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.paragraphs > p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
a, a:visited, a:active {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.horizontal-list {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.horizontal-list > .filler {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
button, .button {
|
||||||
|
box-sizing: border-box;
|
||||||
|
flex-grow: 0;
|
||||||
|
padding: 8px 16px;
|
||||||
|
|
||||||
|
text-decoration: none;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 100%;
|
||||||
|
border: 1px var(--button-border-color) solid;
|
||||||
|
color: black;
|
||||||
|
background-color: var(--button-color);
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
transition: color 0.25s, background-color 0.25s, border 0.25s;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover, .button:hover {
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.narrow, .button.narrow {
|
||||||
|
padding: 4px 16px;
|
||||||
|
font-size: 16px;
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.expand, .button.expand {
|
||||||
|
flex-grow: 2;
|
||||||
|
flex-basis: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.accent, .button.accent {
|
||||||
|
color: white;
|
||||||
|
border: 1px var(--button-accent-color) solid;
|
||||||
|
background-color: var(--button-accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
button.accent:hover, .button.accent:hover {
|
||||||
|
background-color: var(--button-accent-hover-color);
|
||||||
|
border: 1px var(--button-accent-hover-color) solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:disabled, .button:disabled {
|
||||||
|
border: 1px gray solid;
|
||||||
|
background-color: lightgray;
|
||||||
|
color: black;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:disabled:hover, .button:disabled:hover {
|
||||||
|
border: 1px gray solid;
|
||||||
|
background-color: lightgray;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertical-spacer {
|
||||||
|
height: 16px
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertical-list {
|
||||||
|
flex-grow: 1;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-animation {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
opacity: 1;
|
||||||
|
left: 0;
|
||||||
|
|
||||||
|
transition: left 0.25s, opacity 0.25s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-from-left {
|
||||||
|
animation: 0.25s forwards from-left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-from-right {
|
||||||
|
animation: 0.25s forwards from-right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-to-left {
|
||||||
|
animation: 0.25s forwards to-left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-to-right {
|
||||||
|
animation: 0.25s forwards to-right;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes from-left {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
left: -16px;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes from-right {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
left: 16px;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes to-left {
|
||||||
|
from {
|
||||||
|
opacity: 1;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 0;
|
||||||
|
left: -16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes to-right {
|
||||||
|
from {
|
||||||
|
opacity: 1;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 0;
|
||||||
|
left: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="checkbox"] {
|
||||||
|
width: 16px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
flex-grow: 1;
|
||||||
|
text-align: left;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="text"] {
|
||||||
|
flex-grow: 0;
|
||||||
|
padding: 8px 16px;
|
||||||
|
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 100%;
|
||||||
|
border: 1px var(--button-border-color) solid;
|
||||||
|
color: black;
|
||||||
|
background-color: var(--button-color);
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="text"]::placeholder {
|
||||||
|
color: gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="text"]:disabled::placeholder {
|
||||||
|
color: lightgray;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="text"]:disabled {
|
||||||
|
background-color: white;
|
||||||
|
color: lightgray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.zmp-logo {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 20px;
|
||||||
|
height: 1em;
|
||||||
|
}
|
||||||
|
|
1
tul_prdel.svg
Normal file
1
tul_prdel.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 6.1 KiB |
255
ui.js
Normal file
255
ui.js
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
let tul_prdel = null;
|
||||||
|
|
||||||
|
function get_step(id) {
|
||||||
|
return steps[Object.keys(steps)[id]];
|
||||||
|
}
|
||||||
|
|
||||||
|
async function faculty_layout(content, result) {
|
||||||
|
if(!Object.keys(faculties).includes(result.name)) {
|
||||||
|
document.querySelector("#next-button").disabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let buttons = new ButtonListBuilder(Object.keys(faculties))
|
||||||
|
.should_be_pressed((key) => result.name == key)
|
||||||
|
.modify((b, key) => b.class("narrow").text(faculties[key].name))
|
||||||
|
.on_click((key) => {
|
||||||
|
result.name = key;
|
||||||
|
document.querySelector("#next-button").disabled = false;
|
||||||
|
document.documentElement.setAttribute("data-faculty", key);
|
||||||
|
})
|
||||||
|
.finish();
|
||||||
|
|
||||||
|
for(const button of buttons) {
|
||||||
|
content.appendChild(button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function thesis_layout(content, result) {
|
||||||
|
if(!Object.keys(theses).includes(result.type)) {
|
||||||
|
document.querySelector("#next-button").disabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let buttons = new ButtonListBuilder(Object.keys(theses))
|
||||||
|
.should_be_pressed((key) => result.type == key)
|
||||||
|
.modify((b, key) => b.class("narrow").text(theses[key].name))
|
||||||
|
.on_click((key) => {
|
||||||
|
result.type = key;
|
||||||
|
document.querySelector("#next-button").disabled = false;
|
||||||
|
})
|
||||||
|
.finish();
|
||||||
|
|
||||||
|
for(const button of buttons) {
|
||||||
|
content.appendChild(button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function author_info_layout(content, result) {
|
||||||
|
let author_name = new ElementBuilder("input")
|
||||||
|
.type("text")
|
||||||
|
.placeholder("Vaše jméno (vč. titulů), příp. jména všech autorů")
|
||||||
|
.on_input((e) => result.name = e.target.value)
|
||||||
|
.finish();
|
||||||
|
|
||||||
|
if(!Object.keys(genders).includes(result.gender)) {
|
||||||
|
document.querySelector("#next-button").disabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let gender_buttons = new ButtonListBuilder(Object.keys(genders))
|
||||||
|
.should_be_pressed((key) => result.gender == key)
|
||||||
|
.modify((b, key) => b.class("expand").text(genders[key].name))
|
||||||
|
.on_click((key) => {
|
||||||
|
result.gender = key;
|
||||||
|
document.querySelector("#next-button").disabled = false;
|
||||||
|
})
|
||||||
|
.finish();
|
||||||
|
|
||||||
|
content.appendChild(author_name);
|
||||||
|
content.appendChild(new ElementBuilder("div.vertical-spacer").finish());
|
||||||
|
content.appendChild(new ElementBuilder("div")
|
||||||
|
.text("Způsob sebeoslovení")
|
||||||
|
.finish());
|
||||||
|
content.appendChild(new ElementBuilder("div.horizontal-list")
|
||||||
|
.append_all(gender_buttons)
|
||||||
|
.finish());
|
||||||
|
}
|
||||||
|
|
||||||
|
async function collaborators_layout(content, result) {
|
||||||
|
let supervisor_name = new ElementBuilder("input")
|
||||||
|
.type("text")
|
||||||
|
.placeholder("Jméno vedoucího práce (vč. titulů), příp. vedoucích")
|
||||||
|
.on_input((e) => result.supervisor_name = e.target.value)
|
||||||
|
.finish();
|
||||||
|
|
||||||
|
let consultant_name = new ElementBuilder("input")
|
||||||
|
.type("text")
|
||||||
|
.placeholder("Jméno konzultanta práce (vč. titulů), příp. konzultantů")
|
||||||
|
.on_input((e) => result.supervisor_name = e.target.value)
|
||||||
|
.finish();
|
||||||
|
|
||||||
|
let has_supervisor = new ElementBuilder("input#has-supervisor")
|
||||||
|
.type("checkbox")
|
||||||
|
.checked(result.has_supervisor)
|
||||||
|
.on_click(() => update())
|
||||||
|
.finish();
|
||||||
|
|
||||||
|
let has_consultant = new ElementBuilder("input#has-consultant")
|
||||||
|
.type("checkbox")
|
||||||
|
.checked(result.has_consultant)
|
||||||
|
.on_click(() => update())
|
||||||
|
.finish();
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
supervisor_name.disabled = !has_supervisor.checked;
|
||||||
|
consultant_name.disabled = !has_consultant.checked;
|
||||||
|
|
||||||
|
result.has_supervisor = has_supervisor.checked;
|
||||||
|
result.has_consultant = has_consultant.checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
update();
|
||||||
|
|
||||||
|
content.appendChild(new ElementBuilder("div.horizontal-list")
|
||||||
|
.append(has_supervisor)
|
||||||
|
.append(new ElementBuilder("label")
|
||||||
|
.for("has-supervisor")
|
||||||
|
.text("Práce má vedoucího"))
|
||||||
|
.finish());
|
||||||
|
content.appendChild(supervisor_name);
|
||||||
|
content.appendChild(new ElementBuilder("div.vertical-spacer").finish());
|
||||||
|
content.appendChild(new ElementBuilder("div.horizontal-list")
|
||||||
|
.append(has_consultant)
|
||||||
|
.append(new ElementBuilder("label")
|
||||||
|
.for("has-consultant")
|
||||||
|
.text("Práce má konzultanta"))
|
||||||
|
.finish());
|
||||||
|
content.appendChild(consultant_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function run() {
|
||||||
|
startbutton.style.cursor = "auto";
|
||||||
|
startbutton.disabled = true;
|
||||||
|
|
||||||
|
let main_container = document.querySelector(".main-container");
|
||||||
|
let start_content = main_container.querySelector(".main-content");
|
||||||
|
start_content.style.opacity = 0;
|
||||||
|
|
||||||
|
await delay_ms(300);
|
||||||
|
|
||||||
|
start_content.remove();
|
||||||
|
|
||||||
|
let container = new ElementBuilder("div.vertical-list")
|
||||||
|
.style("justifyContent", "space-between")
|
||||||
|
.finish();
|
||||||
|
|
||||||
|
let back_button = new ElementBuilder("button#back-button")
|
||||||
|
.text("← Zpět")
|
||||||
|
.finish();
|
||||||
|
|
||||||
|
let next_button = new ElementBuilder("button.accent#next-button").finish();
|
||||||
|
|
||||||
|
let main_content = new ElementBuilder("div.main-content")
|
||||||
|
.style("opacity", 0)
|
||||||
|
.append(container)
|
||||||
|
.append(new ElementBuilder("div.horizontal-list")
|
||||||
|
.append(back_button)
|
||||||
|
.append(new ElementBuilder("div.filler"))
|
||||||
|
.append(next_button))
|
||||||
|
.finish();
|
||||||
|
|
||||||
|
main_container.appendChild(main_content);
|
||||||
|
await delay_ms(100);
|
||||||
|
main_content.style.opacity = 1;
|
||||||
|
|
||||||
|
let id = 0;
|
||||||
|
let animation = 0;
|
||||||
|
|
||||||
|
let steps_len = Object.keys(steps).length;
|
||||||
|
|
||||||
|
while(id < steps_len) {
|
||||||
|
back_button.style.display = (id != 0) ? "" : "none";
|
||||||
|
next_button.innerText = (id < steps_len - 1) ? "Další →" : "Dokončit";
|
||||||
|
next_button.disabled = false;
|
||||||
|
|
||||||
|
container.innerHTML = "";
|
||||||
|
|
||||||
|
let step = get_step(id);
|
||||||
|
|
||||||
|
let title = new ElementBuilder("h1")
|
||||||
|
.html(step.title)
|
||||||
|
.finish();
|
||||||
|
|
||||||
|
let content = new ElementBuilder("div.vertical-list")
|
||||||
|
.style("gap", "12px")
|
||||||
|
.finish();
|
||||||
|
|
||||||
|
let subcontainer = new ElementBuilder("div.vertical-list.slide-animation")
|
||||||
|
.append(title)
|
||||||
|
.append(content)
|
||||||
|
.if(animation < 0, c => c.class("slide-from-left"))
|
||||||
|
.if(animation > 0, c => c.class("slide-from-right"))
|
||||||
|
.finish();
|
||||||
|
|
||||||
|
await step.layout(content, step.result);
|
||||||
|
|
||||||
|
container.appendChild(subcontainer);
|
||||||
|
animation = 0;
|
||||||
|
|
||||||
|
let new_id = await new Promise((r) => {
|
||||||
|
back_button.onclick = () => {
|
||||||
|
r(id - 1);
|
||||||
|
}
|
||||||
|
next_button.onclick = () => {
|
||||||
|
r(id + 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if(new_id < steps_len) {
|
||||||
|
subcontainer.classList.add((new_id > id) ? "slide-to-left" : "slide-to-right");
|
||||||
|
await delay_ms(250);
|
||||||
|
subcontainer.remove();
|
||||||
|
animation = new_id - id;
|
||||||
|
}
|
||||||
|
|
||||||
|
id = new_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
main_content.style.opacity = 0;
|
||||||
|
let [_, out_blob] = await Promise.all([delay_ms(300), generate_zip()])
|
||||||
|
main_content.remove();
|
||||||
|
|
||||||
|
let color = window.getComputedStyle(document.body).getPropertyValue("--" + steps.faculty.result.name + "-color");
|
||||||
|
let logo = tul_prdel.replaceAll('fill="#5948ad"', 'fill="' + color + '"');
|
||||||
|
|
||||||
|
main_content = new ElementBuilder("div.main-content")
|
||||||
|
.style("opacity", 0)
|
||||||
|
.append(new ElementBuilder("h1").text("Vaše šablona je připravena!"))
|
||||||
|
.append(new ElementBuilder("img")
|
||||||
|
.src(URL.createObjectURL(new Blob([logo], { type: "image/svg+xml" })))
|
||||||
|
.style("width", "100px")
|
||||||
|
.style("height", "100px"))
|
||||||
|
.append(new ElementBuilder("div.paragraphs")
|
||||||
|
.append(new ElementBuilder("p").html(`
|
||||||
|
Po stažení archivu můžete šablonu rozbalit a
|
||||||
|
buď ji otevřít ve svém oblíbeném textovém editoru,
|
||||||
|
nebo ji nahrát do webového rozhraní Typstu.
|
||||||
|
`))
|
||||||
|
.append(new ElementBuilder("p").append(new ElementBuilder("a")
|
||||||
|
.text("Webové rozhraní Typstu otevřete kliknutím zde.")
|
||||||
|
.href("https://typst.app")
|
||||||
|
.target("_blank"))))
|
||||||
|
.append(new ElementBuilder("a.button.accent")
|
||||||
|
.href(URL.createObjectURL(out_blob))
|
||||||
|
.download("tul-thesis-typst.zip")
|
||||||
|
.text("Klikněte zde pro stažení archivu"))
|
||||||
|
.finish();
|
||||||
|
|
||||||
|
main_container.appendChild(main_content);
|
||||||
|
await delay_ms(100);
|
||||||
|
main_content.style.opacity = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.onload = async () => {
|
||||||
|
let img = await fetch("tul_prdel.svg"); // Should already be cached by now (hopefully)
|
||||||
|
tul_prdel = await img.text();
|
||||||
|
}
|
||||||
|
|
187
utils.js
Normal file
187
utils.js
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
class ElementBuilder {
|
||||||
|
constructor(tag) {
|
||||||
|
let parts = tag.split(/(\.|#)/);
|
||||||
|
|
||||||
|
if(parts.length % 2 == 0) {
|
||||||
|
throw new Error("invalid element builder tag string");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.element = document.createElement(parts[0]);
|
||||||
|
|
||||||
|
parts = parts.slice(1);
|
||||||
|
|
||||||
|
for(let i = 0; i < parts.length; i += 2) {
|
||||||
|
let type = parts[i];
|
||||||
|
let value = parts[i + 1];
|
||||||
|
|
||||||
|
switch(type) {
|
||||||
|
case ".":
|
||||||
|
this.element.classList.add(value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "#":
|
||||||
|
this.element.id = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need more stuff? Just implement it here.
|
||||||
|
|
||||||
|
id(id) {
|
||||||
|
this.element.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
class(clazz) {
|
||||||
|
for(const subclass of clazz.split(" ").filter(x => x !== "")) {
|
||||||
|
this.element.classList.add(subclass);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
text(text) {
|
||||||
|
this.element.innerText = text;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
html(html) {
|
||||||
|
this.element.innerHTML = html;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
on_click(handler) {
|
||||||
|
this.element.onclick = handler;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
on_input(handler) {
|
||||||
|
this.element.oninput = handler;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(fr) {
|
||||||
|
this.element.htmlFor = fr;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
src(src) {
|
||||||
|
this.element.src = src;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
target(target) {
|
||||||
|
this.element.target = target;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
href(href) {
|
||||||
|
this.element.href = href;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
download(download) {
|
||||||
|
this.element.download = download;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
type(type) {
|
||||||
|
this.element.type = type;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
checked(checked) {
|
||||||
|
this.element.checked = checked;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
placeholder(placeholder) {
|
||||||
|
this.element.placeholder = placeholder;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
append(element) {
|
||||||
|
if(element instanceof ElementBuilder) {
|
||||||
|
element = element.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.element.appendChild(element);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
append_all(elements) {
|
||||||
|
for(const element of elements) {
|
||||||
|
this.element.appendChild(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
style(name, value) {
|
||||||
|
this.element.style[name] = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(cond, handler) {
|
||||||
|
if(cond) handler(this);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
finish() {
|
||||||
|
return this.element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ButtonListBuilder {
|
||||||
|
constructor(list) {
|
||||||
|
this.list = list;
|
||||||
|
this.display_handler = null;
|
||||||
|
this.onclick_handler = null;
|
||||||
|
this.modify_handler = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
should_be_pressed(handler) {
|
||||||
|
this.display_handler = handler;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
on_click(handler) {
|
||||||
|
this.onclick_handler = handler;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
modify(handler) {
|
||||||
|
this.modify_handler = handler;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
finish() {
|
||||||
|
let buttons = this.list.map((key) => new ElementBuilder("button")
|
||||||
|
.if(this.modify_handler !== null, (b) => this.modify_handler(b, key))
|
||||||
|
.if(this.display_handler(key), (b) => b.class("accent"))
|
||||||
|
.finish()
|
||||||
|
);
|
||||||
|
|
||||||
|
for(let i = 0; i < buttons.length; i++) {
|
||||||
|
buttons[i].onclick = () => {
|
||||||
|
if(this.onclick_handler !== null) this.onclick_handler(this.list[i]);
|
||||||
|
|
||||||
|
for(let j = 0; j < buttons.length; j++) {
|
||||||
|
buttons[j].classList.remove("accent");
|
||||||
|
|
||||||
|
if(this.display_handler(this.list[j])) {
|
||||||
|
buttons[j].classList.add("accent");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return buttons;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function delay_ms(ms) {
|
||||||
|
await new Promise((r) => setTimeout(r, ms));
|
||||||
|
}
|
||||||
|
|
1
zumepro_dark_full.svg
Normal file
1
zumepro_dark_full.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 5.1 KiB |
1
zumepro_dark_mark.svg
Normal file
1
zumepro_dark_mark.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg height="14.863209mm" viewBox="0 0 44.41584 14.863209" width="44.41584mm" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd" transform="matrix(-.63629813 0 0 -.63629813 63.79826 74.07681)"><path d="m-11.759578-96.806604v-3.098032q-2.223016-.189194-3.500067-1.418944-1.253402-1.2534-1.3007-3.28723h2.128418q0 1.15881.685824 1.89193.709473.73312 1.986525.89867v-6.19607l-.638526-.18919q-1.773682-.54393-2.743295-1.82098-.945964-1.27705-.945964-3.00344 0-1.89192 1.158805-3.12168 1.182455-1.22975 3.16898-1.41894v-3.12168h1.418946v3.09803q1.9628749.18919 3.1453299 1.41894 1.182455 1.22976 1.2061041 3.16898h-2.128419q0-1.1115-.5912275-1.79733-.5675783-.70947-1.6317875-.87501v5.86497l.9459637.30744q1.7263842.54393 2.6723482 1.84463.945964 1.3007.945964 3.05073 0 1.93923-1.2534023 3.21628-1.2297532 1.27705-3.3108736 1.489894v3.098032zm-2.199367-16.412476q0 1.06421.567579 1.84463.567578.78042 1.631788 1.11151v-5.41564q-1.040561.16554-1.631788.80406-.567579.63853-.567579 1.65544zm3.618313 11.39887q1.1588056-.1892 1.7973312-.89867.6385257-.70947.6385257-1.84463 0-1.04056-.5675784-1.79733-.5439293-.78042-1.5844895-1.11151l-.283789-.0946z" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.0111" transform="matrix(-1.0225486 0 0 -.97794862 24.554623 -1.612364)"/><path d="m-59.312695-104.31899q-.273111.60084-.710088 1.11065-.436977.4916-1.001405.87395-.546222.36415-1.219894.58264-.673673.20028-1.438383.20028-.782917 0-1.584041-.21849-.801125-.2367-1.547627-.54622-.910369-.36415-1.747908-.69188-.819332-.32774-1.602249-.32774-.910368 0-1.511212.45519-.600843.43698-1.074235 1.20169l-1.857152-1.32914q.273111-.60085.710088-1.09244.436977-.50981.983198-.87396.564429-.38235 1.238101-.58263.673673-.21849 1.438383-.21849.892161 0 1.80253.29131.910368.29132 1.747908.63726.782917.32774 1.529419.60085.746502.2549 1.401968.2549.892161 0 1.511212-.43698.61905-.45518 1.074235-1.21989z" fill="#83d200" stroke-width="1.32731" transform="scale(-1)"/><path d="m100.26473 100.06705v-3.596477l-7.441251 7.192957 7.441251 7.19295v-3.59647l-3.720626-3.59648z"/></g></svg>
|
After Width: | Height: | Size: 2.0 KiB |
1
zumepro_light_full.svg
Normal file
1
zumepro_light_full.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 5.1 KiB |
Reference in New Issue
Block a user