Skip to content

Instantly share code, notes, and snippets.

@programminghoch10
Last active February 26, 2022 12:21
Show Gist options
  • Save programminghoch10/54f6a7b6b629b2da198f0343c1dfca25 to your computer and use it in GitHub Desktop.
Save programminghoch10/54f6a7b6b629b2da198f0343c1dfca25 to your computer and use it in GitHub Desktop.
TrainController AdvancedSplitView
<!doctype html>
<!--
TrainController Advanced SplitView
von Jonas <technik@modellbahn65.de>
(C) 2022
https://www.paypal.com/donate/?cmd=_donations&business=silentvortex10@gmail.com
Diese Datei ermöglicht einfaches Konfigureren von SplitViews in TrainController.
Innerhalb der Dokumentation werden an mehreren Stellen Platzhalter in Beispielen mit <> begrenzt.
Wenn also da steht
name="<beliebiger text>"
bedeutet das, dass nach dem Einsetzen da
name="Dies ist ein langweiliger Text"
steht.
Zum Testen des Layouts,
gehe ein wenig im Code herunter und setze die Variable TEST_MODE auf true.
Dann werden statt TrainController Ansichten nur zufällige Farben angezeigt,
welche das Einrichten eines neuen Layouts einfach machen.
Nach Konfiguration muss dieses Dokument in den Ordner
C:\Program Files (x86)\Railroad & Co.90\SmartHandCustom
platziert werden,
und im TrainController in der SmartHand Mobile Webserver Konfiguration
AdvancedSplitView (oder nach Umbenennen der neue Name der Datei)
ausgewählt werden.
V1 (1.2.2022) - Erste Version
V2 (4.2.2022) - Einrichten von Overviews ist jetzt noch einfacher, Knöpfe werden nun komplett automatisch erstellt
V3 (8.2.2022) - Weitere voreingestelle Overviews, coole Previews zum Auswählen und performantere Farben
V4 (11.2.2022) - Mehrere kleine Bugfixes
V5 (18.2.2022) - Merken einer Ansicht pro Gerät mit Cookies möglich, konfigurierbare Farben, schönere Aufteilung der Auswahl
V6 (18.2.2022) - Wesentliche Verbesserung in Ladezeit, Design und Adaptivität
V7 (22.2.2022) - Namen von iFrames entfernt, "Ansicht merken" standardmäßig versteckt, Zoom hinzugefügt
-->
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
:root {
/* Zoom Level der Auswahl */
--zoom-level: 1.0
}
body {
background-color: black;
color: white;
}
.overview {
position: fixed;
top: 0px;
left: 0px;
width: 100%;
height: 100%;
display: none;
}
.overview.show {
display: unset;
}
iframe {
width: 100%;
height: 100%;
margin: 0;
overflow: hidden;
border: hidden;
}
.overview div, .preoverview div {
display: flex;
width: 100%;
height: 100%;
}
.overview .horizontal, .preoverview div.horizontal {
flex-direction: row;
}
.overview .vertical, .preoverview div.vertical {
flex-direction: column;
}
div#selector {
margin-top: 2%;
user-select: none;
}
div#selector h1 {
margin-block: 0;
margin-top: 5%;
margin-bottom: 1%;
text-align: center;
position: sticky;
top: 0;
width: 100%;
background-color: #000a;
}
#previewbuttons {
width: 100%;
height: 100%;
display: flex;
flex-wrap: wrap;
justify-content: space-evenly;
flex-direction: row;
margin-bottom: 1rem;
}
.preview {
margin: 1%;
border: 1px solid white;
height: 100%;
width: calc(25vw * var(--zoom-level));
min-width: calc(25vh * var(--zoom-level));
}
.preoverview {
position: relative;
width: 100%;
height: calc(25vh * var(--zoom-level));
min-height: calc(10vw * var(--zoom-level));
z-index: -1;
border-bottom: 1px solid #333;
}
.preview p {
margin: 0;
text-align: center;
}
.preview .clickableoverlay {
position: absolute;
width: calc(25vw * var(--zoom-level));
height: calc(25vh * var(--zoom-level));
}
div#selector div#rememberdiv {
margin-inline: 2%;
text-align: center;
}
div#selector div#credit {
width: 100%;
position: fixed;
bottom: 0;
color: grey;
}
div#selector div#credit p {
text-align: center;
margin: 0;
background-color: #000a;
}
label#rememberwarning {
color: red
}
.noscript * {
margin: 0;
text-align: center;
}
.noscript h1 {
color: red;
}
</style>
<script>
// wenn der TEST_MODE auf true gesetzt wird, dann wird statt TrainController in jedem iframe eine zufällige Farbe angezeigt
var TEST_MODE = false
// wenn HIDE_CREDIT auf true gesetzt wird, dann wird die Info Zeile ganz unten ausgeblendet
const HIDE_CREDIT = false
// wenn ENABLE_REMEMBER auf true gesetzt wird, dann wird die Option zum merken einer Ansicht angezeigt
const ENABLE_REMEMBER = false
// der Titel der Auswahl
const SELECT_TITLE = "AdvancedSplitView"
// wie lange die cookies gespeichert werden, also wie lange ein "ansicht merken" erhalten bleibt, in Tagen
const cookiesavedays = 365
// welche Farben für die Vorschau benutzt werden, wenn leer werden zufällige Farben benutzt, nur eines auswählen!
// es sollten mindestens so viele Farben da sein wie die größte Overview Unterteilungen hat
//const colors = ['#f87171', '#fbbf24', '#a3e635', '#34d399', '#22d3ee', '#60a5fa', '#a78bfa', '#e879f9', '#fb7185'] //tailwind 400 (hell), 9 abstufungen
//const colors = ['#dc2626', '#d97706', '#65a30d', '#059669', '#0891b2', '#2563eb', '#7c3aed', '#c026d3', '#e11d48'] //tailwind 600 (dunkel), 9 abstufungen
//const colors = ['#f87171', '#facc15', '#34d399', '#38bdf8', '#a78bfa', '#f472b6'] //tailwind 400 (hell), 6 abstufungen
//const colors = ['#dc2626', '#ca8a04', '#059669', '#0284c7', '#7c3aed', '#db2777'] //tailwind 600 (dunkel), 6 abstufungen
//const colors = ['#ef4444', '#eab308', '#10b981', '#0ea5e9', '#8b5cf6', '#ec4899'] //tailwind 500 (kräftig), 6 abstufungen
const colors = ['#ef4444', '#f59e0b', '#84cc16', '#10b981', '#3b82f6', '#8b5cf6'] //tailwind 500 (kräftig), 6/9 abstufungen (selektiert)
//const colors = ['#f87171', '#facc15', '#34d399', '#38bdf8', '#a78bfa', '#b91c1c', '#a16207', '#047857', '#0369a1', '#6d28d9', '#be185d'] //tailwind 400/700, 11/12 abstufungen (selektiert)
//const colors = ["#F00", "#0F0", "#00F", "#FF0", "#0FF", "#F0F"] //starke farben
//const colors = ["#EEE", "#CCC", "#999", "#666", "#333", "#111"] //grauskala
//const colors = [] //zufällige farben
const remembercookiename = "rememberoverview"
const selectoverviewsearchparam = "view"
const resetremembercookiesearchparam = "select"
function getURL(viewId, iFrame) {
if (!iFrame) throw `iframe ${iFrame} is invalid`
if (TEST_MODE) return getColorURI()
let iFrameList = getIFramesInView(viewId)
let iFrameCounter = Object.values(iFrameList).findIndex(i => i === iFrame) + 1
let config = getAttributes(iFrame)
if (config["src"]) return config["src"]
let u = config["u"] ? `&u=${config["u"]}` : ""
// supply t (transfer) if this is a stellwerk and there is a train frame elsewhere
let t = config["i"] === "s" && Object.values(iFrameList).find(i => i.getAttribute("i") == "t") ? "&t=1" : ""
let i = config["i"] ? `&i=${config["i"]}` : ""
return `index.htm?c=0${i}&w=${iFrameCounter}${t}${u}`
}
function query() {return document.querySelector(...arguments)}
function queryAll() {return document.querySelectorAll(...arguments)}
function getURLSearchParam(param) {return new URLSearchParams(document.location.search).get(param)}
function getIFramesInView(viewId) {return queryAll(`div.overview#${viewId} iframe`)}
//function getIFrameByName(viewId, iFrameName) {return query(`div.overview#${viewId} iframe[name='${iFrameName}']`)}
function getAttributes(htmlElement) {
let result = {}
Object.values(htmlElement.attributes).forEach(value => result[value.nodeName] = value.nodeValue)
return result
}
function setAttributes(htmlElement, attributes) {
Object.keys(attributes).forEach(key => {
htmlElement.setAttribute(key, attributes[key])
})
}
// cookie scripts imported from https://github.com/programminghoch10/tictactoesquared/blob/master/docs/scripts/cookie.js
function getCookie(name) {
var name = name + "="
var decodedCookie = decodeURIComponent(document.cookie)
var ca = decodedCookie.split(";")
for (var i = 0; i < ca.length; i++) {
var c = ca[i]
while (c.charAt(0) == " ") {
c = c.substring(1)
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length)
}
}
return ""
}
function setCookie(name, value) {
var d = new Date()
d.setTime(d.getTime() + cookiesavedays * 24 * 60 * 60 * 1000)
var expires = "expires=" + d.toUTCString()
document.cookie = name + "=" + value + ";" + expires + ";path=/"
}
function deleteCookie(name) { setCookie(name, "") }
function getAutoSelectOverview() {
if (getURLSearchParam(selectoverviewsearchparam)) return getURLSearchParam(selectoverviewsearchparam)
if (getURLSearchParam(resetremembercookiesearchparam)) deleteCookie(remembercookiename)
if (!ENABLE_REMEMBER) deleteCookie(remembercookiename)
if (getCookie(remembercookiename)) return getCookie(remembercookiename)
}
function onBodyLoad() {
document.title = SELECT_TITLE
if (getURLSearchParam("colors")) TEST_MODE = true
if (HIDE_CREDIT) query("div#credit").style.display = "none"
setupIFrames()
if (getAutoSelectOverview()) {
selectOverView(getAutoSelectOverview())
} else {
setupButtons()
query("div#selector").style.display = ""
}
}
function selectOverView(viewId) {
unloadall()
saveRememberOverView(viewId)
let view = query(`div.overview#${viewId}`)
if (!view) throw `overview ${viewId} not found`
let iFrameList = getIFramesInView(viewId)
iFrameList.forEach(iFrame => {
iFrame.src = getURL(viewId, iFrame)
})
view.classList.add("show")
query("div#selector").style.display = "none"
document.title = view.title ?? view.id
}
function saveRememberOverView(viewId) {
if (!ENABLE_REMEMBER) return
let remember = query("input#remember").checked
if (!remember) return
setCookie(remembercookiename, viewId)
}
function unloadall() {
queryAll("iframe").forEach(iFrame => iFrame.src = "")
}
function setupIFrames() {
let overviewCounter = 1
queryAll("div.overview").forEach(overview => {
let config = getAttributes(overview)
config.title = config.title ?? "Fenster " + overviewCounter
config.id = "overview" + overviewCounter
config.button = config.button ?? config.title
config.scrolling = "no"
setAttributes(overview, config)
overviewCounter++
})
}
function setupButtons() {
let previewbuttondiv = query("div#selector div#previewbuttons")
queryAll("div.overview").forEach(overview => {
let config = getAttributes(overview)
let previewdiv = document.createElement("div")
previewdiv.classList.add("preview")
previewdiv.onclick = () => { selectOverView(config.id) }
let clickablediv = document.createElement("div")
clickablediv.classList.add("clickableoverlay")
previewdiv.appendChild(clickablediv)
let previewframe = overview.cloneNode(true)
previewframe.classList.remove("overview")
previewframe.classList.add("preoverview")
previewframe.querySelectorAll("iframe").forEach(iFrame => {
iFrame.src = getColorURI(config.id)
iFrame.title = "Preview " + config.title
})
previewdiv.appendChild(previewframe)
let previewtext = document.createElement("p")
previewtext.innerText = config.button
previewdiv.appendChild(previewtext)
previewbuttondiv.appendChild(previewdiv)
})
if (!ENABLE_REMEMBER) query("div#rememberdiv").style.display = "none"
query("h1#selecttitle").innerText = SELECT_TITLE
}
function getColorURI(scope) {
let color = getColor(scope)
let svg = `<svg xmlns="http://www.w3.org/2000/svg" style="background: ${color}"></svg>`
return `data:image/svg+xml;base64,${btoa(svg)}`
}
var usedColors = {}
function getColor(scope) {
if (colors.length == 0) return getRandomColor()
if (!usedColors[scope]) usedColors[scope] = []
let availableColors = colors.filter(c => !usedColors[scope].includes(c))
if (availableColors.length == 0) availableColors = colors
let randomSelection = Math.floor(Math.random() * availableColors.length)
usedColors[scope].push(availableColors[randomSelection])
return availableColors[randomSelection]
}
function getRandomColor() {
//const letters = '0123456789ABCDEF'
const letters = '3579BD'
var color = '#'
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * letters.length)]
}
console.log("random generated color", color)
return color
}
function updateRememberWarning() {
let rememberwarning = query("label#rememberwarning")
let rememberbutton = query("input#remember")
let showWarning = rememberbutton.checked
rememberwarning.style.display = showWarning ? '' : 'none'
}
</script>
</head>
<body onload="onBodyLoad()">
<noscript>
<div class="noscript">
<h1>Du brauchst JavaScript!</h1>
<p>AdvancedSplitView funktioniert nicht ohne JavaScript.</p>
<p>Du brauchst das aber für TrainController sowieso...</p>
<p>Warum ist es also nicht aktiviert?</p>
</div>
</noscript>
<div id="selector" style="display: none;">
<h1 id="selecttitle"></h1>
<div id="rememberdiv">
<input type="checkbox" id="remember" onchange="updateRememberWarning()">
<label for="remember">Gewählte Ansicht merken</label>
<label for="remember" id="rememberwarning" style="display: none;">
<br>
Hiermit wird die Auswahl in Zukunft überspungen und die ausgewählte Ansicht automatisch ausgewählt!
</label>
</div>
<div id="previewbuttons"></div>
<div id="credit">
<p>AdvancedSplitView - (C) Jonas 2022</p>
</div>
</div>
<!--
Hier die verschiendenen Ansichten (Overviews) konfigureren.
Das Hauptelement einer Overview braucht class="overview"
sowie title="<beliebiger Text>" welcher als Seitentitel benutzt wird.
Auf dem Knopf steht standardmäßig der Seitentitel, sollte dies nicht gewünscht sein,
kann man diesen mit button="<beliebiger Text>" überschreiben
Ein div einer Overview sieht also am Ende so aus:
<div class="overview" id="<id>" title="<Seitentitel>" button="<Beschriftung des Knopfes>">
Jedes iframe in einer Overview benötigt
i="s" für Stellwerke
oder
i="t" für Führerstände
Es kann ein Benutzer mit u="<name des benutzers>" angegeben werden.
Die Parameter i und u funktionieren so wie in der TrainController Anleitung beschrieben.
Alle anderen Parameter werden automatisch verwaltet, diese müssen nicht angegeben werden.
Im Notfall kann man immernoch selber ein src="<url>" angeben, davon rate ich aber ab.
Ein iframe sieht also am Ende so aus:
<iframe i="<s oder t>" u="<Benutzername>"></iframe>
Zum anordnen von iframes mit divs stehen einige Tools zur Verfügung:
Horizontal:
<div class="horizontal">
<div style="width: 50%">...</div>
<div style="width: 50%">...</div>
</div>
Vertikal:
<div class="vertical">
<div style="height: 60%">...</div>
<div style="height: 20%">...</div>
<div style="height: 20%">...</div>
</div>
Die Overview hat immer eine Vertikale Anordnung,
sollte direkt eine nur horizontale Teilung gewünscht sein,
dann muss ein <div class="horizontal">...</div> eingefügt werden.
Die Dimensionen sollten so gewählt sein, dass am Ende alles auf 100% herausläuft.
Mehr als 100% resultieren in einer Scollbar und weniger als 100% in leeren schwarzen Löchern.
Eine Overview kann beim öffnen der Website "automatisch" ausgewählt werden durch Aufruf mit dem Parameter "view":
http://<ip adresse>/?view=<id der overview>
Die ID setzt sich zusammen aus overview + aufsteigende Nummer der Overviews.
Wenn ENABLE_REMEMBER aktiviert ist, kann man ein "Ansicht merken" zurücksetzen, indem man einfach die Website einmal mit dem Parameter "select" aufruft:
http://<ip adresse>/?select=1
Durch deaktivieren von ENABLE_REMEMBER wird die Auswahl beim nächsten Laden ebenfalls zurückgesetzt.
Diese Datei kommt mit einer schon existierenden Konfiguration.
Am meisten lernt man durch analysieren, verstehen und geschicktes kopieren.
Viel Glück!
-->
<div class="overview" title="2 Loks + Stellwerk">
<div class="horizontal">
<div style="width: 25%;" class="vertical">
<div style="height: 50%;">
<iframe i="t"></iframe>
</div>
<div style="height: 50%;">
<iframe i="t"></iframe>
</div>
</div>
<div style="width: 75%;">
<iframe i="s"></iframe>
</div>
</div>
</div>
<div class="overview" title="5 Loks + Stellwerk">
<div class="vertical">
<div style="height: 40%;" class="horizontal">
<div style="width: 20%;">
<iframe i="t"></iframe>
</div>
<div style="width: 20%;">
<iframe i="t"></iframe>
</div>
<div style="width: 20%;">
<iframe i="t"></iframe>
</div>
<div style="width: 20%;">
<iframe i="t"></iframe>
</div>
<div style="width: 20%;">
<iframe i="t"></iframe>
</div>
</div>
<div style="height: 60%;">
<iframe i="s"></iframe>
</div>
</div>
</div>
<div class="overview" title="2 Stellwerke Horizontal">
<div class="horizontal">
<div style="width: 50%;">
<iframe i="s"></iframe>
</div>
<div style="width: 50%;">
<iframe i="s"></iframe>
</div>
</div>
</div>
<div class="overview" title="2 Stellwerke Vertikal">
<div class="vertical">
<div style="height: 50%;">
<iframe i="s"></iframe>
</div>
<div style="height: 50%;">
<iframe i="s"></iframe>
</div>
</div>
</div>
<div class="overview" title="Vollbild Stellwerk">
<iframe i="s"></iframe>
</div>
</body>
</html>
@programminghoch10
Copy link
Author

Reserved

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment