Redesign UI and add first-party Docker runtime support
This commit is contained in:
11
.dockerignore
Normal file
11
.dockerignore
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
.git
|
||||||
|
.github
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
docker-data
|
||||||
|
|
||||||
|
README-DEV.md
|
||||||
|
changelog-beta.md
|
||||||
|
|
||||||
|
*.log
|
||||||
|
*.tmp
|
||||||
42
Dockerfile
Normal file
42
Dockerfile
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# syntax=docker/dockerfile:1.7
|
||||||
|
|
||||||
|
FROM --platform=$BUILDPLATFORM golang:1.22-alpine AS builder
|
||||||
|
WORKDIR /src
|
||||||
|
|
||||||
|
RUN apk add --no-cache ca-certificates tzdata
|
||||||
|
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
RUN go mod download
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
ARG TARGETOS
|
||||||
|
ARG TARGETARCH
|
||||||
|
RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH \
|
||||||
|
go build -trimpath -ldflags='-s -w' -o /out/xteve ./xteve.go
|
||||||
|
|
||||||
|
FROM alpine:3.20
|
||||||
|
|
||||||
|
RUN apk add --no-cache ca-certificates tzdata \
|
||||||
|
&& addgroup -S xteve \
|
||||||
|
&& adduser -S -G xteve xteve \
|
||||||
|
&& mkdir -p /xteve/config \
|
||||||
|
&& chown -R xteve:xteve /xteve
|
||||||
|
|
||||||
|
WORKDIR /xteve
|
||||||
|
|
||||||
|
COPY --from=builder /out/xteve /usr/local/bin/xteve
|
||||||
|
COPY docker/entrypoint.sh /usr/local/bin/docker-entrypoint.sh
|
||||||
|
|
||||||
|
USER xteve
|
||||||
|
|
||||||
|
EXPOSE 34400/tcp
|
||||||
|
VOLUME ["/xteve/config"]
|
||||||
|
|
||||||
|
ENV XTEVE_CONFIG=/xteve/config
|
||||||
|
ENV XTEVE_PORT=34400
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
|
||||||
|
CMD wget -qO- "http://127.0.0.1:${XTEVE_PORT}/lineup_status.json" > /dev/null || exit 1
|
||||||
|
|
||||||
|
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
|
||||||
54
README.md
54
README.md
@@ -48,6 +48,59 @@ Documentation for setup and configuration is [here](https://github.com/xteve-pro
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Project Analysis (UI + Operations)
|
||||||
|
|
||||||
|
The core architecture is strong: a Go backend with websocket-driven UI updates, filesystem-based state, and very low runtime overhead.
|
||||||
|
The weakest points are mostly operational and UX-focused:
|
||||||
|
|
||||||
|
* UI was historically utility-first and desktop-biased, with limited responsive behavior and visual hierarchy.
|
||||||
|
* Container usage was documented externally but there was no first-party Dockerfile/compose setup in this repository.
|
||||||
|
* Static web assets are generated into `src/webUI.go`, which works, but creates large diffs and a heavier edit/build cycle.
|
||||||
|
|
||||||
|
### Recommended next technical improvements
|
||||||
|
|
||||||
|
1. Replace generated `src/webUI.go` with Go `embed` for simpler static asset management and cleaner PR diffs.
|
||||||
|
2. Add CI checks (`go test ./...`, build on Linux/arm64/amd64, docker build smoke test).
|
||||||
|
3. Add a dedicated health endpoint (for example `/healthz`) to decouple health checks from HDHomeRun endpoints.
|
||||||
|
4. Add integration tests around websocket commands that mutate settings/files to reduce regression risk.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Container-First Run (Included In This Repo)
|
||||||
|
|
||||||
|
### Build image
|
||||||
|
```bash
|
||||||
|
docker build -t xteve:local .
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run with Docker Compose (bridge mode)
|
||||||
|
```bash
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
Compose file: `docker-compose.yml`
|
||||||
|
Persistent config volume: `./docker-data/config:/xteve/config`
|
||||||
|
|
||||||
|
### Run with Docker Compose (host networking, Linux recommended for discovery)
|
||||||
|
```bash
|
||||||
|
docker compose -f docker-compose.host.yml up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
Host networking improves LAN discovery behavior (SSDP/DLNA) for Plex/Emby in many setups.
|
||||||
|
|
||||||
|
### Container environment variables
|
||||||
|
|
||||||
|
* `XTEVE_CONFIG` (default: `/xteve/config`)
|
||||||
|
* `XTEVE_PORT` (default: `34400`)
|
||||||
|
|
||||||
|
### Image details
|
||||||
|
|
||||||
|
* Multi-stage build (Go builder + minimal Alpine runtime)
|
||||||
|
* Runs as non-root user (`xteve`)
|
||||||
|
* Built-in healthcheck against `http://127.0.0.1:${XTEVE_PORT}/lineup_status.json`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Downloads v2 | 64 Bit only
|
## Downloads v2 | 64 Bit only
|
||||||
#### 64 Bit Intel / AMD
|
#### 64 Bit Intel / AMD
|
||||||
|
|
||||||
@@ -156,4 +209,3 @@ var GitHub = GitHubStruct{Branch: "master", User: "xteve-project", Repo: "xTeVe-
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
13
docker-compose.host.yml
Normal file
13
docker-compose.host.yml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
services:
|
||||||
|
xteve:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: xteve
|
||||||
|
restart: unless-stopped
|
||||||
|
network_mode: host
|
||||||
|
environment:
|
||||||
|
XTEVE_CONFIG: /xteve/config
|
||||||
|
XTEVE_PORT: "34400"
|
||||||
|
volumes:
|
||||||
|
- ./docker-data/config:/xteve/config
|
||||||
15
docker-compose.yml
Normal file
15
docker-compose.yml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
services:
|
||||||
|
xteve:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: xteve
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
XTEVE_CONFIG: /xteve/config
|
||||||
|
XTEVE_PORT: "34400"
|
||||||
|
ports:
|
||||||
|
- "34400:34400/tcp"
|
||||||
|
- "1900:1900/udp"
|
||||||
|
volumes:
|
||||||
|
- ./docker-data/config:/xteve/config
|
||||||
9
docker/entrypoint.sh
Executable file
9
docker/entrypoint.sh
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
CONFIG_DIR="${XTEVE_CONFIG:-/xteve/config}"
|
||||||
|
PORT="${XTEVE_PORT:-34400}"
|
||||||
|
|
||||||
|
mkdir -p "${CONFIG_DIR}"
|
||||||
|
|
||||||
|
exec /usr/local/bin/xteve -config "${CONFIG_DIR}" -port "${PORT}" "$@"
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
<script language="javascript" type="text/javascript" src="js/base_ts.js"></script>
|
<script language="javascript" type="text/javascript" src="js/base_ts.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body onload="javascript: readyForConfiguration(0);">
|
<body class="auth-screen wizard-screen" onload="javascript: readyForConfiguration(0);">
|
||||||
|
|
||||||
<div id="loading" class="block">
|
<div id="loading" class="block">
|
||||||
<div class="loader"></div>
|
<div class="loader"></div>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
<script language="javascript" type="text/javascript" src="js/authentication_ts.js"></script>
|
<script language="javascript" type="text/javascript" src="js/authentication_ts.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body class="auth-screen">
|
||||||
|
|
||||||
<div id="header" class="imgCenter"></div>
|
<div id="header" class="imgCenter"></div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,241 +1,293 @@
|
|||||||
|
:root {
|
||||||
|
--bg-0: #07111d;
|
||||||
|
--bg-1: #0c1a2b;
|
||||||
|
--bg-2: #12233a;
|
||||||
|
--panel: #102238;
|
||||||
|
--panel-soft: #0f1f33;
|
||||||
|
--line: #274462;
|
||||||
|
--line-soft: #1b334d;
|
||||||
|
--text: #e9f5ff;
|
||||||
|
--text-muted: #9db5cb;
|
||||||
|
--accent: #35d2ff;
|
||||||
|
--accent-strong: #12b9ff;
|
||||||
|
--accent-soft: rgba(53, 210, 255, 0.2);
|
||||||
|
--ok: #2fd18a;
|
||||||
|
--warn: #ffc15a;
|
||||||
|
--error: #ff6f6f;
|
||||||
|
--radius-s: 10px;
|
||||||
|
--radius-m: 14px;
|
||||||
|
--radius-l: 18px;
|
||||||
|
--shadow-1: 0 20px 45px rgba(0, 0, 0, 0.32);
|
||||||
|
--shadow-2: 0 12px 30px rgba(0, 0, 0, 0.28);
|
||||||
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
-moz-appearance: none;
|
-moz-appearance: none;
|
||||||
-ms-appearance: none;
|
-ms-appearance: none;
|
||||||
font-family: "Arial", sans-serif;
|
box-sizing: border-box;
|
||||||
letter-spacing: 2px;
|
font-family: "Space Grotesk", "Avenir Next", "Trebuchet MS", sans-serif;
|
||||||
|
letter-spacing: 0.02em;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 12px;
|
width: 11px;
|
||||||
height: 12px;
|
height: 11px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
::-webkit-scrollbar-track {
|
::-webkit-scrollbar-track {
|
||||||
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
|
border-radius: 999px;
|
||||||
border-radius: 5px;
|
background: rgba(7, 17, 29, 0.65);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb {
|
::-webkit-scrollbar-thumb {
|
||||||
border-radius: 5px;
|
border-radius: 999px;
|
||||||
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0,0.6);
|
border: 2px solid rgba(7, 17, 29, 0.6);
|
||||||
background-color: #444;
|
background: linear-gradient(180deg, #2d597f, #1e4060);
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb:hover {
|
::-webkit-scrollbar-thumb:hover {
|
||||||
background: #333;
|
background: linear-gradient(180deg, #3678ac, #20537c);
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-corner {
|
::-webkit-scrollbar-corner {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
html,
|
||||||
color: #00E6FF;
|
body {
|
||||||
|
min-height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
color: var(--text);
|
||||||
|
font-size: 14px;
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at 15% 12%, rgba(64, 132, 183, 0.22), transparent 32%),
|
||||||
|
radial-gradient(circle at 88% 2%, rgba(18, 185, 255, 0.16), transparent 30%),
|
||||||
|
linear-gradient(180deg, var(--bg-1) 0%, var(--bg-0) 56%, #060d17 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
html, body {
|
body {
|
||||||
color: #fff;
|
line-height: 1.45;
|
||||||
margin: 0px auto;
|
}
|
||||||
height: 100%;
|
|
||||||
font-size: 14px;
|
a {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: #8de7ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5 {
|
||||||
|
margin: 0;
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: 0.03em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 1.72rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
font-size: 24px;
|
font-size: 1.35rem;
|
||||||
letter-spacing: 2px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
font-size: 22px;
|
font-size: 1.2rem;
|
||||||
letter-spacing: 1px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h4 {
|
h4 {
|
||||||
font-size: 20px;
|
font-size: 1.05rem;
|
||||||
letter-spacing: 1px;
|
|
||||||
line-height: 1.5em;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h5 {
|
h5 {
|
||||||
font-size: 16px;
|
font-size: 0.92rem;
|
||||||
letter-spacing: 1px;
|
margin: 8px 0;
|
||||||
line-height: 1.2em;
|
color: var(--text-muted);
|
||||||
margin: 25px 0px 10px 0px;
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--text-muted);
|
||||||
|
font-size: 12px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
line-height: 1.55;
|
||||||
|
letter-spacing: 0.015em;
|
||||||
|
font-family: "IBM Plex Mono", "SFMono-Regular", "Consolas", monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
border: 0;
|
border: 0;
|
||||||
height: 1px;
|
height: 1px;
|
||||||
background: #333;
|
margin: 12px 0;
|
||||||
margin: 10px 0px;
|
background: linear-gradient(90deg, transparent, var(--line), transparent);
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 2px;
|
|
||||||
padding: 2px 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre {
|
|
||||||
margin: 0px 0px 5px 0px;
|
|
||||||
font-size: 12px;
|
|
||||||
color: #ddd;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
font-family: monospace;
|
|
||||||
font-size: 12px;
|
|
||||||
font-style: normal;
|
|
||||||
font-variant: normal;
|
|
||||||
line-height: 1.6em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
label {
|
label {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 8px;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
li {
|
li {
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
background-color: #111;
|
|
||||||
padding: 10px 20px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-left: solid 2px #111;
|
transition: all 0.25s ease;
|
||||||
transition: all 0.3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
li:hover {
|
select,
|
||||||
border-color: #00E6FF
|
input,
|
||||||
|
textarea,
|
||||||
|
button {
|
||||||
|
outline: none;
|
||||||
|
color: var(--text);
|
||||||
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
select {
|
select {
|
||||||
|
margin: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=text],
|
||||||
|
input[type=search],
|
||||||
|
input[type=password],
|
||||||
|
input[type=number],
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
width: 100%;
|
||||||
|
border-radius: var(--radius-s);
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
background-color: rgba(10, 22, 36, 0.78);
|
||||||
|
color: var(--text);
|
||||||
|
padding: 10px 11px;
|
||||||
|
transition: border-color 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=text]:focus,
|
||||||
|
input[type=search]:focus,
|
||||||
|
input[type=password]:focus,
|
||||||
|
input[type=number]:focus,
|
||||||
|
select:focus,
|
||||||
|
textarea:focus {
|
||||||
|
border-color: var(--accent);
|
||||||
|
box-shadow: 0 0 0 3px rgba(53, 210, 255, 0.17);
|
||||||
|
background-color: rgba(9, 25, 40, 0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=text]::placeholder,
|
||||||
|
input[type=search]::placeholder,
|
||||||
|
input[type=password]::placeholder,
|
||||||
|
textarea::placeholder {
|
||||||
|
color: #7e9ab4;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=button],
|
||||||
|
input[type=submit],
|
||||||
|
button {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
width: calc(100% + 2px);
|
border: 1px solid transparent;
|
||||||
border: solid 0px #00E6FF;
|
border-radius: 999px;
|
||||||
border-radius: 0px;
|
color: #03101a;
|
||||||
outline: none;
|
padding: 10px 18px;
|
||||||
|
margin: 8px 8px 8px 0;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.03em;
|
||||||
|
background: linear-gradient(135deg, #67e8ff 0%, #12b9ff 100%);
|
||||||
|
box-shadow: 0 10px 22px rgba(18, 185, 255, 0.3);
|
||||||
|
transition: transform 0.15s ease, box-shadow 0.2s ease, filter 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=button]:hover,
|
||||||
|
input[type=submit]:hover,
|
||||||
|
button:hover {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
filter: brightness(1.05);
|
||||||
|
box-shadow: 0 12px 24px rgba(18, 185, 255, 0.36);
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=button]:active,
|
||||||
|
input[type=submit]:active,
|
||||||
|
button:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=button].delete {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
padding: 9px 10px;
|
background: linear-gradient(135deg, #ff7f7f 0%, #e94343 100%);
|
||||||
display:block;
|
box-shadow: 0 10px 20px rgba(233, 67, 67, 0.35);
|
||||||
background-color: #333;
|
|
||||||
font-size: 14px;
|
|
||||||
margin: 5px 0px 5px 0px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
select:focus {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
|
||||||
-webkit-appearance: none;
|
|
||||||
margin: 5px 0px;
|
|
||||||
padding: 2.5px 10px;
|
|
||||||
outline: none;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type=button], input[type=submit] {
|
|
||||||
cursor: pointer;
|
|
||||||
background-color: #000;
|
|
||||||
margin: 10px 10px;
|
|
||||||
padding: 10px 25px;
|
|
||||||
border: solid 0px;
|
|
||||||
border-color: #000;
|
|
||||||
border-radius: 3px;
|
|
||||||
outline: none;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type=button]:focus {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type=button]:hover {
|
|
||||||
background-color: #00E6FF;
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type=button]:hover.delete {
|
|
||||||
background-color: red;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type=text], input[type=search], input[type=password] {
|
|
||||||
color: #fff;
|
|
||||||
width: -webkit-calc(100% - 0px);
|
|
||||||
width: -moz-calc(100% - 0px);
|
|
||||||
width: calc(100% - 0px);
|
|
||||||
outline: none;
|
|
||||||
border: solid 1px transparent;
|
|
||||||
background-color: transparent;
|
|
||||||
border-bottom-color: #555;
|
|
||||||
border-radius: 0px;
|
|
||||||
padding: 8px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="checkbox"] {
|
|
||||||
border: solid 1px #00E6FF;
|
|
||||||
background-color: #333;
|
|
||||||
height: 25px;
|
|
||||||
width: 25px;
|
|
||||||
cursor: pointer;
|
|
||||||
/*
|
|
||||||
-webkit-appearance: checkbox;
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="checkbox"]:checked {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #00E6FF;
|
|
||||||
/*display: inline-block;*/
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="checkbox"]:before {
|
|
||||||
position: initial;
|
|
||||||
left: 0px;
|
|
||||||
margin-left: -4px;
|
|
||||||
content: " ";
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="checkbox"]:checked:before {
|
|
||||||
position: initial;
|
|
||||||
left: 0px;
|
|
||||||
margin-left: -3px;
|
|
||||||
content: "✓";
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
input[type=button].cancel {
|
input[type=button].cancel {
|
||||||
|
color: #ffd8d8;
|
||||||
background-color: transparent;
|
border-color: rgba(255, 111, 111, 0.45);
|
||||||
border-color: red;
|
background: rgba(87, 22, 22, 0.45);
|
||||||
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=button].save{
|
input[type=button].black,
|
||||||
background-color: #111;
|
input[type=submit].black {
|
||||||
float: right;
|
color: #d8ecff;
|
||||||
}
|
border-color: var(--line);
|
||||||
|
background: rgba(14, 32, 50, 0.85);
|
||||||
|
box-shadow: none;
|
||||||
input[type=button].black, input[type=submit].black{
|
|
||||||
background-color: #000;
|
|
||||||
border-color: #000;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=button].center {
|
input[type=button].center {
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
background-color: #000;
|
}
|
||||||
border-color: #000;
|
|
||||||
|
input[type=button].save {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=checkbox] {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 6px;
|
||||||
|
background-color: rgba(10, 22, 36, 0.86);
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=checkbox]:checked {
|
||||||
|
border-color: #89ebff;
|
||||||
|
background-color: #39d6ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=checkbox]:before {
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=checkbox]:checked:before {
|
||||||
|
content: "\2713";
|
||||||
|
color: #032033;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 800;
|
||||||
|
position: absolute;
|
||||||
|
top: -1px;
|
||||||
|
left: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.changed {
|
||||||
|
border-color: #8de7ff !important;
|
||||||
|
box-shadow: 0 0 0 3px rgba(53, 210, 255, 0.2) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notAvailable {
|
||||||
|
opacity: 0.58;
|
||||||
|
cursor: not-allowed;
|
||||||
|
border-color: rgba(255, 111, 111, 0.45) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pointer {
|
.pointer {
|
||||||
@@ -243,12 +295,11 @@ input[type=button].center{
|
|||||||
}
|
}
|
||||||
|
|
||||||
.pointer:hover {
|
.pointer:hover {
|
||||||
color: #00E6FF;
|
color: var(--accent);
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.sortThis {
|
.sortThis {
|
||||||
color: #00E6FF;
|
color: var(--accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
.w40px {
|
.w40px {
|
||||||
@@ -271,14 +322,9 @@ input[type=button].center{
|
|||||||
max-width: 200px;
|
max-width: 200px;
|
||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
width: 200px;
|
width: 200px;
|
||||||
overflow-x: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
white-space: nowrap;
|
||||||
|
|
||||||
.w300px {
|
|
||||||
max-width: 300px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.w220px {
|
.w220px {
|
||||||
@@ -286,46 +332,18 @@ input[type=button].center{
|
|||||||
cursor: alias;
|
cursor: alias;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.w300px {
|
||||||
|
max-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
font-size: 10px;
|
font-size: 11px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.center {
|
.center {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.screenLogHidden {
|
|
||||||
transform: translate(0px, -110px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.borderSpace {
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.block {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.none {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.notVisible {
|
|
||||||
height: 0px;
|
|
||||||
display: none;
|
|
||||||
opacity: 0;
|
|
||||||
border-bottom: #000 solid 0px;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.visible {
|
|
||||||
opacity: 1;
|
|
||||||
display: block;
|
|
||||||
border-bottom: #444 solid 1px;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.floatRight {
|
.floatRight {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
@@ -334,115 +352,311 @@ input[type=button].center{
|
|||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.borderSpace {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.none {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.showBulk {
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hideBulk {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.noBulk {
|
||||||
|
}
|
||||||
|
|
||||||
|
.notVisible {
|
||||||
|
display: none;
|
||||||
|
opacity: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.visible {
|
||||||
|
display: block;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.menu-active {
|
.menu-active {
|
||||||
background-color: #00E6FF;
|
color: #011019;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-notActive {
|
.menu-notActive {
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#branch {
|
|
||||||
display: table;
|
|
||||||
margin: auto;
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
|
|
||||||
#interaction {
|
|
||||||
margin-bottom: 100px;
|
|
||||||
text-align: center;
|
|
||||||
border-bottom: solid 0px #777;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.half {
|
|
||||||
display: block;
|
|
||||||
width: 45%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu {
|
.menu {
|
||||||
border: solid 1px #00E6FF;
|
border: 1px solid var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.half {
|
||||||
|
width: 45%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.screenLogHidden {
|
||||||
|
transform: translate(0, -110px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.infoMsg {
|
.infoMsg {
|
||||||
color: #aaa;
|
color: #87b0d1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.errorMsg {
|
.errorMsg {
|
||||||
color: red;
|
color: var(--error);
|
||||||
}
|
}
|
||||||
|
|
||||||
.warningMsg {
|
.warningMsg {
|
||||||
color: yellow;
|
color: var(--warn);
|
||||||
}
|
}
|
||||||
|
|
||||||
.debugMsg {
|
.debugMsg {
|
||||||
color: magenta;
|
color: #d687ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.News, .Movie, .Series, .Sports, .Kids {
|
.News,
|
||||||
border-left: solid 2px
|
.Movie,
|
||||||
|
.Series,
|
||||||
|
.Sports,
|
||||||
|
.Kids {
|
||||||
|
border-left: 3px solid transparent;
|
||||||
|
padding-left: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.News {
|
.News {
|
||||||
border-color: tomato
|
border-color: #ff8f6e;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Movie {
|
.Movie {
|
||||||
border-color: royalblue;
|
border-color: #46a4ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Series {
|
.Series {
|
||||||
border-color: gold;
|
border-color: #ffd267;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Sports {
|
.Sports {
|
||||||
border-color: yellowgreen;
|
border-color: #5ee495;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Kids {
|
.Kids {
|
||||||
border-color: mediumpurple;
|
border-color: #f6a8ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Loading */
|
|
||||||
#loading {
|
#loading {
|
||||||
left: 0px;
|
position: fixed;
|
||||||
top: 0px;
|
inset: 0;
|
||||||
z-index: 10000;
|
z-index: 2000;
|
||||||
position: absolute;
|
background-color: rgba(5, 12, 20, 0.72);
|
||||||
background-color: rgba(0,0,0, 0.8);
|
backdrop-filter: blur(1.5px);
|
||||||
margin: auto;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.loader {
|
.loader {
|
||||||
border: 5px solid transparent;
|
width: 52px;
|
||||||
|
height: 52px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
border-top: 5px solid #00E6FF;
|
border: 4px solid transparent;
|
||||||
border-bottom: 5px solid #00E6FF;
|
border-top-color: #58ddff;
|
||||||
width: 50px;
|
border-right-color: #12b9ff;
|
||||||
height: 50px;
|
animation: spin 0.9s linear infinite;
|
||||||
-webkit-animation: spin 1.2s linear infinite;
|
|
||||||
animation: spin 1.2s linear infinite;
|
|
||||||
|
|
||||||
position: fixed;
|
position: fixed;
|
||||||
margin: auto;
|
|
||||||
|
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
margin: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@-webkit-keyframes spin {
|
#popup {
|
||||||
0% { -webkit-transform: rotate(0deg); }
|
position: fixed;
|
||||||
100% { -webkit-transform: rotate(360deg); }
|
inset: 0;
|
||||||
|
background-color: rgba(3, 9, 16, 0.72);
|
||||||
|
backdrop-filter: blur(2px);
|
||||||
|
z-index: 1500;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#popup-custom,
|
||||||
|
#mapping-detail,
|
||||||
|
#user-detail,
|
||||||
|
#file-detail {
|
||||||
|
margin: 0 auto;
|
||||||
|
max-width: 760px;
|
||||||
|
max-height: calc(100vh - 36px);
|
||||||
|
overflow: auto;
|
||||||
|
border-radius: var(--radius-l);
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
background: linear-gradient(180deg, rgba(17, 34, 56, 0.97) 0%, rgba(12, 27, 44, 0.97) 100%);
|
||||||
|
box-shadow: var(--shadow-1);
|
||||||
|
padding: 16px;
|
||||||
|
animation: popupIn 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#popup-custom h3 {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
text-align: center;
|
||||||
|
color: #d5efff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#popup-custom table,
|
||||||
|
#content_settings table,
|
||||||
|
#mapping-detail-table,
|
||||||
|
#user-detail-table {
|
||||||
|
width: 100%;
|
||||||
|
table-layout: fixed;
|
||||||
|
border-collapse: separate;
|
||||||
|
border-spacing: 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#popup-custom td,
|
||||||
|
#content_settings td,
|
||||||
|
#mapping-detail-table td,
|
||||||
|
#user-detail-table td {
|
||||||
|
padding: 2px 6px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
#popup-custom td.left,
|
||||||
|
#mapping-detail-table td.left,
|
||||||
|
#user-detail-table td.left {
|
||||||
|
width: 38%;
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
#popup-custom input[type=text],
|
||||||
|
#popup-custom input[type=password],
|
||||||
|
#mapping-detail input[type=text],
|
||||||
|
#content_settings input[type=text],
|
||||||
|
#content_settings input[type=password] {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mapping-detail img {
|
||||||
|
display: block;
|
||||||
|
max-height: 44px;
|
||||||
|
margin: 8px auto 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#file-detail input[type=text] {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.interaction,
|
||||||
|
#interaction {
|
||||||
|
margin-top: 16px;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.interaction input[type=button],
|
||||||
|
.interaction input[type=submit] {
|
||||||
|
margin: 0;
|
||||||
|
min-width: 110px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#popup-interaction {
|
||||||
|
margin-top: 14px;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notification {
|
||||||
|
position: fixed;
|
||||||
|
right: 12px;
|
||||||
|
top: 12px;
|
||||||
|
width: 260px;
|
||||||
|
max-height: calc(100vh - 24px);
|
||||||
|
overflow: auto;
|
||||||
|
border-radius: var(--radius-m);
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
background: rgba(10, 22, 36, 0.92);
|
||||||
|
box-shadow: var(--shadow-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#notification .element {
|
||||||
|
margin: 8px;
|
||||||
|
border-radius: 10px;
|
||||||
|
border-left: 4px solid var(--ok);
|
||||||
|
background: rgba(17, 35, 56, 0.84);
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notification h5 {
|
||||||
|
padding: 0;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notification p {
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tableEllipsis {
|
||||||
|
width: 150px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes spin {
|
@keyframes spin {
|
||||||
0% { transform: rotate(0deg); }
|
0% {
|
||||||
100% { transform: rotate(360deg); }
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes popupIn {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(7px) scale(0.99);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0) scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 619px) {
|
||||||
|
h1 {
|
||||||
|
font-size: 1.46rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#popup {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#popup-custom,
|
||||||
|
#mapping-detail,
|
||||||
|
#user-detail,
|
||||||
|
#file-detail {
|
||||||
|
max-height: calc(100vh - 20px);
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.interaction,
|
||||||
|
#interaction,
|
||||||
|
#popup-interaction {
|
||||||
|
justify-content: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.interaction input[type=button],
|
||||||
|
.interaction input[type=submit],
|
||||||
|
#popup-interaction input[type=button] {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1092
html/css/screen.css
1092
html/css/screen.css
File diff suppressed because it is too large
Load Diff
@@ -2,9 +2,7 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<!---
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
-->
|
|
||||||
<title>xTeVe</title>
|
<title>xTeVe</title>
|
||||||
<link rel="stylesheet" href="css/screen.css" type="text/css">
|
<link rel="stylesheet" href="css/screen.css" type="text/css">
|
||||||
<link rel="stylesheet" href="css/base.css" type="text/css">
|
<link rel="stylesheet" href="css/base.css" type="text/css">
|
||||||
@@ -17,7 +15,7 @@
|
|||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body onload="javascript: PageReady();">
|
<body class="app-shell" onload="javascript: PageReady();">
|
||||||
|
|
||||||
<div id="loading" class="none">
|
<div id="loading" class="none">
|
||||||
<div class="loader"></div>
|
<div class="loader"></div>
|
||||||
@@ -27,6 +25,7 @@
|
|||||||
<div id="popup-custom"></div>
|
<div id="popup-custom"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="layout-overlay"></div>
|
||||||
<div id="layout">
|
<div id="layout">
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
@@ -40,13 +39,18 @@
|
|||||||
</div>
|
</div>
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<div id="menu-wrapper" class="layout-left">
|
<aside id="menu-wrapper" class="layout-left">
|
||||||
<div id= "branch"></div>
|
<div id= "branch"></div>
|
||||||
<div id="logo"></div>
|
<div id="logo"></div>
|
||||||
<nav id="main-menu"></nav>
|
<nav id="main-menu"></nav>
|
||||||
</div>
|
</aside>
|
||||||
|
|
||||||
<div class="layout-right">
|
<main class="layout-right">
|
||||||
|
<header id="shell-header">
|
||||||
|
<button id="menu-toggle" type="button">Menu</button>
|
||||||
|
<h2 id="shell-title">xTeVe Control Panel</h2>
|
||||||
|
<p id="connection-indicator" class="status-idle">Connecting...</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
<table id="clientInfo" class="">
|
<table id="clientInfo" class="">
|
||||||
|
|
||||||
@@ -88,6 +92,8 @@
|
|||||||
|
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<div id="status-cards" class="dashboard-cards"></div>
|
||||||
|
|
||||||
<div id="myStreamsBox" class="notVisible">
|
<div id="myStreamsBox" class="notVisible">
|
||||||
|
|
||||||
<div id="allStreams">
|
<div id="allStreams">
|
||||||
@@ -99,7 +105,7 @@
|
|||||||
|
|
||||||
<div id="content" class=""></div>
|
<div id="content" class=""></div>
|
||||||
|
|
||||||
</div>
|
</main>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ function login() {
|
|||||||
inputs[i].style.borderColor = "red";
|
inputs[i].style.borderColor = "red";
|
||||||
err = true;
|
err = true;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
inputs[i].style.borderColor = "";
|
||||||
|
}
|
||||||
data[key] = value;
|
data[key] = value;
|
||||||
}
|
}
|
||||||
if (err == true) {
|
if (err == true) {
|
||||||
@@ -20,7 +23,6 @@ function login() {
|
|||||||
}
|
}
|
||||||
if (data.hasOwnProperty("confirm")) {
|
if (data.hasOwnProperty("confirm")) {
|
||||||
if (data["confirm"] != data["password"]) {
|
if (data["confirm"] != data["password"]) {
|
||||||
alert("sdafsd");
|
|
||||||
document.getElementById('password').style.borderColor = "red";
|
document.getElementById('password').style.borderColor = "red";
|
||||||
document.getElementById('confirm').style.borderColor = "red";
|
document.getElementById('confirm').style.borderColor = "red";
|
||||||
document.getElementById("err").innerHTML = "{{.account.failed}}";
|
document.getElementById("err").innerHTML = "{{.account.failed}}";
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ var SEARCH_MAPPING = new Object();
|
|||||||
var UNDO = new Object();
|
var UNDO = new Object();
|
||||||
var SERVER_CONNECTION = false;
|
var SERVER_CONNECTION = false;
|
||||||
var WS_AVAILABLE = false;
|
var WS_AVAILABLE = false;
|
||||||
|
var ACTIVE_MENU_ID = "";
|
||||||
// Menü
|
// Menü
|
||||||
var menuItems = new Array();
|
var menuItems = new Array();
|
||||||
menuItems.push(new MainMenuItem("playlist", "{{.mainMenu.item.playlist}}", "m3u.png", "{{.mainMenu.headline.playlist}}"));
|
menuItems.push(new MainMenuItem("playlist", "{{.mainMenu.item.playlist}}", "m3u.png", "{{.mainMenu.headline.playlist}}"));
|
||||||
@@ -44,7 +45,36 @@ function showElement(elmID, type) {
|
|||||||
cssClass = "none";
|
cssClass = "none";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
document.getElementById(elmID).className = cssClass;
|
var element = document.getElementById(elmID);
|
||||||
|
if (element == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
element.className = cssClass;
|
||||||
|
}
|
||||||
|
function setConnectionState(state, text) {
|
||||||
|
var label = text;
|
||||||
|
if (label == undefined || label.length == 0) {
|
||||||
|
switch (state) {
|
||||||
|
case "online":
|
||||||
|
label = "Connected";
|
||||||
|
break;
|
||||||
|
case "busy":
|
||||||
|
label = "Syncing";
|
||||||
|
break;
|
||||||
|
case "offline":
|
||||||
|
label = "Offline";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
label = "Connecting";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var indicator = document.getElementById("connection-indicator");
|
||||||
|
if (indicator == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
indicator.className = "status-" + state;
|
||||||
|
indicator.innerText = label;
|
||||||
}
|
}
|
||||||
function changeButtonAction(element, buttonID, attribute) {
|
function changeButtonAction(element, buttonID, attribute) {
|
||||||
var value = element.options[element.selectedIndex].value;
|
var value = element.options[element.selectedIndex].value;
|
||||||
@@ -272,14 +302,15 @@ function searchInMapping() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
function calculateWrapperHeight() {
|
function calculateWrapperHeight() {
|
||||||
if (document.getElementById("box-wrapper")) {
|
|
||||||
var elm = document.getElementById("box-wrapper");
|
var elm = document.getElementById("box-wrapper");
|
||||||
var divs = new Array("myStreamsBox", "clientInfo", "content");
|
var content = document.getElementById("content");
|
||||||
var elementsHeight = 0 - elm.offsetHeight;
|
if (elm != null && content != null) {
|
||||||
for (var i = 0; i < divs.length; i++) {
|
var contentTop = content.getBoundingClientRect().top;
|
||||||
elementsHeight = elementsHeight + document.getElementById(divs[i]).offsetHeight;
|
var freeSpace = window.innerHeight - contentTop - 26;
|
||||||
|
if (freeSpace < 180) {
|
||||||
|
freeSpace = 180;
|
||||||
}
|
}
|
||||||
elm.style.height = window.innerHeight - elementsHeight + "px";
|
elm.style.height = freeSpace + "px";
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ var MainMenuItem = /** @class */ (function (_super) {
|
|||||||
var item = document.createElement("LI");
|
var item = document.createElement("LI");
|
||||||
item.setAttribute("onclick", "javascript: openThisMenu(this)");
|
item.setAttribute("onclick", "javascript: openThisMenu(this)");
|
||||||
item.setAttribute("id", this.id);
|
item.setAttribute("id", this.id);
|
||||||
|
item.setAttribute("data-menu", this.menuKey);
|
||||||
var img = this.createIMG(this.imgSrc);
|
var img = this.createIMG(this.imgSrc);
|
||||||
var value = this.createValue(this.value);
|
var value = this.createValue(this.value);
|
||||||
item.appendChild(img);
|
item.appendChild(img);
|
||||||
@@ -560,7 +561,7 @@ var ShowContent = /** @class */ (function (_super) {
|
|||||||
input.setAttribute("id", "searchMapping");
|
input.setAttribute("id", "searchMapping");
|
||||||
input.setAttribute("placeholder", "{{.button.search}}");
|
input.setAttribute("placeholder", "{{.button.search}}");
|
||||||
input.className = "search";
|
input.className = "search";
|
||||||
input.setAttribute("onchange", 'javascript: searchInMapping()');
|
input.setAttribute("oninput", 'javascript: searchInMapping()');
|
||||||
interaction.appendChild(input);
|
interaction.appendChild(input);
|
||||||
break;
|
break;
|
||||||
case "settings":
|
case "settings":
|
||||||
@@ -668,10 +669,127 @@ var ShowContent = /** @class */ (function (_super) {
|
|||||||
};
|
};
|
||||||
return ShowContent;
|
return ShowContent;
|
||||||
}(Content));
|
}(Content));
|
||||||
|
var SHELL_LAYOUT_READY = false;
|
||||||
|
function setLayoutMenuState(open) {
|
||||||
|
if (document.body == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (open == true) {
|
||||||
|
document.body.classList.add("menu-open");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
document.body.classList.remove("menu-open");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function toggleLayoutMenu() {
|
||||||
|
if (document.body == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var isOpen = document.body.classList.contains("menu-open");
|
||||||
|
setLayoutMenuState(!isOpen);
|
||||||
|
}
|
||||||
|
function closeLayoutMenuIfMobile() {
|
||||||
|
if (window.innerWidth <= 900) {
|
||||||
|
setLayoutMenuState(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function setActiveMenu(menuID) {
|
||||||
|
ACTIVE_MENU_ID = menuID.toString();
|
||||||
|
var menu = document.getElementById("main-menu");
|
||||||
|
if (menu == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var items = menu.getElementsByTagName("LI");
|
||||||
|
for (var i = 0; i < items.length; i++) {
|
||||||
|
items[i].classList.remove("menu-active");
|
||||||
|
}
|
||||||
|
var activeItem = document.getElementById(ACTIVE_MENU_ID);
|
||||||
|
if (activeItem != null) {
|
||||||
|
activeItem.classList.add("menu-active");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function renderStatusCards() {
|
||||||
|
var wrapper = document.getElementById("status-cards");
|
||||||
|
if (wrapper == null || SERVER.hasOwnProperty("clientInfo") == false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var info = SERVER["clientInfo"];
|
||||||
|
var errors = parseInt(info["errors"], 10);
|
||||||
|
var warnings = parseInt(info["warnings"], 10);
|
||||||
|
var cards = [
|
||||||
|
{ label: "Streams", value: info["streams"], tone: "ok" },
|
||||||
|
{ label: "EPG Source", value: info["epgSource"], tone: "neutral" },
|
||||||
|
{ label: "XEPG Channels", value: info["xepg"], tone: "ok" },
|
||||||
|
{ label: "Errors", value: info["errors"], tone: errors > 0 ? "error" : "ok" },
|
||||||
|
{ label: "Warnings", value: info["warnings"], tone: warnings > 0 ? "warn" : "ok" },
|
||||||
|
{ label: "DVR", value: info["DVR"], tone: "neutral" }
|
||||||
|
];
|
||||||
|
wrapper.innerHTML = "";
|
||||||
|
cards.forEach(function (card) {
|
||||||
|
var box = document.createElement("DIV");
|
||||||
|
box.className = "status-card status-card-" + card.tone;
|
||||||
|
var label = document.createElement("P");
|
||||||
|
label.className = "status-card-label";
|
||||||
|
label.innerText = card.label;
|
||||||
|
var value = document.createElement("P");
|
||||||
|
value.className = "status-card-value";
|
||||||
|
if (card.value == undefined || card.value == "") {
|
||||||
|
value.innerText = "-";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
value.innerText = card.value;
|
||||||
|
}
|
||||||
|
box.appendChild(label);
|
||||||
|
box.appendChild(value);
|
||||||
|
wrapper.appendChild(box);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function initShellLayout() {
|
||||||
|
if (SHELL_LAYOUT_READY == true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var toggle = document.getElementById("menu-toggle");
|
||||||
|
if (toggle != null) {
|
||||||
|
toggle.onclick = function () {
|
||||||
|
toggleLayoutMenu();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
var overlay = document.getElementById("layout-overlay");
|
||||||
|
if (overlay != null) {
|
||||||
|
overlay.onclick = function () {
|
||||||
|
setLayoutMenuState(false);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
document.addEventListener("keydown", function (event) {
|
||||||
|
if (event.key == "Escape") {
|
||||||
|
setLayoutMenuState(false);
|
||||||
|
showElement("popup", false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.key == "/") {
|
||||||
|
var target = event.target;
|
||||||
|
var onInput = target.tagName == "INPUT" || target.tagName == "TEXTAREA" || target.tagName == "SELECT";
|
||||||
|
if (onInput == true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var search = document.getElementById("searchMapping");
|
||||||
|
if (search != null) {
|
||||||
|
event.preventDefault();
|
||||||
|
search.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setConnectionState("idle");
|
||||||
|
SHELL_LAYOUT_READY = true;
|
||||||
|
}
|
||||||
function PageReady() {
|
function PageReady() {
|
||||||
|
initShellLayout();
|
||||||
var server = new Server("getServerConfig");
|
var server = new Server("getServerConfig");
|
||||||
server.request(new Object());
|
server.request(new Object());
|
||||||
window.addEventListener("resize", function () {
|
window.addEventListener("resize", function () {
|
||||||
|
if (window.innerWidth > 900) {
|
||||||
|
setLayoutMenuState(false);
|
||||||
|
}
|
||||||
calculateWrapperHeight();
|
calculateWrapperHeight();
|
||||||
}, true);
|
}, true);
|
||||||
setInterval(function () {
|
setInterval(function () {
|
||||||
@@ -688,6 +806,7 @@ function createLayout() {
|
|||||||
document.getElementById(keys[i]).innerHTML = obj[keys[i]];
|
document.getElementById(keys[i]).innerHTML = obj[keys[i]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
renderStatusCards();
|
||||||
if (!document.getElementById("main-menu")) {
|
if (!document.getElementById("main-menu")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -713,12 +832,27 @@ function createLayout() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (ACTIVE_MENU_ID.length > 0 && document.getElementById(ACTIVE_MENU_ID)) {
|
||||||
|
setActiveMenu(ACTIVE_MENU_ID);
|
||||||
|
}
|
||||||
|
var content = document.getElementById("content");
|
||||||
|
var menu = document.getElementById("main-menu");
|
||||||
|
if (ACTIVE_MENU_ID.length == 0 && content != null && menu != null) {
|
||||||
|
if (content.innerHTML.replace(/\\s/g, "").length == 0) {
|
||||||
|
var firstItem = menu.getElementsByTagName("LI")[0];
|
||||||
|
if (firstItem != undefined) {
|
||||||
|
firstItem.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
function openThisMenu(element) {
|
function openThisMenu(element) {
|
||||||
var id = element.id;
|
var id = element.id;
|
||||||
var content = new ShowContent(id);
|
var content = new ShowContent(id);
|
||||||
|
setActiveMenu(id);
|
||||||
content.show();
|
content.show();
|
||||||
|
closeLayoutMenuIfMobile();
|
||||||
calculateWrapperHeight();
|
calculateWrapperHeight();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ var Server = /** @class */ (function () {
|
|||||||
if (this.cmd != "updateLog") {
|
if (this.cmd != "updateLog") {
|
||||||
showElement("loading", true);
|
showElement("loading", true);
|
||||||
UNDO = new Object();
|
UNDO = new Object();
|
||||||
|
setConnectionState("busy");
|
||||||
}
|
}
|
||||||
switch (window.location.protocol) {
|
switch (window.location.protocol) {
|
||||||
case "http:":
|
case "http:":
|
||||||
@@ -25,6 +26,9 @@ var Server = /** @class */ (function () {
|
|||||||
var ws = new WebSocket(url);
|
var ws = new WebSocket(url);
|
||||||
ws.onopen = function () {
|
ws.onopen = function () {
|
||||||
WS_AVAILABLE = true;
|
WS_AVAILABLE = true;
|
||||||
|
if (data["cmd"] != "updateLog") {
|
||||||
|
setConnectionState("busy");
|
||||||
|
}
|
||||||
console.log("REQUEST (JS):");
|
console.log("REQUEST (JS):");
|
||||||
console.log(data);
|
console.log(data);
|
||||||
console.log("REQUEST: (JSON)");
|
console.log("REQUEST: (JSON)");
|
||||||
@@ -34,6 +38,7 @@ var Server = /** @class */ (function () {
|
|||||||
ws.onerror = function (e) {
|
ws.onerror = function (e) {
|
||||||
console.log("No websocket connection to xTeVe could be established. Check your network configuration.");
|
console.log("No websocket connection to xTeVe could be established. Check your network configuration.");
|
||||||
SERVER_CONNECTION = false;
|
SERVER_CONNECTION = false;
|
||||||
|
setConnectionState("offline");
|
||||||
if (WS_AVAILABLE == false) {
|
if (WS_AVAILABLE == false) {
|
||||||
alert("No websocket connection to xTeVe could be established. Check your network configuration.");
|
alert("No websocket connection to xTeVe could be established. Check your network configuration.");
|
||||||
}
|
}
|
||||||
@@ -41,6 +46,9 @@ var Server = /** @class */ (function () {
|
|||||||
ws.onmessage = function (e) {
|
ws.onmessage = function (e) {
|
||||||
SERVER_CONNECTION = false;
|
SERVER_CONNECTION = false;
|
||||||
showElement("loading", false);
|
showElement("loading", false);
|
||||||
|
if (data["cmd"] != "updateLog") {
|
||||||
|
setConnectionState("online");
|
||||||
|
}
|
||||||
console.log("RESPONSE:");
|
console.log("RESPONSE:");
|
||||||
var response = JSON.parse(e.data);
|
var response = JSON.parse(e.data);
|
||||||
console.log(response);
|
console.log(response);
|
||||||
@@ -48,6 +56,7 @@ var Server = /** @class */ (function () {
|
|||||||
document.cookie = "Token=" + response["token"];
|
document.cookie = "Token=" + response["token"];
|
||||||
}
|
}
|
||||||
if (response["status"] == false) {
|
if (response["status"] == false) {
|
||||||
|
setConnectionState("offline");
|
||||||
alert(response["err"]);
|
alert(response["err"]);
|
||||||
if (response.hasOwnProperty("reload")) {
|
if (response.hasOwnProperty("reload")) {
|
||||||
location.reload();
|
location.reload();
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
<script language="javascript" type="text/javascript" src="js/authentication_ts.js"></script>
|
<script language="javascript" type="text/javascript" src="js/authentication_ts.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body class="auth-screen">
|
||||||
|
|
||||||
<div id="header" class="imgCenter"></div>
|
<div id="header" class="imgCenter"></div>
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<link rel="stylesheet" href="css/base.css" type="text/css">
|
<link rel="stylesheet" href="css/base.css" type="text/css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body class="auth-screen">
|
||||||
|
|
||||||
<div id="header" class="imgCenter"></div>
|
<div id="header" class="imgCenter"></div>
|
||||||
|
|
||||||
|
|||||||
67
src/webUI.go
67
src/webUI.go
File diff suppressed because one or more lines are too long
@@ -16,6 +16,8 @@ function login() {
|
|||||||
if (value.length == 0) {
|
if (value.length == 0) {
|
||||||
inputs[i].style.borderColor = "red"
|
inputs[i].style.borderColor = "red"
|
||||||
err = true
|
err = true
|
||||||
|
} else {
|
||||||
|
inputs[i].style.borderColor = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
data[key] = value
|
data[key] = value
|
||||||
@@ -30,7 +32,6 @@ function login() {
|
|||||||
if (data.hasOwnProperty("confirm")) {
|
if (data.hasOwnProperty("confirm")) {
|
||||||
|
|
||||||
if (data["confirm"] != data["password"]) {
|
if (data["confirm"] != data["password"]) {
|
||||||
alert("sdafsd")
|
|
||||||
document.getElementById('password').style.borderColor = "red"
|
document.getElementById('password').style.borderColor = "red"
|
||||||
document.getElementById('confirm').style.borderColor = "red"
|
document.getElementById('confirm').style.borderColor = "red"
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ var SEARCH_MAPPING = new Object()
|
|||||||
var UNDO = new Object()
|
var UNDO = new Object()
|
||||||
var SERVER_CONNECTION = false
|
var SERVER_CONNECTION = false
|
||||||
var WS_AVAILABLE = false
|
var WS_AVAILABLE = false
|
||||||
|
var ACTIVE_MENU_ID:string = ""
|
||||||
|
|
||||||
|
|
||||||
// Menü
|
// Menü
|
||||||
@@ -51,7 +52,44 @@ function showElement(elmID, type) {
|
|||||||
case false: cssClass = "none"; break;
|
case false: cssClass = "none"; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById(elmID).className = cssClass;
|
var element = document.getElementById(elmID)
|
||||||
|
if (element == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
element.className = cssClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setConnectionState(state:string, text:string = "") {
|
||||||
|
|
||||||
|
var label:string = text
|
||||||
|
if (label == undefined || label.length == 0) {
|
||||||
|
switch (state) {
|
||||||
|
case "online":
|
||||||
|
label = "Connected"
|
||||||
|
break
|
||||||
|
|
||||||
|
case "busy":
|
||||||
|
label = "Syncing"
|
||||||
|
break
|
||||||
|
|
||||||
|
case "offline":
|
||||||
|
label = "Offline"
|
||||||
|
break
|
||||||
|
|
||||||
|
default:
|
||||||
|
label = "Connecting"
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var indicator = document.getElementById("connection-indicator")
|
||||||
|
if (indicator == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
indicator.className = "status-" + state
|
||||||
|
indicator.innerText = label
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeButtonAction(element, buttonID, attribute) {
|
function changeButtonAction(element, buttonID, attribute) {
|
||||||
@@ -379,17 +417,19 @@ function searchInMapping() {
|
|||||||
|
|
||||||
function calculateWrapperHeight() {
|
function calculateWrapperHeight() {
|
||||||
|
|
||||||
if (document.getElementById("box-wrapper")){
|
|
||||||
|
|
||||||
var elm = document.getElementById("box-wrapper");
|
var elm = document.getElementById("box-wrapper");
|
||||||
|
var content = document.getElementById("content");
|
||||||
|
|
||||||
var divs = new Array("myStreamsBox", "clientInfo", "content");
|
if (elm != null && content != null){
|
||||||
var elementsHeight = 0 - elm.offsetHeight;
|
|
||||||
for (var i = 0; i < divs.length; i++) {
|
var contentTop = content.getBoundingClientRect().top
|
||||||
elementsHeight = elementsHeight + document.getElementById(divs[i]).offsetHeight;
|
var freeSpace = window.innerHeight - contentTop - 26
|
||||||
|
|
||||||
|
if (freeSpace < 180) {
|
||||||
|
freeSpace = 180
|
||||||
}
|
}
|
||||||
|
|
||||||
elm.style.height = window.innerHeight - elementsHeight + "px";
|
elm.style.height = freeSpace + "px";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
168
ts/menu_ts.ts
168
ts/menu_ts.ts
@@ -37,6 +37,7 @@ class MainMenuItem extends MainMenu {
|
|||||||
var item = document.createElement("LI")
|
var item = document.createElement("LI")
|
||||||
item.setAttribute("onclick", "javascript: openThisMenu(this)")
|
item.setAttribute("onclick", "javascript: openThisMenu(this)")
|
||||||
item.setAttribute("id", this.id)
|
item.setAttribute("id", this.id)
|
||||||
|
item.setAttribute("data-menu", this.menuKey)
|
||||||
var img = this.createIMG(this.imgSrc)
|
var img = this.createIMG(this.imgSrc)
|
||||||
var value = this.createValue(this.value)
|
var value = this.createValue(this.value)
|
||||||
|
|
||||||
@@ -683,7 +684,7 @@ class ShowContent extends Content {
|
|||||||
input.setAttribute("id", "searchMapping")
|
input.setAttribute("id", "searchMapping")
|
||||||
input.setAttribute("placeholder", "{{.button.search}}")
|
input.setAttribute("placeholder", "{{.button.search}}")
|
||||||
input.className = "search"
|
input.className = "search"
|
||||||
input.setAttribute("onchange", 'javascript: searchInMapping()')
|
input.setAttribute("oninput", 'javascript: searchInMapping()')
|
||||||
interaction.appendChild(input)
|
interaction.appendChild(input)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -824,12 +825,157 @@ class ShowContent extends Content {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var SHELL_LAYOUT_READY:boolean = false
|
||||||
|
|
||||||
|
function setLayoutMenuState(open:boolean) {
|
||||||
|
if (document.body == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (open == true) {
|
||||||
|
document.body.classList.add("menu-open")
|
||||||
|
} else {
|
||||||
|
document.body.classList.remove("menu-open")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleLayoutMenu() {
|
||||||
|
if (document.body == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var isOpen:boolean = document.body.classList.contains("menu-open")
|
||||||
|
setLayoutMenuState(!isOpen)
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeLayoutMenuIfMobile() {
|
||||||
|
if (window.innerWidth <= 900) {
|
||||||
|
setLayoutMenuState(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setActiveMenu(menuID:string) {
|
||||||
|
|
||||||
|
ACTIVE_MENU_ID = menuID.toString()
|
||||||
|
var menu = document.getElementById("main-menu")
|
||||||
|
if (menu == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var items = menu.getElementsByTagName("LI")
|
||||||
|
for (var i = 0; i < items.length; i++) {
|
||||||
|
items[i].classList.remove("menu-active")
|
||||||
|
}
|
||||||
|
|
||||||
|
var activeItem = document.getElementById(ACTIVE_MENU_ID)
|
||||||
|
if (activeItem != null) {
|
||||||
|
activeItem.classList.add("menu-active")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderStatusCards() {
|
||||||
|
var wrapper = document.getElementById("status-cards")
|
||||||
|
if (wrapper == null || SERVER.hasOwnProperty("clientInfo") == false) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var info = SERVER["clientInfo"]
|
||||||
|
var errors:number = parseInt(info["errors"], 10)
|
||||||
|
var warnings:number = parseInt(info["warnings"], 10)
|
||||||
|
var cards:any[] = [
|
||||||
|
{label: "Streams", value: info["streams"], tone: "ok"},
|
||||||
|
{label: "EPG Source", value: info["epgSource"], tone: "neutral"},
|
||||||
|
{label: "XEPG Channels", value: info["xepg"], tone: "ok"},
|
||||||
|
{label: "Errors", value: info["errors"], tone: errors > 0 ? "error" : "ok"},
|
||||||
|
{label: "Warnings", value: info["warnings"], tone: warnings > 0 ? "warn" : "ok"},
|
||||||
|
{label: "DVR", value: info["DVR"], tone: "neutral"},
|
||||||
|
]
|
||||||
|
|
||||||
|
wrapper.innerHTML = ""
|
||||||
|
cards.forEach(card => {
|
||||||
|
|
||||||
|
var box = document.createElement("DIV")
|
||||||
|
box.className = "status-card status-card-" + card.tone
|
||||||
|
|
||||||
|
var label = document.createElement("P")
|
||||||
|
label.className = "status-card-label"
|
||||||
|
label.innerText = card.label
|
||||||
|
|
||||||
|
var value = document.createElement("P")
|
||||||
|
value.className = "status-card-value"
|
||||||
|
if (card.value == undefined || card.value == "") {
|
||||||
|
value.innerText = "-"
|
||||||
|
} else {
|
||||||
|
value.innerText = card.value
|
||||||
|
}
|
||||||
|
|
||||||
|
box.appendChild(label)
|
||||||
|
box.appendChild(value)
|
||||||
|
wrapper.appendChild(box)
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function initShellLayout() {
|
||||||
|
|
||||||
|
if (SHELL_LAYOUT_READY == true) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var toggle = document.getElementById("menu-toggle")
|
||||||
|
if (toggle != null) {
|
||||||
|
toggle.onclick = function() {
|
||||||
|
toggleLayoutMenu()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var overlay = document.getElementById("layout-overlay")
|
||||||
|
if (overlay != null) {
|
||||||
|
overlay.onclick = function() {
|
||||||
|
setLayoutMenuState(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("keydown", function(event) {
|
||||||
|
|
||||||
|
if (event.key == "Escape") {
|
||||||
|
setLayoutMenuState(false)
|
||||||
|
showElement("popup", false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key == "/") {
|
||||||
|
var target = event.target as HTMLElement
|
||||||
|
var onInput = target.tagName == "INPUT" || target.tagName == "TEXTAREA" || target.tagName == "SELECT"
|
||||||
|
|
||||||
|
if (onInput == true) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var search = document.getElementById("searchMapping")
|
||||||
|
if (search != null) {
|
||||||
|
event.preventDefault()
|
||||||
|
(search as HTMLInputElement).focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
setConnectionState("idle")
|
||||||
|
SHELL_LAYOUT_READY = true
|
||||||
|
}
|
||||||
|
|
||||||
function PageReady() {
|
function PageReady() {
|
||||||
|
|
||||||
|
initShellLayout()
|
||||||
|
|
||||||
var server:Server = new Server("getServerConfig")
|
var server:Server = new Server("getServerConfig")
|
||||||
server.request(new Object())
|
server.request(new Object())
|
||||||
|
|
||||||
window.addEventListener("resize", function(){
|
window.addEventListener("resize", function(){
|
||||||
|
if (window.innerWidth > 900) {
|
||||||
|
setLayoutMenuState(false)
|
||||||
|
}
|
||||||
calculateWrapperHeight();
|
calculateWrapperHeight();
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
@@ -853,6 +999,7 @@ function createLayout() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
renderStatusCards()
|
||||||
|
|
||||||
if (!document.getElementById("main-menu")) {
|
if (!document.getElementById("main-menu")) {
|
||||||
return
|
return
|
||||||
@@ -889,13 +1036,32 @@ function createLayout() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ACTIVE_MENU_ID.length > 0 && document.getElementById(ACTIVE_MENU_ID)) {
|
||||||
|
setActiveMenu(ACTIVE_MENU_ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
var content = document.getElementById("content")
|
||||||
|
var menu = document.getElementById("main-menu")
|
||||||
|
if (ACTIVE_MENU_ID.length == 0 && content != null && menu != null) {
|
||||||
|
|
||||||
|
if (content.innerHTML.replace(/\\s/g, "").length == 0) {
|
||||||
|
var firstItem = menu.getElementsByTagName("LI")[0]
|
||||||
|
if (firstItem != undefined) {
|
||||||
|
firstItem.click()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
function openThisMenu(element) {
|
function openThisMenu(element) {
|
||||||
var id = element.id
|
var id = element.id
|
||||||
var content:ShowContent = new ShowContent(id)
|
var content:ShowContent = new ShowContent(id)
|
||||||
|
setActiveMenu(id)
|
||||||
content.show()
|
content.show()
|
||||||
|
closeLayoutMenuIfMobile()
|
||||||
calculateWrapperHeight()
|
calculateWrapperHeight()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ class Server {
|
|||||||
if (this.cmd != "updateLog") {
|
if (this.cmd != "updateLog") {
|
||||||
showElement("loading", true)
|
showElement("loading", true)
|
||||||
UNDO = new Object()
|
UNDO = new Object()
|
||||||
|
setConnectionState("busy")
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(window.location.protocol) {
|
switch(window.location.protocol) {
|
||||||
@@ -36,6 +37,9 @@ class Server {
|
|||||||
ws.onopen = function() {
|
ws.onopen = function() {
|
||||||
|
|
||||||
WS_AVAILABLE = true
|
WS_AVAILABLE = true
|
||||||
|
if (data["cmd"] != "updateLog") {
|
||||||
|
setConnectionState("busy")
|
||||||
|
}
|
||||||
|
|
||||||
console.log("REQUEST (JS):");
|
console.log("REQUEST (JS):");
|
||||||
console.log(data)
|
console.log(data)
|
||||||
@@ -51,6 +55,7 @@ class Server {
|
|||||||
|
|
||||||
console.log("No websocket connection to xTeVe could be established. Check your network configuration.")
|
console.log("No websocket connection to xTeVe could be established. Check your network configuration.")
|
||||||
SERVER_CONNECTION = false
|
SERVER_CONNECTION = false
|
||||||
|
setConnectionState("offline")
|
||||||
|
|
||||||
if (WS_AVAILABLE == false) {
|
if (WS_AVAILABLE == false) {
|
||||||
alert("No websocket connection to xTeVe could be established. Check your network configuration.")
|
alert("No websocket connection to xTeVe could be established. Check your network configuration.")
|
||||||
@@ -63,6 +68,9 @@ class Server {
|
|||||||
|
|
||||||
SERVER_CONNECTION = false
|
SERVER_CONNECTION = false
|
||||||
showElement("loading", false)
|
showElement("loading", false)
|
||||||
|
if (data["cmd"] != "updateLog") {
|
||||||
|
setConnectionState("online")
|
||||||
|
}
|
||||||
|
|
||||||
console.log("RESPONSE:");
|
console.log("RESPONSE:");
|
||||||
var response = JSON.parse(e.data);
|
var response = JSON.parse(e.data);
|
||||||
@@ -74,6 +82,7 @@ class Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (response["status"] == false) {
|
if (response["status"] == false) {
|
||||||
|
setConnectionState("offline")
|
||||||
|
|
||||||
alert(response["err"])
|
alert(response["err"])
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user