mirror of
https://github.com/journey-ad/Moe-Counter.git
synced 2024-11-27 17:00:51 +01:00
feat: Add params validate
This commit is contained in:
parent
cc1881cc4f
commit
77cac2c504
@ -84,4 +84,4 @@ Glitch can use `.env` file, [documentation](https://help.glitch.com/hc/en-us/art
|
|||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
[MIT License](./LICENSE)
|
[MIT License](./LICENSE), excluding all themes
|
BIN
assets/img/back-to-top.png
Normal file
BIN
assets/img/back-to-top.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 37 KiB |
@ -34,6 +34,69 @@ h2, h3, h4, h5 {
|
|||||||
margin-bottom: .6em;
|
margin-bottom: .6em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.back-to-top {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 2;
|
||||||
|
right: -108px;
|
||||||
|
bottom: 0;
|
||||||
|
width: 108px;
|
||||||
|
height: 150px;
|
||||||
|
background: url('./img/back-to-top.png?v=1') no-repeat 0 0; /* artwork from https://www.pixiv.net/artworks/83996495 */
|
||||||
|
background-size: 108px 450px;
|
||||||
|
opacity: 0.6;
|
||||||
|
transition: opacity 0.3s, right 0.8s;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.back-to-top:hover {
|
||||||
|
background-position: 0 -150px;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.back-to-top.load {
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
.back-to-top.ani-leave {
|
||||||
|
background-position: 0 -150px;
|
||||||
|
animation: ani-leave 390ms ease-in-out;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
}
|
||||||
|
.back-to-top.leaved {
|
||||||
|
pointer-events: none;
|
||||||
|
background: none;
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
.back-to-top.ending {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.back-to-top.ending::after {
|
||||||
|
opacity: 1;
|
||||||
|
transition-delay: 0.35s;
|
||||||
|
}
|
||||||
|
.back-to-top::after {
|
||||||
|
content: '';
|
||||||
|
position: fixed;
|
||||||
|
z-index: 2;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 108px;
|
||||||
|
height: 150px;
|
||||||
|
background: url('./img/back-to-top.png?v=1') no-repeat 0 0;
|
||||||
|
background-size: 108px 450px;
|
||||||
|
background-position: 0 -300px;
|
||||||
|
transition: opacity 0.3s;
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes ani-leave {
|
||||||
|
0% {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translateX(108px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@media screen and (max-width: 900px) {
|
@media screen and (max-width: 900px) {
|
||||||
iframe {
|
iframe {
|
||||||
display: none;
|
display: none;
|
||||||
|
104
index.js
104
index.js
@ -3,9 +3,12 @@
|
|||||||
const config = require("config-yml");
|
const config = require("config-yml");
|
||||||
const express = require("express");
|
const express = require("express");
|
||||||
const compression = require("compression");
|
const compression = require("compression");
|
||||||
|
const { z } = require("zod");
|
||||||
|
|
||||||
const db = require("./db");
|
const db = require("./db");
|
||||||
const themify = require("./utils/themify");
|
const { themeList, getCountImage } = require("./utils/themify");
|
||||||
|
const { ZodValid } = require("./utils/zod");
|
||||||
|
const { randomArray } = require("./utils");
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
@ -15,56 +18,65 @@ app.set("view engine", "pug");
|
|||||||
|
|
||||||
app.get('/', (req, res) => {
|
app.get('/', (req, res) => {
|
||||||
const site = config.app.site || `${req.protocol}://${req.get('host')}`
|
const site = config.app.site || `${req.protocol}://${req.get('host')}`
|
||||||
res.render('index', { site })
|
res.render('index', {
|
||||||
|
site,
|
||||||
|
themeList,
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
// get the image
|
// get the image
|
||||||
app.get(["/@:name", "/get/@:name"], async (req, res) => {
|
app.get(["/@:name", "/get/@:name"],
|
||||||
const { name } = req.params;
|
ZodValid({
|
||||||
const { theme = "moebooru", padding = 7, pixelated = '1', darkmode = 'auto' } = req.query;
|
params: z.object({
|
||||||
const isPixelated = pixelated === '1';
|
name: z.string().max(32),
|
||||||
|
}),
|
||||||
|
query: z.object({
|
||||||
|
theme: z.string().default("moebooru"),
|
||||||
|
padding: z.coerce.number().min(0).max(32).default(7),
|
||||||
|
offset: z.coerce.number().min(-500).max(500).default(0),
|
||||||
|
scale: z.coerce.number().min(0.1).max(2).default(1),
|
||||||
|
pixelated: z.enum(["0", "1"]).default("1"),
|
||||||
|
darkmode: z.enum(["0", "1", "auto"]).default("auto")
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
async (req, res) => {
|
||||||
|
const { name } = req.params;
|
||||||
|
let { theme = "moebooru", ...rest } = req.query;
|
||||||
|
|
||||||
if (name.length > 32) {
|
// This helps with GitHub's image cache
|
||||||
res.status(400).send("name too long");
|
res.set({
|
||||||
return;
|
"content-type": "image/svg+xml",
|
||||||
|
"cache-control": "max-age=0, no-cache, no-store, must-revalidate",
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await getCountByName(name);
|
||||||
|
|
||||||
|
if (name === "demo") {
|
||||||
|
res.set("cache-control", "max-age=31536000");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (theme === "random") {
|
||||||
|
theme = randomArray(Object.keys(themeList));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the generated SVG as the result
|
||||||
|
const renderSvg = getCountImage({
|
||||||
|
count: data.num,
|
||||||
|
theme,
|
||||||
|
...rest
|
||||||
|
});
|
||||||
|
|
||||||
|
res.send(renderSvg);
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
data,
|
||||||
|
`theme: ${theme}`,
|
||||||
|
`ip: ${req.headers['x-forwarded-for'] || req.connection.remoteAddress}`,
|
||||||
|
`ref: ${req.get("Referrer") || null}`,
|
||||||
|
`ua: ${req.get("User-Agent") || null}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
);
|
||||||
if (padding > 32) {
|
|
||||||
res.status(400).send("padding too long");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This helps with GitHub's image cache
|
|
||||||
res.set({
|
|
||||||
"content-type": "image/svg+xml",
|
|
||||||
"cache-control": "max-age=0, no-cache, no-store, must-revalidate",
|
|
||||||
});
|
|
||||||
|
|
||||||
const data = await getCountByName(name);
|
|
||||||
|
|
||||||
if (name === "demo") {
|
|
||||||
res.set("cache-control", "max-age=31536000");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send the generated SVG as the result
|
|
||||||
const renderSvg = themify.getCountImage({
|
|
||||||
count: data.num,
|
|
||||||
theme,
|
|
||||||
padding,
|
|
||||||
darkmode,
|
|
||||||
pixelated: isPixelated
|
|
||||||
});
|
|
||||||
|
|
||||||
res.send(renderSvg);
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
data,
|
|
||||||
`theme: ${theme}`,
|
|
||||||
`ip: ${req.headers['x-forwarded-for'] || req.connection.remoteAddress}`,
|
|
||||||
`ref: ${req.get("Referrer") || null}`,
|
|
||||||
`ua: ${req.get("User-Agent") || null}`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// JSON record
|
// JSON record
|
||||||
app.get("/record/@:name", async (req, res) => {
|
app.get("/record/@:name", async (req, res) => {
|
||||||
|
@ -18,6 +18,10 @@
|
|||||||
"image-size": "^0.8.3",
|
"image-size": "^0.8.3",
|
||||||
"mime-types": "^2.1.27",
|
"mime-types": "^2.1.27",
|
||||||
"mongoose": "^5.9.28",
|
"mongoose": "^5.9.28",
|
||||||
"pug": "^3.0.0"
|
"pug": "^3.0.0",
|
||||||
|
"zod": "^3.23.8"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "16.x"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
7
pnpm-lock.yaml
generated
7
pnpm-lock.yaml
generated
@ -29,6 +29,9 @@ dependencies:
|
|||||||
pug:
|
pug:
|
||||||
specifier: ^3.0.0
|
specifier: ^3.0.0
|
||||||
version: 3.0.3
|
version: 3.0.3
|
||||||
|
zod:
|
||||||
|
specifier: ^3.23.8
|
||||||
|
version: 3.23.8
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
@ -1665,3 +1668,7 @@ packages:
|
|||||||
y18n: 3.2.2
|
y18n: 3.2.2
|
||||||
yargs-parser: 2.4.1
|
yargs-parser: 2.4.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/zod@3.23.8:
|
||||||
|
resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==}
|
||||||
|
dev: false
|
||||||
|
5
utils/index.js
Normal file
5
utils/index.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module.exports = {
|
||||||
|
randomArray: (arr) => {
|
||||||
|
return arr[Math.floor(Math.random() * arr.length)]
|
||||||
|
},
|
||||||
|
}
|
@ -14,10 +14,10 @@ fs.readdirSync(themePath).forEach(theme => {
|
|||||||
const imgList = fs.readdirSync(path.resolve(themePath, theme))
|
const imgList = fs.readdirSync(path.resolve(themePath, theme))
|
||||||
imgList.forEach(img => {
|
imgList.forEach(img => {
|
||||||
const imgPath = path.resolve(themePath, theme, img)
|
const imgPath = path.resolve(themePath, theme, img)
|
||||||
const name = path.parse(img).name
|
const num = path.parse(img).name
|
||||||
const { width, height } = sizeOf(imgPath)
|
const { width, height } = sizeOf(imgPath)
|
||||||
|
|
||||||
themeList[theme][name] = {
|
themeList[theme][num] = {
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
data: convertToDatauri(imgPath)
|
data: convertToDatauri(imgPath)
|
||||||
@ -32,8 +32,12 @@ function convertToDatauri(path) {
|
|||||||
return `data:${mime};base64,${base64}`
|
return `data:${mime};base64,${base64}`
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCountImage({ count, theme = 'moebooru', padding = 7, pixelated = true, darkmode = 'auto' }) {
|
function getCountImage(params) {
|
||||||
|
let { count, theme = 'moebooru', padding = 7, offset = 0, scale = 1, pixelated = '1', darkmode = 'auto' } = params
|
||||||
|
|
||||||
if (!(theme in themeList)) theme = 'moebooru'
|
if (!(theme in themeList)) theme = 'moebooru'
|
||||||
|
padding = parseInt(padding, 10)
|
||||||
|
offset = parseInt(offset, 10)
|
||||||
|
|
||||||
// This is not the greatest way for generating an SVG but it'll do for now
|
// This is not the greatest way for generating an SVG but it'll do for now
|
||||||
const countArray = count.toString().padStart(padding, '0').split('')
|
const countArray = count.toString().padStart(padding, '0').split('')
|
||||||
@ -42,7 +46,9 @@ function getCountImage({ count, theme = 'moebooru', padding = 7, pixelated = tru
|
|||||||
let x = 0, y = 0
|
let x = 0, y = 0
|
||||||
|
|
||||||
const defs = uniqueChar.reduce((ret, cur) => {
|
const defs = uniqueChar.reduce((ret, cur) => {
|
||||||
const { width, height, data } = themeList[theme][cur]
|
let { width, height, data } = themeList[theme][cur]
|
||||||
|
width *= scale
|
||||||
|
height *= scale
|
||||||
|
|
||||||
if (height > y) y = height
|
if (height > y) y = height
|
||||||
|
|
||||||
@ -53,19 +59,23 @@ function getCountImage({ count, theme = 'moebooru', padding = 7, pixelated = tru
|
|||||||
}, '')
|
}, '')
|
||||||
|
|
||||||
const parts = countArray.reduce((ret, cur) => {
|
const parts = countArray.reduce((ret, cur) => {
|
||||||
const { width } = themeList[theme][cur]
|
let { width } = themeList[theme][cur]
|
||||||
|
width *= scale
|
||||||
|
|
||||||
const image = `${ret}
|
const image = `${ret}
|
||||||
<use x="${x}" xlink:href="#${cur}" />`
|
<use x="${x}" xlink:href="#${cur}" />`
|
||||||
|
|
||||||
x += width
|
x += width + offset
|
||||||
|
|
||||||
return image
|
return image
|
||||||
}, '')
|
}, '')
|
||||||
|
|
||||||
|
// Fix the last image offset
|
||||||
|
x -= offset
|
||||||
|
|
||||||
const style = `
|
const style = `
|
||||||
svg {
|
svg {
|
||||||
${pixelated ? 'image-rendering: pixelated;' : ''}
|
${pixelated === '1' ? 'image-rendering: pixelated;' : ''}
|
||||||
${darkmode === '1' ? 'filter: brightness(.6);' : ''}
|
${darkmode === '1' ? 'filter: brightness(.6);' : ''}
|
||||||
}
|
}
|
||||||
${darkmode === 'auto' ? `@media (prefers-color-scheme: dark) { svg { filter: brightness(.6); } }` : ''}
|
${darkmode === 'auto' ? `@media (prefers-color-scheme: dark) { svg { filter: brightness(.6); } }` : ''}
|
||||||
@ -85,5 +95,6 @@ function getCountImage({ count, theme = 'moebooru', padding = 7, pixelated = tru
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
themeList,
|
||||||
getCountImage
|
getCountImage
|
||||||
}
|
}
|
||||||
|
50
utils/zod.js
Normal file
50
utils/zod.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
function parseError(error) {
|
||||||
|
const err = JSON.parse(error)[0];
|
||||||
|
|
||||||
|
return {
|
||||||
|
code: 400,
|
||||||
|
message: `The field \`${err.path[0]}\` is invalid. ${err.message}`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
ZodValid: ({ headers, params, query, body }) => {
|
||||||
|
const handler = (req, res, next) => {
|
||||||
|
if (headers) {
|
||||||
|
const result = headers.safeParse(req.headers);
|
||||||
|
if (!result.success) {
|
||||||
|
res.status(400).send(parseError(result.error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params) {
|
||||||
|
const result = params.safeParse(req.params);
|
||||||
|
if (!result.success) {
|
||||||
|
res.status(400).send(parseError(result.error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (query) {
|
||||||
|
const result = query.safeParse(req.query);
|
||||||
|
if (!result.success) {
|
||||||
|
res.status(400).send(parseError(result.error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (body) {
|
||||||
|
const result = body.safeParse(req.body);
|
||||||
|
if (!result.success) {
|
||||||
|
res.status(400).send(parseError(result.error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
return handler
|
||||||
|
}
|
||||||
|
}
|
147
views/index.pug
147
views/index.pug
@ -35,7 +35,7 @@ html
|
|||||||
h3 How to use
|
h3 How to use
|
||||||
p Set a unique id for your counter, replace
|
p Set a unique id for your counter, replace
|
||||||
code :name
|
code :name
|
||||||
| in the url, that's all.
|
| in the url, That's it!
|
||||||
|
|
||||||
h5 SVG address
|
h5 SVG address
|
||||||
code #{site}/@:name
|
code #{site}/@:name
|
||||||
@ -47,24 +47,15 @@ html
|
|||||||
code ![:name](#{site}/@:name)
|
code ![:name](#{site}/@:name)
|
||||||
|
|
||||||
h5 e.g.
|
h5 e.g.
|
||||||
<img src="#{site}/@index" alt="Moe Count!" />
|
<img src="#{site}/@index" alt="Moe Counter!" />
|
||||||
|
|
||||||
details#themes
|
details#themes(style='margin-top: 2em;')
|
||||||
summary#more_theme(onclick='_evt_push("click", "normal", "more_theme")')
|
summary#more_theme(onclick='_evt_push("click", "normal", "more_theme")')
|
||||||
h3(style='display: inline-block; margin: 0; cursor: pointer;') More theme✨
|
h3(style='display: inline-block; margin: 0; cursor: pointer;') More theme✨
|
||||||
p(style='margin: 0;') Just use the query parameters <code>theme</code>, like this: <code>#{site}/@:name?theme=moebooru</code>
|
p(style='margin: 0;') Just use the query parameters <code>theme</code>, like this: <code>#{site}/@:name?theme=moebooru</code>
|
||||||
h5 asoul
|
each theme in Object.keys(themeList)
|
||||||
<img src="#{site}/@demo?theme=asoul" alt="A-SOUL" />
|
h5 #{theme}
|
||||||
h5 moebooru
|
<img src="#{site}/@demo?theme=#{theme}" alt="#{theme}" />
|
||||||
<img src="#{site}/@demo?theme=moebooru" alt="Moebooru" />
|
|
||||||
h5 moebooru-h
|
|
||||||
<img src="#{site}/@demo?theme=moebooru-h" alt="Moebooru-Hentai" />
|
|
||||||
h5 rule34
|
|
||||||
<img src="#{site}/@demo?theme=rule34" alt="Rule34" />
|
|
||||||
h5 gelbooru
|
|
||||||
<img src="#{site}/@demo?theme=gelbooru" alt="Gelbooru" />
|
|
||||||
h5 gelbooru-h
|
|
||||||
<img src="#{site}/@demo?theme=gelbooru-h" alt="Gelbooru-Hentai" />
|
|
||||||
|
|
||||||
h3 Credits
|
h3 Credits
|
||||||
ul
|
ul
|
||||||
@ -74,14 +65,13 @@ html
|
|||||||
a(href='https://space.bilibili.com/703007996', target='_blank', title='A-SOUL_Official') A-SOUL
|
a(href='https://space.bilibili.com/703007996', target='_blank', title='A-SOUL_Official') A-SOUL
|
||||||
li
|
li
|
||||||
a(href='https://github.com/moebooru/moebooru', target='_blank', rel='nofollow') moebooru
|
a(href='https://github.com/moebooru/moebooru', target='_blank', rel='nofollow') moebooru
|
||||||
li
|
|
||||||
a(href='javascript:alert("!!! NSFW LINK !!!\\nPlease enter the url manually")') rule34.xxx
|
|
||||||
| NSFW
|
|
||||||
li
|
li
|
||||||
a(href='javascript:alert("!!! NSFW LINK !!!\\nPlease enter the url manually")') gelbooru.com
|
a(href='javascript:alert("!!! NSFW LINK !!!\\nPlease enter the url manually")') gelbooru.com
|
||||||
| NSFW
|
| NSFW
|
||||||
li
|
li
|
||||||
a(href='https://icons8.com/icon/80355/star', target='_blank', rel='nofollow') Icons8
|
a(href='https://icons8.com/icon/80355/star', target='_blank', rel='nofollow') Icons8
|
||||||
|
span
|
||||||
|
i And all booru site...
|
||||||
|
|
||||||
h3 Tool
|
h3 Tool
|
||||||
.tool
|
.tool
|
||||||
@ -89,37 +79,58 @@ html
|
|||||||
thead
|
thead
|
||||||
tr
|
tr
|
||||||
th Param
|
th Param
|
||||||
|
th Description
|
||||||
th Value
|
th Value
|
||||||
tbody
|
tbody
|
||||||
tr
|
tr
|
||||||
td
|
td
|
||||||
code name
|
code name
|
||||||
|
td Unique counter name
|
||||||
td
|
td
|
||||||
input#name(type='text', placeholder=':name')
|
input#name(type='text', placeholder=':name')
|
||||||
tr
|
tr
|
||||||
td
|
td
|
||||||
code theme
|
code theme
|
||||||
|
td Select a counter image theme, default is
|
||||||
|
code moebooru
|
||||||
td
|
td
|
||||||
select#theme
|
select#theme
|
||||||
option(value='asoul') asoul
|
option(value="random", selected) * random
|
||||||
option(value='moebooru') moebooru
|
each theme in Object.keys(themeList)
|
||||||
option(value='moebooru-h') moebooru-h
|
<option value="#{theme}">#{theme}</option>
|
||||||
option(value='rule34') rule34
|
|
||||||
option(value='gelbooru') gelbooru
|
|
||||||
option(value='gelbooru-h') gelbooru-h
|
|
||||||
tr
|
|
||||||
td
|
|
||||||
code pixelated
|
|
||||||
td
|
|
||||||
input#pixelated(type='checkbox', checked, style='margin: .5rem .75rem;')
|
|
||||||
tr
|
tr
|
||||||
td
|
td
|
||||||
code padding
|
code padding
|
||||||
|
td Set the minimum length, between 1-32, default is
|
||||||
|
code 7
|
||||||
td
|
td
|
||||||
input#padding(type='number', value='7', min='1', max='32', step='1', oninput='this.value = this.value.replace(/[^0-9]/g, "")')
|
input#padding(type='number', value='7', min='1', max='32', step='1', oninput='this.value = this.value.replace(/[^0-9]/g, "")')
|
||||||
|
tr
|
||||||
|
td
|
||||||
|
code offset
|
||||||
|
td Set the offset pixel value, between -500-500, default is
|
||||||
|
code 0
|
||||||
|
td
|
||||||
|
input#offset(type='number', value='0', min='-500', max='500', step='1', oninput='this.value = this.value.replace(/[^0-9|\-]/g, "")')
|
||||||
|
tr
|
||||||
|
td
|
||||||
|
code scale
|
||||||
|
td Set the image scale, between 0.1-2, default is
|
||||||
|
code 1
|
||||||
|
td
|
||||||
|
input#scale(type='number', value='1', min='0.1', max='2', step='0.1', oninput='this.value = this.value.replace(/[^0-9|\.]/g, "")')
|
||||||
|
tr
|
||||||
|
td
|
||||||
|
code pixelated
|
||||||
|
td Enable pixelated mode, Enum 0/1, default is
|
||||||
|
code 1
|
||||||
|
td
|
||||||
|
input#pixelated(type='checkbox', checked, style='margin: .5rem .75rem;')
|
||||||
tr
|
tr
|
||||||
td
|
td
|
||||||
code darkmode
|
code darkmode
|
||||||
|
td Enable dark mode, Enum 0/1/auto, default is
|
||||||
|
code auto
|
||||||
td
|
td
|
||||||
select#darkmode(name="darkmode")
|
select#darkmode(name="darkmode")
|
||||||
option(value="auto", selected) auto
|
option(value="auto", selected) auto
|
||||||
@ -132,7 +143,13 @@ html
|
|||||||
code#code(style='visibility: hidden; display: inline-block; margin-bottom: 1em;')
|
code#code(style='visibility: hidden; display: inline-block; margin-bottom: 1em;')
|
||||||
img#result(style='display: block;')
|
img#result(style='display: block;')
|
||||||
|
|
||||||
script.
|
p(style='margin-top: 2em;')
|
||||||
|
a(href='https://github.com/journey-ad/Moe-Counter', target='_blank', onclick='_evt_push("click", "normal", "go_github")') source code
|
||||||
|
|
||||||
|
div.back-to-top
|
||||||
|
|
||||||
|
script.
|
||||||
|
(function () {
|
||||||
var btn = document.getElementById('get'),
|
var btn = document.getElementById('get'),
|
||||||
img = document.getElementById('result'),
|
img = document.getElementById('result'),
|
||||||
code = document.getElementById('code')
|
code = document.getElementById('code')
|
||||||
@ -140,15 +157,13 @@ html
|
|||||||
btn.addEventListener('click', throttle(function() {
|
btn.addEventListener('click', throttle(function() {
|
||||||
var $name = document.getElementById('name'),
|
var $name = document.getElementById('name'),
|
||||||
$theme = document.getElementById('theme'),
|
$theme = document.getElementById('theme'),
|
||||||
$pixelated = document.getElementById('pixelated'),
|
|
||||||
$padding = document.getElementById('padding'),
|
$padding = document.getElementById('padding'),
|
||||||
|
$offset = document.getElementById('offset'),
|
||||||
|
$scale = document.getElementById('scale'),
|
||||||
|
$pixelated = document.getElementById('pixelated'),
|
||||||
$darkmode = document.getElementById('darkmode')
|
$darkmode = document.getElementById('darkmode')
|
||||||
|
|
||||||
var name = $name.value ? $name.value.trim() : ''
|
var name = $name.value ? $name.value.trim() : ''
|
||||||
var theme = $theme.value || 'moebooru'
|
|
||||||
var pixelated = $pixelated.checked ? '1' : '0'
|
|
||||||
var padding = $padding.value || '7'
|
|
||||||
var darkmode = $darkmode.value || 'auto'
|
|
||||||
|
|
||||||
if(!name) {
|
if(!name) {
|
||||||
alert('Please input counter name.')
|
alert('Please input counter name.')
|
||||||
@ -157,7 +172,19 @@ html
|
|||||||
|
|
||||||
party.confetti(this, { count: party.variation.range(20, 40) });
|
party.confetti(this, { count: party.variation.range(20, 40) });
|
||||||
|
|
||||||
img.src = `#{site}/@${name}?theme=${theme}&pixelated=${pixelated}&padding=${padding}&darkmode=${darkmode}`
|
var params = {
|
||||||
|
name: $name.value ? $name.value.trim() : '',
|
||||||
|
theme: $theme.value || 'moebooru',
|
||||||
|
padding: $padding.value || '7',
|
||||||
|
offset: $offset.value || '0',
|
||||||
|
scale: $scale.value || '1',
|
||||||
|
pixelated: $pixelated.checked ? '1' : '0',
|
||||||
|
darkmode: $darkmode.value || 'auto',
|
||||||
|
}
|
||||||
|
|
||||||
|
var query = new URLSearchParams(params).toString()
|
||||||
|
|
||||||
|
img.src = `#{site}/@${name}?${query}`
|
||||||
code.textContent = img.src
|
code.textContent = img.src
|
||||||
code.style.visibility = 'visible'
|
code.style.visibility = 'visible'
|
||||||
|
|
||||||
@ -220,6 +247,50 @@ html
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
p(style='margin-top: 2em;')
|
script.
|
||||||
a(href='https://github.com/journey-ad/Moe-Counter', target='_blank', onclick='_evt_push("click", "normal", "go_github")') source code
|
(function () {
|
||||||
|
var isShow = false, lock = false;
|
||||||
|
var btn = document.querySelector('.back-to-top');
|
||||||
|
|
||||||
|
window.addEventListener('scroll', function () {
|
||||||
|
if (lock) return;
|
||||||
|
|
||||||
|
if (document.body.scrollTop >= 1000) {
|
||||||
|
if (!isShow) btn.classList.add('load');
|
||||||
|
isShow = true;
|
||||||
|
} else {
|
||||||
|
if (isShow) {
|
||||||
|
btn.classList.remove('load');
|
||||||
|
isShow = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
btn.addEventListener('click', function () {
|
||||||
|
lock = true;
|
||||||
|
btn.classList.add('ani-leave');
|
||||||
|
|
||||||
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||||
|
|
||||||
|
setTimeout(function () {
|
||||||
|
btn.classList.remove('ani-leave');
|
||||||
|
btn.classList.add('leaved');
|
||||||
|
}, 390);
|
||||||
|
|
||||||
|
setTimeout(function () {
|
||||||
|
btn.classList.add('ending');
|
||||||
|
}, 120);
|
||||||
|
|
||||||
|
setTimeout(function () {
|
||||||
|
btn.classList.remove('load');
|
||||||
|
}, 1500);
|
||||||
|
|
||||||
|
setTimeout(function () {
|
||||||
|
lock = false;
|
||||||
|
isShow = false;
|
||||||
|
btn.classList.remove('leaved', 'ending');
|
||||||
|
}, 2000);
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
Loading…
Reference in New Issue
Block a user