This commit is contained in:
Maurice Grönwoldt 2021-08-19 13:43:41 +02:00
commit 3d91068e08
71 changed files with 4490 additions and 0 deletions

693
.gitignore vendored Normal file
View File

@ -0,0 +1,693 @@
# Created by https://www.toptal.com/developers/gitignore/api/node,intellij+all,intellij,visualstudio,visualstudiocode
# Edit at https://www.toptal.com/developers/gitignore?templates=node,intellij+all,intellij,visualstudio,visualstudiocode
### Intellij ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### Intellij Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
# *.iml
# modules.xml
# .idea/misc.xml
# *.ipr
# Sonarlint plugin
# https://plugins.jetbrains.com/plugin/7973-sonarlint
.idea/**/sonarlint/
# SonarQube Plugin
# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
.idea/**/sonarIssues.xml
# Markdown Navigator plugin
# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
.idea/**/markdown-navigator.xml
.idea/**/markdown-navigator-enh.xml
.idea/**/markdown-navigator/
# Cache file creation bug
# See https://youtrack.jetbrains.com/issue/JBR-2257
.idea/$CACHE_FILE$
# CodeStream plugin
# https://plugins.jetbrains.com/plugin/12206-codestream
.idea/codestream.xml
### Intellij+all ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
# AWS User-specific
# Generated files
# Sensitive or high-churn files
# Gradle
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
# Mongo Explorer plugin
# File-based project format
# IntelliJ
# mpeltonen/sbt-idea plugin
# JIRA plugin
# Cursive Clojure plugin
# Crashlytics plugin (for Android Studio and IntelliJ)
# Editor-based Rest Client
# Android studio 3.1+ serialized cache file
### Intellij+all Patch ###
# Ignores the whole .idea folder and all .iml files
# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
.idea/
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
*.iml
modules.xml
.idea/misc.xml
*.ipr
# Sonarlint plugin
.idea/sonarlint
### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
.env.production
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
### VisualStudioCode Patch ###
# Ignore all local history of files
.history
.ionide
### VisualStudio ###
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.tlog
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Nuget personal access tokens and Credentials
nuget.config
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
# Local History for Visual Studio Code
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp
# JetBrains Rider
*.sln.iml
### VisualStudio Patch ###
# Additional files built by Visual Studio
# End of https://www.toptal.com/developers/gitignore/api/node,intellij+all,intellij,visualstudio,visualstudiocode

2
build/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
node_modules/
package-lock.json

91
build/Readme.md Normal file
View File

@ -0,0 +1,91 @@
# Usage
*NOTES*
THIS NEED TO BE INSTALLED IN A SUBFOLDER!
Copy buildConfig.example.json to parent directory and rename it to buildConfig.json
Compiler Flags in the JS Tree are from "Google Closure Compiler"
# Install
```
git clone https://git.vstz.dev/versustunez/gulp-dynamic.git build
cd build
npm install
cp buildConfig.example.json ../buildConfig.json
```
# Config
### Variables
| Key | VALUE |
|-------|---------------------|
| $dir | ROOT DIR |
| $src | SRC FROM Config |
| $out | OUT FROM CONFIG |
| $name | NAME KEY FROM BLOCK |
| $ | BLOCK INPUT VALUE |
|-------|---------------------|
### ROOT
| Key | isRequired | TYPE |
|------|------------|--------|
| src | YES | STRING |
| out | YES | STRING |
| js | NO | ARRAY |
| scss | NO | ARRAY |
### JS-Array
| Key | isRequired | TYPE | DEFAULT |
|------------|------------|--------|-----------------------|
| name | YES | STRING | - |
| minify | NO | BOOL | false |
| onlyMinify | NO | BOOL | false |
| input | YES | STRING | - |
| output | YES | STRING | - |
| files | YES | ARRAY | - |
| compiler | NO | OBJECT | #JS-Compiler Settings |
### JS-Compiler Settings
| Key | isRequired | TYPE | DEFAULT |
|-------------------|------------|--------|-----------------------|
| compilation_level | NO | STRING | SIMPLE |
| warning_level | NO | STRING | VERBOSE |
| language_in | NO | STRING | ECMASCRIPT6_STRICT |
| language_out | NO | STRING | ECMASCRIPT6_STRICT |
| js_output_file | NO | STRING | $name.min.js |
*NOTES* SEE: [Google Compiler Flags](https://github.com/google/closure-compiler/wiki/Flags-and-Options)
## Example Config
```json
{
"src": "$dir/src",
"out": "$dir/public/out",
"js": [
{
"name": "main",
"minify": true,
"onlyMinify": true,
"input": "$src/js/",
"output": "$out/js/",
"files": [
"app.js"
],
"compiler": {
"compilation_level": "ADVANCED",
"warning_level": "VERBOSE",
"language_in": "ECMASCRIPT6_STRICT",
"language_out": "ECMASCRIPT6_STRICT",
"js_output_file": "$name.min.js"
}
}
],
"scss": [
{
"name": "main",
"input": "$src/theme/**/*.scss",
"output": "$out/theme/$name"
}
]
}
```

View File

@ -0,0 +1,30 @@
{
"src": "$dir/src",
"out": "$dir/public/out",
"js": [
{
"name": "main",
"minify": true,
"onlyMinify": true,
"input": "$src/js/",
"output": "$out/js/",
"files": [
"app.js"
],
"compiler": {
"compilation_level": "ADVANCED",
"warning_level": "VERBOSE",
"language_in": "ECMASCRIPT6_STRICT",
"language_out": "ECMASCRIPT6_STRICT",
"js_output_file": "$name.min.js"
}
}
],
"scss": [
{
"name": "main",
"input": "$src/theme/**/*.scss",
"output": "$out/theme/$name"
}
]
}

20
build/gulpfile.js Normal file
View File

@ -0,0 +1,20 @@
const gulp = require('gulp'),
config = require('./tools/buildConfig'),
smartJS = require('./task/smartjs'),
smartIcon = require('./task/smarticon'),
smartCss = require('./task/smartcss');
config.prepare();
smartJS.prepare(config);
smartCss.prepare(config);
smartIcon.prepare(config);
gulp.task('watchMe', () => {
smartJS.startWatch();
smartCss.startWatch();
smartIcon.startWatch();
});
gulp.task('default', gulp.parallel([...smartJS.build(), ...smartCss.build(), ...smartIcon.build()]));
gulp.task('watch', gulp.parallel(['watchMe', ...smartJS.build(), ...smartCss.build(), ...smartIcon.build()]));

10
build/index.js Normal file
View File

@ -0,0 +1,10 @@
const gulp = require('gulp-cli'),
shouldWatch = process.argv.length > 2 && process.argv[2] === 'watch';
gulp()
require('./gulpfile')
if (shouldWatch) {
gulp.series('watchMe')();
} else {
gulp.series('default')();
}

29
build/package.json Normal file
View File

@ -0,0 +1,29 @@
{
"name": "v-build",
"version": "1.0.0",
"description": "A Small Gulp Build System for Dynamic JS and SCSs",
"main": "index.js",
"scripts": {
"build-nm": "./node_modules/gulp-cli/bin/gulp.js",
"watch-nm": "./node_modules/gulp-cli/bin/gulp.js watch",
"build": "gulp",
"watch": "gulp watch"
},
"keywords": [
"GULP",
"JS",
"SCSS"
],
"author": "Maurice Grönwoldt",
"license": "GPL-3.0-or-later",
"dependencies": {
"google-closure-compiler": "^20210202.0.0",
"gulp": "^4.0.2",
"gulp-clean-css": "^4.3.0",
"gulp-cli": "^2.3.0",
"gulp-concat": "^2.6.1",
"gulp-sass": "^4.1.0",
"node-fs-extra": "^0.8.2",
"svg-sprite": "^1.5.0"
}
}

58
build/task/smartcss.js Normal file
View File

@ -0,0 +1,58 @@
const gulp = require('gulp'),
HelperUnit = require('./../tools/helperUnit'),
sass = require('gulp-sass'),
clean = require('gulp-clean-css'),
path = require('path'),
workers = [];
class SCSSWorker {
constructor(config, helper) {
this.config = config;
this.helper = helper.clone();
this.input = this.helper.replaceVariables(this.config.input);
this.watchInput = '.' + this.helper.replaceAbsolutePath(this.input);
this.helper.addConfigItem("$name", this.config.name);
this.helper.addConfigItem("$watch", this.watchInput);
this.helper.addConfigItem("$", this.input);
this.out = this.helper.replaceVariables(this.config.output);
this.watch = this.helper.replaceVariables(this.config.watch || '$watch');
gulp.task(this.taskName, this.work.bind(this));
}
startWatch() {
console.log(`[WATCH][${this.config.name}] >> ${this.watch}`);
gulp.watch(this.watch, gulp.series(this.taskName))
}
get taskName() {
return `scss-${this.config.name || 'unknown'}`;
}
work() {
return gulp.src(this.input)
.pipe(sass().on('error', sass.logError))
.pipe(clean())
.pipe(gulp.dest(this.out));
}
}
module.exports = {
build: function () {
let array = [];
for (let worker of workers) {
array.push(worker.taskName);
}
return array;
},
prepare: function (config) {
const scss = config.scss || [];
for (let scssItem of scss) {
workers.push(new SCSSWorker(scssItem, config.helper));
}
},
startWatch: function () {
for (let worker of workers) {
worker.startWatch();
}
}
}

53
build/task/smarticon.js Normal file
View File

@ -0,0 +1,53 @@
const gulp = require('gulp'),
builder = require('./../tools/iconSprite'),
workers = [];
class IconWorker {
constructor(icon, helper) {
this.config = icon;
this.helper = helper.clone();
this.input = this.helper.replaceVariables(this.config.input);
this.watchInput = '.' + this.helper.replaceAbsolutePath(this.input);
this.helper.addConfigItem("$name", this.config.name);
this.helper.addConfigItem("$watch", this.watchInput);
this.helper.addConfigItem("$", this.input);
this.out = this.helper.replaceVariables(this.config.output);
this.watch = this.helper.replaceVariables(this.config.watch || '$watch');
gulp.task(this.taskName, this.work.bind(this));
}
startWatch() {
console.log(`[WATCH][${this.config.name}] >> ${this.watch}`);
gulp.watch(this.watch, gulp.series(this.taskName))
}
get taskName() {
return `icon-${this.config.name}`;
}
work() {
builder.buildIconSprites(this);
return gulp.src(this.input)
}
}
module.exports = {
build: function () {
let array = [];
for (let worker of workers) {
array.push(worker.taskName);
}
return array;
},
prepare: function (config) {
const icons = config.icons || [];
for (let icon of icons) {
workers.push(new IconWorker(icon, config.helper));
}
},
startWatch: function () {
for (let worker of workers) {
worker.startWatch();
}
}
}

116
build/task/smartjs.js Normal file
View File

@ -0,0 +1,116 @@
const closureCompiler = require('google-closure-compiler').gulp(),
concat = require('gulp-concat'),
gulp = require('gulp'),
path = require('path'),
fileInclude = require('./../tools/file-includer'),
workers = [];
class JSWorker {
constructor(config, helper) {
this.config = config;
this.helper = helper.clone();
if (!this.config.name) {
throw Error("Found Empty name for JS Job...");
}
this.validateConfig();
this.input = this.helper.replaceVariables(this.config.input);
this.watchInput = '.' + this.helper.replaceAbsolutePath(this.input);
this.helper.addConfigItem("$name", this.config.name);
this.helper.addConfigItem("$watch", this.watchInput)
this.helper.addConfigItem("$", this.input);
this.getCompilerConfig();
this.out = this.helper.replaceVariables(this.config.output);
this.watch = this.helper.replaceVariables(this.config.watch || '$watch/**/*.js');
if (this.config.includeFile) {
this.includeFile = `${this.input}/${this.config.includeFile}`;
}
this.prepareFiles()
this.path = path.parse(this.out);
this.path = this.path.dir + "/" + this.path.base + "/";
gulp.task(this.taskName, this.work.bind(this));
}
validateConfig() {
let config = this.config;
config.onlyMinify = config.onlyMinify || false;
config.minify = config.minify || false;
if (!this.config.input || !this.config.output || !(this.config.files || this.config.includeFile)) {
throw Error("Invalid Config for: " + this.config.name + ")");
}
}
getCompilerConfig() {
this.compiler = {
compilation_level: 'SIMPLE',
warning_level: 'VERBOSE',
language_in: 'ECMASCRIPT6_STRICT',
language_out: 'ECMASCRIPT6_STRICT',
js_output_file: this.config.name + ".min.js"
}
if (this.config.compiler) {
this.compiler = Object.assign(this.compiler, this.config.compiler);
}
this.compiler.js_output_file = this.helper.replaceVariables(this.compiler.js_output_file);
}
startWatch() {
console.log(`[WATCH][${this.config.name}] >> ${this.watch}`);
const watchArray = [this.watch];
if (this.includeFile) {
watchArray.push(this.includeFile);
}
gulp.watch(watchArray, gulp.parallel([this.taskName]));
}
get taskName() {
return `js-${this.config.name}`;
}
prepareFiles() {
if (this.config.includeFile) {
this.files = fileInclude.findFiles(this.input, this.includeFile, this.helper);
} else {
this.files = [];
for (const file of this.config.files) {
this.files.push(this.input + "/" + this.helper.replaceVariables(file));
}
}
}
work() {
if (this.config.includeFile) {
this.prepareFiles();
}
let d = gulp.src(this.files).pipe(concat(this.config.name + '.js'))
if (!this.config.onlyMinify) {
d.pipe(gulp.dest(this.path));
}
if (this.config.minify) {
d.pipe(closureCompiler(this.compiler)).pipe(gulp.dest(this.path));
}
return d;
}
}
module.exports = {
build: function () {
let array = [];
for (let worker of workers) {
array.push(worker.taskName);
}
return array;
},
prepare: function (config) {
// we load the json and parse it here
const js = config.js || [];
for (let jsConfig of js) {
const worker = new JSWorker(jsConfig, config.helper);
workers.push(worker);
}
},
startWatch: function () {
for (let worker of workers) {
worker.startWatch();
}
}
}

View File

@ -0,0 +1,36 @@
const fs = require('fs'),
HelperUnit = require('./helperUnit'),
helper = new HelperUnit(),
config = {};
module.exports = {
config: config,
helper: helper,
prepare: function () {
if (!fs.existsSync(__dirname + '/../../buildConfig.json')) {
console.error("Cannot find Config JSON");
process.exit(40);
}
const currentPath = process.cwd();
const baseDir = currentPath + '/../';
const data = JSON.parse(fs.readFileSync(currentPath + '/../buildConfig.json').toString());
const src = data['src'].replace(/(\$dir)/gm, baseDir);
const out = data['out'].replace(/(\$dir)/gm, baseDir);
helper.setConfig({
$dir: baseDir,
$out: out,
$src: src,
$rawDir: __dirname,
$forWatch: data['src'].replace(/(\$dir)/gm, '../')
})
config.dir = baseDir;
config.src = src;
config.out = out;
config.js = data.js || [];
config.scss = data.scss || [];
config.icons = data.icons || [];
this.js = config.js;
this.scss = config.scss;
this.icons = config.icons;
}
}

View File

@ -0,0 +1,24 @@
const fs = require('fs');
function findFiles(dir, file, helper) {
file = helper.replaceVariables(file);
const d = fs.readFileSync(file).toString("UTF8");
const split = d.split("\n");
let files = [];
for (let file of split) {
if (file.startsWith("--") || file.trim() === "" || file.startsWith("#")) {
continue;
}
if (file.startsWith("@import")) {
let iFile = file.split("@import")[1].trim();
files.push(...this.findFiles(dir, `${dir}/${iFile}.path`, helper));
} else {
files.push(`${dir}/${helper.replaceVariables(file)}.js`);
}
}
return files;
}
module.exports = {
findFiles
};

57
build/tools/helperUnit.js Normal file
View File

@ -0,0 +1,57 @@
let inConfig = {
$dir: __dirname,
$out: __dirname,
$src: __dirname,
$: __dirname
}
class HelperUnit {
constructor(config = null) {
this.config = config || inConfig;
this.regEx = {};
this.buildRegex();
}
static setGlobalConfig(config) {
inConfig = config;
}
addConfigItem(name, value, regexOnly = false) {
if (!regexOnly) {
this.config[name] = value;
}
let replace = name.replace("$", "\\$");
this.regEx[name] = new RegExp(`(${replace})`, "gm");
}
setConfig(config) {
this.config = config;
this.buildRegex();
}
buildRegex() {
const keys = Object.keys(this.config);
this.regEx = {};
for (const key of keys) {
this.addConfigItem(key, null, true);
}
}
replaceVariables(string) {
const keys = Object.keys(this.config);
for (const key of keys) {
string = string.replace(this.regEx[key], this.config[key]);
}
return string;
}
replaceAbsolutePath(string) {
string = string.replace(this.config["$dir"], this.config["$forWatch"])
return string;
}
clone() {
return new HelperUnit(this.config);
}
}
module.exports = HelperUnit;

71
build/tools/iconSprite.js Normal file
View File

@ -0,0 +1,71 @@
const path = require('path'),
SVGSpriter = require('svg-sprite'),
fs = require('fs'),
fsExtra = require('node-fs-extra');
let toMergeData = [];
function buildIconSprites(config) {
config.spriter = {
shape: {
id: {
generator: function (name) {
return "vt-" + name.replace('.svg', '');
}
}
},
svg: {
xmlDeclaration: true,
doctypeDeclaration: true
},
mode: {
symbol: true,
defs: true
}
}
builder.init(config);
builder.generateFromIcons();
}
let builder = {
init: function (config) {
this.config = config;
},
generateFromIcons: function () {
const spriter = new SVGSpriter(this.config.spriter);
this.readFiles(this.config.input, spriter);
spriter.compile(function (error, result) {
if (error !== null) {
throw new Error(error);
}
builder.writeData(result.symbol.sprite.contents.toString());
});
},
writeData: function (data) {
let out = builder.config.out + builder.config.config.name + ".svg";
let dirname = path.dirname(out);
if (!fs.existsSync(dirname)) {
fsExtra.mkdirpSync(dirname);
}
fs.writeFileSync(out, data);
},
readFiles: function (dir, spriter) {
let files = fs.readdirSync(dir);
for (let file of files) {
const split = file.split('.');
if (split[split.length - 1] === 'svg') {
let filePath = path.resolve(dir, file);
spriter.add(
filePath,
file,
fs.readFileSync(filePath, {encoding: 'utf-8'})
);
}
}
}
};
module.exports.buildIconSprites = buildIconSprites;

54
buildConfig.json Normal file
View File

@ -0,0 +1,54 @@
{
"src": "$dir/src",
"out": "$dir/public/out",
"js": [
{
"name": "app",
"minify": true,
"onlyMinify": true,
"input": "$src/app/",
"output": "$out/app/",
"watch": "../src/app/**/*.js",
"includeFile": "../app.path",
"compiler": {
"compilation_level": "SIMPLE",
"warning_level": "DEFAULT",
"language_in": "ECMASCRIPT_2018",
"language_out": "ECMASCRIPT_2018",
"js_output_file": "$name.min.js"
}
},
{
"name": "loader",
"minify": true,
"onlyMinify": true,
"input": "$src/app/",
"output": "$out/app/",
"watch": "../src/app/**/*.js",
"includeFile": "../loader.path",
"compiler": {
"compilation_level": "SIMPLE",
"warning_level": "DEFAULT",
"language_in": "ECMASCRIPT_2018",
"language_out": "ECMASCRIPT_2018",
"js_output_file": "$name.min.js"
}
}
],
"scss": [
{
"name": "main",
"input": "$src/theme/**/*.scss",
"output": "$out/theme/$name",
"watch": "../src/theme/**/*.scss"
}
],
"icons": [
{
"name": "icon-sprite",
"input": "$src/icons/",
"output": "$out/",
"watch": "../src/icons/**/*.svg"
}
]
}

BIN
public/favicon.ico Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
public/favicon/favicon-16x16.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 311 B

BIN
public/favicon/favicon-32x32.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 B

BIN
public/favicon/mstile-150x150.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 890 B

View File

@ -0,0 +1 @@
<svg version="1" xmlns="http://www.w3.org/2000/svg" width="893.333" height="893.333" viewBox="0 0 670.000000 670.000000"><path d="M48 256.8c0 .4 10.7 23.9 23.9 52.2 13.1 28.3 25.5 55.1 27.5 59.5l3.7 7.9 5.8-12.7 5.9-12.7L93 303.7l-21.9-47.2-11.5-.3c-6.4-.1-11.6.1-11.6.6zM178.2 262.2c-4.6 10-42.4 91.1-53.8 115.5L114 399.8l5.7 12.4c3.1 6.7 5.8 12.5 5.9 12.7.2.2 1.6-2.3 3.1-5.5 1.4-3.2 18.8-40.6 38.6-82.9 19.7-42.4 36.1-77.8 36.4-78.8.5-1.5-.5-1.7-11.1-1.7H181l-2.8 6.2zM255 257.9c-8.2 2.6-11.8 4.8-18.3 10.9-10.9 10.4-15.2 21.4-14.5 37.1.3 8.6.7 10.5 4.1 17.4 6.5 13.7 18.8 23.5 33.5 26.6 2.9.6 9.5 1.1 14.6 1.1 11.4 0 17.1 1.8 22.6 7.2 5.7 5.4 8 10.8 8 18.8 0 11.1-5.4 19.5-15.5 24.1-3.6 1.7-7.2 1.9-35.7 1.9H222v22h31.3c24.9 0 32.5-.3 37.2-1.5 23.9-6.3 39.7-30 35.7-53.8-2.7-15.8-11.6-27.9-25.6-34.7-9-4.4-16.6-6-29.6-6-9.7 0-14.5-1.7-19.8-7.1-5.8-5.7-7.7-10.4-7.6-18.9 0-9 2.2-14 8.5-19.3 7.4-6.4 9.6-6.7 44.1-6.7H327v-21l-33.2.1c-29.1 0-34 .2-38.8 1.8zM360 266.5V277h113v-21H360v10.5zM495 266.5V277h62.5l5.8-9.9c3.1-5.4 5.7-10.1 5.7-10.5 0-.3-16.6-.6-37-.6h-37v10.5zM575.1 286.3c-9.6 16.6-31.4 54.2-48.3 83.5-16.9 29.4-30.8 53.8-30.8 54.3s24.3.8 60.8.7l60.7-.3v-21l-42.2-.3c-23.3-.1-42.3-.6-42.3-1s.6-1.6 1.4-2.7c.7-1.1 8.7-14.8 17.6-30.5 9-15.7 26.7-46.5 39.5-68.5s23.7-41 24.4-42.3l1.2-2.2h-12.3l-12.3.1-17.4 30.2zM407 361v64h21V297h-21v64z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

36
public/index.html Normal file
View File

@ -0,0 +1,36 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=2.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta name="description" content="WebAudio Player created by VersusTuneZ"/>
<link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon/favicon-16x16.png">
<link rel="mask-icon" href="/favicon/safari-pinned-tab.svg" color="#5bbad5">
<meta name="msapplication-TileColor" content="#3949ab">
<meta name="theme-color" content="#3949ab">
<title>Template</title>
<link rel="stylesheet" href="out/theme/main/main.css">
</head>
<body>
<v-loading-screen>
<v-loading-text>Loading</v-loading-text>
<v-loader></v-loader>
<v-loading-stats>0%</v-loading-stats>
</v-loading-screen>
<v-app>
<header id="main-header">
<span class="logo">Template</span>
</header>
<v-content><!-- RENDER HERE!--></v-content>
<v-modal-container>
</v-modal-container>
<v-notification></v-notification>
<v-toast></v-toast>
</v-app>
<script src="out/app/loader.min.js"></script>
</body>
</html>

10
src/app.path Normal file
View File

@ -0,0 +1,10 @@
@import External/external
Core/Template
Core/CustomElements
Core/Config
Core/Notification
Core/ModalController
Core/EventHandler
Core/Registry
app

44
src/app/Core/Config.js Normal file
View File

@ -0,0 +1,44 @@
(() => {
class Config {
constructor() {
this._config = {};
this.updateCB = {};
}
get data() {
return this._config;
}
get(key, def = null) {
if (this._config[key] !== undefined) {
return this._config[key];
}
this.set(key, def);
return def;
}
getFloat(key, def = 0) {
return parseFloat(this.get(key, def));
}
getInt(key, def) {
return parseInt(this.get(key, def));
}
set(key, value) {
this._config[key] = value;
if (this.updateCB[key]) {
this.updateCB[key](value);
}
}
onUpdate(s, cb) {
this.updateCB[s] = cb;
}
}
moduleLoader.isModuleLoaded(['Registry'], () => {
window.config = new Config();
registry.set('config', window.config);
});
})();

View File

@ -0,0 +1,257 @@
(() => {
class NavItem extends HTMLElement {
constructor() {
super()
if (!this.hasAttribute('tabindex')) {
this.setAttribute('tabindex', '0');
}
}
}
class VInput extends HTMLElement {
constructor() {
super();
let self = this;
self.id = self.id || VUtils.tempId();
let val = self.innerHTML;
self.innerHTML = '';
let input = self.input = self.createNew('input', {id: self.id})
let label = self.createNew('label', {content: self.dataset.label});
self.createNew('span', {classes: 'error', content: self.dataset.error});
label.setAttribute('for', self.id);
input.type = self.getAttribute('type') || 'text';
input.value = val.trim();
input.required = self.hasAttribute('required');
input.name = self.getAttribute('name');
input.addMultiListener('change input', self.cb.bind(self));
}
get value() {
return this.input.value;
}
connectedCallback() {
this.cb({currentTarget: this.input}, true);
}
cb(e, noInvalid) {
let el = e.currentTarget
let errorMessage = $('.error-message', el.find('form'));
if (errorMessage) {
errorMessage.classList.add('hide')
}
let cl = this.classList;
if (el.value === "") {
cl.remove('focus')
} else {
cl.add('focus')
}
if (el.checkValidity()) {
cl.add('valid');
cl.remove('invalid');
} else {
if (!noInvalid) {
cl.remove('valid');
cl.add('invalid');
}
}
}
}
class VSwitch extends HTMLElement {
constructor() {
super();
const id = this.dataset.id || VUtils.tempId();
$('input', this).id = id;
$('label', this).setAttribute('for', id);
}
}
class VSlider extends HTMLElement {
constructor() {
super();
this.input = $('input', this);
this.input.id = this.dataset.id || VUtils.tempId();
this.input.addEventListener('input', this.changeValue.bind(this));
this.cur = $('.current', this);
this.cur.addEventListener('input', this.onChange.bind(this));
this.cur.addEventListener('keypress', this.onKey.bind(this));
this.cur.addEventListener('focusout', this.onChange.bind(this));
this.checkStepSize();
this.minLabel = $('.min', this);
this.maxLabel = $('.max', this);
}
checkStepSize() {
const stepSize = this.input.getAttribute('step');
const max = parseFloat(this.input.getAttribute('max'));
const min = parseFloat(this.input.getAttribute('min'));
if (stepSize === "" && max - min <= 1) {
this.input.setAttribute('step', "0.05");
}
}
set value(value) {
this.input.value = value;
this.changeValue();
this.dispatchEvent(new Event('input'));
}
get value() {
return this.input.value;
}
set max(value) {
this.input.max = value;
this.maxLabel.innerHTML = value;
}
get max() {
return this.input.max;
}
set min(value) {
this.input.min = value;
this.minLabel.innerHTML = value;
}
get min() {
return this.input.min;
}
onKey(evt) {
const code = evt.keyCode;
if (code === 44 || code === 46) return;
if (code < 48 || code > 57 || code === 13) evt.preventDefault();
}
changeValue() {
this.cur.innerText = this.input.value;
}
onChange(e) {
if (e.type === 'focusout') {
this.cur.innerText = this.input.value;
return;
}
try {
const text = this.cur.innerText.trim();
if (text === '') {
this.cur.innerText = 0;
}
let val = parseFloat(text.replace(",", "."));
let min = parseFloat(this.input.getAttribute('min'));
let max = parseFloat(this.input.getAttribute('max'));
if (val < min) val = min;
if (val > max) val = max;
if (isNaN(val)) {
val = this.input.value;
}
let step = this.input.step || 1;
if (Math.floor(step) === step) {
val = Math.round(val);
}
this.input.value = val;
} catch (err) {
this.cur.innerText = this.input.value;
PrettyConsole.error(VSlider, err);
}
}
}
class VColor extends HTMLElement {
constructor() {
super();
this.input = $('input', this);
this.blob = $('.colorBlob', this);
this.addEventListener('input', this.onChange.bind(this));
}
onChange() {
requestAnimationFrame(() => {
this.blob.style.backgroundColor = this.input.value;
});
}
set value(value) {
this.input.value = value;
this.onChange();
this.dispatchEvent(new Event('input'));
}
get value() {
return this.input.value;
}
}
class VCollapseHead extends HTMLElement {
constructor() {
super();
if (!$('.btn-ripple', this)) {
this.createNew('div', {
classes: ['ripple']
});
}
}
}
class GUIItem extends HTMLElement {
constructor() {
super();
}
connectedCallback() {
requestAnimationFrame(() => {
this.update();
})
}
update() {
let bindItem = $('[data-bind]', this);
if (bindItem && bindItem.dataset.bind && bindItem.dataset.bind.trim() !== '') {
let bind = bindItem.dataset.bind;
if (bindItem.nodeName === 'INPUT' && bindItem.type === 'checkbox') {
bindItem.checked = config.get(bind, bindItem.checked);
} else if (bindItem.nodeName === 'V-SELECT') {
this.initVSelect(bindItem, bind);
} else {
bindItem.value = config.get(bind, bindItem.value);
}
if (!bindItem._hasEvent) {
bindItem.addEventListener('input', () => {
if (bindItem.nodeName === 'INPUT' && bindItem.type === 'checkbox') {
config.set(bind, bindItem.checked);
} else if (bindItem.nodeName === 'V-SELECT') {
config.set(bind, bindItem.rawValue);
} else {
config.set(bind, bindItem.value);
}
bindItem._hasEvent = true;
});
}
}
}
initVSelect(bindItem, bind) {
let value = config.get(bind, bindItem.rawValue).toString().split(", ");
bindItem.options.forEach(item => {
if (value.indexOf(item.value) === -1) {
item.removeAttribute('selected');
} else {
item.setAttribute('selected', value.indexOf(item.value) !== -1);
}
})
}
}
customElements.define("v-input", VInput);
customElements.define("v-switch", VSwitch);
customElements.define("v-slider", VSlider);
customElements.define("v-color", VColor);
customElements.define("nav-item", NavItem);
customElements.define("v-gui-item", GUIItem);
customElements.define("v-collapse-head", VCollapseHead);
})();

View File

@ -0,0 +1,52 @@
(() => {
class EventHandler {
constructor() {
this.events = {};
document.body.addDelegatedEventListener('click', '[data-event]', this.handleClickEvent.bind(this))
}
handleClickEvent(ev, el) {
this.send(el.dataset.event, el);
}
addEvent(events, cb) {
let names = events.split(",");
for (let name of names) {
this.events[name.trim()] = cb;
}
}
addEvents(obj) {
let keys = Object.keys(obj);
for (let key of keys) {
this.addEvent(key, obj[key]);
}
}
handleEvent(event) {
let data = event.data;
if (!data.cmd) {
return false;
}
return this.send(data.cmd, data.data, data.status);
}
// will send the request to internal events
send(name, data, status = 'success') {
if (this.events[name]) {
try {
this.events[name](data, status);
} catch (e) {
PrettyConsole.error(EventHandler, e.message);
}
return true;
}
return false;
}
}
moduleLoader.registerInit(i => {
window.eventHandler = new EventHandler();
moduleLoader.finishModule('EventHandler');
});
})();

13
src/app/Core/Math.js Normal file
View File

@ -0,0 +1,13 @@
class VMath {
static lerp(a, b, f) {
return (a + (b - a) * f);
}
static clamp(value, min, max) {
return value < min ? min : value > max ? max : value;
}
static easeIn(a) {
return a * a * a;
}
}

View File

@ -0,0 +1,96 @@
class ModalController {
constructor() {
const self = this;
self.modalContainer = $('v-modal-container');
self.observer = new MutationObserver(function () {
if (self.modalContainer.children.length > 0) {
self.modalContainer.classList.add('open');
} else {
self.modalContainer.classList.remove('open');
}
});
const config = {attributes: false, childList: true, characterData: false};
self.observer.observe(self.modalContainer, config);
}
static create(header, content, closeButton = true) {
return registry.get('modal').createModal(header, content, closeButton);
}
createModal(header, content, closeButton) {
return template.render('gui/modal', {
header: header,
content: content,
closeButton: closeButton ? '' : 'close'
}).then(node => {
this.modalContainer.appendChild(node);
if (closeButton) {
node.addDelegatedEventListener('click', '.close-modal', () => {
console.log("remove");
node.remove();
})
}
return node;
});
}
}
class ConfirmDialog {
constructor() {
const self = this;
this.dissmiss = new Promise((resolve, reject) => {
self.resolve = resolve;
self.reject = reject;
});
this.id = VUtils.tempId();
}
static async create(header, text, options) {
const dialog = new ConfirmDialog();
const data = dialog.getContent(text, options);
const content = await template.renderOn('gui/dialog-confirm', data);
const modal = await ModalController.create(header, content, false);
modal.addDelegatedEventListener('click', '.modal-confirm-button', (ev, el) => {
dialog.close(el.dataset.value);
});
dialog.modal = modal;
return dialog.onDismiss();
}
static async createBasic(header, text) {
return this.create(header, text, [
{
type: 'error',
value: "0",
content: 'Nein',
}, {
type: 'primary',
value: "1",
content: 'Ja',
}
]);
}
getContent(text, options) {
return {
text: text,
options: options
}
}
close(value) {
this.modal.remove();
this.resolve(value);
}
onDismiss() {
return this.dissmiss;
}
}
(() => {
moduleLoader.isModuleLoaded(['Registry'], () => {
const modal = new ModalController();
registry.set('modal', modal);
})
})();

25
src/app/Core/Network.js Normal file
View File

@ -0,0 +1,25 @@
class Network {
static getStaticUrl(path) {
if (!path.startsWith('/')) {
path = '/' + path;
}
return path;
}
static async requestUrl(path) {
let url = this.getStaticUrl(path);
let raw = await fetch(url)
if (!raw.ok) {
return Promise.reject(`Failed to load: ${url}`);
}
return await raw.text();
}
static loadAll(array, richUrl) {
const promises = []
for (const name of array) {
promises.push(this.requestUrl(richUrl + name));
}
return Promise.allSettled(promises);
}
}

View File

@ -0,0 +1,87 @@
class NotificationHandler {
constructor() {
this.outer = $('v-notification');
this.toast = $('v-toast');
this.notifications = [];
}
static get instance() {
if (NotificationHandler._instance === undefined) {
NotificationHandler._instance = new NotificationHandler();
}
return NotificationHandler._instance;
}
static createNotification(message, type, time, toast = false) {
time = parseInt(time || "3000");
let handler = NotificationHandler.instance,
notification = new NotificationItem(message, type, time, toast);
handler.notifications.push(notification);
notification.show();
return notification;
}
static createToast(message, type = 'info') {
let handler = NotificationHandler.instance,
notification = new NotificationItem(message, type, 2000, true);
handler.notifications.push(notification);
notification.show();
return notification;
}
}
class NotificationItem {
constructor(message, type, time, toast = false) {
this.outer = NotificationHandler.instance.outer;
this.toastCon = NotificationHandler.instance.toast;
this.message = message;
this.type = type;
this.time = time || 1000;
this.isRemoved = false;
this.toast = toast;
}
async show() {
let self = this,
endless = self.time === -1;
self.updateContent(self.message);
if (!endless) {
setTimeout(this.remove.bind(this), self.time)
}
}
async remove() {
if (this.isRemoved) {
return;
}
this.isRemoved = true;
const cont = this.toast ? this.toastCon : this.outer;
cont.removeChild($('#' + this.id));
let not = NotificationHandler.instance.notifications,
index = not.indexOf(this);
not.splice(index, 1);
}
updateContent(message) {
this.id = VUtils.tempId();
let self = this,
isEndless = self.time === -1,
data = {
message: message,
type: self.type,
id: self.id
};
if (isEndless && !self.toast) data.type += ' endless';
const tpl = self.toast ? 'gui/toast' : 'gui/notification';
const cont = self.toast ? self.toastCon : self.outer;
template.renderOn(tpl, data).then(html => {
cont.prepend(document.createRange().createContextualFragment(html));
if (!self.toast) $(`#${self.id} .fade-bar`).style.animationDuration = isEndless ? '1000ms' : `${self.time + 1}ms`;
});
}
updateMessageOnly(message) {
let item = $(`#${this.id} .message`);
if (item) item.innerHTML = message;
}
}

18
src/app/Core/PreLoader.js Normal file
View File

@ -0,0 +1,18 @@
class PreLoader {
constructor() {
this.preloadData = {};
moduleLoader.registerModule(PreLoader);
Network.requestUrl('/out/preload-list.json').then(data => {
this.preloadData = JSON.parse(data);
PrettyConsole.debug(PreLoader, this.preloadData)
moduleLoader.finishModule(PreLoader)
}).catch(err => {
moduleLoader.moduleErrored(PreLoader, err);
PrettyConsole.error(PreLoader, err);
})
}
getData(name) {
return this.preloadData[name] || [];
}
}

View File

@ -0,0 +1,112 @@
class PrettyConsole {
static setColour(module, color) {
module = this.getModuleNameFromArgs([module]);
if (!this.colours) {
this.colours = {};
}
this.colours[module] = color;
}
static getColour(module) {
if (!this.colours) {
this.colours = {};
}
if (!this.colours.hasOwnProperty(module)) {
this.colours[module] = this.rgbToHex(...this.hexToRgb(this.generateHexFromString(module)));
}
return this.colours[module];
}
static log(...args) {
const module = this.getModuleNameFromArgs(args);
if (args.length === 1 && !Array.isArray(args[0])) {
this.output(module, "#5c6bc0", args[0], 'log');
} else {
if (args.length === 1) args = args[0];
this.outputGroup(module, "#5c6bc0", args, 'log');
}
}
static error(...args) {
const module = this.getModuleNameFromArgs(args);
if (args.length === 1 && !Array.isArray(args[0])) {
this.output(module, "#c62828", args[0], 'error');
} else {
if (args.length === 1) args = args[0];
this.outputGroup(module, "#c62828", args, 'error');
}
}
static warn(...args) {
const module = this.getModuleNameFromArgs(args);
if (args.length === 1 && !Array.isArray(args[0])) {
this.output(module, "#f9a825", args[0], 'warn');
} else {
if (args.length === 1) args = args[0];
this.outputGroup(module, "#f9a825", args, 'warn');
}
}
static debug(...args) {
const module = this.getModuleNameFromArgs(args);
if (args.length > 1) {
this.outputGroup(module, '#e91e63', args, 'debug');
} else {
this.output(module, '#e91e63', args[0], 'debug');
}
}
static output(module, style, message, type) {
const color = this.getColour(module);
if (typeof message === "object") {
console[type](`%c[${module}]`, `color:${color};font-weight: bold;`, message);
return;
}
console[type](`%c[${module}] %c ${message}`, `color:${color};font-weight: bold;`, `color: ${style}`);
}
static outputGroup(module, style, messages, type) {
const color = this.getColour(module);
console.group(`%c===[${module}]===`, `color:${color};font-weight: bold;`);
for (let message of messages) {
if (type === 'debug' && typeof message === 'string') {
message = `%c${message}`;
console.debug(message, `color: ${style}`);
} else {
console[type](message);
}
}
console.groupEnd();
}
static generateHexFromString(string = "") {
let hash = 0, len = string.length;
for (let i = 0; i < len; i++) {
string.charCodeAt(i)
hash = ((hash << 5) - hash) + string.charCodeAt(i);
hash |= 0; // to 32bit integer
}
return Math.abs(hash).toString(16).slice(0, 6);
}
static hexToRgb(hex) {
let bigint = parseInt(hex, 16);
let r = (bigint >> 16) & 255;
let g = (bigint >> 8) & 255;
let b = bigint & 255;
return [r, g, b];
}
static rgbToHex(r, g, b) {
return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
}
static getModuleNameFromArgs(args) {
if (typeof args[0] === 'function') {
const x = args.shift();
return x.name;
}
return 'App';
}
}
PrettyConsole.setColour("APP", '#90ff20');

20
src/app/Core/Registry.js Normal file
View File

@ -0,0 +1,20 @@
(() => {
class Registry {
constructor() {
this.items = {};
}
get(name) {
return this.items[name];
}
set(name, item) {
this.items[name] = item;
}
}
moduleLoader.registerInit(i => {
window.registry = new Registry();
moduleLoader.finishModule('Registry');
})
})();

33
src/app/Core/Template.js Normal file
View File

@ -0,0 +1,33 @@
const TemplateModule = "Template";
(() => {
class Template {
constructor() {
moduleLoader.registerModule(TemplateModule);
this.onInit();
}
onInit() {
PrettyConsole.log(TemplateModule, "Initialize...")
window.template = new VTepLCore({
path: "/out/tpl/",
cache: false // we want never caching :) i mean it's nice but horrible to update // maybe adding later checksum
});
PrettyConsole.debug(TemplateModule, "Use Cache? " + template.cache)
// init requests
this.loadArray(preLoader.getData('tpl'), true).catch(console.error);
}
async loadArray(files, moduleLoading = false) {
window.template.loadArray(files).then(() => {
if (moduleLoading) moduleLoader.finishModule(TemplateModule);
}).catch(err => {
PrettyConsole.error(TemplateModule, err);
if (moduleLoading) moduleLoader.moduleErrored(TemplateModule, err);
});
}
}
moduleLoader.registerInit(() => {
new Template();
})
})();

210
src/app/External/SelectJs.js vendored Normal file
View File

@ -0,0 +1,210 @@
(function () {
window._openVSelect = null;
requestAnimationFrame(e => {
document.body.addEventListener('click', ev => {
if (window._openVSelect && ev.target.closest('v-select') !== window._openVSelect) {
window._openVSelect.toggle(false);
}
})
})
class VSelectElement extends HTMLElement {
constructor() {
super();
let self = this;
self.rawValue = "";
self.setAttribute('tabindex', '0');
requestAnimationFrame(() => {
self.update();
})
}
get value() {
return this._value;
}
set value(value) {
this._value = value;
this.dispatchEvent(new Event('input'));
}
get required() {
return this.hasAttribute('required');
}
set required(flag) {
this.toggleAttribute('required', Boolean(flag));
}
get name() {
return this.getAttribute('name');
}
set name(val) {
this.toggleAttribute('name', val);
}
get form() {
return this.closest('form');
}
get options() {
return $$('v-options v-option', this);
}
get selected() {
return $$('v-options v-option[selected]', this);
}
connectedCallback() {
/* const selected = this.selected;
if (selected) return;*/
const options = Array.prototype.slice.call(this.options);
const values = this.dataset.value ? this.dataset.value.split(",") : [""];
if (values.length === 1 && values[0].trim() === '') {
return;
}
options
.filter(item => values.indexOf(item.getAttribute('value')) !== -1)
.forEach(item => item.setAttribute('selected', 'true'));
}
update() {
let selected = [],
lbl = $('v-label', this),
fd = new FormData();
this.selected.forEach(e => {
selected.push(e.innerText);
fd.append(this.name, e.value);
})
if (lbl.attributeChangedCallback) {
lbl.attributeChangedCallback('value', '', selected.join(", "));
}
this.isValid = !(this.required && selected.length === 0);
this.rawValue = selected.join(", ");
this.value = fd;
}
checkValidity() {
return this.isValid;
}
toggle(open) {
if (window._openVSelect && window._openVSelect.toggleSelect && open) {
window._openVSelect.toggleSelect(false);
}
const options = $('v-options', this);
if (!open || this.isOpen) {
options.style.maxHeight = '0';
window._openVSelect = false;
this.isOpen = false;
this.update();
} else {
options.focus();
let height = 0,
children = options.children;
for (let i = 0; i < children.length; i++) {
height += children[i].offsetHeight;
}
options.style.maxHeight = height + 'px';
window._openVSelect = this;
this.isOpen = true;
}
let l = $('v-label', this).classList;
if (this.isOpen) {
l.add('open');
} else {
l.remove('open');
}
}
}
class VSelectOptionElement extends HTMLElement {
constructor() {
super();
this._value = this.getAttribute('value');
this.addEventListener('click', e => {
let parent = this.parentNode.parentNode,
select = !this.selected;
if (!parent.hasAttribute('multiple')) {
parent.toggle(false);
for (let item of parent.selected) {
if (item !== this) {
item.removeAttribute('selected');
}
}
}
if (!this.disabled) {
this.attributeChangedCallback('selected', false, select, true);
this.parentNode.parentNode.update();
}
});
this.createNew('div', {classes: ['ripple']});
}
static get observedAttributes() {
return ['selected', 'disabled', 'value'];
}
get value() {
return this._value;
}
set value(value) {
this._value = value;
}
attributeChangedCallback(name, oldValue, newValue, force) {
if (name === 'selected' && this.hasAttribute('disabled')) {
this.removeAttribute(name);
return;
}
if (name === 'disabled' && newValue === true && this.hasAttribute('selected')) {
this.attributeChangedCallback('selected', false, false);
}
if (force) {
if (newValue) {
this.setAttribute(name, newValue);
} else {
this.removeAttribute(name);
}
}
this[name] = newValue;
}
}
class VLabel extends HTMLElement {
constructor() {
super();
this.span = this.createNew('span');
this.createNew('div', {classes: ['ripple']});
this.empty = this.getAttribute('empty') || "";
this.span.innerHTML = this.getAttribute("value") || this.empty;
this.addEventListener('click', this.openPopUp.bind(this));
}
static get observedAttributes() {
return ['empty', 'value'];
}
openPopUp() {
this.parentNode.toggle(true);
}
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'value') {
this.span.innerHTML = newValue || this.empty;
}
this[name] = newValue;
}
}
customElements.define("v-label", VLabel);
customElements.define("v-option", VSelectOptionElement);
customElements.define("v-select", VSelectElement);
})();

72
src/app/External/VCollapse.js vendored Normal file
View File

@ -0,0 +1,72 @@
(() => {
const defaultConfig = "{\"outer\":\"v-collapse\",\"listenTo\":\"body\",\"itemSelector\":\"v-collapse-item\",\"headSelector\":\"v-collapse-head\",\"contentSelector\":\"v-collapse-content\"}";
window.VCollapse = class VCollapse {
constructor(options = {}) {
this.options = Object.assign(options, JSON.parse(defaultConfig));
this.initListener();
}
initListener() {
const self = this,
opt = self.options;
$(opt.listenTo).addDelegatedEventListener('click', opt.headSelector, (e, el) => {
self.toggleItem(el, false);
});
this.resize();
window.addEventListener('resize', this.resize.bind(this));
}
toggleItem(el, forceClose, noClose) {
if (!el._maxHeight) {
this.resize();
}
if (!noClose && el.find(this.options.outer).hasAttribute('accordion')) {
this.closeAll(el);
}
let parent = el.find(this.options.itemSelector),
cont = $(this.options.contentSelector, parent),
cl = parent.classList;
if (cl.contains('open') || forceClose) {
cont.style.maxHeight = '0px';
cl.remove('open');
} else {
cont.style.maxHeight = parent._maxHeight + 'px';
cl.add('open');
}
}
closeAll(el) {
const self = this,
opt = self.options,
items = $$(opt.headSelector, el.find(opt.outer));
VUtils.forEach(items, e => {
if (e !== el) {
self.toggleItem(e, true, true);
}
})
}
calcHeight(el) {
let children = $(this.options.contentSelector, el).children,
height = 0;
VUtils.forEach(children, e => {
height += e.offsetHeight;
})
el._maxHeight = height;
}
resize() {
const self = this,
opt = self.options,
items = $$(opt.itemSelector, $(opt.listenTo));
VUtils.forEach(items, e => {
self.calcHeight(e);
if (e.classList.contains('open')) {
$(opt.contentSelector, e).style.maxHeight = parent._maxHeight + 'px';
}
})
}
}
new VCollapse();
})();

77
src/app/External/VRipple.js vendored Normal file
View File

@ -0,0 +1,77 @@
class VRipple {
constructor(options = {}) {
if (!VUtils.isUsed) {
throw "VRipply is only with Public VUtils usable!"
}
let self = this;
self.options = JSON.parse('{"classes":["ripple__effect"],"target":"body","selector":".ripple"}');
VUtils.mergeOptions(self.options, options);
if (self.options.selector.indexOf("#") > -1) {
throw "ID's are not allowed as selector!";
}
this.instanceCheck();
this.ripples = [];
requestAnimationFrame(this.initHandler.bind(this));
}
instanceCheck() {
let opts = this.options;
const rawKey = [opts.target, opts.selector, opts.classes.join(".")].join(" ");
VRipple.instances = VRipple.instances || {};
VRipple.instances[rawKey] = this;
}
initHandler() {
let self = this;
let selector = self.options.selector;
let target = $(self.options.target);
target.addDelegatedEventListener('mousedown touchstart', selector, (e, el) => {
let pos = e.touches ? e.touches[0] : e;
let parent = el.parentNode;
let circle = el.createNew('span', self.options);
let bounds = parent.getBoundingClientRect();
let x = pos.clientX - bounds.left;
let y = pos.clientY - bounds.top;
circle.style.top = y + 'px';
circle.style.left = x + 'px';
circle._mouseDown = true;
circle._animationEnded = false;
self.ripples.push(circle);
});
document.body.addDelegatedEventListener('animationend', '.' + VUtils.get(self.options.classes, ''), self.rippleEnd.bind(self))
if (!document.body._vRippleInit) {
document.body.addMultiListener('mouseup touchend mouseleave rippleClose', e => {
let keys = Object.keys(VRipple.instances);
for (let key of keys) {
for (let ripple of VRipple.instances[key].ripples) {
requestAnimationFrame(x => {
self.rippleEnd.bind(VRipple.instances[key])(e, ripple);
});
}
}
})
document.body._vRippleInit = true;
}
}
rippleEnd(ev, el) {
const parent = el.parentNode;
if (parent) {
if (ev.type === 'animationend') {
el._animationEnded = true;
} else {
el._mouseDown = false;
}
if (!el._mouseDown && el._animationEnded) {
if (el.classList.contains('to-remove')) {
el.parentNode.removeChild(el);
this.ripples.splice(this.ripples.indexOf(el), 1)
} else {
el.classList.add('to-remove');
}
}
}
}
}
const rippler = new VRipple();

70
src/app/External/VTepL/Core.js vendored Normal file
View File

@ -0,0 +1,70 @@
class VTepLCore {
constructor({path, suffix, template, cache} = {}) {
this.templates = {};
this.dir = path || '/tpl/';
this.suffix = suffix || 'tpl';
this.path = template || `${this.dir}%s.${this.suffix}`;
this.cache = cache === undefined ? true : cache;
}
// Add download queue!
async loadTemplate(name) {
if (this.templates[name]) {
return null;
}
this.templates[name] = true; // small hack to prevent double loading :)
let path = this.path.replace('%s', name);
let data = await Network.requestUrl(path)
this.addTpl(name, data);
loader.addLoadedItems(1);
return null;
}
async loadArray(names) {
loader.addToLoadItem(names.length)
const promises = []
for (const name of names) {
promises.push(this.loadTemplate(name));
}
await Promise.all(promises)
}
addTpl(name, content) {
let temp = this.templates[name] = new VTepLTemplate(name, content, this);
temp.parseContent(this.cache);
}
async renderOn(name, data) {
if (this.templates[name]) {
return await this.templates[name].render(data);
}
PrettyConsole.warn("VTepLRender", `Template: "${name}" dont exist`);
return '';
}
renderTo(element, name, data = {}) {
this.renderOn(name, data).then(html => {
element.innerHTML = html;
})
}
// gets a html node element
render(name, data = {}) {
return this.renderOn(name, data).then(html => {
const template = document.createElement('template');
template.innerHTML = html.trim();
return template.content.firstChild;
})
}
async renderToAsync(element, name, data = {}) {
return this.renderOn(name, data).then(html => {
element.innerHTML = html;
})
}
has(toRender) {
return this.templates[toRender]
}
}

205
src/app/External/VTepL/Interpreter.js vendored Normal file
View File

@ -0,0 +1,205 @@
class VTepLInterpreter {
constructor(parser, core) {
this.parser = parser;
this.data = [];
this.content = '';
this.core = core;
}
async render(data) {
let self = this;
self.data = data;
let newData = await self.interpreter(self.parser.parsed);
self.data = [];
return newData[0];
}
// This stuff is slow as fuck xD Optimize parser to create faster interpreter
async interpreter(parsed, index = 0) {
let self = this;
let types = VParserTypes;
let tplCont = '';
for (let i = index; i < parsed.length; i++) {
let item = parsed[i],
content = item.content;
switch (item.type) {
case types.content:
tplCont += content;
break;
case types.variable:
tplCont += self.getVariable(content)
break;
case types.assign:
let data = content.split("="),
key = data.shift();
self.setVariable(data.join("=").trim().replace(/"/g, ""), key.trim());
break;
case types.forEach:
let d = await this.handleForEach(item, parsed, i);
i = d[0];
tplCont += d[1];
break;
case types.for:
let fd = await this.handleFor(item, parsed, i);
i = fd[0];
tplCont += fd[1];
break;
case types.if:
let id = await this.handleIf(item, parsed, i);
i = id[0];
tplCont += id[1];
break;
case types.include:
tplCont += await this.handleInclude(item);
break;
case types.ifEnd:
tplCont += content;
return [tplCont, i];
case types.forEnd:
tplCont += content;
return [tplCont, i];
default:
console.warn("Invalid Type found");
break;
}
}
return [tplCont, parsed.length];
}
getVariable(variable, isEqualCheck = false) {
variable = variable.toString();
if (!isEqualCheck) {
let v = variable.split("==");
if (v.length > 1) {
return this.getEqual(v[0].trim(), v[1].trim());
}
v = variable.split("?");
if (v.length > 1) {
return this.getIsDefined(v[0].trim(), v[1].trim());
}
v = variable.split("||");
if (v.length > 1) {
return this.getDefault(v[0].trim(), v[1].trim());
}
}
if (this.data[variable]) {
return this.data[variable];
}
let split = variable.split("."),
prevVar = this.data;
for (let i = 0; i < split.length; i++) {
prevVar = prevVar[split[i]] || prevVar;
}
if (typeof prevVar === 'string') {
return prevVar;
}
if (typeof prevVar === 'number') {
return prevVar.toString();
}
return '';
}
getEqual(variable1, variable2) {
let split = variable2.split("?");
let var1 = this.getVariable(variable1, true);
let var2 = this.getVariable(split[0].trim(), true);
if (split.length > 1) {
let newSplit = split[1].split(":");
let right = newSplit[1] || '';
return var1 === var2 ? newSplit[0].trim() : right.trim();
}
return var1 === var2 ? 'true' : 'false';
}
setVariable(value, variable) {
let c = this.getVariable(value);
if (c !== '') {
value = c;
}
this.data[variable] = value;
}
async handleForEach(item, parsed, i) {
let content = item.content.split(" as ");
let root = this.getVariable(content[0].trim());
let addTo = 0,
isInvalid = false;
if (root === '') {
isInvalid = true;
root = {invalid: "true"};
}
if (typeof root === 'string') {
root = JSON.parse(root);
}
if (Array.isArray(root) && root.length === 0) {
root.push("");
isInvalid = true;
}
let d = Object.keys(root),
raw = '',
varContent = content[1].trim().split(",");
for (let x of d) {
if (varContent.length === 2) {
this.setVariable(x, varContent[1]);
}
this.setVariable(root[x], varContent[0]);
let data = await this.interpreter(parsed, i + 1);
addTo = data[1];
raw += data[0];
}
if (isInvalid) {
raw = '';
}
return [addTo, raw];
}
async handleInclude(item) {
let split = item.content.split(";"),
name = split.shift(),
data = {};
await this.core.loadTemplate(name);
for (let item of split) {
let d = item.split("="),
index = d.shift(),
dat = d.join("=");
if (!dat.startsWith("\"")) {
dat = this.getVariable(dat);
} else {
dat = dat.replace(/"/g, "");
}
data[index] = dat;
}
return await this.core.renderOn(name, data);
}
async handleFor(item, parsed, ind) {
let content = item.content.split(" as "),
addTo = 0,
count = content[0].trim().split(".."),
max = parseInt(count[1]),
min = parseInt(count[0]),
newContent = '';
for (let i = min; i < max; i++) {
this.setVariable(i.toString(), content[1]);
let data = await this.interpreter(parsed, ind + 1);
addTo = data[1];
newContent += data[0];
}
return [addTo, newContent];
}
async handleIf(item, parsed, i) {
let data = await this.interpreter(parsed, i + 1);
return [data[1], data[0]];
}
getIsDefined(variable, value) {
return this.getVariable(variable, true) !== '' ? value : '';
}
getDefault(variable, value) {
let vars = this.getVariable(variable, true);
return vars !== '' ? vars : value;
}
}

208
src/app/External/VTepL/Parser.js vendored Normal file
View File

@ -0,0 +1,208 @@
const VParserTypes = {
content: 0,
variable: 1,
for: 2,
forEach: 3,
forContent: 4,
forEnd: 5,
if: 6,
ifContent: 7,
ifEnd: 8,
assign: 9,
include: 10,
none: -1,
};
class VTpeLParser {
constructor(name, content) {
let self = this;
self.name = name;
self.legex = content.trim();
self.index = 0;
self.content = '';
self.parsed = [];
self.contexts = [0];
self.line = 1;
self.charPos = 1;
}
tokenize() {
let self = this;
for (self.index = 0; self.index < self.legex.length; self.index++) {
let i = self.index,
char = self.legex.charAt(i);
self.charPos++;
if (char === "\n") {
self.line++;
self.charPos = 0;
}
if (self.nextContains('/* ', i, true)) {
self.extract(' */', VParserTypes.none)
} else if (self.nextContains('// ', i, true)) {
self.extract("\n", VParserTypes.none);
} else if (self.nextContains('<!--', i, true)) {
self.extract('-->', VParserTypes.none);
} else if (self.nextContains('{for(', i, true)) {
self.extract(')}', VParserTypes.for);
self.contexts.push(VParserTypes.for);
} else if (self.nextContains('{include(', i, true)) {
self.extract(')}', VParserTypes.include);
self.contexts.push(VParserTypes.include);
} else if (self.nextContains('{foreach(', i, true)) {
self.extract(')}', VParserTypes.forEach);
self.contexts.push(VParserTypes.forEach);
} else if (self.nextContains('{/for}', i, true)) {
self.addType(VParserTypes.forEnd);
self.contexts.pop();
} else if (self.nextContains('{if(', i, true)) {
self.extract(')}', VParserTypes.if);
self.contexts.push(VParserTypes.if);
} else if (self.nextContains('{/if}', i, true)) {
self.addType(VParserTypes.ifEnd);
self.contexts.pop();
} else if (self.nextContains('$${', i, true)) {
self.extract('}', VParserTypes.assign);
} else if (self.nextContains('${', i, true)) {
self.extract('}', VParserTypes.variable);
} else {
self.content += char;
}
}
self.addType(VParserTypes.content);
return self.parsed;
}
addType(type) {
let self = this;
let content = self.content.replace(/^\n+|\n+$/g, ''),
instructions = self.findInstructions(type);
self.content = '';
if (type !== VParserTypes.none) {
if (type === VParserTypes.content && content === '') {
return null;
}
return self.parsed.push({
content: content,
type: type,
context: self.contexts[self.contexts.length - 1],
instructions: instructions
});
}
return null;
}
nextContains(find, index, add = false) {
let count = this.nextContainsRaw(this.legex, find, index);
if (add && count > 0) {
this.index += count;
}
return count > 0 || count === -1;
}
nextContainsRaw(raw, find, index) {
if (typeof find === "string") {
find = find.split("");
}
let count = find.length;
if (count < 1) {
return -1;
}
for (let i = 0; i < count; i++) {
let nc = raw.charAt(index + i);
if ((find[i] === '\n' && nc === undefined)) {
return count;
}
if (find[i] !== nc) {
return 0;
}
}
return count;
}
extract(findUntil = '}', type = 1) {
let self = this;
self.addType(0);
findUntil = findUntil.split("")
let content = '',
index = self.index,
legex = self.legex,
firstFind = findUntil.shift();
for (let i = self.index; i < legex.length; i++) {
let char = legex.charAt(i);
if (char === firstFind && self.nextContains(findUntil, i + 1)) {
// PrettyConsole.debug("Parser", `[${index} > ${i}] >> ${content}`);
// console.debug(`[Parser][${index} > ${i}] >> ${content}`);
self.index = i + findUntil.length;
self.content = content.trim();
self.addType(type);
return;
}
content += char;
}
if (firstFind === "\n") {
self.index = legex.length;
self.content = content.trim();
self.addType(type);
return
}
throw `Template "${self.name}" Parsing Failed because variable at Position: ${index} <${self.line}:${self.charPos}> not closed!`;
}
// @todo implement split operator Splitter
getOperators(string) {
let statements = [];
let innerCon = '';
for (let i = 0; i < string.length; i++) {
let c = string.charAt(i);
if (innerCon === '' && c === ' ') {
continue;
}
if (c === '(') {
if (innerCon !== '') {
statements.push(this.parseOperator(innerCon));
}
innerCon = '';
for (let x = 1; x < string.length; x++) {
let char = string.charAt(i + x);
if (char === ')') {
i = i + x;
break;
}
innerCon += char;
}
statements.push(this.parseOperator(innerCon));
innerCon = '';
} else {
innerCon += c;
}
}
if (innerCon !== '') {
statements.push(this.parseOperator(innerCon));
}
return statements;
}
parseOperator(operatorString) {
return this.operator(operatorString);
}
findInstructions(type) {
if (type === VParserTypes.if) {
return this.getOperators(this.content);
}
// @todo add support for assign, for, foreach and variables... can optimize interpreter times because we dont need to find it then
return [];
}
// right needs to be a string or a operator and should not be null!
// left cant be null! this is the case if it's ! operator
operator(op, left, right) {
return {
type: op,
lvalue: left,
r: right
}
}
}

26
src/app/External/VTepL/Template.js vendored Normal file
View File

@ -0,0 +1,26 @@
class VTepLTemplate {
constructor(name, content, core) {
this.name = name;
this.tpl = content;
this.parser = new VTpeLParser(name, content);
this.core = core;
}
async render(data = {}) {
return await new VTepLInterpreter(this.parser, this.core).render(data);
}
parseContent(cache) {
if (cache) {
let storage = localStorage.getItem("vtepl-" + this.name);
if (storage) {
this.parser.parsed = JSON.parse(storage);
return;
}
}
this.parser.tokenize();
if (cache) {
localStorage.setItem("vtepl-" + this.name, JSON.stringify(this.parser.parsed));
}
}
}

147
src/app/External/VUtils.js vendored Normal file
View File

@ -0,0 +1,147 @@
class VUtils {
static makePublic() {
if (VUtils.isUsed) {
return;
}
moduleLoader.registerModule("VUtils")
this.initHandlers();
VUtils.isUsed = true;
PrettyConsole.log("VUtils", "VUtils is now available in the Global Space! no VUtils. anymore needed");
moduleLoader.finishModule("VUtils")
}
static initHandlers() {
window.$ = this.$;
window.$$ = this.$$;
window.tryCatch = this.tryCatch;
VUtils.nodePrototypes();
}
static $(selector, from) {
from = from || document;
return from.querySelector(selector);
}
static $$(selector, from) {
from = from || document;
return from.querySelectorAll(selector);
}
static tryCatch(data, callback, error) {
data = VUtils.wrap(data, []);
error = error || console.error;
callback = callback || console.log;
try {
callback(...data);
} catch (e) {
error(e);
}
}
static forEach(items, cb, error) {
for (let i = 0; i < items.length; i++) {
VUtils.tryCatch([items[i], i], cb, error);
}
}
static get(valueOne, value) {
return this.wrap(valueOne, value);
}
static mergeKeys(root, target) {
root = root || {};
let keys = Object.keys(root);
for (let key of keys) {
target[key] = root[key];
}
return target;
}
static mergeOptions(target, root) {
root = root || {};
let keys = Object.keys(root);
for (let key of keys) {
target[key] = VUtils.get(root[key], target[key]);
}
return target;
}
static wrap(valueOne, valueTwo) {
let x = typeof valueTwo;
if (!(valueOne instanceof Array) && valueTwo instanceof Array) {
return [valueOne];
}
if (x === 'string' && valueOne instanceof Array) {
return valueOne.join(".");
}
return valueOne === undefined ? valueTwo : valueOne;
}
static tempId() {
return 'temp_' + Math.random().toString(36).substr(2, 16);
}
static hexToRgb(hex) {
hex = hex.replace("#", "");
let bigint = parseInt(hex, 16),
r = (bigint >> 16) & 255,
g = (bigint >> 8) & 255,
b = bigint & 255;
return [r / 255, g / 255, b / 255];
}
static nodePrototypes() {
Node.prototype.find = function (selector) {
return this.closest(selector);
};
Node.prototype.createNew = function (tag, options = {}) {
let el = document.createElement(tag);
if (options.classes) {
el.classList.add(...VUtils.get(options.classes, []));
}
el.id = VUtils.get(options.id, '');
el.innerHTML = VUtils.get(options.content, "");
VUtils.mergeKeys(options.dataset, el.dataset);
if (VUtils.get(options.append, true) === true) {
this.appendChild(el);
}
return el;
};
Node.prototype.addDelegatedEventListener = function (type, aim, callback, err) {
if (!callback || !type || !aim)
return;
this.addMultiListener(type, (event) => {
let target = event.target;
if (event.detail instanceof HTMLElement) {
target = event.detail;
}
if (target instanceof HTMLElement) {
if (target.matches(aim)) {
VUtils.tryCatch([event, target], callback, err);
} else {
const parent = target.find(aim);
if (parent) {
VUtils.tryCatch([event, parent], callback, err);
}
}
}
});
};
Node.prototype.addMultiListener = function (listener, cb, options = {}) {
let splits = listener.split(" ");
for (let split of splits) {
this.addEventListener(split, cb, options);
}
};
String.prototype.firstUpper = function () {
return this.charAt(0).toUpperCase() + this.slice(1);
};
String.prototype.firstLower = function () {
return this.charAt(0).toLowerCase() + this.slice(1);
};
}
}
VUtils.makePublic();

8
src/app/External/external.path vendored Normal file
View File

@ -0,0 +1,8 @@
External/VTepL/Core
External/VTepL/Interpreter
External/VTepL/Parser
External/VTepL/Template
External/VUtils
External/VRipple
External/VCollapse
External/SelectJs

28
src/app/app.js Normal file
View File

@ -0,0 +1,28 @@
class App {
async ignite() {
window.onerror = (err) => {
PrettyConsole.error(err);
}
moduleLoader.ignite();
moduleLoader.finishModule('Startup');
}
finish() {
setTimeout(() => {
$('v-loading-screen').classList.add('hide');
}, 300)
$('body').addDelegatedEventListener('click', '.menu-toggle', () => {
$('v-app').classList.toggle('open');
})
PrettyConsole.debug(App, moduleLoader.modules);
}
}
const app = new App();
moduleLoader.setCb(app.finish.bind(app));
(() => {
app.ignite().catch(onerror => {
PrettyConsole.error(App, onerror);
alert("App Startup failed... open console to see details");
});
})();

173
src/app/startup.js Normal file
View File

@ -0,0 +1,173 @@
const EVENT_MODULE_LOADED = "module-loaded";
class Startup {
constructor(modules) {
this.modules = modules;
this.cb = null;
this.isErrored = false;
this.isAllLoadedFired = false;
document.addEventListener(EVENT_MODULE_LOADED, this.allModulesLoaded.bind(this));
}
setCb(cb) {
this.cb = cb;
}
setErrCb(cb) {
this.errCb = cb;
}
finishModule(name) {
name = Startup.getName(name);
this.modules[name] = !0;
PrettyConsole.debug(name, "Module Loading Done");
document.dispatchEvent(new CustomEvent(EVENT_MODULE_LOADED, {detail: name}));
}
moduleErrored(name, error) {
name = Startup.getName(name);
if (this.errCb) {
this.errCb(name, error);
}
this.isErrored = true;
document.dispatchEvent(new CustomEvent(`module-errored`, {
data: {name, error}
}));
}
isModuleLoaded(names, cb) {
let self = this;
if (typeof names === 'string') {
names = [names];
}
let pendingModules = names.filter(function (module) {
return !self.modules[Startup.getName(module)]
});
if (pendingModules.length > 0) {
function listener() {
document.removeEventListener(EVENT_MODULE_LOADED, listener);
self.isModuleLoaded.bind(self)(pendingModules, cb);
}
document.addEventListener(EVENT_MODULE_LOADED, listener);
} else {
cb();
}
}
registerModule(name) {
this.modules[Startup.getName(name)] = false;
}
allModulesLoaded() {
if (this.isErrored) {
return false;
}
if (this.isAllLoadedFired) {
return true;
}
for (let module in this.modules) {
if (this.modules.hasOwnProperty(module) && !this.modules[module]) {
return false;
}
}
this.isAllLoadedFired = true;
if (this.cb) {
this.cb();
}
document.dispatchEvent(new CustomEvent('startupFin'));
return true;
}
ignite() {
document.dispatchEvent(new CustomEvent('ignite'));
}
registerInit(onInit) {
document.addEventListener('ignite', onInit);
}
loadOtherJsFiles(files) {
for (let file of files) {
this.registerModule(file[1]);
let script = document.createElement('script');
script.onload = () => {
moduleLoader.finishModule(file[1]);
}
script.onerror = (err) => {
moduleLoader.moduleErrored(file[1], err);
}
script.src = file[0];
document.body.appendChild(script);
}
}
static getName(name) {
if (typeof name === 'function') {
return name.name;
}
return name;
}
}
class Loader {
constructor() {
document.addEventListener(EVENT_MODULE_LOADED, this.loadProgress.bind(this));
document.addEventListener('startupFin', this.finish.bind(this));
this.loadingText = document.querySelector('v-loading-text');
this.loadingPerc = document.querySelector('v-loading-stats');
this.loadingScreen = document.querySelector('v-loading-screen');
this.loaded = 0;
this.toLoad = 0;
this.isErrored = false;
}
addToLoadItem(i) {
this.toLoad += i;
}
addLoadedItems(i) {
this.loaded += i;
this.loadProgress();
}
loadProgress() {
if (this.isErrored) return;
let loaded = 0,
keys = Object.keys(moduleLoader.modules);
for (let key of keys) {
if (moduleLoader.modules[key]) {
loaded++;
}
}
loaded += this.loaded;
let toLoad = keys.length + this.toLoad;
const perc = ((loaded / toLoad) * 100).toFixed(2) + "%";
this.loadingScreen.style.setProperty('--loading-scale', perc);
this.loadingPerc.innerText = `${loaded} / ${toLoad} (${perc})`;
}
onError(module, error) {
this.isErrored = true;
this.loadingText.innerText = "Errored";
this.loadingPerc.innerHTML = `Module ${module} failed to load<br>${error}`;
this.loadingScreen.style.setProperty('--loading-color', '#fa0000');
PrettyConsole.error("Startup", `Module ${module} failed to load`)
}
finish() {
document.removeEventListener('module-loaded-progress', this.loadProgress.bind(this));
}
}
const moduleLoader = new Startup({
'Startup': false,
});
const loader = new Loader();
moduleLoader.setErrCb(loader.onError.bind(loader));
const preLoader = new PreLoader();
moduleLoader.isModuleLoaded('PreLoader', () => {
moduleLoader.loadOtherJsFiles([
['/out/app/app.min.js', 'app.js'],
]);
});

0
src/icons/.gitkeep Normal file
View File

3
src/icons/close.svg Normal file
View File

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg enable-background="new 0 0 512 512" version="1.1" viewBox="0 0 512 512" xml:space="preserve"
xmlns="http://www.w3.org/2000/svg"><path d="m443.6 387.1-131.2-131.7 131.5-130c5.4-5.4 5.4-14.2 0-19.6l-37.4-37.6c-2.6-2.6-6.1-4-9.8-4s-7.2 1.5-9.8 4l-130.9 129.6-131.1-129.5c-2.6-2.6-6.1-4-9.8-4s-7.2 1.5-9.8 4l-37.3 37.6c-5.4 5.4-5.4 14.2 0 19.6l131.5 130-131.1 131.6c-2.6 2.6-4.1 6.1-4.1 9.8s1.4 7.2 4.1 9.8l37.4 37.6c2.7 2.7 6.2 4.1 9.8 4.1 3.5 0 7.1-1.3 9.8-4.1l130.6-131.2 130.7 131.1c2.7 2.7 6.2 4.1 9.8 4.1 3.5 0 7.1-1.3 9.8-4.1l37.4-37.6c2.6-2.6 4.1-6.1 4.1-9.8-0.1-3.6-1.6-7.1-4.2-9.7z"/></svg>

After

Width:  |  Height:  |  Size: 646 B

1
src/icons/error.svg Normal file
View File

@ -0,0 +1 @@
<svg focusable="false" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 192 512"><path fill="currentColor" d="M176 432c0 44.112-35.888 80-80 80s-80-35.888-80-80 35.888-80 80-80 80 35.888 80 80zM25.26 25.199l13.6 272C39.499 309.972 50.041 320 62.83 320h66.34c12.789 0 23.331-10.028 23.97-22.801l13.6-272C167.425 11.49 156.496 0 142.77 0H49.23C35.504 0 24.575 11.49 25.26 25.199z"></path></svg>

After

Width:  |  Height:  |  Size: 403 B

View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon-help-circle">
<circle cx="12" cy="12" r="10"/>
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/>
<line x1="12" y1="17" x2="12.01" y2="17"/>
</svg>

After

Width:  |  Height:  |  Size: 356 B

4
src/icons/info.svg Normal file
View File

@ -0,0 +1,4 @@
<svg focusable="false" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 192 512">
<path fill="currentColor"
d="M20 424.229h20V279.771H20c-11.046 0-20-8.954-20-20V212c0-11.046 8.954-20 20-20h112c11.046 0 20 8.954 20 20v212.229h20c11.046 0 20 8.954 20 20V492c0 11.046-8.954 20-20 20H20c-11.046 0-20-8.954-20-20v-47.771c0-11.046 8.954-20 20-20zM96 0C56.235 0 24 32.235 24 72s32.235 72 72 72 72-32.235 72-72S135.764 0 96 0z"></path>
</svg>

After

Width:  |  Height:  |  Size: 460 B

4
src/icons/success.svg Normal file
View File

@ -0,0 +1,4 @@
<svg focusable="false" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path fill="currentColor"
d="M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z"></path>
</svg>

After

Width:  |  Height:  |  Size: 426 B

5
src/icons/warning.svg Normal file
View File

@ -0,0 +1,5 @@
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="exclamation-triangle" role="img" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 576 512">
<path fill="currentColor"
d="M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"></path>
</svg>

After

Width:  |  Height:  |  Size: 675 B

5
src/loader.path Normal file
View File

@ -0,0 +1,5 @@
Core/PrettyConsole
Core/Network
Core/Math
Core/PreLoader
startup

44
src/theme/_base.scss Normal file
View File

@ -0,0 +1,44 @@
* {
box-sizing: border-box;
}
*:focus {
outline: none;
}
html, body {
margin: 0;
padding: 0;
background-color: $bg;
color: #fff;
font-size: 16px;
font-family: 'Inter', sans-serif;;
}
.hideAll {
display: none !important;
}
.icon {
width: 1em;
height: 1em;
vertical-align: middle;
font-size: 1em;
shape-rendering: geometricPrecision;
transition: transform .5s cubic-bezier(.22, .61, .36, 1);
stroke-width: 5px;
text-align: center;
margin-right: .5rem;
&.block {
display: block;
}
}
v-content {
display: block;
}
v-collapse-content .inner {
padding: 1rem;
}

60
src/theme/_loading.scss Normal file
View File

@ -0,0 +1,60 @@
@import "variable";
v-loading-screen {
background-color: #000;
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
z-index: 10000;
&.hide {
pointer-events: none;
opacity: 0;
transition-delay: 1s;
transition: opacity 1s;
z-index: -1;
}
v-loading-text {
font-family: monospace;
font-size: 4vw;
text-transform: uppercase;
}
v-loading-stats {
font-family: monospace;
color: var(--loading-color, $primary);
display: block;
font-size: .75rem;
margin-top: .2rem;
height: 1.5rem;
letter-spacing: .01rem;
text-align: center;
transition: color .5s ease-in;
}
v-loader {
position: relative;
display: block;
width: 30vw;
height: 1vw;
background-color: $loader-delay-color;
&::after {
content: '';
width: var(--loading-scale, 0);
height: 100%;
transition: all .5s ease-in;
background-color: var(--loading-color, $primary);
position: absolute;
top: 0;
left: 0;
}
}
}

42
src/theme/_scrollbar.scss Normal file
View File

@ -0,0 +1,42 @@
::-webkit-scrollbar {
width: 3px;
height: 3px;
}
::-webkit-scrollbar-button {
width: 15px;
height: 15px;
}
::-webkit-scrollbar-thumb {
background: #e1e1e1;
border: 0 none #ffffff;
border-radius: 100px;
}
::-webkit-scrollbar-thumb:hover {
background: #ffffff;
}
::-webkit-scrollbar-thumb:active {
background: $primary;
}
::-webkit-scrollbar-track {
background: #666666;
border: 0 none #ffffff;
border-radius: 46px;
}
::-webkit-scrollbar-track:hover {
background: #666666;
}
::-webkit-scrollbar-track:active {
background: #666666;
}
::-webkit-scrollbar-corner {
background: transparent;
}

31
src/theme/_variable.scss Normal file
View File

@ -0,0 +1,31 @@
$bg: #303030;
$darker: #212121;
$transparentDark: rgba(33, 33, 33, 0.39);
$nav: #1b1b1b;
$primary: #3949ab;
$second: #ff0089;
$active: #4dbb5d;
$loader-delay-color: rgb(1, 21, 29);
$hoverDark: rgba(0,0,0,.6);
$successBorder: #39ab48;
$errorBorder: #a70101;
$warningBorder: #ff7700;
$box-shadow-1: 0 .3rem .34rem rgba(0, 0, 0, 0.16), 0 .3rem .34rem rgba(0, 0, 0, 0.23);
$box-shadow-2: 0 .3rem .4rem rgba(0, 0, 0, 0.16), 0 .3rem .4rem rgba(0, 0, 0, 0.23);
$textColor: #fff;
$textColorLessBrightness: #dcdcdc;
$textColorMoreLessBrightness: #aaa;
$surfaceOnColor: #e9e9e9;
$errorInYourFace: #f32f32;
$errorInYourFaceBorder: #cf1b1b;
$errorColor: #c51162;
$errorOnColor: #8e0038;
$validColor: #39ab48;
$validOnColor: #39ab48;

View File

@ -0,0 +1,150 @@
.btn {
font-size: inherit;
border: none;
background: $primary radial-gradient(circle at 0px 0px, #3949ab 0%, rgba(0, 0, 0, .2) 100%) no-repeat;
color: #fff;
padding: 0.3em 1.1em;
margin: .2em 0;
cursor: pointer;
position: relative;
user-select: none;
border-radius: .1rem;
box-shadow: $box-shadow-1;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
transition: background-color .5s, box-shadow .5s;
&:focus {
box-shadow: $box-shadow-2;
}
&--empty {
background: transparent;
}
&--outline {
background: transparent;
border: 1px solid $primary;
}
&--space {
margin-top: .5rem;
margin-bottom: .5rem;
}
&[disabled] {
background: $hoverDark !important;
color: #303030 !important;
pointer-events: none;
}
&--valid {
background: $validColor radial-gradient(circle at 0px 0px, $validColor 0%, rgba(0, 0, 0, .2) 100%) no-repeat;
}
&--accent {
background: $second radial-gradient(circle at 0px 0px, $second 0%, rgba(0, 0, 0, .2) 100%) no-repeat;
}
&--warn {
background: $warningBorder radial-gradient(circle at 0px 0px, $warningBorder 0%, rgba(0, 0, 0, .2) 100%) no-repeat;
}
&--error {
background: $errorBorder radial-gradient(circle at 0px 0px, $errorBorder 0%, rgba(0, 0, 0, .2) 100%) no-repeat;
}
&--icon {
.icon {
font-size: 1em;
margin-right: 0;
}
}
}
.btn-fab {
border-radius: 2em;
width: 2em;
height: 2em;
padding: .2em;
}
.ripple {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
background: transparent;
pointer-events: stroke;
&__effect {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
opacity: 1;
width: 200%;
height: 0;
padding-bottom: 200%;
border-radius: 50%;
background: rgba(99, 99, 99, 0.2);
animation: a-ripple .5s ease-in;
pointer-events: none;
&.to-remove {
animation: remove-ripple .5s;
}
}
}
.btn--outline .ripple__effect {
background: rgba(57, 73, 171, .5);
z-index: -1;
}
.btn__content {
z-index: 1;
font-weight: 400;
pointer-events: none;
}
@keyframes remove-ripple {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
@keyframes a-ripple {
0% {
opacity: 0;
padding-bottom: 0;
width: 0;
}
25% {
opacity: 1;
}
100% {
width: 200%;
padding-bottom: 200%;
}
}
.btn-line {
text-align: right;
button {
margin-right: 0.2em;
margin-left: 0.2em;
display: inline-block;
}
button:last-child {
margin-right: 0;
}
}

View File

@ -0,0 +1,85 @@
v-collapse {
display: flex;
flex-direction: column;
v-collapse-item.open {
margin-top: .5rem;
margin-bottom: 1rem;
}
}
v-collapse-item, v-collapse-content, v-collapse-head {
display: block;
}
v-collapse-item {
margin-top: 0;
transition: box-shadow .5s, margin .3s;
box-shadow: #3949ab;
&.open {
box-shadow: $box-shadow-1;
v-collapse-content {
overflow: unset;
animation: collapse-overflow .5s linear;
}
}
}
@keyframes collapse-overflow {
0%, 99% {
overflow: hidden;
}
100% {
overflow: unset;
}
}
v-collapse-item:not(:last-child) {
v-collapse-head {
border-bottom: 0.05rem solid rgba(59, 59, 59, .3);
}
}
v-collapse-head {
padding: 1rem .5rem;
cursor: pointer;
user-select: none;
background-color: rgba(0, 0, 0, .2);
color: #ffffff;
font-weight: bold;
position: relative;
&:after {
content: '';
border: solid #fff;
border-width: 0 .1rem .1rem 0;
width: .5rem;
height: .5rem;
position: absolute;
right: 1em;
margin-top: .1rem;
transform: rotate(45deg);
transition: .3s;
}
}
v-collapse-item.open {
v-collapse-head:after {
transform: rotate(225deg);
margin-top: .4rem;
}
}
v-collapse-content {
max-height: 0;
overflow: hidden;
transition: max-height .5s cubic-bezier(0.65, -0.02, 0.56, 1.04);
background-color: rgba(0, 0, 0, .05);
}
.collapse-inner {
display: block;
padding: 1rem;
}

View File

@ -0,0 +1,29 @@
input[type="color"] {
opacity: 0;
}
.colorBlob {
display: block;
width: 100%;
height: 100%;
}
.color-picker {
display: block;
position: relative;
margin: .2rem 0;
height: 1.5rem;
width: 4rem;
border-radius: 5rem;
overflow: hidden;
box-shadow: $box-shadow-1;
input {
position: absolute !important;
top: 0;
width: 100%;
height: 100%;
cursor: pointer;
}
}

View File

@ -0,0 +1,111 @@
v-input input {
font-size: 1em;
background-color: rgba(0, 0, 0, .4);
border: none;
border-bottom: 0.15em solid $primary;
color: $textColor;
padding: 0.5em;
}
v-input.focus input,
v-input input:focus {
border-color: $second;
}
v-input.valid input {
border-color: $validOnColor;
}
v-input.invalid input {
border-color: $errorInYourFaceBorder;
}
v-input {
display: flex;
flex-direction: column;
margin-bottom: 0.2em;
position: relative;
font-size: 1.3rem;
padding-top: 1em;
&.no-space {
padding-top: .1em;
}
}
v-input label {
font-size: .7em;
position: absolute;
top: 1.8em;
left: .5em;
pointer-events: none;
vertical-align: middle;
transform: translateY(50%);
color: #dcdcdc;
transition: all .2s ease-out;
}
v-input.focus label,
v-input input:focus ~ label {
top: 0.3em;
left: 0;
transform: translateY(0);
font-size: .6em;
}
v-input .error {
display: none;
}
v-input.invalid .error {
margin-top: 0.2em;
display: block;
font-size: .7em;
color: $errorInYourFace;
}
$height: 1.5rem;
$innerHeight: 1rem;
$width: 3rem;
$innerWidth: 1rem;
v-switch {
display: flex;
flex-direction: row;
align-items: center;
margin-top: .5rem;
input {
position: absolute;
appearance: none;
opacity: 0;
}
label {
display: block;
border-radius: 9999px;
width: $width;
height: $height;
background-color: $hoverDark;
position: relative;
cursor: pointer;
margin-right: 0.5rem;
&:after {
content: '';
background-color: #dcdcdc;
position: absolute;
top: 0.2rem;
left: 0.25rem;
height: $innerHeight;
width: $innerWidth;
border-radius: 9999px;
transition: .5s;
}
}
input:checked + label:after {
transform: translateX($width - $height);
background-color: $primary;
}
}

View File

@ -0,0 +1,79 @@
v-modal-container {
display: none;
&.open {
display: flex;
}
justify-content: center;
align-items: center;
width: 100vw;
height: 100vh;
position: absolute;
top: 0;
left: 0;
&:after {
content: '';
position: absolute;
z-index: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, .7);
}
v-modal {
display: block;
max-width: 768px;
min-width: 300px;
z-index: 1;
background-color: #333333;
color: #fff;
box-shadow: $box-shadow-1;
v-modal-head, v-modal-content {
display: block;
padding: .2rem;
}
v-modal-head {
background-color: #212121;
box-shadow: $box-shadow-1;
display: flex;
align-items: center;
.headline {
padding: .5rem 1rem;
font-weight: bold;
}
}
v-modal-content {
padding: 1rem;
.button-group {
display: flex;
justify-content: flex-end;
margin-top: 1rem;
.btn {
margin-right: 1rem;
}
}
}
.close-modal {
margin-left: auto;
fill: #fff;
cursor: pointer;
* {
pointer-events: none;
}
&.close {
display: none;
}
}
}
}

View File

@ -0,0 +1,141 @@
@import "../variable";
v-notification, v-toast {
top: 10vh;
padding-bottom: .3rem;
height: 90vh;
display: flex;
flex-direction: column-reverse;
pointer-events: none;
position: absolute;
}
v-notification {
width: 90%;
max-width: 400px;
right: .3rem;
}
v-toast {
right: 0;
left: 0;
margin: 0 auto;
align-items: center;
}
v-notification-toast {
margin-bottom: 10px;
box-shadow: $box-shadow-1;
overflow: hidden;
background-color: $darker;
color: #fff;
border-radius: 9999rem;
animation: toastOut ease-in-out 2000ms;
display: flex;
.message {
padding: 1rem 1.5rem;
}
.icon-wrap {
background-color: $primary;
font-size: 1.3em;
padding: .5em .5em .5em 1rem;
display: flex;
justify-content: center;
align-items: center;
.icon {
margin-right: 0;
}
&.success {
background-color: $successBorder;
}
&.error {
background-color: $errorBorder;
}
&.warning {
background-color: $warningBorder;
}
}
}
v-notification-item {
margin-bottom: 10px;
position: relative;
width: 100%;
box-shadow: $box-shadow-1;
overflow: hidden;
background-color: $darker;
.message {
padding: 1rem;
}
.fade-bar {
animation: fadeOut ease-in-out 3000ms;
height: .25rem;
width: 100%;
position: absolute;
bottom: 0;
transform-origin: left;
background-color: $primary;
&.endless {
animation: endlessFade ease-in-out 500ms infinite;
}
&.success {
background-color: $successBorder;
}
&.error {
background-color: $errorBorder;
}
&.warning {
background-color: $warningBorder;
}
}
}
@keyframes fadeOut {
from {
transform: scaleX(1);
}
to {
transform: scaleX(0);
}
}
@keyframes toastOut {
0%, 90% {
transform: translateY(0);
opacity: 1;
}
100% {
transform: translateY(200%);
opacity: 0;
}
}
@keyframes endlessFade {
0% {
transform: scaleX(1);
transform-origin: right;
}
49% {
transform-origin: right;
}
50% {
transform: scaleX(0);
transform-origin: left;
}
100% {
transform: scaleX(1);
}
}

View File

@ -0,0 +1,94 @@
.range {
-webkit-appearance: none;
width: 100%;
margin: .5rem 0;
background-color: rgba(0, 0, 0, 0.12);
&.center {
margin-left: auto;
margin-right: auto;
}
&.right {
margin-left: 10%;
}
&:focus {
outline: none;
}
&::-webkit-slider-runnable-track {
width: 100%;
height: .3rem;
cursor: pointer;
background-color: rgba(0, 0, 0, 0.12);
color: rgba(0, 0, 0, 0.12);
border-radius: 9999px;
border: none;
}
&::-webkit-slider-thumb {
border: 0 solid rgba(0, 0, 30, 0);
height: 1rem;
width: 1rem;
border-radius: 9999px;
background: $primary;
cursor: pointer;
-webkit-appearance: none;
margin-top: -.33rem;
}
&::-moz-range-track {
width: 100%;
height: .3rem;
cursor: pointer;
background-color: rgba(0, 0, 0, 0.12);
color: rgba(0, 0, 0, 0.12);
border-radius: 9999px;
border: none;
}
&::-moz-range-thumb {
border: 0 solid rgba(0, 0, 30, 0);
height: 1rem;
width: 1rem;
border-radius: 9999px;
background: $primary;
cursor: pointer;
margin-top: -.33rem;
}
}
.input-range {
display: block;
position: relative;
margin-bottom: 2rem;
.max, .min, .current {
font-size: .8em;
color: #dcdcdc;
position: absolute;
bottom: -1rem;
}
.current {
display: block;
width: 100%;
text-align: center;
&:focus {
outline: none;
font-weight: bold;
color: $second;
}
}
.min {
left: 0;
}
.max {
right: 0;
}
}

View File

@ -0,0 +1,87 @@
$borderRadius: 4px;
v-select, v-options, v-option, v-label {
display: inline-block;
box-sizing: border-box;
}
v-select {
display: block;
position: relative;
margin-bottom: .5rem;
&:focus {
outline: none;
v-label {
border-color: rgba(0, 0, 0, .8);
}
}
}
v-label {
padding: .5rem 1rem;
background-color: rgba(0, 0, 0, .1);
border: 0.1rem solid rgba(0, 0, 0, .6);
color: $textColor;
cursor: pointer;
display: block;
position: relative;
margin-top: .5rem;
&:after {
content: '';
border: solid #fff;
border-width: 0 .1rem .1rem 0;
height: .5rem;
width: .5rem;
position: absolute;
right: 1.1em;
margin-top: .1rem;
transition: .3s;
transform: rotate(45deg);
}
&.open:after {
transform: rotate(-135deg);
margin-top: .4rem;
}
}
v-options {
position: absolute;
display: flex;
flex-direction: column;
overflow: hidden;
max-height: 0;
background-color: $bg;
z-index: 1000;
cursor: pointer;
color: $textColor;
box-shadow: $box-shadow-1;
width: 100%;
transition: max-height .3s;
}
v-option {
padding: .5rem 1rem .5rem .5rem;
position: relative;
&:not(:last-child) {
border-bottom: solid 1px #333;
}
}
v-option:not([disabled]):hover {
background-color: rgba(0, 0, 0, 0.3);
}
v-option[selected] {
background-color: $primary;
color: $textColor;
}
v-option[disabled] {
color: $textColorMoreLessBrightness;
}

63
src/theme/gui/_graph.scss Normal file
View File

@ -0,0 +1,63 @@
.graphContent {
position: relative;
height: 250px;
background-color: #171717;
margin: 20px auto;
box-shadow: $box-shadow-1;
canvas {
position: absolute;
top: 0;
left: 0;
}
}
.indicator {
display: inline;
font-size: .5rem;
border: 1px solid #dcdcdc;
color: #dcdcdc;
padding: 2px;
vertical-align: middle;
&.running {
border-color: $successBorder;
color: $successBorder;
}
&.warning {
border-color: $warningBorder;
color: $warningBorder;
}
&.finished {
border-color: $primary;
color: $primary;
}
}
.table {
margin-bottom: 20px;
width: 50%;
@media only screen and (max-width: 860px) {
width: 100%;
.table-group .right {
display: flex;
flex-grow: 1;
justify-content: flex-end;
}
}
.table-group {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
margin-top: 5px;
border-bottom: 0.001em solid #dcdcdc;
.left {
min-width: 50%
}
}
}

View File

@ -0,0 +1,44 @@
#main-header {
background-color: #1b1b1b;
color: #ffffff;
box-shadow: $box-shadow-1;
display: flex;
.logo {
display: block;
font-family: monospace;
font-size: 32px;
font-weight: bolder;
padding: .5rem 1rem;
}
nav {
margin-left: auto;
display: flex;
nav-item {
display: flex;
align-items: center;
padding: 0.5rem 1rem;
cursor: pointer;
position: relative;
&:hover {
background-color: rgba(0, 0, 0, .5) !important;
}
&.active {
background-color: rgba(0, 0, 0, .3);
&:after {
position: absolute;
content: '';
width: 100%;
height: 4px;
top: 0;
left: 0;
background-color: $primary;
}
}
}
}
}

11
src/theme/gui/_page.scss Normal file
View File

@ -0,0 +1,11 @@
.error-page {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.wrapper {
padding: 1rem;
}

19
src/theme/main.scss Normal file
View File

@ -0,0 +1,19 @@
@import "variable";
@import "base";
@import "scrollbar";
@import "loading";
// Components
@import "components/btn";
@import "components/input";
@import "components/select";
@import "components/range";
@import "components/color";
@import "components/collapse";
@import "components/modal";
@import "components/notification-look";
// GUI
@import "gui/header";
@import "gui/graph";
@import "gui/page";