Several updates
feat: New website UI feat: Add few params feat: Improve performance
This commit is contained in:
parent
81936a9883
commit
cc1881cc4f
47
Readme.md
47
Readme.md
@ -7,6 +7,8 @@
|
||||
<details>
|
||||
<summary>More theme</summary>
|
||||
|
||||
### *Contribute themes is welcome!*
|
||||
|
||||
##### asoul
|
||||
![asoul](https://count.getloli.com/get/@demo?theme=asoul)
|
||||
|
||||
@ -17,29 +19,35 @@
|
||||
![Rule34](https://count.getloli.com/get/@demo?theme=rule34)
|
||||
|
||||
##### gelbooru
|
||||
![Gelbooru](https://count.getloli.com/get/@demo?theme=gelbooru)</details>
|
||||
![Gelbooru](https://count.getloli.com/get/@demo?theme=gelbooru)
|
||||
|
||||
</details>
|
||||
|
||||
## Demo
|
||||
[https://count.getloli.com](https://count.getloli.com)
|
||||
|
||||
## How to use
|
||||
|
||||
About the counter usage, please see the [website homepage](https://count.getloli.com).
|
||||
|
||||
## Usage
|
||||
|
||||
### Install
|
||||
|
||||
#### Run on Replit
|
||||
#### Run on Glitch
|
||||
|
||||
- Open the url [https://replit.com/@journeyad/Moe-Counter](https://replit.com/@journeyad/Moe-Counter)
|
||||
- Just hit the **Fork** button
|
||||
- And hit the **Run** button
|
||||
- Open [Glitch project](https://glitch.com/~moe-counter-api)
|
||||
- Just hit the **Remix your own** button
|
||||
- That's it!
|
||||
|
||||
#### Deploying on your own server
|
||||
|
||||
```shell
|
||||
$ git clone https://github.com/journey-ad/Moe-Counter.git
|
||||
$ cd Moe-Counter
|
||||
$ yarn install
|
||||
$ pnpm install
|
||||
|
||||
$ yarn start
|
||||
$ pnpm run start
|
||||
```
|
||||
|
||||
### Configuration
|
||||
@ -53,30 +61,27 @@ app:
|
||||
|
||||
db:
|
||||
type: sqlite # sqlite or mongodb
|
||||
interval: 60 # write to db interval in seconds (0 for realtime)
|
||||
```
|
||||
|
||||
If you use mongodb, you need to specify the environment variable `DB_URL`
|
||||
If using mongodb, you need to specify the environment variable `DB_URL`
|
||||
|
||||
```shell
|
||||
# eg:
|
||||
# e.g.
|
||||
export DB_URL=mongodb+srv://account:passwd@***.***.***.mongodb.net/db_count
|
||||
```
|
||||
|
||||
replit can use Secrets, [documentation](https://docs.replit.com/programming-ide/storing-sensitive-information-environment-variables)
|
||||
|
||||
```
|
||||
DB_URL="mongodb+srv://account:passwd@***.***.***.mongodb.net/db_count"
|
||||
```
|
||||
Glitch can use `.env` file, [documentation](https://help.glitch.com/hc/en-us/articles/16287550167437-Adding-Private-Data)
|
||||
|
||||
## Credits
|
||||
|
||||
* [replit](https://replit.com/)
|
||||
* [A-SOUL_Official](https://space.bilibili.com/703007996)
|
||||
* [moebooru](https://github.com/moebooru/moebooru)
|
||||
* rule34.xxx NSFW
|
||||
* gelbooru.com NSFW
|
||||
* [Icons8](https://icons8.com/icons/set/star)
|
||||
* [Glitch](https://glitch.com/)
|
||||
* [A-SOUL_Official](https://space.bilibili.com/703007996)
|
||||
* [moebooru](https://github.com/moebooru/moebooru)
|
||||
* rule34.xxx NSFW
|
||||
* gelbooru.com NSFW
|
||||
* [Icons8](https://icons8.com/icon/80355/star)
|
||||
|
||||
## License
|
||||
|
||||
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fjourney-ad%2FMoe-Counter.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fjourney-ad%2FMoe-Counter?ref=badge_large)
|
||||
[MIT License](./LICENSE)
|
||||
|
@ -1,3 +1,39 @@
|
||||
@media screen and (min-width: 800px) {
|
||||
body {
|
||||
max-width: min(90%, 800px);
|
||||
}
|
||||
}
|
||||
|
||||
details>summary {
|
||||
list-style: none
|
||||
}
|
||||
|
||||
details>summary::-webkit-details-marker,details>summary::marker {
|
||||
display: none
|
||||
}
|
||||
|
||||
summary:before {
|
||||
border-bottom: 6px solid transparent;
|
||||
border-left: 10px solid var(--b-txt);
|
||||
border-top: 6px solid transparent;
|
||||
content: "";
|
||||
display: inline-block;
|
||||
height: 0;
|
||||
margin-right: 10px;
|
||||
position: relative;
|
||||
transition: .2s;
|
||||
width: 0
|
||||
}
|
||||
|
||||
details[open] summary:before {
|
||||
transform: rotate(90deg)
|
||||
}
|
||||
|
||||
h2, h3, h4, h5 {
|
||||
margin-top: 1.5em;
|
||||
margin-bottom: .6em;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 900px) {
|
||||
iframe {
|
||||
display: none;
|
||||
|
@ -4,3 +4,4 @@ app:
|
||||
|
||||
db:
|
||||
type: sqlite # sqlite or mongodb
|
||||
interval: 60 # write to db interval in seconds (0 for realtime)
|
||||
|
@ -1,55 +1,60 @@
|
||||
'use strict'
|
||||
"use strict";
|
||||
|
||||
const mongoose = require('mongoose')
|
||||
const schema = require('./schema')
|
||||
const mongoose = require("mongoose");
|
||||
|
||||
const schema = new mongoose.Schema(
|
||||
{
|
||||
name: { type: String, required: true },
|
||||
num: { type: Number, required: true }
|
||||
},
|
||||
{ collection: 'tb_count', versionKey: false }
|
||||
);
|
||||
|
||||
// the default mongodb url (local server)
|
||||
const mongodbURL = process.env.DB_URL || 'mongodb://127.0.0.1:27017'
|
||||
const mongodbURL = process.env.DB_URL || "mongodb://127.0.0.1:27017";
|
||||
|
||||
mongoose.connect(mongodbURL, {
|
||||
useNewUrlParser: true,
|
||||
useUnifiedTopology: true,
|
||||
useFindAndModify: false
|
||||
})
|
||||
useFindAndModify: false,
|
||||
});
|
||||
|
||||
const Count = mongoose.connection.model('Count', schema)
|
||||
const Count = mongoose.connection.model("Count", schema);
|
||||
|
||||
function getNum(name) {
|
||||
return Count
|
||||
.findOne({ name }, '-_id -__v')
|
||||
.exec()
|
||||
return Count.findOne({ name }, "-_id -__v").exec();
|
||||
}
|
||||
|
||||
function getAll() {
|
||||
return Count
|
||||
.find({ }, '-_id -__v')
|
||||
.exec()
|
||||
return Count.find({}, "-_id -__v").exec();
|
||||
}
|
||||
|
||||
function setNum(name, num) {
|
||||
return Count
|
||||
.findOneAndUpdate({ name }, { name, num }, { upsert: true })
|
||||
.exec()
|
||||
return Count.findOneAndUpdate(
|
||||
{ name },
|
||||
{ name, num },
|
||||
{ upsert: true }
|
||||
).exec();
|
||||
}
|
||||
|
||||
function setNumMulti(counters) {
|
||||
const bulkOps = counters.map(obj => {
|
||||
const { name, num } = obj
|
||||
const bulkOps = counters.map((obj) => {
|
||||
const { name, num } = obj;
|
||||
return {
|
||||
updateOne: {
|
||||
filter: { name },
|
||||
update: { name, num },
|
||||
upsert: true
|
||||
}
|
||||
}
|
||||
})
|
||||
upsert: true,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
return Count.bulkWrite(bulkOps, { ordered : false })
|
||||
return Count.bulkWrite(bulkOps, { ordered: false });
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getNum,
|
||||
getAll,
|
||||
setNum,
|
||||
setNumMulti
|
||||
}
|
||||
setNumMulti,
|
||||
};
|
||||
|
@ -1,8 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const mongoose = require('mongoose');
|
||||
|
||||
module.exports = new mongoose.Schema({
|
||||
name: { type: String, required: true },
|
||||
num: { type: Number, required: true }
|
||||
}, { collection: 'tb_count', versionKey: false });
|
159
index.js
159
index.js
@ -1,20 +1,17 @@
|
||||
'use strict'
|
||||
"use strict";
|
||||
|
||||
const fs = require('fs')
|
||||
const config = require('config-yml')
|
||||
const express = require('express')
|
||||
const compression = require('compression')
|
||||
const config = require("config-yml");
|
||||
const express = require("express");
|
||||
const compression = require("compression");
|
||||
|
||||
const db = require('./db')
|
||||
const themify = require('./utils/themify')
|
||||
const db = require("./db");
|
||||
const themify = require("./utils/themify");
|
||||
|
||||
const PLACES = 7
|
||||
const app = express();
|
||||
|
||||
const app = express()
|
||||
|
||||
app.use(express.static('assets'))
|
||||
app.use(compression())
|
||||
app.set('view engine', 'pug')
|
||||
app.use(express.static("assets"));
|
||||
app.use(compression());
|
||||
app.set("view engine", "pug");
|
||||
|
||||
app.get('/', (req, res) => {
|
||||
const site = config.app.site || `${req.protocol}://${req.get('host')}`
|
||||
@ -22,104 +19,122 @@ app.get('/', (req, res) => {
|
||||
});
|
||||
|
||||
// get the image
|
||||
app.get('/get/@:name', async (req, res) => {
|
||||
const { name } = req.params
|
||||
const { theme = 'moebooru' } = req.query
|
||||
let length = PLACES
|
||||
app.get(["/@:name", "/get/@:name"], async (req, res) => {
|
||||
const { name } = req.params;
|
||||
const { theme = "moebooru", padding = 7, pixelated = '1', darkmode = 'auto' } = req.query;
|
||||
const isPixelated = pixelated === '1';
|
||||
|
||||
// This helps with GitHub's image cache
|
||||
if (name.length > 32) {
|
||||
res.status(400).send("name too long");
|
||||
return;
|
||||
}
|
||||
|
||||
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'
|
||||
})
|
||||
"content-type": "image/svg+xml",
|
||||
"cache-control": "max-age=0, no-cache, no-store, must-revalidate",
|
||||
});
|
||||
|
||||
const data = await getCountByName(name)
|
||||
const data = await getCountByName(name);
|
||||
|
||||
if (name === 'demo') {
|
||||
res.set({
|
||||
'cache-control': 'max-age=31536000'
|
||||
})
|
||||
length = 10
|
||||
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, length })
|
||||
res.send(renderSvg)
|
||||
const renderSvg = themify.getCountImage({
|
||||
count: data.num,
|
||||
theme,
|
||||
padding,
|
||||
darkmode,
|
||||
pixelated: isPixelated
|
||||
});
|
||||
|
||||
console.log(data, `theme: ${theme}`, `ref: ${req.get('Referrer') || null}`, `ua: ${req.get('User-Agent') || null}`)
|
||||
})
|
||||
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
|
||||
app.get('/record/@:name', async (req, res) => {
|
||||
const { name } = req.params
|
||||
app.get("/record/@:name", async (req, res) => {
|
||||
const { name } = req.params;
|
||||
|
||||
const data = await getCountByName(name)
|
||||
const data = await getCountByName(name);
|
||||
|
||||
res.json(data)
|
||||
})
|
||||
res.json(data);
|
||||
});
|
||||
|
||||
app.get('/heart-beat', (req, res) => {
|
||||
res.set({
|
||||
'cache-control': 'max-age=0, no-cache, no-store, must-revalidate'
|
||||
})
|
||||
|
||||
res.send('alive')
|
||||
console.log('heart-beat')
|
||||
app.get("/heart-beat", (req, res) => {
|
||||
res.set("cache-control", "max-age=0, no-cache, no-store, must-revalidate");
|
||||
res.send("alive");
|
||||
console.log("heart-beat");
|
||||
});
|
||||
|
||||
const listener = app.listen(config.app.port || 3000, () => {
|
||||
console.log('Your app is listening on port ' + listener.address().port)
|
||||
})
|
||||
console.log("Your app is listening on port " + listener.address().port);
|
||||
});
|
||||
|
||||
let __cache_counter = {}, shouldPush = false
|
||||
let __cache_counter = {};
|
||||
let enablePushDelay = config.db.interval > 0
|
||||
let needPush = false;
|
||||
|
||||
setInterval(() => {
|
||||
shouldPush = true
|
||||
}, 1000 * 60);
|
||||
if (enablePushDelay) {
|
||||
setInterval(() => {
|
||||
needPush = true;
|
||||
}, 1000 * config.db.interval);
|
||||
}
|
||||
|
||||
async function pushDB() {
|
||||
if (!shouldPush) return
|
||||
if (Object.keys(__cache_counter).length === 0) return;
|
||||
if (enablePushDelay && !needPush) return;
|
||||
|
||||
try {
|
||||
shouldPush = false
|
||||
if (Object.keys(__cache_counter).length === 0) return
|
||||
needPush = false;
|
||||
console.log("pushDB", __cache_counter);
|
||||
|
||||
console.log("pushDB", __cache_counter)
|
||||
|
||||
const counters = Object.keys(__cache_counter).map(key => {
|
||||
const counters = Object.keys(__cache_counter).map((key) => {
|
||||
return {
|
||||
name: key,
|
||||
num: __cache_counter[key]
|
||||
}
|
||||
})
|
||||
num: __cache_counter[key],
|
||||
};
|
||||
});
|
||||
|
||||
await db.setNumMulti(counters)
|
||||
__cache_counter = {}
|
||||
await db.setNumMulti(counters);
|
||||
__cache_counter = {};
|
||||
} catch (error) {
|
||||
console.log("pushDB is error: ", error)
|
||||
console.log("pushDB is error: ", error);
|
||||
}
|
||||
}
|
||||
|
||||
async function getCountByName(name) {
|
||||
const defaultCount = { name, num: 0 }
|
||||
const defaultCount = { name, num: 0 };
|
||||
|
||||
if (name === 'demo') return { name, num: '0123456789' }
|
||||
if (name === "demo") return { name, num: "0123456789" };
|
||||
|
||||
try {
|
||||
if (!(name in __cache_counter)) {
|
||||
const counter = await db.getNum(name) || defaultCount
|
||||
__cache_counter[name] = counter.num + 1
|
||||
const counter = (await db.getNum(name)) || defaultCount;
|
||||
__cache_counter[name] = counter.num + 1;
|
||||
} else {
|
||||
__cache_counter[name]++
|
||||
__cache_counter[name]++;
|
||||
}
|
||||
|
||||
pushDB()
|
||||
|
||||
return { name, num: __cache_counter[name] }
|
||||
pushDB();
|
||||
|
||||
return { name, num: __cache_counter[name] };
|
||||
} catch (error) {
|
||||
console.log("get count by name is error: ", error)
|
||||
return defaultCount
|
||||
|
||||
console.log("get count by name is error: ", error);
|
||||
return defaultCount;
|
||||
}
|
||||
}
|
||||
|
2091
package-lock.json
generated
2091
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -11,10 +11,10 @@
|
||||
"author": "journey-ad",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"better-sqlite3": "^8.1.0",
|
||||
"better-sqlite3": "^8.2.0",
|
||||
"compression": "^1.7.4",
|
||||
"config-yml": "^0.10.3",
|
||||
"express": "^4.17.1",
|
||||
"express": "^4.18.2",
|
||||
"image-size": "^0.8.3",
|
||||
"mime-types": "^2.1.27",
|
||||
"mongoose": "^5.9.28",
|
||||
|
1667
pnpm-lock.yaml
generated
Normal file
1667
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -10,7 +10,7 @@ const themePath = path.resolve(__dirname, '../assets/theme')
|
||||
const themeList = {}
|
||||
|
||||
fs.readdirSync(themePath).forEach(theme => {
|
||||
if(!(theme in themeList)) themeList[theme] = {}
|
||||
if (!(theme in themeList)) themeList[theme] = {}
|
||||
const imgList = fs.readdirSync(path.resolve(themePath, theme))
|
||||
imgList.forEach(img => {
|
||||
const imgPath = path.resolve(themePath, theme, img)
|
||||
@ -25,39 +25,61 @@ fs.readdirSync(themePath).forEach(theme => {
|
||||
})
|
||||
})
|
||||
|
||||
function convertToDatauri(path){
|
||||
function convertToDatauri(path) {
|
||||
const mime = mimeType.lookup(path)
|
||||
const base64 = fs.readFileSync(path).toString('base64')
|
||||
|
||||
return `data:${mime};base64,${base64}`
|
||||
}
|
||||
|
||||
function getCountImage({ count, theme='moebooru', length=7 }) {
|
||||
if(!(theme in themeList)) theme = 'moebooru'
|
||||
function getCountImage({ count, theme = 'moebooru', padding = 7, pixelated = true, darkmode = 'auto' }) {
|
||||
if (!(theme in themeList)) theme = 'moebooru'
|
||||
|
||||
// This is not the greatest way for generating an SVG but it'll do for now
|
||||
const countArray = count.toString().padStart(length, '0').split('')
|
||||
const countArray = count.toString().padStart(padding, '0').split('')
|
||||
const uniqueChar = [...new Set(countArray)]
|
||||
|
||||
let x = 0, y = 0
|
||||
const parts = countArray.reduce((acc, next, index) => {
|
||||
const { width, height, data } = themeList[theme][next]
|
||||
|
||||
const image = `${acc}
|
||||
<image x="${x}" y="0" width="${width}" height="${height}" xlink:href="${data}" />`
|
||||
const defs = uniqueChar.reduce((ret, cur) => {
|
||||
const { width, height, data } = themeList[theme][cur]
|
||||
|
||||
if (height > y) y = height
|
||||
|
||||
ret = `${ret}
|
||||
<image id="${cur}" width="${width}" height="${height}" xlink:href="${data}" />`
|
||||
|
||||
return ret
|
||||
}, '')
|
||||
|
||||
const parts = countArray.reduce((ret, cur) => {
|
||||
const { width } = themeList[theme][cur]
|
||||
|
||||
const image = `${ret}
|
||||
<use x="${x}" xlink:href="#${cur}" />`
|
||||
|
||||
x += width
|
||||
|
||||
if(height > y) y = height
|
||||
|
||||
return image
|
||||
}, '')
|
||||
|
||||
const style = `
|
||||
svg {
|
||||
${pixelated ? 'image-rendering: pixelated;' : ''}
|
||||
${darkmode === '1' ? 'filter: brightness(.6);' : ''}
|
||||
}
|
||||
${darkmode === 'auto' ? `@media (prefers-color-scheme: dark) { svg { filter: brightness(.6); } }` : ''}
|
||||
`
|
||||
|
||||
return `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="${x}" height="${y}" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="image-rendering: pixelated;">
|
||||
<title>Moe Count</title>
|
||||
<g>
|
||||
${parts}
|
||||
</g>
|
||||
<!-- Generated by https://github.com/journey-ad/Moe-Counter -->
|
||||
<svg viewBox="0 0 ${x} ${y}" width="${x}" height="${y}" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Moe Counter!</title>
|
||||
<style>${style}</style>
|
||||
<defs>${defs}
|
||||
</defs>
|
||||
<g>${parts}
|
||||
</g>
|
||||
</svg>
|
||||
`
|
||||
}
|
||||
|
203
views/index.pug
203
views/index.pug
@ -3,7 +3,8 @@ html
|
||||
title='Moe Counter!'
|
||||
meta(name='viewport', content='width=device-width, initial-scale=1')
|
||||
link(rel='icon', type='image/png', href='favicon.png')
|
||||
link(rel='stylesheet', href='https://cdn.jsdelivr.net/gh/kognise/water.css@latest/dist/light.min.css')
|
||||
link(rel='stylesheet', href='https://unpkg.com/normalize.css')
|
||||
link(rel='stylesheet', href='https://unpkg.com/bamboo.css')
|
||||
link(rel='stylesheet', href='style.css')
|
||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||
script(async, src='https://www.googletagmanager.com/gtag/js?id=G-2RLWN5JXRL')
|
||||
@ -20,47 +21,55 @@ html
|
||||
'event_label' : label
|
||||
});
|
||||
}
|
||||
script(async, src='https://unpkg.com/party-js@2/bundle/party.min.js')
|
||||
|
||||
style.
|
||||
html {
|
||||
scroll-padding: 50px 0;
|
||||
}
|
||||
|
||||
body
|
||||
h3 How to use:
|
||||
h1#main_title(style='margin-top: 0.5em;')
|
||||
i Moe Counter!
|
||||
|
||||
h3 How to use
|
||||
p Set a unique id for your counter, replace
|
||||
code :name
|
||||
| in the url, that's all.
|
||||
|
||||
h5 SVG address
|
||||
code #{site}/get/@:name
|
||||
code #{site}/@:name
|
||||
|
||||
h5 Img tag
|
||||
code <img src="#{site}/get/@:name" alt=":name" />
|
||||
code <img src="#{site}/@:name" alt=":name" />
|
||||
|
||||
h5 Markdown
|
||||
code ![:name](#{site}/get/@:name)
|
||||
code ![:name](#{site}/@:name)
|
||||
|
||||
h3 eg:
|
||||
<img src="#{site}/get/@index" alt="Moe Count!" />
|
||||
h5 e.g.
|
||||
<img src="#{site}/@index" alt="Moe Count!" />
|
||||
|
||||
i Data can access by anyone, please
|
||||
| <span style="color: #ff4500;"> DO NOT</span>
|
||||
| enter personal information
|
||||
|
||||
details
|
||||
summary(style='display: inline-block;', onclick='_evt_push("click", "normal", "more_theme")')
|
||||
h3(style='display: inline-block; cursor: pointer;') More theme
|
||||
p(style='margin: 0;') Just use the query parameters <code>theme</code>, like this: <code>#{site}/get/@:name?theme=moebooru</code>
|
||||
details#themes
|
||||
summary#more_theme(onclick='_evt_push("click", "normal", "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>
|
||||
h5 asoul
|
||||
img(src='#{site}/get/@demo?theme=asoul', alt='A-SOUL')
|
||||
<img src="#{site}/@demo?theme=asoul" alt="A-SOUL" />
|
||||
h5 moebooru
|
||||
img(src='#{site}/get/@demo?theme=moebooru', alt='Moebooru')
|
||||
<img src="#{site}/@demo?theme=moebooru" alt="Moebooru" />
|
||||
h5 moebooru-h
|
||||
img(src='#{site}/get/@demo?theme=moebooru-h', alt='Moebooru-Hentai')
|
||||
<img src="#{site}/@demo?theme=moebooru-h" alt="Moebooru-Hentai" />
|
||||
h5 rule34
|
||||
img(src='#{site}/get/@demo?theme=rule34', alt='Rule34')
|
||||
<img src="#{site}/@demo?theme=rule34" alt="Rule34" />
|
||||
h5 gelbooru
|
||||
img(src='#{site}/get/@demo?theme=gelbooru', alt='Gelbooru')
|
||||
<img src="#{site}/@demo?theme=gelbooru" alt="Gelbooru" />
|
||||
h5 gelbooru-h
|
||||
img(src='#{site}/get/@demo?theme=gelbooru-h', alt='Gelbooru-Hentai')
|
||||
<img src="#{site}/@demo?theme=gelbooru-h" alt="Gelbooru-Hentai" />
|
||||
|
||||
h3 Credits
|
||||
ul
|
||||
li
|
||||
a(href='https://repl.it/', target='_blank', rel='nofollow') repl.it
|
||||
a(href='https://glitch.com/', target='_blank', rel='nofollow') Glitch
|
||||
li
|
||||
a(href='https://space.bilibili.com/703007996', target='_blank', title='A-SOUL_Official') A-SOUL
|
||||
li
|
||||
@ -72,41 +81,145 @@ html
|
||||
a(href='javascript:alert("!!! NSFW LINK !!!\\nPlease enter the url manually")') gelbooru.com
|
||||
| NSFW
|
||||
li
|
||||
a(href='https://icons8.com/icons/set/star', target='_blank', rel='nofollow') Icons8
|
||||
a(href='https://icons8.com/icon/80355/star', target='_blank', rel='nofollow') Icons8
|
||||
|
||||
h3 Tool
|
||||
.tool
|
||||
code #{site}/get/@
|
||||
input#name(type='text', placeholder=':name', style='display: inline-block; width: 80px; height: 1.4em; line-height: 1.4em; margin: 0 4px; vertical-align: middle;')
|
||||
code ?theme=
|
||||
select#theme(style='display: inline-block; height: 1.6em; line-height: 1.6em; font-size: 14px; margin: 0 4px; padding: 0 4px; vertical-align: middle;')
|
||||
option(value='asoul') asoul
|
||||
option(value='moebooru') moebooru
|
||||
option(value='moebooru-h') moebooru-h
|
||||
option(value='rule34') rule34
|
||||
option(value='gelbooru') gelbooru
|
||||
option(value='gelbooru-h') gelbooru-h
|
||||
button#get(style='margin: 10px 0;', onclick='_evt_push("click", "normal", "get_counter")') Get
|
||||
img#result(style='display: block;')
|
||||
table
|
||||
thead
|
||||
tr
|
||||
th Param
|
||||
th Value
|
||||
tbody
|
||||
tr
|
||||
td
|
||||
code name
|
||||
td
|
||||
input#name(type='text', placeholder=':name')
|
||||
tr
|
||||
td
|
||||
code theme
|
||||
td
|
||||
select#theme
|
||||
option(value='asoul') asoul
|
||||
option(value='moebooru') moebooru
|
||||
option(value='moebooru-h') moebooru-h
|
||||
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
|
||||
td
|
||||
code padding
|
||||
td
|
||||
input#padding(type='number', value='7', min='1', max='32', step='1', oninput='this.value = this.value.replace(/[^0-9]/g, "")')
|
||||
tr
|
||||
td
|
||||
code darkmode
|
||||
td
|
||||
select#darkmode(name="darkmode")
|
||||
option(value="auto", selected) auto
|
||||
option(value="1") yes
|
||||
option(value="0") no
|
||||
|
||||
button#get(style='margin-bottom: 1em;', onclick='_evt_push("click", "normal", "get_counter")') Generate
|
||||
|
||||
div
|
||||
code#code(style='visibility: hidden; display: inline-block; margin-bottom: 1em;')
|
||||
img#result(style='display: block;')
|
||||
|
||||
script.
|
||||
var btn = document.getElementById('get'),
|
||||
img = document.getElementById('result')
|
||||
img = document.getElementById('result'),
|
||||
code = document.getElementById('code')
|
||||
|
||||
btn.addEventListener('click', function() {
|
||||
var name = document.getElementById('name'),
|
||||
themeEl = document.getElementById('theme')
|
||||
var text = name.value ? name.value.trim() : ''
|
||||
var theme = themeEl.value || 'moebooru'
|
||||
if(!text) {
|
||||
btn.addEventListener('click', throttle(function() {
|
||||
var $name = document.getElementById('name'),
|
||||
$theme = document.getElementById('theme'),
|
||||
$pixelated = document.getElementById('pixelated'),
|
||||
$padding = document.getElementById('padding'),
|
||||
$darkmode = document.getElementById('darkmode')
|
||||
|
||||
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) {
|
||||
alert('Please input counter name.')
|
||||
return
|
||||
}
|
||||
|
||||
img.src = '#{site}/get/@' + text + '?theme=' + theme
|
||||
party.confetti(this, { count: party.variation.range(20, 40) });
|
||||
|
||||
img.src = `#{site}/@${name}?theme=${theme}&pixelated=${pixelated}&padding=${padding}&darkmode=${darkmode}`
|
||||
code.textContent = img.src
|
||||
code.style.visibility = 'visible'
|
||||
|
||||
img.onload = function() {
|
||||
img.scrollIntoView({block:'start', behavior: 'smooth'})
|
||||
}
|
||||
}, 500))
|
||||
|
||||
code.addEventListener('click', function(e) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
var target = e.target
|
||||
var range, selection
|
||||
|
||||
if (document.body.createTextRange) {
|
||||
range = document.body.createTextRange()
|
||||
range.moveToElementText(target)
|
||||
range.select()
|
||||
} else if (window.getSelection) {
|
||||
selection = window.getSelection()
|
||||
range = document.createRange()
|
||||
range.selectNodeContents(target)
|
||||
selection.removeAllRanges()
|
||||
selection.addRange(range)
|
||||
}
|
||||
})
|
||||
|
||||
iframe(src="https://chat.getloli.com/room/@Moe-counter?title=%E8%90%8C%E8%90%8C%E8%AE%A1%E6%95%B0%E5%99%A8%E7%9A%84%E7%95%99%E8%A8%80%E6%9D%BF", scrolling="no", frameborder="0", height="70%", width="26%", style="position: fixed;top: 2%;right: 5%;")
|
||||
var $main_title = document.querySelector('#main_title i'),
|
||||
$themes = document.querySelector('#themes'),
|
||||
$more_theme = document.querySelector('#more_theme')
|
||||
$main_title.addEventListener('click', throttle(function() {
|
||||
party.sparkles(document.documentElement, { count: party.variation.range(40, 100) });
|
||||
}, 1000))
|
||||
$more_theme.addEventListener('click', function() {
|
||||
if (!$themes.hasAttribute('open')) {
|
||||
party.sparkles($more_theme.querySelector('h3'), { count: party.variation.range(20, 40) });
|
||||
$themes.scrollIntoView({block:'start', behavior: 'smooth'})
|
||||
}
|
||||
})
|
||||
|
||||
p.copy
|
||||
function throttle(fn, threshhold, scope) {
|
||||
threshhold || (threshhold = 250)
|
||||
var last
|
||||
var deferTimer
|
||||
return function () {
|
||||
var context = scope || this
|
||||
var now = +new Date
|
||||
var args = arguments
|
||||
if (last && now < last + threshhold) {
|
||||
// hold on to it
|
||||
clearTimeout(deferTimer)
|
||||
deferTimer = setTimeout(function () {
|
||||
last = now
|
||||
fn.apply(context, args)
|
||||
}, threshhold)
|
||||
} else {
|
||||
last = now
|
||||
fn.apply(context, args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user