Compare commits
1 Commits
1.0.1
...
7e9b26499a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e9b26499a |
BIN
.github/media/preview.gif
vendored
BIN
.github/media/preview.gif
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 MiB |
28
.github/workflows/ci.yaml
vendored
28
.github/workflows/ci.yaml
vendored
@@ -11,8 +11,6 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Check out repository code
|
- name: Check out repository code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Setup JDK
|
- name: Setup JDK
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
@@ -20,14 +18,8 @@ jobs:
|
|||||||
distribution: 'zulu'
|
distribution: 'zulu'
|
||||||
java-version: '21'
|
java-version: '21'
|
||||||
|
|
||||||
- name: new Version
|
|
||||||
if: github.ref_name == 'main'
|
|
||||||
run: |
|
|
||||||
./mvnw -B --no-transfer-progress versions:set -DremoveSnapshot
|
|
||||||
|
|
||||||
- name: Build Runner
|
- name: Build Runner
|
||||||
run: |
|
run: ./mvnw --no-transfer-progress clean verify -Pnative -Dquarkus.native.remote-container-build=true
|
||||||
./mvnw -B --no-transfer-progress clean verify -Pnative -Dquarkus.native.remote-container-build=true -Djgitver.skip=true
|
|
||||||
|
|
||||||
- name: Add coverage to PR
|
- name: Add coverage to PR
|
||||||
id: jacoco
|
id: jacoco
|
||||||
@@ -39,10 +31,6 @@ jobs:
|
|||||||
min-coverage-changed-files: 60
|
min-coverage-changed-files: 60
|
||||||
title: ${{ env.REPO }} Coverage
|
title: ${{ env.REPO }} Coverage
|
||||||
|
|
||||||
- id: version
|
|
||||||
name: Version
|
|
||||||
run: echo "VERSION=$(./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout)" >> ${GITHUB_OUTPUT}
|
|
||||||
|
|
||||||
- name: Build Container
|
- name: Build Container
|
||||||
if: github.ref_name == 'main'
|
if: github.ref_name == 'main'
|
||||||
run: docker build -f src/main/docker/Dockerfile.native-micro -t ${{ env.REPO }} .
|
run: docker build -f src/main/docker/Dockerfile.native-micro -t ${{ env.REPO }} .
|
||||||
@@ -50,17 +38,3 @@ jobs:
|
|||||||
- name: Deploy
|
- name: Deploy
|
||||||
if: github.ref_name == 'main'
|
if: github.ref_name == 'main'
|
||||||
run: "docker compose up -d"
|
run: "docker compose up -d"
|
||||||
|
|
||||||
- name: create tag
|
|
||||||
if: github.ref_name == 'main'
|
|
||||||
run: |
|
|
||||||
git config user.email "ci@git.arindy.de"
|
|
||||||
git config user.name "gitea"
|
|
||||||
git add ./pom.xml
|
|
||||||
git commit -m "[no ci] release ${{ steps.version.outputs.VERSION }}"
|
|
||||||
./mvnw -B --no-transfer-progress clean validate -Pnew-snapshot
|
|
||||||
git add ./pom.xml
|
|
||||||
git commit -m "[no ci] prepare new Version"
|
|
||||||
git tag ${{ steps.version.outputs.VERSION }} -m "release ${{ steps.version.outputs.VERSION }}"
|
|
||||||
git push origin main
|
|
||||||
git push origin ${{ steps.version.outputs.VERSION }}
|
|
||||||
|
|||||||
63
README.md
63
README.md
@@ -1,63 +1,2 @@
|
|||||||
<h1 align="center">
|
# dice-tower
|
||||||
Dice-Tower
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<h4 align="center">... they see them rolling ...</h4>
|
|
||||||
|
|
||||||
<p align="center">
|
|
||||||
<a href="#key-features">Key Features</a> •
|
|
||||||
<a href="#how-to-build">How To Build</a> •
|
|
||||||
<a href="#credits">Credits</a> •
|
|
||||||
<a href="#license">License</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Key Features
|
|
||||||
|
|
||||||
* Connect to a room with others
|
|
||||||
* Configure your dice (theme and color)
|
|
||||||
* Roll any dice
|
|
||||||
* See the dice roll (Can be used as a Browser Source in OBS)
|
|
||||||
* Watch roll results (also available as Browser Source in OBS)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## How To Build
|
|
||||||
|
|
||||||
To clone and run this application, you'll need `git`, `java21` and `docker`.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Clone this repository
|
|
||||||
$ git clone https://git.arindy.de/arindy/dice-tower.git
|
|
||||||
|
|
||||||
# Go into the repository
|
|
||||||
$ cd dice-tower
|
|
||||||
|
|
||||||
# Build the binary
|
|
||||||
$ ./mvnw clean verify -Pnative
|
|
||||||
|
|
||||||
# Build the container
|
|
||||||
$ docker build -f src/main/docker/Dockerfile.native-micro -t dice-tower .
|
|
||||||
|
|
||||||
# run the container in the background
|
|
||||||
$ docker run --network host -d dice-tower
|
|
||||||
|
|
||||||
# Visit the dice-tower in your browser on http://localhost:8080
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Credits
|
|
||||||
|
|
||||||
This software uses the following open source packages:
|
|
||||||
|
|
||||||
- [3D-Dice/dice-box](https://github.com/3d-dice/dice-box)
|
|
||||||
- [3D-Dice/dice-themes](https://github.com/3d-dice/dice-themes)
|
|
||||||
- [W3.CSS Color Themes](https://www.w3schools.com/w3css/w3css_color_themes.asp)
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
GPL-3
|
|
||||||
|
|||||||
46
pom.xml
46
pom.xml
@@ -4,7 +4,7 @@
|
|||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>de.arindy</groupId>
|
<groupId>de.arindy</groupId>
|
||||||
<artifactId>dice-tower</artifactId>
|
<artifactId>dice-tower</artifactId>
|
||||||
<version>1.0.2-SNAPSHOT</version>
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<compiler-plugin.version>3.13.0</compiler-plugin.version>
|
<compiler-plugin.version>3.13.0</compiler-plugin.version>
|
||||||
@@ -21,13 +21,6 @@
|
|||||||
<rxjava-version>2.2.8</rxjava-version>
|
<rxjava-version>2.2.8</rxjava-version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<scm>
|
|
||||||
<connection>scm:git:https://git.arindy.de/arindy/dice-tower.git</connection>
|
|
||||||
<developerConnection>scm:git:https://git.arindy.de/arindy/dice-tower.git</developerConnection>
|
|
||||||
<url>https://git.arindy.de/arindy/dice-tower</url>
|
|
||||||
<tag>HEAD</tag>
|
|
||||||
</scm>
|
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -274,42 +267,5 @@
|
|||||||
<quarkus.native.enabled>true</quarkus.native.enabled>
|
<quarkus.native.enabled>true</quarkus.native.enabled>
|
||||||
</properties>
|
</properties>
|
||||||
</profile>
|
</profile>
|
||||||
<profile>
|
|
||||||
<id>new-snapshot</id>
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.codehaus.mojo</groupId>
|
|
||||||
<artifactId>build-helper-maven-plugin</artifactId>
|
|
||||||
<version>3.0.0</version>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>next-version</id>
|
|
||||||
<goals>
|
|
||||||
<goal>parse-version</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.codehaus.mojo</groupId>
|
|
||||||
<artifactId>versions-maven-plugin</artifactId>
|
|
||||||
<version>2.18.0</version>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>release-version</id>
|
|
||||||
<phase>validate</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>set</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<newVersion>${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.nextIncrementalVersion}-SNAPSHOT</newVersion>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</profile>
|
|
||||||
</profiles>
|
</profiles>
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -26,6 +26,9 @@ final class DiceResource(@Context val sse: Sse) {
|
|||||||
@POST
|
@POST
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
fun parseCommand(@PathParam("id") id: String, data: RollPayload) {
|
fun parseCommand(@PathParam("id") id: String, data: RollPayload) {
|
||||||
|
if (!sseBroadcasters.containsKey(id)) {
|
||||||
|
sseBroadcasters[id] = sse.newBroadcaster()
|
||||||
|
}
|
||||||
data.roll = data.command.split(" ", "&", "and").filter { it.isNotEmpty() }.map { it.trim() }.toTypedArray<String>()
|
data.roll = data.command.split(" ", "&", "and").filter { it.isNotEmpty() }.map { it.trim() }.toTypedArray<String>()
|
||||||
data.room = id.split(":")[0]
|
data.room = id.split(":")[0]
|
||||||
data.user = id.split(":")[1]
|
data.user = id.split(":")[1]
|
||||||
@@ -38,17 +41,6 @@ final class DiceResource(@Context val sse: Sse) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@POST
|
|
||||||
@Path("/register")
|
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
|
||||||
fun register(@PathParam("id") id: String, data: Any) {
|
|
||||||
println("id = [${id}], data = [${data}]")
|
|
||||||
sseBroadcasters["register:$id"]?.broadcast(
|
|
||||||
eventBuilder.id((UUID.randomUUID()).toString())
|
|
||||||
.mediaType(MediaType.APPLICATION_JSON_TYPE).data(data).build())
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
@Path("/results")
|
@Path("/results")
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
@@ -63,7 +55,9 @@ final class DiceResource(@Context val sse: Sse) {
|
|||||||
@Path("/stream")
|
@Path("/stream")
|
||||||
@Produces(MediaType.SERVER_SENT_EVENTS)
|
@Produces(MediaType.SERVER_SENT_EVENTS)
|
||||||
fun stream(@PathParam("id") id: String, @Context sseEventSink: SseEventSink) {
|
fun stream(@PathParam("id") id: String, @Context sseEventSink: SseEventSink) {
|
||||||
|
if (!sseBroadcasters.containsKey(id)) {
|
||||||
sseBroadcasters[id] = sse.newBroadcaster()
|
sseBroadcasters[id] = sse.newBroadcaster()
|
||||||
|
}
|
||||||
sseBroadcasters[id]?.register(sseEventSink)
|
sseBroadcasters[id]?.register(sseEventSink)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,17 +71,7 @@ final class DiceResource(@Context val sse: Sse) {
|
|||||||
sseBroadcasters[id]?.register(sseEventSink)
|
sseBroadcasters[id]?.register(sseEventSink)
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
|
||||||
@Path("/users")
|
|
||||||
@Produces(MediaType.SERVER_SENT_EVENTS)
|
|
||||||
fun users(@PathParam("id") id: String, @Context sseEventSink: SseEventSink) {
|
|
||||||
if (!sseBroadcasters.containsKey("register:$id")) {
|
|
||||||
sseBroadcasters["register:$id"] = sse.newBroadcaster()
|
|
||||||
}
|
|
||||||
sseBroadcasters["register:$id"]?.register(sseEventSink)
|
|
||||||
}
|
|
||||||
|
|
||||||
@RegisterForReflection
|
@RegisterForReflection
|
||||||
data class Result(val name: String, val user: String, val themeColor: String)
|
data class Result(val name: String, val user: String, val themeColor: String) {}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import jakarta.ws.rs.GET
|
|||||||
import jakarta.ws.rs.Path
|
import jakarta.ws.rs.Path
|
||||||
import jakarta.ws.rs.PathParam
|
import jakarta.ws.rs.PathParam
|
||||||
import jakarta.ws.rs.Produces
|
import jakarta.ws.rs.Produces
|
||||||
import jakarta.ws.rs.QueryParam
|
|
||||||
import jakarta.ws.rs.core.MediaType
|
import jakarta.ws.rs.core.MediaType
|
||||||
|
|
||||||
@Path("overlay/{diceid}")
|
@Path("overlay/{diceid}")
|
||||||
@@ -13,14 +12,14 @@ class OverlayResource {
|
|||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Produces(MediaType.TEXT_HTML)
|
@Produces(MediaType.TEXT_HTML)
|
||||||
fun get(@PathParam("diceid") diceid: String, @QueryParam("scale") scale: Int? = 7, @QueryParam("clearAfter") clearAfter: Long? = -1): TemplateInstance {
|
fun get(@PathParam("diceid") diceid: String): TemplateInstance {
|
||||||
return Templates.overlay(diceid, scale ?: 7, clearAfter ?: -1)
|
return Templates.overlay(diceid)
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("/results")
|
@Path("/results")
|
||||||
@Produces(MediaType.TEXT_HTML)
|
@Produces(MediaType.TEXT_HTML)
|
||||||
fun results(@PathParam("diceid") room: String, @QueryParam("name") name: String?, @QueryParam("user") user: String?): TemplateInstance {
|
fun results(@PathParam("diceid") room: String): TemplateInstance {
|
||||||
return Templates.results(room, name ?: "all", user ?: "all")
|
return Templates.results(room)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
18
src/main/kotlin/de/arindy/dicetower/ResultsResource.kt
Normal file
18
src/main/kotlin/de/arindy/dicetower/ResultsResource.kt
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package de.arindy.dicetower
|
||||||
|
|
||||||
|
import io.quarkus.qute.TemplateInstance
|
||||||
|
import jakarta.ws.rs.GET
|
||||||
|
import jakarta.ws.rs.Path
|
||||||
|
import jakarta.ws.rs.PathParam
|
||||||
|
import jakarta.ws.rs.Produces
|
||||||
|
import jakarta.ws.rs.core.MediaType
|
||||||
|
|
||||||
|
@Path("results/{room}")
|
||||||
|
class ResultsResource {
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Produces(MediaType.TEXT_HTML)
|
||||||
|
fun get(@PathParam("room") room: String): TemplateInstance {
|
||||||
|
return Templates.results(room)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ import io.quarkus.qute.TemplateInstance
|
|||||||
@CheckedTemplate
|
@CheckedTemplate
|
||||||
object Templates {
|
object Templates {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
external fun overlay(diceid: String, scale: Int?, clearAfter: Long?): TemplateInstance
|
external fun overlay(diceid: String): TemplateInstance
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
external fun results(room: String, name: String?, user: String?): TemplateInstance
|
external fun results(room: String): TemplateInstance
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,6 @@ class ColorPicker extends HTMLElement {
|
|||||||
.rgba-display-wrapper {
|
.rgba-display-wrapper {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding: 1.25em;
|
padding: 1.25em;
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
#rgba-display {
|
#rgba-display {
|
||||||
font-family: "Courier New", monospace;
|
font-family: "Courier New", monospace;
|
||||||
@@ -73,11 +72,11 @@ class ColorPicker extends HTMLElement {
|
|||||||
display: inline;
|
display: inline;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 0.2em;
|
border-radius: 0.2em;
|
||||||
|
margin-bottom: 2.5em;
|
||||||
letter-spacing: 0.1em;
|
letter-spacing: 0.1em;
|
||||||
width: 5em;
|
width: 8em;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
font-variant-numeric: tabular-nums;
|
font-variant-numeric: tabular-nums;
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
input[type=range] {
|
input[type=range] {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -133,17 +132,13 @@ class ColorPicker extends HTMLElement {
|
|||||||
this.lightnessInput = this.shadowRoot.getElementById( 'lightness' );
|
this.lightnessInput = this.shadowRoot.getElementById( 'lightness' );
|
||||||
this.colorPreview = this.shadowRoot.getElementById( 'color-preview' );
|
this.colorPreview = this.shadowRoot.getElementById( 'color-preview' );
|
||||||
this.rgbaDisplay = this.shadowRoot.getElementById( 'rgba-display' );
|
this.rgbaDisplay = this.shadowRoot.getElementById( 'rgba-display' );
|
||||||
this.rgbaDisplay.onkeyup = (event) => {
|
|
||||||
if(event.keyCode === 13) {
|
|
||||||
this.setColor( this.rgbaDisplay.value );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a temporary canvas to read color values.
|
// Create a temporary canvas to read color values.
|
||||||
let tempCanvas = document.createElement( 'canvas' );
|
let tempCanvas = document.createElement( 'canvas' );
|
||||||
tempCanvas.height = 1;
|
tempCanvas.height = 1;
|
||||||
tempCanvas.width = 1;
|
tempCanvas.width = 1;
|
||||||
this.tempCanvasCTX = tempCanvas.getContext( '2d', { willReadFrequently: true } );
|
this.tempCanvasCTX = tempCanvas.getContext( '2d', { willReadFrequently: true } );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
}
|
}
|
||||||
import DiceBox from "/vendor/dice-box/dice-box.es.js";
|
import DiceBox from "/vendor/dice-box/dice-box.es.js";
|
||||||
const evtSource = new EventSource(url() + "/dice/{diceid??}/stream");
|
const evtSource = new EventSource(url() + "/dice/{diceid??}/stream");
|
||||||
|
|
||||||
const diceBox = new DiceBox("#dice-box", {
|
const diceBox = new DiceBox("#dice-box", {
|
||||||
assetPath: "/vendor/assets/",
|
assetPath: "/vendor/assets/",
|
||||||
theme: 'default',
|
theme: 'default',
|
||||||
@@ -55,15 +56,13 @@
|
|||||||
'smooth',
|
'smooth',
|
||||||
'wooden'
|
'wooden'
|
||||||
],
|
],
|
||||||
scale: {scale}
|
scale: 7
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", async() => {
|
document.addEventListener("DOMContentLoaded", async() => {
|
||||||
await diceBox.init()
|
await diceBox.init()
|
||||||
|
|
||||||
let timeout = 0;
|
|
||||||
evtSource.addEventListener("message", function (event) {
|
evtSource.addEventListener("message", function (event) {
|
||||||
clearInterval(timeout);
|
|
||||||
let data = JSON.parse(event.data);
|
let data = JSON.parse(event.data);
|
||||||
diceBox.onRollComplete = (rollResult) => {
|
diceBox.onRollComplete = (rollResult) => {
|
||||||
let httpRequest = new XMLHttpRequest();
|
let httpRequest = new XMLHttpRequest();
|
||||||
@@ -75,12 +74,10 @@
|
|||||||
themeColor: data.themeColor,
|
themeColor: data.themeColor,
|
||||||
results: rollResult,
|
results: rollResult,
|
||||||
} ))
|
} ))
|
||||||
if ({clearAfter} > 0) {
|
|
||||||
timeout = setTimeout(() => diceBox.clear(), {clearAfter} * 1000)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
diceBox.roll(data.roll, { theme: data.theme?.length > 0 ? data.theme : 'default', themeColor: data.themeColor.length > 0 ? data.themeColor : '#4545FF' });
|
diceBox.roll(data.roll, { theme: data.theme?.length > 0 ? data.theme : 'default', themeColor: data.themeColor.length > 0 ? data.themeColor : '#4545FF' });
|
||||||
|
|
||||||
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -4,30 +4,14 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Dice-Tower</title>
|
<title>Dice-Tower</title>
|
||||||
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
|
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
|
||||||
|
<link rel="stylesheet" href="https://www.w3schools.com/lib/w3-theme-black.css">
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||||
<script src="/vendor/color-picker.js"></script>
|
<script src="/vendor/color-picker.js"></script>
|
||||||
<style>
|
<style>
|
||||||
.w3-theme-l6 {
|
|
||||||
color: #000 !important;
|
|
||||||
background-color: #999999 !important;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.w3-theme-l4 {
|
|
||||||
color: #fff !important;
|
|
||||||
background-color: #666666 !important;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.w3-theme-l1 {
|
|
||||||
color: #fff !important;
|
|
||||||
background-color: #333333 !important;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.collapsible {
|
.collapsible {
|
||||||
background-color: #333333;
|
background-color: grey;
|
||||||
color: black;
|
color: black;
|
||||||
|
cursor: pointer;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
padding: 18px;
|
padding: 18px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -41,265 +25,32 @@
|
|||||||
padding: 0 18px;
|
padding: 0 18px;
|
||||||
display: none;
|
display: none;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background-color: #999999;
|
background-color: lightgrey;
|
||||||
color: #000;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border: #333333 3px solid;
|
|
||||||
border-radius: 10px;
|
|
||||||
background: #333333;
|
|
||||||
color: #fff
|
|
||||||
}
|
}
|
||||||
|
|
||||||
button:hover {
|
|
||||||
background: #444444;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:active {
|
|
||||||
background: #222222;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
input {
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The switch - the box around the slider */
|
|
||||||
.switch {
|
|
||||||
position: relative;
|
|
||||||
display: inline-block;
|
|
||||||
width: 60px;
|
|
||||||
height: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hide default HTML checkbox */
|
|
||||||
.switch input {
|
|
||||||
opacity: 0;
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The slider */
|
|
||||||
.slider {
|
|
||||||
position: absolute;
|
|
||||||
cursor: pointer;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background-color: #ccc;
|
|
||||||
-webkit-transition: .4s;
|
|
||||||
transition: .4s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slider:before {
|
|
||||||
position: absolute;
|
|
||||||
content: "";
|
|
||||||
height: 18px;
|
|
||||||
width: 26px;
|
|
||||||
left: 4px;
|
|
||||||
bottom: 4px;
|
|
||||||
background-color: white;
|
|
||||||
-webkit-transition: .4s;
|
|
||||||
transition: .4s;
|
|
||||||
}
|
|
||||||
|
|
||||||
input:checked + .slider {
|
|
||||||
background-color: #333333;
|
|
||||||
}
|
|
||||||
|
|
||||||
input:focus + .slider {
|
|
||||||
box-shadow: 0 0 1px #333333;
|
|
||||||
}
|
|
||||||
|
|
||||||
input:checked + .slider:before {
|
|
||||||
-webkit-transform: translateX(26px);
|
|
||||||
-ms-transform: translateX(26px);
|
|
||||||
transform: translateX(26px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.overlayButton {
|
|
||||||
background: transparent;
|
|
||||||
border: none;
|
|
||||||
width: 5px;
|
|
||||||
height: 5px;
|
|
||||||
font-size: large;
|
|
||||||
color: #000
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.overlayButton:hover {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tooltip {
|
|
||||||
position: fixed;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
padding: 15px;
|
|
||||||
color: #fff !important;
|
|
||||||
background-color: #333333dd !important
|
|
||||||
}
|
|
||||||
|
|
||||||
#dice-box {
|
|
||||||
position: relative;
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: transparent;
|
|
||||||
background-size: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
#dice-box canvas {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
margin: 20px
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox {
|
|
||||||
position: relative;
|
|
||||||
padding-left: 100px;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
margin-left: 10px;
|
|
||||||
cursor: pointer;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-moz-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox input {
|
|
||||||
position: absolute;
|
|
||||||
opacity: 0;
|
|
||||||
cursor: pointer;
|
|
||||||
height: 0;
|
|
||||||
width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkmark {
|
|
||||||
margin-left: 75px;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
height: 15px;
|
|
||||||
width: 15px;
|
|
||||||
background-color: #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox:hover input ~ .checkmark {
|
|
||||||
background-color: #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* When the checkbox is checked, add a blue background */
|
|
||||||
.checkbox input:checked ~ .checkmark {
|
|
||||||
background-color: #333333;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create the checkmark/indicator (hidden when not checked) */
|
|
||||||
.checkmark:after {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Show the checkmark when checked */
|
|
||||||
.checkbox input:checked ~ .checkmark:after {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Style the checkmark/indicator */
|
|
||||||
.checkbox .checkmark:after {
|
|
||||||
left: 5px;
|
|
||||||
top: 2px;
|
|
||||||
width: 5px;
|
|
||||||
height: 10px;
|
|
||||||
border: solid white;
|
|
||||||
border-width: 0 3px 3px 0;
|
|
||||||
-webkit-transform: rotate(45deg);
|
|
||||||
-ms-transform: rotate(45deg);
|
|
||||||
transform: rotate(45deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
#snackbar-container {
|
|
||||||
visibility: hidden;
|
|
||||||
position: fixed;
|
|
||||||
width: 100%;
|
|
||||||
z-index: 1;
|
|
||||||
bottom: 200px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
#snackbar {
|
|
||||||
visibility: hidden;
|
|
||||||
text-align: center;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
padding: 15px;
|
|
||||||
color: #fff !important;
|
|
||||||
border: #fff 5px solid;
|
|
||||||
background-color: #333333dd !important
|
|
||||||
}
|
|
||||||
|
|
||||||
#snackbar-container.show {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
#snackbar.show {
|
|
||||||
visibility: visible;
|
|
||||||
-webkit-animation: fadein 0.5s, fadeout 0.5s 2.5s;
|
|
||||||
animation: fadein 0.5s, fadeout 0.5s 2.5s;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body class="w3-theme-l1">
|
<body class="w3-theme-l4">
|
||||||
<div class="w3-container w3-content"
|
<div class="w3-container w3-content" style="height: 100vh">
|
||||||
style="height: 95vh; display: flex; flex-direction: column; justify-content: space-between; padding: 25px">
|
<h1>Dice-Tower</h1>
|
||||||
<h1 style="text-align: center">Dice-Tower</h1>
|
<p>Welcome to Dice-Tower</p>
|
||||||
<div class="w3-panel w3-theme-l4 w3-card w3-display-container"
|
<div class="w3-panel w3-white w3-card w3-display-container" style="padding: 25px">
|
||||||
style="padding: 25px; text-align: center; margin-bottom: auto;">
|
<label for="name" id="nameLabel">Name </label><input type="text" id="name" style="width: 75%" required onkeyup="start(event)"/><br/>
|
||||||
<label for="name" id="nameLabel">Name </label><input type="text" id="name" style="width: 50%; margin-top: 20px"
|
<label for="room" id="roomLabel">Room </label><input type="text" id="room" style="width: 75%" required onkeyup="start(event)"/><br/>
|
||||||
required onkeyup="start(event)"/><br/>
|
|
||||||
<label for="room" id="roomLabel">Room </label><input type="text" id="room" style="width: 50%" required
|
|
||||||
onkeyup="start(event)"/><br/>
|
|
||||||
<div>
|
|
||||||
<button id="start" onclick="start()" style="align-self: center; margin-top: 20px">Start 🞂</button>
|
|
||||||
<label class="checkbox" id="gm-container">Join as GM
|
|
||||||
<input type="checkbox" id="gm" style="margin-left: 50px">
|
|
||||||
<span class="checkmark"></span>
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div id="dice-tower" hidden class="w3-panel w3-white w3-card w3-display-container" style="padding: 25px">
|
||||||
<div id="dice-tower" hidden class="w3-panel w3-theme-l4 w3-card w3-display-container"
|
<button type="button" class="collapsible" style="color: white; font-weight: bold">Overlay URLs</button>
|
||||||
style="padding: 25px; margin-bottom: auto">
|
|
||||||
<button type="button" class="collapsible" style="color: white; font-weight: bold">Overlay URLs <a>🞃</a></button>
|
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div id="overlay-urls">
|
<label for="overlayId">Dice-Overlay </label><input type="text" readonly id="overlayId" style="width: 75%"/><br/>
|
||||||
<div style="display: flex; flex-direction: row; justify-content: space-between; align-items: baseline;">
|
<label for="resultsId">Results-Overlay </label><input type="text" readonly id="resultsId" style="width: 75%"/><br/>
|
||||||
<label for="overlayId" id="overlayLabel">Dice-Overlay </label>
|
|
||||||
<input type="text" readonly id="overlayId" style="flex-grow: 1" onclick="copyToClipboard(this.id)"/>
|
|
||||||
<button id="overlay-hint-button" popovertarget="overlay-hint" data-trigger="hover"
|
|
||||||
class="overlayButton">🛈
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<button type="button" class="collapsible" style="color: white; font-weight: bold">Customize Dice</button>
|
||||||
<div style="display: flex; flex-direction: row; justify-content: space-between; align-items: baseline;"
|
|
||||||
hidden id="all-results-urls">
|
|
||||||
<label for="resultsId">All-Results-Overlay </label>
|
|
||||||
<input type="text" readonly id="resultsId" style="flex-grow: 1" onclick="copyToClipboard(this.id)"/>
|
|
||||||
<button popovertarget="all-results-hint" data-trigger="hover" class="overlayButton">🛈</button>
|
|
||||||
</div>
|
|
||||||
<div style="display: flex; flex-direction: row; justify-content: space-between; align-items: baseline;">
|
|
||||||
<label for="myResultsId">My-Results-Overlay </label>
|
|
||||||
<input type="text" readonly id="myResultsId" style="flex-grow: 1" onclick="copyToClipboard(this.id)"/>
|
|
||||||
<button popovertarget="my-results-hint" data-trigger="hover" class="overlayButton">🛈</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button type="button" class="collapsible" style="color: white; font-weight: bold">Customize Dice <a>🞃</a>
|
|
||||||
</button>
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<label for="theme">Theme </label>
|
<label for="theme">Theme </label>
|
||||||
<select name="theme" id="theme" style="margin: 25px">
|
<select name="theme" id="theme" style="margin: 25px">
|
||||||
@@ -313,197 +64,40 @@
|
|||||||
<option value="smooth">Smooth</option>
|
<option value="smooth">Smooth</option>
|
||||||
<option value="wooden">Wooden</option>
|
<option value="wooden">Wooden</option>
|
||||||
</select>
|
</select>
|
||||||
<color-picker id="themeColor"></color-picker>
|
<br/>
|
||||||
<div id="dice-box"></div>
|
<color-picker value="#cd72fe" id="themeColor"></color-picker><br/>
|
||||||
|
</div><br/>
|
||||||
<button style="margin: 10px" id="preview">Preview 🔎</button>
|
<p>Example Commands: "1d6", "2d8 1d100", "1d4 and 1d6", "2d20 & 1d2, "5d6+10"</p>
|
||||||
<button style="margin: 10px" onclick="saveDice()">Save 💾</button>
|
<label for="command">Command </label><input type="text" id="command" onkeyup="roll(event)"/>
|
||||||
<button popovertarget="save-dice-hint" data-trigger="hover" class="overlayButton">🛈</button>
|
<button hidden id="roll" onclick="roll()">Roll</button>
|
||||||
</div>
|
|
||||||
<div style="display: flex; flex-direction: row; justify-content: space-between; align-items: center; margin: 20px 25px;">
|
|
||||||
<label style="font-size: x-large; font-weight: bold;">Roll </label>
|
|
||||||
<button style="border: transparent; border-radius: 100%; font-size: x-large; font-weight: bold; height: 50px; width: 50px"
|
|
||||||
onclick="removeDice()">-
|
|
||||||
</button>
|
|
||||||
<label style="font-size: x-large; font-weight: bold;" id="dice-amount">1</label>
|
|
||||||
<button style="border: transparent; border-radius: 100%; font-size: x-large; font-weight: bold; height: 50px; width: 50px"
|
|
||||||
onclick="addDice()">+
|
|
||||||
</button>
|
|
||||||
<button style="font-size: x-large; font-weight: bold;" onclick="rollEasy('d4')">D4</button>
|
|
||||||
<button style="font-size: x-large; font-weight: bold;" onclick="rollEasy('d6')">D6</button>
|
|
||||||
<button style="font-size: x-large; font-weight: bold;" onclick="rollEasy('d8')">D8</button>
|
|
||||||
<button style="font-size: x-large; font-weight: bold;" onclick="rollEasy('d10')">D10</button>
|
|
||||||
<button style="font-size: x-large; font-weight: bold;" onclick="rollEasy('d12')">D12</button>
|
|
||||||
<button style="font-size: x-large; font-weight: bold;" onclick="rollEasy('d20')">D20</button>
|
|
||||||
<button style="font-size: x-large; font-weight: bold;" onclick="rollEasy('d100')">D100</button>
|
|
||||||
</div>
|
|
||||||
<div style="display: flex; flex-direction: row; justify-content: space-between; align-items: baseline; margin: 20px 25px;">
|
|
||||||
<label for="command">Command </label>
|
|
||||||
<input type="text" id="command" style="flex-grow: 1" onkeyup="roll(event)"/>
|
|
||||||
<button hidden id="roll" onclick="roll()">Roll 🎲</button>
|
|
||||||
<button popovertarget="command-hint" data-trigger="hover" class="overlayButton">🛈</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="margin: 20px 25px" id="all-results">
|
<button id="start" onclick="start()">Start</button>
|
||||||
<label for="resultSwitch">Show all results </label>
|
|
||||||
<label class="switch">
|
<div id="results" hidden class="w3-panel w3-white w3-card w3-display-container" style="padding: 25px; overflow: hidden; height: 50vh;">
|
||||||
<input type="checkbox" id="resultSwitch">
|
<h2>Results</h2>
|
||||||
<span class="slider"></span>
|
<iframe id="resultFrame" title="results" style="width: 100%; height: 85%; overflow: hidden" onload="this.height=this.contentWindow.document.body.scrollHeight;"></iframe>
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="results" hidden class="w3-panel w3-theme-l6 w3-card w3-display-container"
|
|
||||||
style="padding: 25px; flex-grow: 1; margin-bottom: auto">
|
|
||||||
<iframe id="resultFrame" title="results" style="width: 100%; height: 85%; overflow: hidden; border: 0"
|
|
||||||
onload="this.height=this.contentWindow.document.body.scrollHeight;"></iframe>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div popover id="overlay-hint" class="tooltip">
|
|
||||||
<p style="color: red; font-weight: bold">Only the last loaded instance of that overlay rolls the dice!</p>
|
|
||||||
<p>Query Params you can Change:</p>
|
|
||||||
<ul>
|
|
||||||
<li><strong>scale</strong> changes the size of the dice (any value over 1)</li>
|
|
||||||
<li><strong>clearAfter</strong> time until dice are cleared (in seconds; remove param or set -1 to keep the
|
|
||||||
dice)
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div popover id="all-results-hint" class="tooltip">
|
|
||||||
<p>Shows all Results in this room</p>
|
|
||||||
</div>
|
|
||||||
<div popover id="my-results-hint" class="tooltip">
|
|
||||||
<p>Shows only my Results in this room</p>
|
|
||||||
</div>
|
|
||||||
<div popover id="save-dice-hint" class="tooltip">
|
|
||||||
This saves your current theme and theme color for current Name
|
|
||||||
</div>
|
|
||||||
<div popover id="command-hint" class="tooltip">
|
|
||||||
Example Commands: "1d6", "2d8 1d100", "1d4 and 1d6", "2d20 & 1d2, "5d6+10"
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="margin-top: 20px; flex-grow: 1" id="how-to">
|
|
||||||
<div class="w3-panel w3-theme-l4 w3-card w3-display-container" style="padding: 25px; margin-bottom: auto">
|
|
||||||
<h2 style="text-align: center">How-To</h2>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
Join a room by entering your character name and the name of the room.<br/>
|
|
||||||
<strong>If you are a GM, make sure to join the room first or let all other players rejoin to get all
|
|
||||||
Overlay-URLs.</strong>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li>Open your Dice-Overlay either in a new Tab or as a browser source in OBS</li>
|
|
||||||
<ul>
|
|
||||||
<li style="color: red; font-weight: bold">Only the last loaded instance of that overlay rolls the
|
|
||||||
dice!
|
|
||||||
</li>
|
|
||||||
<li>You can configure your Overlay with query parameters (more information at the info element next
|
|
||||||
to the link)
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<li>Configure your dice</li>
|
|
||||||
<li>Save your dice configuration</li>
|
|
||||||
<li>Start rolling</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="snackbar-container">
|
|
||||||
<div id="snackbar">.. they see them rolling</div>
|
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
function addDice() {
|
|
||||||
let amount = +document.getElementById('dice-amount').innerText
|
|
||||||
document.getElementById('dice-amount').innerText = amount + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeDice() {
|
|
||||||
let amount = +document.getElementById('dice-amount').innerText
|
|
||||||
if (amount > 1) {
|
|
||||||
document.getElementById('dice-amount').innerText = amount - 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function url() {
|
function url() {
|
||||||
return window.location.protocol + '//' + window.location.hostname + (window.location.port?.length > 0 ? ':' + window.location.port : '');
|
return window.location.protocol + '//' + window.location.hostname + (window.location.port?.length > 0 ? ':' + window.location.port : '');
|
||||||
}
|
}
|
||||||
|
|
||||||
function start(event) {
|
function start(event) {
|
||||||
console.log(document.getElementById('gm').checked)
|
|
||||||
|
|
||||||
if((!event || event.keyCode === 13) && document.getElementById('name').value.length > 0 && document.getElementById('room').value.length > 0) {
|
if((!event || event.keyCode === 13) && document.getElementById('name').value.length > 0 && document.getElementById('room').value.length > 0) {
|
||||||
document.getElementById('overlayId').value = url() + '/overlay/' + document.getElementById('room').value + ':' + localStorage.getItem('userId') + '?scale=7&clearAfter=30';
|
document.getElementById('overlayId').value = url() + '/overlay/' + document.getElementById('room').value + ':' + localStorage.getItem('userId');
|
||||||
document.getElementById('resultsId').value = url() + '/overlay/' + document.getElementById('room').value + '/results';
|
document.getElementById('resultsId').value = url() + '/overlay/' + document.getElementById('room').value + '/results';
|
||||||
document.getElementById('myResultsId').value = document.getElementById('resultsId').value + '?name=' + encodeURIComponent(document.getElementById('name').value) + '&user=' + localStorage.getItem('userId');
|
document.getElementById('resultFrame').src = url() + '/overlay/' + document.getElementById('room').value + '/results';
|
||||||
document.getElementById('resultFrame').src = document.getElementById('myResultsId').value;
|
|
||||||
document.getElementById('roll').hidden = false;
|
document.getElementById('roll').hidden = false;
|
||||||
document.getElementById('start').hidden = true;
|
document.getElementById('start').hidden = true;
|
||||||
document.getElementById('dice-tower').hidden = false;
|
document.getElementById('dice-tower').hidden = false;
|
||||||
document.getElementById('name').hidden = true;
|
document.getElementById('name').hidden = true;
|
||||||
document.getElementById('room').hidden = true;
|
document.getElementById('room').hidden = true;
|
||||||
document.getElementById('gm-container').hidden = true;
|
|
||||||
document.getElementById('how-to').hidden = true;
|
|
||||||
document.getElementById('results').hidden = false;
|
document.getElementById('results').hidden = false;
|
||||||
document.getElementById('all-results').hidden = !document.getElementById('gm').checked;
|
document.getElementById('nameLabel').innerHTML = 'Name: <strong>' + document.getElementById('name').value + '</strong>';
|
||||||
document.getElementById('all-results-urls').style.display = document.getElementById('gm').checked ? 'fles' : 'none';
|
document.getElementById('roomLabel').innerHTML = 'Room: <strong>' + document.getElementById('room').value + '</strong>';
|
||||||
document.getElementById('nameLabel').innerHTML = '<strong style="font-size:x-large;">' + document.getElementById('name').value + '</strong>';
|
|
||||||
document.getElementById('roomLabel').innerHTML = '<strong style="font-size:medium;">' + document.getElementById('room').value + '</strong>';
|
|
||||||
document.getElementById('overlayLabel').innerHTML = 'Dice-Overlay for <strong>' + document.getElementById('name').value + '</strong>';
|
|
||||||
document.title = document.getElementById('name').value + ' - Dice-Tower';
|
|
||||||
|
|
||||||
if (localStorage.getItem(document.getElementById('name').value + "-theme")) {
|
|
||||||
document.getElementById('theme').value = localStorage.getItem(document.getElementById('name').value + "-theme")
|
|
||||||
}
|
}
|
||||||
if (localStorage.getItem(document.getElementById('name').value + "-themeColor")) {
|
|
||||||
document.getElementById('themeColor').setColor(localStorage.getItem(document.getElementById('name').value + "-themeColor"));
|
|
||||||
}
|
|
||||||
let httpRequest = new XMLHttpRequest();
|
|
||||||
httpRequest.open('POST', url() + '/dice/' + document.getElementById('room').value + '/register')
|
|
||||||
httpRequest.setRequestHeader('Content-Type', 'application/json')
|
|
||||||
httpRequest.send(JSON.stringify({
|
|
||||||
name: document.getElementById('name').value,
|
|
||||||
overlay: document.getElementById('overlayId').value,
|
|
||||||
id: document.getElementById('room').value + ':' + localStorage.getItem('userId')
|
|
||||||
}))
|
|
||||||
if (document.getElementById('gm').checked) {
|
|
||||||
document.getElementById('resultSwitch').checked = true;
|
|
||||||
document.getElementById('resultFrame').src = document.getElementById('resultsId').value;
|
|
||||||
const evtSource = new EventSource(url() + '/dice/' + document.getElementById('room').value + '/users');
|
|
||||||
evtSource.addEventListener('message', function (event) {
|
|
||||||
let data = JSON.parse(event.data);
|
|
||||||
if (data.id !== document.getElementById('room').value + ':' + localStorage.getItem('userId')) {
|
|
||||||
let overlays = document.getElementById('overlay-urls');
|
|
||||||
let newOverlay = document.getElementById(data.id) ?? document.createElement('div');
|
|
||||||
newOverlay.replaceChildren(...[]);
|
|
||||||
newOverlay.id = data.id;
|
|
||||||
newOverlay.style.display = "flex";
|
|
||||||
newOverlay.style.flexDirection = "row";
|
|
||||||
newOverlay.style.justifyContent = "space-between";
|
|
||||||
newOverlay.style.alignItems = "baseline";
|
|
||||||
let newLabel = document.createElement('label');
|
|
||||||
newLabel.for = data.id + 'url';
|
|
||||||
newLabel.innerHTML = "Dice-Overlay for <strong>" + data.name + "</strong>";
|
|
||||||
let newInput = document.createElement('input');
|
|
||||||
newInput.type = "text";
|
|
||||||
newInput.readOnly = true;
|
|
||||||
newInput.id = data.id + 'url';
|
|
||||||
newInput.style.flexGrow = '1';
|
|
||||||
newInput.value = data.overlay;
|
|
||||||
newInput.onclick = () => copyToClipboard(newInput.id)
|
|
||||||
let hint = document.getElementById('overlay-hint-button').cloneNode(true);
|
|
||||||
newOverlay.appendChild(newLabel);
|
|
||||||
newOverlay.appendChild(newInput);
|
|
||||||
newOverlay.appendChild(hint);
|
|
||||||
overlays.appendChild(newOverlay);
|
|
||||||
}
|
|
||||||
showPopover();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function rollEasy(dice) {
|
|
||||||
document.getElementById('command').value = document.getElementById('dice-amount').innerText + dice;
|
|
||||||
roll();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function roll(event) {
|
function roll(event) {
|
||||||
@@ -520,98 +114,31 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveDice() {
|
|
||||||
localStorage.setItem(document.getElementById('name').value + "-theme", document.getElementById('theme').value)
|
|
||||||
localStorage.setItem(document.getElementById('name').value + "-themeColor", document.getElementById('themeColor').value)
|
|
||||||
}
|
|
||||||
|
|
||||||
let coll = document.getElementsByClassName("collapsible");
|
let coll = document.getElementsByClassName("collapsible");
|
||||||
|
|
||||||
for (let i = 0; i < coll.length; i++) {
|
for (let i = 0; i < coll.length; i++) {
|
||||||
coll[i].addEventListener("click", function () {
|
coll[i].addEventListener("click", function () {
|
||||||
|
localStorage.setItem("theme", document.getElementById('theme').value)
|
||||||
|
localStorage.setItem("themeColor", document.getElementById('themeColor').value)
|
||||||
const content = this.nextElementSibling;
|
const content = this.nextElementSibling;
|
||||||
if (content.style.display === "block") {
|
if (content.style.display === "block") {
|
||||||
content.style.display = "none";
|
content.style.display = "none";
|
||||||
this.children[0].innerHTML = "🞃"
|
|
||||||
} else {
|
} else {
|
||||||
content.style.display = "block";
|
content.style.display = "block";
|
||||||
this.children[0].innerHTML = "🞁"
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function showPopover() {
|
|
||||||
const popover = document.querySelectorAll("[popovertarget][data-trigger='hover']");
|
|
||||||
|
|
||||||
popover.forEach((e) => {
|
|
||||||
const target = document.querySelector("#" + e.getAttribute("popovertarget"));
|
|
||||||
e.addEventListener("mouseover", () => {
|
|
||||||
target.showPopover();
|
|
||||||
});
|
|
||||||
|
|
||||||
e.addEventListener("mouseout", () => {
|
|
||||||
target.hidePopover();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
showPopover();
|
|
||||||
|
|
||||||
document.getElementById('resultSwitch').addEventListener('change', function () {
|
|
||||||
if (!this.checked) {
|
|
||||||
document.getElementById('resultFrame').src = document.getElementById('myResultsId').value;
|
|
||||||
} else {
|
|
||||||
document.getElementById('resultFrame').src = document.getElementById('resultsId').value;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", async() => {
|
document.addEventListener("DOMContentLoaded", async() => {
|
||||||
if (!localStorage.getItem("userId")) {
|
if (!localStorage.getItem("userId")) {
|
||||||
localStorage.setItem("userId", self.crypto.randomUUID());
|
localStorage.setItem("userId", self.crypto.randomUUID());
|
||||||
}
|
}
|
||||||
})
|
if (localStorage.getItem("theme")) {
|
||||||
|
document.getElementById('theme').value = localStorage.getItem("theme")
|
||||||
function copyToClipboard(id) {
|
|
||||||
let copyText = document.getElementById(id);
|
|
||||||
copyText.select();
|
|
||||||
copyText.setSelectionRange(0, 99999);
|
|
||||||
navigator.clipboard.writeText(copyText.value);
|
|
||||||
showSnackbar("Link copied to clipboard: <br/>" + copyText.value);
|
|
||||||
}
|
}
|
||||||
|
if (localStorage.getItem("themeColor")) {
|
||||||
function showSnackbar(message) {
|
document.getElementById('themeColor').setColor(localStorage.getItem("themeColor"));
|
||||||
let snackbar = document.getElementById("snackbar");
|
|
||||||
let snackbarContainer = document.getElementById("snackbar-container");
|
|
||||||
snackbar.innerHTML = message;
|
|
||||||
snackbar.className = "show";
|
|
||||||
snackbarContainer.className = "show";
|
|
||||||
setTimeout(function () {
|
|
||||||
snackbar.className = snackbar.className.replace("show", "");
|
|
||||||
snackbarContainer.className = snackbarContainer.className.replace("show", "");
|
|
||||||
}, 5000);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<script type="module">
|
|
||||||
import DiceBox from "/vendor/dice-box/dice-box.es.js";
|
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", async () => {
|
|
||||||
document.getElementById('preview').onclick = async () => {
|
|
||||||
document.getElementById('dice-box').replaceChildren(...[])
|
|
||||||
const diceBox = new DiceBox("#dice-box", {
|
|
||||||
assetPath: "/vendor/assets/",
|
|
||||||
theme: document.getElementById('theme').value,
|
|
||||||
themeColor: document.getElementById('themeColor').value,
|
|
||||||
scale: 14
|
|
||||||
});
|
|
||||||
await diceBox.init()
|
|
||||||
diceBox.roll(['1d2', '1d4', '1d6', '1d8', '1d10', '1d12', '1d20', '1d100']);
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
<footer class="w3-theme-l1 w3-center w3-padding-16">
|
|
||||||
<a href="https://git.arindy.de/arindy/dice-tower" target="_blank" class="w3-hover-text-black">Dice-Tower on my
|
|
||||||
GitTea</a>
|
|
||||||
</footer>
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<title>Results</title>
|
<title>Results</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="results" style="font-size: x-large">
|
<div id="results" style="padding: 25px; font-size: x-large">
|
||||||
</div>
|
</div>
|
||||||
<script type="module">
|
<script type="module">
|
||||||
function url() {
|
function url() {
|
||||||
@@ -14,9 +14,8 @@
|
|||||||
const evtSource = new EventSource(url() + '/dice/{room}/results');
|
const evtSource = new EventSource(url() + '/dice/{room}/results');
|
||||||
evtSource.addEventListener('message', function (event) {
|
evtSource.addEventListener('message', function (event) {
|
||||||
let data = JSON.parse(event.data);
|
let data = JSON.parse(event.data);
|
||||||
if ("{name}" === "all" && "{user}" === "all" || "{name}" === data.name && "{user}" === data.user || "{name}" === "all" && "{user}" === data.user || "{name}" === data.name && "{user}" === "all") {
|
let name = document.getElementById(data.user) ?? document.createElement('div');
|
||||||
let name = document.getElementById(data.user + '-' + data.name) ?? document.createElement('div');
|
name.id = data.user;
|
||||||
name.id = data.user + '-' + data.name;
|
|
||||||
name.replaceChildren(...[]);
|
name.replaceChildren(...[]);
|
||||||
let node = document.createElement('p');
|
let node = document.createElement('p');
|
||||||
let resultText = ''
|
let resultText = ''
|
||||||
@@ -31,10 +30,9 @@
|
|||||||
resultText += '<br/>  D' + result.sides + ': [' + values.map(value => value === 1 ? '<strong style="color: red">' + value + '</strong>' : value === result.sides ? '<strong style="color: green">' + value + '</strong>' : value).join(' + ') + (result.modifier > 0 ? ' <a style="text-decoration: underline">+' + result.modifier + '</a>': result.modifier < 0 ? ' <a style="text-decoration: underline">' + result.modifier + '</a>': '') + '] = <strong style="font-size: x-large">' + result.value + '</strong> '
|
resultText += '<br/>  D' + result.sides + ': [' + values.map(value => value === 1 ? '<strong style="color: red">' + value + '</strong>' : value === result.sides ? '<strong style="color: green">' + value + '</strong>' : value).join(' + ') + (result.modifier > 0 ? ' <a style="text-decoration: underline">+' + result.modifier + '</a>': result.modifier < 0 ? ' <a style="text-decoration: underline">' + result.modifier + '</a>': '') + '] = <strong style="font-size: x-large">' + result.value + '</strong> '
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
node.innerHTML = '<strong style="text-shadow: 2px 2px 10px ' + data.themeColor + ';">' + data.name + ':</strong> 🎲 ' + resultText
|
node.innerHTML = '<strong style="text-shadow: 1px 1px 3px black; color: ' + data.themeColor + ';">' + data.name + ':</strong> 🎲 ' + resultText
|
||||||
name.appendChild(node)
|
name.appendChild(node)
|
||||||
document.getElementById('results').insertBefore(name, document.getElementById('results').firstChild);
|
document.getElementById('results').appendChild(name);
|
||||||
}
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Reference in New Issue
Block a user