Lots of QOL + Icon Kit improvements
- added an optional col3 to icon kit - added an options menu to the icon kit - added a random button to the icon kit - added a proper popup for the icon kit's 'steal icon' button - redid the buttons on the icon kit - tripled icon cache duration - rewrote and minified the XOR class - idk some other fun stuff
|
@ -20,7 +20,7 @@ module.exports = async (app, req, res) => {
|
|||
sheet.loadInfo().then(async () => {
|
||||
let tab = sheet.sheetsById[1555821000]
|
||||
await tab.loadCells('A2:C2')
|
||||
let topPlayers = tab.getCell(1, type == "demons" ? 2 : type == "usercoins" ? 1 : 0).value
|
||||
let topPlayers = tab.getCell(1, type == "demons" ? 2 : type == "coins" ? 1 : 0).value
|
||||
|
||||
let idArray = topPlayers.split(",")
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ module.exports = async (app, req, res, api, ID, analyze) => {
|
|||
}
|
||||
}, async function (err, resp, body) {
|
||||
|
||||
if (err || !body || body == '-1') {
|
||||
if (err || !body || body == '-1' || body.startsWith("<!")) {
|
||||
if (!api && levelID < 0) return res.redirect('/')
|
||||
if (!api) return res.redirect('search/' + req.params.id)
|
||||
else return res.send("-1")
|
||||
|
|
48
api/icon.js
|
@ -6,7 +6,6 @@ const icons = require('../icons/gameSheet.json');
|
|||
const colors = require('../misc/colors.json');
|
||||
const forms = require('../icons/forms.json')
|
||||
const offsets = require('../icons/offsets.json');
|
||||
const { finished } = require('stream');
|
||||
|
||||
function recolor(img, col) {
|
||||
return img.scan(0, 0, img.bitmap.width, img.bitmap.height, function (x, y, idx) {
|
||||
|
@ -44,7 +43,8 @@ module.exports = async (app, req, res) => {
|
|||
if (!glowOffset.some(x => x != 0)) glowOffset = []
|
||||
|
||||
let topless = form == "bird" && req.query.topless
|
||||
let sizeParam = req.query.size && !isNaN(req.query.size)
|
||||
let autoSize = req.query.size == "auto"
|
||||
let sizeParam = autoSize || (req.query.size && !isNaN(req.query.size))
|
||||
if (outline == "0") outline = false;
|
||||
|
||||
if (iconID && iconID.toString().length == 1) iconID = "0" + iconID;
|
||||
|
@ -69,14 +69,20 @@ module.exports = async (app, req, res) => {
|
|||
if (!fs.existsSync(fromIcons(icon))) return res.sendFile(path.join(__dirname, '../assets/unknownIcon.png'))
|
||||
}
|
||||
|
||||
let ex = fromIcons(extra)
|
||||
let hasExtra = fs.existsSync(ex)
|
||||
|
||||
if (!colors[col1]) col1 = 0
|
||||
if (!colors[col2]) col2 = 3
|
||||
|
||||
let iconCode = `${req.query.form == "cursed" ? "cursed" : form}${topless ? "topless" : ""}-${iconID}-${col1}-${col2}-${outline ? 1 : 0}`
|
||||
|
||||
let col3 = req.query.col3
|
||||
if (col3 && (!hasExtra || !colors[col3] || col3 == "12")) col3 = null
|
||||
|
||||
let iconCode = `${req.query.form == "cursed" ? "cursed" : form}${topless ? "top" : ""}-${iconID}-${col1}-${col2}-${col3 || "x"}-${outline ? 1 : 0}`
|
||||
|
||||
if (!sizeParam && !glowOffset.length && cache[iconCode]) {
|
||||
clearTimeout(cache[iconCode].timeoutID);
|
||||
cache[iconCode].timeoutID = setTimeout(function() {delete cache[iconCode]}, 600000);
|
||||
cache[iconCode].timeoutID = setTimeout(function() {delete cache[iconCode]}, 1800000);
|
||||
return res.end(cache[iconCode].value);
|
||||
}
|
||||
|
||||
|
@ -89,6 +95,7 @@ module.exports = async (app, req, res) => {
|
|||
let robotOffset1, robotOffset2, robotOffset3, robotOffset1b, robotOffset2b, robotOffset3b;
|
||||
let robotGlow1, robotGlow2, robotGlow3
|
||||
let ufoTop, ufoOffset, ufoCoords, ufoSprite
|
||||
let extrabit, offset2, size2;
|
||||
|
||||
if (isSpecial) {
|
||||
const legs = [1,2,3].map(function(val) {return genImageName(`0${val+1}`)});
|
||||
|
@ -108,23 +115,22 @@ module.exports = async (app, req, res) => {
|
|||
if (!glowOffset.length) glowOffset = offsets[form][+iconID] || []
|
||||
}
|
||||
|
||||
res.contentType('image/png');
|
||||
let extrabit, offset2, size2;
|
||||
if (fs.existsSync(fromIcons(extra))) {
|
||||
extrabit = icons[extra]
|
||||
offset2 = extrabit.spriteOffset.map(minusOrigOffset);
|
||||
size2 = extrabit.spriteSize;
|
||||
|
||||
extra = new Jimp(fromIcons(extra));
|
||||
useExtra = true
|
||||
}
|
||||
|
||||
Jimp.read(fromIcons(glow)).then(async function (image) {
|
||||
|
||||
let size = [image.bitmap.width, image.bitmap.height]
|
||||
let glow = recolor(image, col2)
|
||||
let imgOff = isSpecial ? 100 : 0
|
||||
|
||||
let eb = fromIcons(extra)
|
||||
if (fs.existsSync(eb)) {
|
||||
extrabit = icons[extra]
|
||||
offset2 = extrabit.spriteOffset.map(minusOrigOffset);
|
||||
size2 = extrabit.spriteSize;
|
||||
extra = new Jimp(eb);
|
||||
if (col3) await Jimp.read(eb).then(e => { extra = recolor(e, col3) })
|
||||
useExtra = true
|
||||
}
|
||||
|
||||
Jimp.read(fromIcons(icon)).then(async function (ic) {
|
||||
|
||||
let iconSize = [ic.bitmap.width, ic.bitmap.height]
|
||||
|
@ -279,18 +285,19 @@ module.exports = async (app, req, res) => {
|
|||
if (form == "swing") img.resize(120, 111)
|
||||
if (img.bitmap.height == 300) ic.autocrop(1, false)
|
||||
if (sizeParam) {
|
||||
let imgSize = Math.round(req.query.size)
|
||||
let thicc = img.bitmap.width > img.bitmap.height
|
||||
let imgSize = req.query.size == "auto" ? (thicc ? img.bitmap.width : img.bitmap.height) : Math.round(req.query.size)
|
||||
if (imgSize < 32) imgSize = 32
|
||||
if (imgSize > 512) imgSize = 512
|
||||
if (img.bitmap.width > img.bitmap.height) img.contain(img.bitmap.width, img.bitmap.width, Jimp.HORIZONTAL_ALIGN_CENTER | Jimp.VERTICAL_ALIGN_MIDDLE)
|
||||
else if (img.bitmap.width < img.bitmap.height) img.contain(img.bitmap.height, img.bitmap.height, Jimp.HORIZONTAL_ALIGN_CENTER | Jimp.VERTICAL_ALIGN_MIDDLE)
|
||||
if (thicc) img.contain(img.bitmap.width, img.bitmap.width, Jimp.HORIZONTAL_ALIGN_CENTER | Jimp.VERTICAL_ALIGN_MIDDLE)
|
||||
else img.contain(img.bitmap.height, img.bitmap.height, Jimp.HORIZONTAL_ALIGN_CENTER | Jimp.VERTICAL_ALIGN_MIDDLE)
|
||||
img.resize(imgSize, Jimp.AUTO)
|
||||
}
|
||||
img.getBuffer(Jimp.AUTO, (err, buffer) => {
|
||||
if (!sizeParam && !glowOffset.length) {
|
||||
cache[iconCode] = {
|
||||
value: buffer,
|
||||
timeoutID: setTimeout(function() {delete cache[iconCode]}, 600000)
|
||||
timeoutID: setTimeout(function() {delete cache[iconCode]}, 1800000)
|
||||
}
|
||||
}
|
||||
return res.end(buffer, 'base64')
|
||||
|
@ -351,6 +358,7 @@ module.exports = async (app, req, res) => {
|
|||
let result = []
|
||||
|
||||
if (app.offline || req.query.hasOwnProperty("noUser") || req.query.hasOwnProperty("nouser") || username == "icon") return buildIcon()
|
||||
res.contentType('image/png');
|
||||
|
||||
request.post(app.endpoint + 'getGJUsers20.php', {
|
||||
form: {
|
||||
|
|
|
@ -2,6 +2,8 @@ const request = require('request')
|
|||
const fs = require('fs')
|
||||
const Level = require('../classes/Level.js')
|
||||
|
||||
function xor(str, key) { return Buffer.from(String.fromCodePoint(...str.split('').map((char, i) => char.charCodeAt(0) ^ key.charCodeAt(i % key.length)))).toString('base64') }
|
||||
|
||||
module.exports = async (app, req, res, api, analyze) => {
|
||||
|
||||
if (app.offline) {
|
||||
|
@ -28,7 +30,7 @@ module.exports = async (app, req, res, api, analyze) => {
|
|||
}
|
||||
}, async function (err, resp, body) {
|
||||
|
||||
if (err || !body || body == '-1') {
|
||||
if (err || !body || body == '-1' || body.startsWith("<!")) {
|
||||
if (!api) return res.redirect('search/' + req.params.id)
|
||||
else return res.send("-1")
|
||||
}
|
||||
|
@ -41,7 +43,6 @@ module.exports = async (app, req, res, api, analyze) => {
|
|||
let levelInfo = app.parseResponse(preRes[0])
|
||||
let level = new Level(levelInfo, author)
|
||||
|
||||
|
||||
if (song[2]) {
|
||||
level.songName = song[2] || "Unknown"
|
||||
level.songAuthor = song[4] || "Unknown"
|
||||
|
|
|
@ -9,7 +9,7 @@ module.exports = async (app, req, res, api) => {
|
|||
if (!req.body.accountID) return res.status(400).send("No account ID provided!")
|
||||
if (!req.body.password) return res.status(400).send("No password provided!")
|
||||
|
||||
let subject = new Buffer(req.body.subject ? (req.body.color ? "☆" : "") + (req.body.subject.slice(0, 50)) : (req.body.color ? "☆" : "") + "No subject").toString('base64').replace(/\//g, '_').replace(/\+/g, "-")
|
||||
let subject = Buffer.from(req.body.subject ? (req.body.color ? "☆" : "") + (req.body.subject.slice(0, 50)) : (req.body.color ? "☆" : "") + "No subject").toString('base64').replace(/\//g, '_').replace(/\+/g, "-")
|
||||
let body = xor.encrypt(req.body.message.slice(0, 300), 14251)
|
||||
|
||||
let params = {
|
||||
|
|
|
@ -31,7 +31,7 @@ module.exports = async (app, req, res) => {
|
|||
percent: 0
|
||||
}
|
||||
|
||||
params.comment = new Buffer(req.body.comment + (req.body.color ? "☆" : "")).toString('base64').replace(/\//g, '_').replace(/\+/g, "-")
|
||||
params.comment = Buffer.from(req.body.comment + (req.body.color ? "☆" : "")).toString('base64').replace(/\//g, '_').replace(/\+/g, "-")
|
||||
params.gjp = xor.encrypt(req.body.password, 37526)
|
||||
params.levelID = req.body.levelID.toString()
|
||||
params.accountID = req.body.accountID.toString()
|
||||
|
|
|
@ -20,7 +20,7 @@ module.exports = async (app, req, res) => {
|
|||
cType: '1'
|
||||
}
|
||||
|
||||
params.comment = new Buffer(req.body.comment.slice(0, 190) + (req.body.color ? "☆" : "")).toString('base64').replace(/\//g, '_').replace(/\+/g, "-")
|
||||
params.comment = Buffer.from(req.body.comment.slice(0, 190) + (req.body.color ? "☆" : "")).toString('base64').replace(/\//g, '_').replace(/\+/g, "-")
|
||||
params.gjp = xor.encrypt(req.body.password, 37526)
|
||||
params.accountID = req.body.accountID.toString()
|
||||
params.userName = req.body.username
|
||||
|
|
|
@ -8,11 +8,11 @@ module.exports = async (app, req, res, api, getLevels) => {
|
|||
request.post(app.endpoint + 'getGJUsers20.php', {
|
||||
form: {
|
||||
str: getLevels || req.params.id,
|
||||
secret: app.secret
|
||||
secret: app.secret,
|
||||
}
|
||||
}, function (err1, res1, b1) {
|
||||
|
||||
let searchResult = (err1 || b1 == '-1' || !b1) ? req.params.id : app.parseResponse(b1)[16]
|
||||
let searchResult = (req.query.hasOwnProperty("account") || err1 || b1 == '-1' || b1.startsWith("<!") || !b1) ? req.params.id : app.parseResponse(b1)[16]
|
||||
|
||||
request.post(app.endpoint + 'getGJUserInfo20.php', {
|
||||
form: {
|
||||
|
|
|
@ -74,7 +74,7 @@ module.exports = async (app, req, res) => {
|
|||
request.post(app.endpoint + 'getGJLevels21.php', {
|
||||
form : filters}, async function(err, resp, body) {
|
||||
|
||||
if (err || !body || body == '-1') return res.send("-1")
|
||||
if (err || !body || body == '-1' || body.startsWith("<!")) return res.send("-1")
|
||||
let splitBody = body.split('#')
|
||||
let preRes = splitBody[0].split('|', 10)
|
||||
let authorList = {}
|
||||
|
|
|
@ -18,26 +18,45 @@ body {
|
|||
}
|
||||
|
||||
p {
|
||||
font-family: "Roboto";
|
||||
font-family: aller, helvetica, arial;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-family: "Roboto";
|
||||
h1 {
|
||||
font-weight: normal;
|
||||
font-size: 20px;
|
||||
margin: 0 0;
|
||||
font-size: 60px;
|
||||
font-family: Pusab;
|
||||
color: white;
|
||||
letter-spacing: 0.02em;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-shadow: -2.2px -2.2px 0px #000, 2.2px -2.2px 0px #000, -2.2px 2.2px 0px #000, 2.2px 2.2px 0px #000, 5px 5px 0px rgba(0,0,0,0.4);
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-family: "Roboto";
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
h2 {
|
||||
font-weight: normal;
|
||||
margin: 0 0;
|
||||
font-size: 35px;
|
||||
font-family: Pusab;
|
||||
width: fit-content;
|
||||
color: white;
|
||||
letter-spacing: 0.02em;
|
||||
text-shadow: -1.5px -1.5px 0px #000, 1.5px -1.5px 0px #000, -1.5px 1.5px 0px #000, 1.5px 1.5px 0px #000;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.inline, .help {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.help {
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.small {
|
||||
font-size: 16px
|
||||
}
|
||||
|
@ -62,12 +81,6 @@ h3 {
|
|||
border: none;
|
||||
}
|
||||
|
||||
input[type=checkbox] {
|
||||
margin-left: 7px;
|
||||
transform: scale(1.2) translateY(1px);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input:focus, select:focus, textarea:focus, button:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
@ -79,6 +92,12 @@ input:focus, select:focus, textarea:focus, button:focus {
|
|||
border-style: 1px inset black;
|
||||
}
|
||||
|
||||
input:-webkit-autofill, input:-webkit-autofill:hover, input:-webkit-autofill:focus, input:-webkit-autofill:active {
|
||||
box-shadow: 0 0 0px 1000px #764F1A inset !important;
|
||||
-webkit-box-shadow: 0 0 0px 1000px #764F1A inset !important;
|
||||
-webkit-text-fill-color: white !important;
|
||||
}
|
||||
|
||||
textarea {
|
||||
border-radius: 5px;
|
||||
padding: 7 7 7 7;
|
||||
|
@ -115,6 +134,13 @@ input:focus, select:focus, textarea:focus, button:focus {
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
.supercenter {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%,-50%);
|
||||
}
|
||||
|
||||
.iconimage {
|
||||
height: 60px;
|
||||
}
|
||||
|
@ -262,12 +288,16 @@ input:focus, select:focus, textarea:focus, button:focus {
|
|||
border-image: url('https://gdcolon.com/assets/gj_select.png') 10 stretch !important;
|
||||
}
|
||||
|
||||
.iconTabButton, .glowToggle {
|
||||
.iconTabButton, .glowToggle, .copyForm {
|
||||
margin: 0 5 0 5;
|
||||
transition: transform .1s ease-in-out;
|
||||
}
|
||||
|
||||
.iconTabButton:active, .glowToggle:active {
|
||||
.copyForm {
|
||||
margin: 18 10 0 10 !important;
|
||||
}
|
||||
|
||||
.iconTabButton:active, .glowToggle:active, .copyForm:active {
|
||||
transform:scale(1.05)
|
||||
}
|
||||
|
||||
|
@ -297,7 +327,7 @@ input:focus, select:focus, textarea:focus, button:focus {
|
|||
margin-top: -5;
|
||||
}
|
||||
|
||||
#col1, #col2 {
|
||||
#colors div {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
|
@ -316,7 +346,11 @@ input:focus, select:focus, textarea:focus, button:focus {
|
|||
}
|
||||
|
||||
.menuButton:hover {
|
||||
transform:scale(1.1)
|
||||
transform:scale(1.07)
|
||||
}
|
||||
|
||||
.menuButton:active {
|
||||
transform:scale(1.15)
|
||||
}
|
||||
|
||||
#iconprogressbar {
|
||||
|
@ -335,6 +369,78 @@ input:focus, select:focus, textarea:focus, button:focus {
|
|||
background: transparent;
|
||||
}
|
||||
|
||||
.popup {
|
||||
position: fixed;
|
||||
display: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
background-color: rgba(0,0,0,0.4);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.brownBox {
|
||||
border: 17px solid transparent;
|
||||
border-radius: 25px;
|
||||
background-color: #995533;
|
||||
border-image: url('./../assets/brownbox.png') 10% round;
|
||||
}
|
||||
|
||||
.xButton {
|
||||
position: absolute;
|
||||
top: -35px;
|
||||
left: -40px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
input[type=text] {
|
||||
font-weight: normal;
|
||||
margin: 0 0;
|
||||
font-size: 45px;
|
||||
font-family: Pusab;
|
||||
color: white;
|
||||
letter-spacing: 0.02em;
|
||||
text-shadow: -0.2vh -0.2vh 0vh #000, 0.2vh -0.2vh 0vh #000, -0.2vh 0.2vh 0vh #000, 0.2vh 0.2vh 0vh #000;
|
||||
border: 0 solid transparent;
|
||||
border-radius: 15px;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
input[type=text]::placeholder {
|
||||
color: #6F98D8;
|
||||
font-size: 40px;
|
||||
text-shadow: -0.15vh -0.15vh 0vh #000, 0.15vh -0.15vh 0vh #000, -0.15vh 0.15vh 0vh #000, 0.15vh 0.15vh 0vh #000;
|
||||
}
|
||||
|
||||
input[type=checkbox] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.gdcheckbox {
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
background-image: url(../assets/check-off.png);
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
padding-right: 40px;
|
||||
height: 42px;
|
||||
text-align: left;
|
||||
margin: 0 15 0 35;
|
||||
}
|
||||
|
||||
input[type=checkbox]:checked + label.gdcheckbox {
|
||||
background-image: url(../assets/check-on.png);
|
||||
}
|
||||
|
||||
.bounce {
|
||||
animation: boxAnimator 0.25s;
|
||||
}
|
||||
|
||||
.gold {
|
||||
color: rgb(255, 200, 0);
|
||||
}
|
||||
|
||||
.blue {
|
||||
color: blue;
|
||||
}
|
||||
|
@ -344,7 +450,7 @@ input:focus, select:focus, textarea:focus, button:focus {
|
|||
}
|
||||
|
||||
.inline {
|
||||
display: inline;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.spin {
|
||||
|
@ -354,6 +460,21 @@ input:focus, select:focus, textarea:focus, button:focus {
|
|||
mix-blend-mode: luminosity;
|
||||
}
|
||||
|
||||
@keyframes boxAnimator {
|
||||
0% {
|
||||
transform: scale(0) translate(-50%, -50%);
|
||||
transform-origin:left top
|
||||
}
|
||||
75% {
|
||||
transform: scale(1.075) translate(-50%, -50%);
|
||||
transform-origin:left top
|
||||
}
|
||||
100% {
|
||||
transform: scale(1) translate(-50%, -50%);
|
||||
transform-origin:left top
|
||||
}
|
||||
}
|
||||
|
||||
@-moz-keyframes spin { 100% { -moz-transform: rotate(360deg); } }
|
||||
@-webkit-keyframes spin { 100% { -webkit-transform: rotate(360deg); } }
|
||||
@keyframes spin { 100% { -webkit-transform: rotate(360deg); transform:rotate(360deg); } }
|
BIN
assets/empty.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
assets/iconkitbuttons/cog.png
Normal file
After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 7.6 KiB |
BIN
assets/iconkitbuttons/shuffle.png
Normal file
After Width: | Height: | Size: 33 KiB |
|
@ -1,62 +1,5 @@
|
|||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// ******** ********** ******* ** ******** **** ** //
|
||||
// **////// /////**/// **/////** /** /**///// /**/** /** //
|
||||
// /** /** ** //**/** /** /**//** /** //
|
||||
// /********* /** /** /**/** /******* /** //** /** //
|
||||
// ////////** /** /** /**/** /**//// /** //**/** //
|
||||
// /** /** //** ** /** /** /** //**** //
|
||||
// ******** /** //******* /********/********/** //*** //
|
||||
// //////// // /////// //////// //////// // /// //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//Stolen from https://github.com/fakemancat/geometry-dash-api/blob/master/classes/XOR.js because I am stupid.
|
||||
|
||||
module.exports = class XOR {
|
||||
// Base64 functions
|
||||
b64from(text) {
|
||||
return Buffer.from(text, 'base64').toString('utf-8');
|
||||
}
|
||||
b64to(text) {
|
||||
return Buffer.from(text).toString('base64');
|
||||
}
|
||||
|
||||
// Xor functions
|
||||
chr(ascii) {
|
||||
return String.fromCodePoint(ascii);
|
||||
}
|
||||
text2ascii(input) {
|
||||
return String(input).split('').map(letter => letter.charCodeAt());
|
||||
}
|
||||
cipher(data, key) {
|
||||
key = this.text2ascii(key);
|
||||
data = this.text2ascii(data);
|
||||
let keysize = key.length;
|
||||
let input_size = data.length;
|
||||
let cipher = '';
|
||||
|
||||
for (let i = 0; i < input_size; i++) {
|
||||
cipher += this.chr(data[i] ^ key[i % keysize]);
|
||||
}
|
||||
return cipher;
|
||||
}
|
||||
encrypt(password, key = 37526) {
|
||||
let encode = this.cipher(password, key);
|
||||
encode = Buffer.from(encode).toString('base64');
|
||||
encode = encode
|
||||
.replace(/\//g, '_')
|
||||
.replace(/\+/g, '-');
|
||||
|
||||
return encode;
|
||||
}
|
||||
decrypt(gjp, key = 37526) {
|
||||
let decode = gjp
|
||||
.replace(/_/g, '/')
|
||||
.replace(/-/g, '+');
|
||||
decode = Buffer.from(decode, 'base64').toString();
|
||||
decode = this.cipher(decode, key);
|
||||
|
||||
return decode;
|
||||
}
|
||||
};
|
||||
xor(str, key) { return String.fromCodePoint(...str.split('').map((char, i) => char.charCodeAt(0) ^ key.toString().charCodeAt(i % key.toString().length))) }
|
||||
encrypt(str, key = 37526) { return Buffer.from(this.xor(str, key)).toString('base64').replace(/\//g, '_').replace(/\+/g, '-'); }
|
||||
decrypt(str, key = 37526) { return this.xor(Buffer.from(str.replace(/\//g, '_').replace(/\+/g, '-'), 'base64').toString(), key) }
|
||||
}
|
|
@ -185,9 +185,9 @@
|
|||
<p>Unlike the Geometry Dash API, both username and ID can be used to fetch a user profile.</p>
|
||||
|
||||
<br>
|
||||
<p class="reveal" onclick="$('#params-profile').slideToggle(100)"><b>Parameters (0)</b></p>
|
||||
<p class="reveal" onclick="$('#params-profile').slideToggle(100)"><b>Parameters (1)</b></p>
|
||||
<div class="subdiv" id="params-profile">
|
||||
<p>No parameters for this one!</p>
|
||||
<p>account: Forces the account ID to be used for fetching (normally Player ID is tried first)</p>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
@ -714,16 +714,17 @@
|
|||
<p style="font-size:15px; margin-top:-7px">This one isn't really part of the API, but dammit, my website my rules</p>
|
||||
|
||||
<br>
|
||||
<p class="reveal" onclick="$('#params-icons').slideToggle(100)"><b>Parameters (7)</b></p>
|
||||
<p class="reveal" onclick="$('#params-icons').slideToggle(100)"><b>Parameters (9)</b></p>
|
||||
<div class="subdiv" id="params-icons">
|
||||
<p><b>Parameters can be used to modify parts of a fetched user's icon</b></p>
|
||||
<p>IDs generally correspond to their order of appearance in GD</p>
|
||||
<p>form: The form of the icon (cube/ship/ball/ufo/wave/robot/spider/cursed)</p>
|
||||
<p>form: The form of the icon (cube/ship/ball/ufo/wave/robot/spider/swing/cursed)</p>
|
||||
<p>icon: The ID of the icon to use</p>
|
||||
<p>col1: The ID of the primary color to use</p>
|
||||
<p>col2: The ID of the secondary color to use</p>
|
||||
<p>col3: Optional color ID to overwrite the 'white' layer used by some detailed icons</p>
|
||||
<p>glow: If the icon should have a glow/outline (0 = off, anything else = on)</p>
|
||||
<p>size: The size (in pixels) the icon should be, in case you don't want the default. Will always be square</p>
|
||||
<p>size: The size in pixels that the icon should be (always square), in case you don't want the default. "Auto" also works.</p>
|
||||
<p>topless: Removes the glass 'dome' from generated UFOs (legacy)</p>
|
||||
<p>noUser: Disables fetching the icon from the GD servers. Slightly faster, but comes at the cost of having to build icons from the ground up using the parameters listed above. It completely ignores the entered username and always returns the default icon</p>
|
||||
</div>
|
||||
|
|
|
@ -13,14 +13,41 @@
|
|||
</head>
|
||||
<body class="iconscroll" style="background-image: linear-gradient(rgb(139, 139, 139), rgb(100, 100, 100));" onbeforeunload="saveUrl()">
|
||||
<div class="center hidden"><br>
|
||||
|
||||
<div class="popup" id="steal">
|
||||
<div class="brownbox bounce center supercenter" style="height: 350px; width: 700px">
|
||||
<h1 class="center gold" style="margin-top: 10px">Copy Icon</h1>
|
||||
<input type="text" name="gdbrowser" id="playerName" autocomplete="off" placeholder="Username" maxlength="32" style="height: 58px; width: 90%; text-align: center; margin-top: 25px; margin-bottom: 5px;">
|
||||
<div id="copyForms"></div>
|
||||
<img src="../assets/ok.png" height=55px; class="postButton gdButton center" style="margin-top: 30px" id="fetchUser">
|
||||
<img class="gdButton xButton" src="../assets/close.png" width="70px" onclick="$('#steal').hide()">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="popup" id="settings">
|
||||
<div class="brownbox bounce center supercenter" style="height: 350px; width: 720px">
|
||||
<h1 class="center gold" style="margin-top: 10px; margin-bottom: 20px;">Settings</h1>
|
||||
<div class="inline"><h2 class="help" help="Removes the clear dome on top of UFOs"><input type="checkbox" class="iconsetting" id="box-ufo"><label for="box-ufo" class="gdcheckbox gdButton"></label>No UFO Dome</h2></div>
|
||||
<div class="inline"><h2 class="help" help="Forces a square aspect ratio on generated icons"><input type="checkbox" class="iconsetting" id="box-square"><label for="box-square" class="gdcheckbox gdButton"></label>Square Image</h2></div>
|
||||
<br>
|
||||
<div class="inline" style="margin-top: 12px"><h2 class="help" help="Adds a third color option for normally non-colorable details"><input type="checkbox" class="iconsetting" id="box-col3"><label for="box-col3" class="gdcheckbox gdButton"></label>Color 3</h2></div>
|
||||
<br>
|
||||
<p class="white" id="helpText" style="font-size: 24px; margin-bottom: 0;">(Hover over a setting for information)</p>
|
||||
<img src="../assets/ok.png" height=55px; class="postButton gdButton center" style="margin-top: 30px" onclick="$('#settings').hide()">
|
||||
<img class="gdButton xButton" src="../assets/close.png" width="70px" onclick="$('#settings').hide()">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<img id="iconkitlogo" src="../assets/iconkit.png" height=50px; style="margin: 7px 0;"><br><br>
|
||||
<img id="loading" src="../assets/loading.png" class="spin" height=95px; style="margin: auto auto 25px auto">
|
||||
<img id="result" src="../icon/icon" download="cube_1.png">
|
||||
<hr id="gdfloor">
|
||||
<div id="menuButtons" style="height: 65px; margin: 0 0 15 0;">
|
||||
<button class="blankButton menuButton" id="downloadIcon" title="Download icon"><a id="downloadLink" download="cube_1.png" href="../icon/icon"><img src="../iconkitbuttons/download.png" width=50px></a></button>
|
||||
<button class="blankButton menuButton" id="generateIcon" title="Generate icon"><img src="../iconkitbuttons/play.png" width=65px></button>
|
||||
<button class="blankButton menuButton" id="fetchUser" title="Get player icon"><img src="../iconkitbuttons/player.png" width=50px></button>
|
||||
<button class="blankButton menuButton" id="customColors" title="Settings" onclick="$('#settings').show()"><img src="../iconkitbuttons/cog.png" width=55px></button>
|
||||
<button class="blankButton menuButton" id="downloadIcon" title="Download icon"><a id="downloadLink" download="cube_1.png" href="../icon/icon"><img src="../iconkitbuttons/download.png" width=55px></a></button>
|
||||
<button class="blankButton menuButton" id="generateIcon" title="Generate icon"><img src="../iconkitbuttons/play.png" width=70px></button>
|
||||
<button class="blankButton menuButton" id="getUserIcon" title="Get player icon"><img src="../iconkitbuttons/player.png" width=55px></button>
|
||||
<button class="blankButton menuButton" id="randomIcon" title="Random Icon"><img src="../iconkitbuttons/shuffle.png" width=55px></button>
|
||||
</div>
|
||||
<div id="iconTabs"></div><br>
|
||||
<div id="iconKitParent" class="iconKit">
|
||||
|
@ -31,9 +58,9 @@
|
|||
<div id="colors" class="iconKit">
|
||||
<div id="col1"></div>
|
||||
<div id="col2"></div>
|
||||
<div style="margin-top: 12px" id="col3"></div>
|
||||
</div><br>
|
||||
<p style="color: rgb(20, 20, 20); margin-top: 3;">All sprites/assets belong to <a target=_blank href="http://robtopgames.com">RobTop Games</a>.</p>
|
||||
<p><a target=_blank href="https://gdbrowser.com/api#icons">API Reference</a></p>
|
||||
<p style="color: rgb(20, 20, 20); margin-top: 3px">Created by <a target=_blank href="https://gdcolon.com">GD Colon</a> • All sprites/assets belong to <a target=_blank href="http://robtopgames.com">RobTop Games</a> • <a target=_blank href="https://gdbrowser.com/api#icons">API Reference</a></p>
|
||||
|
||||
<div style="position:absolute; top: 2%; left: -1.95%; width: 10%; height: 25%; pointer-events: none">
|
||||
<img class="gdButton" style="pointer-events: all" id="backButton" src="../assets/back.png" height="30%" onclick="backButton()">
|
||||
|
@ -52,22 +79,36 @@ $('.hidden').show();
|
|||
let mobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent)
|
||||
let forms = ['cube', 'ship', 'ball', 'ufo', 'wave', 'robot', 'spider', 'swing']
|
||||
let currentForm = 'cube'
|
||||
let formCopy = 'cube'
|
||||
let beenThereDoneThat = false
|
||||
|
||||
let selectedIcon = 1
|
||||
let selectedForm = 'cube'
|
||||
let selectedCol1 = 0
|
||||
let selectedCol2 = 3
|
||||
let selectedCol3 = null
|
||||
let enableGlow = 0
|
||||
|
||||
let imagesLoaded = 0
|
||||
let totalLoaded = 0
|
||||
|
||||
function capitalize(str) {return str[0].toUpperCase() + str.substr(1)}
|
||||
|
||||
if (mobile) $('#logo').attr('width', '80%');
|
||||
|
||||
forms.forEach(form => {$("#iconTabs").append(`<button form="${form}" class="blankButton iconTabButton"><img src="../iconkitbuttons/${form}_off.png" width=50px></button>`)})
|
||||
let iconSettings = localStorage.iconkit.split(",")
|
||||
iconSettings.forEach(x => {
|
||||
$(`#box-${x}`).prop('checked', true)
|
||||
})
|
||||
|
||||
function capitalize(str) { return str[0].toUpperCase() + str.substr(1) }
|
||||
function randInt(min, max) { return Math.floor(Math.random() * (max - min + 1) ) + min }
|
||||
function row3() { if (iconSettings.includes("col3")) $('#col3').show(); else $('#col3').hide() }
|
||||
|
||||
row3()
|
||||
|
||||
forms.forEach(form => {
|
||||
$("#iconTabs").append(`<button form="${form}" class="blankButton iconTabButton"><img src="../iconkitbuttons/${form}_off.png" width=50px></button>`)
|
||||
$("#copyForms").append(`<button form="${form}" class="blankButton copyForm"><img src="../iconkitbuttons/${form}_off.png" width=50px></button>`)
|
||||
})
|
||||
$("#iconTabs").append(`<button title="Glow" class="blankButton glowToggle" id="glowbtn"><img id="glow" src="../iconkitbuttons/streak_off.png" width=50px></button>`)
|
||||
|
||||
forms.forEach(form => {$("#iconKitParent").append(`<div id="${form}s"></div>`)})
|
||||
|
@ -101,15 +142,19 @@ fetch('./api/icons').then(res => {
|
|||
$('[num="1"][form="cube"]').addClass('iconSelected');
|
||||
})
|
||||
|
||||
filterIcon('color').forEach(function (i, p) {
|
||||
$('#col1').append(`<button col=${p} class="blankButton color1" title="Color ${p}" id="col1-${p}"><img src="../gdicon/color_${p}.png" width=50px></button>`)
|
||||
$('#col2').append(`<button col=${p} class="blankButton color2" title="Color ${p}" id="col2-${p}"><img src="../gdicon/color_${p}.png" width=50px></button>`)})
|
||||
filterIcon('color').forEach(function (n, p) {
|
||||
for (i=1; i<=3; i++) {
|
||||
$(`#col${i}`).append(`<button col=${p} class="blankButton color${i}" title="Color ${p}" id="col${i}-${p}"><img src="../gdicon/color_${p}.png" width=50px></button>`)
|
||||
}
|
||||
})
|
||||
|
||||
$('#col1').append("<span style='min-width: 10px'></span>")
|
||||
$('#col2').append("<span style='min-width: 10px'></span>")
|
||||
$('#col3').append("<span style='min-width: 10px'></span>")
|
||||
|
||||
$('.color1[col="0"]').addClass('iconSelected');
|
||||
$('.color2[col="3"]').addClass('iconSelected');
|
||||
$('.color3[col="12"]').addClass('iconSelected');
|
||||
|
||||
$(document).on('click', '.iconTabButton', function () {
|
||||
|
||||
|
@ -135,6 +180,28 @@ fetch('./api/icons').then(res => {
|
|||
})
|
||||
|
||||
$('#iconTabs').find('.iconTabButton').first().children().first().attr('src', $('.iconTabButton').first().children().first().attr('src').replace('_off', '_on'))
|
||||
|
||||
|
||||
$("#randomIcon").click(function() {
|
||||
|
||||
selectedForm = forms[Math.floor(Math.random() * forms.length)]
|
||||
selectedIcon = randInt(0, iconArray.filter(x => x.startsWith(selectedForm)).length)
|
||||
selectedCol1 = randInt(0, iconArray.filter(x => x.startsWith("color")).length) - 1
|
||||
selectedCol2 = randInt(0, iconArray.filter(x => x.startsWith("color")).length) - 1
|
||||
selectedCol3 = null
|
||||
enableGlow = randInt(0, 2) == 1 ? 1 : 0 // 1 in 3 chance of glow
|
||||
|
||||
$('#glow').attr('src', '../iconkitbuttons/streak_off.png')
|
||||
|
||||
$(`.iconTabButton[form=${selectedForm}]`).trigger('click')
|
||||
$(`#${selectedForm}-${selectedIcon}`).trigger('click')
|
||||
$(`#col1-${selectedCol1}`).trigger('click')
|
||||
$(`#col2-${selectedCol2}`).trigger('click')
|
||||
$(`#col3-15`).trigger('click')
|
||||
if (enableGlow == 1) $("#glow").attr('src', $("#glow").attr('src').replace('_off', '_on'))
|
||||
else $("#glow").attr('src', $("#glow").attr('src').replace('_on', '_off'))
|
||||
$("#generateIcon").trigger('click')
|
||||
})
|
||||
})
|
||||
|
||||
$(document).on('click', '.glowToggle', function () {
|
||||
|
@ -151,6 +218,13 @@ fetch('./api/icons').then(res => {
|
|||
|
||||
})
|
||||
|
||||
$(document).on('click', '.copyForm', function () {
|
||||
$('.copyForm').each(function(x, y) {$(this).children().first().attr('src', $(this).children().first().attr('src').replace('_on', '_off'))})
|
||||
formCopy = $(this).attr('form')
|
||||
let src = $(this).children().first().attr('src')
|
||||
$(this).children().first().attr('src', src.replace('_off', '_on'))
|
||||
})
|
||||
|
||||
$(document).on('click', '.iconButton', function () {
|
||||
$(".iconButton").removeClass("iconSelected");
|
||||
$(this).addClass('iconSelected');
|
||||
|
@ -171,11 +245,21 @@ fetch('./api/icons').then(res => {
|
|||
selectedCol2 = $(this).attr('col');
|
||||
})
|
||||
|
||||
$(document).on('click', '.color3', function () {
|
||||
$(".color3").removeClass("iconSelected");
|
||||
$(this).addClass('iconSelected');
|
||||
selectedCol3 = $(this).attr('col');
|
||||
if (selectedCol3 == 12) selectedCol3 = null
|
||||
})
|
||||
|
||||
$("#generateIcon").click(function () {
|
||||
$("#loading").show()
|
||||
$("#result").hide()
|
||||
$("#result").attr('src', `../icon/icon?icon=${selectedIcon}&form=${selectedForm}&col1=${selectedCol1}&col2=${selectedCol2}&glow=${enableGlow}`).attr('download', `${selectedForm}_${selectedIcon}.png`)
|
||||
$("#downloadLink").attr('href', `../icon/icon?icon=${selectedIcon}&form=${selectedForm}&col1=${selectedCol1}&col2=${selectedCol2}&glow=${enableGlow}`).attr('download', `${selectedForm}_${selectedIcon}.png`)
|
||||
let noDome = selectedForm == "ufo" && iconSettings.includes("ufo")
|
||||
let square = iconSettings.includes("square")
|
||||
let finalURL = `../icon/icon?icon=${selectedIcon}&form=${selectedForm}${noDome ? "&topless=1" : ""}&col1=${selectedCol1}&col2=${selectedCol2}${iconSettings.includes("col3") && selectedCol3 ? `&col3=${selectedCol3}` : ""}${enableGlow == 1 ? "&glow=1" : ""}${square ? "&size=auto" : ""}`
|
||||
$("#result").attr('src', finalURL).attr('download', `${selectedForm}_${selectedIcon}.png`)
|
||||
$("#downloadLink").attr('href', finalURL).attr('download', `${selectedForm}_${selectedIcon}.png`)
|
||||
})
|
||||
|
||||
$('#result').on('load', function() {
|
||||
|
@ -185,25 +269,34 @@ fetch('./api/icons').then(res => {
|
|||
else $("#gdfloor").css('margin-top', '0px')
|
||||
})
|
||||
|
||||
$("#fetchUser").click(function () {
|
||||
|
||||
let user = prompt(`Enter a player's username to get their ${currentForm == "ufo" ? "UFO" : currentForm}!\n(Form will depend on selected tab)\n`)
|
||||
if (!user) return;
|
||||
$("#getUserIcon").click(function() {
|
||||
$(`.copyForm[form=${currentForm}]`).trigger('click')
|
||||
$('#steal').show();
|
||||
$('#playerName').focus()
|
||||
})
|
||||
|
||||
let iconURL = `../icon/${user}?form=${currentForm}`
|
||||
$("#fetchUser").click(function () {
|
||||
|
||||
let user = $("#playerName").val()
|
||||
if (!user || !user.length) return $("#steal").hide()
|
||||
|
||||
let iconURL = `../icon/${user}?form=${formCopy}`
|
||||
$(`.iconTabButton[form=${formCopy}]`).trigger('click')
|
||||
$("#steal").hide()
|
||||
$("#loading").show()
|
||||
$("#result").hide()
|
||||
$("#result").attr('src', iconURL).attr('download', `${user}_${currentForm}.png`)
|
||||
$("#downloadLink").attr('href', iconURL).attr('download', `${user}_${currentForm}.png`)
|
||||
$("#result").attr('src', iconURL).attr('download', `${user}_${formCopy}.png`)
|
||||
$("#downloadLink").attr('href', iconURL).attr('download', `${user}_${formCopy}.png`)
|
||||
$('#glow').attr('src', '../iconkitbuttons/streak_off.png')
|
||||
enableGlow = 0
|
||||
|
||||
fetch('../api/profile/' + user).then(res => res.json())
|
||||
.then(info => {
|
||||
$(`#${currentForm}-${info.icon}`).trigger('click')
|
||||
$(`#${formCopy}-${info[formCopy == "cube" ? "icon" : formCopy] || 1}`).trigger('click')
|
||||
$(`#col1-${info.col1}`).trigger('click')
|
||||
$(`#col2-${info.col2}`).trigger('click')
|
||||
if (info.glow)$('#glowbtn').trigger('click')
|
||||
$(`#col3-12`).trigger('click')
|
||||
if (info.glow) $('#glowbtn').trigger('click')
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -222,13 +315,43 @@ function backButton() {
|
|||
window.location.href = "../../../../../"
|
||||
}
|
||||
|
||||
let hoverText = $('#helpText').html()
|
||||
$(".help").hover(function() {
|
||||
$(this).css('color', 'rgba(200, 255, 255)')
|
||||
$('#helpText').html($(this).attr('help'))
|
||||
}, function() {
|
||||
$(this).css('color', 'white')
|
||||
$('#helpText').html(hoverText)
|
||||
})
|
||||
|
||||
$(document).on('change', '.iconsetting', function (e) {
|
||||
let checkedSettings = []
|
||||
$('.iconsetting:checkbox:checked').each((i, x) => { checkedSettings.push(x.id.split('-')[1]) })
|
||||
iconSettings = checkedSettings
|
||||
localStorage.iconkit = checkedSettings.join(",")
|
||||
row3()
|
||||
})
|
||||
|
||||
$(document).keydown(function(k) {
|
||||
if (k.keyCode == 27) { //esc
|
||||
if (k.keyCode == 13) {
|
||||
if ($("#steal").is(":visible")) $("#fetchUser").trigger('click')
|
||||
else $("#generateIcon").trigger('click')
|
||||
}
|
||||
if (k.keyCode == 27) { //esc
|
||||
if ($(".popup").is(":visible")) return $('.popup').hide()
|
||||
k.preventDefault()
|
||||
$('#backButton').trigger('click')
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on('click', '.brownbox', function (e) {
|
||||
e.stopPropagation();
|
||||
})
|
||||
|
||||
$(document).on('click', '.popup', function () {
|
||||
$('.popup').hide()
|
||||
})
|
||||
|
||||
function saveUrl() {
|
||||
sessionStorage.setItem('prevUrl', window.location.href);
|
||||
}
|
||||
|
|