diff --git a/index.php b/index.php
index ca9a112..bd21837 100644
--- a/index.php
+++ b/index.php
@@ -75,180 +75,7 @@ function print_month($start) {
Soli-Bus-Fahrten nach Lützerath
-
+
Soli-Bus-Fahrten von Berlin nach Lützerath
@@ -277,94 +104,7 @@ print_month($month_end);
?>
-
+
Tag X
Kontaktmöglichkeiten
diff --git a/script.js b/script.js
new file mode 100644
index 0000000..637366a
--- /dev/null
+++ b/script.js
@@ -0,0 +1,84 @@
+"use strict"
+
+const months = document.getElementById('months')
+let selecting = null // { origin, className, range }
+let is_select = false
+
+months.addEventListener("pointerdown", event => {
+ selecting = null
+ is_select = false
+ for (const elem of document.querySelectorAll(".selecting, .unselecting")) {
+ elem.className = ""
+ }
+
+ if (event.buttons !== 1) return
+ let target = event.target
+ if (target.nodeType == 3) target = target.parentNode
+ if (target.tagName !== "LABEL") return
+
+ event.preventDefault()
+
+ const className = target.className = document.getElementById(target.htmlFor).checked ? "unselecting" : "selecting"
+ selecting = {
+ origin: target,
+ className,
+ range: [ target ]
+ }
+})
+
+months.addEventListener('pointermove', event => {
+ if (selecting === null) return
+ event.preventDefault()
+ is_select = true
+
+ const range = document.createRange()
+ range.selectNode(selecting.origin)
+ if (range.comparePoint(event.target, 0) > 0) range.setEndAfter(event.target)
+ else range.setStartBefore(event.target)
+
+ const treeWalker = document.createTreeWalker(
+ range.commonAncestorContainer,
+ NodeFilter.SHOW_ELEMENT,
+ { acceptNode: (node) => node.nodeName.toLowerCase() === 'label' ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP }
+ )
+
+ const new_range = []
+ let currentNode
+ while (currentNode = treeWalker.nextNode()) {
+ if (range.intersectsNode(currentNode)) {
+ new_range.push(currentNode)
+ const idx = selecting.range.indexOf(currentNode)
+ if (idx !== -1) selecting.range.splice(idx, 1)
+ else currentNode.className = selecting.className
+ }
+ }
+ for (const elem of selecting.range) {
+ elem.className = ""
+ }
+ selecting.range = new_range
+})
+
+document.addEventListener('pointercancel', event => {
+ if (selecting === null) return
+ for (const elem of selecting.range) {
+ elem.className = ""
+ }
+ selecting = null
+ is_select = false
+})
+
+document.addEventListener('pointerup', event => {
+ if (selecting === null) return
+ event.preventDefault()
+ for (const elem of selecting.range) {
+ if (is_select) document.getElementById(elem.htmlFor).checked = selecting.className == "selecting"
+ elem.className = ""
+ }
+ selecting = null
+})
+months.addEventListener('click', event => {
+ if (is_select) {
+ event.preventDefault()
+ is_select = false
+ }
+})
diff --git a/style.css b/style.css
new file mode 100644
index 0000000..74b4887
--- /dev/null
+++ b/style.css
@@ -0,0 +1,174 @@
+/* LAYOUT */
+body {
+ margin: 0
+}
+footer {
+ border-top: 0.3em dashed #722CA2;
+ padding: 1em 1em 0 1em;
+ background: #f1cc37;
+ color: #5A4B0B;
+ padding: 1em 0
+}
+main {
+ background: #FFE98E;
+ padding: 1em 0
+}
+header {
+ border-bottom: 0.3em dashed #722CA2;
+ background: #f1cc37;
+ color: #5A4B0B;
+ padding: 1em 0
+}
+h1 {
+ margin: 0
+}
+footer > *,
+header > *,
+main > * {
+ margin: 0 auto;
+ max-width: 80rem;
+ padding: 0 1rem
+}
+
+/* CALENDAR */
+.month {
+ display: inline-block;
+ vertical-align: top;
+ margin-right: 2em;
+ border-spacing: 0
+}
+.month tbody {
+ border-radius: 0.3em;
+ color: #5A4B0B;
+ box-shadow: .4em .5em 0 0 RGBA(114, 44, 162,0.5)
+}
+.month th:first-of-type {
+ border-top-left-radius: 0.3em
+}
+.month th:last-of-type {
+ border-top-right-radius: 0.3em
+}
+.month tr > :last-of-type {
+ border-right-width: 1px
+}
+.month tr:last-of-type td:first-of-type {
+ border-bottom-left-radius: 0.3em
+}
+.month tr:last-of-type td:last-of-type {
+ border-bottom-right-radius: 0.3em
+}
+.month tr:last-of-type td {
+ border-bottom-width: 1px
+}
+
+/* date in calendar */
+.month th, .month td {
+ width: 3.5em;
+ height: 3.5em;
+ border: 1px solid #CBC19A;
+ border-width: 1px 0 0 1px;
+ text-align: center;
+ padding: 0;
+ background: white;
+ box-sizing: border-box
+}
+.month input[type=checkbox] {
+ appearance: none;
+ position: absolute
+}
+.month label {
+ display: block;
+ line-height: 3.5em
+}
+.month :focus + label {
+ border: 1px solid #722CA2;
+ margin: -1px
+}
+:checked + label:not(.unselecting),
+.selecting {
+ background: #f1cc37
+}
+.invalid {
+ color: #CBC19A
+}
+
+/* calendar on smaller screens */
+@media only screen and (max-width: 55em) {
+ .month {
+ margin-right: 1.5vw
+ }
+ .month th, .month td {
+ width: 6vw
+ }
+}
+@media only screen and (max-width: 40em) {
+ .month {
+ margin: 0 auto;
+ display: table
+ }
+ .month th, .month td {
+ width: 3.5em
+ }
+}
+
+/* events in calendar */
+.event {
+ position: relative;
+ top: -3.5em;
+ right: -0.9em;
+ line-height: initial;
+ display: block
+}
+.event::before {
+ content: "★";
+ position: absolute;
+ padding: 0.5em;
+ top: -0.5em;
+ right: 0.5em
+}
+.event-content {
+ visibility: hidden;
+ position: absolute;
+ background: white;
+ top: 0.4em;
+ left: 1.8em;
+ padding: 0.4em;
+ border: 1px solid #CBC19A;
+ border-radius: 0.2em;
+ box-shadow: .2em .25em 0 0 RGBA(114, 44, 162,0.5);
+ z-index: 1
+}
+.month :focus + label .event-content,
+.month td:hover .event-content,
+.month td:active .event-content {
+ visibility: initial
+}
+
+/* OTHER FORM ELEMENTS */
+.row {
+ display: block;
+ margin: 1em 0
+}
+.row > input,
+.row > select {
+ padding: 0.5rem;
+ margin-right: 1rem;
+ width: 12rem;
+ box-sizing: border-box;
+ border: 1px solid #5A4B0B;
+ border-radius: .3em;
+ box-shadow: .4em .5em 0 0 RGBA(114, 44, 162,0.5)
+}
+.row > input[type="number"] {
+ margin-left: 8.5rem;
+ width: 3.5rem
+}
+.row > input[type="submit"] {
+ background: #f1cc37
+}
+.row > input[type="submit"]:active {
+ background: white
+}
+.row :focus-visible {
+ outline: 0.2em #722CA2 solid
+}