Compare commits

..

79 commits

Author SHA1 Message Date
c54cc103eb Nav buttons on subpages 2024-05-20 11:43:00 -05:00
97f7234f92 Improved stats section 2024-05-20 11:34:25 -05:00
e22005dc86 Fix CSS on main page for videos 2024-05-20 11:04:49 -05:00
312089c58d Announcements are properly implemented (yay) 2024-05-20 10:45:24 -05:00
8e703ecd3f Change old config to constants, add real conffig file, add daily video URLs. 2024-05-20 10:20:19 -05:00
710d7c237f randomizes pfp with each spin 2024-05-19 09:56:03 -05:00
242a8c55fb revert 7c9797fa0e
revert Trying something dumb
2024-05-17 19:19:52 -05:00
7c9797fa0e Trying something dumb 2024-05-17 18:27:05 -05:00
6ab16fa4d3 revert f5c572b3b2
revert Bun stuff?
2024-05-17 18:18:13 -05:00
330dbc5124 revert 7df10ace84
revert bun
2024-05-17 18:18:01 -05:00
f5c572b3b2 Bun stuff? 2024-05-17 18:14:12 -05:00
7df10ace84 bun 2024-05-17 18:09:29 -05:00
e29c78b49b Remove exclamation point 2024-05-16 11:55:35 -05:00
fc8dc40cf0 Remove debug line 2024-05-16 11:55:18 -05:00
456795e0d9 More formatting for services section 2024-05-16 11:52:07 -05:00
25353fe2df Unfinished stats page, FAQ mini section, other changes 2024-05-15 22:31:17 -05:00
4805c1ba48 Be more specific 2024-05-14 15:31:00 -05:00
166d7eddc8 Update commit counter to reflect update 2024-05-14 15:22:30 -05:00
1223bc522c Reduce volume 2024-04-28 12:30:40 -05:00
66ded0e710 Update embeds 2024-04-26 09:09:01 -05:00
5285623831 List Thumbor as Service 2024-04-25 20:04:35 -05:00
448b053749 Move Lengthbars to global 2024-04-25 20:01:42 -05:00
3352ceb796 PNGs 2024-04-25 20:01:02 -05:00
80ad607d92 Its here!!! its finally here!!!! 2024-04-24 15:55:11 -05:00
f34bcea44a update Todo 2024-04-24 15:43:38 -05:00
1122d1fafc Fix empty weather text 2024-04-24 15:41:03 -05:00
ed33f4b7be Remove weather 2024-04-24 14:42:44 -05:00
2899126ff2 Basic stats section 2024-04-23 09:08:04 -05:00
4e05808f4f Add CSS to highlighted words 2024-04-22 10:17:35 -05:00
a78542bdf9 FAQ section better formatted 2024-04-22 09:48:58 -05:00
fea2557005 HTML added to word list 2024-04-22 09:46:29 -05:00
3b5945b326 Finally fixed the jitter 2024-04-22 07:56:44 -05:00
93ce3204a7 Test commit 2024-04-22 07:49:12 -05:00
878e1829ee Forgejo service 2024-04-21 20:49:06 -05:00
ebd8581fa4 Update Forgejo links 2024-04-21 20:45:41 -05:00
6744165d3e Switch to Forgejo link 2024-04-21 19:13:18 -05:00
a07665962a Obligatory "I forgot to remove debug line" commit 2024-04-21 13:33:24 -05:00
9e28abc38c Switch scraping from Codeberg to Github because scraping codeberg is mean but big company scraping is moral 2024-04-21 13:32:01 -05:00
77aebee1da add by one to remove the jitter 2024-04-21 13:27:02 -05:00
02a8a44125 Remove websocket code, show global spin count for users without JS, and new noscript class 2024-04-21 13:01:21 -05:00
56150f1ce8 Remove rain (again) 2024-04-20 18:40:27 -05:00
e9e3f05208 Ping more consistent 2024-04-20 17:00:58 -05:00
17327706f5 Global Spinner 2024-04-20 16:46:12 -05:00
fb85cdf770 test 2024-04-20 15:39:25 -05:00
de69048ebe Losing my mind over here 2024-04-20 13:18:01 -05:00
32c6532a4f logging ?! 2024-04-20 13:03:54 -05:00
980be6f21e Testing 2024-04-20 12:56:37 -05:00
440865f91a Spin counter more consistent 2024-04-20 12:24:46 -05:00
5b5d5887a2 Dont minify js for now 2024-04-20 12:15:04 -05:00
e6b13dad62 Higher res for main img 2024-04-20 11:57:33 -05:00
38651429db forgot to remove log lol 2024-04-20 07:12:37 -05:00
ac4aa31a3b Thumbor compression is real 2024-04-20 07:12:16 -05:00
c39b2736c2 How long has minification been broken? 2024-04-20 06:50:20 -05:00
df49e37654 Spin count 2024-04-19 16:14:09 +00:00
4efc1e09b3 Spinny Counter 2024-04-19 14:31:26 +00:00
689e66e1d5 Spinny pfp 2024-04-18 23:50:10 -05:00
ace19a693d MORE ORGANIZE!!! 2024-04-18 21:39:08 -05:00
bfcad31cb4 FAQ section, easter egg, general format improvements 2024-04-18 19:17:55 -05:00
c140edf16e Minor formatting, less quotes 2024-04-17 11:21:02 -05:00
1166137cb3 Minor improvements to activity images, new todo 2024-04-17 11:12:42 -05:00
73cfa2e664 Add activities to socials page 2024-04-17 09:33:05 -05:00
338bb7a704 Reducing max width and changing order 2024-04-16 20:05:12 +00:00
a6723522e6 Change min width slightly to prevent funky linebreaks 2024-04-16 20:01:54 +00:00
f44d193dfb Links are in a grid layout 2024-04-16 20:01:16 +00:00
a65c5c129e Properly update link depending on branch 2024-04-16 19:38:32 +00:00
7aa9ba6311 Socials page finally gets acknowledged 2024-04-16 19:25:15 +00:00
94b24d614c DDoSing myself? Don't mind if I do! 2024-04-13 00:08:16 -05:00
5ef6b5525c JUST incase 2024-04-12 23:37:21 -05:00
dcf610a432 Its javascript time!!! 2024-04-12 23:35:09 -05:00
16bce9f0ba Update blurb 2024-04-12 22:42:21 -05:00
ae51271791 fix weird padding on links 2024-04-02 22:14:52 -05:00
af469a82f1 Remove Extra Code 2024-04-02 22:12:03 -05:00
573f02df52 Make card image with text better 2024-03-30 01:42:20 -05:00
1c66904e48 remove emotes 2024-03-30 01:31:48 -05:00
ca90201ee2 dark scheme 2024-03-30 01:27:50 -05:00
ba60e76436 Asahi update Asahi update 2024-03-30 01:26:45 -05:00
bd80d1ba0d change min height vh to % 2024-03-29 20:41:17 -05:00
47286cda59 New Fedi 2024-03-29 20:40:15 -05:00
290a37c5f3 Anchors that dont link to anything are white 2024-03-29 16:55:40 -05:00
27 changed files with 1037 additions and 720 deletions

4
.gitignore vendored
View file

@ -131,4 +131,6 @@ dist
.pnp.*
# Violet's Purgatory
static/cached
static/cached
cached
config

View file

@ -1,23 +1,42 @@
# Violets-Purgatory
Violet's Purgatory is a website filled to the brim with whatever I feel like adding! Currently, the stable version can be found at https://violets-purgatory.dev and the beta (based on the dev branch) can be found at https://beta.violets-purgatory.dev
Although beta probably *isn't* the right term, `dev.violets-purgatory.dev` is just kinda ugly :/
We also have an API, which can be located at https://api.violets-purgatory.dev, which is currently very under developed, but will continue to have updates for features I see fit.
We also have an API, which can be located at https://api.violets-purgatory.dev, which is currently under developed, but will continue to have updates for features I see fit.
## To-do
- [ ] Add more content to the socials page
# How it works
### The config
Although the code for it isn't nessacarily pretty, theres a few important things to go over with how it works.
The config.json file, soon to be renamed for a local config, contains constants for Violet's Purgatory. In the file, there are lots of important notable features, such as fallback activity images for the Discord Activity section, and words that are automatically highlighted.
### Word highlighting
Word highlighting is a feature that automatically sets the color of certain keywords, including but not limited to Violet being purple, Javascript being yellow, NodeJS being green, and Godot Engine being blue. This is nothing more than a fancy feature to reduce the amount of code required on the site.
These highlighted words ARE case senstive. You may notice certain things such as "Violet" at the top of the card are not highlighted despite being in the words list. To make a word in the highlight list not highlighted, simply add `{}` around it. E.G. for the title of the page, on the site it shows as `Violet`, but in the code is written as `{Violet}` to prevent highlighting.
To add new highlighted words, find the highlighted words section in config.json. The key is the word to highlight, and the value is the color.
### Dynamic HTML
Currently this system is extremely unsophisticated, adding new dynamic HTML isn't as streamlined as it should be. Basically, in the code, is a dictionary that specifies every keyword to look for. Then, it looks for those keywords, and replaces them with HTML.
A good example is the activity system. The keyword for the discord activities is `ACTIVITIES`. So, if you wanted to create another activity section, you would simply put `{ACTIVITIES}` in the HTML code.
There is also a `{PATH_[html file]}` keyword. Currently, this is not used much, but may be more useful in the future.
On the main page, you can find `{PATH_SOCIALS}`. This effectively "embeds" the socials page on the site. The part of the page that is used is based upon the `main` HTML tag.
For an easy to digest example, look at the socials section on the main page of [Violet's Purgatory](https://violets-purgatory.dev). Afterwards, look at the [*socials page*](https://violets-purgatory.dev/socials). You will notice they're the same, because in the code for the main page, I put {PATH_SOCIALS} which got the page at /socials.
# To-do
## Highlighter:
- [ ] Desperately need to rework the highlighting system.
- [ ] Make it ignore classes, IDs, inline-CSS attributes, etc
- [ ] Only index visible elements for words
- [x] Ignore text ouside the body
## Socials:
- [ ] Make it more easily findable on the site
- [ ] Add more content
- [ ] Pull latest Youtube video & display it
- [ ] Display current Discord Activities
- [ ] Display current steam game
- [x] Display current Discord Activities
Completed:
- [x] Stop using Lanyard Web Socket Directly and proxy it through the API (Alternatively, self host the Lanyard API)
- [x] Cut the main CSS file into multiple so that only the nessacary CSS is loaded (Reduces traffic and loading times)
- [x] Add image caching instead of using image proxies (keeps the security benefit and decreases loading times)
- [x] Add code to automatically minify the HTML
- [x] Add random quotes
- [x] Seperate Values from the javascript into their own config for readability
- [x] Add a commit counter
## Activities
- [ ] Compress images to reduce space used and network usage (Probably using thumbor to prevent needing MORE packages TwT)

BIN
assets/Images/asahiPFP.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

View file

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,3 @@
{
"dailyVideoURL": ""
}

View file

@ -23,10 +23,7 @@
},
"quotes": [
"Remember the 14th commandment: Thou shalt always clean thy plate and not waste anything, whether thy stomach is full, or not.",
"Remember son, dying is gay!",
"Your friendly neighborhood queer",
"I hate javascript!!!",
"Don't fuck with this senator!",
"Happy 400 commits!",
"Play Cave Story!",
"Cave Story+ Sucks!",
@ -35,9 +32,8 @@
"IMAGE CACHING!!! THE IMAGE CACHING IS REAL!!!!",
"Is sharing your IP <em>reallyy</em> that bad?",
"The worst git user to exist",
"no idea how branches work",
"Their ass is NOT listening",
"These birds are Pissing me off... I'm the original &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Starwalker"
"These birds are Pissing me off... I'm the original &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Starwalker",
"Fun fact: Did you know the <a href='./socials'>Socials section</a> is considered its own page?"
],
"titles": [
"Boykisser",
@ -60,6 +56,10 @@
"NodeJS": "limegreen",
"Violet's": "rgb(200, 150, 255)",
"Violet": "rgb(200, 150, 255)",
"Asahi": "rgb(255, 175, 175)",
"Lunya": "rgb(255, 175, 175)",
"bisexual": "rgb(214, 2, 112)",
"enby": "rgb(252, 244, 52)",
"Purgatory": "rgb(200, 150, 255)",
"Youtube": "rgb(255, 0, 0)",
"Fedi": "rgb(175, 125, 200)",
@ -73,6 +73,12 @@
"Ko-fi": "rgb(255, 150, 150)",
"Revolt": "rgb(255, 50, 50)",
"Discord": "rgb(150, 150, 255)",
"SearXNG": "rgb(100, 100, 255)"
"SearXNG": "rgb(100, 100, 255)",
"Highlighting": "yellow",
"highlighted": "yellow",
"Forgejo": "orange",
"HTML": "orange",
"CSS": "rgb(50, 200, 255)",
"Thumbor": "rgb(225, 225, 255)"
}
}

View file

@ -1,25 +1,43 @@
const express = require('express'),
path = require('path'),
fs = require('fs'),
pageUpdater = require('./pageUpdater.js')
pageUpdater = require('./pageUpdater.js'),
WebSocket = require("ws")
var app = express()
const PORT = process.env.PORT || 8080
const staticpath = path.join(__dirname, 'static')
const cachePath = path.join(__dirname, 'cached')
const assetPath = path.join(__dirname, "assets")
const configPath = path.join(__dirname, 'config')
var config = JSON.parse(fs.readFileSync(path.join(__dirname, 'config.json')))
const configFile = path.join(configPath, "config.json")
const announcementFile = path.join(configPath, "announcement.html")
if (!fs.existsSync(configPath)) {
fs.mkdirSync(configPath)
}
if (!fs.existsSync(configFile)) {
fs.writeFileSync(configFile, fs.readFileSync(path.join(assetPath, "defaults/config.json")))
}
if (!fs.existsSync(announcementFile)) {
fs.writeFileSync(announcementFile, ``)
}
var constants = JSON.parse(fs.readFileSync(path.join(__dirname, 'constants.json')))
app.listen(PORT, () => {
console.log("Violet's Purgatory is now listening on port: " + PORT)
})
var cachePath = path.join(staticpath, 'cached')
var fontPath = path.join(staticpath, "fonts")
app.use("/fonts", express.static(fontPath))
app.use("/fonts", express.static(path.join(assetPath, "fonts")))
app.use("/cached", express.static(cachePath))
app.use("/imgs", express.static(path.join(assetPath, "Images")))
app.use("/snds", express.static(path.join(assetPath, "Sounds")))
if (!fs.existsSync(cachePath)) {
fs.mkdirSync(cachePath)
@ -31,6 +49,10 @@ if (!fs.existsSync(cachePath)) {
}
}
app.get("/discHTML", (req, res) => {
res.send(pageUpdater.getActivities())
})
app.use(pageUpdater.middleWare)
process.on('uncaughtException', (err, origin) => {

View file

@ -1,9 +1,9 @@
const path = require("path"),
fs = require("fs")
var config = JSON.parse(fs.readFileSync(path.join(__dirname, 'config.json')))
var constants = JSON.parse(fs.readFileSync(path.join(__dirname, 'constants.json')))
var activityImages = config.activityImages
var activityImages = constants.activityImages
function get_img_url(activity, size = "large_image") {
if ("assets" in activity) {
@ -85,9 +85,9 @@ module.exports = {
function get_img(activity, size = "large_image") {
if (cachedImages[get_img_url(activity, size)]) {
var fn = cachedImages[get_img_url(activity, size)]
var fp = path.join(__dirname, 'static/cached', fn)
var fp = path.join(__dirname, 'cached', fn)
} else {
return 'imgs/notFound.png'
return '/imgs/notFound.png'
}
return '/cached/' + fn

365
package-lock.json generated
View file

@ -11,7 +11,7 @@
"dependencies": {
"express": "^4.18.2",
"minify-html": "^0.0.2",
"ws": "^8.14.2",
"ws": "^8.16.0",
"youtubei.js": "^9.0.2"
}
},
@ -52,12 +52,12 @@
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
},
"node_modules/body-parser": {
"version": "1.20.1",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
"dependencies": {
"bytes": "3.1.2",
"content-type": "~1.0.4",
"content-type": "~1.0.5",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
@ -65,7 +65,7 @@
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.11.0",
"raw-body": "2.5.1",
"raw-body": "2.5.2",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
},
@ -83,12 +83,18 @@
}
},
"node_modules/call-bind": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
"integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
"dependencies": {
"function-bind": "^1.1.1",
"get-intrinsic": "^1.0.2"
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.4",
"set-function-length": "^1.2.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@ -114,9 +120,9 @@
}
},
"node_modules/cookie": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
"engines": {
"node": ">= 0.6"
}
@ -134,6 +140,22 @@
"ms": "2.0.0"
}
},
"node_modules/define-data-property": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
"dependencies": {
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"gopd": "^1.0.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
@ -164,6 +186,25 @@
"node": ">= 0.8"
}
},
"node_modules/es-define-property": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
"integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
"dependencies": {
"get-intrinsic": "^1.2.4"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@ -178,16 +219,16 @@
}
},
"node_modules/express": {
"version": "4.18.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
"integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
"version": "4.19.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
"integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
"body-parser": "1.20.1",
"body-parser": "1.20.2",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
"cookie": "0.5.0",
"cookie": "0.6.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
@ -252,39 +293,57 @@
}
},
"node_modules/function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-intrinsic": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
"integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==",
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
"integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
"dependencies": {
"function-bind": "^1.1.1",
"has": "^1.0.3",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"has-proto": "^1.0.1",
"has-symbols": "^1.0.3"
"has-symbols": "^1.0.3",
"hasown": "^2.0.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"node_modules/gopd": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
"dependencies": {
"function-bind": "^1.1.1"
"get-intrinsic": "^1.1.3"
},
"engines": {
"node": ">= 0.4.0"
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-property-descriptors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
"dependencies": {
"es-define-property": "^1.0.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
"integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
"integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
"engines": {
"node": ">= 0.4"
},
@ -303,6 +362,17 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/http-errors": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
@ -438,9 +508,9 @@
}
},
"node_modules/object-inspect": {
"version": "1.12.3",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
"integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
"version": "1.13.1",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
"integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@ -504,9 +574,9 @@
}
},
"node_modules/raw-body": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
@ -583,19 +653,39 @@
"node": ">= 0.8.0"
}
},
"node_modules/set-function-length": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
"dependencies": {
"define-data-property": "^1.1.4",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.4",
"gopd": "^1.0.1",
"has-property-descriptors": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
},
"node_modules/side-channel": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
"integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
"dependencies": {
"call-bind": "^1.0.0",
"get-intrinsic": "^1.0.2",
"object-inspect": "^1.9.0"
"call-bind": "^1.0.7",
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.4",
"object-inspect": "^1.13.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@ -664,9 +754,9 @@
"integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA=="
},
"node_modules/undici": {
"version": "5.28.3",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.28.3.tgz",
"integrity": "sha512-3ItfzbrhDlINjaP0duwnNsKpDQk3acHI3gVJ1z4fmwMK31k5G9OVIAMLSIaP6w4FaGkaAkN6zaQO9LUvZ1t7VA==",
"version": "5.28.4",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz",
"integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==",
"dependencies": {
"@fastify/busboy": "^2.0.0"
},
@ -699,9 +789,9 @@
}
},
"node_modules/ws": {
"version": "8.14.2",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz",
"integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==",
"version": "8.16.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz",
"integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==",
"engines": {
"node": ">=10.0.0"
},
@ -758,12 +848,12 @@
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
},
"body-parser": {
"version": "1.20.1",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
"requires": {
"bytes": "3.1.2",
"content-type": "~1.0.4",
"content-type": "~1.0.5",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
@ -771,7 +861,7 @@
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.11.0",
"raw-body": "2.5.1",
"raw-body": "2.5.2",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
}
@ -782,12 +872,15 @@
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="
},
"call-bind": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
"integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
"requires": {
"function-bind": "^1.1.1",
"get-intrinsic": "^1.0.2"
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.4",
"set-function-length": "^1.2.1"
}
},
"content-disposition": {
@ -804,9 +897,9 @@
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="
},
"cookie": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw=="
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="
},
"cookie-signature": {
"version": "1.0.6",
@ -821,6 +914,16 @@
"ms": "2.0.0"
}
},
"define-data-property": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
"requires": {
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"gopd": "^1.0.1"
}
},
"depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
@ -841,6 +944,19 @@
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="
},
"es-define-property": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
"integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
"requires": {
"get-intrinsic": "^1.2.4"
}
},
"es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="
},
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@ -852,16 +968,16 @@
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="
},
"express": {
"version": "4.18.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
"integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
"version": "4.19.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
"integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
"requires": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
"body-parser": "1.20.1",
"body-parser": "1.20.2",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
"cookie": "0.5.0",
"cookie": "0.6.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
@ -914,39 +1030,56 @@
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="
},
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="
},
"get-intrinsic": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
"integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==",
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
"integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
"requires": {
"function-bind": "^1.1.1",
"has": "^1.0.3",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"has-proto": "^1.0.1",
"has-symbols": "^1.0.3"
"has-symbols": "^1.0.3",
"hasown": "^2.0.0"
}
},
"has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"gopd": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
"requires": {
"function-bind": "^1.1.1"
"get-intrinsic": "^1.1.3"
}
},
"has-property-descriptors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
"requires": {
"es-define-property": "^1.0.0"
}
},
"has-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
"integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg=="
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
"integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q=="
},
"has-symbols": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
},
"hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"requires": {
"function-bind": "^1.1.2"
}
},
"http-errors": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
@ -1048,9 +1181,9 @@
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="
},
"object-inspect": {
"version": "1.12.3",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
"integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g=="
"version": "1.13.1",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
"integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ=="
},
"on-finished": {
"version": "2.4.1",
@ -1093,9 +1226,9 @@
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
},
"raw-body": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
"requires": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
@ -1151,19 +1284,33 @@
"send": "0.18.0"
}
},
"set-function-length": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
"requires": {
"define-data-property": "^1.1.4",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.4",
"gopd": "^1.0.1",
"has-property-descriptors": "^1.0.2"
}
},
"setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
},
"side-channel": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
"integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
"requires": {
"call-bind": "^1.0.0",
"get-intrinsic": "^1.0.2",
"object-inspect": "^1.9.0"
"call-bind": "^1.0.7",
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.4",
"object-inspect": "^1.13.1"
}
},
"source-map": {
@ -1212,9 +1359,9 @@
}
},
"undici": {
"version": "5.28.3",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.28.3.tgz",
"integrity": "sha512-3ItfzbrhDlINjaP0duwnNsKpDQk3acHI3gVJ1z4fmwMK31k5G9OVIAMLSIaP6w4FaGkaAkN6zaQO9LUvZ1t7VA==",
"version": "5.28.4",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz",
"integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==",
"requires": {
"@fastify/busboy": "^2.0.0"
}
@ -1235,9 +1382,9 @@
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="
},
"ws": {
"version": "8.14.2",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz",
"integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==",
"version": "8.16.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz",
"integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==",
"requires": {}
},
"youtubei.js": {

View file

@ -19,7 +19,7 @@
"dependencies": {
"express": "^4.18.2",
"minify-html": "^0.0.2",
"ws": "^8.14.2",
"ws": "^8.16.0",
"youtubei.js": "^9.0.2"
}
}

View file

@ -2,22 +2,51 @@ const path = require('path'),
fs = require('fs'),
WebSocket = require('ws'),
minify = require('minify-html'),
activityToHTML = require("./overcomplicatedStatuses.js"),
weatherGenerator = require("./weatherGenerator")
activityToHTML = require("./overcomplicatedStatuses.js")
var config = JSON.parse(fs.readFileSync(path.join(__dirname, 'config.json')))
// weatherGenerator = require("./weatherGenerator")
var highlightedWords = config.highlightedWords
var quotes = config.quotes
var titles = config.titles
var constants = JSON.parse(fs.readFileSync(path.join(__dirname, 'constants.json')))
var commitCount = "400+"
var highlightedWords = constants.highlightedWords
var quotes = constants.quotes
var titles = constants.titles
var globalSpins = 0
var commitCount = "500+"
var lanyardData = undefined
var uptime = Date.now()
var reloads = 0
function firstToUpper(str) {
return str.charAt(0).toUpperCase() + str.slice(1)
}
var thumborURL = "https://thumbor.violets-purgatory.dev/unsafe/"
var imgExtension = "png"
var thumborArgs = `filters:format(${imgExtension})/`
function timeFormatter(seconds) {
seconds = Math.ceil(seconds)
var minutes = Math.ceil(seconds / 60)
var hours = Math.floor(minutes / 60)
if (seconds <= 60) {
return 'about ' + seconds + ' seconds'
} else if (minutes < 60) {
return `${minutes} Minutes`
}
return `${hours} hours and ${minutes % 60} minutes`
}
function converter(html, query) {
var config = JSON.parse(fs.readFileSync(path.join(__dirname, 'config/config.json')))
reloads += 1
var startTime = Date.now()
while (html.includes("{PATH_")) {
var pagePath = html.substring(html.indexOf("{PATH_"))
pagePath = pagePath.substring(6, pagePath.indexOf('}'))
@ -33,20 +62,32 @@ function converter(html, query) {
var statusText = ""
if (lanyardData) {
var statusData = config.discStatuses[lanyardData.discord_status]
var statusData = constants.discStatuses[lanyardData.discord_status]
var username = lanyardData.discord_user.username
if (lanyardData.activities[0] && lanyardData.activities[0].type == 4) {
var statusText = `<hr><p>${lanyardData.activities[0].state}</p>`
}
} else {
var statusData = config.discStatuses.offline
var statusData = constants.discStatuses.offline
var username = "bingus_violet"
}
var time = new Date(Date.now())
var bnchName = "Beta"
var bnchSub = "beta."
if (process.env.BRANCH == "dev") {
bnchName = "Stable"
bnchSub = ""
}
var replacers = {
"ALL_KEYWORDS": undefined,
"ALL_HIGHLIGHTS": Object.keys(highlightedWords).join(", "),
"BRANCH_NAME": bnchName,
"BRANCH_SUB": bnchSub,
"COMMIT_COUNT": commitCount,
"RANDOM_QUOTE": quotes[Math.floor(Math.random() * quotes.length)],
"QUOTE_COUNT": quotes.length,
@ -57,8 +98,25 @@ function converter(html, query) {
"UPTIME": uptime,
"TOPBAR": `<div id="topbar"><h3><a href="/socials">Socials</a></h3></div>`,
"DISCORD_USER": username,
"CUSTOM_STATUS": statusText
"CUSTOM_STATUS": statusText,
"SELECTED_VIDEO": () => {
if (config.dailyVideoURL) {
return `<h2><hr>Random video!</h2><p>I would call it random <em>daily</em> video but its not at all daily...</p>
<br>
<video controls src="${config.dailyVideoURL}"></video>`
}
return ``
},
"SPINCOUNT": globalSpins,
"UPTIME": timeFormatter((Date.now() - uptime) / 1000),
"RELOAD_COUNT": reloads,
"WEATHER_MODIFIER": "",
"WEATHER_TEXT": "",
"ANNOUNCEMENT": fs.readFileSync(path.join(__dirname, "config/announcement.html")),
"CACHED_IMAGES": fs.readdirSync(path.join(__dirname, "cached")).length.toString()
}
replacers.ALL_KEYWORDS = "{" + Object.keys(replacers).join("}{") + "} "
var rpTable = Object.keys(replacers)
@ -69,6 +127,7 @@ function converter(html, query) {
var bodyHTML = html.substring(html.indexOf("<body>") + 6, html.lastIndexOf("</body>"))
var highTable = Object.keys(highlightedWords)
for (let index = 0; index < highTable.length; index++) {
var term = highTable[index];
var replacement = `<span style="color: ${highlightedWords[term]}">${term}</span>`
@ -82,41 +141,21 @@ function converter(html, query) {
html = html.substring(0, html.indexOf("<body>")) + bodyHTML + html.substring(html.indexOf("</body>") + 7)
var weathers = ["rain", "none"]
var weather = weathers[time.getDate() % weathers.length]
if (weather == "rain" || "rain" in query || "hardRain" in query) {
html = html.replaceAll("{WEATHER_MODIFIER}", weatherGenerator.makeRain("hardRain" in query))
html = html.replaceAll("{WEATHER_TEXT}", `The rain is so pretty... <a href="?hardRain">I wish I saw it more...</a>`)
} else {
html = html.replaceAll("{WEATHER_MODIFIER}", "")
html = html.replaceAll("{WEATHER_TEXT}", "")
}
html = html.replaceAll("{LOAD_TIME}", (Date.now() - startTime).toString() + "ms")
return html
}
module.exports = {
getActivities: function () {
return activityToHTML.activitiesToHTML(lanyardData, cachedImages)
return minify.minify(activityToHTML.activitiesToHTML(lanyardData, cachedImages))
},
middleWare: function (req, res, next) {
var filePath = (req.baseUrl + req.path).trim()
if (filePath.includes("cached") || filePath.includes("imgs")) {
filePath = path.join(__dirname, 'static', filePath)
res.send(fs.readFileSync(filePath))
return
}
if (filePath.includes(".")) {
} else {
if (!filePath.includes(".")) {
if (filePath.charAt(filePath.length - 1) != '/') {
res.redirect(filePath + '/')
return
@ -127,13 +166,24 @@ module.exports = {
filePath = path.join(__dirname, 'static', filePath || 'index.html')
if (fs.existsSync(filePath)) {
var data = fs.readFileSync(filePath).toString()
if (req.path.includes(".css")) {
res.setHeader("Content-Type", "text/css")
} else if (!req.path.includes(".woff2")) {
res.contentType(path.basename(filePath))
// if (req.path.includes(".css")) {
// res.setHeader("Content-Type", "text/css")
// } else if (!req.path.includes(".woff2")) {
// data = converter(data, req.query)
// }
if (filePath.includes(".html")) {
data = converter(data, req.query)
}
res.send(minify.minify(data))
if (!filePath.includes(".js")) {
data = minify.minify(data)
}
res.send(data)
} else {
res.status(404).send(`
<link rel="stylesheet" href="/style.css">
@ -145,10 +195,19 @@ module.exports = {
}
async function updateCommits() {
var codebergResponse = await (await fetch(`https://codeberg.org/Bingus_Violet/Violets-Purgatory/src/branch/${process.env.BRANCH || "origin"}`)).text()
var commits = codebergResponse.substring(0, codebergResponse.indexOf("Commits"))
var siteResponse = await (await fetch(`https://git.violets-purgatory.dev/bingus_violet/violets-purgatory/src/branch/${process.env.BRANCH || "origin"}`)).text()
var commits = siteResponse.substring(0, siteResponse.indexOf("Commits"))
commits = commits.substring(commits.lastIndexOf("<b>") + 3, commits.lastIndexOf("</b>"))
commitCount = commits
// ^ this works for Forgejo (basically everything i use that isnt Github E.G. Codeberg)
// commits = commits.substring(commits.lastIndexOf(">") + 1)
// ^ This works for Github (fuck you Github)
commitCount = commits.toString()
if (process.env.BRANCH == "dev") {
commitCount += " | Beta site"
}
}
updateCommits()
@ -158,7 +217,7 @@ updateCommits()
var lastLanyardUpdate = Date.now()
var lastPong = 0
var activityImages = config.activityImages
var activityImages = constants.activityImages
var cachedImages = {}
function get_img_url(activity, size = "large_image") {
@ -196,7 +255,7 @@ function socketeer() {
console.log("Connection Closed. Attempting Reconnect in 30 seconds.")
setTimeout(() => {
socketeer()
}, 3000);
}, 30000);
})
function ping(dur) {
@ -230,12 +289,11 @@ function socketeer() {
if (get_img_url(activity)) {
var url = get_img_url(activity)
var fn = Math.ceil(Math.random() * 100_000_000_000).toString()
var fp = path.join(__dirname, 'static/cached', fn)
var fn = Math.ceil(Math.random() * 100_000_000_000).toString() + "." + imgExtension
var fp = path.join(__dirname, 'cached', fn)
if (!cachedImages[url]) {
const response = await (await fetch(url)).arrayBuffer()
const response = await (await fetch(thumborURL + "200x200/" + thumborArgs + url)).arrayBuffer()
fs.writeFileSync(fp, Buffer.from(response))
cachedImages[url] = fn
@ -244,11 +302,11 @@ function socketeer() {
if (get_img_url(activity, "small_image")) {
var url = get_img_url(activity, "small_image")
var fn = Math.ceil(Math.random() * 100_000_000_000).toString()
var fp = path.join(__dirname, 'static/cached', fn)
var fn = Math.ceil(Math.random() * 100_000_000_000).toString() + "." + imgExtension
var fp = path.join(__dirname, 'cached', fn)
if (!cachedImages[url]) {
const response = await (await fetch(url)).arrayBuffer()
const response = await (await fetch(thumborURL + "64x64/" + thumborArgs + url)).arrayBuffer()
fs.writeFileSync(fp, Buffer.from(response))
@ -257,6 +315,8 @@ function socketeer() {
}
}
} else if (data.op == 4) {
globalSpins = data.spins
}
})
}

View file

@ -1,19 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/global.css">
<link rel="stylesheet" href="/root.css">
<link rel="stylesheet" href="/subpage.css">
<title>Dynamic Disc Status</title>
</head>
<body>
<main>
<br>
<h1>Dynamicly Updating Discord Status</h1>
<p>This page is basically the same as the one present on the site, but it updates in real time! It basically only exists to serve my brain rot :P</p>
<h2><hr>How does it work?</h2>
<p>Its a little big buggy- Pretty much, it just keeps writing html to the client, and telling it the HTML never finished. Yeah. Not great. Very easily could cause a data leak i think? Idk, what do you think I am, a programmer????</p>

88
static/asahi/index.html Normal file
View file

@ -0,0 +1,88 @@
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" type="text/css" href="/style.css">
<link rel="stylesheet" type="text/css" href="/root.css">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Violet's Purgatory</title>
<meta name="darkreader-lock">
<meta content="Violet's Purgatory" property="og:title" />
<meta
content="Hello this is tje asahi a uhm uuhhh uhm ummmm uhmmm uhhh uhmmm ummm uhhh um!"
property="og:description" />
<meta content="./pfp.png" property="og:image" />
<meta content="#a200ff" data-react-helmet="true" name="theme-color" />
</head>
<body>
{WEATHER_MODIFIER}
<h1 class="animatedTitle">Welcome to my<span class="mainTitle" style="color: rgb(200, 150, 255)">Humble Abode</span>
</h1>
<main class="animatedMain">
<p>nice seeing you here! while you're at it, why not check out my socials or about me?</p>
<hr>
<div id="card">
<h2>Asahi</h2>
<div style="display: flex; justify-content: center; align-items: center;">
<div style="width: 50%;">
<img src="/imgs/asahiPFP.png" class="pfp">
</div>
<div style="width: 50%;">
<div style="float: left;">
<p>They/Them</p>
<p>Marcy & Violet &lt;3</p>
</div>
<!-- <p>THIS TEXT IS THE EPIC EXTREME FILLER TO TEST THE SITE'S FORMATTING :fire:</p> -->
</div>
</div>
<hr>
<div>
<p style="padding: 10px;">
<br>hi! my name is Asahi Lunya :3 i'm a bisexual enby who's a very queer mess
<br>i have interests in tech, aerospace, trains, art, and music! i'm also a privacy/security
enthusiast
<br>i'm currently learning many new things in my life, expanding my knowledge
<br>i hope you got to know me a little ^^
</p>
<!-- <a class="chip" href="https://beta.violets-purgatory.dev">Beta site</a>
<a class="chip" href="https://blog.violets-purgatory.dev">Blog</a> -->
<!-- <a class="chip" href="https://fs.violets-purgatory.dev">FileShare</a> -->
</div>
</div>
<h2><hr>Disclaimer!</h2>
<p>
This is NOT Asahi's real site! Please find it <a href="https://asahixp.pages.gay">here</a> instead!
</p>
<h2><hr>Quotes:</h2>
<p style="white-space: pre-wrap;">“literally anything from the 1995 movie 'Hackers' will absolutely fit here” -tyberry
"ooooo you like boys, ur a boykisser" -Elodie
"asahi is twink-esk in spirit. aspirational." -Juniper
"cute huggable nice huggable cute cute" -marcy
"Petting Asahi makes the world better” -yassie
"meow meow meow mrrrp nya~" -gettie
"cute and queer catenby that functions as fedi's algorithm on the side" -7331
"sometimes this one still forgets how friendly some people are here… like Asahi, for instance!” -Ariadne
"Asahi wa sugoi desu ne?" -Bard
"this is a quote :3c" -Ukko
"best Asahi I've ever met (awww!)" -Kristina
"asahi is friend shaped and they have a good heart" -Drew
"10/10 will give loving headpats and will protect them!" -Natsura</p>
</main>
</body>
</html>

51
static/faq/index.html Normal file
View file

@ -0,0 +1,51 @@
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" type="text/css" href="/style.css">
<link rel="stylesheet" type="text/css" href="/subpage.css">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Violet's Purgatory</title>
<meta name="darkreader-lock">
<meta content="FAQ - Violet's Purgatory" property="og:title" />
<meta content="Some not-so frequently asked questions with some not-so frequently answered answers!!!" property="og:description" />
<meta content="https://api.violets-purgatory.dev/v1/pfp" property="og:image" />
<meta content="#a200ff" data-react-helmet="true" name="theme-color" />
</head>
<body>
{WEATHER_MODIFIER}
<a href="../" class="chip">Home</a>
<a href="../socials" class="chip">Socials</a>
<a href="../stats" class="chip">Stats</a>
<h1>FAQ (and more!)</h1>
<div class="mainDiv">
<h2><br>1. Questions & Answers<hr></h2>
<main>
<h3>Why are so many words colored?</h3>
<p>I like the way it looks to have certain words be highlighted, makes it look fancier and easier to read (imo). So, when I remade this site, I went ahead and added an <em>Automatic Word Highlighting System!</em> This allows me have words automatically highlighted, on the server side, without having to do it in the code manually. Here's the current list of highlighted words:</p>
<p class="textBlock">{ALL_HIGHLIGHTS}</p>
<br>
<h3>I thought the site doesn't use Javascript? So why is it there?</h3>
<p>Originally, all my sites were completely Javascript free. As of late, though, I decided to add Javascript to this one. Javascript will <b><em>NEVER</em></b> be a requirement on this site. Javascript will ONLY be used where nessacary, and I will do everything possible to make the experience indistinguishable.</p>
</main>
<p>For example, things such as the song duration bar on my activities are pure HTML, using some mathy NodeJS-generated CSS animations! The only thing the site uses Javascript for right now, is setting the scroll to the top of the page on reload, and real time updating discord activities.</p>
<h3>Why are there 4 CSS files?</h3>
<p>Originally the idea was that I could organize the site with:</p>
<ol>
<li>CSS for the main page</li>
<li>CSS for the subsites</li>
<li>Global CSS</li>
</ol>
<p>Slowly, though, they're slowly all merging into one CSS file, so I can't be sure it'll stay this way for long.</p>
<p>As for the "unused" style.css, though, that exists for compatibility reasons- many of my older sites just grabbed CSS from the site for their css. This has been <b>mostly</b> fixed, but the FileShare still does, so it'll stay until I finally give the FileShare its own CSS.</p>
</div>
</body>
</html>

View file

@ -1,160 +0,0 @@
/*
This is the GLOBAL css file.
Any changes made in here, will apply to the ENTIRE site.
Only put changes here if you intend to put changes across
the whole site!
*/
@font-face {
font-display: swap;
font-family: 'Rubik';
font-style: normal;
font-weight: 400;
src: url('./fonts/rubik-v28-latin-regular.woff2') format('woff2');
}
* {
font-family: 'Rubik', Verdana, Geneva, Tahoma, sans-serif;
padding: 0;
margin: 0;
font-weight: 400;
text-align: center;
color: white;
}
#topbar {
background-color: rgb(75, 50, 125);
width: 100%;
padding: 1vh 0px;
position: sticky;
left: 0px;
top: 0px;
z-index: 10;
opacity: 0.5;
transform: scale(1);
transition: all 2s cubic-bezier(0.075, 0.82, 0.165, 1);
}
#topbar:hover {
opacity: 1;
transform: scale(1.05);
}
#topbar>* {
display: inline-block;
font-size: 1.5rem;
}
main:nth-of-type(1) {
width: 95%;
max-width: 1000px;
margin: auto;
}
body {
overflow-x: hidden;
background-color: rgb(55, 4, 75);
background: linear-gradient(rgb(40, 4, 75), black);
background-attachment: local;
min-height: 100vh;
animation: hideContent 2.5s;
}
a {
color: rgb(175, 225, 255);
display: inline-block;
transition: 1.5s all cubic-bezier(0.075, 0.82, 0.165, 1);
}
h3 {
font-size: 1.5rem;
padding: 10px;
}
@media screen and (min-width: 650px) {
.grid-container {
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 10px;
}
.grid-child {
margin: auto;
align-content: center;
/* border: 2px white solid; */
}
}
.chip {
position: relative;
z-index: 3;
font-size: 1.3rem;
border: 2px gray solid;
border-radius: 6px;
background-color: black;
padding: 8px;
margin: 3px;
display: inline-block;
transform: scale(0.95);
transition: transform 1.25s cubic-bezier(0.075, 0.82, 0.165, 1), background-color 2s cubic-bezier(0.075, 0.82, 0.165, 1);
}
a.chip {
text-decoration: none;
/* background-image: linear-gradient(rgb(175, 225, 255), rgb(175, 225, 255));
background-size: calc(100% - 15px) 2px;
background-position: 50% 87.5%;
background-repeat: no-repeat; */
}
.chip:hover {
background-color: rgb(10, 0, 25);
transform: scale(1);
/* font-size: 1.35rem; */
border-color: white;
transition: transform 0.75s cubic-bezier(0.075, 0.82, 0.165, 1), background-color 3s cubic-bezier(0.075, 0.82, 0.165, 1);
}
.chip:hover>.smallimg {
background-color: rgb(10, 0, 25);
}
hr {
color: white;
border: white solid;
opacity: 0.25;
border-width: 2px;
margin: 15px 10%;
/* background-color: none; */
}
p {
font-size: 1.25rem;
margin: 5px;
line-height: 2rem;
text-wrap: pretty;
}
img {
width: 100%;
/* max-width: 135px; */
transition: all 2s cubic-bezier(0.075, 0.82, 0.165, 1);
}
h2 {
font-size: 1.8rem;
}
.note {
color: darkgray;
font-size: 1rem;
}
.striked {
text-decoration: line-through;
text-decoration-color: white;
}

View file

@ -2,8 +2,13 @@
<html lang="en">
<head>
<link rel="stylesheet" type="text/css" href="./global.css">
<link rel="stylesheet" type="text/css" href="./style.css">
<link rel="stylesheet" type="text/css" href="./root.css">
<noscript>
<link rel="stylesheet" href="./noScript.css">
</noscript>
<script src="./main.js"></script>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
@ -13,50 +18,77 @@
<meta name="darkreader-lock">
<meta content="Violet's Purgatory" property="og:title" />
<meta content="Hi, I'm Violet, a 15 year old web developer. My site lists my projects, passions, and where you can find me, so please visit!" property="og:description" />
<meta content="Hi, I'm Violet, a 15 year old web & game developer. My site has info about me, so please visit!" property="og:description" />
<meta content="https://api.violets-purgatory.dev/v1/pfp" property="og:image" />
<meta content="#a200ff" data-react-helmet="true" name="theme-color" />
</head>
<body>
{WEATHER_MODIFIER}
<h1 class="animatedTitle">Welcome to <span class="mainTitle">Violet's Purgatory</span><span class="note">Commit {COMMIT_COUNT}</span><br></h1>
<h1 class="animatedTitle">Welcome to <span class="mainTitle">Violet's Purgatory</span><span class="note">Commit {COMMIT_COUNT}</span></h1>
<main class="animatedMain">
<p>{RANDOM_QUOTE}</p>
<p>Make sure to check out this project on <a href="https://git.violets-purgatory.dev/bingus_violet/violets-purgatory">Forgejo</a>!</p>
<hr>
<div id="card">
<h2>{Violet}</h2>
<div style="display: flex; justify-content: center; align-items: center;">
<img src="https://api.violets-purgatory.dev/v1/pfp" class="pfp">
<div>
<p>They/Them</p>
<p>Developer</p>
<p>{DISCORD_STATUS}</p>
<div style="width: 50%;">
<img draggable="false" src="https://api.violets-purgatory.dev/v1/pfp" class="pfp">
</div>
<div style="width: 50%;">
<div style="float: left;">
<p>They/Them</p>
<p>Developer</p>
<!-- <p>Dating Asahi &lt;3</p> -->
<p>{DISCORD_STATUS}</p>
<!-- <p>THIS TEXT IS THE EPIC EXTREME FILLER TO TEST THE SITE'S FORMATTING :fire:</p> -->
</div>
</div>
</div>
<p class="noscript">Violet has been spun {SPINCOUNT} times!</p>
<p class="spinnyCount" style="display: none;">You have spun Violet <span class="localSpins">4</span> times!<br>
Everyone has spun Violet <span class="globalSpins">{SPINCOUNT}</span> times!</p>
<hr>
<div>
<p style="padding: 10px;">Hi! I'm Violet, a 15 year old web and game developer. <br> I aspire to make fast and Javascript free websites! I'm currently learning the Godot Engine, but most of my time recently has been spent learning NodeJS.</p>
<a class="chip" href="https://beta.violets-purgatory.dev">Beta site</a>
<p style="padding: 10px;">Hi! I'm Violet, a 15 year old web and game developer. I make server-sided dynamic websites, with no Javascript required! I'm currently making games in the Godot Engine, and my dynamic sites in NodeJS.</p>
<div class="linkContainer">
<a class="chip" href="./socials/">Socials</a>
<a class="chip" href="https://blog.violets-purgatory.dev">Blog</a>
<!-- <a class="chip" href="https://fs.violets-purgatory.dev">FileShare</a> -->
<!-- <a class="chip" href="./stats">Stats</a> -->
<a class="chip" href="./faq">Nerd FAQ</a>
<a class="chip" href="https://{BRANCH_SUB}violets-purgatory.dev">{BRANCH_NAME} site</a>
<a class="chip" href="https://fs.violets-purgatory.dev">FileShare</a>
</div>
{CUSTOM_STATUS}
</div>
</div>
<p>Make sure to check out this project on <a href="https://codeberg.org/bingus_violet/violets-purgatory">Codeberg</a>!</p>
<p>{RANDOM_QUOTE}</p>
{ACTIVITIES}
<div id="activityHtml">
{ACTIVITIES}
</div>
{ANNOUNCEMENT}
<h2><hr>Services</h2>
<p>List of services for public use hosted on Violet's Purgatory.</p>
<a href="https://sxng.violets-purgatory.dev" class="chip">SearXNG: sxng.violets-purgatory.dev</a>
<br>
<p class="chip">Matrix: matrix.violets-purgatory.dev</p>
<a class="chip">Matrix: matrix.violets-purgatory.dev</a>
<a href="https://element.violets-purgatory.dev" class="chip">Element: element.violets-purgatory.dev</a>
<a href="https://git.violets-purgatory.dev" class="chip">Forgejo: git.violets-purgatory.dev (contact me for an account)</a>
<a class="chip">Thumbor: thumbor.violets-purgatory.dev</a>
<hr>
<h1>Socials</h1>
{PATH_SOCIALS}
<br>
<h1><hr>FAQ</h1>
{PATH_FAQ}
{SELECTED_VIDEO}
{PATH_STATS}
<p>{WEATHER_TEXT}</p>
<br>
</main>

125
static/main.js Normal file
View file

@ -0,0 +1,125 @@
var catsOnMars = new Audio("/snds/cats on mars.mp3")
var whipLash = new Audio("/snds/johnny-test-whip-crack.mp3")
catsOnMars.loop = true
catsOnMars.volume = 0.25
whipLash.volume = 0.25
var sock
var spins = 1
var globalSpins = 0
var firsttimeDebounce = true
var spinWaiting = false
function spinLoop() {
spinWaiting = true
setTimeout(() => {
spinWaiting = false
var pfp = document.querySelector(".pfp")
if (!catsOnMars.paused) {
if (spins > 1) {
document.querySelector(".spinnyCount").style.display = "block"
document.querySelector(".localSpins").innerHTML = Math.ceil(spins - 1);
}
spins += 0.5
if (Math.round(spins) == spins && sock && sock.OPEN) {
document.querySelector(".pfp").src = "https://api.violets-purgatory.dev/v1/pfp?" + new Date().getTime()
sock.send(`{"op": 4}`)
console.log("Spin Sent!")
}
spinLoop()
}
}, 1000);
}
window.onbeforeunload = function () {
window.scrollTo(0, 0);
}
window.onload = function () {
window.scrollTo(0, 0);
var pfp = document.querySelector(".pfp")
pfp.addEventListener("mousedown", () => {
if (!spinWaiting) {
spinLoop();
}
catsOnMars.play()
pfp.style.animationName = "spinny"
pfp.style.scale = "1.1"
})
document.body.onmouseup = () => {
if (catsOnMars.currentTime != 0) {
catsOnMars.currentTime = 0
catsOnMars.pause()
whipLash.currentTime = 0
whipLash.play()
pfp.style.animationName = "unset"
pfp.style.scale = "1"
}
}
socketeer()
}
var lastPong = Date.now()
function ping(dur) {
sock.send(JSON.stringify({
op: 3
}))
setTimeout(() => {
ping(dur)
if (Date.now() - lastPong > 120000) {
sock.close()
console.log("Max duration since last pong exceeded- Closing socket.")
}
}, dur);
}
function socketeer() {
sock = new WebSocket('wss://api.violets-purgatory.dev')
sock.addEventListener("open", () => {
ping(30000)
})
sock.addEventListener("error", (error) => {
console.log(error)
})
sock.addEventListener("close", () => {
console.log("Connection Closed. Attempting Reconnect in 30 seconds.")
setTimeout(() => {
socketeer()
}, 30000);
})
sock.addEventListener("message", async(data) => {
data = data.data
data = JSON.parse(data)
if (data.op == 4) {
globalSpins = data.spins
if (firsttimeDebounce == true) {
firsttimeDebounce = false
document.querySelector(".globalSpins").innerHTML = globalSpins + 1;
} else {
document.querySelector(".globalSpins").innerHTML = globalSpins;
}
} else if (data.op == 0) {
var discFetch = await (await fetch("/discHTML")).text()
document.querySelector("#activityHtml").innerHTML = discFetch
} else if (data.op == 3) {
lastPong = Date.now()
} else {
console.log(data)
}
})
}

9
static/noScript.css Normal file
View file

@ -0,0 +1,9 @@
.pfp:active {
rotate: 7200deg;
transform: scale(1.15);
transition: all 5s cubic-bezier(0.445, 0.05, 0.55, 0.95) !important;
}
.noscript {
display: initial;
}

View file

@ -24,6 +24,14 @@
box-shadow: outset rgb(35, 20, 60) 0px 0px 20px;
}
.linkContainer {
display: flex;
flex-wrap: wrap;
max-width: 385px;
margin: auto;
justify-content: center;
}
.animatedTitle {
animation: mainText 2s cubic-bezier(0.075, 0.82, 0.165, 1);
margin: auto;
@ -40,60 +48,21 @@
.pfp {
border-radius: 15px;
border: darkgray 4px solid;
float: right;
width: 60%;
aspect-ratio: 1/1;
transform: scale(0.9);
border-radius: 50%;
rotate: 0deg;
user-select: none;
animation-duration: 2s;
animation-timing-function: linear;
animation-iteration-count: infinite;
}
.activity {
border-width: 3px;
border-radius: 10px;
overflow: hidden;
margin: auto;
padding: 0;
display: flex;
position: relative;
z-index: 3;
/* max-height: 200px; */
}
.activity>p {
width: 100%;
max-height: 100%;
overflow-wrap: anywhere;
text-overflow: ellipsis;
padding: 3px;
line-height: 1.5rem;
align-self: center;
}
.activity>img {
width: 40%;
aspect-ratio: 1/1;
object-fit: cover;
}
.activity>img:not(.smallimg) {
min-width: 150px;
max-width: 150px;
}
.activity>.smallimg {
width: 48px;
height: 48px;
position: absolute;
bottom: 0px;
left: 0px;
border-radius: 50px;
background: black;
padding: 5px;
/* border: 2px gray solid; */
transform: scale(0.9);
}
.activity>.smallimg:hover {
transform: scale(1);
.CLAlign {
display: inline-block;
}
img:not(.project-inner > div > img):not(.activity>img) {
@ -118,25 +87,14 @@ img:not(.project-inner > div > img):not(.activity>img) {
}
}
.lengthBar {
background-color: rgb(50, 40, 60);
display: inline-block;
width: 80%;
height: 10px;
padding: 0;
overflow: hidden;
border-radius: 5px;
margin-right: 1.9%;
}
@keyframes spinny {
0% {
rotate: 0deg;
}
.lengthBar>span {
margin: 0;
padding: 0;
width: 100%;
background-color: rgb(200, 200, 230);
height: 20px;
display: block;
position: relative;
100% {
rotate: 360deg;
}
}
@keyframes mainText {

View file

@ -2,8 +2,9 @@
<html lang="en">
<head>
<link rel="stylesheet" type="text/css" href="../global.css">
<link rel="stylesheet" type="text/css" href="../style.css">
<link rel="stylesheet" type="text/css" href="../subpage.css">
<script src="../main.js"></script>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
@ -13,35 +14,43 @@
<meta name="darkreader-lock">
<meta content="Socials - Violet's Purgatory" property="og:title" />
<meta content="This page lists all the social media I use :3" property="og:description" />
<meta content="This page lists all the platforms you can find me on!" property="og:description" />
<meta content="https://api.violets-purgatory.dev/v1/pfp" property="og:image" />
<meta content="#a200ff" data-react-helmet="true" name="theme-color" />
</head>
<body>
{WEATHER_MODIFIER}
<a href="../" class="chip">Home</a>
<a href="../faq" class="chip">FAQ</a>
<a href="../stats" class="chip">Stats</a>
<h1>Socials</h1>
<div class="mainDiv">
<hr>
<main>
<p>Here's most of the sites you can find me on-<br>if you needed that for some reason?</p>
<div class="grid-container">
<div class="grid-child">
<div>
<h3>Social Media</h3>
<a class="chip" href="https://floofy.city/@bingus_violet" rel="me">Fedi: bingus_violet&ZeroWidthSpace;@floofy.city</a>
<a class="chip" href="https://www.youtube.com/channel/UChcrBJNJLZucy3TPyGyAY2g">Youtube: {Violet}'s Fiasco</a>
<a class="chip" href="https://ko-fi.com/bingus_violet">Ko-fi: Bingus_{Violet}</a>
<a class="chip" href="https://void.lgbt/bingus_violet" rel="me">Fedi: bingus_violet&ZeroWidthSpace;@void.lgbt</a>
</div>
</div>
<div class="grid-child">
<div>
<h3>Chat</h3>
<a class="chip" href="https://matrix.to/#/@bingus_violet:matrix.violets-purgatory.dev">Matrix: @bingus_violet:&ZeroWidthSpace;matrix.violets-purgatory.dev</a>
<p class="chip">Discord: {DISCORD_USER}</p>
<p class="chip">Revolt: Bingus{Violet}#5573</p>
<a class="chip">Discord: {DISCORD_USER}</a>
<a class="chip">Revolt: Bingus{Violet}#5573</a>
</div>
</div>
<div class="grid-child">
<div>
<h3>Coding</h3>
<a class="chip" href="https://git.violets-purgatory.dev/bingus_violet/">Forgejo: bingus_violet (git.violets-purgatory.dev)</a>
<a class="chip" href="https://codeberg.org/Bingus_violet">Codeberg: bingus_violet</a>
<a class="chip" href="https://hub.docker.com/u/bingusviolet">Docker: bingusviolet</a>
<a class="chip" href="https://github.com/violets-puragtory">Github: violets-puragtory</a>
@ -55,8 +64,13 @@
</div>
</div>
<br>
<p class="note">Please note I am extremely unhinged and gay on Fedi, I don't use Github, and I barely understand Docker.</p>
</main>
<div id="activityHtml">
{ACTIVITIES}
</div>
{SELECTED_VIDEO}
</div>
</body>
</html>

52
static/stats/index.html Normal file
View file

@ -0,0 +1,52 @@
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" type="text/css" href="../style.css">
<link rel="stylesheet" type="text/css" href="../subpage.css">
<script src="../main.js"></script>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Violet's Purgatory</title>
<meta name="darkreader-lock">
<meta content="Stats - Violet's Purgatory" property="og:title" />
<meta content="This page just does a semi-intensive speed test on the site, and displays related statistics." property="og:description" />
<meta content="https://api.violets-purgatory.dev/v1/pfp" property="og:image" />
<meta content="#a200ff" data-react-helmet="true" name="theme-color" />
</head>
<body>
{WEATHER_MODIFIER}
<a href="../" class="chip">Home</a>
<a href="../socials" class="chip">Socials</a>
<a href="../faq" class="chip">FAQ</a>
<h1>Stats</h1>
<p>This is the <em>full</em> stats page! This page exists for testing the speed of site generation, and contains every keyword on Violet's Purgatory in a hidden div.</p>
<div style="display: none">
{ALL_KEYWORDS} {ALL_KEYWORDS} {ALL_KEYWORDS} {ALL_KEYWORDS} {ALL_KEYWORDS}
</div>
<main>
<h1><hr>Stats</h1>
<br>
<ul>
<li>Page generation time: {LOAD_TIME}</li>
<li>Uptime: {UPTIME}</li>
<li>Total reloads: {RELOAD_COUNT} <sup>*1</sup></li>
<li>Cached Images: {CACHED_IMAGES} <sup>*2</sup></li>
</ul>
<br><br><br>
<ol class="noteList">
<li>Increments by 1 <em>EVERY</em> time <em>ANY</em> page is loaded.</li>
<li>Cached Images are cleared on server restart.</li>
</ul>
</main>
</div>
</body>
</html>

View file

@ -1,10 +1,13 @@
/*
DO NOT MODIFY IF YOU ARE TRYING TO CHANGE THINGS ON THE SITE!!!
This exists for compatibility with services on Violet's purgatory
Soon, this will be removed, and replaced with a CDN or something...
/*
This is the GLOBAL css file.
Any changes made in here, will apply to the ENTIRE site.
Only put changes here if you intend to put changes across
the whole site!
*/
@import url("https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css");
:root {
color-scheme: dark;
}
@font-face {
font-display: swap;
@ -17,68 +20,85 @@ Soon, this will be removed, and replaced with a CDN or something...
* {
font-family: 'Rubik', Verdana, Geneva, Tahoma, sans-serif;
padding: 0;
margin: 0;
font-weight: 400;
text-align: center;
}
h1 {
color: rgb(225, 215, 255);
font-size: 2.5rem;
}
h2 {
font-size: 2rem;
color: rgb(225, 215, 255)
}
h3 {
font-size: 1.65rem
}
h3,
li {
color: white;
}
ul, ol {
display: inline-block
#topbar {
background-color: rgb(75, 50, 125);
width: 100%;
padding: 1vh 0px;
position: sticky;
left: 0px;
top: 0px;
z-index: 10;
opacity: 0.5;
transform: scale(1);
transition: all 2s cubic-bezier(0.075, 0.82, 0.165, 1);
}
li {
font-size: 1.2rem;
text-align: left;
#topbar:hover {
opacity: 1;
transform: scale(1.05);
}
body,
html {
overflow-x: hidden;
#topbar>* {
display: inline-block;
font-size: 1.5rem;
}
main:nth-of-type(1) {
width: 95%;
max-width: 1000px;
margin: auto;
background-color: rgb(15, 4, 45);
background: linear-gradient(rgb(20, 4, 55), black);
width: 100vw;
height: 100vh;
margin: 0;
padding: 0;
background-attachment: local;
}
body {
padding: 2.5%;
overflow-x: hidden;
background-color: rgb(55, 4, 75);
background: linear-gradient(rgb(40, 4, 75), black);
background-attachment: local;
animation: hideContent 2.5s;
}
.fadediv {
animation-name: fade-in;
animation-duration: .75s;
animation-timing-function: cubic-bezier(0.165, 0.84, 0.44, 1);
margin: auto;
max-width: 1000px;
body, html {
min-height: 100%;
}
a {
color: rgb(175, 225, 255);
display: inline-block;
transition: 1.5s all cubic-bezier(0.075, 0.82, 0.165, 1);
}
a[href] {
color: rgb(175, 225, 255);
}
h3 {
font-size: 1.5rem;
padding: 10px;
}
@media screen and (min-width: 650px) {
.grid-container {
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 10px;
}
.grid-child {
margin: auto;
align-content: center;
/* border: 2px white solid; */
}
}
.chip {
position: relative;
z-index: 3;
@ -93,9 +113,12 @@ a {
transition: transform 1.25s cubic-bezier(0.075, 0.82, 0.165, 1), background-color 2s cubic-bezier(0.075, 0.82, 0.165, 1);
}
.chip,
.chip>* {
a.chip {
text-decoration: none;
/* background-image: linear-gradient(rgb(175, 225, 255), rgb(175, 225, 255));
background-size: calc(100% - 15px) 2px;
background-position: 50% 87.5%;
background-repeat: no-repeat; */
}
.chip:hover {
@ -106,10 +129,47 @@ a {
transition: transform 0.75s cubic-bezier(0.075, 0.82, 0.165, 1), background-color 3s cubic-bezier(0.075, 0.82, 0.165, 1);
}
.chip:hover > .smallimg {
.chip:hover>.smallimg {
background-color: rgb(10, 0, 25);
}
hr {
color: white;
border: white solid;
opacity: 0.25;
border-width: 2px;
margin: 15px 10%;
/* background-color: none; */
}
p {
font-size: 1.25rem;
margin: 5px;
line-height: 2rem;
text-wrap: pretty;
}
img {
width: 100%;
/* max-width: 135px; */
}
h2 {
font-size: 1.8rem;
}
.note {
color: darkgray;
font-size: 1rem;
}
.striked {
text-decoration: line-through;
text-decoration-color: white;
}
.activity {
border-width: 3px;
border-radius: 10px;
@ -119,25 +179,36 @@ a {
display: flex;
position: relative;
z-index: 3;
/* max-height: 200px; */
}
.activity>p {
width: 100%;
overflow-wrap: break-word;
max-height: 100%;
overflow-wrap: anywhere;
text-overflow: ellipsis;
padding: 3px;
line-height: 1.5rem;
align-self: center;
}
.activity>img {
width: 128px;
width: 40%;
aspect-ratio: 1/1;
object-fit: cover;
}
.activity>img:not(.smallimg) {
min-width: 150px;
max-width: 150px;
}
.activity>.smallimg {
width: 64px;
height: 64px;
width: 48px;
height: 48px;
position: absolute;
bottom: 0px;
left: 72px;
left: 0px;
border-radius: 50px;
background: black;
padding: 5px;
@ -149,101 +220,17 @@ a {
transform: scale(1);
}
a:hover {
color: white;
transition: 0.5s all cubic-bezier(0.075, 0.82, 0.165, 1);
ul, ol {
display: inline-block
}
p {
color: white;
li {
font-size: 1.2rem;
padding: 0;
margin: 10px;
text-align: left;
}
img:not(.project-inner > div > img) {
width: 100%;
max-width: 135px;
transition: all 2s cubic-bezier(0.075, 0.82, 0.165, 1);
}
.pfp {
border-radius: 15px;
border: darkgray 4px solid;
width: 60%;
aspect-ratio: 1/1;
transform: scale(0.9);
border-radius: 50%;
}
.pfp:hover {
transform: scale(1);
border-color: rgb(255, 200, 255);
object-fit: cover;
}
.emoji {
width: 32px;
border-radius: 10%;
/* border: 1px gray solid; */
}
hr {
color: white;
opacity: 0.25;
border-width: 2px;
margin: 15px 10%;
}
#card {
background-color: rgb(15, 5, 80);
padding: 15px;
border: 2px white solid;
margin: 20px auto;
width: 95%;
max-width: 800px;
z-index: 3;
position: relative;
}
.me {
border-color: limegreen;
}
.project {
background-color: rgba(35, 35, 35, 0.8);
padding: 15px;
border: 2px gray solid;
margin: 20px 0px;
border-radius: 15px;
}
.project-inner {
overflow: hidden;
padding: 0;
margin: auto;
z-index: 3;
}
.project-inner>div>img {
object-fit: cover;
border: 2px white solid;
width: 80%;
max-width: 500px;
margin: auto;
border-radius: 10px;
}
.project>p {
display: inline-block;
}
.minipfp {
width: 70px;
display: inline-block;
margin-right: 10px;
border: 2px lightgray solid;
border-radius: 5px;
.noscript {
display: none;
}
.lengthBar {
@ -257,6 +244,16 @@ hr {
margin-right: 1.9%;
}
.textBlock {
color: rgb(255, 255, 255);
white-space: pre-wrap;
background-color: rgb(20, 20, 20);
border: 2px lightgray solid;
padding: 15px;
/* font-family: 'Source Code Pro', sans-serif; */
text-align: center;
}
.lengthBar>span {
margin: 0;
padding: 0;
@ -267,19 +264,29 @@ hr {
position: relative;
}
.note {
font-size: 0.95rem;
color: lightgray;
video {
width: 95%;
max-height: 90vh;
}
@keyframes fade-in {
0% {
opacity: 0;
transform: translateY(50vh);
}
sup {
color: gray;
}
100% {
opacity: 1;
transform: none;
}
.noteList {
list-style: none;
}
.noteList > * {
counter-increment: noteList;
color: gray;
font-size: 1rem;
}
.noteList > li::before {
content: "*" counter(noteList) ". ";
}
em {
color: inherit;
}

View file

@ -6,14 +6,18 @@
h1:nth-of-type(1) {
font-size: 2.5rem;
}
body {
padding: 5vh 0;
}
main {
.mainDiv {
animation: fadeUp 1s cubic-bezier(0.075, 0.82, 0.165, 1);
padding: 0 2.5vw;
max-width: 800px;
margin: auto;
}
@keyframes fadeUp {

View file

@ -1,103 +0,0 @@
module.exports = {
makeRain: function (hardRain) {
var html = ""
html += `<div class="rainStuff"><div class="rainContainer">`
html +=
`
<style>
#card {
background-color: rgba(50, 0, 90, 0.5);
backdrop-filter: blur(5px);
}
.rainStuff {
position: sticky;
top: 0;
height: 0;
z-index: -5;
}
.rainContainer {
height: 100vh;
width: 80vw;
top: 0px;
left: 10vw;
position: absolute;
overflow: hidden;
}
.rainDrop {
position: absolute;
width: 5px;
backdrop-filter: blur(5px);
background-color: rgba(0, 0, 255, 0.2);
height: 10vh;
visibility: hidden;
}
body {
background: linear-gradient(rgb(10, 10, 75), black);
}
</style>
`
var amount = 7
var iterationReducer = 3
if (hardRain) {
amount = 100
}
for (let index = 0; index < amount; index++) {
html += `<div class="rainDrop"></div>`
}
html += "<style>"
for (let index = 0; index < amount; index++) {
html += `
.rainDrop:nth-of-type(${index + 1}) {
animation: rainAnim${index} ${(Math.random() * 0.3) + (5 - iterationReducer)}s linear;
animation-iteration-count: infinite;
animation-delay: ${Math.round(Math.random() * 100) / 100}s;
}
`
var randos = []
for (let index = 0; index < 11; index++) {
randos.push(Math.round(Math.random() * 100))
}
html += `@keyframes rainAnim${index} { `
for (let index = 0; index < (iterationReducer * -3.5) + 14.5; index++) {
html += `
${index * iterationReducer}0% {
top: 110vh;
right: ${randos[index]}%;
visibility: hidden;
}
${index * iterationReducer}0.1% {
top: -10vh;
right: ${randos[index + 1]}%;
visibility: hidden;
}
${index * iterationReducer}0.2% {
visibility: visible;
}
`
}
// console.log(html)
html += `90.3% { visibility: hidden; }`
html += `}`
}
html += "</style>"
html += "</div></div>"
return html
}
}