Compare commits
135 Commits
650f147700
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8942988228 | ||
|
|
6fbedd92c7 | ||
| f4ec070f8f | |||
| b41d423837 | |||
|
|
18bb994a0f | ||
|
|
0e0406fa39 | ||
| 37ce450f2c | |||
| 152511b203 | |||
|
|
9e7bb02832 | ||
|
|
0a3beb2d74 | ||
| 67cffae431 | |||
| 19ff90284a | |||
|
|
6bf306dcc7 | ||
|
|
a215647947 | ||
| 741276e477 | |||
| 4da0d7b1f0 | |||
|
|
2ca9ad6f5a | ||
|
|
ab51fdc281 | ||
| c04b4f67d2 | |||
| d030eda0e9 | |||
|
|
8a4032756e | ||
|
|
717d337535 | ||
| c79cce8651 | |||
| fc82d0397f | |||
|
|
cd9942068f | ||
|
|
fb4c0e7b92 | ||
| 1a0528933b | |||
|
|
0d324239bf | ||
|
|
10eedc06f7 | ||
| fe856aa7c8 | |||
| 8524a8674d | |||
|
|
5d15976c9f | ||
|
|
d0a6a9ce3f | ||
| 87f4d71cc9 | |||
|
|
53a047217a | ||
|
|
3dbd19ee3b | ||
| c0415561ee | |||
|
|
7b9143c26d | ||
|
|
a21db87b98 | ||
| f1c5792a12 | |||
| d163160eb6 | |||
|
|
1c7e793fb2 | ||
|
|
cb1d34fd09 | ||
| 8a212a5044 | |||
| 09473f627b | |||
|
|
fbfd23d335 | ||
|
|
0ccbd9dd0b | ||
| b79c2b1e42 | |||
|
|
f3f788ea51 | ||
|
|
cd5417d1d1 | ||
| f3e8432452 | |||
| 1eae3a8bbf | |||
|
|
dd1497c432 | ||
|
|
867883b1e7 | ||
| af04783685 | |||
| 9a9aa95cd1 | |||
| 9e32ed95d1 | |||
|
|
8089bbeebd | ||
|
|
52d16cfee5 | ||
| e23592b6a4 | |||
| a166eecd60 | |||
| 4b8b2ce5f3 | |||
|
|
022186964d | ||
|
|
4604be151b | ||
| e906c38630 | |||
| 035445ac3f | |||
|
|
562a21c4c5 | ||
|
|
e8159c78f4 | ||
| b9d057a125 | |||
| e565d90b0d | |||
| d313045005 | |||
| f5dd819c5b | |||
|
|
e4b0e6a822 | ||
|
|
abf5783811 | ||
| 0e40272463 | |||
| 197ee02b93 | |||
| 6a5c05f82f | |||
| 21ecf42004 | |||
|
|
1dc4940d8c | ||
|
|
1cefb117fe | ||
| ba48da80ed | |||
| 4fd83f48cd | |||
| 24acc0b942 | |||
| cab7ec34d2 | |||
|
|
c8cd05f035 | ||
|
|
3e35dfb9ba | ||
| f465e1c101 | |||
|
|
2f4cc2de8b | ||
| b977996689 | |||
| 1544776ecc | |||
| 468d9660c4 | |||
| a43f5b30f7 | |||
| 4ebd9a7262 | |||
| ad4179a517 | |||
|
|
f7705e15e5 | ||
| 0299f89901 | |||
|
|
7f7a27a178 | ||
| d9caee420a | |||
|
|
f9893e2a17 | ||
| fdd06978d8 | |||
|
|
13f206ae97 | ||
|
|
827b74ebf1 | ||
| 80d331cdd3 | |||
|
|
0e3d88cd4a | ||
|
|
cb077b50de | ||
|
|
9def3240b2 | ||
|
|
13eacc886d | ||
| af41ff5d78 | |||
|
|
153b25e53f | ||
|
|
2e6267e7b4 | ||
|
|
821e939b3d | ||
| 7ee16d5f3e | |||
|
|
4643ec107e | ||
| 36b7c290da | |||
|
|
b69bdb72ee | ||
| 1518783090 | |||
|
|
39d5107ea4 | ||
| bdbc120e61 | |||
|
|
9b25a69636 | ||
|
|
7f40d85022 | ||
|
|
70f0cc99fc | ||
|
|
f616726aae | ||
|
|
d548ae2ea0 | ||
| cc370fc3a0 | |||
|
|
3a3677c17e | ||
|
|
df5d072636 | ||
|
|
9feee85cd1 | ||
|
|
ce1f43fe98 | ||
|
|
001a35165a | ||
|
|
77f392ed77 | ||
|
|
2dc53ad6f9 | ||
| 5108a72e11 | |||
|
|
9d72d51013 | ||
| 2aba24b677 | |||
|
|
ca538e7980 |
BIN
.github/media/preview.gif
vendored
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
95
.github/workflows/ci.yaml
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
name: CI
|
||||
on: [ push, pull_request ]
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
env:
|
||||
REPO: ${{ github.event.repository.name }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup JDK
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: '21'
|
||||
|
||||
- name: new Version
|
||||
if: github.ref_name == 'main'
|
||||
run: |
|
||||
./mvnw -B --no-transfer-progress versions:set -DremoveSnapshot
|
||||
|
||||
- name: Build Runner
|
||||
run: |
|
||||
./mvnw -B --no-transfer-progress clean verify -Pnative -Dquarkus.native.remote-container-build=true -Djgitver.skip=true
|
||||
|
||||
- name: Add coverage to PR
|
||||
id: jacoco
|
||||
uses: madrapps/jacoco-report@v1.7.1
|
||||
with:
|
||||
paths: ${{ github.workspace }}/**/target/coverage/jacoco.xml
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
min-coverage-overall: 40
|
||||
min-coverage-changed-files: 60
|
||||
title: ${{ env.REPO }} Coverage
|
||||
|
||||
- id: version
|
||||
name: Version
|
||||
run: echo "VERSION=$(./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout)" >> ${GITHUB_OUTPUT}
|
||||
|
||||
- name: Build unstable Container
|
||||
if: github.ref_name != 'main'
|
||||
run: |
|
||||
echo ${{secrets.PACKAGES_TOKEN}} | docker login --username ${{ secrets.PACKAGES_USER }} --password-stdin git.arindy.de
|
||||
docker build -f src/main/docker/Dockerfile.native-micro -t git.arindy.de/arindy/dice-tower:unstable -t git.arindy.de/arindy/dice-tower:${{ steps.version.outputs.VERSION }} .
|
||||
docker push git.arindy.de/arindy/dice-tower:unstable
|
||||
|
||||
- name: Build stable Container
|
||||
if: github.ref_name == 'main'
|
||||
run: |
|
||||
echo ${{secrets.PACKAGES_TOKEN}} | docker login --username ${{ secrets.PACKAGES_USER }} --password-stdin git.arindy.de
|
||||
docker build -f src/main/docker/Dockerfile.native-micro -t git.arindy.de/arindy/dice-tower:latest -t git.arindy.de/arindy/dice-tower:${{ steps.version.outputs.VERSION }} .
|
||||
docker push git.arindy.de/arindy/dice-tower:${{ steps.version.outputs.VERSION }}
|
||||
docker push git.arindy.de/arindy/dice-tower:latest
|
||||
|
||||
- name: Prepare deploy
|
||||
run: |
|
||||
echo ${{secrets.SSH_KNOWN_HOSTS}} >> ~/.ssh/known_hosts
|
||||
base64 -d <<< ${{secrets.SSH_KEY}} > ./.key
|
||||
chmod 600 ./.key
|
||||
|
||||
- name: Deploy unstable
|
||||
if: github.ref_name != 'main'
|
||||
run: "ssh -i ./.key dice-tower@${{secrets.SSH_HOST}} 'docker compose -f compose.unstable.yml pull && docker compose -f compose.unstable.yml up -d' "
|
||||
|
||||
- name: Deploy
|
||||
if: github.ref_name == 'main'
|
||||
run: "ssh -i ./.key dice-tower@${{secrets.SSH_HOST}} 'docker compose -f compose.yml pull && docker compose -f compose.yml up -d' "
|
||||
|
||||
- name: Deploy local
|
||||
if: github.ref_name == 'main'
|
||||
run: "docker compose up -d"
|
||||
|
||||
- name: clean up
|
||||
run: |
|
||||
rm ./.key
|
||||
|
||||
- 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 }}"
|
||||
git tag ${{ steps.version.outputs.VERSION }} -m "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 push origin main
|
||||
git push origin ${{ steps.version.outputs.VERSION }}
|
||||
4
.gitignore
vendored
@@ -48,6 +48,8 @@ replay_pid*
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
*.iml
|
||||
.idea
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
@@ -122,3 +124,5 @@ fabric.properties
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
.key
|
||||
.key/
|
||||
|
||||
1
.mvn/wrapper/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
maven-wrapper.jar
|
||||
93
.mvn/wrapper/MavenWrapperDownloader.java
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.Authenticator;
|
||||
import java.net.PasswordAuthentication;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
public final class MavenWrapperDownloader {
|
||||
private static final String WRAPPER_VERSION = "3.3.2";
|
||||
|
||||
private static final boolean VERBOSE = Boolean.parseBoolean(System.getenv("MVNW_VERBOSE"));
|
||||
|
||||
public static void main(String[] args) {
|
||||
log("Apache Maven Wrapper Downloader " + WRAPPER_VERSION);
|
||||
|
||||
if (args.length != 2) {
|
||||
System.err.println(" - ERROR wrapperUrl or wrapperJarPath parameter missing");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
log(" - Downloader started");
|
||||
final URL wrapperUrl = URI.create(args[0]).toURL();
|
||||
final String jarPath = args[1].replace("..", ""); // Sanitize path
|
||||
final Path wrapperJarPath = Paths.get(jarPath).toAbsolutePath().normalize();
|
||||
downloadFileFromURL(wrapperUrl, wrapperJarPath);
|
||||
log("Done");
|
||||
} catch (IOException e) {
|
||||
System.err.println("- Error downloading: " + e.getMessage());
|
||||
if (VERBOSE) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
private static void downloadFileFromURL(URL wrapperUrl, Path wrapperJarPath)
|
||||
throws IOException {
|
||||
log(" - Downloading to: " + wrapperJarPath);
|
||||
if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
|
||||
final String username = System.getenv("MVNW_USERNAME");
|
||||
final char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
|
||||
Authenticator.setDefault(new Authenticator() {
|
||||
@Override
|
||||
protected PasswordAuthentication getPasswordAuthentication() {
|
||||
return new PasswordAuthentication(username, password);
|
||||
}
|
||||
});
|
||||
}
|
||||
Path temp = wrapperJarPath
|
||||
.getParent()
|
||||
.resolve(wrapperJarPath.getFileName() + "."
|
||||
+ Long.toUnsignedString(ThreadLocalRandom.current().nextLong()) + ".tmp");
|
||||
try (InputStream inStream = wrapperUrl.openStream()) {
|
||||
Files.copy(inStream, temp, StandardCopyOption.REPLACE_EXISTING);
|
||||
Files.move(temp, wrapperJarPath, StandardCopyOption.REPLACE_EXISTING);
|
||||
} finally {
|
||||
Files.deleteIfExists(temp);
|
||||
}
|
||||
log(" - Downloader complete");
|
||||
}
|
||||
|
||||
private static void log(String msg) {
|
||||
if (VERBOSE) {
|
||||
System.out.println(msg);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
20
.mvn/wrapper/maven-wrapper.properties
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
wrapperVersion=3.3.2
|
||||
distributionType=source
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.8/apache-maven-3.9.8-bin.zip
|
||||
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar
|
||||
90
README.md
@@ -1,2 +1,90 @@
|
||||
# dice-tower
|
||||
<h1 align="center">
|
||||
Dice-Tower
|
||||
</h1>
|
||||
|
||||
<h4 align="center">... they see them rolling ...</h4>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://dice-tower.com" target="_blank">Dice-Tower</a> •
|
||||
<a href="#key-features">Key Features</a> •
|
||||
<a href="#start-container">Start Container</a> •
|
||||
<a href="#how-to-build-from-scratch">How To Build from scratch</a> •
|
||||
<a href="#credits">Credits</a> •
|
||||
<a href="#license">License</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src=".github/media/preview.gif" />
|
||||
</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)
|
||||
|
||||
---
|
||||
|
||||
## Start Container
|
||||
|
||||
You can start dice-tower with docker compose
|
||||
|
||||
Create a `compose.yml`-File with following content:
|
||||
```yaml
|
||||
services:
|
||||
dice-tower:
|
||||
container_name: dice-tower
|
||||
image: git.arindy.de/arindy/dice-tower:latest
|
||||
restart: always
|
||||
ports:
|
||||
- "8080:8080"
|
||||
environment:
|
||||
DICE_LIMIT: 30 # OPTIONAL: amount of dice allowed to roll (default: 30)
|
||||
```
|
||||
Run the container with:
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## How To Build from scratch
|
||||
|
||||
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-threejs](https://github.com/3d-dice/dice-box-threejs)
|
||||
- [W3.CSS Color Themes](https://www.w3schools.com/w3css/w3css_color_themes.asp)
|
||||
- [Font Awesome](https://fontawesome.com)
|
||||
|
||||
## License
|
||||
|
||||
GPL-3
|
||||
|
||||
11
compose.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
services:
|
||||
dice-tower:
|
||||
container_name: dice-tower
|
||||
image: git.arindy.de/arindy/dice-tower:latest
|
||||
restart: always
|
||||
networks:
|
||||
- reverse_proxy
|
||||
|
||||
networks:
|
||||
reverse_proxy:
|
||||
external: true
|
||||
332
mvnw
vendored
Executable file
@@ -0,0 +1,332 @@
|
||||
#!/bin/sh
|
||||
# ----------------------------------------------------------------------------
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Apache Maven Wrapper startup batch script, version 3.3.2
|
||||
#
|
||||
# Required ENV vars:
|
||||
# ------------------
|
||||
# JAVA_HOME - location of a JDK home dir
|
||||
#
|
||||
# Optional ENV vars
|
||||
# -----------------
|
||||
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||
# e.g. to debug Maven itself, use
|
||||
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
if [ -z "$MAVEN_SKIP_RC" ]; then
|
||||
|
||||
if [ -f /usr/local/etc/mavenrc ]; then
|
||||
. /usr/local/etc/mavenrc
|
||||
fi
|
||||
|
||||
if [ -f /etc/mavenrc ]; then
|
||||
. /etc/mavenrc
|
||||
fi
|
||||
|
||||
if [ -f "$HOME/.mavenrc" ]; then
|
||||
. "$HOME/.mavenrc"
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
# OS specific support. $var _must_ be set to either true or false.
|
||||
cygwin=false
|
||||
darwin=false
|
||||
mingw=false
|
||||
case "$(uname)" in
|
||||
CYGWIN*) cygwin=true ;;
|
||||
MINGW*) mingw=true ;;
|
||||
Darwin*)
|
||||
darwin=true
|
||||
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
|
||||
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
if [ -x "/usr/libexec/java_home" ]; then
|
||||
JAVA_HOME="$(/usr/libexec/java_home)"
|
||||
export JAVA_HOME
|
||||
else
|
||||
JAVA_HOME="/Library/Java/Home"
|
||||
export JAVA_HOME
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
if [ -r /etc/gentoo-release ]; then
|
||||
JAVA_HOME=$(java-config --jre-home)
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched
|
||||
if $cygwin; then
|
||||
[ -n "$JAVA_HOME" ] \
|
||||
&& JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
|
||||
[ -n "$CLASSPATH" ] \
|
||||
&& CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
|
||||
fi
|
||||
|
||||
# For Mingw, ensure paths are in UNIX format before anything is touched
|
||||
if $mingw; then
|
||||
[ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] \
|
||||
&& JAVA_HOME="$(
|
||||
cd "$JAVA_HOME" || (
|
||||
echo "cannot cd into $JAVA_HOME." >&2
|
||||
exit 1
|
||||
)
|
||||
pwd
|
||||
)"
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
javaExecutable="$(which javac)"
|
||||
if [ -n "$javaExecutable" ] && ! [ "$(expr "$javaExecutable" : '\([^ ]*\)')" = "no" ]; then
|
||||
# readlink(1) is not available as standard on Solaris 10.
|
||||
readLink=$(which readlink)
|
||||
if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then
|
||||
if $darwin; then
|
||||
javaHome="$(dirname "$javaExecutable")"
|
||||
javaExecutable="$(cd "$javaHome" && pwd -P)/javac"
|
||||
else
|
||||
javaExecutable="$(readlink -f "$javaExecutable")"
|
||||
fi
|
||||
javaHome="$(dirname "$javaExecutable")"
|
||||
javaHome=$(expr "$javaHome" : '\(.*\)/bin')
|
||||
JAVA_HOME="$javaHome"
|
||||
export JAVA_HOME
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$JAVACMD" ]; then
|
||||
if [ -n "$JAVA_HOME" ]; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ]; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
else
|
||||
JAVACMD="$(
|
||||
\unset -f command 2>/dev/null
|
||||
\command -v java
|
||||
)"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -x "$JAVACMD" ]; then
|
||||
echo "Error: JAVA_HOME is not defined correctly." >&2
|
||||
echo " We cannot execute $JAVACMD" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
echo "Warning: JAVA_HOME environment variable is not set." >&2
|
||||
fi
|
||||
|
||||
# traverses directory structure from process work directory to filesystem root
|
||||
# first directory with .mvn subdirectory is considered project base directory
|
||||
find_maven_basedir() {
|
||||
if [ -z "$1" ]; then
|
||||
echo "Path not specified to find_maven_basedir" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
basedir="$1"
|
||||
wdir="$1"
|
||||
while [ "$wdir" != '/' ]; do
|
||||
if [ -d "$wdir"/.mvn ]; then
|
||||
basedir=$wdir
|
||||
break
|
||||
fi
|
||||
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
|
||||
if [ -d "${wdir}" ]; then
|
||||
wdir=$(
|
||||
cd "$wdir/.." || exit 1
|
||||
pwd
|
||||
)
|
||||
fi
|
||||
# end of workaround
|
||||
done
|
||||
printf '%s' "$(
|
||||
cd "$basedir" || exit 1
|
||||
pwd
|
||||
)"
|
||||
}
|
||||
|
||||
# concatenates all lines of a file
|
||||
concat_lines() {
|
||||
if [ -f "$1" ]; then
|
||||
# Remove \r in case we run on Windows within Git Bash
|
||||
# and check out the repository with auto CRLF management
|
||||
# enabled. Otherwise, we may read lines that are delimited with
|
||||
# \r\n and produce $'-Xarg\r' rather than -Xarg due to word
|
||||
# splitting rules.
|
||||
tr -s '\r\n' ' ' <"$1"
|
||||
fi
|
||||
}
|
||||
|
||||
log() {
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
printf '%s\n' "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
BASE_DIR=$(find_maven_basedir "$(dirname "$0")")
|
||||
if [ -z "$BASE_DIR" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
|
||||
export MAVEN_PROJECTBASEDIR
|
||||
log "$MAVEN_PROJECTBASEDIR"
|
||||
|
||||
##########################################################################################
|
||||
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||
# This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||
##########################################################################################
|
||||
wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar"
|
||||
if [ -r "$wrapperJarPath" ]; then
|
||||
log "Found $wrapperJarPath"
|
||||
else
|
||||
log "Couldn't find $wrapperJarPath, downloading it ..."
|
||||
|
||||
if [ -n "$MVNW_REPOURL" ]; then
|
||||
wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar"
|
||||
else
|
||||
wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar"
|
||||
fi
|
||||
while IFS="=" read -r key value; do
|
||||
# Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' )
|
||||
safeValue=$(echo "$value" | tr -d '\r')
|
||||
case "$key" in wrapperUrl)
|
||||
wrapperUrl="$safeValue"
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done <"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
|
||||
log "Downloading from: $wrapperUrl"
|
||||
|
||||
if $cygwin; then
|
||||
wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
|
||||
fi
|
||||
|
||||
if command -v wget >/dev/null; then
|
||||
log "Found wget ... using wget"
|
||||
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet"
|
||||
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||
wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
|
||||
else
|
||||
wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
|
||||
fi
|
||||
elif command -v curl >/dev/null; then
|
||||
log "Found curl ... using curl"
|
||||
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent"
|
||||
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||
curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
|
||||
else
|
||||
curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
|
||||
fi
|
||||
else
|
||||
log "Falling back to using Java to download"
|
||||
javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java"
|
||||
javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class"
|
||||
# For Cygwin, switch paths to Windows format before running javac
|
||||
if $cygwin; then
|
||||
javaSource=$(cygpath --path --windows "$javaSource")
|
||||
javaClass=$(cygpath --path --windows "$javaClass")
|
||||
fi
|
||||
if [ -e "$javaSource" ]; then
|
||||
if [ ! -e "$javaClass" ]; then
|
||||
log " - Compiling MavenWrapperDownloader.java ..."
|
||||
("$JAVA_HOME/bin/javac" "$javaSource")
|
||||
fi
|
||||
if [ -e "$javaClass" ]; then
|
||||
log " - Running MavenWrapperDownloader.java ..."
|
||||
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
##########################################################################################
|
||||
# End of extension
|
||||
##########################################################################################
|
||||
|
||||
# If specified, validate the SHA-256 sum of the Maven wrapper jar file
|
||||
wrapperSha256Sum=""
|
||||
while IFS="=" read -r key value; do
|
||||
case "$key" in wrapperSha256Sum)
|
||||
wrapperSha256Sum=$value
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done <"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
|
||||
if [ -n "$wrapperSha256Sum" ]; then
|
||||
wrapperSha256Result=false
|
||||
if command -v sha256sum >/dev/null; then
|
||||
if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c >/dev/null 2>&1; then
|
||||
wrapperSha256Result=true
|
||||
fi
|
||||
elif command -v shasum >/dev/null; then
|
||||
if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c >/dev/null 2>&1; then
|
||||
wrapperSha256Result=true
|
||||
fi
|
||||
else
|
||||
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
|
||||
echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ $wrapperSha256Result = false ]; then
|
||||
echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2
|
||||
echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2
|
||||
echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin; then
|
||||
[ -n "$JAVA_HOME" ] \
|
||||
&& JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
|
||||
[ -n "$CLASSPATH" ] \
|
||||
&& CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
|
||||
[ -n "$MAVEN_PROJECTBASEDIR" ] \
|
||||
&& MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
|
||||
fi
|
||||
|
||||
# Provide a "standardized" way to retrieve the CLI args that will
|
||||
# work with both Windows and non-Windows executions.
|
||||
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*"
|
||||
export MAVEN_CMD_LINE_ARGS
|
||||
|
||||
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||
|
||||
# shellcheck disable=SC2086 # safe args
|
||||
exec "$JAVACMD" \
|
||||
$MAVEN_OPTS \
|
||||
$MAVEN_DEBUG_OPTS \
|
||||
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
|
||||
"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
|
||||
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
|
||||
315
pom.xml
Normal file
@@ -0,0 +1,315 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>de.arindy</groupId>
|
||||
<artifactId>dice-tower</artifactId>
|
||||
<version>1.2.8-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<compiler-plugin.version>3.13.0</compiler-plugin.version>
|
||||
<kotlin.version>2.0.21</kotlin.version>
|
||||
<maven.compiler.release>21</maven.compiler.release>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
|
||||
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
|
||||
<quarkus.platform.version>3.18.2</quarkus.platform.version>
|
||||
<skipITs>true</skipITs>
|
||||
<surefire-plugin.version>3.5.2</surefire-plugin.version>
|
||||
<jacoco.version>0.8.12</jacoco.version>
|
||||
<rxjava-version>2.2.8</rxjava-version>
|
||||
</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>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>${quarkus.platform.group-id}</groupId>
|
||||
<artifactId>${quarkus.platform.artifact-id}</artifactId>
|
||||
<version>${quarkus.platform.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-rest-jackson</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-kotlin</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-arc</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-rest</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkiverse.qute.web</groupId>
|
||||
<artifactId>quarkus-qute-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-junit5</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.rest-assured</groupId>
|
||||
<artifactId>rest-assured</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.rest-assured</groupId>
|
||||
<artifactId>kotlin-extensions</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib-jdk8</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-test</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-jacoco</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-messaging</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-reactive-routes</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<sourceDirectory>src/main/kotlin</sourceDirectory>
|
||||
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>${quarkus.platform.group-id}</groupId>
|
||||
<artifactId>quarkus-maven-plugin</artifactId>
|
||||
<version>${quarkus.platform.version}</version>
|
||||
<extensions>true</extensions>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>build</goal>
|
||||
<goal>generate-code</goal>
|
||||
<goal>generate-code-tests</goal>
|
||||
<goal>native-image-agent</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>${compiler-plugin.version}</version>
|
||||
<configuration>
|
||||
<parameters>true</parameters>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>${surefire-plugin.version}</version>
|
||||
<configuration>
|
||||
<systemPropertyVariables>
|
||||
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
|
||||
<quarkus.jacoco.data-file>${maven.multiModuleProjectDirectory}/target/jacoco.exec
|
||||
</quarkus.jacoco.data-file>
|
||||
<quarkus.jacoco.reuse-data-file>true</quarkus.jacoco.reuse-data-file>
|
||||
<quarkus.jacoco.report-location>${maven.multiModuleProjectDirectory}/target/coverage
|
||||
</quarkus.jacoco.report-location>
|
||||
<maven.home>${maven.home}</maven.home>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<version>${surefire-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>integration-test</goal>
|
||||
<goal>verify</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<systemPropertyVariables>
|
||||
<native.image.path>${project.build.directory}/${project.build.finalName}-runner
|
||||
</native.image.path>
|
||||
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
|
||||
<maven.home>${maven.home}</maven.home>
|
||||
<quarkus.test.arg-line>${argLine}</quarkus.test.arg-line>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<version>${jacoco.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>default-prepare-agent</id>
|
||||
<goals>
|
||||
<goal>prepare-agent</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<exclClassLoaders>*QuarkusClassLoader</exclClassLoaders>
|
||||
<destFile>${project.build.directory}/jacoco-quarkus.exec</destFile>
|
||||
<append>true</append>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>default-prepare-agent-integration</id>
|
||||
<goals>
|
||||
<goal>prepare-agent-integration</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<destFile>${project.build.directory}/jacoco-quarkus.exec</destFile>
|
||||
<append>true</append>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>report</id>
|
||||
<phase>post-integration-test</phase>
|
||||
<goals>
|
||||
<goal>report</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<dataFile>${project.build.directory}/jacoco-quarkus.exec</dataFile>
|
||||
<outputDirectory>${project.build.directory}/jacoco-report</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-plugin</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>compile</id>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<sourceDirs>
|
||||
<source>src/main/kotlin</source>
|
||||
<source>target/generated-sources/annotations</source>
|
||||
</sourceDirs>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>test-compile</id>
|
||||
<phase>test-compile</phase>
|
||||
<goals>
|
||||
<goal>test-compile</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<sourceDirs>
|
||||
<source>src/test/kotlin</source>
|
||||
<source>target/generated-test-sources/test-annotations</source>
|
||||
</sourceDirs>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-allopen</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<configuration>
|
||||
<javaParameters>true</javaParameters>
|
||||
<jvmTarget>1.8</jvmTarget>
|
||||
<compilerPlugins>
|
||||
<plugin>all-open</plugin>
|
||||
</compilerPlugins>
|
||||
<pluginOptions>
|
||||
<option>all-open:annotation=jakarta.ws.rs.Path</option>
|
||||
<option>all-open:annotation=jakarta.enterprise.context.ApplicationScoped</option>
|
||||
<option>all-open:annotation=jakarta.persistence.Entity</option>
|
||||
<option>all-open:annotation=io.quarkus.test.junit.QuarkusTest</option>
|
||||
</pluginOptions>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>native</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>native</name>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<skipITs>false</skipITs>
|
||||
<quarkus.native.enabled>true</quarkus.native.enabled>
|
||||
</properties>
|
||||
</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>
|
||||
</project>
|
||||
11
src/main/docker/Dockerfile.native
Normal file
@@ -0,0 +1,11 @@
|
||||
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.10
|
||||
WORKDIR /work/
|
||||
RUN chown 1001 /work \
|
||||
&& chmod "g+rwX" /work \
|
||||
&& chown 1001:root /work
|
||||
COPY --chown=1001:root target/*-runner /work/application
|
||||
|
||||
EXPOSE 8080
|
||||
USER 1001
|
||||
|
||||
ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"]
|
||||
11
src/main/docker/Dockerfile.native-micro
Normal file
@@ -0,0 +1,11 @@
|
||||
FROM quay.io/quarkus/quarkus-micro-image:2.0
|
||||
WORKDIR /work/
|
||||
RUN chown 1001 /work \
|
||||
&& chmod "g+rwX" /work \
|
||||
&& chown 1001:root /work
|
||||
COPY --chown=1001:root target/*-runner /work/application
|
||||
|
||||
EXPOSE 8080
|
||||
USER 1001
|
||||
|
||||
ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"]
|
||||
43
src/main/kotlin/de/arindy/dicetower/ChatOverlayResource.kt
Normal file
@@ -0,0 +1,43 @@
|
||||
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.QueryParam
|
||||
import jakarta.ws.rs.core.MediaType
|
||||
|
||||
@Path("chatoverlay")
|
||||
class ChatOverlayResource {
|
||||
|
||||
@GET
|
||||
@Path("/{channel}")
|
||||
@Produces(MediaType.TEXT_HTML)
|
||||
fun get(
|
||||
@PathParam("channel") channel: String,
|
||||
@QueryParam("scale") scale: Int? = 7,
|
||||
@QueryParam("maxDice") maxDice: Int? = 20,
|
||||
@QueryParam("modsAllowed") modsAllowed: Boolean = false,
|
||||
@QueryParam("vipAllowed") vipAllowed: Boolean = false,
|
||||
@QueryParam("subsAllowed") subsAllowed: Boolean = false,
|
||||
@QueryParam("allAllowed") allAllowed: Boolean = false,
|
||||
@QueryParam("cmd") cmd: String? = "roll",
|
||||
@QueryParam("theme") theme: String? = "default",
|
||||
@QueryParam("faceColor") faceColor: String? = "#ff0202",
|
||||
@QueryParam("numberColor") numberColor: String? = "#ffffff",
|
||||
@QueryParam("clearAfter") clearAfter: Long? = -1,
|
||||
@QueryParam("timeout") timeout: Long? = -1,
|
||||
@QueryParam("showResults") showResults: Boolean = true
|
||||
): TemplateInstance {
|
||||
return Templates.chatoverlay(channel, scale ?: 7, maxDice ?: 20, modsAllowed, vipAllowed, subsAllowed, allAllowed, cmd ?: "roll", theme ?: "default", faceColor ?: "#ff0202", numberColor ?: "#ffffff", clearAfter ?: 10, timeout ?: 60, showResults)
|
||||
}
|
||||
|
||||
@GET
|
||||
@Produces(MediaType.TEXT_HTML)
|
||||
fun config(
|
||||
): TemplateInstance {
|
||||
return Templates.chatoverlayconfig()
|
||||
}
|
||||
|
||||
}
|
||||
188
src/main/kotlin/de/arindy/dicetower/DiceResource.kt
Normal file
@@ -0,0 +1,188 @@
|
||||
package de.arindy.dicetower
|
||||
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection
|
||||
import jakarta.enterprise.context.ApplicationScoped
|
||||
import jakarta.ws.rs.Consumes
|
||||
import jakarta.ws.rs.GET
|
||||
import jakarta.ws.rs.POST
|
||||
import jakarta.ws.rs.Path
|
||||
import jakarta.ws.rs.PathParam
|
||||
import jakarta.ws.rs.Produces
|
||||
import jakarta.ws.rs.core.Context
|
||||
import jakarta.ws.rs.core.MediaType
|
||||
import jakarta.ws.rs.sse.OutboundSseEvent
|
||||
import jakarta.ws.rs.sse.Sse
|
||||
import jakarta.ws.rs.sse.SseBroadcaster
|
||||
import jakarta.ws.rs.sse.SseEventSink
|
||||
import org.eclipse.microprofile.config.inject.ConfigProperty
|
||||
import java.util.Optional
|
||||
import java.util.UUID
|
||||
|
||||
private const val LIMIT = 30
|
||||
|
||||
@Path("/dice/{id}")
|
||||
@ApplicationScoped
|
||||
final class DiceResource(@Context val sse: Sse) {
|
||||
|
||||
@ConfigProperty(name = "dice.limit")
|
||||
private lateinit var diceLimit: Optional<Int>
|
||||
|
||||
private var eventBuilder: OutboundSseEvent.Builder = sse.newEventBuilder()
|
||||
private var sseBroadcasters: MutableMap<String, SseBroadcaster> = HashMap()
|
||||
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
fun parseCommand(@PathParam("id") id: String, data: RollPayload) {
|
||||
data.room = id.split(":")[0]
|
||||
data.user = id.split(":")[1]
|
||||
val results = ArrayList<Results>()
|
||||
if (data.results == null) {
|
||||
var numberOfDice = 0
|
||||
data.command.split(" ", "&", "and").filter { it.isNotEmpty() }.map { it.trim() }.toTypedArray<String>().forEach { command ->
|
||||
val dice = command.split("d")
|
||||
var amount = dice[0].toInt()
|
||||
val limit = diceLimit.orElse(LIMIT)
|
||||
if (limit < numberOfDice + amount) {
|
||||
amount = limit - numberOfDice
|
||||
}
|
||||
numberOfDice += amount
|
||||
if (amount > 0) {
|
||||
val result = IntArray(amount)
|
||||
val sides = dice[1].split("+", "-")
|
||||
val modifier = if (dice[1].contains("+")) sides[1].toInt() else if (dice[1].contains("+")) -1 * sides[1].toInt() else 0
|
||||
repeat(amount) { index ->
|
||||
result[index] = (Math.random() * sides[0].toInt() + 1).toInt()
|
||||
}
|
||||
results.add(Results(sides[0].toInt(), modifier, result.sum() + modifier, result.map { Roll(it) }.toTypedArray()))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
results.addAll(data.results)
|
||||
}
|
||||
|
||||
val map = results.map { r ->
|
||||
"${r.rolls.size}d${r.sides}@${
|
||||
if (r.sides == 100) r.rolls.map { roll -> Roll(roll.value / 10 * 10) }
|
||||
.joinToString(",") else r.rolls.joinToString(",")
|
||||
}"
|
||||
}.toTypedArray()
|
||||
|
||||
data.roll = map + results.filter { it.sides == 100 }.map { r -> "${r.rolls.size}d10@${r.rolls.map { roll -> Roll(roll.value % 10) }.joinToString(",")}"}.toTypedArray()
|
||||
if (data.roll.all { it.trim().isNotEmpty() }) {
|
||||
results(data.room!!, Result(data.name, data.user!!, data.faceColor, null))
|
||||
}
|
||||
sseBroadcasters[id]?.broadcast(
|
||||
eventBuilder.id((UUID.randomUUID()).toString())
|
||||
.mediaType(MediaType.APPLICATION_JSON_TYPE).data(data).build()
|
||||
)
|
||||
Thread.sleep(1000)
|
||||
results(data.room!!, Result(data.name, data.user!!, data.faceColor, results.toTypedArray()))
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/register")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
fun register(@PathParam("id") id: String, data: Any) {
|
||||
sseBroadcasters["register:$id"]?.broadcast(
|
||||
eventBuilder.id((UUID.randomUUID()).toString())
|
||||
.mediaType(MediaType.APPLICATION_JSON_TYPE).data(data).build())
|
||||
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/results")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
fun results(@PathParam("id") id: String, data: Any) {
|
||||
sseBroadcasters[id]?.broadcast(
|
||||
eventBuilder.id((UUID.randomUUID()).toString())
|
||||
.mediaType(MediaType.APPLICATION_JSON_TYPE).data(data).build()
|
||||
)
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/stream")
|
||||
@Produces(MediaType.SERVER_SENT_EVENTS)
|
||||
fun stream(@PathParam("id") id: String, @Context sseEventSink: SseEventSink) {
|
||||
if (!sseBroadcasters.containsKey(id)) {
|
||||
sseBroadcasters[id] = sse.newBroadcaster()
|
||||
}
|
||||
sseBroadcasters[id]?.register(sseEventSink)
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/results")
|
||||
@Produces(MediaType.SERVER_SENT_EVENTS)
|
||||
fun results(@PathParam("id") id: String, @Context sseEventSink: SseEventSink) {
|
||||
if (!sseBroadcasters.containsKey(id)) {
|
||||
sseBroadcasters[id] = sse.newBroadcaster()
|
||||
}
|
||||
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
|
||||
data class Result(val name: String, val user: String, val faceColor: String, val results: Array<Results>?) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as Result
|
||||
|
||||
if (name != other.name) return false
|
||||
if (user != other.user) return false
|
||||
if (faceColor != other.faceColor) return false
|
||||
if (results != null) {
|
||||
if (other.results == null) return false
|
||||
if (!results.contentEquals(other.results)) return false
|
||||
} else if (other.results != null) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = name.hashCode()
|
||||
result = 31 * result + user.hashCode()
|
||||
result = 31 * result + faceColor.hashCode()
|
||||
result = 31 * result + (results?.contentHashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@RegisterForReflection
|
||||
data class Results(val sides: Int, val modifier: Int, val value: Int, val rolls: Array<Roll>) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as Results
|
||||
|
||||
if (sides != other.sides) return false
|
||||
if (modifier != other.modifier) return false
|
||||
if (value != other.value) return false
|
||||
if (!rolls.contentEquals(other.rolls)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = sides
|
||||
result = 31 * result + modifier
|
||||
result = 31 * result + value
|
||||
result = 31 * result + rolls.contentHashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@RegisterForReflection
|
||||
data class Roll(val value: Int)
|
||||
|
||||
}
|
||||
24
src/main/kotlin/de/arindy/dicetower/IndexResource.kt
Normal file
@@ -0,0 +1,24 @@
|
||||
package de.arindy.dicetower
|
||||
|
||||
import io.quarkus.qute.TemplateInstance
|
||||
import jakarta.enterprise.context.ApplicationScoped
|
||||
import jakarta.ws.rs.GET
|
||||
import jakarta.ws.rs.Path
|
||||
import jakarta.ws.rs.Produces
|
||||
import jakarta.ws.rs.core.MediaType
|
||||
import org.eclipse.microprofile.config.inject.ConfigProperty
|
||||
|
||||
@Path("/")
|
||||
@ApplicationScoped
|
||||
final class IndexResource() {
|
||||
|
||||
@ConfigProperty(name = "quarkus.application.version")
|
||||
private lateinit var version: String
|
||||
|
||||
@GET
|
||||
@Produces(MediaType.TEXT_HTML)
|
||||
fun get(): TemplateInstance {
|
||||
return Templates.index(version)
|
||||
}
|
||||
|
||||
}
|
||||
26
src/main/kotlin/de/arindy/dicetower/OverlayResource.kt
Normal file
@@ -0,0 +1,26 @@
|
||||
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.QueryParam
|
||||
import jakarta.ws.rs.core.MediaType
|
||||
|
||||
@Path("overlay/{diceid}")
|
||||
class OverlayResource {
|
||||
|
||||
@GET
|
||||
@Produces(MediaType.TEXT_HTML)
|
||||
fun get(@PathParam("diceid") diceid: String, @QueryParam("scale") scale: Int? = 7, @QueryParam("clearAfter") clearAfter: Long? = -1): TemplateInstance {
|
||||
return Templates.overlay(diceid, scale ?: 7, clearAfter ?: -1)
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/results")
|
||||
@Produces(MediaType.TEXT_HTML)
|
||||
fun results(@PathParam("diceid") room: String, @QueryParam("name") name: String?): TemplateInstance {
|
||||
return Templates.results(room, name ?: "all")
|
||||
}
|
||||
}
|
||||
18
src/main/kotlin/de/arindy/dicetower/RollPayload.kt
Normal file
@@ -0,0 +1,18 @@
|
||||
package de.arindy.dicetower
|
||||
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection
|
||||
|
||||
@RegisterForReflection
|
||||
data class RollPayload(
|
||||
val name: String,
|
||||
var command: String,
|
||||
val results: Array<DiceResource.Results>?,
|
||||
val faceColor: String = "white",
|
||||
val numberColor: String = "white",
|
||||
val theme: String = "default",
|
||||
var room: String?,
|
||||
var user: String?,
|
||||
) {
|
||||
lateinit var roll: Array<String>
|
||||
}
|
||||
|
||||
37
src/main/kotlin/de/arindy/dicetower/Templates.kt
Normal file
@@ -0,0 +1,37 @@
|
||||
package de.arindy.dicetower
|
||||
|
||||
import io.quarkus.qute.CheckedTemplate
|
||||
import io.quarkus.qute.TemplateInstance
|
||||
|
||||
@CheckedTemplate
|
||||
object Templates {
|
||||
@JvmStatic
|
||||
external fun overlay(diceid: String, scale: Int?, clearAfter: Long?): TemplateInstance
|
||||
|
||||
@JvmStatic
|
||||
external fun results(room: String, name: String?): TemplateInstance
|
||||
|
||||
@JvmStatic
|
||||
external fun index(version: String): TemplateInstance
|
||||
|
||||
@JvmStatic
|
||||
external fun chatoverlayconfig(): TemplateInstance
|
||||
|
||||
@JvmStatic
|
||||
external fun chatoverlay(
|
||||
channel: String,
|
||||
scale: Int?,
|
||||
maxDice: Int?,
|
||||
modsAllowed: Boolean,
|
||||
vipAllowed: Boolean,
|
||||
subsAllowed: Boolean,
|
||||
allAllowed: Boolean,
|
||||
cmd: String?,
|
||||
theme: String?,
|
||||
faceColor: String?,
|
||||
numberColor: String?,
|
||||
clearAfter: Long?,
|
||||
timeout: Long?,
|
||||
showResults: Boolean?
|
||||
): TemplateInstance
|
||||
}
|
||||
BIN
src/main/resources/META-INF/resources/192.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
src/main/resources/META-INF/resources/512.png
Normal file
|
After Width: | Height: | Size: 76 KiB |
225
src/main/resources/META-INF/resources/app.js
Normal file
@@ -0,0 +1,225 @@
|
||||
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() {
|
||||
return window.location.protocol + '//' + window.location.hostname + (window.location.port?.length > 0 ? ':' + window.location.port : '');
|
||||
}
|
||||
|
||||
function register() {
|
||||
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')
|
||||
}))
|
||||
}
|
||||
|
||||
function start(event = undefined) {
|
||||
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=10&clearAfter=30';
|
||||
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 = document.getElementById('myResultsId').value;
|
||||
document.getElementById('diceFrame').src = document.getElementById('overlayId').value;
|
||||
document.getElementById('roll').hidden = false;
|
||||
document.getElementById('start-container').hidden = true;
|
||||
document.getElementById('options-container').hidden = false;
|
||||
document.getElementById('dice-tower').hidden = false;
|
||||
document.getElementById('name').hidden = true;
|
||||
document.getElementById('room').hidden = true;
|
||||
document.getElementById('how-to').hidden = true;
|
||||
document.getElementById('chatOverlay').hidden = true;
|
||||
document.getElementById('results').hidden = false;
|
||||
document.getElementById('all-results').hidden = !document.getElementById('gm').checked;
|
||||
document.getElementById('all-results-urls').style.display = document.getElementById('gm').checked ? 'fles' : 'none';
|
||||
document.getElementById('nameH').innerHTML = '<strong style="font-size:x-large;">' + document.getElementById('name').value + '</strong>';
|
||||
document.getElementById('save-dice-hint-name').innerHTML = '<strong>' + document.getElementById('name').value + '</strong>';
|
||||
document.getElementById('roomLabel').hidden = true;
|
||||
document.getElementById('nameLabel').hidden = true;
|
||||
document.getElementById('nameH').hidden = false;
|
||||
document.getElementById('room-hint').innerHTML = '<p>Room: <strong style="font-size:medium;">' + document.getElementById('room').value + '</strong></p>';
|
||||
document.getElementById('overlayLabel').innerHTML = 'Dice-Overlay for <strong>' + document.getElementById('name').value + '</strong>';
|
||||
document.title = document.getElementById('name').value + ' - Dice-Tower';
|
||||
|
||||
localStorage.setItem('last-name', document.getElementById('name').value)
|
||||
localStorage.setItem('last-room', document.getElementById('room').value)
|
||||
localStorage.setItem('last-gm', document.getElementById('gm').checked)
|
||||
|
||||
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 + "-faceColor")) {
|
||||
document.getElementById('faceColor').setColor(localStorage.getItem(document.getElementById('name').value + "-faceColor"));
|
||||
}
|
||||
if (localStorage.getItem(document.getElementById('name').value + "-numberColor")) {
|
||||
document.getElementById('numberColor').setColor(localStorage.getItem(document.getElementById('name').value + "-numberColor"));
|
||||
}
|
||||
|
||||
if (!localStorage.getItem(document.getElementById('name').value + '-started')) {
|
||||
document.getElementById('urls-overlay').showPopover();
|
||||
}
|
||||
|
||||
localStorage.setItem(document.getElementById('name').value + '-started', "true")
|
||||
|
||||
register();
|
||||
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.getElementById('overlayId').cloneNode();
|
||||
newInput.type = "text";
|
||||
newInput.readOnly = true;
|
||||
newInput.id = data.id + 'url';
|
||||
newInput.style.flexGrow = '1';
|
||||
newInput.value = data.overlay;
|
||||
newInput.onclick = () => copyToClipboard(newInput.id)
|
||||
newOverlay.appendChild(newLabel);
|
||||
newOverlay.appendChild(newInput);
|
||||
overlays.appendChild(newOverlay);
|
||||
|
||||
if (!document.getElementById(data.id + '-diceFrame')) {
|
||||
let dice = document.createElement('iframe');
|
||||
dice.id = data.id + '-diceFrame'
|
||||
dice.style.width = "50%";
|
||||
dice.style.height = "100%";
|
||||
dice.style.overflow = "hidden";
|
||||
dice.style.border = "0";
|
||||
dice.style.zIndex = "4";
|
||||
dice.style.position = "absolute";
|
||||
dice.style.top = "0";
|
||||
dice.style.left = "50%";
|
||||
dice.src = data.overlay;
|
||||
document.getElementById('results-dice').appendChild(dice)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
configurePopover();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function rollEasy(dice) {
|
||||
document.getElementById('command').value = document.getElementById('dice-amount').innerText + dice;
|
||||
roll();
|
||||
}
|
||||
|
||||
function roll(event) {
|
||||
if ((!event || event.keyCode === 13) && document.getElementById('command').value?.length > 0) {
|
||||
register()
|
||||
let httpRequest = new XMLHttpRequest();
|
||||
httpRequest.open('POST', url() + '/dice/' + document.getElementById('room').value + ':' + localStorage.getItem(`userId`))
|
||||
httpRequest.setRequestHeader('Content-Type', 'application/json')
|
||||
httpRequest.send(JSON.stringify({
|
||||
name: document.getElementById('name').value,
|
||||
command: document.getElementById('command').value,
|
||||
faceColor: document.getElementById('faceColor').value,
|
||||
numberColor: document.getElementById('numberColor').value,
|
||||
theme: document.getElementById('theme').value
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
function saveDice() {
|
||||
localStorage.setItem(document.getElementById('name').value + "-theme", document.getElementById('theme').value)
|
||||
localStorage.setItem(document.getElementById('name').value + "-faceColor", document.getElementById('faceColor').value)
|
||||
localStorage.setItem(document.getElementById('name').value + "-numberColor", document.getElementById('numberColor').value)
|
||||
}
|
||||
|
||||
function configurePopover() {
|
||||
|
||||
const popover = document.querySelectorAll("[popovertarget][data-trigger='hover']");
|
||||
popover.forEach((e) => {
|
||||
|
||||
const target = document.querySelector("#" + e.getAttribute("popovertarget"));
|
||||
e.addEventListener("mouseover", () => {
|
||||
showSnackbar(target.innerHTML);
|
||||
});
|
||||
e.addEventListener("mouseout", () => {
|
||||
hideSnackbar();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function copyToClipboard(id) {
|
||||
let copyText = document.getElementById(id);
|
||||
copyText.select();
|
||||
copyText.setSelectionRange(0, 99999);
|
||||
navigator.clipboard.writeText(copyText.value).then(() => {
|
||||
showSnackbar("<i class='fa-regular fa-copy'></i> Link copied to clipboard: <br/>" + copyText.value);
|
||||
})
|
||||
}
|
||||
|
||||
function showSnackbar(message) {
|
||||
let snackbar = document.getElementById("snackbar");
|
||||
let snackbarContainer = document.getElementById("snackbar-container");
|
||||
snackbar.innerHTML = message;
|
||||
snackbar.className = "show";
|
||||
snackbarContainer.className = "show";
|
||||
}
|
||||
|
||||
function hideSnackbar() {
|
||||
let snackbar = document.getElementById("snackbar");
|
||||
let snackbarContainer = document.getElementById("snackbar-container");
|
||||
snackbar.className = snackbar.className.replace("show", "");
|
||||
snackbarContainer.className = snackbarContainer.className.replace("show", "");
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", async () => {
|
||||
|
||||
document.querySelector('meta[property="og:url"]').setAttribute("content", url());
|
||||
document.querySelector('meta[property="twitter:url"]').setAttribute("content", url());
|
||||
document.querySelector('meta[property="og:image"]').setAttribute("content", url() + '/rich.png');
|
||||
document.querySelector('meta[name="twitter:image"]').setAttribute("content", url() + '/rich.png');
|
||||
document.querySelector('meta[property="twitter:domain"]').setAttribute("content", window.location.hostname);
|
||||
|
||||
if (localStorage.getItem('last-name') && localStorage.getItem('last-room')) {
|
||||
document.getElementById('name').value = localStorage.getItem('last-name');
|
||||
document.getElementById('room').value = localStorage.getItem('last-room');
|
||||
document.getElementById('gm').checked = localStorage.getItem('last-gm') === 'true';
|
||||
}
|
||||
|
||||
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.getElementById('resultDiceSwitch').addEventListener('change', function () {
|
||||
document.getElementById('results-dice').hidden = !this.checked;
|
||||
})
|
||||
|
||||
document.getElementById('chatOverlayLink').href = url() + '/chatoverlay'
|
||||
|
||||
configurePopover();
|
||||
})
|
||||
|
||||
125
src/main/resources/META-INF/resources/dice-preview.js
Normal file
@@ -0,0 +1,125 @@
|
||||
import DiceBox from "/vendor/dice-box/dice-box-threejs.es.js";
|
||||
|
||||
let diceBox
|
||||
|
||||
const Themes = {
|
||||
cloudy: {
|
||||
name: "Clouds (Transparent)"
|
||||
},
|
||||
cloudy_2: {
|
||||
name: "Clouds"
|
||||
},
|
||||
fire: {
|
||||
name: "Fire"
|
||||
},
|
||||
marble: {
|
||||
name: "Marble"
|
||||
},
|
||||
water: {
|
||||
name: "Water"
|
||||
},
|
||||
ice: {
|
||||
name: "Ice"
|
||||
},
|
||||
paper: {
|
||||
name: "Paper"
|
||||
},
|
||||
speckles: {
|
||||
name: "Speckles"
|
||||
},
|
||||
glitter: {
|
||||
name: "Glitter"
|
||||
},
|
||||
glitter_2: {
|
||||
name: "Glitter (Transparent)"
|
||||
},
|
||||
stars: {
|
||||
name: "Stars"
|
||||
},
|
||||
stainedglass: {
|
||||
name: "Stained Glass"
|
||||
},
|
||||
wood: {
|
||||
name: "Wood"
|
||||
},
|
||||
metal: {
|
||||
name: "Stainless Steel"
|
||||
},
|
||||
skulls: {
|
||||
name: "Skulls"
|
||||
},
|
||||
leopard: {
|
||||
name: "Leopard"
|
||||
},
|
||||
tiger: {
|
||||
name: "Tiger"
|
||||
},
|
||||
cheetah: {
|
||||
name: "Cheetah"
|
||||
},
|
||||
dragon: {
|
||||
name: "Dragon"
|
||||
},
|
||||
lizard: {
|
||||
name: "Lizard"
|
||||
},
|
||||
bird: {
|
||||
name: "Bird"
|
||||
},
|
||||
astral: {
|
||||
name: "Astral Sea"
|
||||
},
|
||||
bronze01: {
|
||||
name: "Bronze 1"
|
||||
},
|
||||
bronze02: {
|
||||
name: "Bronze 2"
|
||||
},
|
||||
bronze03: {
|
||||
name: "Bronze 3"
|
||||
},
|
||||
bronze03a: {
|
||||
name: "Bronze 3a"
|
||||
},
|
||||
bronze03b: {
|
||||
name: "Bronze 3b"
|
||||
},
|
||||
bronze04: {
|
||||
name: "Bronze 4"
|
||||
},
|
||||
none: {
|
||||
name: "none"
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", async () => {
|
||||
const themeSelector = document.getElementById('theme');
|
||||
for (const theme in Themes) {
|
||||
let option = document.createElement('option');
|
||||
option.value = theme;
|
||||
option.innerText = Themes[theme].name;
|
||||
themeSelector.appendChild(option);
|
||||
}
|
||||
themeSelector.value = 'none';
|
||||
|
||||
document.getElementById('preview').onclick = async () => {
|
||||
document.getElementById('app').replaceChildren(...[])
|
||||
diceBox = new DiceBox("#app", {
|
||||
assetPath: "/vendor/dice-box/",
|
||||
light_intensity: 2,
|
||||
gravity_multiplier: 600,
|
||||
baseScale: 120,
|
||||
strength: Math.floor(Math.random() * 4),
|
||||
});
|
||||
await diceBox.initialize();
|
||||
diceBox.clearDice();
|
||||
await diceBox.updateConfig({
|
||||
theme_customColorset: {
|
||||
background: document.getElementById('faceColor').value,
|
||||
foreground: document.getElementById('numberColor').value,
|
||||
texture: document.getElementById('theme').value
|
||||
}
|
||||
});
|
||||
await diceBox.roll('1d2 & 1d4 & 1d6 & 1d8 & 1d10 & 1d12 & 1d20 & 1d100');
|
||||
}
|
||||
})
|
||||
BIN
src/main/resources/META-INF/resources/favicon.png
Normal file
|
After Width: | Height: | Size: 9.7 KiB |
18
src/main/resources/META-INF/resources/manifest.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "Dice-Tower",
|
||||
"icons": [
|
||||
{
|
||||
"src": "192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": "/",
|
||||
"display_override": ["window-controls-overlay", "minimal-ui"],
|
||||
"display": "standalone"
|
||||
}
|
||||
36
src/main/resources/META-INF/resources/overlay/style.css
Normal file
@@ -0,0 +1,36 @@
|
||||
html,
|
||||
body {
|
||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
perspective: 1000px;
|
||||
}
|
||||
|
||||
#dice-box {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: transparent;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
#dice-box canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
position: fixed;
|
||||
border-radius: 0.5rem;
|
||||
padding: 15px;
|
||||
max-width: 80%;
|
||||
font-size: 400%;
|
||||
color: #fff; !important;
|
||||
background-color: #333333dd; !important
|
||||
}
|
||||
BIN
src/main/resources/META-INF/resources/rich.png
Normal file
|
After Width: | Height: | Size: 206 KiB |
240
src/main/resources/META-INF/resources/style.css
Normal file
@@ -0,0 +1,240 @@
|
||||
.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;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 10px;
|
||||
border: #333333 3px solid;
|
||||
border-radius: 10px;
|
||||
background: #333333;
|
||||
color: #fff
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: #444444;
|
||||
}
|
||||
|
||||
button:active {
|
||||
background: #222222;
|
||||
}
|
||||
|
||||
#install {
|
||||
padding: 10px;
|
||||
border: #666666 3px solid;
|
||||
border-radius: 10px;
|
||||
background: #666666;
|
||||
color: #fff
|
||||
}
|
||||
|
||||
#install:hover {
|
||||
background: #444444;
|
||||
}
|
||||
|
||||
#install:active {
|
||||
background: #222222;
|
||||
}
|
||||
|
||||
input {
|
||||
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);
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
position: fixed;
|
||||
border-radius: 0.5rem;
|
||||
padding: 15px;
|
||||
color: #fff !important;
|
||||
background-color: #333333dd !important
|
||||
}
|
||||
|
||||
#app {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#preview {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
#dice-box {
|
||||
position: relative;
|
||||
justify-self: center;
|
||||
box-sizing: border-box;
|
||||
width: 640px;
|
||||
height: 300px;
|
||||
background: transparent;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
#dice-box canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
position: relative;
|
||||
padding-left: 50px;
|
||||
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: 25px;
|
||||
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: 10%;
|
||||
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;
|
||||
}
|
||||
515
src/main/resources/META-INF/resources/vendor/color-picker.js
vendored
Normal file
@@ -0,0 +1,515 @@
|
||||
class ColorPicker extends HTMLElement {
|
||||
|
||||
// Declare elements as properties
|
||||
hueInput;
|
||||
saturationInput;
|
||||
lightnessInput;
|
||||
colorPreview;
|
||||
rgbaDisplay;
|
||||
tempCanvasCTX;
|
||||
name;
|
||||
|
||||
|
||||
/**
|
||||
* Define observed attributes and custom properties
|
||||
* @type {array}
|
||||
*/
|
||||
static observedAttributes = [ 'value' ];
|
||||
|
||||
|
||||
/**
|
||||
* Create a new instance of the component.
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
|
||||
this._timeoutId = null;
|
||||
|
||||
this._color = '#ffffff';
|
||||
|
||||
// Shadow DOM.
|
||||
this.attachShadow( { mode: 'open' } );
|
||||
|
||||
const rangeThumbStyle = `
|
||||
box-shadow: none;
|
||||
border: none;
|
||||
height: 1.5em;
|
||||
width: 1.5em;
|
||||
border-radius: 1em;
|
||||
background: white;
|
||||
cursor: pointer;
|
||||
-webkit-appearance: none;
|
||||
`;
|
||||
|
||||
// HTML
|
||||
this.shadowRoot.innerHTML = `
|
||||
<style>
|
||||
#color-preview {
|
||||
border-radius: 10px;
|
||||
margin: inherit;
|
||||
font-family: inherit;
|
||||
height: inherit;
|
||||
transition: background 0.5s ease-in-out;
|
||||
overflow: hidden;
|
||||
}
|
||||
#controls {
|
||||
padding: 1.5em;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
}
|
||||
.rgba-display-wrapper {
|
||||
height: 100%;
|
||||
padding: 1.25em;
|
||||
text-align: center;
|
||||
}
|
||||
#rgba-display-name {
|
||||
padding: 0.25em;
|
||||
display: inline;
|
||||
border: none;
|
||||
border-radius: 0.2em;
|
||||
background: rgba(0,0,0,0.7);
|
||||
}
|
||||
#rgba-display {
|
||||
font-family: "Courier New", monospace;
|
||||
font-size: 1em;
|
||||
font-weight: 900;
|
||||
padding: 0.25em;
|
||||
display: inline;
|
||||
border: none;
|
||||
border-radius: 0.2em;
|
||||
letter-spacing: 0.1em;
|
||||
width: 5em;
|
||||
max-width: 100%;
|
||||
font-variant-numeric: tabular-nums;
|
||||
text-align: center;
|
||||
}
|
||||
input[type=range] {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
height: 1em;
|
||||
appearance: none;
|
||||
border-radius: 1em;
|
||||
border: 1px solid black;
|
||||
}
|
||||
input[type=range]::-webkit-slider-thumb {
|
||||
${rangeThumbStyle}
|
||||
}
|
||||
input[type="range"]::-moz-range-thumb {
|
||||
${rangeThumbStyle}
|
||||
}
|
||||
label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
label abbr {
|
||||
width: 2em;
|
||||
font-weight: 900;
|
||||
line-height: 1;
|
||||
}
|
||||
.bg-cover {
|
||||
background: rgba(0,0,0,0.7);
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
<div class="color-preview" id="color-preview">
|
||||
<div class="rgba-display-wrapper">
|
||||
<label for="rgba-display" id="rgba-display-name"></label>
|
||||
<input type="text" id="rgba-display" class="bg-cover">
|
||||
</div>
|
||||
<div class="bg-cover" id="controls">
|
||||
<label for="hue">
|
||||
<abbr title="Hue: The color tint.">H</abbr>
|
||||
<input type="range" id="hue" min="0" max="360" value="0" step="0.01">
|
||||
</label>
|
||||
<label for="saturation">
|
||||
<abbr title="Saturation: The amount of gray in the color.">S</abbr>
|
||||
<input type="range" id="saturation" min="0" max="100" value="0" step="0.01">
|
||||
</label>
|
||||
<label for="lightness">
|
||||
<abbr title="Lightness: How light or dark the color is.">L</abbr>
|
||||
<input type="range" id="lightness" min="0" max="100" value="0" step="0.01">
|
||||
</label>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
// Initialize elements.
|
||||
this.hueInput = this.shadowRoot.getElementById( 'hue' );
|
||||
this.saturationInput = this.shadowRoot.getElementById( 'saturation' );
|
||||
this.lightnessInput = this.shadowRoot.getElementById( 'lightness' );
|
||||
this.colorPreview = this.shadowRoot.getElementById( 'color-preview' );
|
||||
this.rgbaDisplay = this.shadowRoot.getElementById( 'rgba-display' );
|
||||
this.name = this.shadowRoot.getElementById( 'rgba-display-name' );
|
||||
this.rgbaDisplay.onkeyup = (event) => {
|
||||
if(event.keyCode === 13) {
|
||||
this.setColor( this.rgbaDisplay.value );
|
||||
}
|
||||
}
|
||||
|
||||
// Create a temporary canvas to read color values.
|
||||
let tempCanvas = document.createElement( 'canvas' );
|
||||
tempCanvas.height = 1;
|
||||
tempCanvas.width = 1;
|
||||
this.tempCanvasCTX = tempCanvas.getContext( '2d', { willReadFrequently: true } );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handle attribute changes
|
||||
*
|
||||
* @param {string} name The name of the attribute that changed.
|
||||
* @param {string} oldValue The old value of the attribute.
|
||||
* @param {string} newValue The new value of the attribute.
|
||||
* @return {void}
|
||||
*/
|
||||
attributeChangedCallback( name, oldValue, newValue ) {
|
||||
|
||||
if ( name === 'value' && oldValue !== newValue ) {
|
||||
this.setColor( newValue );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Getter for the attribute value.
|
||||
*
|
||||
* @return {string}
|
||||
*/
|
||||
get value() {
|
||||
|
||||
return this._color;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Setter for the attribute value.
|
||||
*
|
||||
* @param {string} value The value to set.
|
||||
*/
|
||||
set value( value ) {
|
||||
|
||||
this.setAttribute( 'color', value );
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handle the component being added to the DOM.
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
connectedCallback() {
|
||||
|
||||
this.setupEventListeners();
|
||||
this.name.textContent = this.getAttribute( 'name' );
|
||||
const color = this.getAttribute( 'value' );
|
||||
if ( !color ) {
|
||||
this.setColor( 'green' );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Setup event listeners.
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
setupEventListeners() {
|
||||
|
||||
const inputs = this.shadowRoot.querySelectorAll( 'input[type="range"]' );
|
||||
inputs.forEach( input => input.addEventListener( 'input', () => this.maybeUpdateColor() ) );
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update the color after a short delay to prevent lag.
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
maybeUpdateColor() {
|
||||
|
||||
clearTimeout( this._timeoutId );
|
||||
this._timeoutId = setTimeout(
|
||||
() => this.updateColor(),
|
||||
50
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update the color and the relevant sliders and html.
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
updateColor() {
|
||||
|
||||
const hue = parseFloat( this.hueInput.value );
|
||||
const saturation = parseFloat( this.saturationInput.value );
|
||||
const lightness = parseFloat( this.lightnessInput.value );
|
||||
const hsla = [ hue, saturation, lightness ];
|
||||
|
||||
const rgba = this.HSLAToRGBA( hsla );
|
||||
|
||||
const hexString = this.rgbToHexString( rgba );
|
||||
|
||||
this._color = hexString;
|
||||
|
||||
this.dispatchEvent(
|
||||
new CustomEvent(
|
||||
'change',
|
||||
{
|
||||
bubbles: true,
|
||||
detail: {
|
||||
hsla,
|
||||
rgba,
|
||||
hexString,
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// Display RGBA string
|
||||
this.rgbaDisplay.value = hexString;
|
||||
|
||||
// Update color and gradients
|
||||
this.colorPreview.style.background = 'white';
|
||||
this.colorPreview.style.backgroundImage = `
|
||||
linear-gradient(to right, ${hexString}, ${hexString}),
|
||||
linear-gradient(45deg, #ccc 25%, transparent 25%, transparent 75%, #ccc 75%, #ccc),
|
||||
linear-gradient(45deg, #ccc 25%, transparent 25%, transparent 75%, #ccc 75%, #ccc)
|
||||
`;
|
||||
this.colorPreview.style.backgroundSize = '100% 100%, 10px 10px, 10px 10px';
|
||||
this.colorPreview.style.backgroundPosition = '0 0, 0 0, 5px 5px';
|
||||
|
||||
// Update the hue slider gradient
|
||||
this.hueInput.style.background = `linear-gradient(to right,
|
||||
hsl(0, ${saturation}%, ${lightness}%) 0%,
|
||||
hsl(60, ${saturation}%, ${lightness}%) 17%,
|
||||
hsl(120, ${saturation}%, ${lightness}%) 33%,
|
||||
hsl(180, ${saturation}%, ${lightness}%) 50%,
|
||||
hsl(240, ${saturation}%, ${lightness}%) 67%,
|
||||
hsl(300, ${saturation}%, ${lightness}%) 83%,
|
||||
hsl(360, ${saturation}%, ${lightness}%) 100%
|
||||
)`;
|
||||
|
||||
// Update the saturation slider gradient
|
||||
this.saturationInput.style.background = `linear-gradient(to right,
|
||||
hsl(${hue}, 0%, ${lightness}%) 0%,
|
||||
hsl(${hue}, 100%, ${lightness}%) 100%
|
||||
)`;
|
||||
|
||||
// Update the lightness slider gradient
|
||||
this.lightnessInput.style.background = `linear-gradient(to right,
|
||||
hsl(${hue}, ${saturation}%, 0%) 0%,
|
||||
hsl(${hue}, ${saturation}%, 50%) 50%,
|
||||
hsl(${hue}, ${saturation}%, 100%) 100%
|
||||
)`;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the color of the picker.
|
||||
*
|
||||
* @param {string} color The color to set. It can be any valid CSS color value.
|
||||
* @return {void}
|
||||
*/
|
||||
setColor( color = 'hsla(60, 100%, 50%, 0.5)' ) {
|
||||
|
||||
if ( /^[0-9A-F]{3,6}$/i.test( color ) ) {
|
||||
color = '#' + color;
|
||||
}
|
||||
|
||||
const rgba = this.colorToRGBA( color );
|
||||
const hsla = this.rgbToHSLA( rgba );
|
||||
|
||||
const [ hue, saturation, lightness ] = hsla;
|
||||
|
||||
// Set input values
|
||||
this.hueInput.value = hue;
|
||||
this.saturationInput.value = saturation;
|
||||
this.lightnessInput.value = lightness;
|
||||
|
||||
this.updateColor();
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function to convert HSLA array to RGBA array.
|
||||
*
|
||||
* @param {array} hslaArray An array of HSLA values.
|
||||
* @return {array} An array of RGBA values.
|
||||
*/
|
||||
HSLAToRGBA( hslaArray ) {
|
||||
|
||||
const [ h, s, l] = hslaArray;
|
||||
|
||||
// Normalize HSL values
|
||||
const normalizedH = h % 360 / 360;
|
||||
const normalizedS = s / 100;
|
||||
const normalizedL = l / 100;
|
||||
|
||||
// Helper function to convert hue to RGB
|
||||
function hueToRGB( p, q, t ) {
|
||||
if ( t < 0 ) t += 1;
|
||||
if ( t > 1 ) t -= 1;
|
||||
if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t;
|
||||
if ( t < 1 / 2 ) return q;
|
||||
if ( t < 2 / 3 ) return p + ( q - p ) * ( 2 / 3 - t ) * 6;
|
||||
return p;
|
||||
}
|
||||
|
||||
// Calculate RGB values
|
||||
let r, g, b;
|
||||
|
||||
if ( normalizedS === 0 ) {
|
||||
// If saturation is 0, the color is a shade of gray
|
||||
r = g = b = normalizedL;
|
||||
} else {
|
||||
const q = normalizedL < 0.5 ? normalizedL * ( 1 + normalizedS ) : normalizedL + normalizedS - normalizedL * normalizedS;
|
||||
const p = 2 * normalizedL - q;
|
||||
|
||||
r = hueToRGB( p, q, normalizedH + 1 / 3 );
|
||||
g = hueToRGB( p, q, normalizedH );
|
||||
b = hueToRGB( p, q, normalizedH - 1 / 3 );
|
||||
}
|
||||
|
||||
// Convert RGB values to the range [0, 255]
|
||||
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a colour string, whatever it contains, to an RGBA value.
|
||||
*
|
||||
* @param {string} color The color to convert.
|
||||
* @return {array}
|
||||
*/
|
||||
colorToRGBA( color ) {
|
||||
|
||||
const ctx = this.tempCanvasCTX;
|
||||
|
||||
// Clear canvas first.
|
||||
// This ensures transparent colours don't add to each other.
|
||||
ctx.clearRect( 0, 0, 1, 1 );
|
||||
|
||||
// Draw color.
|
||||
ctx.fillStyle = color;
|
||||
ctx.fillRect( 0, 0, 1, 1 );
|
||||
|
||||
// Read color.
|
||||
let rgba = Array.from( ctx.getImageData( 0, 0, 1, 1 ).data );
|
||||
|
||||
// Convert alpha to range 0 -> 1
|
||||
rgba[ 3 ] = 255;
|
||||
|
||||
return rgba;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function to convert RGB array to hexa.
|
||||
*
|
||||
* @param {array} rgbArray An array of RGB values.
|
||||
* @return {string} The hexa string.
|
||||
*/
|
||||
rgbToHexString( rgbArray ) {
|
||||
|
||||
const [ r, g, b] = rgbArray;
|
||||
return `#${r.toString( 16 ).padStart( 2, '0' )}${g.toString( 16 ).padStart( 2, '0' )}${b.toString( 16 ).padStart( 2, '0' )}`;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function to convert RGB array to hsla.
|
||||
*
|
||||
* @param {array} rgbArray An array of RGB values.
|
||||
* @return {string} The hsla string.
|
||||
*/
|
||||
rgbToHSLString( rgbArray ) {
|
||||
|
||||
const hsla = this.rgbToHSLA( rgbArray );
|
||||
return `hsla( ${hsla[ 0 ]}, ${hsla[ 1 ]} %, ${hsla[ 2 ]} %, ${hsla[ 3 ]} )`;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function to convert RGB array to rgba.
|
||||
*
|
||||
* @param {array} rgbArray An array of RGB values.
|
||||
* @return {string} The rgba string.
|
||||
*/
|
||||
rgbToRGBString( rgbArray ) {
|
||||
|
||||
return `rgba( ${rgbArray.join( ',' )} )`;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function to convert RGBA array to hsla.
|
||||
*
|
||||
* @param {array} rgbaArray An array of RGBA values.
|
||||
* @return {array} An array of HSLA values.
|
||||
*/
|
||||
rgbToHSLA( rgbaArray ) {
|
||||
|
||||
const [ r, g, b, a ] = rgbaArray;
|
||||
|
||||
// Normalize RGBA values to the range [0, 1]
|
||||
const normalizedR = r / 255;
|
||||
const normalizedG = g / 255;
|
||||
const normalizedB = b / 255;
|
||||
|
||||
// Find the maximum and minimum values among the normalized RGB components
|
||||
const max = Math.max( normalizedR, normalizedG, normalizedB );
|
||||
const min = Math.min( normalizedR, normalizedG, normalizedB );
|
||||
|
||||
// Calculate lightness
|
||||
const lightness = ( max + min ) / 2;
|
||||
|
||||
// Calculate saturation
|
||||
let saturation = 0;
|
||||
if ( max !== min ) {
|
||||
saturation = ( max - min ) / ( 1 - Math.abs( 2 * lightness - 1 ) );
|
||||
}
|
||||
|
||||
// Calculate hue
|
||||
let hue = 0;
|
||||
if ( max !== min ) {
|
||||
if ( max === normalizedR ) {
|
||||
hue = ( ( normalizedG - normalizedB ) / ( max - min ) ) % 6;
|
||||
} else if ( max === normalizedG ) {
|
||||
hue = ( ( normalizedB - normalizedR ) / ( max - min ) + 2 );
|
||||
} else {
|
||||
hue = ( ( normalizedR - normalizedG ) / ( max - min ) + 4 );
|
||||
}
|
||||
}
|
||||
|
||||
// Convert hue to degrees
|
||||
hue *= 60;
|
||||
|
||||
if ( hue < 0 ) {
|
||||
hue += 360;
|
||||
}
|
||||
|
||||
return [ parseFloat(hue.toFixed(2)), parseFloat((saturation * 100).toFixed(2)), parseFloat((lightness * 100).toFixed(2)), a ];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
customElements.define( 'color-picker', ColorPicker );
|
||||
1
src/main/resources/META-INF/resources/vendor/comfy.js/comfy.min.js
vendored
Normal file
17251
src/main/resources/META-INF/resources/vendor/dice-box/dice-box-threejs.es.js
vendored
Normal file
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/astral.webp
vendored
Executable file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/bronze01.webp
vendored
Executable file
|
After Width: | Height: | Size: 197 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/bronze02.webp
vendored
Executable file
|
After Width: | Height: | Size: 165 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/bronze03.webp
vendored
Executable file
|
After Width: | Height: | Size: 190 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/bronze03a.webp
vendored
Executable file
|
After Width: | Height: | Size: 182 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/bronze03b.webp
vendored
Executable file
|
After Width: | Height: | Size: 140 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/bronze04.webp
vendored
Executable file
|
After Width: | Height: | Size: 184 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/cheetah.webp
vendored
Executable file
|
After Width: | Height: | Size: 21 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/cloudy.alt.webp
vendored
Executable file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/cloudy.webp
vendored
Executable file
|
After Width: | Height: | Size: 28 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/dragon-bump.webp
vendored
Executable file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/dragon.webp
vendored
Executable file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/feather-bump.webp
vendored
Executable file
|
After Width: | Height: | Size: 11 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/feather.webp
vendored
Executable file
|
After Width: | Height: | Size: 34 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/fire.webp
vendored
Executable file
|
After Width: | Height: | Size: 12 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/glitter-alpha.webp
vendored
Executable file
|
After Width: | Height: | Size: 14 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/glitter-bump.webp
vendored
Executable file
|
After Width: | Height: | Size: 8.3 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/glitter.webp
vendored
Executable file
|
After Width: | Height: | Size: 10 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/ice.webp
vendored
Executable file
|
After Width: | Height: | Size: 49 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/leopard.webp
vendored
Executable file
|
After Width: | Height: | Size: 22 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/lizard-bump.webp
vendored
Executable file
|
After Width: | Height: | Size: 19 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/lizard.webp
vendored
Executable file
|
After Width: | Height: | Size: 19 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/marble.webp
vendored
Executable file
|
After Width: | Height: | Size: 51 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/metal-bump.webp
vendored
Executable file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/metal.webp
vendored
Executable file
|
After Width: | Height: | Size: 5.0 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/noise-thin-film.webp
vendored
Executable file
|
After Width: | Height: | Size: 16 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/noise.webp
vendored
Executable file
|
After Width: | Height: | Size: 33 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/paper-bump.webp
vendored
Executable file
|
After Width: | Height: | Size: 20 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/paper.webp
vendored
Executable file
|
After Width: | Height: | Size: 12 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/skulls.webp
vendored
Executable file
|
After Width: | Height: | Size: 7.5 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/speckles.webp
vendored
Executable file
|
After Width: | Height: | Size: 9.3 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/stainedglass-bump.webp
vendored
Executable file
|
After Width: | Height: | Size: 8.8 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/stainedglass.webp
vendored
Executable file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/stars.webp
vendored
Executable file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/stone.webp
vendored
Executable file
|
After Width: | Height: | Size: 9.5 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/tiger.webp
vendored
Executable file
|
After Width: | Height: | Size: 12 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/water.webp
vendored
Executable file
|
After Width: | Height: | Size: 20 KiB |
BIN
src/main/resources/META-INF/resources/vendor/dice-box/textures/wood.webp
vendored
Executable file
|
After Width: | Height: | Size: 16 KiB |
165
src/main/resources/META-INF/resources/vendor/font-awesome/LICENSE.txt
vendored
Normal file
@@ -0,0 +1,165 @@
|
||||
Fonticons, Inc. (https://fontawesome.com)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Font Awesome Free License
|
||||
|
||||
Font Awesome Free is free, open source, and GPL friendly. You can use it for
|
||||
commercial projects, open source projects, or really almost whatever you want.
|
||||
Full Font Awesome Free license: https://fontawesome.com/license/free.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
# Icons: CC BY 4.0 License (https://creativecommons.org/licenses/by/4.0/)
|
||||
|
||||
The Font Awesome Free download is licensed under a Creative Commons
|
||||
Attribution 4.0 International License and applies to all icons packaged
|
||||
as SVG and JS file types.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
# Fonts: SIL OFL 1.1 License
|
||||
|
||||
In the Font Awesome Free download, the SIL OFL license applies to all icons
|
||||
packaged as web and desktop font files.
|
||||
|
||||
Copyright (c) 2024 Fonticons, Inc. (https://fontawesome.com)
|
||||
with Reserved Font Name: "Font Awesome".
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
SIL OPEN FONT LICENSE
|
||||
Version 1.1 - 26 February 2007
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting — in part or in whole — any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
# Code: MIT License (https://opensource.org/licenses/MIT)
|
||||
|
||||
In the Font Awesome Free download, the MIT license applies to all non-font and
|
||||
non-icon files.
|
||||
|
||||
Copyright 2024 Fonticons, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without limitation the rights to use, copy,
|
||||
modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so, subject to the
|
||||
following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
# Attribution
|
||||
|
||||
Attribution is required by MIT, SIL OFL, and CC BY licenses. Downloaded Font
|
||||
Awesome Free files already contain embedded comments with sufficient
|
||||
attribution, so you shouldn't need to do anything additional when using these
|
||||
files normally.
|
||||
|
||||
We've kept attribution comments terse, so we ask that you do not actively work
|
||||
to remove them from files, especially code. They're a great way for folks to
|
||||
learn about Font Awesome.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
# Brand Icons
|
||||
|
||||
All brand icons are trademarks of their respective owners. The use of these
|
||||
trademarks does not indicate endorsement of the trademark holder by Font
|
||||
Awesome, nor vice versa. **Please do not use brand logos for any purpose except
|
||||
to represent the company, product, or service to which they refer.**
|
||||
7913
src/main/resources/META-INF/resources/vendor/font-awesome/css/all.css
vendored
Normal file
9
src/main/resources/META-INF/resources/vendor/font-awesome/css/all.min.css
vendored
Normal file
1609
src/main/resources/META-INF/resources/vendor/font-awesome/css/brands.css
vendored
Normal file
6
src/main/resources/META-INF/resources/vendor/font-awesome/css/brands.min.css
vendored
Normal file
6243
src/main/resources/META-INF/resources/vendor/font-awesome/css/fontawesome.css
vendored
Normal file
9
src/main/resources/META-INF/resources/vendor/font-awesome/css/fontawesome.min.css
vendored
Normal file
19
src/main/resources/META-INF/resources/vendor/font-awesome/css/regular.css
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
/*!
|
||||
* Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2024 Fonticons, Inc.
|
||||
*/
|
||||
:root, :host {
|
||||
--fa-style-family-classic: 'Font Awesome 6 Free';
|
||||
--fa-font-regular: normal 400 1em/1 'Font Awesome 6 Free'; }
|
||||
|
||||
@font-face {
|
||||
font-family: 'Font Awesome 6 Free';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.ttf") format("truetype"); }
|
||||
|
||||
.far,
|
||||
.fa-regular {
|
||||
font-weight: 400; }
|
||||
6
src/main/resources/META-INF/resources/vendor/font-awesome/css/regular.min.css
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/*!
|
||||
* Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2024 Fonticons, Inc.
|
||||
*/
|
||||
:host,:root{--fa-style-family-classic:"Font Awesome 6 Free";--fa-font-regular:normal 400 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype")}.fa-regular,.far{font-weight:400}
|
||||
19
src/main/resources/META-INF/resources/vendor/font-awesome/css/solid.css
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
/*!
|
||||
* Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2024 Fonticons, Inc.
|
||||
*/
|
||||
:root, :host {
|
||||
--fa-style-family-classic: 'Font Awesome 6 Free';
|
||||
--fa-font-solid: normal 900 1em/1 'Font Awesome 6 Free'; }
|
||||
|
||||
@font-face {
|
||||
font-family: 'Font Awesome 6 Free';
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.ttf") format("truetype"); }
|
||||
|
||||
.fas,
|
||||
.fa-solid {
|
||||
font-weight: 900; }
|
||||
6
src/main/resources/META-INF/resources/vendor/font-awesome/css/solid.min.css
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/*!
|
||||
* Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2024 Fonticons, Inc.
|
||||
*/
|
||||
:host,:root{--fa-style-family-classic:"Font Awesome 6 Free";--fa-font-solid:normal 900 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}.fa-solid,.fas{font-weight:900}
|
||||
461
src/main/resources/META-INF/resources/vendor/font-awesome/css/svg-with-js.css
vendored
Normal file
@@ -0,0 +1,461 @@
|
||||
/*!
|
||||
* Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2024 Fonticons, Inc.
|
||||
*/
|
||||
:root, :host {
|
||||
--fa-font-solid: normal 900 1em/1 'Font Awesome 6 Free';
|
||||
--fa-font-regular: normal 400 1em/1 'Font Awesome 6 Free';
|
||||
--fa-font-light: normal 300 1em/1 'Font Awesome 6 Pro';
|
||||
--fa-font-thin: normal 100 1em/1 'Font Awesome 6 Pro';
|
||||
--fa-font-duotone: normal 900 1em/1 'Font Awesome 6 Duotone';
|
||||
--fa-font-duotone-regular: normal 400 1em/1 'Font Awesome 6 Duotone';
|
||||
--fa-font-duotone-light: normal 300 1em/1 'Font Awesome 6 Duotone';
|
||||
--fa-font-duotone-thin: normal 100 1em/1 'Font Awesome 6 Duotone';
|
||||
--fa-font-brands: normal 400 1em/1 'Font Awesome 6 Brands';
|
||||
--fa-font-sharp-solid: normal 900 1em/1 'Font Awesome 6 Sharp';
|
||||
--fa-font-sharp-regular: normal 400 1em/1 'Font Awesome 6 Sharp';
|
||||
--fa-font-sharp-light: normal 300 1em/1 'Font Awesome 6 Sharp';
|
||||
--fa-font-sharp-thin: normal 100 1em/1 'Font Awesome 6 Sharp';
|
||||
--fa-font-sharp-duotone-solid: normal 900 1em/1 'Font Awesome 6 Sharp Duotone';
|
||||
--fa-font-sharp-duotone-regular: normal 400 1em/1 'Font Awesome 6 Sharp Duotone';
|
||||
--fa-font-sharp-duotone-light: normal 300 1em/1 'Font Awesome 6 Sharp Duotone';
|
||||
--fa-font-sharp-duotone-thin: normal 100 1em/1 'Font Awesome 6 Sharp Duotone'; }
|
||||
|
||||
svg.svg-inline--fa:not(:root), svg.svg-inline--fa:not(:host) {
|
||||
overflow: visible;
|
||||
box-sizing: content-box; }
|
||||
|
||||
.svg-inline--fa {
|
||||
display: var(--fa-display, inline-block);
|
||||
height: 1em;
|
||||
overflow: visible;
|
||||
vertical-align: -.125em; }
|
||||
.svg-inline--fa.fa-2xs {
|
||||
vertical-align: 0.1em; }
|
||||
.svg-inline--fa.fa-xs {
|
||||
vertical-align: 0em; }
|
||||
.svg-inline--fa.fa-sm {
|
||||
vertical-align: -0.07143em; }
|
||||
.svg-inline--fa.fa-lg {
|
||||
vertical-align: -0.2em; }
|
||||
.svg-inline--fa.fa-xl {
|
||||
vertical-align: -0.25em; }
|
||||
.svg-inline--fa.fa-2xl {
|
||||
vertical-align: -0.3125em; }
|
||||
.svg-inline--fa.fa-pull-left {
|
||||
margin-right: var(--fa-pull-margin, 0.3em);
|
||||
width: auto; }
|
||||
.svg-inline--fa.fa-pull-right {
|
||||
margin-left: var(--fa-pull-margin, 0.3em);
|
||||
width: auto; }
|
||||
.svg-inline--fa.fa-li {
|
||||
width: var(--fa-li-width, 2em);
|
||||
top: 0.25em; }
|
||||
.svg-inline--fa.fa-fw {
|
||||
width: var(--fa-fw-width, 1.25em); }
|
||||
|
||||
.fa-layers svg.svg-inline--fa {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
margin: auto;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0; }
|
||||
|
||||
.fa-layers-counter, .fa-layers-text {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
text-align: center; }
|
||||
|
||||
.fa-layers {
|
||||
display: inline-block;
|
||||
height: 1em;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
vertical-align: -.125em;
|
||||
width: 1em; }
|
||||
.fa-layers svg.svg-inline--fa {
|
||||
transform-origin: center center; }
|
||||
|
||||
.fa-layers-text {
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
transform-origin: center center; }
|
||||
|
||||
.fa-layers-counter {
|
||||
background-color: var(--fa-counter-background-color, #ff253a);
|
||||
border-radius: var(--fa-counter-border-radius, 1em);
|
||||
box-sizing: border-box;
|
||||
color: var(--fa-inverse, #fff);
|
||||
line-height: var(--fa-counter-line-height, 1);
|
||||
max-width: var(--fa-counter-max-width, 5em);
|
||||
min-width: var(--fa-counter-min-width, 1.5em);
|
||||
overflow: hidden;
|
||||
padding: var(--fa-counter-padding, 0.25em 0.5em);
|
||||
right: var(--fa-right, 0);
|
||||
text-overflow: ellipsis;
|
||||
top: var(--fa-top, 0);
|
||||
transform: scale(var(--fa-counter-scale, 0.25));
|
||||
transform-origin: top right; }
|
||||
|
||||
.fa-layers-bottom-right {
|
||||
bottom: var(--fa-bottom, 0);
|
||||
right: var(--fa-right, 0);
|
||||
top: auto;
|
||||
transform: scale(var(--fa-layers-scale, 0.25));
|
||||
transform-origin: bottom right; }
|
||||
|
||||
.fa-layers-bottom-left {
|
||||
bottom: var(--fa-bottom, 0);
|
||||
left: var(--fa-left, 0);
|
||||
right: auto;
|
||||
top: auto;
|
||||
transform: scale(var(--fa-layers-scale, 0.25));
|
||||
transform-origin: bottom left; }
|
||||
|
||||
.fa-layers-top-right {
|
||||
top: var(--fa-top, 0);
|
||||
right: var(--fa-right, 0);
|
||||
transform: scale(var(--fa-layers-scale, 0.25));
|
||||
transform-origin: top right; }
|
||||
|
||||
.fa-layers-top-left {
|
||||
left: var(--fa-left, 0);
|
||||
right: auto;
|
||||
top: var(--fa-top, 0);
|
||||
transform: scale(var(--fa-layers-scale, 0.25));
|
||||
transform-origin: top left; }
|
||||
|
||||
.fa-1x {
|
||||
font-size: 1em; }
|
||||
|
||||
.fa-2x {
|
||||
font-size: 2em; }
|
||||
|
||||
.fa-3x {
|
||||
font-size: 3em; }
|
||||
|
||||
.fa-4x {
|
||||
font-size: 4em; }
|
||||
|
||||
.fa-5x {
|
||||
font-size: 5em; }
|
||||
|
||||
.fa-6x {
|
||||
font-size: 6em; }
|
||||
|
||||
.fa-7x {
|
||||
font-size: 7em; }
|
||||
|
||||
.fa-8x {
|
||||
font-size: 8em; }
|
||||
|
||||
.fa-9x {
|
||||
font-size: 9em; }
|
||||
|
||||
.fa-10x {
|
||||
font-size: 10em; }
|
||||
|
||||
.fa-2xs {
|
||||
font-size: 0.625em;
|
||||
line-height: 0.1em;
|
||||
vertical-align: 0.225em; }
|
||||
|
||||
.fa-xs {
|
||||
font-size: 0.75em;
|
||||
line-height: 0.08333em;
|
||||
vertical-align: 0.125em; }
|
||||
|
||||
.fa-sm {
|
||||
font-size: 0.875em;
|
||||
line-height: 0.07143em;
|
||||
vertical-align: 0.05357em; }
|
||||
|
||||
.fa-lg {
|
||||
font-size: 1.25em;
|
||||
line-height: 0.05em;
|
||||
vertical-align: -0.075em; }
|
||||
|
||||
.fa-xl {
|
||||
font-size: 1.5em;
|
||||
line-height: 0.04167em;
|
||||
vertical-align: -0.125em; }
|
||||
|
||||
.fa-2xl {
|
||||
font-size: 2em;
|
||||
line-height: 0.03125em;
|
||||
vertical-align: -0.1875em; }
|
||||
|
||||
.fa-fw {
|
||||
text-align: center;
|
||||
width: 1.25em; }
|
||||
|
||||
.fa-ul {
|
||||
list-style-type: none;
|
||||
margin-left: var(--fa-li-margin, 2.5em);
|
||||
padding-left: 0; }
|
||||
.fa-ul > li {
|
||||
position: relative; }
|
||||
|
||||
.fa-li {
|
||||
left: calc(-1 * var(--fa-li-width, 2em));
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
width: var(--fa-li-width, 2em);
|
||||
line-height: inherit; }
|
||||
|
||||
.fa-border {
|
||||
border-color: var(--fa-border-color, #eee);
|
||||
border-radius: var(--fa-border-radius, 0.1em);
|
||||
border-style: var(--fa-border-style, solid);
|
||||
border-width: var(--fa-border-width, 0.08em);
|
||||
padding: var(--fa-border-padding, 0.2em 0.25em 0.15em); }
|
||||
|
||||
.fa-pull-left {
|
||||
float: left;
|
||||
margin-right: var(--fa-pull-margin, 0.3em); }
|
||||
|
||||
.fa-pull-right {
|
||||
float: right;
|
||||
margin-left: var(--fa-pull-margin, 0.3em); }
|
||||
|
||||
.fa-beat {
|
||||
animation-name: fa-beat;
|
||||
animation-delay: var(--fa-animation-delay, 0s);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-duration: var(--fa-animation-duration, 1s);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-timing-function: var(--fa-animation-timing, ease-in-out); }
|
||||
|
||||
.fa-bounce {
|
||||
animation-name: fa-bounce;
|
||||
animation-delay: var(--fa-animation-delay, 0s);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-duration: var(--fa-animation-duration, 1s);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.28, 0.84, 0.42, 1)); }
|
||||
|
||||
.fa-fade {
|
||||
animation-name: fa-fade;
|
||||
animation-delay: var(--fa-animation-delay, 0s);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-duration: var(--fa-animation-duration, 1s);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); }
|
||||
|
||||
.fa-beat-fade {
|
||||
animation-name: fa-beat-fade;
|
||||
animation-delay: var(--fa-animation-delay, 0s);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-duration: var(--fa-animation-duration, 1s);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); }
|
||||
|
||||
.fa-flip {
|
||||
animation-name: fa-flip;
|
||||
animation-delay: var(--fa-animation-delay, 0s);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-duration: var(--fa-animation-duration, 1s);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-timing-function: var(--fa-animation-timing, ease-in-out); }
|
||||
|
||||
.fa-shake {
|
||||
animation-name: fa-shake;
|
||||
animation-delay: var(--fa-animation-delay, 0s);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-duration: var(--fa-animation-duration, 1s);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-timing-function: var(--fa-animation-timing, linear); }
|
||||
|
||||
.fa-spin {
|
||||
animation-name: fa-spin;
|
||||
animation-delay: var(--fa-animation-delay, 0s);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-duration: var(--fa-animation-duration, 2s);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-timing-function: var(--fa-animation-timing, linear); }
|
||||
|
||||
.fa-spin-reverse {
|
||||
--fa-animation-direction: reverse; }
|
||||
|
||||
.fa-pulse,
|
||||
.fa-spin-pulse {
|
||||
animation-name: fa-spin;
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-duration: var(--fa-animation-duration, 1s);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-timing-function: var(--fa-animation-timing, steps(8)); }
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.fa-beat,
|
||||
.fa-bounce,
|
||||
.fa-fade,
|
||||
.fa-beat-fade,
|
||||
.fa-flip,
|
||||
.fa-pulse,
|
||||
.fa-shake,
|
||||
.fa-spin,
|
||||
.fa-spin-pulse {
|
||||
animation-delay: -1ms;
|
||||
animation-duration: 1ms;
|
||||
animation-iteration-count: 1;
|
||||
transition-delay: 0s;
|
||||
transition-duration: 0s; } }
|
||||
|
||||
@keyframes fa-beat {
|
||||
0%, 90% {
|
||||
transform: scale(1); }
|
||||
45% {
|
||||
transform: scale(var(--fa-beat-scale, 1.25)); } }
|
||||
|
||||
@keyframes fa-bounce {
|
||||
0% {
|
||||
transform: scale(1, 1) translateY(0); }
|
||||
10% {
|
||||
transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); }
|
||||
30% {
|
||||
transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); }
|
||||
50% {
|
||||
transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); }
|
||||
57% {
|
||||
transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); }
|
||||
64% {
|
||||
transform: scale(1, 1) translateY(0); }
|
||||
100% {
|
||||
transform: scale(1, 1) translateY(0); } }
|
||||
|
||||
@keyframes fa-fade {
|
||||
50% {
|
||||
opacity: var(--fa-fade-opacity, 0.4); } }
|
||||
|
||||
@keyframes fa-beat-fade {
|
||||
0%, 100% {
|
||||
opacity: var(--fa-beat-fade-opacity, 0.4);
|
||||
transform: scale(1); }
|
||||
50% {
|
||||
opacity: 1;
|
||||
transform: scale(var(--fa-beat-fade-scale, 1.125)); } }
|
||||
|
||||
@keyframes fa-flip {
|
||||
50% {
|
||||
transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); } }
|
||||
|
||||
@keyframes fa-shake {
|
||||
0% {
|
||||
transform: rotate(-15deg); }
|
||||
4% {
|
||||
transform: rotate(15deg); }
|
||||
8%, 24% {
|
||||
transform: rotate(-18deg); }
|
||||
12%, 28% {
|
||||
transform: rotate(18deg); }
|
||||
16% {
|
||||
transform: rotate(-22deg); }
|
||||
20% {
|
||||
transform: rotate(22deg); }
|
||||
32% {
|
||||
transform: rotate(-12deg); }
|
||||
36% {
|
||||
transform: rotate(12deg); }
|
||||
40%, 100% {
|
||||
transform: rotate(0deg); } }
|
||||
|
||||
@keyframes fa-spin {
|
||||
0% {
|
||||
transform: rotate(0deg); }
|
||||
100% {
|
||||
transform: rotate(360deg); } }
|
||||
|
||||
.fa-rotate-90 {
|
||||
transform: rotate(90deg); }
|
||||
|
||||
.fa-rotate-180 {
|
||||
transform: rotate(180deg); }
|
||||
|
||||
.fa-rotate-270 {
|
||||
transform: rotate(270deg); }
|
||||
|
||||
.fa-flip-horizontal {
|
||||
transform: scale(-1, 1); }
|
||||
|
||||
.fa-flip-vertical {
|
||||
transform: scale(1, -1); }
|
||||
|
||||
.fa-flip-both,
|
||||
.fa-flip-horizontal.fa-flip-vertical {
|
||||
transform: scale(-1, -1); }
|
||||
|
||||
.fa-rotate-by {
|
||||
transform: rotate(var(--fa-rotate-angle, 0)); }
|
||||
|
||||
.fa-stack {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
height: 2em;
|
||||
position: relative;
|
||||
width: 2.5em; }
|
||||
|
||||
.fa-stack-1x,
|
||||
.fa-stack-2x {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
margin: auto;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
z-index: var(--fa-stack-z-index, auto); }
|
||||
|
||||
.svg-inline--fa.fa-stack-1x {
|
||||
height: 1em;
|
||||
width: 1.25em; }
|
||||
|
||||
.svg-inline--fa.fa-stack-2x {
|
||||
height: 2em;
|
||||
width: 2.5em; }
|
||||
|
||||
.fa-inverse {
|
||||
color: var(--fa-inverse, #fff); }
|
||||
|
||||
.sr-only,
|
||||
.fa-sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border-width: 0; }
|
||||
|
||||
.sr-only-focusable:not(:focus),
|
||||
.fa-sr-only-focusable:not(:focus) {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border-width: 0; }
|
||||
|
||||
.svg-inline--fa .fa-primary {
|
||||
fill: var(--fa-primary-color, currentColor);
|
||||
opacity: var(--fa-primary-opacity, 1); }
|
||||
|
||||
.svg-inline--fa .fa-secondary {
|
||||
fill: var(--fa-secondary-color, currentColor);
|
||||
opacity: var(--fa-secondary-opacity, 0.4); }
|
||||
|
||||
.svg-inline--fa.fa-swap-opacity .fa-primary {
|
||||
opacity: var(--fa-secondary-opacity, 0.4); }
|
||||
|
||||
.svg-inline--fa.fa-swap-opacity .fa-secondary {
|
||||
opacity: var(--fa-primary-opacity, 1); }
|
||||
|
||||
.svg-inline--fa mask .fa-primary,
|
||||
.svg-inline--fa mask .fa-secondary {
|
||||
fill: black; }
|
||||
6
src/main/resources/META-INF/resources/vendor/font-awesome/css/svg-with-js.min.css
vendored
Normal file
26
src/main/resources/META-INF/resources/vendor/font-awesome/css/v4-font-face.css
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
/*!
|
||||
* Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2024 Fonticons, Inc.
|
||||
*/
|
||||
@font-face {
|
||||
font-family: 'FontAwesome';
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.ttf") format("truetype"); }
|
||||
|
||||
@font-face {
|
||||
font-family: 'FontAwesome';
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-brands-400.woff2") format("woff2"), url("../webfonts/fa-brands-400.ttf") format("truetype"); }
|
||||
|
||||
@font-face {
|
||||
font-family: 'FontAwesome';
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.ttf") format("truetype");
|
||||
unicode-range: U+F003,U+F006,U+F014,U+F016-F017,U+F01A-F01B,U+F01D,U+F022,U+F03E,U+F044,U+F046,U+F05C-F05D,U+F06E,U+F070,U+F087-F088,U+F08A,U+F094,U+F096-F097,U+F09D,U+F0A0,U+F0A2,U+F0A4-F0A7,U+F0C5,U+F0C7,U+F0E5-F0E6,U+F0EB,U+F0F6-F0F8,U+F10C,U+F114-F115,U+F118-F11A,U+F11C-F11D,U+F133,U+F147,U+F14E,U+F150-F152,U+F185-F186,U+F18E,U+F190-F192,U+F196,U+F1C1-F1C9,U+F1D9,U+F1DB,U+F1E3,U+F1EA,U+F1F7,U+F1F9,U+F20A,U+F247-F248,U+F24A,U+F24D,U+F255-F25B,U+F25D,U+F271-F274,U+F278,U+F27B,U+F28C,U+F28E,U+F29C,U+F2B5,U+F2B7,U+F2BA,U+F2BC,U+F2BE,U+F2C0-F2C1,U+F2C3,U+F2D0,U+F2D2,U+F2D4,U+F2DC; }
|
||||
|
||||
@font-face {
|
||||
font-family: 'FontAwesome';
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-v4compatibility.woff2") format("woff2"), url("../webfonts/fa-v4compatibility.ttf") format("truetype");
|
||||
unicode-range: U+F041,U+F047,U+F065-F066,U+F07D-F07E,U+F080,U+F08B,U+F08E,U+F090,U+F09A,U+F0AC,U+F0AE,U+F0B2,U+F0D0,U+F0D6,U+F0E4,U+F0EC,U+F10A-F10B,U+F123,U+F13E,U+F148-F149,U+F14C,U+F156,U+F15E,U+F160-F161,U+F163,U+F175-F178,U+F195,U+F1F8,U+F219,U+F27A; }
|
||||
6
src/main/resources/META-INF/resources/vendor/font-awesome/css/v4-font-face.min.css
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/*!
|
||||
* Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2024 Fonticons, Inc.
|
||||
*/
|
||||
@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype");unicode-range:u+f003,u+f006,u+f014,u+f016-f017,u+f01a-f01b,u+f01d,u+f022,u+f03e,u+f044,u+f046,u+f05c-f05d,u+f06e,u+f070,u+f087-f088,u+f08a,u+f094,u+f096-f097,u+f09d,u+f0a0,u+f0a2,u+f0a4-f0a7,u+f0c5,u+f0c7,u+f0e5-f0e6,u+f0eb,u+f0f6-f0f8,u+f10c,u+f114-f115,u+f118-f11a,u+f11c-f11d,u+f133,u+f147,u+f14e,u+f150-f152,u+f185-f186,u+f18e,u+f190-f192,u+f196,u+f1c1-f1c9,u+f1d9,u+f1db,u+f1e3,u+f1ea,u+f1f7,u+f1f9,u+f20a,u+f247-f248,u+f24a,u+f24d,u+f255-f25b,u+f25d,u+f271-f274,u+f278,u+f27b,u+f28c,u+f28e,u+f29c,u+f2b5,u+f2b7,u+f2ba,u+f2bc,u+f2be,u+f2c0-f2c1,u+f2c3,u+f2d0,u+f2d2,u+f2d4,u+f2dc}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-v4compatibility.woff2) format("woff2"),url(../webfonts/fa-v4compatibility.ttf) format("truetype");unicode-range:u+f041,u+f047,u+f065-f066,u+f07d-f07e,u+f080,u+f08b,u+f08e,u+f090,u+f09a,u+f0ac,u+f0ae,u+f0b2,u+f0d0,u+f0d6,u+f0e4,u+f0ec,u+f10a-f10b,u+f123,u+f13e,u+f148-f149,u+f14c,u+f156,u+f15e,u+f160-f161,u+f163,u+f175-f178,u+f195,u+f1f8,u+f219,u+f27a}
|
||||
2194
src/main/resources/META-INF/resources/vendor/font-awesome/css/v4-shims.css
vendored
Normal file
6
src/main/resources/META-INF/resources/vendor/font-awesome/css/v4-shims.min.css
vendored
Normal file
22
src/main/resources/META-INF/resources/vendor/font-awesome/css/v5-font-face.css
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
/*!
|
||||
* Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2024 Fonticons, Inc.
|
||||
*/
|
||||
@font-face {
|
||||
font-family: 'Font Awesome 5 Brands';
|
||||
font-display: block;
|
||||
font-weight: 400;
|
||||
src: url("../webfonts/fa-brands-400.woff2") format("woff2"), url("../webfonts/fa-brands-400.ttf") format("truetype"); }
|
||||
|
||||
@font-face {
|
||||
font-family: 'Font Awesome 5 Free';
|
||||
font-display: block;
|
||||
font-weight: 900;
|
||||
src: url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.ttf") format("truetype"); }
|
||||
|
||||
@font-face {
|
||||
font-family: 'Font Awesome 5 Free';
|
||||
font-display: block;
|
||||
font-weight: 400;
|
||||
src: url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.ttf") format("truetype"); }
|
||||
6
src/main/resources/META-INF/resources/vendor/font-awesome/css/v5-font-face.min.css
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/*!
|
||||
* Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2024 Fonticons, Inc.
|
||||
*/
|
||||
@font-face{font-family:"Font Awesome 5 Brands";font-display:block;font-weight:400;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:900;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:400;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype")}
|
||||
BIN
src/main/resources/META-INF/resources/vendor/font-awesome/webfonts/fa-brands-400.ttf
vendored
Normal file
BIN
src/main/resources/META-INF/resources/vendor/font-awesome/webfonts/fa-brands-400.woff2
vendored
Normal file
BIN
src/main/resources/META-INF/resources/vendor/font-awesome/webfonts/fa-regular-400.ttf
vendored
Normal file
BIN
src/main/resources/META-INF/resources/vendor/font-awesome/webfonts/fa-regular-400.woff2
vendored
Normal file
BIN
src/main/resources/META-INF/resources/vendor/font-awesome/webfonts/fa-solid-900.ttf
vendored
Normal file
BIN
src/main/resources/META-INF/resources/vendor/font-awesome/webfonts/fa-solid-900.woff2
vendored
Normal file
BIN
src/main/resources/META-INF/resources/vendor/font-awesome/webfonts/fa-v4compatibility.ttf
vendored
Normal file
BIN
src/main/resources/META-INF/resources/vendor/font-awesome/webfonts/fa-v4compatibility.woff2
vendored
Normal file
235
src/main/resources/META-INF/resources/vendor/w3css/4/w3.css
vendored
Normal file
@@ -0,0 +1,235 @@
|
||||
/* W3.CSS 4.15 December 2020 by Jan Egil and Borge Refsnes */
|
||||
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}
|
||||
/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */
|
||||
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
||||
article,aside,details,figcaption,figure,footer,header,main,menu,nav,section{display:block}summary{display:list-item}
|
||||
audio,canvas,progress,video{display:inline-block}progress{vertical-align:baseline}
|
||||
audio:not([controls]){display:none;height:0}[hidden],template{display:none}
|
||||
a{background-color:transparent}a:active,a:hover{outline-width:0}
|
||||
abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}
|
||||
b,strong{font-weight:bolder}dfn{font-style:italic}mark{background:#ff0;color:#000}
|
||||
small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
|
||||
sub{bottom:-0.25em}sup{top:-0.5em}figure{margin:1em 40px}img{border-style:none}
|
||||
code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}hr{box-sizing:content-box;height:0;overflow:visible}
|
||||
button,input,select,textarea,optgroup{font:inherit;margin:0}optgroup{font-weight:bold}
|
||||
button,input{overflow:visible}button,select{text-transform:none}
|
||||
button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}
|
||||
button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0}
|
||||
button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted ButtonText}
|
||||
fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:.35em .625em .75em}
|
||||
legend{color:inherit;display:table;max-width:100%;padding:0;white-space:normal}textarea{overflow:auto}
|
||||
[type=checkbox],[type=radio]{padding:0}
|
||||
[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}
|
||||
[type=search]{-webkit-appearance:textfield;outline-offset:-2px}
|
||||
[type=search]::-webkit-search-decoration{-webkit-appearance:none}
|
||||
::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}
|
||||
/* End extract */
|
||||
html,body{font-family:Verdana,sans-serif;font-size:15px;line-height:1.5}html{overflow-x:hidden}
|
||||
h1{font-size:36px}h2{font-size:30px}h3{font-size:24px}h4{font-size:20px}h5{font-size:18px}h6{font-size:16px}
|
||||
.w3-serif{font-family:serif}.w3-sans-serif{font-family:sans-serif}.w3-cursive{font-family:cursive}.w3-monospace{font-family:monospace}
|
||||
h1,h2,h3,h4,h5,h6{font-family:"Segoe UI",Arial,sans-serif;font-weight:400;margin:10px 0}.w3-wide{letter-spacing:4px}
|
||||
hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-image{max-width:100%;height:auto}img{vertical-align:middle}a{color:inherit}
|
||||
.w3-table,.w3-table-all{border-collapse:collapse;border-spacing:0;width:100%;display:table}.w3-table-all{border:1px solid #ccc}
|
||||
.w3-bordered tr,.w3-table-all tr{border-bottom:1px solid #ddd}.w3-striped tbody tr:nth-child(even){background-color:#f1f1f1}
|
||||
.w3-table-all tr:nth-child(odd){background-color:#fff}.w3-table-all tr:nth-child(even){background-color:#f1f1f1}
|
||||
.w3-hoverable tbody tr:hover,.w3-ul.w3-hoverable li:hover{background-color:#ccc}.w3-centered tr th,.w3-centered tr td{text-align:center}
|
||||
.w3-table td,.w3-table th,.w3-table-all td,.w3-table-all th{padding:8px 8px;display:table-cell;text-align:left;vertical-align:top}
|
||||
.w3-table th:first-child,.w3-table td:first-child,.w3-table-all th:first-child,.w3-table-all td:first-child{padding-left:16px}
|
||||
.w3-btn,.w3-button{border:none;display:inline-block;padding:8px 16px;vertical-align:middle;overflow:hidden;text-decoration:none;color:inherit;background-color:inherit;text-align:center;cursor:pointer;white-space:nowrap}
|
||||
.w3-btn:hover{box-shadow:0 8px 16px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19)}
|
||||
.w3-btn,.w3-button{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
|
||||
.w3-disabled,.w3-btn:disabled,.w3-button:disabled{cursor:not-allowed;opacity:0.3}.w3-disabled *,:disabled *{pointer-events:none}
|
||||
.w3-btn.w3-disabled:hover,.w3-btn:disabled:hover{box-shadow:none}
|
||||
.w3-badge,.w3-tag{background-color:#000;color:#fff;display:inline-block;padding-left:8px;padding-right:8px;text-align:center}.w3-badge{border-radius:50%}
|
||||
.w3-ul{list-style-type:none;padding:0;margin:0}.w3-ul li{padding:8px 16px;border-bottom:1px solid #ddd}.w3-ul li:last-child{border-bottom:none}
|
||||
.w3-tooltip,.w3-display-container{position:relative}.w3-tooltip .w3-text{display:none}.w3-tooltip:hover .w3-text{display:inline-block}
|
||||
.w3-ripple:active{opacity:0.5}.w3-ripple{transition:opacity 0s}
|
||||
.w3-input{padding:8px;display:block;border:none;border-bottom:1px solid #ccc;width:100%}
|
||||
.w3-select{padding:9px 0;width:100%;border:none;border-bottom:1px solid #ccc}
|
||||
.w3-dropdown-click,.w3-dropdown-hover{position:relative;display:inline-block;cursor:pointer}
|
||||
.w3-dropdown-hover:hover .w3-dropdown-content{display:block}
|
||||
.w3-dropdown-hover:first-child,.w3-dropdown-click:hover{background-color:#ccc;color:#000}
|
||||
.w3-dropdown-hover:hover > .w3-button:first-child,.w3-dropdown-click:hover > .w3-button:first-child{background-color:#ccc;color:#000}
|
||||
.w3-dropdown-content{cursor:auto;color:#000;background-color:#fff;display:none;position:absolute;min-width:160px;margin:0;padding:0;z-index:1}
|
||||
.w3-check,.w3-radio{width:24px;height:24px;position:relative;top:6px}
|
||||
.w3-sidebar{height:100%;width:200px;background-color:#fff;position:fixed!important;z-index:1;overflow:auto}
|
||||
.w3-bar-block .w3-dropdown-hover,.w3-bar-block .w3-dropdown-click{width:100%}
|
||||
.w3-bar-block .w3-dropdown-hover .w3-dropdown-content,.w3-bar-block .w3-dropdown-click .w3-dropdown-content{min-width:100%}
|
||||
.w3-bar-block .w3-dropdown-hover .w3-button,.w3-bar-block .w3-dropdown-click .w3-button{width:100%;text-align:left;padding:8px 16px}
|
||||
.w3-main,#main{transition:margin-left .4s}
|
||||
.w3-modal{z-index:3;display:none;padding-top:100px;position:fixed;left:0;top:0;width:100%;height:100%;overflow:auto;background-color:rgb(0,0,0);background-color:rgba(0,0,0,0.4)}
|
||||
.w3-modal-content{margin:auto;background-color:#fff;position:relative;padding:0;outline:0;width:600px}
|
||||
.w3-bar{width:100%;overflow:hidden}.w3-center .w3-bar{display:inline-block;width:auto}
|
||||
.w3-bar .w3-bar-item{padding:8px 16px;float:left;width:auto;border:none;display:block;outline:0}
|
||||
.w3-bar .w3-dropdown-hover,.w3-bar .w3-dropdown-click{position:static;float:left}
|
||||
.w3-bar .w3-button{white-space:normal}
|
||||
.w3-bar-block .w3-bar-item{width:100%;display:block;padding:8px 16px;text-align:left;border:none;white-space:normal;float:none;outline:0}
|
||||
.w3-bar-block.w3-center .w3-bar-item{text-align:center}.w3-block{display:block;width:100%}
|
||||
.w3-responsive{display:block;overflow-x:auto}
|
||||
.w3-container:after,.w3-container:before,.w3-panel:after,.w3-panel:before,.w3-row:after,.w3-row:before,.w3-row-padding:after,.w3-row-padding:before,
|
||||
.w3-cell-row:before,.w3-cell-row:after,.w3-clear:after,.w3-clear:before,.w3-bar:before,.w3-bar:after{content:"";display:table;clear:both}
|
||||
.w3-col,.w3-half,.w3-third,.w3-twothird,.w3-threequarter,.w3-quarter{float:left;width:100%}
|
||||
.w3-col.s1{width:8.33333%}.w3-col.s2{width:16.66666%}.w3-col.s3{width:24.99999%}.w3-col.s4{width:33.33333%}
|
||||
.w3-col.s5{width:41.66666%}.w3-col.s6{width:49.99999%}.w3-col.s7{width:58.33333%}.w3-col.s8{width:66.66666%}
|
||||
.w3-col.s9{width:74.99999%}.w3-col.s10{width:83.33333%}.w3-col.s11{width:91.66666%}.w3-col.s12{width:99.99999%}
|
||||
@media (min-width:601px){.w3-col.m1{width:8.33333%}.w3-col.m2{width:16.66666%}.w3-col.m3,.w3-quarter{width:24.99999%}.w3-col.m4,.w3-third{width:33.33333%}
|
||||
.w3-col.m5{width:41.66666%}.w3-col.m6,.w3-half{width:49.99999%}.w3-col.m7{width:58.33333%}.w3-col.m8,.w3-twothird{width:66.66666%}
|
||||
.w3-col.m9,.w3-threequarter{width:74.99999%}.w3-col.m10{width:83.33333%}.w3-col.m11{width:91.66666%}.w3-col.m12{width:99.99999%}}
|
||||
@media (min-width:993px){.w3-col.l1{width:8.33333%}.w3-col.l2{width:16.66666%}.w3-col.l3{width:24.99999%}.w3-col.l4{width:33.33333%}
|
||||
.w3-col.l5{width:41.66666%}.w3-col.l6{width:49.99999%}.w3-col.l7{width:58.33333%}.w3-col.l8{width:66.66666%}
|
||||
.w3-col.l9{width:74.99999%}.w3-col.l10{width:83.33333%}.w3-col.l11{width:91.66666%}.w3-col.l12{width:99.99999%}}
|
||||
.w3-rest{overflow:hidden}.w3-stretch{margin-left:-16px;margin-right:-16px}
|
||||
.w3-content,.w3-auto{margin-left:auto;margin-right:auto}.w3-content{max-width:980px}.w3-auto{max-width:1140px}
|
||||
.w3-cell-row{display:table;width:100%}.w3-cell{display:table-cell}
|
||||
.w3-cell-top{vertical-align:top}.w3-cell-middle{vertical-align:middle}.w3-cell-bottom{vertical-align:bottom}
|
||||
.w3-hide{display:none!important}.w3-show-block,.w3-show{display:block!important}.w3-show-inline-block{display:inline-block!important}
|
||||
@media (max-width:1205px){.w3-auto{max-width:95%}}
|
||||
@media (max-width:600px){.w3-modal-content{margin:0 10px;width:auto!important}.w3-modal{padding-top:30px}
|
||||
.w3-dropdown-hover.w3-mobile .w3-dropdown-content,.w3-dropdown-click.w3-mobile .w3-dropdown-content{position:relative}
|
||||
.w3-hide-small{display:none!important}.w3-mobile{display:block;width:100%!important}.w3-bar-item.w3-mobile,.w3-dropdown-hover.w3-mobile,.w3-dropdown-click.w3-mobile{text-align:center}
|
||||
.w3-dropdown-hover.w3-mobile,.w3-dropdown-hover.w3-mobile .w3-btn,.w3-dropdown-hover.w3-mobile .w3-button,.w3-dropdown-click.w3-mobile,.w3-dropdown-click.w3-mobile .w3-btn,.w3-dropdown-click.w3-mobile .w3-button{width:100%}}
|
||||
@media (max-width:768px){.w3-modal-content{width:500px}.w3-modal{padding-top:50px}}
|
||||
@media (min-width:993px){.w3-modal-content{width:900px}.w3-hide-large{display:none!important}.w3-sidebar.w3-collapse{display:block!important}}
|
||||
@media (max-width:992px) and (min-width:601px){.w3-hide-medium{display:none!important}}
|
||||
@media (max-width:992px){.w3-sidebar.w3-collapse{display:none}.w3-main{margin-left:0!important;margin-right:0!important}.w3-auto{max-width:100%}}
|
||||
.w3-top,.w3-bottom{position:fixed;width:100%;z-index:1}.w3-top{top:0}.w3-bottom{bottom:0}
|
||||
.w3-overlay{position:fixed;display:none;width:100%;height:100%;top:0;left:0;right:0;bottom:0;background-color:rgba(0,0,0,0.5);z-index:2}
|
||||
.w3-display-topleft{position:absolute;left:0;top:0}.w3-display-topright{position:absolute;right:0;top:0}
|
||||
.w3-display-bottomleft{position:absolute;left:0;bottom:0}.w3-display-bottomright{position:absolute;right:0;bottom:0}
|
||||
.w3-display-middle{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%)}
|
||||
.w3-display-left{position:absolute;top:50%;left:0%;transform:translate(0%,-50%);-ms-transform:translate(-0%,-50%)}
|
||||
.w3-display-right{position:absolute;top:50%;right:0%;transform:translate(0%,-50%);-ms-transform:translate(0%,-50%)}
|
||||
.w3-display-topmiddle{position:absolute;left:50%;top:0;transform:translate(-50%,0%);-ms-transform:translate(-50%,0%)}
|
||||
.w3-display-bottommiddle{position:absolute;left:50%;bottom:0;transform:translate(-50%,0%);-ms-transform:translate(-50%,0%)}
|
||||
.w3-display-container:hover .w3-display-hover{display:block}.w3-display-container:hover span.w3-display-hover{display:inline-block}.w3-display-hover{display:none}
|
||||
.w3-display-position{position:absolute}
|
||||
.w3-circle{border-radius:50%}
|
||||
.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px}
|
||||
.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px}
|
||||
.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px}
|
||||
.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px}
|
||||
.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word}
|
||||
.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%}
|
||||
.w3-card,.w3-card-2{box-shadow:0 2px 5px 0 rgba(0,0,0,0.16),0 2px 10px 0 rgba(0,0,0,0.12)}
|
||||
.w3-card-4,.w3-hover-shadow:hover{box-shadow:0 4px 10px 0 rgba(0,0,0,0.2),0 4px 20px 0 rgba(0,0,0,0.19)}
|
||||
.w3-spin{animation:w3-spin 2s infinite linear}@keyframes w3-spin{0%{transform:rotate(0deg)}100%{transform:rotate(359deg)}}
|
||||
.w3-animate-fading{animation:fading 10s infinite}@keyframes fading{0%{opacity:0}50%{opacity:1}100%{opacity:0}}
|
||||
.w3-animate-opacity{animation:opac 0.8s}@keyframes opac{from{opacity:0} to{opacity:1}}
|
||||
.w3-animate-top{position:relative;animation:animatetop 0.4s}@keyframes animatetop{from{top:-300px;opacity:0} to{top:0;opacity:1}}
|
||||
.w3-animate-left{position:relative;animation:animateleft 0.4s}@keyframes animateleft{from{left:-300px;opacity:0} to{left:0;opacity:1}}
|
||||
.w3-animate-right{position:relative;animation:animateright 0.4s}@keyframes animateright{from{right:-300px;opacity:0} to{right:0;opacity:1}}
|
||||
.w3-animate-bottom{position:relative;animation:animatebottom 0.4s}@keyframes animatebottom{from{bottom:-300px;opacity:0} to{bottom:0;opacity:1}}
|
||||
.w3-animate-zoom {animation:animatezoom 0.6s}@keyframes animatezoom{from{transform:scale(0)} to{transform:scale(1)}}
|
||||
.w3-animate-input{transition:width 0.4s ease-in-out}.w3-animate-input:focus{width:100%!important}
|
||||
.w3-opacity,.w3-hover-opacity:hover{opacity:0.60}.w3-opacity-off,.w3-hover-opacity-off:hover{opacity:1}
|
||||
.w3-opacity-max{opacity:0.25}.w3-opacity-min{opacity:0.75}
|
||||
.w3-greyscale-max,.w3-grayscale-max,.w3-hover-greyscale:hover,.w3-hover-grayscale:hover{filter:grayscale(100%)}
|
||||
.w3-greyscale,.w3-grayscale{filter:grayscale(75%)}.w3-greyscale-min,.w3-grayscale-min{filter:grayscale(50%)}
|
||||
.w3-sepia{filter:sepia(75%)}.w3-sepia-max,.w3-hover-sepia:hover{filter:sepia(100%)}.w3-sepia-min{filter:sepia(50%)}
|
||||
.w3-tiny{font-size:10px!important}.w3-small{font-size:12px!important}.w3-medium{font-size:15px!important}.w3-large{font-size:18px!important}
|
||||
.w3-xlarge{font-size:24px!important}.w3-xxlarge{font-size:36px!important}.w3-xxxlarge{font-size:48px!important}.w3-jumbo{font-size:64px!important}
|
||||
.w3-left-align{text-align:left!important}.w3-right-align{text-align:right!important}.w3-justify{text-align:justify!important}.w3-center{text-align:center!important}
|
||||
.w3-border-0{border:0!important}.w3-border{border:1px solid #ccc!important}
|
||||
.w3-border-top{border-top:1px solid #ccc!important}.w3-border-bottom{border-bottom:1px solid #ccc!important}
|
||||
.w3-border-left{border-left:1px solid #ccc!important}.w3-border-right{border-right:1px solid #ccc!important}
|
||||
.w3-topbar{border-top:6px solid #ccc!important}.w3-bottombar{border-bottom:6px solid #ccc!important}
|
||||
.w3-leftbar{border-left:6px solid #ccc!important}.w3-rightbar{border-right:6px solid #ccc!important}
|
||||
.w3-section,.w3-code{margin-top:16px!important;margin-bottom:16px!important}
|
||||
.w3-margin{margin:16px!important}.w3-margin-top{margin-top:16px!important}.w3-margin-bottom{margin-bottom:16px!important}
|
||||
.w3-margin-left{margin-left:16px!important}.w3-margin-right{margin-right:16px!important}
|
||||
.w3-padding-small{padding:4px 8px!important}.w3-padding{padding:8px 16px!important}.w3-padding-large{padding:12px 24px!important}
|
||||
.w3-padding-16{padding-top:16px!important;padding-bottom:16px!important}.w3-padding-24{padding-top:24px!important;padding-bottom:24px!important}
|
||||
.w3-padding-32{padding-top:32px!important;padding-bottom:32px!important}.w3-padding-48{padding-top:48px!important;padding-bottom:48px!important}
|
||||
.w3-padding-64{padding-top:64px!important;padding-bottom:64px!important}
|
||||
.w3-padding-top-64{padding-top:64px!important}.w3-padding-top-48{padding-top:48px!important}
|
||||
.w3-padding-top-32{padding-top:32px!important}.w3-padding-top-24{padding-top:24px!important}
|
||||
.w3-left{float:left!important}.w3-right{float:right!important}
|
||||
.w3-button:hover{color:#000!important;background-color:#ccc!important}
|
||||
.w3-transparent,.w3-hover-none:hover{background-color:transparent!important}
|
||||
.w3-hover-none:hover{box-shadow:none!important}
|
||||
/* Colors */
|
||||
.w3-amber,.w3-hover-amber:hover{color:#000!important;background-color:#ffc107!important}
|
||||
.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important}
|
||||
.w3-blue,.w3-hover-blue:hover{color:#fff!important;background-color:#2196F3!important}
|
||||
.w3-light-blue,.w3-hover-light-blue:hover{color:#000!important;background-color:#87CEEB!important}
|
||||
.w3-brown,.w3-hover-brown:hover{color:#fff!important;background-color:#795548!important}
|
||||
.w3-cyan,.w3-hover-cyan:hover{color:#000!important;background-color:#00bcd4!important}
|
||||
.w3-blue-grey,.w3-hover-blue-grey:hover,.w3-blue-gray,.w3-hover-blue-gray:hover{color:#fff!important;background-color:#607d8b!important}
|
||||
.w3-green,.w3-hover-green:hover{color:#fff!important;background-color:#4CAF50!important}
|
||||
.w3-light-green,.w3-hover-light-green:hover{color:#000!important;background-color:#8bc34a!important}
|
||||
.w3-indigo,.w3-hover-indigo:hover{color:#fff!important;background-color:#3f51b5!important}
|
||||
.w3-khaki,.w3-hover-khaki:hover{color:#000!important;background-color:#f0e68c!important}
|
||||
.w3-lime,.w3-hover-lime:hover{color:#000!important;background-color:#cddc39!important}
|
||||
.w3-orange,.w3-hover-orange:hover{color:#000!important;background-color:#ff9800!important}
|
||||
.w3-deep-orange,.w3-hover-deep-orange:hover{color:#fff!important;background-color:#ff5722!important}
|
||||
.w3-pink,.w3-hover-pink:hover{color:#fff!important;background-color:#e91e63!important}
|
||||
.w3-purple,.w3-hover-purple:hover{color:#fff!important;background-color:#9c27b0!important}
|
||||
.w3-deep-purple,.w3-hover-deep-purple:hover{color:#fff!important;background-color:#673ab7!important}
|
||||
.w3-red,.w3-hover-red:hover{color:#fff!important;background-color:#f44336!important}
|
||||
.w3-sand,.w3-hover-sand:hover{color:#000!important;background-color:#fdf5e6!important}
|
||||
.w3-teal,.w3-hover-teal:hover{color:#fff!important;background-color:#009688!important}
|
||||
.w3-yellow,.w3-hover-yellow:hover{color:#000!important;background-color:#ffeb3b!important}
|
||||
.w3-white,.w3-hover-white:hover{color:#000!important;background-color:#fff!important}
|
||||
.w3-black,.w3-hover-black:hover{color:#fff!important;background-color:#000!important}
|
||||
.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover{color:#000!important;background-color:#9e9e9e!important}
|
||||
.w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important}
|
||||
.w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important}
|
||||
.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important}
|
||||
.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important}
|
||||
.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important}
|
||||
.w3-pale-blue,.w3-hover-pale-blue:hover{color:#000!important;background-color:#ddffff!important}
|
||||
.w3-text-amber,.w3-hover-text-amber:hover{color:#ffc107!important}
|
||||
.w3-text-aqua,.w3-hover-text-aqua:hover{color:#00ffff!important}
|
||||
.w3-text-blue,.w3-hover-text-blue:hover{color:#2196F3!important}
|
||||
.w3-text-light-blue,.w3-hover-text-light-blue:hover{color:#87CEEB!important}
|
||||
.w3-text-brown,.w3-hover-text-brown:hover{color:#795548!important}
|
||||
.w3-text-cyan,.w3-hover-text-cyan:hover{color:#00bcd4!important}
|
||||
.w3-text-blue-grey,.w3-hover-text-blue-grey:hover,.w3-text-blue-gray,.w3-hover-text-blue-gray:hover{color:#607d8b!important}
|
||||
.w3-text-green,.w3-hover-text-green:hover{color:#4CAF50!important}
|
||||
.w3-text-light-green,.w3-hover-text-light-green:hover{color:#8bc34a!important}
|
||||
.w3-text-indigo,.w3-hover-text-indigo:hover{color:#3f51b5!important}
|
||||
.w3-text-khaki,.w3-hover-text-khaki:hover{color:#b4aa50!important}
|
||||
.w3-text-lime,.w3-hover-text-lime:hover{color:#cddc39!important}
|
||||
.w3-text-orange,.w3-hover-text-orange:hover{color:#ff9800!important}
|
||||
.w3-text-deep-orange,.w3-hover-text-deep-orange:hover{color:#ff5722!important}
|
||||
.w3-text-pink,.w3-hover-text-pink:hover{color:#e91e63!important}
|
||||
.w3-text-purple,.w3-hover-text-purple:hover{color:#9c27b0!important}
|
||||
.w3-text-deep-purple,.w3-hover-text-deep-purple:hover{color:#673ab7!important}
|
||||
.w3-text-red,.w3-hover-text-red:hover{color:#f44336!important}
|
||||
.w3-text-sand,.w3-hover-text-sand:hover{color:#fdf5e6!important}
|
||||
.w3-text-teal,.w3-hover-text-teal:hover{color:#009688!important}
|
||||
.w3-text-yellow,.w3-hover-text-yellow:hover{color:#d2be0e!important}
|
||||
.w3-text-white,.w3-hover-text-white:hover{color:#fff!important}
|
||||
.w3-text-black,.w3-hover-text-black:hover{color:#000!important}
|
||||
.w3-text-grey,.w3-hover-text-grey:hover,.w3-text-gray,.w3-hover-text-gray:hover{color:#757575!important}
|
||||
.w3-text-light-grey,.w3-hover-text-light-grey:hover,.w3-text-light-gray,.w3-hover-text-light-gray:hover{color:#f1f1f1!important}
|
||||
.w3-text-dark-grey,.w3-hover-text-dark-grey:hover,.w3-text-dark-gray,.w3-hover-text-dark-gray:hover{color:#3a3a3a!important}
|
||||
.w3-border-amber,.w3-hover-border-amber:hover{border-color:#ffc107!important}
|
||||
.w3-border-aqua,.w3-hover-border-aqua:hover{border-color:#00ffff!important}
|
||||
.w3-border-blue,.w3-hover-border-blue:hover{border-color:#2196F3!important}
|
||||
.w3-border-light-blue,.w3-hover-border-light-blue:hover{border-color:#87CEEB!important}
|
||||
.w3-border-brown,.w3-hover-border-brown:hover{border-color:#795548!important}
|
||||
.w3-border-cyan,.w3-hover-border-cyan:hover{border-color:#00bcd4!important}
|
||||
.w3-border-blue-grey,.w3-hover-border-blue-grey:hover,.w3-border-blue-gray,.w3-hover-border-blue-gray:hover{border-color:#607d8b!important}
|
||||
.w3-border-green,.w3-hover-border-green:hover{border-color:#4CAF50!important}
|
||||
.w3-border-light-green,.w3-hover-border-light-green:hover{border-color:#8bc34a!important}
|
||||
.w3-border-indigo,.w3-hover-border-indigo:hover{border-color:#3f51b5!important}
|
||||
.w3-border-khaki,.w3-hover-border-khaki:hover{border-color:#f0e68c!important}
|
||||
.w3-border-lime,.w3-hover-border-lime:hover{border-color:#cddc39!important}
|
||||
.w3-border-orange,.w3-hover-border-orange:hover{border-color:#ff9800!important}
|
||||
.w3-border-deep-orange,.w3-hover-border-deep-orange:hover{border-color:#ff5722!important}
|
||||
.w3-border-pink,.w3-hover-border-pink:hover{border-color:#e91e63!important}
|
||||
.w3-border-purple,.w3-hover-border-purple:hover{border-color:#9c27b0!important}
|
||||
.w3-border-deep-purple,.w3-hover-border-deep-purple:hover{border-color:#673ab7!important}
|
||||
.w3-border-red,.w3-hover-border-red:hover{border-color:#f44336!important}
|
||||
.w3-border-sand,.w3-hover-border-sand:hover{border-color:#fdf5e6!important}
|
||||
.w3-border-teal,.w3-hover-border-teal:hover{border-color:#009688!important}
|
||||
.w3-border-yellow,.w3-hover-border-yellow:hover{border-color:#ffeb3b!important}
|
||||
.w3-border-white,.w3-hover-border-white:hover{border-color:#fff!important}
|
||||
.w3-border-black,.w3-hover-border-black:hover{border-color:#000!important}
|
||||
.w3-border-grey,.w3-hover-border-grey:hover,.w3-border-gray,.w3-hover-border-gray:hover{border-color:#9e9e9e!important}
|
||||
.w3-border-light-grey,.w3-hover-border-light-grey:hover,.w3-border-light-gray,.w3-hover-border-light-gray:hover{border-color:#f1f1f1!important}
|
||||
.w3-border-dark-grey,.w3-hover-border-dark-grey:hover,.w3-border-dark-gray,.w3-hover-border-dark-gray:hover{border-color:#616161!important}
|
||||
.w3-border-pale-red,.w3-hover-border-pale-red:hover{border-color:#ffe7e7!important}.w3-border-pale-green,.w3-hover-border-pale-green:hover{border-color:#e7ffe7!important}
|
||||
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
||||
3
src/main/resources/application.properties
Normal file
@@ -0,0 +1,3 @@
|
||||
quarkus.http.port=8080
|
||||
quarkus.http.cors=true
|
||||
quarkus.http.cors.origins=*
|
||||
75
src/main/resources/templates/chatoverlay.html
Normal file
@@ -0,0 +1,75 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Dice-Tower - Overlay</title>
|
||||
<link rel="icon" type="image/png" href="/favicon.png">
|
||||
<link rel="stylesheet" href="/overlay/style.css">
|
||||
<script src="/vendor/comfy.js/comfy.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="dice-box"></div>
|
||||
<div popover id="results" class="tooltip">
|
||||
</div>
|
||||
<script type="module">
|
||||
import DiceBox from "/vendor/dice-box/dice-box-threejs.es.js";
|
||||
const diceBox = new DiceBox("#dice-box", {
|
||||
assetPath: "/vendor/dice-box/",
|
||||
light_intensity: 2,
|
||||
gravity_multiplier: 600,
|
||||
baseScale: {scale} * 10,
|
||||
strength: Math.floor(Math.random() * 4),
|
||||
theme_customColorset: {
|
||||
texture: '{theme}',
|
||||
background: '{faceColor}',
|
||||
foreground: '{numberColor}'
|
||||
}
|
||||
});
|
||||
diceBox.initialize();
|
||||
ComfyJS.Init('{channel}');
|
||||
|
||||
//maxDice
|
||||
|
||||
ComfyJS.onCommand = async (user, command, message, flags) => {
|
||||
if ((flags.broadcaster || {allAllowed} || ({modsAllowed} && flags.mod) || ({vipAllowed} && flags.vip) || ({subsAllowed} && flags.subscriber)) && '{cmd}' === command && !shouldWait() && message.match(/^\d+d(4|6|8|10|12|20|100)$/) && (+message.split('d')[0] <= {maxDice})
|
||||
) {
|
||||
toggleWait(true);
|
||||
|
||||
diceBox.onRollComplete = (rollResult) => {
|
||||
rollResult.sets.forEach(result => {
|
||||
let values = []
|
||||
result.rolls.forEach(roll => {
|
||||
values.push(roll.value);
|
||||
})
|
||||
document.getElementById('results').innerHTML = '<strong>' + user + '</strong> rolls <strong>' + message + '</strong>:<br/> [' + values.map(value => value === 1 ? '<strong style="text-shadow: 2px 2px 10px red">' + value + '</strong>' : value === result.sides ? '<strong style="text-shadow: 2px 2px 10px green">' + value + '</strong>' : value).join(' + ') + '] = <strong>' + result.total + '</strong> '
|
||||
})
|
||||
if({showResults}) {
|
||||
document.getElementById('results').showPopover()
|
||||
}
|
||||
setTimeout(() => {
|
||||
diceBox.clearDice();
|
||||
document.getElementById('results').hidePopover()
|
||||
}, {clearAfter} * 1000)
|
||||
}
|
||||
|
||||
diceBox.roll(message);
|
||||
setTimeout(() => {
|
||||
toggleWait(false);
|
||||
}, {clearAfter} * 1000 + {timeout} * 1000)
|
||||
} else {
|
||||
console.log('Not a valid command or not ready yet');
|
||||
}
|
||||
};
|
||||
|
||||
let wait;
|
||||
|
||||
function shouldWait() {
|
||||
return wait;
|
||||
}
|
||||
|
||||
function toggleWait(value) {
|
||||
wait = value;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
262
src/main/resources/templates/chatoverlayconfig.html
Normal file
@@ -0,0 +1,262 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Dice-Tower - Chatoverlay</title>
|
||||
<link rel="stylesheet" href="/vendor/w3css/4/w3.css">
|
||||
<link rel="stylesheet" href="/vendor/font-awesome/css/fontawesome.css">
|
||||
<link rel="stylesheet" href="/vendor/font-awesome/css/all.css">
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<link rel="icon" type="image/png" href="/favicon.png">
|
||||
<script src="/vendor/color-picker.js"></script>
|
||||
</head>
|
||||
<body class="w3-theme-l1">
|
||||
|
||||
<div class="w3-container w3-content"
|
||||
style="height: 95vh; display: flex; flex-direction: column; justify-content: space-between; padding: 25px">
|
||||
<h1 style="text-align: center"><i class="fa-solid fa-dice-d20"></i> Dice-Tower - Chatoverlay <i
|
||||
class="fa-solid fa-dice-d20"></i></h1>
|
||||
<div class="w3-panel w3-theme-l4 w3-card w3-display-container" style="padding: 25px; text-align: center;">
|
||||
<p>Allows Chat to roll a given amount of one dice</p>
|
||||
<p>Example: "!roll 5d20"</p>
|
||||
</div>
|
||||
<div class="w3-panel w3-theme-l4 w3-card w3-display-container"
|
||||
style="padding: 25px; text-align: center; margin-bottom: auto;">
|
||||
<label for="theme">Theme </label>
|
||||
<select name="theme" id="theme" style="margin: 0 25px"></select>
|
||||
|
||||
<div style="display: flex; flex-direction: row; justify-content: space-between; align-items: baseline">
|
||||
<div style="flex-grow: 1; padding: 0 10px">
|
||||
<color-picker id="faceColor" name="Face" value="black"></color-picker>
|
||||
</div>
|
||||
<div style="flex-grow: 1; padding: 10px 0">
|
||||
<color-picker id="numberColor" name="Numbers" value="white"></color-picker>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label for="channel">Channel </label>
|
||||
<input type="text" id="channel" style="width: 400px; margin-top: 20px" value="arindy"/>
|
||||
<label for="cmd">Command !</label>
|
||||
<input type="text" id="cmd" style="width: 100px; margin-top: 20px; margin-left: 0" value="roll"/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="checkbox" id="modsAllowed-container">Allow mods to roll
|
||||
<input type="checkbox" id="modsAllowed">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
<label class="checkbox" id="vipAllowed-container">Allow VIPs to roll
|
||||
<input type="checkbox" id="vipAllowed">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
<label class="checkbox" id="subsAllowed-container">Allow subs to roll
|
||||
<input type="checkbox" id="subsAllowed">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
<label class="checkbox" id="allAllowed-container">Allow everyone to roll (yes, everyone!!)
|
||||
<input type="checkbox" id="allAllowed">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label for="scale">Dice-Scale </label>
|
||||
<input type="number" id="scale" style="width: 50px; margin-top: 20px" value="15"/>
|
||||
<label for="maxDice">Max number of dice </label>
|
||||
<input type="number" id="maxDice" style="width: 50px; margin-top: 20px" value="20"/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="clearAfter">Clear dice after (in seconds)</label>
|
||||
<input type="number" id="clearAfter" style="width: 50px; margin-top: 20px" value="10"/>
|
||||
<label for="timeout">Command-timeout (in seconds)</label>
|
||||
<input type="number" id="timeout" style="width: 50px; margin-top: 20px" value="60"/>
|
||||
<label class="checkbox" id="showResults-container">Show Results Overlay
|
||||
<input type="checkbox" id="showResults" checked>
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="dice-box" style="width: 850px; height: 400px">
|
||||
<div id="app"></div>
|
||||
</div>
|
||||
<button style="margin: 10px" id="preview">Preview <i class="fa-solid fa-magnifying-glass"></i></button>
|
||||
<button style="margin: 10px" id="generate">Generate overlay-link <i class="fa-solid fa-link"></i></button>
|
||||
<div>
|
||||
<input type="text" readonly id="link" style="width: 80%; margin-top: 20px"
|
||||
onclick="copyToClipboard(this.id)"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="snackbar-container">
|
||||
<div id="snackbar">.. they see them rolling</div>
|
||||
</div>
|
||||
<script>
|
||||
function copyToClipboard(id) {
|
||||
let copyText = document.getElementById(id)
|
||||
if (copyText.value.length > 0) {
|
||||
copyText.select();
|
||||
copyText.setSelectionRange(0, 99999);
|
||||
navigator.clipboard.writeText(copyText.value).then(() => {
|
||||
showSnackbar("<i class='fa-regular fa-copy'></i> Link copied to clipboard: <br/>" + copyText.value);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function showSnackbar(message) {
|
||||
let snackbar = document.getElementById("snackbar");
|
||||
let snackbarContainer = document.getElementById("snackbar-container");
|
||||
snackbar.innerHTML = message;
|
||||
snackbar.className = "show";
|
||||
snackbarContainer.className = "show";
|
||||
setTimeout(() => {
|
||||
snackbar.className = "";
|
||||
snackbarContainer.className = "";
|
||||
}, 3000)
|
||||
}
|
||||
</script>
|
||||
<script type="module">
|
||||
import DiceBox from "/vendor/dice-box/dice-box-threejs.es.js";
|
||||
let diceBox
|
||||
|
||||
const Themes = {
|
||||
cloudy: {
|
||||
name: "Clouds (Transparent)"
|
||||
},
|
||||
cloudy_2: {
|
||||
name: "Clouds"
|
||||
},
|
||||
fire: {
|
||||
name: "Fire"
|
||||
},
|
||||
marble: {
|
||||
name: "Marble"
|
||||
},
|
||||
water: {
|
||||
name: "Water"
|
||||
},
|
||||
ice: {
|
||||
name: "Ice"
|
||||
},
|
||||
paper: {
|
||||
name: "Paper"
|
||||
},
|
||||
speckles: {
|
||||
name: "Speckles"
|
||||
},
|
||||
glitter: {
|
||||
name: "Glitter"
|
||||
},
|
||||
glitter_2: {
|
||||
name: "Glitter (Transparent)"
|
||||
},
|
||||
stars: {
|
||||
name: "Stars"
|
||||
},
|
||||
stainedglass: {
|
||||
name: "Stained Glass"
|
||||
},
|
||||
wood: {
|
||||
name: "Wood"
|
||||
},
|
||||
metal: {
|
||||
name: "Stainless Steel"
|
||||
},
|
||||
skulls: {
|
||||
name: "Skulls"
|
||||
},
|
||||
leopard: {
|
||||
name: "Leopard"
|
||||
},
|
||||
tiger: {
|
||||
name: "Tiger"
|
||||
},
|
||||
cheetah: {
|
||||
name: "Cheetah"
|
||||
},
|
||||
dragon: {
|
||||
name: "Dragon"
|
||||
},
|
||||
lizard: {
|
||||
name: "Lizard"
|
||||
},
|
||||
bird: {
|
||||
name: "Bird"
|
||||
},
|
||||
astral: {
|
||||
name: "Astral Sea"
|
||||
},
|
||||
bronze01: {
|
||||
name: "Bronze 1"
|
||||
},
|
||||
bronze02: {
|
||||
name: "Bronze 2"
|
||||
},
|
||||
bronze03: {
|
||||
name: "Bronze 3"
|
||||
},
|
||||
bronze03a: {
|
||||
name: "Bronze 3a"
|
||||
},
|
||||
bronze03b: {
|
||||
name: "Bronze 3b"
|
||||
},
|
||||
bronze04: {
|
||||
name: "Bronze 4"
|
||||
},
|
||||
none: {
|
||||
name: "none"
|
||||
}
|
||||
}
|
||||
|
||||
function url() {
|
||||
return window.location.protocol + '//' + window.location.hostname + (window.location.port?.length > 0 ? ':' + window.location.port : '');
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", async () => {
|
||||
const themeSelector = document.getElementById('theme');
|
||||
for (const theme in Themes) {
|
||||
let option = document.createElement('option');
|
||||
option.value = theme;
|
||||
option.innerText = Themes[theme].name;
|
||||
themeSelector.appendChild(option);
|
||||
}
|
||||
themeSelector.value = 'cloudy';
|
||||
document.getElementById('preview').onclick = async () => {
|
||||
document.getElementById('app').replaceChildren(...[])
|
||||
diceBox = new DiceBox("#app", {
|
||||
assetPath: "/vendor/dice-box/",
|
||||
light_intensity: 2,
|
||||
gravity_multiplier: 600,
|
||||
baseScale: 120,
|
||||
strength: Math.floor(Math.random() * 4),
|
||||
});
|
||||
await diceBox.initialize();
|
||||
diceBox.clearDice();
|
||||
await diceBox.updateConfig({
|
||||
theme_customColorset: {
|
||||
background: document.getElementById('faceColor').value,
|
||||
foreground: document.getElementById('numberColor').value,
|
||||
texture: document.getElementById('theme').value
|
||||
}
|
||||
});
|
||||
await diceBox.roll('1d2 & 1d4 & 1d6 & 1d8 & 1d10 & 1d12 & 1d20 & 1d100');
|
||||
}
|
||||
document.getElementById('generate').onclick = async () => {
|
||||
document.getElementById('link').value = url() +
|
||||
"/chatoverlay/" + document.getElementById('channel').value +
|
||||
"?cmd=" + document.getElementById('cmd').value +
|
||||
"&theme=" + document.getElementById('theme').value +
|
||||
"&faceColor=" + encodeURIComponent(document.getElementById('faceColor').value) +
|
||||
"&numberColor=" + encodeURIComponent(document.getElementById('numberColor').value) +
|
||||
"&scale=" + document.getElementById('scale').value +
|
||||
"&maxDice=" + document.getElementById('maxDice').value +
|
||||
"&clearAfter=" + document.getElementById('clearAfter').value +
|
||||
"&timeout=" + document.getElementById('timeout').value +
|
||||
"&modsAllowed=" + document.getElementById('modsAllowed').checked +
|
||||
"&vipAllowed=" + document.getElementById('vipAllowed').checked +
|
||||
"&subsAllowed=" + document.getElementById('subsAllowed').checked +
|
||||
"&allAllowed=" + document.getElementById('allAllowed').checked +
|
||||
"&showResults=" + document.getElementById('showResults').checked
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
231
src/main/resources/templates/index.html
Normal file
@@ -0,0 +1,231 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Dice-Tower</title>
|
||||
<meta name="version" content="{version}">
|
||||
<link rel="manifest" href="manifest.json" />
|
||||
<link rel="stylesheet" href="/vendor/w3css/4/w3.css">
|
||||
<link rel="stylesheet" href="/vendor/font-awesome/css/fontawesome.css">
|
||||
<link rel="stylesheet" href="/vendor/font-awesome/css/all.css">
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<link rel="icon" type="image/png" href="/favicon.png">
|
||||
<script src="/vendor/color-picker.js"></script>
|
||||
<script type="module" src="/dice-preview.js"></script>
|
||||
<script type="text/javascript" src="/app.js"></script>
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="Dice-Tower">
|
||||
<meta name="twitter:image" content="{http:request.scheme}://{http:request.authority}/rich.png">
|
||||
<meta name="description" content="Easy to use online dice rolling with customizable overlays.">
|
||||
<meta name="twitter:description" content="Easy to use online dice rolling with customizable overlays.">
|
||||
<meta property="og:url" content="{http:request.absoluteURI}">
|
||||
<meta property="og:image" content="{http:request.scheme}://{http:request.authority}/rich.png">
|
||||
<meta property="og:description" content="Easy to use online dice rolling with customizable overlays.">
|
||||
<meta property="og:title" content="Dice-Tower">
|
||||
<meta property="og:site_name" content="Dice-Tower">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="twitter:url" content="{http:request.absoluteURI}">
|
||||
<meta property="twitter:domain" content="{http:request.authority}">
|
||||
</head>
|
||||
<body class="w3-theme-l1">
|
||||
<script>
|
||||
if (!localStorage.getItem("userId")) {
|
||||
localStorage.setItem("userId", self.crypto.randomUUID());
|
||||
}
|
||||
addEventListener("beforeinstallprompt", (event) => {
|
||||
event.preventDefault()
|
||||
let install = document.createElement('button');
|
||||
install.id = 'install'
|
||||
install.style.position = 'absolute'
|
||||
install.style.top = '25px'
|
||||
install.style.right = '25px'
|
||||
install.innerHTML = 'Install Dice-Tower'
|
||||
install.onclick = () => event.prompt()
|
||||
document.body.appendChild(install)
|
||||
});
|
||||
</script>
|
||||
<div class="w3-container w3-content"
|
||||
style="height: 95vh; display: flex; flex-direction: column; justify-content: space-between; padding: 25px">
|
||||
<h1 style="text-align: center"><i class="fa-solid fa-dice-d20"></i> Dice-Tower <i class="fa-solid fa-dice-d20"></i>
|
||||
</h1>
|
||||
|
||||
<div class="w3-panel w3-theme-l4 w3-card w3-display-container"
|
||||
style="padding: 25px; text-align: center; margin-bottom: auto;">
|
||||
<h2 id="nameH" popovertarget="room-hint" data-trigger="hover" style="margin: 0" hidden>Name</h2>
|
||||
<div>
|
||||
<label for="name" id="nameLabel">Name </label>
|
||||
<input type="text" id="name" style="width: 50%; margin-top: 20px" required onkeyup="start(event)"/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="room" id="roomLabel">Room </label>
|
||||
<input type="text" id="room" style="width: 50%" required onkeyup="start(event)"/>
|
||||
</div>
|
||||
<div id="start-container">
|
||||
<div>
|
||||
<button id="start" onclick="start()" style="align-self: center; margin-top: 20px">Join <i
|
||||
class="fa-solid fa-right-to-bracket"></i></button>
|
||||
<label class="checkbox" id="gm-container">Join as GM
|
||||
<input type="checkbox" id="gm">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div id="options-container" hidden style="position: absolute; right: 25px; top: 5px">
|
||||
<button id="customize" popovertarget="customize-overlay" style="align-self: center; margin-top: 20px"><i class="fa-solid fa-palette"></i>
|
||||
</button>
|
||||
<button id="urls" popovertarget="urls-overlay" style="align-self: center; margin-top: 20px"><i class="fa-solid fa-link"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="dice-tower" hidden class="w3-panel w3-theme-l4 w3-card w3-display-container"
|
||||
style="padding: 25px; margin-bottom: auto">
|
||||
|
||||
<div style="display: flex; flex-direction: row; justify-content: space-between; align-items: center; margin: 10px; padding-right: 25px; padding-left: 25px">
|
||||
<label style="font-size: large; font-weight: bold; margin: 10px">Roll </label>
|
||||
<button style="border: transparent; border-radius: 100%; font-size: large; font-weight: bold; height: 50px; width: 50px"
|
||||
onclick="removeDice()">-
|
||||
</button>
|
||||
<label style="font-size: large; font-weight: bold" id="dice-amount">1</label>
|
||||
<button style="border: transparent; border-radius: 100%; font-size: large; font-weight: bold; height: 50px; width: 50px"
|
||||
onclick="addDice()">+
|
||||
</button>
|
||||
<!--<button style="font-size: large; font-weight: bold;" onclick="rollEasy('d2')">D2</button><-->
|
||||
<button style="font-size: large; font-weight: bold;" onclick="rollEasy('d4')">D4</button>
|
||||
<button style="font-size: large; font-weight: bold;" onclick="rollEasy('d6')">D6</button>
|
||||
<button style="font-size: large; font-weight: bold;" onclick="rollEasy('d8')">D8</button>
|
||||
<button style="font-size: large; font-weight: bold;" onclick="rollEasy('d10')">D10</button>
|
||||
<button style="font-size: large; font-weight: bold;" onclick="rollEasy('d12')">D12</button>
|
||||
<button style="font-size: large; font-weight: bold;" onclick="rollEasy('d20')">D20</button>
|
||||
<button style="font-size: 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 0;">
|
||||
<label for="command">Command </label>
|
||||
<input popovertarget="command-hint" data-trigger="hover" type="text" id="command" style="flex-grow: 1" onkeyup="roll(event)"/>
|
||||
<button hidden id="roll" onclick="roll()">Roll <i class="fa-solid fa-dice"></i></button>
|
||||
<div style="margin: 20px 25px" id="all-results">
|
||||
<label for="resultSwitch">Show all results </label>
|
||||
<label class="switch">
|
||||
<input type="checkbox" id="resultSwitch">
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</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: 100%; overflow: hidden; border: 0"></iframe>
|
||||
<label for="resultDiceSwitch" style="position: absolute; top: 25px; right: 95px">Show dice </label>
|
||||
<label class="switch" style="position: absolute; top: 25px; right: 25px; z-index: 5">
|
||||
<input type="checkbox" id="resultDiceSwitch">
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
<div id="results-dice" hidden>
|
||||
<iframe id="diceFrame" title="dice" style="width: 50%; height: 100%; overflow: hidden; border: 0; z-index: 4; position: absolute; top: 0; left: 50%"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div popover id="urls-overlay" class="tooltip" style="width: 600px">
|
||||
<div id="overlay-urls">
|
||||
<div style="display: flex; flex-direction: row; justify-content: space-between; align-items: baseline;">
|
||||
<label for="overlayId" id="overlayLabel">Dice-Overlay </label>
|
||||
<input popovertarget="overlay-hint" data-trigger="hover" type="text" readonly id="overlayId" style="flex-grow: 1" onclick="copyToClipboard(this.id)"/>
|
||||
</div>
|
||||
</div>
|
||||
<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 popovertarget="all-results-hint" data-trigger="hover" type="text" readonly id="resultsId" style="flex-grow: 1" onclick="copyToClipboard(this.id)"/>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: row; justify-content: space-between; align-items: baseline;">
|
||||
<label for="myResultsId">My-Results-Overlay </label>
|
||||
<input popovertarget="my-results-hint" data-trigger="hover" type="text" readonly id="myResultsId" style="flex-grow: 1" onclick="copyToClipboard(this.id)"/>
|
||||
</div>
|
||||
</div>
|
||||
<div popover id="customize-overlay" class="tooltip" style="width: 680px;">
|
||||
<div style="text-align: center;">
|
||||
<label for="theme">Theme </label>
|
||||
<select name="theme" id="theme" style="margin: 0 25px"></select>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: row; justify-content: space-between; align-items: baseline">
|
||||
<div style="flex-grow: 1; padding: 0 10px">
|
||||
<color-picker id="faceColor" name="Face" value="#8d8981"></color-picker>
|
||||
</div>
|
||||
<div style="flex-grow: 1; padding: 10px 0">
|
||||
<color-picker id="numberColor" name="Numbers" value="black"></color-picker>
|
||||
</div>
|
||||
</div>
|
||||
<div id="dice-box">
|
||||
<div id="app"></div>
|
||||
</div>
|
||||
|
||||
<button style="margin: 10px" id="preview">Preview <i class="fa-solid fa-magnifying-glass"></i></button>
|
||||
<button popovertarget="save-dice-hint" data-trigger="hover" style="margin: 10px" onclick="saveDice()">Save <i class="fa-solid fa-floppy-disk"></i></button>
|
||||
</div>
|
||||
<div hidden id="overlay-hint">
|
||||
<div style="text-align: left">
|
||||
<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>
|
||||
|
||||
<div hidden id="all-results-hint">
|
||||
<p>Shows all Results in this room</p>
|
||||
</div>
|
||||
<div hidden id="my-results-hint">
|
||||
<p>Shows only my Results in this room</p>
|
||||
</div>
|
||||
<div hidden id="save-dice-hint">
|
||||
<p>This saves your current theme and theme color for <a id="save-dice-hint-name"></a></p>
|
||||
</div>
|
||||
<div hidden id="command-hint">
|
||||
<p>Example Commands: "1d6", "2d8 1d100", "1d4 and 1d6", "2d20 & 1d2, "5d6+10"</p>
|
||||
</div>
|
||||
<div hidden id="room-hint">
|
||||
<p>How is your character called?</p>
|
||||
</div>
|
||||
<div hidden id="madeby-hint">
|
||||
<p>
|
||||
made by Arindy<br/>
|
||||
Click to go to source
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 20px" id="how-to">
|
||||
<div class="w3-panel w3-theme-l4 w3-card w3-display-container" style="padding: 25px;">
|
||||
<h2 style="text-align: center">How-To</h2>
|
||||
<ul>
|
||||
<li>
|
||||
Join a room by entering your character name and the name of the room
|
||||
</li>
|
||||
<li>Open your Dice-Overlay either in a new Tab or as a browser source in OBS</li>
|
||||
<ul>
|
||||
<li>You can configure your Overlay with query parameters (for more information hover over the link)
|
||||
</li>
|
||||
</ul>
|
||||
<li>Configure your dice</li>
|
||||
<li>Save your dice configuration</li>
|
||||
<li>Start rolling</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-top: 20px; flex-grow: 1" id="chatOverlay">
|
||||
<div class="w3-panel w3-theme-l4 w3-card w3-display-container" style="padding: 25px; margin-bottom: auto">
|
||||
If you are looking for a way to let your twitch chat roll click <a href target="_blank" id="chatOverlayLink">here</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="snackbar-container">
|
||||
<div id="snackbar">.. they see them rolling</div>
|
||||
</div>
|
||||
</body>
|
||||
<footer class="w3-theme-l1 w3-center">
|
||||
<a popovertarget="madeby-hint" data-trigger="hover" href="https://git.arindy.de/arindy/dice-tower" target="_blank" class="w3-hover-text-black">Version {version}</a>
|
||||
</footer>
|
||||
</html>
|
||||