A lot of refactoring, and etc #232

Open
Rudxain wants to merge 62 commits from Rudxain/patch-1 into master
61 changed files with 2167 additions and 1946 deletions

View file

@ -1,3 +1,4 @@
"use strict";
const zlib = require('zlib')
const blocks = require('../misc/analysis/blocks.json')
const colorStuff = require('../misc/analysis/colorProperties.json')
@ -7,121 +8,130 @@ const ids = require('../misc/analysis/objects.json')
module.exports = async (app, req, res, level) => {
if (!level) {
level = {
name: (req.body.name || "Unnamed").slice(0, 64),
data: (req.body.data || "")
}
level ||= {
name: (req.body.name || "Unnamed").slice(0, 64),
data: (req.body.data || "")
}
let unencrypted = level.data.startsWith('kS') // some gdps'es don't encrypt level data
let levelString = unencrypted ? level.data : Buffer.from(level.data, 'base64')
if (unencrypted) {
const raw_data = level.data;
const response_data = analyze_level(level, raw_data);
return res.send(response_data);
} else {
const raw_data = level.data
const response_data = analyze_level(level, raw_data)
return res.send(response_data)
}
else {
zlib.unzip(levelString, (err, buffer) => {
if (err) { return res.status(500).send("-2"); }
if (err) return res.status(500).send("-2")
const raw_data = buffer.toString();
const response_data = analyze_level(level, raw_data);
return res.send(response_data);
});
const raw_data = buffer.toString()
const response_data = analyze_level(level, raw_data)
return res.send(response_data)
})
}
}
function sortObj(obj, sortBy) {
var sorted = {}
var keys = !sortBy ? Object.keys(obj).sort((a,b) => obj[b] - obj[a]) : Object.keys(obj).sort((a,b) => obj[b][sortBy] - obj[a][sortBy])
/**
* Sorts any `Object` by its keys
* @param {{}} obj
* @param {PropertyKey} [sortBy] optional inner key to sort
*/
const sortObj = (obj, sortBy) => {
let keys = Object.keys(obj)
.sort((a,b) => sortBy ? obj[b][sortBy] - obj[a][sortBy] : obj[b] - obj[a])
let sorted = {}
keys.forEach(x => {sorted[x] = obj[x]})
return sorted
}
function parse_obj(obj, splitter, name_arr, valid_only) {
const s_obj = obj.split(splitter);
let robtop_obj = {};
/**
* game-object (**not** JS `Object`) parser
* @param {string} obj
* @param {string} splitter
* @param {string[]} name_arr
* @param {boolean} [valid_only]
*/
const parse_obj = (obj, splitter, name_arr, valid_only) => {
const s_obj = obj.split(splitter)
let robtop_obj = {}
// semi-useless optimization depending on where at node js you're at
for (let i = 0, obj_l = s_obj.length; i < obj_l; i += 2) {
let k_name = s_obj[i];
let k_name = s_obj[i]
if (s_obj[i] in name_arr) {
if (!valid_only) {
k_name = name_arr[s_obj[i]];
}
robtop_obj[k_name] = s_obj[i + 1];
if (!valid_only) k_name = name_arr[s_obj[i]]
robtop_obj[k_name] = s_obj[i + 1]
}
}
return robtop_obj;
return robtop_obj
}
/**
* @param {{}} level
* @param {string} rawData
*/
function analyze_level(level, rawData) {
let response = {};
let blockCounts = {}
let miscCounts = {}
let triggerGroups = []
let blockCounts = {};
let miscCounts = {};
/**@type {string[]}*/
let triggerGroups = [];
let highDetail = 0
let alphaTriggers = []
/**@type {{}[]}*/
let alphaTriggers = [];
let misc_objects = {};
let block_ids = {};
for (const [name, object_ids] of Object.entries(ids.misc)) {
const copied_ids = object_ids.slice(1);
const copied_ids = object_ids.slice(1)
// funny enough, shift effects the original id list
copied_ids.forEach((object_id) => {
misc_objects[object_id] = name;
});
copied_ids.forEach(object_id => { misc_objects[object_id] = name })
}
for (const [name, object_ids] of Object.entries(blocks)) {
object_ids.forEach((object_id) => {
block_ids[object_id] = name;
});
}
for (const [name, object_ids] of Object.entries(blocks))
object_ids.forEach(object_id => { block_ids[object_id] = name });
const data = rawData.split(";");
const header = data.shift();
/**@type {(string|{})[]}*/
const data = rawData.split(";")
const header = data.shift()
let level_portals = [];
let level_coins = [];
let level_text = [];
// "why are these Objects instead of Arrays?" @Rudxain
let orb_array = {};
let trigger_array = {};
let last = 0;
let last = 0
const obj_length = data.length;
const obj_length = data.length
for (let i = 0; i < obj_length; ++i) {
obj = parse_obj(data[i], ',', properties);
let obj = parse_obj(data[i], ',', properties)
let id = obj.id
let {id} = obj
if (id in ids.portals) {
obj.portal = ids.portals[id];
level_portals.push(obj);
obj.portal = ids.portals[id]
level_portals.push(obj)
} else if (id in ids.coins) {
obj.coin = ids.coins[id];
level_coins.push(obj);
obj.coin = ids.coins[id]
level_coins.push(obj)
} else if (id in ids.orbs) {
obj.orb = ids.orbs[id];
obj.orb = ids.orbs[id]
if (obj.orb in orb_array) {
orb_array[obj.orb]++;
} else {
orb_array[obj.orb] = 1;
}
const orb = orb_array[obj.orb]
orb_array[obj.orb] = orb ? +orb + 1 : 1
} else if (id in ids.triggers) {
obj.trigger = ids.triggers[id];
obj.trigger = ids.triggers[id]
if (obj.trigger in trigger_array) {
trigger_array[obj.trigger]++;
trigger_array[obj.trigger]++
} else {
trigger_array[obj.trigger] = 1;
trigger_array[obj.trigger] = 1
}
}
@ -130,60 +140,66 @@ function analyze_level(level, rawData) {
}
if (obj.triggerGroups) obj.triggerGroups.split('.').forEach(x => triggerGroups.push(x))
if (obj.highDetail == 1) highDetail += 1
if (obj.highDetail == 1) highDetail++
if (id in misc_objects) {
const name = misc_objects[id];
const name = misc_objects[id]
if (name in miscCounts) {
miscCounts[name][0] += 1;
miscCounts[name][0]++
} else {
miscCounts[name] = [1, ids.misc[name][0]];
miscCounts[name] = [1, ids.misc[name][0]]
}
}
if (id in block_ids) {
const name = block_ids[id];
const name = block_ids[id]
if (name in blockCounts) {
blockCounts[name] += 1;
blockCounts[name]++
} else {
blockCounts[name] = 1;
blockCounts[name] = 1
}
}
if (obj.x) { // sometimes the field doesn't exist
last = Math.max(last, obj.x);
}
// sometimes the field doesn't exist
if (obj.x) last = Math.max(last, obj.x)
if (obj.trigger == "Alpha") { // invisible triggers
alphaTriggers.push(obj)
}
// invisible triggers
if (obj.trigger == "Alpha") alphaTriggers.push(obj)
data[i] = obj;
data[i] = obj
}
let invisTriggers = []
alphaTriggers.forEach(tr => {
if (tr.x < 500 && !tr.touchTriggered && !tr.spawnTriggered && tr.opacity == 0 && tr.duration == 0
&& alphaTriggers.filter(x => x.targetGroupID == tr.targetGroupID).length == 1) invisTriggers.push(Number(tr.targetGroupID))
if (
tr.x < 500
&& !tr.touchTriggered && !tr.spawnTriggered
&& tr.opacity == 0 && tr.duration == 0
&& alphaTriggers.filter(x => x.targetGroupID == tr.targetGroupID).length == 1
)
invisTriggers.push(Number(tr.targetGroupID))
})
response.level = {
name: level.name, id: level.id, author: level.author, playerID: level.playerID, accountID: level.accountID, large: level.large
}
response.level = {};
['name', 'id', 'author', 'playerID', 'accountID', 'large'].forEach(k => {response.level[k] = level[k]})
response.objects = data.length - 2
response.highDetail = highDetail
response.settings = {}
response.portals = level_portals.sort(function (a, b) {return parseInt(a.x) - parseInt(b.x)}).map(x => x.portal + " " + Math.floor(x.x / (Math.max(last, 529.0) + 340.0) * 100) + "%").join(", ")
response.coins = level_coins.sort(function (a, b) {return parseInt(a.x) - parseInt(b.x)}).map(x => Math.floor(x.x / (Math.max(last, 529.0) + 340.0) * 100))
// "I have no idea what to name this lmao" @Rudxain
let WTF = x => Math.floor(x.x / (Math.max(last, 529) + 340) * 100)
response.portals = level_portals.sort((a, b) => parseInt(a.x) - parseInt(b.x)).map(x => x.portal + " " + WTF(x) + "%").join(", ")
response.coins = level_coins.sort((a, b) => parseInt(a.x) - parseInt(b.x)).map(WTF)
response.coinsVerified = level.verifiedCoins
/**@param {number[]} arr*/
const sum = arr => arr.reduce((a, x) => a + x, 0)
response.orbs = orb_array
response.orbs.total = Object.values(orb_array).reduce((a, x) => a + x, 0); // we already have an array of objects, use it
response.orbs.total = sum(Object.values(orb_array)) // we already have an array of objects, use it
response.triggers = trigger_array
response.triggers.total = Object.values(trigger_array).reduce((a, x) => a + x, 0);
response.triggers.total = sum(Object.values(trigger_array))
response.triggerGroups = {}
response.blocks = sortObj(blockCounts)
@ -191,7 +207,7 @@ function analyze_level(level, rawData) {
response.colors = []
triggerGroups.forEach(x => {
if (response.triggerGroups['Group ' + x]) response.triggerGroups['Group ' + x] += 1
if (response.triggerGroups['Group ' + x]) response.triggerGroups['Group ' + x]++
else response.triggerGroups['Group ' + x] = 1
})
@ -202,99 +218,113 @@ function analyze_level(level, rawData) {
// find alpha group with the most objects
response.invisibleGroup = triggerKeys.find(x => invisTriggers.includes(x))
response.text = level_text.sort(function (a, b) {return parseInt(a.x) - parseInt(b.x)}).map(x => [Buffer.from(x.message, 'base64').toString(), Math.round(x.x / last * 99) + "%"])
response.text = level_text.sort((a, b) => parseInt(a.x) - parseInt(b.x)).map(x => [Buffer.from(x.message, 'base64').toString(), Math.round(x.x / last * 99) + "%"])
const header_response = parse_header(header);
response.settings = header_response.settings;
response.colors = header_response.colors;
const header_response = parse_header(header)
response.settings = header_response.settings
response.colors = header_response.colors
response.dataLength = rawData.length
response.data = rawData
return response;
return response
}
function parse_header(header) {
let response = {};
response.settings = {};
response.colors = [];
function parse_header(/**@type {string}*/ header) {
let response = {}
response.settings = {}
response.colors = []
const header_keyed = parse_obj(header, ',', init.values, true);
const header_keyed = parse_obj(header, ',', init.values, true)
Object.keys(header_keyed).forEach(x => {
let val = init.values[x]
Object.keys(header_keyed).forEach(k => {
let val = init.values[k]
/**@type {string}*/
let name = val[0]
let property = header_keyed[x]
let property = header_keyed[k]
switch (val[1]) {
case 'list':
val = init[(val[0] + "s")][property];
break;
break
case 'number':
val = Number(property);
break;
break
case 'bump':
val = Number(property) + 1;
break;
break
case 'bool':
val = property != "0";
break;
break
case 'extra-legacy-color': { // scope?
// you can only imagine my fear when i discovered this was a thing
// these literally are keys set the value, and to convert this to the color list we have to do this fun messy thing that shouldn't exist
// since i wrote the 1.9 color before this, a lot of explaination will be there instead
const colorInfo = name.split('-');
const color = colorInfo[2]; // r,g,b
const channel = colorInfo[1];
const colorInfo = name.split('-')
/** r,g,b */
const color = colorInfo[2]
const channel = colorInfo[1]
// first we create the color object
if (color == 'r') response.colors.push({"channel": channel, "opacity": 1})
if (color == 'r') {
// first we create the color object
response.colors.push({"channel": channel, "opacity": 1});
}
// from here we touch the color object
let currentChannel = response.colors.find(k => k.channel == channel);
if (color == 'blend') {
currentChannel.blending = true; // only one color has blending though lol
} else if (color == 'pcol' && property != 0) {
currentChannel.pColor = property;
}
currentChannel[color] = property;
break;
let currentChannel = response.colors.find(k => k.channel == channel)
if (color == 'blend') currentChannel.blending = true // only one color has blending though lol
if (color == 'pcol' && property != 0) currentChannel.pColor = property
currentChannel[color] = property
break
}
case 'legacy-color': {
// if a level has a legacy color, we can assume that it does not have a kS38 at all
const color = parse_obj(property, "_", colorStuff.properties);
const color = parse_obj(property, "_", colorStuff.properties)
let colorObj = color
// so here we parse the color to something understandable by the rest
// slightly smart naming but it is also pretty gross
// in a sense - the name would be something like legacy-G -> G
const colorVal = name.split('-').pop()
const colorVal = name.split('-').at(-1)
colorObj.channel = colorVal
// from here stuff can continue as normal, ish
if (colorObj.pColor == "-1" || colorObj.pColor == "0") delete colorObj.pColor;
colorObj.opacity = 1; // 1.9 colors don't have this!
if (colorObj.blending && colorObj.blending == '1') colorObj.blending = true; // 1.9 colors manage to always think they're blending - they're not
else delete colorObj.blending;
if (colorObj.pColor == "-1" || colorObj.pColor == "0")
delete colorObj.pColor
colorObj.opacity = 1 // 1.9 colors don't have this!
if (colorVal == '3DL') { response.colors.splice(4, 0, colorObj); } // hardcode the position of 3DL, it typically goes at the end due to how RobTop make the headers
else if (colorVal == 'Line') { colorObj.blending = true; response.colors.push(colorObj); } // in line with 2.1 behavior
else { response.colors.push(colorObj); } // bruh whatever was done to make the color list originally was long
break;
if (colorObj?.blending === '1')
colorObj.blending = true // 1.9 colors manage to always think they're blending - they're not
else
delete colorObj.blending
switch (colorVal) {
case '3DL':
response.colors.splice(4, 0, colorObj) // hardcode the position of 3DL, it typically goes at the end due to how RobTop make the headers
break
case 'Line': {
colorObj.blending = true; response.colors.push(colorObj) // in line with 2.1 behavior
break
}
default:
response.colors.push(colorObj) // bruh whatever was done to make the color list originally was long
break
}
break
}
case 'colors': {
let colorList = property.split("|")
colorList.forEach((x, y) => {
const color = parse_obj(x, "_", colorStuff.properties)
let colorObj = color
if (!color.channel) return colorList = colorList.filter((h, i) => y != i)
if (!color.channel) return colorList = colorList.filter((_, i) => y != i)
if (colorStuff.channels[colorObj.channel]) colorObj.channel = colorStuff.channels[colorObj.channel]
if (colorObj.channel > 1000) return;
if (colorObj.channel > 1000) return
if (colorStuff.channels[colorObj.copiedChannel]) colorObj.copiedChannel = colorStuff.channels[colorObj.copiedChannel]
if (colorObj.copiedChannel > 1000) delete colorObj.copiedChannel;
if (colorObj.copiedChannel > 1000) delete colorObj.copiedChannel
if (colorObj.pColor == "-1") delete colorObj.pColor
if (colorObj.blending) colorObj.blending = true
if (colorObj.copiedHSV) {
@ -303,37 +333,39 @@ function parse_header(header) {
hsv.forEach((x, y) => { colorObj.copiedHSV[colorStuff.hsv[y]] = x })
colorObj.copiedHSV['s-checked'] = colorObj.copiedHSV['s-checked'] == 1
colorObj.copiedHSV['v-checked'] = colorObj.copiedHSV['v-checked'] == 1
if (colorObj.copyOpacity == 1) colorObj.copyOpacity = true
if (colorObj.copyOpacity == 1) colorObj.copyOpacity = true
}
colorObj.opacity = +Number(colorObj.opacity).toFixed(2)
colorList[y] = colorObj
});
})
// we assume this is only going to be run once so... some stuff can go here
colorList = colorList.filter(x => typeof x == "object")
if (!colorList.find(x => x.channel == "Obj")) colorList.push({"r": "255", "g": "255", "b": "255", "channel": "Obj", "opacity": "1"})
const specialSort = ["BG", "G", "G2", "Line", "Obj", "3DL"]
let specialColors = colorList.filter(x => isNaN(x.channel)).sort(function (a, b) {return specialSort.indexOf( a.channel ) > specialSort.indexOf( b.channel ) } )
let regularColors = colorList.filter(x => !isNaN(x.channel)).sort(function(a, b) {return (+a.channel) - (+b.channel) } );
let specialColors = colorList.filter(x => isNaN(x.channel)).sort((a, b) => specialSort.indexOf(a.channel) > specialSort.indexOf(b.channel))
let regularColors = colorList.filter(x => !isNaN(x.channel)).sort((a, b) => a.channel - b.channel)
response.colors = specialColors.concat(regularColors)
break;
break
}
}
response.settings[name] = val
})
if (!response.settings.ground || response.settings.ground > 17) response.settings.ground = 1
if (!response.settings.background || response.settings.background > 20) response.settings.background = 1
if (!response.settings.font) response.settings.font = 1
if (!response.settings.ground || response.settings.ground > 17)
response.settings.ground = 1
if (!response.settings.background || response.settings.background > 20)
response.settings.background = 1
if (!response.settings.font)
response.settings.font = 1
if (response.settings.alternateLine == 2) response.settings.alternateLine = true
else response.settings.alternateLine = false
response.settings.alternateLine = response.settings.alternateLine == 2
Object.keys(response.settings).filter(k => {
// this should be parsed into color list instead
if (k.includes('legacy')) delete response.settings[k];
});
if (k.includes('legacy')) delete response.settings[k]
})
delete response.settings['colors'];
return response;
delete response.settings['colors']
return response
}

View file

@ -1,3 +1,4 @@
"use strict";
const Player = require('../classes/Player.js')
module.exports = async (app, req, res) => {
@ -8,8 +9,8 @@ module.exports = async (app, req, res) => {
if (count > 1000) count = 1000
let params = {
userID : req.params.id,
accountID : req.params.id,
userID : req.params.id,
accountID : req.params.id,
levelID: req.params.id,
page: +req.query.page || 0,
count,
@ -20,7 +21,7 @@ module.exports = async (app, req, res) => {
if (req.query.type == "commentHistory") { path = "getGJCommentHistory"; delete params.levelID }
else if (req.query.type == "profile") path = "getGJAccountComments20"
req.gdRequest(path, req.gdParams(params), function(err, resp, body) {
req.gdRequest(path, req.gdParams(params), function(err, resp, body) {
if (err) return res.sendError()
@ -32,7 +33,7 @@ module.exports = async (app, req, res) => {
if (!comments.length) return res.status(204).send([])
let pages = body.split('#')[1].split(":")
let lastPage = +Math.ceil(+pages[0] / +pages[2]);
let lastPage = +Math.ceil(+pages[0] / +pages[2])
let commentArray = []
@ -41,7 +42,7 @@ module.exports = async (app, req, res) => {
var x = c[0] //comment info
var y = c[1] //account info
if (!x[2]) return;
if (!x[2]) return
let comment = {}
comment.content = Buffer.from(x[2], 'base64').toString();
@ -50,9 +51,9 @@ module.exports = async (app, req, res) => {
comment.date = (x[9] || "?") + req.timestampSuffix
if (comment.content.endsWith("⍟") || comment.content.endsWith("☆")) {
comment.content = comment.content.slice(0, -1)
comment.browserColor = true
comment.browserColor = true
}
if (req.query.type != "profile") {
let commentUser = new Player(y)
Object.keys(commentUser).forEach(k => {
@ -74,7 +75,7 @@ module.exports = async (app, req, res) => {
commentArray.push(comment)
})
})
return res.send(commentArray)

View file

@ -1,3 +1,4 @@
"use strict";
const request = require('request')
const fs = require('fs')
const Level = require('../classes/Level.js')
@ -5,27 +6,27 @@ const Level = require('../classes/Level.js')
module.exports = async (app, req, res, api, ID, analyze) => {
function rejectLevel() {
if (!api) return res.redirect('search/' + req.params.id)
else return res.sendError()
return !api ? res.redirect('search/' + req.params.id) : res.sendError()
}
if (req.offline) {
if (!api && levelID < 0) return res.redirect('/')
return rejectLevel()
return !api && levelID < 0 ? res.redirect('/') : rejectLevel()
}
let levelID = ID || req.params.id
if (levelID == "daily") levelID = -1
else if (levelID == "weekly") levelID = -2
else levelID = levelID.replace(/[^0-9]/g, "")
levelID = (
levelID == "daily" ? -1 :
levelID == "weekly" ? -2 :
levelID.replace(/\D/g, "")
)
req.gdRequest('downloadGJLevel22', { levelID }, function (err, resp, body) {
if (err) {
if (analyze && api && req.server.downloadsDisabled) return res.status(403).send("-3")
else if (!api && levelID < 0) return res.redirect(`/?daily=${levelID * -1}`)
else return rejectLevel()
}
if (err) return (
analyze && api && req.server.downloadsDisabled ? res.status(403).send("-3")
: !api && levelID < 0 ? res.redirect(`/?daily=${levelID * -1}`)
: rejectLevel()
)
let authorData = body.split("#")[3] // daily/weekly only, most likely
@ -43,7 +44,7 @@ module.exports = async (app, req, res, api, ID, analyze) => {
if (err2 && (foundID || authorData)) {
let authorInfo = foundID || authorData.split(":")
level.author = authorInfo[1] || "-"
level.accountID = authorInfo[0] && authorInfo[0].includes(",") ? "0" : authorInfo[0]
level.accountID = authorInfo[0]?.includes(",") ? "0" : authorInfo[0]
}
else if (!err && b2 != '-1') {
@ -72,7 +73,7 @@ module.exports = async (app, req, res, api, ID, analyze) => {
if (api) return res.send(level)
else return fs.readFile('./html/level.html', 'utf8', function (err, data) {
let html = data;
let html = data
let variables = Object.keys(level)
variables.forEach(x => {
let regex = new RegExp(`\\[\\[${x.toUpperCase()}\\]\\]`, "g")
@ -89,7 +90,7 @@ module.exports = async (app, req, res, api, ID, analyze) => {
level.nextDaily = +dailyTime
level.nextDailyTimestamp = Math.round((Date.now() + (+dailyTime * 1000)) / 100000) * 100000
return sendLevel()
})
})
}
else if (req.server.demonList && level.difficulty == "Extreme Demon") {

View file

@ -1,3 +1,4 @@
"use strict";
let cache = {}
let gauntletNames = ["Fire", "Ice", "Poison", "Shadow", "Lava", "Bonus", "Chaos", "Demon", "Time", "Crystal", "Magic", "Spike", "Monster", "Doom", "Death"]
@ -6,17 +7,19 @@ module.exports = async (app, req, res) => {
if (req.offline) return res.sendError()
let cached = cache[req.id]
if (app.config.cacheGauntlets && cached && cached.data && cached.indexed + 2000000 > Date.now()) return res.send(cached.data) // half hour cache
if (app.config.cacheGauntlets && cached && cached.data && cached.indexed + 2000000 > Date.now())
return res.send(cached.data) // half hour cache
req.gdRequest('getGJGauntlets21', {}, function (err, resp, body) {
if (err) return res.sendError()
let gauntlets = body.split('#')[0].split('|').map(x => app.parseResponse(x)).filter(x => x[3])
let gauntlets = body.split('#', 1)[0].split('|').map(x => app.parseResponse(x)).filter(x => x[3])
let gauntletList = gauntlets.map(x => ({ id: +x[1], name: gauntletNames[+x[1] - 1] || "Unknown", levels: x[3].split(",") }))
if (app.config.cacheGauntlets) cache[req.id] = {data: gauntletList, indexed: Date.now()}
if (app.config.cacheGauntlets)
cache[req.id] = {data: gauntletList, indexed: Date.now()}
res.send(gauntletList)
})
}

View file

@ -1,5 +1,6 @@
const {GoogleSpreadsheet} = require('google-spreadsheet');
const sheet = new GoogleSpreadsheet('1ADIJvAkL0XHGBDhO7PP9aQOuK3mPIKB2cVPbshuBBHc'); // accurate leaderboard spreadsheet
"use strict";
const {GoogleSpreadsheet} = require('google-spreadsheet')
const sheet = new GoogleSpreadsheet('1ADIJvAkL0XHGBDhO7PP9aQOuK3mPIKB2cVPbshuBBHc') // accurate leaderboard spreadsheet
let indexes = ["stars", "coins", "demons", "diamonds"]
@ -9,39 +10,41 @@ let caches = [{"stars": null, "coins": null, "demons": null, "diamonds": null},
module.exports = async (app, req, res, post) => {
// Accurate leaderboard returns 418 because private servers do not use.
if (req.isGDPS) return res.status(418).send("-2")
if (!app.sheetsKey) return res.status(500).send([])
let gdMode = post || req.query.hasOwnProperty("gd")
let modMode = !gdMode && req.query.hasOwnProperty("mod")
let cache = caches[gdMode ? 2 : modMode ? 1 : 0]
// Accurate leaderboard returns 418 because private servers do not use.
if (req.isGDPS) return res.status(418).send("-2")
if (!app.sheetsKey) return res.status(500).send([])
const gdMode = post || req.query.hasOwnProperty("gd")
const modMode = !gdMode && req.query.hasOwnProperty("mod")
let cache = caches[gdMode ? 2 : modMode ? 1 : 0]
let type = req.query.type ? req.query.type.toLowerCase() : 'stars'
if (type == "usercoins") type = "coins"
if (!indexes.includes(type)) type = "stars"
if (lastIndex[modMode ? 1 : 0][type] + 600000 > Date.now() && cache[type]) return res.send(gdMode ? cache[type] : JSON.parse(cache[type])) // 10 min cache
let type = req.query.type ? req.query.type.toLowerCase() : 'stars'
if (type == "usercoins") type = "coins"
if (!indexes.includes(type)) type = "stars"
if (lastIndex[modMode ? 1 : 0][type] + 600000 > Date.now() && cache[type])
return res.send(gdMode ? cache[type] : JSON.parse(cache[type])) // 10 min cache
sheet.useApiKey(app.sheetsKey)
sheet.loadInfo().then(async () => {
let tab = sheet.sheetsById[1555821000]
await tab.loadCells('A2:H2')
sheet.useApiKey(app.sheetsKey)
sheet.loadInfo().then(async () => {
let tab = sheet.sheetsById[1555821000]
await tab.loadCells('A2:H2')
let cellIndex = indexes.findIndex(x => type == x)
if (modMode) cellIndex += indexes.length
let cellIndex = indexes.indexOf(type)
if (modMode) cellIndex += indexes.length
let cell = tab.getCell(1, cellIndex).value
if (!cell || typeof cell != "string" || cell.startsWith("GoogleSpreadsheetFormulaError")) { console.log("Spreadsheet Error:"); console.log(cell); return res.sendError() }
let leaderboard = JSON.parse(cell.replace(/~( |$)/g, ""))
let gdFormatting = ""
leaderboard.forEach(x => {
app.userCache(req.id, x.accountID, x.playerID, x.username)
gdFormatting += `1:${x.username}:2:${x.playerID}:13:${x.coins}:17:${x.usercoins}:6:${x.rank}:9:${x.icon.icon}:10:${x.icon.col1}:11:${x.icon.col2}:14:${forms.indexOf(x.icon.form)}:15:${x.icon.glow ? 2 : 0}:16:${x.accountID}:3:${x.stars}:8:${x.cp}:46:${x.diamonds}:4:${x.demons}|`
})
caches[modMode ? 1 : 0][type] = JSON.stringify(leaderboard)
caches[2][type] = gdFormatting
lastIndex[modMode ? 1 : 0][type] = Date.now()
return res.send(gdMode ? gdFormatting : leaderboard)
let cell = tab.getCell(1, cellIndex).value
if (!cell || typeof cell != "string" || cell.startsWith("GoogleSpreadsheetFormulaError")) {
console.log("Spreadsheet Error:"); console.log(cell); return res.sendError()
}
let leaderboard = JSON.parse(cell.replace(/~( |$)/g, ""))
let gdFormatting = ""
leaderboard.forEach(x => {
app.userCache(req.id, x.accountID, x.playerID, x.username)
gdFormatting += `1:${x.username}:2:${x.playerID}:13:${x.coins}:17:${x.usercoins}:6:${x.rank}:9:${x.icon.icon}:10:${x.icon.col1}:11:${x.icon.col2}:14:${forms.indexOf(x.icon.form)}:15:${x.icon.glow ? 2 : 0}:16:${x.accountID}:3:${x.stars}:8:${x.cp}:46:${x.diamonds}:4:${x.demons}|`
})
caches[modMode ? 1 : 0][type] = JSON.stringify(leaderboard)
caches[2][type] = gdFormatting
lastIndex[modMode ? 1 : 0][type] = Date.now()
return res.send(gdMode ? gdFormatting : leaderboard)
})
}

View file

@ -1,12 +1,15 @@
"use strict";
const request = require('request')
module.exports = async (app, req, res) => {
// Accurate leaderboard returns 418 because Private servers do not use.
if (req.isGDPS) return res.status(418).send("0")
// Accurate leaderboard returns 418 because Private servers do not use.
if (req.isGDPS) return res.status(418).send("0")
request.post('http://robtopgames.com/Boomlings/get_scores.php', {
form : { secret: app.config.params.secret || "Wmfd2893gb7", name: "Player" } }, function(err, resp, body) {
request.post(
'http://robtopgames.com/Boomlings/get_scores.php',
{ form : { secret: app.config.params.secret || "Wmfd2893gb7", name: "Player" } },
function(err, resp, body) {
if (err || !body || body == 0) return res.status(500).send("0")
@ -24,7 +27,7 @@ module.exports = async (app, req, res) => {
score: +scores.slice(3, 10),
boomling: +visuals.slice(5, 7),
boomlingLevel: +visuals.slice(2, 4),
powerups: [+visuals.slice(7, 9), +visuals.slice(9, 11), +visuals.slice(11, 13)].map(x => (x > 8 || x < 1) ? 0 : x),
powerups: [+visuals.slice(7, 9), +visuals.slice(9, 11), +visuals.slice(11, 13)].map(x => (x > 8 || x < 1) ? 0 : x),
unknownVisual: +visuals.slice(0, 2),
unknownScore: +scores.slice(0, 1),
@ -34,10 +37,10 @@ module.exports = async (app, req, res) => {
if (!user.boomling || user.boomling > 66 || user.boomling < 0) user.boomling = 0
if (!user.boomlingLevel || user.boomlingLevel > 25 || user.boomlingLevel < 1) user.boomlingLevel = 25
users.push(user)
users.push(user)
})
return res.send(users)
})
}
)
}

View file

@ -1,53 +1,50 @@
const colors = require('../../iconkit/sacredtexts/colors.json');
"use strict";
const colors = require('../../iconkit/sacredtexts/colors.json')
module.exports = async (app, req, res) => {
if (req.offline) return res.sendError()
let amount = 100;
let count = req.query.count ? parseInt(req.query.count) : null
if (count && count > 0) {
if (count > 200) amount = 200
else amount = count;
}
let amount = 100
let count = req.query.count ? parseInt(req.query.count) : null
if (count && count > 0) amount = Math.min(count, 200)
let params = {
levelID: req.params.id,
accountID: app.id,
gjp: app.gjp,
type: req.query.hasOwnProperty("week") ? "2" : "1",
}
let params = {
levelID: req.params.id,
accountID: app.id,
gjp: app.gjp,
type: req.query.hasOwnProperty("week") ? "2" : "1",
}
req.gdRequest('getGJLevelScores211', params, function(err, resp, body) {
req.gdRequest('getGJLevelScores211', params, function(err, resp, body) {
if (err) return res.status(500).send({error: true, lastWorked: app.timeSince(req.id)})
scores = body.split('|').map(x => app.parseResponse(x)).filter(x => x[1])
if (!scores.length) return res.status(500).send([])
else app.trackSuccess(req.id)
if (err) return res.status(500).send({error: true, lastWorked: app.timeSince(req.id)})
scores = body.split('|').map(x => app.parseResponse(x)).filter(x => x[1])
if (!scores.length) return res.status(500).send([])
app.trackSuccess(req.id)
scores.forEach(x => {
let keys = Object.keys(x)
x.rank = x[6]
x.username = x[1]
x.percent = +x[3]
x.coins = +x[13]
x.playerID = x[2]
x.accountID = x[16]
x.date = x[42] + req.timestampSuffix
x.icon = {
form: ['icon', 'ship', 'ball', 'ufo', 'wave', 'robot', 'spider'][+x[14]],
icon: +x[9],
col1: +x[10],
col2: +x[11],
glow: +x[15] > 1,
col1RGB: colors[x[10]] || colors["0"],
col2RGB: colors[x[11]] || colors["3"]
}
keys.forEach(k => delete x[k])
app.userCache(req.id, x.accountID, x.playerID, x.username)
})
scores.forEach(x => {
let keys = Object.keys(x)
x.rank = x[6]
x.username = x[1]
x.percent = +x[3]
x.coins = +x[13]
x.playerID = x[2]
x.accountID = x[16]
x.date = x[42] + req.timestampSuffix
x.icon = {
form: ['icon', 'ship', 'ball', 'ufo', 'wave', 'robot', 'spider'][+x[14]],
icon: +x[9],
col1: +x[10],
col2: +x[11],
glow: +x[15] > 1,
col1RGB: colors[x[10]] || colors["0"],
col2RGB: colors[x[11]] || colors["3"]
}
keys.forEach(k => delete x[k])
app.userCache(req.id, x.accountID, x.playerID, x.username)
})
return res.send(scores.slice(0, amount))
})
return res.send(scores.slice(0, amount))
})
}

View file

@ -1,33 +1,35 @@
"use strict";
const Player = require('../../classes/Player.js')
module.exports = async (app, req, res) => {
if (req.offline) return res.sendError()
if (req.offline) return res.sendError()
let amount = 100;
let count = req.query.count ? parseInt(req.query.count) : null
if (count && count > 0) {
if (count > 10000) amount = 10000
else amount = count;
}
let amount = 100
let count = req.query.count ? parseInt(req.query.count) : 0
if (count > 0) amount = Math.min(count, 10000)
let params = {count: amount, type: "top"}
let params = {count: amount, type: "top"}
if (["creators", "creator", "cp"].some(x => req.query.hasOwnProperty(x) || req.query.type == x)) params.type = "creators"
else if (["week", "weekly"].some(x => req.query.hasOwnProperty(x) || req.query.type == x)) params.type = "week"
else if (["global", "relative"].some(x => req.query.hasOwnProperty(x) || req.query.type == x)) {
params.type = "relative"
params.accountID = req.query.accountID
}
let isInQuery = (...args) => args.some(x => req.query.hasOwnProperty(x) || req.query.type == x)
req.gdRequest('getGJScores20', params, function(err, resp, body) {
if (isInQuery("creators", "creator", "cp"))
params.type = "creators"
else if (isInQuery("week", "weekly"))
params.type = "week"
else if (isInQuery("global", "relative")) {
params.type = "relative"
params.accountID = req.query.accountID
}
if (err) return res.sendError()
scores = body.split('|').map(x => app.parseResponse(x)).filter(x => x[1])
if (!scores.length) return res.sendError()
req.gdRequest('getGJScores20', params, function(err, resp, body) {
scores = scores.map(x => new Player(x))
scores.forEach(x => app.userCache(req.id, x.accountID, x.playerID, x.username))
return res.send(scores.slice(0, amount))
})
if (err) return res.sendError()
scores = body.split('|').map(x => app.parseResponse(x)).filter(x => x[1])
if (!scores.length) return res.sendError()
scores = scores.map(x => new Player(x))
scores.forEach(x => app.userCache(req.id, x.accountID, x.playerID, x.username))
return res.send(scores.slice(0, amount))
})
}

View file

@ -1,3 +1,4 @@
"use strict";
const request = require('request')
const fs = require('fs')
const Level = require('../classes/Level.js')
@ -5,17 +6,14 @@ const Level = require('../classes/Level.js')
module.exports = async (app, req, res, api, analyze) => {
function rejectLevel() {
if (!api) return res.redirect('search/' + req.params.id)
else return res.sendError()
return !api ? res.redirect('search/' + req.params.id) : res.sendError()
}
if (req.offline) return rejectLevel()
let levelID = req.params.id
if (levelID == "daily") return app.run.download(app, req, res, api, 'daily', analyze)
else if (levelID == "weekly") return app.run.download(app, req, res, api, 'weekly', analyze)
else if (levelID.match(/[^0-9]/)) return rejectLevel()
else levelID = levelID.replace(/[^0-9]/g, "")
if (levelID == "daily" || levelID == "weekly") return app.run.download(app, req, res, api, levelID, analyze)
else if (/\D/.test(levelID)) return rejectLevel()
if (analyze || req.query.hasOwnProperty("download")) return app.run.download(app, req, res, api, levelID, analyze)
@ -23,9 +21,11 @@ module.exports = async (app, req, res, api, analyze) => {
if (err || body.startsWith("##")) return rejectLevel()
let preRes = body.split('#')[0].split('|', 10)
let author = body.split('#')[1].split('|')[0].split(':')
let song = '~' + body.split('#')[2];
// "revolutionary name XD" @Rudxain
const bodySplit = body.split('#')
let preRes = bodySplit[0].split('|', 10)
let author = bodySplit[1].split('|', 1)[0].split(':')
let song = '~' + bodySplit[2]
song = app.parseResponse(song, '~|~')
let levelInfo = app.parseResponse(preRes.find(x => x.startsWith(`1:${levelID}`)) || preRes[0])
@ -40,7 +40,7 @@ module.exports = async (app, req, res, api, analyze) => {
if (api) return res.send(level)
else return fs.readFile('./html/level.html', 'utf8', function (err, data) {
let html = data;
let html = data
let filteredSong = level.songName.replace(/[^ -~]/g, "") // strip off unsupported characters
level.songName = filteredSong || level.songName
let variables = Object.keys(level)

View file

@ -1,12 +1,14 @@
"use strict";
let difficulties = ["auto", "easy", "normal", "hard", "harder", "insane", "demon", "demon-easy", "demon-medium", "demon-insane", "demon-extreme"]
let cache = {}
module.exports = async (app, req, res) => {
if (req.offline) return res.sendError()
let cached = cache[req.id]
if (app.config.cacheMapPacks && cached && cached.data && cached.indexed + 5000000 > Date.now()) return res.send(cached.data) // 1.5 hour cache
if (app.config.cacheMapPacks && cached && cached.data && cached.indexed + 5000000 > Date.now())
return res.send(cached.data) // 1.5 hour cache
let params = { count: 250, page: 0 }
let packs = []
@ -15,7 +17,7 @@ module.exports = async (app, req, res) => {
if (err) return res.sendError()
let newPacks = body.split('#')[0].split('|').map(x => app.parseResponse(x)).filter(x => x[2])
let newPacks = body.split('#', 1)[0].split('|').map(x => app.parseResponse(x)).filter(x => x[2])
packs = packs.concat(newPacks)
// not all GDPS'es support the count param, which means recursion time!!!
@ -23,7 +25,7 @@ module.exports = async (app, req, res) => {
params.page++
return mapPackLoop()
}
let mappacks = packs.map(x => ({ // "packs.map()" laugh now please
id: +x[1],
name: x[2],

View file

@ -1,9 +1,11 @@
"use strict";
module.exports = async (app, req, res) => {
const send = (msg, c=400) => res.status(c).send(msg)
if (req.method !== 'POST') return res.status(405).send("Method not allowed.")
if (req.method !== 'POST') return send("Method not allowed.", 405)
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!")
if (!req.body.accountID) return send("No account ID provided!")
if (!req.body.password) return send("No password provided!")
let params = {
accountID: req.body.accountID,
@ -13,11 +15,11 @@ module.exports = async (app, req, res) => {
req.gdRequest('getGJUserInfo20', params, function (err, resp, body) {
if (err) return res.status(400).send(`Error counting messages! Messages get blocked a lot so try again later, or make sure your username and password are entered correctly. Last worked: ${app.timeSince(req.id)} ago.`)
else app.trackSuccess(req.id)
if (err) return send(`Error counting messages! Messages get blocked a lot so try again later, or make sure your username and password are entered correctly. Last worked: ${app.timeSince(req.id)} ago.`)
app.trackSuccess(req.id)
let count = app.parseResponse(body)[38]
if (!count) return res.status(400).send("Error fetching unread messages!")
else res.send(count)
if (!count) return send("Error fetching unread messages!")
res.send(count)
})
}

View file

@ -1,23 +1,26 @@
"use strict";
module.exports = async (app, req, res) => {
const send = (msg, c=400) => res.status(c).send(msg)
if (req.method !== 'POST') return res.status(405).send("Method not allowed.")
if (req.method !== 'POST') return send("Method not allowed.", 405)
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!")
if (!req.body.id) return res.status(400).send("No message ID(s) provided!")
if (!req.body.accountID) return send("No account ID provided!")
if (!req.body.password) return send("No password provided!")
if (!req.body.id) return send("No message ID(s) provided!")
let params = {
accountID: req.body.accountID,
gjp: app.xor.encrypt(req.body.password, 37526),
// serialize to CSV if needed
messages: Array.isArray(req.body.id) ? req.body.id.map(x => x.trim()).join(",") : req.body.id,
}
let deleted = params.messages.split(",").length
let deleted = params.messages.split(",").length // CSV record count
req.gdRequest('deleteGJMessages20', params, function (err, resp, body) {
if (body != 1) return res.status(400).send(`The Geometry Dash servers refused to delete the message! Try again later, or make sure your username and password are entered correctly. Last worked: ${app.timeSince(req.id)} ago.`)
else res.send(`${deleted == 1 ? "1 message" : `${deleted} messages`} deleted!`)
if (body != 1) return send(`The Geometry Dash servers refused to delete the message! Try again later, or make sure your username and password are entered correctly. Last worked: ${app.timeSince(req.id)} ago.`)
res.send(`${deleted} message${deleted == 1 ? "" : "s"} deleted!`)
app.trackSuccess(req.id)
})

View file

@ -1,9 +1,11 @@
"use strict";
module.exports = async (app, req, res) => {
const send = (msg, c=400) => res.status(c).send(msg)
if (req.method !== 'POST') return res.status(405).send("Method not allowed.")
if (req.method !== 'POST') return send("Method not allowed.", 405)
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!")
if (!req.body.accountID) return send("No account ID provided!")
if (!req.body.password) return send("No password provided!")
let params = req.gdParams({
accountID: req.body.accountID,
@ -13,24 +15,25 @@ module.exports = async (app, req, res) => {
req.gdRequest('downloadGJMessage20', params, function (err, resp, body) {
if (err) return res.status(400).send(`Error fetching message! Try again later, or make sure your username and password are entered correctly. Last worked: ${app.timeSince(req.id)} ago.`)
else app.trackSuccess(req.id)
if (err) return send(`Error fetching message! Try again later, or make sure your username and password are entered correctly. Last worked: ${app.timeSince(req.id)} ago.`)
app.trackSuccess(req.id)
let x = app.parseResponse(body)
let msg = {}
msg.id = x[1];
msg.playerID = x[3]
msg.accountID = x[2]
msg.author = x[6]
msg.subject = Buffer.from(x[4], "base64").toString().replace(/^Re: ☆/, "Re: ")
msg.content = app.xor.decrypt(x[5], 14251)
msg.date = x[7] + req.timestampSuffix
if (msg.subject.endsWith("☆") || msg.subject.startsWith("☆")) {
if (msg.subject.endsWith("☆")) msg.subject = msg.subject.slice(0, -1)
else msg.subject = msg.subject.slice(1)
msg.browserColor = true
}
let subject = Buffer.from(x[4], "base64").toString().replace(/^Re: ☆/, "Re: ")
let msg = {
id: x[1],
playerID: x[3],
accountID: x[2],
author: x[6],
subject,
content: app.xor.decrypt(x[5], 14251),
date: x[7] + req.timestampSuffix
}
if (/^☆|☆$/.test(subject)) {
msg.subject = subject.slice(...(subject.endsWith("☆") ? [0, -1] : [1]))
msg.browserColor = true
}
return res.send(msg)
})

View file

@ -1,10 +1,12 @@
"use strict";
module.exports = async (app, req, res) => {
const send = (msg, c=400) => res.status(c).send(msg)
if (req.method !== 'POST') return res.status(405).send("Method not allowed.")
if (req.method !== 'POST') return send("Method not allowed.", 405)
if (req.body.count) return app.run.countMessages(app, req, res)
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!")
if (!req.body.accountID) return send("No account ID provided!")
if (!req.body.password) return send("No password provided!")
let params = req.gdParams({
accountID: req.body.accountID,
@ -15,26 +17,26 @@ module.exports = async (app, req, res) => {
req.gdRequest('getGJMessages20', params, function (err, resp, body) {
if (err) return res.status(400).send(`Error fetching messages! Messages get blocked a lot so try again later, or make sure your username and password are entered correctly. Last worked: ${app.timeSince(req.id)} ago.`)
if (err) return send(`Error fetching messages! Messages get blocked a lot so try again later, or make sure your username and password are entered correctly. Last worked: ${app.timeSince(req.id)} ago.`)
else app.trackSuccess(req.id)
let messages = body.split("|").map(msg => app.parseResponse(msg))
let messageArray = []
messages.forEach(x => {
let msg = {}
msg.id = x[1];
msg.playerID = x[3]
msg.accountID = x[2]
msg.author = x[6]
msg.subject = Buffer.from(x[4], "base64").toString().replace(/^Re: ☆/, "Re: ")
msg.date = x[7] + req.timestampSuffix
msg.unread = x[8] != "1"
if (msg.subject.endsWith("☆") || msg.subject.startsWith("☆")) {
if (msg.subject.endsWith("☆")) msg.subject = msg.subject.slice(0, -1)
else msg.subject = msg.subject.slice(1)
msg.browserColor = true
}
let subject = Buffer.from(x[4], "base64").toString().replace(/^Re: ☆/, "Re: ")
let msg = {
id: x[1],
playerID: x[3],
accountID: x[2],
author: x[6],
subject,
date: x[7] + req.timestampSuffix,
unread: x[8] != "1"
}
if (/^☆|☆$/.test(subject)) {
msg.subject = subject.slice(...(subject.endsWith("☆") ? [0, -1] : [1]))
msg.browserColor = true
}
app.userCache(req.id, msg.accountID, msg.playerID, msg.author)
messageArray.push(msg)

View file

@ -1,25 +1,33 @@
"use strict";
module.exports = async (app, req, res) => {
const send = (msg, c=400) => res.status(c).send(msg)
if (req.method !== 'POST') return res.status(405).send("Method not allowed.")
if (req.method !== 'POST') return send("Method not allowed.", 405)
if (!req.body.targetID) return res.status(400).send("No target ID provided!")
if (!req.body.message) return res.status(400).send("No message provided!")
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!")
if (!req.body.targetID) return send("No target ID provided!")
if (!req.body.message) return send("No message provided!")
if (!req.body.accountID) return send("No account ID provided!")
if (!req.body.password) return send("No password provided!")
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 subject = Buffer.from(
(req.body.color ? "☆" : "") + (req.body.subject ? req.body.subject.slice(0, 50) : "No subject")
).toString('base64url')
let body = app.xor.encrypt(req.body.message.slice(0, 300), 14251)
let params = req.gdParams({
accountID: req.body.accountID,
gjp: app.xor.encrypt(req.body.password, 37526),
toAccountID: req.body.targetID,
subject, body,
subject, body
})
req.gdRequest('uploadGJMessage20', params, function (err, resp, body) {
if (body != 1) return res.status(400).send(`The Geometry Dash servers refused to send the message! Try again later, or make sure your username and password are entered correctly. Last worked: ${app.timeSince(req.id)} ago.`)
else res.send('Message sent!')
if (body != 1) return send(
`The Geometry Dash servers refused to send the message! `+
`Try again later, or make sure your username and password are entered correctly. `+
`Last worked: ${app.timeSince(req.id)} ago.`
)
res.send('Message sent!')
app.trackSuccess(req.id)
})

View file

@ -1,17 +1,31 @@
"use strict";
const crypto = require('crypto')
function sha1(data) { return crypto.createHash("sha1").update(data, "binary").digest("hex"); }
const sha1 = data => crypto.createHash("sha1").update(data, "binary").digest("hex")
module.exports = async (app, req, res) => {
const send = (msg, c=400) => res.status(c).send(msg)
if (req.method !== 'POST') return res.status(405).send("Method not allowed.")
if (req.method !== 'POST') return send("Method not allowed.", 405)
if (!req.body.ID) return send("No ID provided!")
if (!req.body.accountID) return send("No account ID provided!")
if (!req.body.password) return send("No password provided!")
if (!req.body.like) return send("No like flag provided! (1=like, 0=dislike)")
if (!req.body.type) return send("No type provided! (1=level, 2=comment, 3=profile")
if (!req.body.extraID) return send("No extra ID provided! (this should be a level ID, account ID, or '0' for levels")
/*
// A compound error message is more helpful, but IDK if this may cause bugs,
// so this is commented-out
let errMsg = ""
if (!req.body.ID) errMsg += "No ID provided!\n"
if (!req.body.accountID) errMsg += "No account ID provided!\n"
if (!req.body.password) errMsg += "No password provided!\n"
if (!req.body.like) errMsg += "No like flag provided! (1=like, 0=dislike)\n"
if (!req.body.type) errMsg += "No type provided! (1=level, 2=comment, 3=profile\n"
if (!req.body.extraID) errMsg += "No extra ID provided! (this should be a level ID, account ID, or '0' for levels)\n"
if (errMsg) return send(errMsg)
*/
if (!req.body.ID) return res.status(400).send("No ID provided!")
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!")
if (!req.body.like) return res.status(400).send("No like flag provided! (1=like, 0=dislike)")
if (!req.body.type) return res.status(400).send("No type provided! (1=level, 2=comment, 3=profile")
if (!req.body.extraID) return res.status(400).send("No extra ID provided! (this should be a level ID, account ID, or '0' for levels")
let params = {
udid: '0',
uuid: '0',
@ -25,14 +39,15 @@ module.exports = async (app, req, res) => {
params.special = req.body.extraID.toString()
params.type = req.body.type.toString()
let chk = params.special + params.itemID + params.like + params.type + params.rs + params.accountID + params.udid + params.uuid + "ysg6pUrtjn0J"
let chk = "";
['special', 'itemID', 'like', 'type', 'rs', 'accountID', 'udid', 'uuid'].forEach(k => chk += params[k])
chk += "ysg6pUrtjn0J"
chk = sha1(chk)
chk = app.xor.encrypt(chk, 58281)
params.chk = chk
req.gdRequest('likeGJItem211', params, function (err, resp, body) {
if (err) return res.status(400).send(`The Geometry Dash servers rejected your vote! Try again later, or make sure your username and password are entered correctly. Last worked: ${app.timeSince(req.id)} ago.`)
if (err) return send(`The Geometry Dash servers rejected your vote! Try again later, or make sure your username and password are entered correctly. Last worked: ${app.timeSince(req.id)} ago.`)
else app.trackSuccess(req.id)
res.send((params.like == 1 ? 'Successfully liked!' : 'Successfully disliked!') + " (this will only take effect if this is your first time doing so)")
})

View file

@ -1,31 +1,46 @@
"use strict";
const crypto = require('crypto')
function sha1(data) { return crypto.createHash("sha1").update(data, "binary").digest("hex"); }
const sha1 = data => crypto.createHash("sha1").update(data, "binary").digest("hex")
github-advanced-security[bot] commented 2022-07-03 13:45:55 -04:00 (Migrated from github.com)
Review

Use of a broken or weak cryptographic algorithm

Sensitive data from an access to username is used in a broken or weak cryptographic algorithm.
Sensitive data from an access to userName is used in a broken or weak cryptographic algorithm.

Show more details

## Use of a broken or weak cryptographic algorithm Sensitive data from [an access to username](1) is used in a broken or weak cryptographic algorithm. Sensitive data from [an access to userName](2) is used in a broken or weak cryptographic algorithm. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/73)
Rudxain commented 2022-07-03 14:04:42 -04:00 (Migrated from github.com)
Review

Bruh

Bruh
let rateLimit = {};
let rateLimit = {}
let cooldown = 15000 // GD has a secret rate limit and doesn't return -1 when a comment is rejected, so this keeps track
// converts timestamp miliseconds to s (wrapped-around minutes)
function getTime(time) {
let seconds = Math.ceil(time / 1000);
seconds = seconds % 60;
return seconds}
let seconds = Math.ceil(time / 1000)
seconds %= 60
return seconds
}
module.exports = async (app, req, res) => {
const send = (msg, c=400) => res.status(c).send(msg)
if (req.method !== 'POST') return res.status(405).send("Method not allowed.")
if (req.method !== 'POST') return send("Method not allowed.", 405)
if (!req.body.comment) return res.status(400).send("No comment provided!")
if (!req.body.username) return res.status(400).send("No username provided!")
if (!req.body.levelID) return res.status(400).send("No level ID provided!")
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!")
if (!req.body.comment) return send("No comment provided!")
if (!req.body.username) return send("No username provided!")
if (!req.body.levelID) return send("No level ID provided!")
if (!req.body.accountID) return send("No account ID provided!")
if (!req.body.password) return send("No password provided!")
/*
// A compound error message is more helpful, but IDK if this may cause bugs,
// so this is commented-out
let errMsg = ""
if (!req.body.comment) errMsg += "No comment provided!\n"
if (!req.body.username) errMsg += "No username provided!\n"
if (!req.body.levelID) errMsg += "No level ID provided!\n"
if (!req.body.accountID) errMsg += "No account ID provided!\n"
if (!req.body.password) errMsg += "No password provided!\n"
if (errMsg) return send(errMsg)
*/
if (req.body.comment.includes('\n')) return res.status(400).send("Comments cannot contain line breaks!")
if (req.body.comment.includes('\n')) return send("Comments cannot contain line breaks!")
if (rateLimit[req.body.username]) return send(`Please wait ${getTime(rateLimit[req.body.username] + cooldown - Date.now())} seconds before posting another comment!`)
if (rateLimit[req.body.username]) return res.status(400).send(`Please wait ${getTime(rateLimit[req.body.username] + cooldown - Date.now())} seconds before posting another comment!`)
let params = { percent: 0 }
params.comment = Buffer.from(req.body.comment + (req.body.color ? "☆" : "")).toString('base64').replace(/\//g, '_').replace(/\+/g, "-")
params.comment = Buffer.from(req.body.comment + (req.body.color ? "☆" : "")).toString('base64url')
params.gjp = app.xor.encrypt(req.body.password, 37526)
params.levelID = req.body.levelID.toString()
params.accountID = req.body.accountID.toString()
@ -40,15 +55,22 @@ module.exports = async (app, req, res) => {
params.chk = chk
req.gdRequest('uploadGJComment21', params, function (err, resp, body) {
if (err) return res.status(400).send(`The Geometry Dash servers rejected your comment! Try again later, or make sure your username and password are entered correctly. Last worked: ${app.timeSince(req.id)} ago.`)
if (err) return send(
`The Geometry Dash servers rejected your comment! `+
`Try again later, or make sure your username and password are entered correctly. `+
`Last worked: ${app.timeSince(req.id)} ago.`
)
if (body.startsWith("temp")) {
let banStuff = body.split("_")
return res.status(400).send(`You have been banned from commenting for ${(parseInt(banStuff[1]) / 86400).toFixed(0)} days. Reason: ${banStuff[2] || "None"}`)
return send(
`You have been banned from commenting for ${(parseInt(banStuff[1]) / 86400).toFixed(0)} days. `+
`Reason: ${banStuff[2] || "None"}`
)
}
res.send(`Comment posted to level ${params.levelID} with ID ${body}`)
app.trackSuccess(req.id)
rateLimit[req.body.username] = Date.now();
setTimeout(() => {delete rateLimit[req.body.username]; }, cooldown);
rateLimit[req.body.username] = Date.now()
setTimeout(() => {delete rateLimit[req.body.username]}, cooldown);
})
}

View file

@ -1,20 +1,32 @@
"use strict";
const crypto = require('crypto')
function sha1(data) { return crypto.createHash("sha1").update(data, "binary").digest("hex"); }
const sha1 = data => crypto.createHash("sha1").update(data, "binary").digest("hex")
github-advanced-security[bot] commented 2022-07-03 13:45:55 -04:00 (Migrated from github.com)
Review

Use of a broken or weak cryptographic algorithm

Sensitive data from an access to username is used in a broken or weak cryptographic algorithm.
Sensitive data from an access to userName is used in a broken or weak cryptographic algorithm.

Show more details

## Use of a broken or weak cryptographic algorithm Sensitive data from [an access to username](1) is used in a broken or weak cryptographic algorithm. Sensitive data from [an access to userName](2) is used in a broken or weak cryptographic algorithm. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/74)
Rudxain commented 2022-07-03 14:04:20 -04:00 (Migrated from github.com)
Review

LMAO I just changed the function to arrow fn

LMAO I just changed the function to arrow fn
module.exports = async (app, req, res) => {
const send = (msg, c=400) => res.status(c).send(msg)
if (req.method !== 'POST') return res.status(405).send("Method not allowed.")
if (req.method !== 'POST') return send("Method not allowed.", 405)
if (!req.body.comment) return res.status(400).send("No comment provided!")
if (!req.body.username) return res.status(400).send("No username provided!")
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!")
if (!req.body.comment) return send("No comment provided!")
if (!req.body.username) return send("No username provided!")
if (!req.body.accountID) return send("No account ID provided!")
if (!req.body.password) return send("No password provided!")
/*
// A compound error message is more helpful, but IDK if this may cause bugs,
// so this is commented-out
let errMsg = ""
if (!req.body.comment) errMsg += "No comment provided!\n"
if (!req.body.username) errMsg += "No username provided!\n"
if (!req.body.accountID) errMsg += "No account ID provided!\n"
if (!req.body.password) errMsg += "No password provided!\n"
if (errMsg) return send(errMsg)
*/
if (req.body.comment.includes('\n')) return send("Profile posts cannot contain line breaks!")
if (req.body.comment.includes('\n')) return res.status(400).send("Profile posts cannot contain line breaks!")
let params = { cType: '1' }
params.comment = Buffer.from(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('base64url')
params.gjp = app.xor.encrypt(req.body.password, 37526)
params.accountID = req.body.accountID.toString()
params.userName = req.body.username
@ -25,10 +37,10 @@ module.exports = async (app, req, res) => {
params.chk = chk
req.gdRequest('uploadGJAccComment20', params, function (err, resp, body) {
if (err) return res.status(400).send(`The Geometry Dash servers rejected your profile post! Try again later, or make sure your username and password are entered correctly. Try again later, or make sure your username and password are entered correctly. Last worked: ${app.timeSince(req.id)} ago.`)
if (err) return send(`The Geometry Dash servers rejected your profile post! Try again later, or make sure your username and password are entered correctly. Try again later, or make sure your username and password are entered correctly. Last worked: ${app.timeSince(req.id)} ago.`)
else if (body.startsWith("temp")) {
let banStuff = body.split("_")
return res.status(400).send(`You have been banned from commenting for ${(parseInt(banStuff[1]) / 86400).toFixed(0)} days. Reason: ${banStuff[2] || "None"}`)
return send(`You have been banned from commenting for ${(parseInt(banStuff[1]) / 86400).toFixed(0)} days. Reason: ${banStuff[2] || "None"}`)
}
else app.trackSuccess(req.id)
res.send(`Comment posted to ${params.userName} with ID ${body}`)

View file

@ -1,13 +1,15 @@
"use strict";
const fs = require('fs')
const Player = require('../classes/Player.js')
module.exports = async (app, req, res, api, getLevels) => {
if (req.offline) {
if (!api) return res.redirect('/search/' + req.params.id)
else return res.sendError()
function rejectLevel() {
// this variant has an extra "/"
return !api ? res.redirect('/search/' + req.params.id) : res.sendError()
}
if (req.offline) return rejectLevel()
let username = getLevels || req.params.id
let probablyID
if (username.endsWith(".") && req.isGDPS) {
@ -17,18 +19,18 @@ module.exports = async (app, req, res, api, getLevels) => {
let accountMode = !req.query.hasOwnProperty("player") && Number(req.params.id)
let foundID = app.userCache(req.id, username)
let skipRequest = accountMode || foundID || probablyID
let searchResult;
let searchResult
// if you're searching by account id, an intentional error is caused to skip the first request to the gd servers. see i pulled a sneaky on ya. (fuck callbacks man)
req.gdRequest(skipRequest ? "" : 'getGJUsers20', skipRequest ? {} : { str: username, page: 0 }, function (err1, res1, b1) {
req.gdRequest(skipRequest ? "" : 'getGJUsers20', skipRequest ? {} : { str: username, page: 0 }, function (err1, res1, b1) {
if (foundID) searchResult = foundID[0]
else if (accountMode || err1 || b1 == '-1' || b1.startsWith("<") || !b1) searchResult = probablyID ? username : req.params.id
else if (!req.isGDPS) searchResult = app.parseResponse(b1.split("|")[0])[16]
else if (!req.isGDPS) searchResult = app.parseResponse(b1.split("|", 1)[0])[16]
else { // GDPS's return multiple users, GD no longer does this
let userResults = b1.split("|").map(x => app.parseResponse(x))
searchResult = userResults.find(x => x[1].toLowerCase() == username.toLowerCase() || x[2] == username) || ""
if (searchResult) searchResult = searchResult[16]
searchResult &&= searchResult[16]
}
if (getLevels) {
@ -40,20 +42,17 @@ module.exports = async (app, req, res, api, getLevels) => {
let account = app.parseResponse(body || "")
let dumbGDPSError = req.isGDPS && (!account[16] || account[1].toLowerCase() == "undefined")
if (err2 || dumbGDPSError) {
if (!api) return res.redirect('/search/' + req.params.id)
else return res.sendError()
}
if (err2 || dumbGDPSError) return rejectLevel()
if (!foundID) app.userCache(req.id, account[16], account[2], account[1])
let userData = new Player(account)
let userData = new Player(account)
if (api) return res.send(userData)
else fs.readFile('./html/profile.html', 'utf8', function(err, data) {
let html = data;
let html = data
let variables = Object.keys(userData)
variables.forEach(x => {
let regex = new RegExp(`\\[\\[${x.toUpperCase()}\\]\\]`, "g")
@ -61,7 +60,6 @@ module.exports = async (app, req, res, api, getLevels) => {
})
return res.send(html)
})
})
})
}
})
}

View file

@ -1,94 +1,92 @@
"use strict";
const request = require('request')
const music = require('../misc/music.json')
const Level = require('../classes/Level.js')
let demonList = {}
module.exports = async (app, req, res) => {
const {query} = req
if (req.offline) return res.status(500).send(query.hasOwnProperty("err") ? "err" : "-1")
if (req.offline) return res.status(500).send(req.query.hasOwnProperty("err") ? "err" : "-1")
let demonMode = req.query.hasOwnProperty("demonlist") || req.query.hasOwnProperty("demonList") || req.query.type == "demonlist" || req.query.type == "demonList"
let demonMode = query.hasOwnProperty("demonlist") || query.hasOwnProperty("demonList") || query.type == "demonlist" || query.type == "demonList"
if (demonMode) {
if (!req.server.demonList) return res.sendError(400)
let dList = demonList[req.id]
if (!dList || !dList.list.length || dList.lastUpdated + 600000 < Date.now()) { // 10 minute cache
return request.get(req.server.demonList + 'api/v2/demons/listed/?limit=100', function (err1, resp1, list1) {
let demonStr = req.server.demonList + 'api/v2/demons/listed/?limit=100'
return request.get(demonStr, function (err1, resp1, list1) {
if (err1) return res.sendError()
else return request.get(req.server.demonList + 'api/v2/demons/listed/?limit=100&after=100', function (err2, resp2, list2) {
else return request.get(demonStr + '&after=100', function (err2, resp2, list2) {
if (err2) return res.sendError()
demonList[req.id] = {list: JSON.parse(list1).concat(JSON.parse(list2)).map(x => String(x.level_id)), lastUpdated: Date.now()}
demonList[req.id] = {
list: JSON.parse(list1).concat(JSON.parse(list2))
.map(x => String(x.level_id)),
lastUpdated: Date.now()
}
return app.run.search(app, req, res)
})
})
}
}
let amount = 10;
let count = req.isGDPS ? 10 : +req.query.count
if (count && count > 0) {
if (count > 500) amount = 500
else amount = count;
}
let amount = 10
let count = req.isGDPS ? 10 : +query.count
if (count && count > 0) amount = Math.min(count, 500)
let filters = {
str: req.params.text,
diff: req.query.diff,
demonFilter: req.query.demonFilter,
page: req.query.page || 0,
gauntlet: req.query.gauntlet || 0,
len: req.query.length,
song: req.query.songID,
followed: req.query.creators,
diff: query.diff,
demonFilter: query.demonFilter,
page: query.page || 0,
gauntlet: query.gauntlet || 0,
len: query.length,
song: query.songID,
followed: query.creators,
featured: req.query.hasOwnProperty("featured") ? 1 : 0,
originalOnly: req.query.hasOwnProperty("original") ? 1 : 0,
twoPlayer: req.query.hasOwnProperty("twoPlayer") ? 1 : 0,
coins: req.query.hasOwnProperty("coins") ? 1 : 0,
epic: req.query.hasOwnProperty("epic") ? 1 : 0,
star: req.query.hasOwnProperty("starred") ? 1 : 0,
noStar: req.query.hasOwnProperty("noStar") ? 1 : 0,
customSong: req.query.hasOwnProperty("customSong") ? 1 : 0,
featured: query.hasOwnProperty("featured") ? 1 : 0,
originalOnly: query.hasOwnProperty("original") ? 1 : 0,
twoPlayer: query.hasOwnProperty("twoPlayer") ? 1 : 0,
coins: query.hasOwnProperty("coins") ? 1 : 0,
epic: query.hasOwnProperty("epic") ? 1 : 0,
star: query.hasOwnProperty("starred") ? 1 : 0,
noStar: query.hasOwnProperty("noStar") ? 1 : 0,
customSong: query.hasOwnProperty("customSong") ? 1 : 0,
type: req.query.type || 0,
type: query.type || 0,
count: amount
}
if (req.query.type) {
let filterCheck = req.query.type.toLowerCase()
switch(filterCheck) {
case 'mostdownloaded': filters.type = 1; break;
case 'mostliked': filters.type = 2; break;
case 'trending': filters.type = 3; break;
case 'recent': filters.type = 4; break;
case 'featured': filters.type = 6; break;
case 'magic': filters.type = 7; break;
case 'awarded': filters.type = 11; break;
case 'starred': filters.type = 11; break;
case 'halloffame': filters.type = 16; break;
case 'hof': filters.type = 16; break;
case 'gdw': filters.type = 17; break;
case 'gdworld': filters.type = 17; break;
if (query.type) {
let filterCheck = query.type.toLowerCase()
let typeMap = {
'mostdownloaded': 1, 'mostliked': 2,
'trending': 3, 'recent': 4,
'featured': 6, 'magic': 7,
'awarded': 11, 'starred': 11,
'halloffame': 16, 'hof': 16,
'gdw': 17, 'gdworld': 17
}
if (typeMap.hasOwnProperty(filterCheck)) // JIC there's no match
filters.type = typeMap[filterCheck]
}
if (req.query.hasOwnProperty("user")) {
if (query.hasOwnProperty("user")) {
let accountCheck = app.userCache(req.id, filters.str)
filters.type = 5
if (accountCheck) filters.str = accountCheck[1]
else if (!filters.str.match(/^[0-9]*$/)) return app.run.profile(app, req, res, null, req.params.text)
}
else if ( !(/^\d*$/).test(filters.str) ) return app.run.profile(app, req, res, null, req.params.text)
}
if (req.query.hasOwnProperty("creators")) filters.type = 12
if (query.hasOwnProperty("creators")) filters.type = 12
let listSize = 10
if (demonMode || req.query.gauntlet || req.query.type == "saved" || ["mappack", "list", "saved"].some(x => req.query.hasOwnProperty(x))) {
if (demonMode || query.gauntlet || query.type == "saved" || ["mappack", "list", "saved"].some(x => query.hasOwnProperty(x))) {
filters.type = 10
filters.str = demonMode ? demonList[req.id].list : filters.str.split(",")
listSize = filters.str.length
filters.str = filters.str.slice(filters.page*amount, filters.page*amount + amount)
if (!filters.str.length) return res.sendError(400)
filters.str = filters.str.map(x => String(Number(x) + (+req.query.l || 0))).join()
filters.str = filters.str.map(x => String(Number(x) + (+query.l || 0))).join()
filters.page = 0
}
@ -103,25 +101,25 @@ module.exports = async (app, req, res) => {
let authorList = {}
let songList = {}
let authors = splitBody[1].split('|')
let songs = splitBody[2]; songs = songs.split('~:~').map(x => app.parseResponse(`~${x}~`, '~|~'))
let songs = splitBody[2].split('~:~').map(x => app.parseResponse(`~${x}~`, '~|~'))
songs.forEach(x => {songList[x['~1']] = x['2']})
authors.forEach(x => {
if (x.startsWith('~')) return
let arr = x.split(':')
authorList[arr[0]] = [arr[1], arr[2]]})
if (x.startsWith('~')) return
let arr = x.split(':')
authorList[arr[0]] = [arr[1], arr[2]]
})
let levelArray = preRes.map(x => app.parseResponse(x)).filter(x => x[1])
let parsedLevels = []
levelArray.forEach((x, y) => {
let songSearch = songs.find(y => y['~1'] == x[35]) || []
let level = new Level(x, req.server).getSongInfo(songSearch)
if (!level.id) return
level.author = authorList[x[6]] ? authorList[x[6]][0] : "-";
level.accountID = authorList[x[6]] ? authorList[x[6]][1] : "0";
level.author = authorList[x[6]] ? authorList[x[6]][0] : "-"
level.accountID = authorList[x[6]] ? authorList[x[6]][1] : "0"
if (demonMode) {
if (!y) level.demonList = req.server.demonList
@ -133,21 +131,21 @@ module.exports = async (app, req, res) => {
//this is broken if you're not on page 0, blame robtop
if (filters.page == 0 && y == 0 && splitBody[3]) {
let pages = splitBody[3].split(":");
let pages = splitBody[3].split(":")
if (filters.gauntlet) { // gauntlet page stuff
level.results = levelArray.length
level.results = levelArray.length
level.pages = 1
}
else if (filters.type == 10) { // custom page stuff
level.results = listSize
level.pages = +Math.ceil(listSize / (amount || 10))
level.pages = Math.ceil(listSize / (amount || 10))
}
else { // normal page stuff
level.results = +pages[0];
level.pages = +pages[0] == 9999 ? 1000 : +Math.ceil(pages[0] / amount);
level.results = +pages[0]
level.pages = +pages[0] == 9999 ? 1000 : Math.ceil(pages[0] / amount)
}
}

View file

@ -1,10 +1,12 @@
"use strict";
module.exports = async (app, req, res) => {
if (req.offline) return res.sendError()
let songID = req.params.song
req.gdRequest('getGJSongInfo', {songID: songID}, function(err, resp, body) {
if (err) return res.sendError(400)
return res.send(!body.startsWith("-") && body.length > 10)
return err
? res.sendError(400)
: res.send(!body.startsWith("-") && body.length > 10)
})
}
}

View file

@ -184,7 +184,7 @@ h2 {
.brownBox {
border: 2.5vh solid transparent;
border-radius: 3vh;
background-color: #995533;
background-color: #953;
border-image: url('../../assets/brownbox.png') 10% round;
}
@ -205,4 +205,4 @@ h2 {
-webkit-transform: rotate(360deg);
transform: rotate(360deg)
}
}
}

View file

@ -54,9 +54,9 @@ img, .noSelect {
}
.supercenter {
position: absolute;
top: 50%;
left: 50%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
}
@ -252,7 +252,7 @@ textarea::-webkit-scrollbar {
width: 1.5vh;
background: #6d3c24;
}
textarea::-webkit-scrollbar-thumb {
background: rgb(83, 47, 28);
overflow: hidden;
@ -345,7 +345,7 @@ input::-webkit-outer-spin-button, input::-webkit-inner-spin-button {
border-width: 2.5vh;
border-style: solid;
border-radius: 3vh;
background-color: #995533;
background-color: #953;
border-image: url('../../assets/brownbox.png') 10% round;
}
@ -353,7 +353,7 @@ input::-webkit-outer-spin-button, input::-webkit-inner-spin-button {
border-width: 2.5vh;
border-style: solid;
border-radius: 3vh;
background-color: #334499;
background-color: #349;
border-image: url('../../assets/bluebox.png') 10% round;
}
@ -563,8 +563,8 @@ input::-webkit-outer-spin-button, input::-webkit-inner-spin-button {
}
.popup {
position: fixed;
display: none;
position: fixed;
display: none;
width: 100%;
height: 100%;
top: 0; left: 0; right: 0; bottom: 0;
@ -619,9 +619,9 @@ input::-webkit-outer-spin-button, input::-webkit-inner-spin-button {
}
#scoreTabs {
position: absolute;
position: absolute;
text-align: center;
top: 2.45%;
top: 2.45%;
width: 100%;
margin-left: -13.5vh
}
@ -629,13 +629,13 @@ input::-webkit-outer-spin-button, input::-webkit-inner-spin-button {
#searchBox {
background-color: rgb(173, 115, 76);
width: 122vh;
height: 75%;
overflow-y: auto;
height: 75%;
overflow-y: auto;
overflow-x: hidden;
}
#searchBox::-webkit-scrollbar, #statusDiv::-webkit-scrollbar, #commentBox::-webkit-scrollbar {
display: none;
#searchBox::-webkit-scrollbar, #statusDiv::-webkit-scrollbar, #commentBox::-webkit-scrollbar {
display: none;
}
.searchResult {
@ -703,7 +703,7 @@ input::-webkit-outer-spin-button, input::-webkit-inner-spin-button {
.comment h2, .smallGold {
background: -webkit-linear-gradient(#e28000, #ffee44);
background: -webkit-linear-gradient(#e28000, #fe4);
background-clip: text;
width: fit-content;
vertical-align: top;
@ -902,7 +902,7 @@ input::-webkit-outer-spin-button, input::-webkit-inner-spin-button {
background-color: #BE6F3F;
overflow-y: scroll;
scrollbar-width: none;
-ms-overflow-style: none;
-ms-overflow-style: none;
}
#msgList::-webkit-scrollbar {
@ -1029,7 +1029,7 @@ input::-webkit-outer-spin-button, input::-webkit-inner-spin-button {
}
.analysis::-webkit-scrollbar-thumb, .levelCode::-webkit-scrollbar-thumb {
background: rgba(0, 0, 0, 0.3);
background: rgba(0, 0, 0, 0.3);
overflow: hidden;
}
@ -1292,7 +1292,7 @@ input::-webkit-outer-spin-button, input::-webkit-inner-spin-button {
.gdpslogo {
margin-bottom: 0%;
border-radius: 10%;
filter: drop-shadow(0.5vh 0.5vh 0.15vh rgba(0, 0, 0, 0.6))
filter: drop-shadow(0.5vh 0.5vh 0.15vh rgba(0, 0, 0, 0.6))
}
.menuDisabled, .downloadDisabled {
@ -1311,14 +1311,14 @@ input::-webkit-outer-spin-button, input::-webkit-inner-spin-button {
cr { color: #ff5a5a }
co { color: #ffa54b }
cy { color: #ffff00 }
cy { color: #ff0 }
cg { color: #40e348 }
ca { color: #00ffff }
ca { color: #0ff }
cb { color: #60abef }
cp { color: #ff00ff }
cp { color: #f0f }
.red {
color: #ff0000 !important;
color: #f00 !important;
}
.yellow {
@ -1350,11 +1350,11 @@ cp { color: #ff00ff }
}
.brightblue {
color: #99ffff
color: #9ff
}
.brightred {
color: #ffaaaa;
color: #faa;
}
.gray {
@ -1380,10 +1380,10 @@ cp { color: #ff00ff }
@-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); }
@keyframes spin {
100% {
-webkit-transform: rotate(360deg);
transform:rotate(360deg); }
}
@keyframes boxAnimator {
@ -1434,11 +1434,11 @@ cp { color: #ff00ff }
* Also disabled on devices that may be slow to render new frames for performance optimization
*/
@media screen and
(prefers-reduced-motion: reduce),
(prefers-reduced-motion: reduce),
(update: slow) {
*, *::before, *::after {
animation-duration: 0.001ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.001ms !important;
}
}
}

View file

@ -14,7 +14,7 @@
}
body {
background-color: 555555;
background-color: #555;
}
p {
@ -144,9 +144,9 @@ input:focus, select:focus, textarea:focus, button:focus {
}
.supercenter {
position: absolute;
top: 50%;
left: 50%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
}
@ -194,7 +194,7 @@ input:focus, select:focus, textarea:focus, button:focus {
padding: 10 10 10 10;
overflow: auto;
white-space: nowrap;
border-radius: 8px;
border-radius: 8px;
}
#symbols p {
@ -238,8 +238,8 @@ input:focus, select:focus, textarea:focus, button:focus {
#generate {
font-family: "Roboto";
border: rgba(0, 0, 0, 0);
background-color: #88FF33;
box-shadow: 2px 2px 3px #66AA22;
background-color: #8F3;
box-shadow: 2px 2px 3px #6A2;
border-radius: 10px;
color: black;
padding: 10px 15px 10px 15px;
@ -259,8 +259,8 @@ input:focus, select:focus, textarea:focus, button:focus {
.miniButton {
font-family: "Roboto";
border: rgba(0, 0, 0, 0);
background-color: #88FF33;
box-shadow: 1.5px 1.5px 2px #66AA22;
background-color: #8F3;
box-shadow: 1.5px 1.5px 2px #6A2;
border-radius: 10px;
color: black;
padding: 7px 9px 7px 9px;
@ -375,11 +375,11 @@ input:focus, select:focus, textarea:focus, button:focus {
#colors::-webkit-scrollbar, #iconKitParent::-webkit-scrollbar {
width: 9px;
height: 10px;
background: rgba(0, 0, 0, 0.5);
background: rgba(0, 0, 0, 0.5);
}
#colors::-webkit-scrollbar-thumb, #iconKitParent::-webkit-scrollbar-thumb {
background: rgb(185, 185, 185);
background: rgb(185, 185, 185);
}
#iconKitParent {
@ -394,7 +394,7 @@ input:focus, select:focus, textarea:focus, button:focus {
.iconColor div {
width: 50px;
height: 50px;
height: 50px;
}
#gdfloor {
@ -423,13 +423,13 @@ input:focus, select:focus, textarea:focus, button:focus {
}
#iconprogressbar {
background: rgba(0, 0, 0, 0.5);
background: rgba(0, 0, 0, 0.5);
height: 7px;
margin-bottom: 12px;
}
#iconloading {
background: rgba(255, 255, 255, 0.5);
background: rgba(255, 255, 255, 0.5);
height: 7px;
width: 0%;
}
@ -447,8 +447,8 @@ body::-webkit-scrollbar {
}
.popup {
position: fixed;
display: none;
position: fixed;
display: none;
width: 100%;
height: 100%;
top: 0; left: 0; right: 0; bottom: 0;
@ -459,7 +459,7 @@ body::-webkit-scrollbar {
.brownBox {
border: 17px solid rgba(0, 0, 0, 0);
border-radius: 25px;
background-color: #995533;
background-color: #953;
border-image: url('../../assets/brownbox.png') 10% round;
}
@ -686,4 +686,4 @@ input[type="range"]::-webkit-slider-thumb:active { background-image: url("../../
@-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); } }
@keyframes spin { 100% { -webkit-transform: rotate(360deg); transform:rotate(360deg); } }

View file

@ -1,16 +1,19 @@
const XOR = require(__dirname + "/../classes/XOR");
const music = require(__dirname + "/../misc/music.json");
"use strict";
const XOR = require("./XOR")
const music = require("../misc/music.json")
let orbs = [0, 0, 50, 75, 125, 175, 225, 275, 350, 425, 500]
let length = ['Tiny', 'Short', 'Medium', 'Long', 'XL']
// this can't be shortened with a loop
let difficulty = { 0: 'Unrated', 10: 'Easy', 20: 'Normal', 30: 'Hard', 40: 'Harder', 50: 'Insane' }
let demonTypes = { 3: "Easy", 4: "Medium", 5: "Insane", 6: "Extreme" }
let dailyLimit = 100000
class Level {
constructor(levelInfo, server, download, author = []) {
this.name = levelInfo[2] || "-";
this.id = levelInfo[1] || 0;
this.description = Buffer.from((levelInfo[3] || ""), "base64").toString() || "(No description provided)";
this.name = levelInfo[2] || "-"
this.id = levelInfo[1] || 0
this.description = Buffer.from((levelInfo[3] || ""), "base64").toString() || "(No description provided)"
this.author = author[1] || "-"
this.playerID = levelInfo[6] || 0
this.accountID = author[2] || 0
@ -29,8 +32,8 @@ class Level {
if (levelInfo[29]) this.updated = levelInfo[29] + (server.timestampSuffix || "")
if (levelInfo[46]) this.editorTime = +levelInfo[46] || 0
if (levelInfo[47]) this.totalEditorTime = +levelInfo[47] || 0
if (levelInfo[27]) this.password = levelInfo[27];
this.version = +levelInfo[5] || 0;
if (levelInfo[27]) this.password = levelInfo[27]
this.version = +levelInfo[5] || 0
this.copiedID = levelInfo[30] || "0"
this.twoPlayer = levelInfo[31] > 0
this.officialSong = +levelInfo[35] ? 0 : parseInt(levelInfo[12]) + 1
@ -39,21 +42,24 @@ class Level {
this.verifiedCoins = levelInfo[38] > 0
this.starsRequested = +levelInfo[39] || 0
this.ldm = levelInfo[40] > 0
if (+levelInfo[41] > 100000) this.weekly = true
if (+levelInfo[41]) { this.dailyNumber = (+levelInfo[41] > 100000 ? +levelInfo[41] - 100000 : +levelInfo[41]); this.nextDaily = null; this.nextDailyTimestamp = null }
if (+levelInfo[41] > dailyLimit) this.weekly = true
if (+levelInfo[41]) {
this.dailyNumber = (+levelInfo[41] > dailyLimit ? +levelInfo[41] - dailyLimit : +levelInfo[41])
this.nextDaily = null
this.nextDailyTimestamp = null
}
this.objects = +levelInfo[45] || 0
this.large = levelInfo[45] > 40000;
this.large = levelInfo[45] > 40000
this.cp = Number((this.stars > 0) + this.featured + this.epic)
if (levelInfo[17] > 0) this.difficulty = (demonTypes[levelInfo[43]] || "Hard") + " Demon"
if (levelInfo[25] > 0) this.difficulty = 'Auto'
this.difficultyFace = `${levelInfo[17] != 1 ? this.difficulty.toLowerCase() : `demon-${this.difficulty.toLowerCase().split(' ')[0]}`}${this.epic ? '-epic' : `${this.featured ? '-featured' : ''}`}`
this.difficultyFace = `${levelInfo[17] != 1 ? this.difficulty.toLowerCase() : `demon-${this.difficulty.toLowerCase().split(' ', 1)[0]}`}${this.epic ? '-epic' : `${this.featured ? '-featured' : ''}`}`
if (this.password && this.password != 0) {
let xor = new XOR();
let pass = xor.decrypt(this.password, 26364);
if (pass.length > 1) this.password = pass.slice(1);
else this.password = pass;
let xor = new XOR()
let pass = xor.decrypt(this.password, 26364)
this.password = pass.length > 1 ? pass.slice(1) : pass
}
if (server.onePointNine) {
@ -83,9 +89,9 @@ class Level {
this.songSize = "0MB"
this.songID = "Level " + this.officialSong
}
return this
}
}
module.exports = Level;
module.exports = Level

View file

@ -1,4 +1,4 @@
const colors = require('../iconkit/sacredtexts/colors.json');
const colors = require('../iconkit/sacredtexts/colors.json')
class Player {
constructor(account) {

View file

@ -1,5 +1,16 @@
//https://nodejs.org/docs/latest/api/buffer.html#buffers-and-character-encodings
//both only work on "binary strings" and "URI-safe B64"
let toB64 = str => Buffer.from(str).toString('base64url')
let fromB64 = str => Buffer.from(str, 'base64').toString()
const defKey = 37526
module.exports = class XOR {
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(/./gs, c => ({'/': '_', '+': '-'}[c] || c)); }
decrypt(str, key = 37526) { return this.xor(Buffer.from(str.replace(/./gs, c => ({'/': '_', '+': '-'}[c] || c)), 'base64').toString(), key) }
}
xor(str, key) {
key = key.toString()
return String.fromCodePoint(...str.split('')
.map((c, i) => c.charCodeAt(0) ^ key.charCodeAt(i % key.length)))
}
encrypt(str, key = defKey) { return toB64(this.xor(str, key)) }
decrypt(str, key = defKey) { return this.xor(fromB64(str), key) }
}

View file

@ -21,23 +21,23 @@
<div id="rewardLabel">
<img filter="rewardFilter" class="achSelect selectAll" style="margin: 0% 2% 1.9% 0%" class="gdButton inline" src="../assets/select-all.png" height="5%">
<h1 class="smaller inline" style="margin-bottom: 1.5%">Reward</h1>
<h1 class="smaller inline" style="margin-bottom: 1.5%">Reward</h1>
<img filter="rewardFilter" class="achSelect selectNone" style="margin: 0% 0% 1.9% 2%" class="gdButton inline" src="../assets/select-none.png" height="5%">
</div>
</div>
<div id="forms"></div>
<div id="typeLabel">
<img filter="typeFilter" class="achSelect selectAll" style="margin: 0% 2% 1.4% 0%" class="gdButton inline" src="../assets/select-all.png" height="5%">
<h1 class="smaller inline" style="margin-top: 3%; margin-bottom: 1%">Requirement</h1>
<h1 class="smaller inline" style="margin-top: 3%; margin-bottom: 1%">Requirement</h1>
<img filter="typeFilter" class="achSelect selectNone" style="margin: 0% 0% 1.4% 2%" class="gdButton inline" src="../assets/select-none.png" height="5%">
</div>
</div>
<div id="types"></div>
<div id="gameLabel">
<img filter="gameFilter" class="achSelect selectAll" style="margin: 0% 2% 1.8% 0%" class="gdButton inline" src="../assets/select-all.png" height="5%">
<h1 class="smaller inline" style="margin-top: 1%; margin-bottom: 1.3%">Game</h1>
<h1 class="smaller inline" style="margin-top: 1%; margin-bottom: 1.3%">Game</h1>
<img filter="gameFilter" class="achSelect selectNone" style="margin: 0% 0% 1.8% 2%" class="gdButton inline" src="../assets/select-none.png" height="5%">
</div>
</div>
<div id="games">
<img class="gdButton achFilter gameFilter" src="../assets/achievements/gd.png" height="12%" style="margin: 0% 1%" title="Geometry Dash" filter="gd">
<img class="gdButton achFilter gameFilter" src="../assets/achievements/gdm.png" height="12%" style="margin: 0% 1%" title="Geometry Dash Meltdown" filter="meltdown">
@ -59,7 +59,7 @@
<div id="searchBox" class="supercenter dragscroll" style="width: 128vh">
<div style="height: 4.5%"></div>
</div>
<div class="epicbox supercenter gs" style="width: 126vh; height: 80%; pointer-events: none"></div>
<div class="center" style="position:absolute; top: 8%; left: 0%; right: 0%">
@ -79,10 +79,10 @@
</body>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script type="text/javascript" src="../global.js?v=1"></script>
<script type="text/javascript" src="../dragscroll.js"></script>
<script type="text/javascript" src="./global.js?v=1"></script>
<script type="text/javascript" src="./dragscroll.js"></script>
<script>
"use strict";
let disabledFilters = {reward: [], type: [], game: []}
let forms = ["icon", "ship", "ball", "ufo", "wave", "robot", "spider"]
let gameColors = {gd: "255,200,0", meltdown: "255, 100, 0", world: "100,220,0", subzero: "0,255,255"}
@ -99,7 +99,7 @@ function append(reset=true) {
$('#searchBox').html(`<div style="height: 4.5%"></div>`)
achievements.forEach(x => {
let iconImg = `"`;
let iconImg = `"`
if (forms.includes(x.rewardType)) iconImg = `../iconkit/premade/${x.rewardType}_${x.rewardID}.png" width="90%" title="${x.rewardType}_${x.rewardID < 10 ? "0" : ""}${x.rewardID}"`
else if (x.rewardType.startsWith("color")) {
let col = colors[x.rewardID]
@ -129,8 +129,7 @@ Object.keys(ach.types).forEach(x => {
$('#types').append(`<img class="gdButton achFilter typeFilter" filter="${ach.types[x][1].join(" ")}" src="../assets/achievements/${x}.png" title="${ach.types[x][0]}" height="8.5%" style="margin: 0.6% 0.4%">`)
})
achievements = ach.achievements
colors = ach.colors
{achievements, colors} = ach
append()
function label(labelName) {
@ -148,23 +147,23 @@ function label(labelName) {
$(labelFilter).click(function() {
let filters = $(this).attr('filter').split(" ")
let args
if (!$(this).hasClass('achDeselected')) {
$(this).addClass('achDeselected')
filters.forEach(x => disabledFilters[labelName].push(x))
if (labelName == "reward") $(this).attr('src', $(this).attr('src').replace("_on", "_off"))
args = ['_on', '_off']
}
else {
$(this).removeClass('achDeselected')
filters.forEach(x => disabledFilters[labelName] = disabledFilters[labelName].filter(y => y != x))
if (labelName == "reward") $(this).attr('src', $(this).attr('src').replace("_off", "_on"))
args = ['_off', '_on']
}
if (labelName == "reward") $(this).attr('src', $(this).attr('src').replace(...args))
})
}
label("reward")
label("type")
label("game")
const labels = ['reward', 'type', 'game']
labels.forEach(label)
$(document).on('click', '.selectNone', function() { $(`.${$(this).attr('filter')}:not(.achDeselected)`).trigger('click'); $('#selectNone').hide(); $('#selectAll').show() })
$(document).on('click', '.selectAll', function() { $(`.${$(this).attr('filter')}.achDeselected`).trigger('click'); $('#selectNone').show(); $('#selectAll').hide() })
@ -173,23 +172,20 @@ $('#submitFilters').click(function() {
$('.popup').hide()
if (!$('.rewardFilter:not(.achDeselected)').length) $('#rewardLabel .selectAll').trigger('click')
if (!$('.typeFilter:not(.achDeselected)').length) $('#typeLabel .selectAll').trigger('click')
if (!$('.gameFilter:not(.achDeselected)').length) $('#gameLabel .selectAll').trigger('click')
labels.forEach(x => {
if (!$(`.${x}Filter:not(.achDeselected)`).length) $(`#${x}Label .selectAll`).trigger('click')
})
achievements = ach.achievements
.filter(x => !disabledFilters.reward.includes(x.rewardType))
.filter(x => !disabledFilters.type.some(y => x.id.startsWith(y)))
.filter(x => !disabledFilters.game.includes(x.game))
.filter(x => !disabledFilters.reward.includes(x.rewardType))
.filter(x => !disabledFilters.type.some(y => x.id.startsWith(y)))
.filter(x => !disabledFilters.game.includes(x.game))
append()
})
$('#check').click(function() {
completed = !completed
if (completed) $('#check').attr('src', '../assets/check-on.png')
else $('#check').attr('src', '../assets/check-off.png')
$('#check').click(() => {
$('#check').attr('src', `../assets/check-o${completed = !completed ? 'n' : 'ff'}.png`)
append(false)
})
}))
</script>

View file

@ -3,11 +3,12 @@
<meta charset="utf-8">
<meta property="og:type" content="website">
<meta id="meta-title" property="og:title" content="Level Analysis">
<meta id="meta-desc" property="og:description" content="Analyze a Geometry Dash level and view it's objects, portals, color channels, code, and more!">
<meta id="meta-desc" property="og:description" content="Analyze a Geometry Dash level and view it's objects, portals, color channels, code, and more!">
<meta id="meta-image" name="og:image" itemprop="image" content="https://gdbrowser.com/assets/cp.png">
<meta name="twitter:card" content="summary">
<link href="../assets/css/browser.css?v=1" type="text/css" rel="stylesheet">
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-135255146-3"></script><script>window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);}gtag('js', new Date());gtag('config', 'UA-135255146-3');</script>
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-135255146-3"></script>
<script>window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);}gtag('js', new Date());gtag('config', 'UA-135255146-3')</script>
<link rel="icon" href="../assets/cp.png">
</head>
@ -100,25 +101,27 @@
</div>
</body>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script type="text/javascript" src="../global.js"></script>
<script type="text/javascript" src="/misc/global.js"></script>
<script>
function clean(text) {return text.toString().replace(/&/g, "&#38;").replace(/</g, "&#60;").replace(/>/g, "&#62;").replace(/=/g, "&#61;").replace(/"/g, "&#34;").replace(/'/g, "&#39;")}
let disabledPortals = []
let altTriggerSort = false
let formPortals = ['cube', 'ship', 'ball', 'ufo', 'wave', 'robot', 'spider']
let speedPortals = ['-1x', '1x', '2x', '3x', '4x']
let sizePortals = ['mini', 'big']
let dualPortals = ['dual', 'single']
let mirrorPortals = ['mirrorOn', 'mirrorOff']
const PORTALS = {
form: ['cube', 'ship', 'ball', 'ufo', 'wave', 'robot', 'spider'],
speed: ['-1x', '1x', '2x', '3x', '4x'],
size: ['mini', 'big'],
dual: ['dual', 'single'],
mirror: ['mirrorOn', 'mirrorOff']
}
fetch(`/api${window.location.pathname}`).then(res => res.json()).then(res => {
fetch(`../api${window.location.pathname}`).then(res => res.json()).then(res => {
if (!res.level) {
switch(res) {
case -1: $('#errorMessage').html("This level could not be be <cr>downloaded</cr>. Either the servers rejected the request, or the level simply <co>doesn't exist at all</co>."); break;
case -2: $('#errorMessage').html("This level's data appears to be <cr>corrupt</cr> and cannot be <cy>parsed</cy>. It most likely won't load in GD."); break;
case -1: $('#errorMessage').html("This level could not be be <cr>downloaded</cr>. Either the servers rejected the request, or the level simply <co>doesn't exist at all</co>."); break
case -2: $('#errorMessage').html("This level's data appears to be <cr>corrupt</cr> and cannot be <cy>parsed</cy>. It most likely won't load in GD."); break
case -3: $('#errorMessage').html("This level's data could not be obtained because <ca>RobTop</ca> has disabled <cg>level downloading</cg>."); break;
}
popupEsc = false
@ -129,8 +132,8 @@ fetch(`../api${window.location.pathname}`).then(res => res.json()).then(res => {
$('#objectCount').text(commafy(res.objects) + " objects")
document.title = "Analysis of " + res.level.name
$('#meta-title').attr('content', "Analysis of " + res.level.name)
$('#meta-desc').attr('content', `${res.portals.split(",").length}x portals, ${res.orbs.total || 0}x orbs, ${res.triggers.total || 0}x triggers, ${res.misc.glow || 0}x glow...`)
$('#meta-title').attr('content', "Analysis of " + res.level.name)
$('#meta-desc').attr('content', `${res.portals.split(",").length}x portals, ${res.orbs.total || 0}x orbs, ${res.triggers.total || 0}x triggers, ${res.misc.glow || 0}x glow...`)
let hdPercent = (res.highDetail / res.objects) * 100
@ -216,7 +219,7 @@ function appendTriggerGroups() {
})
miscList.forEach(x => {
if (x == "objects") return;
if (x == "objects") return
else $('#misc').append(`<div class="inline miscDiv"><img height="40%" src='../assets/objects/${x.slice(0, -1)}.png'><h3 style="padding-top: 15%">x${commafy(res.misc[x][0])}<br>${res.misc[x][1]}</h3></div>`)
})
@ -259,11 +262,8 @@ function appendTriggerGroups() {
else disabledPortals.push($(this).attr('portal'))
portals = res.portals.split(", ").map(x => x.split(" "))
if (disabledPortals.includes('form')) portals = portals.filter(x => !formPortals.includes(x[0]))
if (disabledPortals.includes('speed')) portals = portals.filter(x => !speedPortals.includes(x[0]))
if (disabledPortals.includes('size')) portals = portals.filter(x => !sizePortals.includes(x[0]))
if (disabledPortals.includes('dual')) portals = portals.filter(x => !dualPortals.includes(x[0]))
if (disabledPortals.includes('mirror')) portals = portals.filter(x => !mirrorPortals.includes(x[0]))
for (let P in PORTALS)
if (disabledPortals.includes(P)) portals = portals.filter(x => !PORTALS[P].includes(x[0]))
if (disabledPortals.includes('dupe')) {
portals.reverse().forEach((x, y) => {if (portals[y+1] && portals[y+1][0] == x[0]) portals[y][0] = null;})
@ -293,7 +293,8 @@ function appendTriggerGroups() {
hsv.s = Number(hsv.s).toFixed(2)
hsv.v = Number(hsv.v).toFixed(2)
}
let hex = "#" + ((1 << 24) + (+col.r << 16) + (+col.g << 8) + +col.b).toString(16).slice(1)
// padded (fixed-size) RGB hex
let hex = "#" + ((1 << 24) | (col.r << 16) | (col.g << 8) | col.b).toString(16).slice(1) // remove sentinel nibble
$('#colorStuff').html(`
<h2 class="slightlySmaller">${isNaN(col.channel) ? col.channel : "Color " + col.channel}</h2>
<div class="colorSection">
@ -343,6 +344,5 @@ function appendTriggerGroups() {
$('#loadingDiv').hide()
$('#analysisDiv').show()
});
})
</script>

View file

@ -3,11 +3,11 @@
<head>
<title>GD Level Browser API</title>
<meta charset="utf-8">
<link rel="icon" href="../assets/difficulties/auto.png">
<link href="../assets/css/api.css" type="text/css" rel="stylesheet">
<link rel="icon" href="/assets/difficulties/auto.png">
<link href="/assets/css/api.css" type="text/css" rel="stylesheet">
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.js"></script>
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,600,700,800&display=swap" rel="stylesheet">
<meta id="meta-title" property="og:title" content="GDBrowser API">
<meta id="meta-desc" property="og:description" content="It's basically the Geometry Dash API, but not terrible!">
<meta id="meta-image" name="og:image" itemprop="image" content="https://gdbrowser.com/assets/difficulties/auto.png">
@ -35,22 +35,22 @@
// smooth scrolling through anchors
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function(e) {
e.preventDefault();
e.preventDefault()
document.querySelector(this.getAttribute('href')).scrollIntoView({
behavior: 'smooth'
});
});
});
// menu button
document.getElementById('menu-btn').onclick = function(){
document.getElementsByClassName('header-links')[0].classList.toggle('hid');
document.getElementsByClassName('header-links')[0].classList.toggle('hid')
document.getElementById('menu-btn').classList.toggle('active');
}
for(let i = 0; i < document.getElementsByClassName('header-link').length; i++){
document.getElementsByClassName('header-link')[i].onclick = function(){
document.getElementsByClassName('header-links')[0].classList.toggle('hid');
document.getElementsByClassName('header-links')[0].classList.toggle('hid')
document.getElementById('menu-btn').classList.toggle('active');
}
}

View file

@ -3,11 +3,11 @@
<head>
<title>GD Level Browser API</title>
<meta charset="utf-8">
<link rel="icon" href="../assets/difficulties/auto.png">
<link href="../assets/css/api.css" type="text/css" rel="stylesheet">
<link rel="icon" href="/assets/difficulties/auto.png">
<link href="/assets/css/api.css" type="text/css" rel="stylesheet">
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.js"></script>
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,600,700,800&display=swap" rel="stylesheet">
<meta id="meta-title" property="og:title" content="GDBrowser API">
<meta id="meta-desc" property="og:description" content="It's basically the Geometry Dash API, but not terrible!">
<meta id="meta-image" name="og:image" itemprop="image" content="https://gdbrowser.com/assets/difficulties/auto.png">
@ -19,7 +19,7 @@
<div class="main-header-wrap">
<div class="main-header">
<div class="header-drawer" id="menu-btn">
<img src="../assets/menu-ic.svg" alt="Menu" id="menu-btn-img">
<img src="/assets/menu-ic.svg" alt="Menu" id="menu-btn-img">
</div>
<div class="header-links hid">
<a class="header-link" href="#intro">Introduction</a>
@ -37,24 +37,24 @@
<div class="category-content">
<a class="header-link" href="#profile">Profiles</a>
<a class="header-link" href="#leaderboard">Leaderboards</a>
<a class="header-link" href="#comments">Comments & Posts</a>
<a class="header-link" href="#comments">Comments & Posts</a>
</div>
</div>
<div class="header-category">
<div class="category-name">Account</div>
<div class="category-content">
<a class="header-link" href="#commenting">Commenting</a>
<a class="header-link" href="#profileposting">Profile Posting</a>
<a class="header-link" href="#liking">Liking</a>
<a class="header-link" href="#messages">Messages</a>
<a class="header-link" href="#commenting">Commenting</a>
<a class="header-link" href="#profileposting">Profile Posting</a>
<a class="header-link" href="#liking">Liking</a>
<a class="header-link" href="#messages">Messages</a>
</div>
</div>
<div class="header-category">
<div class="category-name">Misc</div>
<div class="category-content">
<a class="header-link" href="#analyze">Level Analysis</a>
<a class="header-link" href="#artist">Song Verification</a>
<a class="header-link" href="#icons">Icons</a>
<a class="header-link" href="#analyze">Level Analysis</a>
<a class="header-link" href="#artist">Song Verification</a>
<a class="header-link" href="#icons">Icons</a>
</div>
</div>
</div>
@ -186,7 +186,7 @@
<main>
<div id="profile" class="anchor"></div>
<div class="main-block">
<h1>Profiles</h1>
<p>/api/profile/username-or-id</p>
@ -244,7 +244,7 @@
<main>
<div id="search" class="anchor"></div>
<div class="main-block">
<h1>Searching</h1>
<p>/api/search/search-query</p>
@ -540,10 +540,10 @@
<div class="main-block">
<h1>Commenting (usually broken)</h1>
<p>POST: /postComment</p>
<p>Leaves a comment on a level. This one is a POST request!</p>
<p>*Commenting has a rate limit of 15 seconds</p>
<br>
<p class="reveal" onclick="revealSection('#params-commenting')"><b>Parameters (6)</b></p>
<div class="subdiv" id="params-commenting">
@ -555,7 +555,7 @@
<p>percent: The percent shown on the comment (optional)</p>
<p>color: If the comment should have a special pink color on GDBrowser (optional)</p>
</div>
<br>
<p class="reveal" onclick="revealSection('#request-commenting')"><b>Example</b></p>
@ -571,7 +571,7 @@
<br>
<p>If a status of 200 is returned, then the comment was successfully posted. Otherwise, a 400 will return with an error message.</p>
</div>
</div>
<div class="seperator"></div>
</main>
@ -581,9 +581,9 @@
<div class="main-block">
<h1>Profile Posting (usually broken)</h1>
<p>POST: /postProfileComment</p>
<p>Leaves a profile post. This one is a POST request!</p>
<br>
<p class="reveal" onclick="revealSection('#params-profileposting')"><b>Parameters (5)</b></p>
<div class="subdiv" id="params-profileposting">
@ -593,7 +593,7 @@
<p>password: Your password (as plain text)</p>
<p>color: If the comment should have a special pink color on GDBrowser (optional)</p>
</div>
<br>
<p class="reveal" onclick="revealSection('#request-profileposting')"><b>Example</b></p>
@ -607,7 +607,7 @@
<br>
<p>If a status of 200 is returned, then the profile post was successfully posted. Otherwise, a 400 will return with an error message.</p>
</div>
</div>
<div class="seperator"></div>
</main>
@ -618,9 +618,9 @@
<div class="main-block">
<h1>Liking (usually broken)</h1>
<p>POST: /like</p>
<p>Likes/dislikes level, comment, or post. This one is a POST request!</p>
<br>
<p class="reveal" onclick="revealSection('#params-liking')"><b>Parameters (6)</b></p>
<div class="subdiv" id="params-liking">
@ -631,7 +631,7 @@
<p>accountID: Your account ID</p>
<p>password: Your password (as plain text)</p>
</div>
<br>
<p class="reveal" onclick="revealSection('#request-liking')"><b>Example</b></p>
@ -649,7 +649,7 @@
<p>A status of 200 will return if everything goes well, otherwise a 400 will return with an error message.<br>
Liking a comment multiple times on the same account will return a 200, but not actually increase the in-game like counter.</p>
</div>
</div>
<div class="seperator"></div>
</main>
@ -665,7 +665,7 @@
<b>/sendMessage</b> (sends a message)</p>
<p>I decided to put all 4 of these requests in one section because they're fairly similar ¯\_(ツ)_/¯</p>
<br>
<p class="reveal" onclick="revealSection('#params-msg')"><b>Parameters</b></p>
<div class="subdiv" id="params-msg">
@ -687,7 +687,7 @@
<p>message: The content of the message, max 300 characters</p>
<p>color: If the message should have a special pink color on GDBrowser (optional)</p>
</div>
<br>
<p class="reveal" onclick="revealSection('#request-msg')"><b>Example</b></p>
@ -699,7 +699,7 @@
&accountID=106255<br>
&password=KitsuneColon333<br>
<br>
<p>Read message with ID of 177013:</p>
<p>POST /messages/177013<br>
?accountID=106255<br>
@ -712,7 +712,7 @@
&id=177013<br>
&password=KitsuneColon333<br>
<br>
<p>Send "Hello!" to Tubular9:</p>
<p>POST /sendMessage<br>
?accountID=106255<br>
@ -877,22 +877,22 @@
// smooth scrolling through anchors
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function(e) {
e.preventDefault();
e.preventDefault()
document.querySelector(this.getAttribute('href')).scrollIntoView({
behavior: 'smooth'
});
});
});
// menu button
document.getElementById('menu-btn').onclick = function(){
document.getElementsByClassName('header-links')[0].classList.toggle('hid');
document.getElementsByClassName('header-links')[0].classList.toggle('hid')
document.getElementById('menu-btn').classList.toggle('active');
}
for(let i = 0; i < document.getElementsByClassName('header-link').length; i++){
document.getElementsByClassName('header-link')[i].onclick = function(){
document.getElementsByClassName('header-links')[0].classList.toggle('hid');
document.getElementsByClassName('header-links')[0].classList.toggle('hid')
document.getElementById('menu-btn').classList.toggle('active');
}
}

View file

@ -3,13 +3,13 @@
<meta charset="utf-8">
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-135255146-3"></script>
<script>window.dataLayer = window.dataLayer || []; function gtag() { dataLayer.push(arguments); } gtag('js', new Date()); gtag('config', 'UA-135255146-3');</script>
<link rel="icon" href="../assets/folder.png">
<link rel="icon" href="/assets/folder.png">
<meta name="robots" content="noindex">
<meta id="meta-desc" property="og:description" content='i only upload high quality geometry dash asset rips'>
<style>
body {
background-image: linear-gradient(#2b2b2b, #171717) !important;
background-image: linear-gradient(#2b2b2b, #171717) !important
margin-left: 12px;
}
@ -21,16 +21,16 @@
}
h1, p {
color: white;
font-family: aller, helvetica, arial;
color: white
font-family: aller, helvetica, arial
font-weight: normal;
}
.name {
width: 300px;
display: inline-block;
margin: 0 5 4 5;
font-family: aller, helvetica, arial;
width: 300px
display: inline-block
margin: 0 5 4 5
font-family: aller, helvetica, arial
font-size: 20px;
}
</style>

View file

@ -1,10 +1,10 @@
<head>
<title>Boomlings Leaderboard</title>
<meta charset="utf-8">
<link href="../assets/css/boomlings.css?v=1" type="text/css" rel="stylesheet">
<link href="/assets/css/boomlings.css?v=1" type="text/css" rel="stylesheet">
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-135255146-3"></script>
<script>window.dataLayer = window.dataLayer || []; function gtag() { dataLayer.push(arguments); } gtag('js', new Date()); gtag('config', 'UA-135255146-3');</script>
<link rel="icon" href="../assets/boomlings/red.png">
<link rel="icon" href="/assets/boomlings/red.png">
<meta id="meta-title" property="og:title" content="Boomlings Leaderboard">
<meta id="meta-desc" property="og:description" content='"Never ask me for anything ever again." - Masahiro Sakurai (probably)'>
<meta id="meta-image" name="og:image" itemprop="image" content="https://gdbrowser.com/assets/boomlings/red.png">
@ -22,19 +22,19 @@
<div id="borderbox" class="supercenter dragscroll"></div>
<div class="supercenter" id="loading" style="height: 10%; top: 47%; display: none;">
<img class="spin noSelect" src="../assets/loading.png" height="105%">
<img class="spin noSelect" src="/assets/loading.png" height="105%">
</div>
<div style="position:absolute; top: 2%; left: 1.5%; width: 10%; height: 25%; pointer-events: none">
<img class="gdButton yesClick" id="backButton" src="../assets/back.png" height="30%" onclick="backButton()">
<img class="gdButton yesClick" id="backButton" src="/assets/back.png" height="30%" onclick="backButton()">
</div>
<div style="position:absolute; bottom: 0%; left: 0%; width: 100%">
<img class="cornerPiece" src="../assets/corner.png" width=7%;>
<img class="cornerPiece" src="/assets/corner.png" width=7%;>
</div>
<div style="position:absolute; bottom: 0%; right: 0%; width: 100%; text-align: right;">
<img class="cornerPiece" src="../assets/corner.png" width=7%; style="transform: scaleX(-1)">
<img class="cornerPiece" src="/assets/corner.png" width=7%; style="transform: scaleX(-1)">
</div>
</div>
@ -42,14 +42,14 @@
</body>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery.lazy/1.7.9/jquery.lazy.min.js"></script>
<script type="text/javascript" src="../global.js?v=1"></script>
<script type="text/javascript" src="../dragscroll.js"></script>
<script type="text/javascript" src="/misc/global.js?v=1"></script>
<script type="text/javascript" src="/misc/dragscroll.js"></script>
<script>
$('#boomerbox').html('')
$('#loading').show()
fetch(`../api/boomlings`).then(res => res.json()).then(res => {
fetch(`/api/boomlings`).then(res => res.json()).then(res => {
if (res == "0") res = []
$('#boomerbox').html('')
@ -59,19 +59,19 @@
$('#boomerbox').append(`<div class="inline leaderboardSlot">
<div class="flex" style="width: 7vh; height: 100%"><h1 class="rankNumber" style="width: inherit">${x.rank}</h1></div>
<img src="../assets/boomlings/icons/${x.boomling}.png" style="width: 12vh; align-self: center">
<img src="/assets/boomlings/icons/${x.boomling}.png" style="width: 12vh; align-self: center">
<p class="flex username" style="width: 48%">${x.name}</p>
<div class="flex" style="width: 22%">
<h1 class="level">Level ${x.level}<br><span class="score">${x.score}</span>
<img class="powerup inline" height="20%" src="../assets/boomlings/powerups/${x.powerups[0]}.png">` +
`<img class="powerup inline" height="20%" src="../assets/boomlings/powerups/${x.powerups[1]}.png">` +
`<img class="powerup inline" height="20%" src="../assets/boomlings/powerups/${x.powerups[2]}.png">
<img class="powerup inline" height="20%" src="/assets/boomlings/powerups/${x.powerups[0]}.png">` +
`<img class="powerup inline" height="20%" src="/assets/boomlings/powerups/${x.powerups[1]}.png">` +
`<img class="powerup inline" height="20%" src="/assets/boomlings/powerups/${x.powerups[2]}.png">
</h1>
</div>
<img src="../assets/boomlings/levels/${x.boomlingLevel}.png" style="width: 6.5%; align-self: center">
<img src="/assets/boomlings/levels/${x.boomlingLevel}.png" style="width: 6.5%; align-self: center">
</div>`)
})

View file

@ -1,19 +1,19 @@
<head>
<title>Coming Soon...</title>
<meta charset="utf-8">
<link href="../assets/css/browser.css?v=1" type="text/css" rel="stylesheet">
<link rel="icon" href="../assets/coin.png">
<link href="/assets/css/browser.css?v=1" type="text/css" rel="stylesheet">
<link rel="icon" href="/assets/coin.png">
</head>
<body class="levelBG">
<div class="supercenter" style="height: 70%;">
<h1>Coming soon!</h1>
<br>
<img src="../assets/javi.png" style="width: 400px; border-radius: 20px; border: 2px solid black;">
<img src="/assets/javi.png" style="width: 400px; border-radius: 20px; border: 2px solid black;">
<br>
<br>
<div class="gdbutton">
<a href="https://twitter.com/TheRealGDColon"><img class="inline"src="../assets/twitter.png" width=6%> <h2 class="inline" style="margin-left: 2%;">stay updated</h2></a>
<a href="https://twitter.com/TheRealGDColon"><img class="inline"src="/assets/twitter.png" width=6%> <h2 class="inline" style="margin-left: 2%;">stay updated</h2></a>
</div>
</div>
</body>

View file

@ -1,9 +1,9 @@
<head>
<title>Comments</title>
<meta charset="utf-8">
<link href="../assets/css/browser.css?v=1" type="text/css" rel="stylesheet">
<link href="/assets/css/browser.css?v=1" type="text/css" rel="stylesheet">
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-135255146-3"></script><script>window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);}gtag('js', new Date());gtag('config', 'UA-135255146-3');</script>
<link rel="icon" href="../assets/like.png">
<link rel="icon" href="/assets/like.png">
<meta id="meta-title" property="og:title" content="Level Comments">
<meta id="meta-desc" property="og:description" content="View the comments of a Geometry Dash level!">
<meta id="meta-image" name="og:image" itemprop="image" content="https://gdbrowser.com/assets/like.png">
@ -28,16 +28,16 @@
<div style="min-height: 16%; max-height: 16%">
<p id="message" style="padding: 0% 10%; margin-top: 1.5%"></p>
</div>
<img src="../assets/btn-cancel.png" height=10%; class="postButton gdButton center" style="margin-right: 1%" onclick="$('#postComment').hide(); $('textarea').val('')">
<img src="../assets/btn-submit.png" type="submit" height=10%; class="postButton gdButton center" style="margin-left: 1%" id="submitComment">
<img src="/assets/btn-cancel.png" height=10%; class="postButton gdButton center" style="margin-right: 1%" onclick="$('#postComment').hide(); $('textarea').val('')">
<img src="/assets/btn-submit.png" type="submit" height=10%; class="postButton gdButton center" style="margin-left: 1%" id="submitComment">
</div>
</div>
<div class="popup" id="likeComment">
<div class="brownbox bounce center supercenter" style="height: 75%; width: 100vh">
<h1 class="smaller center" style="font-size: 5.5vh">Vote</h1>
<img src="../assets/smashLike.png" id="likebtn" class="inline gdButton likeButton"><!--
--><img src="../assets/smashDislike.png" id="dislikebtn" class="inline gdButton likeButton youAreNotTheOne">
<img src="/assets/smashLike.png" id="likebtn" class="inline gdButton likeButton"><!--
--><img src="/assets/smashDislike.png" id="dislikebtn" class="inline gdButton likeButton youAreNotTheOne">
<form action="nothing lol">
<h3 class="center">GD Username</h3>
<input type="text" name="gdbrowser" id="like-username" maxlength="50" style="height: 8vh; width: 90%; text-align: center; margin-top: 0.5%">
@ -47,17 +47,17 @@
<div style="min-height: 18%; max-height: 18%">
<p id="likeMessage" style="padding: 0% 10%; margin-top: 2.5%"></p>
</div>
<img src="../assets/btn-cancel.png" height=10%; class="postButton gdButton center" style="margin-right: 1%" onclick="$('#likeComment').hide(); $('#likebtn').trigger('click');">
<img src="../assets/btn-submit.png" type="submit" height=10%; class="postButton gdButton center" style="margin-left: 1%" id="submitVote">
<img src="/assets/btn-cancel.png" height=10%; class="postButton gdButton center" style="margin-right: 1%" onclick="$('#likeComment').hide(); $('#likebtn').trigger('click');">
<img src="/assets/btn-submit.png" type="submit" height=10%; class="postButton gdButton center" style="margin-left: 1%" id="submitVote">
</div>
</div>
<div style="position:absolute; bottom: 0%; left: 0%; width: 100%">
<img class="cornerPiece" src="../assets/corner.png" width=7%;>
<img class="cornerPiece" src="/assets/corner.png" width=7%;>
</div>
<div style="position:absolute; bottom: 0%; right: 0%; width: 100%; text-align: right;">
<img class="cornerPiece" src="../assets/corner.png" width=7%; style="transform: scaleX(-1)">
<img class="cornerPiece" src="/assets/corner.png" width=7%; style="transform: scaleX(-1)">
</div>
<div style="text-align: right; position:absolute; top: 1%; right: 1.25%">
@ -69,39 +69,39 @@
<div class="lightBox center dragscroll" id="commentBox" style="margin: 4.5% auto; width: 115vh; height: 86%; background-color: #934E27"></div>
<div class="supercenter" style="left: 97%; top: 15.5%; height: 8.5%">
<img class="gdButton" id="lastPage" src="../assets/double-arrow.png" height="90%">
<img class="gdButton" id="lastPage" src="/assets/double-arrow.png" height="90%">
</div>
<div class="supercenter" id="firstPage" style="left: 97%; top: 27.5%; height: 8.5%; display: none;">
<img class="gdButton refreshBtn" src="../assets/double-arrow.png" style="transform: scaleX(-1);" height="90%">
<img class="gdButton refreshBtn" src="/assets/double-arrow.png" style="transform: scaleX(-1);" height="90%">
</div>
<div class="supercenter" id="refreshButton" style="left: 97%; top: 27.5%; height: 10%">
<img class="gdButton refreshBtn" src="../assets/refresh.png" height="90%">
<img class="gdButton refreshBtn" src="/assets/refresh.png" height="90%">
</div>
<div class="supercenter" style="left: 3%; top: 15%; height: 10%">
<img class="gdButton" id="topSort" src="../assets/sort-likes.png" height="90%">
<img class="gdButton" id="topSort" src="/assets/sort-likes.png" height="90%">
</div>
<div class="supercenter" style="left: 3%; top: 25.5%; height: 10%">
<img class="gdButton" id="timeSort" src="../assets/sort-time.png" height="90%">
<img class="gdButton" id="timeSort" src="/assets/sort-time.png" height="90%">
</div>
<div class="supercenter" style="left: 3%; top: 36%; height: 10%">
<img class="gdButton" id="compactMode" src="../assets/compact-off.png" height="90%">
<img class="gdButton" id="compactMode" src="/assets/compact-off.png" height="90%">
</div>
<div class="supercenter" style="left: 3%; top: 63%; height: 11%">
<img class="gdButton" id="autoMode" src="../assets/playbutton.png" height="90%">
<img class="gdButton" id="autoMode" src="/assets/playbutton.png" height="90%">
</div>
<div class="supercenter" id="pageDown" style="left: 3%; top: 50%; height: 10%; display: none;">
<img class="gdButton" src="../assets/arrow-left.png" height="95%">
<img class="gdButton" src="/assets/arrow-left.png" height="95%">
</div>
<div class="supercenter" id="pageUp" style="left: 97%; top: 50%; height: 10%;">
<img class="gdButton" src="../assets/arrow-right.png" height="95%">
<img class="gdButton" src="/assets/arrow-right.png" height="95%">
</div>
<div class="supercenter" style="top: 4.8%; height: 10%; width: 100%;">
@ -114,15 +114,15 @@
<h2 class="smallGold inline" id="levelVersion"></h2>
<a id="levelLink"><h2 class="smallGold inline gdButton" id="levelID" style="margin-left: 6vh"></h2></a>
</div>
<div id="leaveComment" style="display: none; position:absolute;bottom: 0%;right: 0%;width: 14%;text-align: right;transform: translate(30%, 40%);">
<img class="gdButton" src="../assets/comment.png" width="60%" onclick="$('#content').trigger('input'); $('#postComment').show();">
<img class="gdButton" src="/assets/comment.png" width="60%" onclick="$('#content').trigger('input'); $('#postComment').show();">
</div>
</div>
<div style="position:absolute; top: 2%; left: 1.5%; width: 10%; height: 25%; pointer-events: none">
<img class="gdButton yesClick" id="backButton" src="../assets/back.png" height="30%" onclick="backButton()">
<img class="gdButton yesClick" id="backButton" src="/assets/back.png" height="30%" onclick="backButton()">
</div>
</div>
@ -130,9 +130,9 @@
</body>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/6.2.2/browser/pixi.js"></script>
<script type="text/javascript" src="../iconkit/icon.js"></script>
<script type="text/javascript" src="../global.js?v=1"></script>
<script type="text/javascript" src="../dragscroll.js"></script>
<script type="text/javascript" src="/iconkit/icon.js"></script>
<script type="text/javascript" src="/misc/global.js?v=1"></script>
<script type="text/javascript" src="/misc/dragscroll.js"></script>
<script>
let {mode, compact} = JSON.parse(localStorage.getItem('commentPreset') || '{"mode": "top", "compact": true}')
@ -150,13 +150,13 @@ let auto = false
let interval = null
let commentCache = {}
let target = `../api/level/${lvlID}`
let target = `/api/level/${lvlID}`
if (lvlID > 999999999 || lvlID < -999999999) window.location.href = window.location.href.replace("comments", "search")
if (!Number.isInteger(+lvlID)) {history = true; target = `../api/profile/${lvlID}`}
if (!Number.isInteger(+lvlID)) {history = true; target = `/api/profile/${lvlID}`}
else lvlID = Math.round(+lvlID)
if (mode == "top") { mode = "top"; $('#topSort').attr('src', "../assets/sort-likes-on.png") }
else $('#timeSort').attr('src', "../assets/sort-time-on.png")
if (mode == "top") { mode = "top"; $('#topSort').attr('src', "/assets/sort-likes-on.png") }
else $('#timeSort').attr('src', "/assets/sort-time-on.png")
function clean(text) {return text.replace(/&/g, "&#38;").replace(/</g, "&#60;").replace(/>/g, "&#62;").replace(/=/g, "&#61;").replace(/"/g, "&#34;").replace(/'/g, "&#39;")}
@ -197,7 +197,7 @@ function buildComments(lvl) {
if (!lvl.name) $('#leaveComment').hide()
$('#levelAuthor').text("By " + (lvl.author || "-"))
$('#levelID').text("ID: " + lvlID)
if (lvl.accountID && lvl.author != "-")
if (lvl.accountID && lvl.author != "-")
if (lvl.id) $('#levelLink').attr('href', '../' + lvl.id)
else $('#levelID').removeClass("gdButton")
$('#levelVersion').text("Version: " + (lvl.version || 0))
@ -211,10 +211,10 @@ function buildComments(lvl) {
function appendComments(auto, noCache) {
if (loadingComments) return;
else loadingComments = true;
if (loadingComments) return
else loadingComments = true
if (!auto) $('#commentBox').html(`<div class="supercenter" id="loading" style="height: 12%;"><img class="spin noSelect" src="../assets/loading.png" height="105%"></div>`)
if (!auto) $('#commentBox').html(`<div class="supercenter" id="loading" style="height: 12%;"><img class="spin noSelect" src="/assets/loading.png" height="105%"></div>`)
if (page == 0) {
$('#pageDown').hide()
@ -230,13 +230,12 @@ else {
}
if (!noCache && commentCache[page]) addComments(commentCache[page])
fetch(`../api${!history ? window.location.pathname : "/comments/" + lvl.playerID}?count=${compact && !auto ? 20 : 10}&page=${page}${history ? "&type=commentHistory" : ""}&${mode}`).then((res) => {
if (res.status === 204) return [];
return res.json();
}).then(addComments)
fetch(
`/api${!history ? window.location.pathname : "/comments/" + lvl.playerID}?count=${compact && !auto ? 20 : 10}&page=${page}${history ? "&type=commentHistory" : ""}&${mode}`
).then(res => res.status === 204 ? [] : res.json()).then(addComments)
function addComments(res) {
if (history && lvl.commentHistory != "all") $('#pageUp').hide()
if (res == -1 || (history && lvl.commentHistory != "all")) {
@ -275,7 +274,7 @@ function addComments(res) {
<gdicon class="commentIcon inline" cacheID=${x.playerID} iconID=${x.icon.icon} iconForm="${x.icon.form}" col1="${x.icon.col1}" col2="${x.icon.col2}" glow="${x.icon.glow}"></gdicon>
<a href=../${x.accountID == "0" ? `search/${x.playerID}?user` : `../u/${x.accountID}.`}>
<h2 class="inline gdButton ${x.accountID == "0" ? "green unregistered" : ""}">${userName}</h2></a>
${modNumber > 0 ? `<img class="inline" src="../assets/mod${modNumber > 2 ? "-extra" : modNumber == 2 ? "-elder" : ""}.png" style="margin-left: 1%; width: 3.5%">` : ""}
${modNumber > 0 ? `<img class="inline" src="/assets/mod${modNumber > 2 ? "-extra" : modNumber == 2 ? "-elder" : ""}.png" style="margin-left: 1%; width: 3.5%">` : ""}
<p class="commentPercent inline">${x.percent ? x.percent + "%" : ""}</p>
</div>
@ -287,14 +286,14 @@ function addComments(res) {
<div class="commentLikes">
${history ? `<h3 style="margin-right: 1.5vh; pointer-events: all;" class="gold inline"><a href="../${x.levelID}">(${x.levelID})</a></h3>` : ""}
<div class="inline gdButton likeComment" commentID="${x.ID}" ${x.levelID ? `levelID="${x.levelID}"` : ""}">
<img id="thumb-${x.ID}" class="inline gdButton" ${x.likes < 0 ? "style='transform: translateY(25%); margin-right: 0.4%'" : ""} src="../assets/${x.likes < 0 ? "dis" : ""}like.png" height=20%>
<img id="thumb-${x.ID}" class="inline gdButton" ${x.likes < 0 ? "style='transform: translateY(25%); margin-right: 0.4%'" : ""} src="/assets/${x.likes < 0 ? "dis" : ""}like.png" height=20%>
</div>
<h3 id="likes-${x.ID}" class="inline">${x.likes}</h3>
</div>
</div>`
</div>`
////// COMPACT MODE //////
: `
<div class="commentBG compactBG ${bgCol}">
<div class="comment compact" commentID="${x.ID}">
@ -302,7 +301,7 @@ function addComments(res) {
<gdicon class="commentIcon inline" cacheID=${x.playerID} iconID=${x.icon.icon} iconForm="${x.icon.form}" col1="${x.icon.col1}" col2="${x.icon.col2}" glow="${x.icon.glow}"></gdicon>
<a href=../${x.accountID == "0" ? `search/${x.playerID}?user` : `../u/${x.accountID}.`}>
<h2 class="inline gdButton ${x.accountID == "0" ? "green unregistered" : ""}">${userName}</h2></a>
${modNumber > 0 ? `<img class="inline" src="../assets/mod${modNumber > 2 ? "-extra" : modNumber == 2 ? "-elder" : ""}.png" style="margin-left: 1.2%; width: 3.2%">` : ""}
${modNumber > 0 ? `<img class="inline" src="/assets/mod${modNumber > 2 ? "-extra" : modNumber == 2 ? "-elder" : ""}.png" style="margin-left: 1.2%; width: 3.2%">` : ""}
<p class="commentPercent inline">${x.percent ? x.percent + "%" : ""}</p>
</div>
@ -314,7 +313,7 @@ function addComments(res) {
<div class="commentLikes">
${history ? `<h3 style="margin-right: 0.5vh; pointer-events: all;" class="gold inline"><a href="../${x.levelID}">(${x.levelID})</a></h3>` : ""}
<div class="inline gdButton likeComment" commentID="${x.ID}"${x.levelID ? `levelID="${x.levelID}"` : ""}>
<img id="thumb-${x.ID}" class="inline" ${x.likes < 0 ? "style='transform: translateY(15%); margin-right: 0.4%'" : ""} src="../assets/${x.likes < 0 ? "dis" : ""}like.png" height=27%>
<img id="thumb-${x.ID}" class="inline" ${x.likes < 0 ? "style='transform: translateY(15%); margin-right: 0.4%'" : ""} src="/assets/${x.likes < 0 ? "dis" : ""}like.png" height=27%>
</div>
<h3 id="likes-${x.ID}" class="inline">${x.likes}</h3>
</div>
@ -331,7 +330,7 @@ function addComments(res) {
$(this).css('font-size', (3.5 - (overflow)) + 'vh')
}
});
})
renderIcons()
$('#loading').hide()
@ -356,39 +355,39 @@ function resetSort() {
}
$('#topSort').click(function() {
if (mode == "top" || loadingComments) return;
if (mode == "top" || loadingComments) return
resetSort()
mode = "top";
$('#timeSort').attr('src', "../assets/sort-time.png")
$('#topSort').attr('src', "../assets/sort-likes-on.png")
$('#timeSort').attr('src', "/assets/sort-time.png")
$('#topSort').attr('src', "/assets/sort-likes-on.png")
appendComments()
})
$('#timeSort').click(function() {
if (mode == "time" || loadingComments) return;
if (mode == "time" || loadingComments) return
resetSort()
mode = "time";
$('#timeSort').attr('src', "../assets/sort-time-on.png")
$('#topSort').attr('src', "../assets/sort-likes.png")
$('#timeSort').attr('src', "/assets/sort-time-on.png")
$('#topSort').attr('src', "/assets/sort-likes.png")
appendComments()
})
$('#compactMode').click(function() {
if (loadingComments) return;
if (loadingComments) return
compact = !compact
lastPage = 0;
castPage = 0;
page = 0;
$('#compactMode').attr('src', `../assets/compact-${compact ? "on" : "off"}.png`)
appendComments()
})
$('#autoMode').click(function() {
if (loadingComments) return;
if (loadingComments) return
auto = !auto
mode = "time"
page = 0;
$('#timeSort').attr('src', "../assets/sort-time-on.png")
$('#topSort').attr('src', "../assets/sort-likes.png")
$('#timeSort').attr('src', "/assets/sort-time-on.png")
$('#topSort').attr('src', "/assets/sort-likes.png")
if (auto) {
document.title = "[LIVE] " + document.title
@ -407,8 +406,8 @@ $('#autoMode').click(function() {
$(document).on('click', '.refreshBtn', function () {
if (loadingComments) return
lastPage = 0;
page = 0;
lastPage = 0
page = 0
appendComments(false, true)
})
@ -430,7 +429,7 @@ $('#submitComment').click(function() {
$('.postbutton').hide()
allowEsc = false
fetch(`../api/profile/${username}`).then(res => res.json()).then(res => {
fetch(`/api/profile/${username}`).then(res => res.json()).then(res => {
if (!res || res == "-1") {allowEsc = true; $('.postbutton').show(); return $('#message').text("The username you provided doesn't exist!")}
else accountID = res.accountID
@ -440,13 +439,13 @@ $('#submitComment').click(function() {
$('#postComment').hide()
$('.postbutton').show()
$('#message').html(messageText)
$('#timeSort').attr('src', "../assets/sort-time-on.png")
$('#topSort').attr('src', "../assets/sort-likes.png")
$('#timeSort').attr('src', "/assets/sort-time-on.png")
$('#topSort').attr('src', "/assets/sort-likes.png")
allowEsc = true
mode = "time"
page = 0
appendComments()
})
})
.fail(e => {allowEsc = true; $('.postbutton').show();$('#message').text(e.responseText.includes("DOCTYPE") ? "Something went wrong..." : e.responseText)})
})
})
@ -465,7 +464,7 @@ $('#dislikebtn').click(function() {
let commentID = 0
let lvID = 0
let likeCount, likeImg;
let likeCount, likeImg
let likedComments;
$(document).on('click', '.likeComment', function(cmnt) {
@ -473,7 +472,7 @@ $(document).on('click', '.likeComment', function(cmnt) {
commentID = $(this).attr('commentID')
likedComments = localStorage.likedComments ? JSON.parse(localStorage.likedComments) : []
if (likedComments.includes(commentID)) return;
if (likedComments.includes(commentID)) return
lvID = $(this).attr('levelID') || 0
likeImg = $(this).find('img')
@ -483,7 +482,7 @@ $(document).on('click', '.likeComment', function(cmnt) {
$('#submitVote').click(function() {
if (likedComments.includes(commentID)) return $('#likeMessage').text("You've already liked/disliked this comment!");
if (likedComments.includes(commentID)) return $('#likeMessage').text("You've already liked/disliked this comment!")
let ID = commentID
let username = $('#like-username').val()
@ -491,14 +490,14 @@ $('#submitVote').click(function() {
let extraID = lvID || window.location.pathname.split('/')[2]
let accountID = 0
let likeType = like ? "1" : "0"
if (!ID || !username || !password || loadingComments) return $('#postComment').hide()
$('#likeMessage').text(like ? "Liking..." : "Disliking... :(")
$('.postbutton').hide()
allowEsc = false
fetch(`../api/profile/${username}`).then(res => res.json()).then(res => {
fetch(`/api/profile/${username}`).then(res => res.json()).then(res => {
if (!res || res == "-1") {allowEsc = true; $('.postbutton').show(); return $('#likeMessage').text("The username you provided doesn't exist!")}
else accountID = res.accountID
@ -506,8 +505,8 @@ $('#submitVote').click(function() {
.done(x => {
let newCount = parseInt(likeCount.text()) + (like ? 1 : -1)
likeCount.text(newCount)
if (newCount < 0) likeImg.attr('src', '../assets/dislike.png').css('transform', compact ? 'translateY(15%)' : 'translateY(25%)')
else likeImg.attr('src', '../assets/like.png').removeAttr('style')
if (newCount < 0) likeImg.attr('src', '/assets/dislike.png').css('transform', compact ? 'translateY(15%)' : 'translateY(25%)')
else likeImg.attr('src', '/assets/like.png').removeAttr('style')
$('#likeComment').hide()
$('#likebtn').trigger('click')
$('.postbutton').show()
@ -515,7 +514,7 @@ $('#submitVote').click(function() {
allowEsc = true
likedComments.push(commentID)
localStorage.setItem('likedComments', JSON.stringify(likedComments))
})
})
.fail(e => {allowEsc = true; $('.postbutton').show();$('#likeMessage').text(e.responseText.includes("DOCTYPE") ? "Something went wrong..." : e.responseText)})
})
})
@ -534,12 +533,12 @@ $(document).keydown(function(k) {
if (k.which == 13) k.preventDefault() //enter
}
if (loadingComments || $('.popup').is(":visible")) return;
if (loadingComments || $('.popup').is(":visible")) return
if (k.which == 37 && $('#pageDown').is(":visible")) { //left
$('#pageDown').trigger('click')
}
if (k.which == 39 && $('#pageUp').is(":visible")) { //right
$('#pageUp').trigger('click')
}
@ -548,4 +547,3 @@ $(document).keydown(function(k) {
};
</script>

View file

@ -1,9 +1,9 @@
<head>
<title>Demon Leaderboard</title>
<meta charset="utf-8">
<link href="../assets/css/browser.css?v=1" type="text/css" rel="stylesheet">
<link href="/assets/css/browser.css?v=1" type="text/css" rel="stylesheet">
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-135255146-3"></script><script>window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);}gtag('js', new Date());gtag('config', 'UA-135255146-3');</script>
<link rel="icon" href="../assets/trophy-demon.png">
<link rel="icon" href="/assets/trophy-demon.png">
<meta id="meta-title" property="og:title" content="Demon Leaderboard">
<meta id="meta-desc" property="og:description" content="View the victors of a very hard Geometry Dash level!">
<meta id="meta-image" name="og:image" itemprop="image" content="https://gdbrowser.com/assets/trophy-demon.png">
@ -24,22 +24,22 @@
<p class="bigger center" style="margin-top: 1vh">
Usernames may <cy>differ</cy> from what is used in GD
</p>
<img src="../assets/ok.png" width=15%; class="gdButton center" onclick="$('.popup').hide()">
<img src="/assets/ok.png" width=15%; class="gdButton center" onclick="$('.popup').hide()">
</div>
</div>
<div style="position:absolute; bottom: 0%; left: 0%; width: 100%">
<img class="cornerPiece grayscale" src="../assets/corner.png" width=7%;>
<img class="cornerPiece grayscale" src="/assets/corner.png" width=7%;>
</div>
<div style="position:absolute; bottom: 0%; right: 0%; width: 100%; text-align: right;">
<img class="cornerPiece grayscale" src="../assets/corner.png" width=7%; style="transform: scaleX(-1)">
<img class="cornerPiece grayscale" src="/assets/corner.png" width=7%; style="transform: scaleX(-1)">
</div>
<div id="searchBox" class="supercenter dragscroll"; style="width: 124vh">
<div style="height: 4.5%"></div>
</div>
<div class="epicbox supercenter gs" style="width: 126vh; height: 80%; pointer-events: none"></div>
<div class="center" style="position:absolute; top: 8%; left: 0%; right: 0%">
@ -47,35 +47,35 @@
</div>
<div style="position: absolute; left: 7%; top: 45%; height: 10%;">
<a id="pageDown"><img class="gdButton" src="../assets/arrow-left.png" height="90%"></a>
<a id="pageDown"><img class="gdButton" src="/assets/arrow-left.png" height="90%"></a>
</div>
<div style="position: absolute; right: 7%; top: 45%; height: 10%;">
<a id="pageUp"><img class="gdButton" src="../assets/arrow-right.png" height="90%"></a>
<a id="pageUp"><img class="gdButton" src="/assets/arrow-right.png" height="90%"></a>
</div>
<div style="position:absolute; top: 2%; left: 1.5%; width: 10%; height: 25%; pointer-events: none">
<img class="gdButton yesClick" id="backButton" src="../assets/back.png" height="30%" onclick="backButton()">
<img class="gdButton yesClick" id="backButton" src="/assets/back.png" height="30%" onclick="backButton()">
</div>
<div class="supercenter" id="loading" style="height: 10%; top: 47%">
<img class="spin noSelect" src="../assets/loading.png" height="105%">
<img class="spin noSelect" src="/assets/loading.png" height="105%">
</div>
<div style="position:absolute; top: 3%; right: 2%; text-align: right; width: 20%;">
<a target="_blank" id="pointercrate"><img class="inline gdButton" src="../assets/demonButton.png" width="23%" style="margin-right: 4%"></a>
<img id="creditsButton" class="inline gdButton" src="../assets/credits.png" width="25%" onclick="$('#infoDiv').show()">
<a target="_blank" id="pointercrate"><img class="inline gdButton" src="/assets/demonButton.png" width="23%" style="margin-right: 4%"></a>
<img id="creditsButton" class="inline gdButton" src="/assets/credits.png" width="25%" onclick="$('#infoDiv').show()">
</div>
<div style="position:absolute; bottom: 5.5%; right: 3.5%; text-align: right; width: 18%;">
<a id="realLeaderboardLink"><img class="gdButton" src="../assets/leaderboard.png" width="35%"></a>
<a id="realLeaderboardLink"><img class="gdButton" src="/assets/leaderboard.png" width="35%"></a>
</div>
</div>
</body>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script type="text/javascript" src="../global.js?v=1"></script>
<script type="text/javascript" src="/misc/global.js?v=1"></script>
<script type="text/javascript" src="../dragscroll.js"></script>
<script>
@ -83,7 +83,7 @@ let max = 250
let trophies = [1, 5, 10, 25, 50, 100, max]
let demonID = Math.round(window.location.pathname.split('/')[2])
let illegal = (!demonID || demonID > max || demonID < 1)
let illegal = (!demonID || demonID > max || demonID < 1)
if (demonID > 1) $('#pageDown').attr('href', `./${demonID - 1}`)
else $('#pageDown').hide()
@ -91,8 +91,8 @@ else $('#pageDown').hide()
if (demonID < max) $('#pageUp').attr('href', `./${demonID + 1}`)
else $('#pageUp').hide()
Fetch(`../api/gdps?current=1`).then(server => {
if (illegal || !server.demonList) return $('#loading').hide();
Fetch(`/api/gdps?current=1`).then(server => {
if (illegal || !server.demonList) return $('#loading').hide()
fetch(`${server.demonList}api/v2/demons/listed?after=${demonID-1}&before=${demonID+1}`).then(res => res.json()).then(rawDemon => {
@ -116,16 +116,16 @@ fetch(`${server.demonList}api/v2/demons/listed?after=${demonID-1}&before=${demon
if (x.video && x.video.includes("youtube.com")) videoIcon = "youtube"
else if (x.video && x.video.includes("twitch.tv")) videoIcon = "twitch"
$('#searchBox').append(`<div class="searchresult leaderboardSlot" style="align-items: center; padding-left: 1vh; height: 15%; width: 100%; position: relative;">
$('#searchBox').append(`<div class="searchresult leaderboardSlot" style="align-items: center; padding-left: 1vh; height: 15%; width: 100%; position: relative;">
<h2 class="center" style="width: 12%; margin: 0% 0% 0% 0.5%; transform: scale(${1 - (Math.max(0, String(y+1).length - 1) * 0.2)}">${y+1}</h2>
<img class="inline spaced" src="../assets/trophies/${trophies.findIndex(z => y+1 <= z) + 1}.png" style="margin-bottom: 0%; height: 80%;">
<img class="inline spaced" src="/assets/trophies/${trophies.findIndex(z => y+1 <= z) + 1}.png" style="margin-bottom: 0%; height: 80%;">
<h2 class="small gdButton" style="font-size: 6.5vh; margin-right: 3%; margin-left: 3%"><a href="../u/${x.player.name}">${x.player.name}</a></h2>
<h3 class="lessSpaced" style="font-size: 4vh; margin-top: 1.3%; margin-right: 2%">${x.progress}%</h3>
<div style="${!x.video ? "display: none; " : ""}position:absolute; margin-top: 1.5%; width: 12.5%; height: 90%; right: 0%;">
<a target="_blank" href="${x.video}">
<img class="gdButton inline spaced" src="../assets/${videoIcon}.png" height="80%">
<img class="gdButton inline spaced" src="/assets/${videoIcon}.png" height="80%">
</a>
</div>

View file

@ -1,9 +1,9 @@
<head>
<title>Level Search</title>
<meta charset="utf-8">
<link href="../assets/css/browser.css?v=1" type="text/css" rel="stylesheet">
<link href="/assets/css/browser.css?v=1" type="text/css" rel="stylesheet">
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-135255146-3"></script><script>window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);}gtag('js', new Date());gtag('config', 'UA-135255146-3');</script>
<link rel="icon" href="../assets/coin.png">
<link rel="icon" href="/assets/coin.png">
<meta id="meta-title" property="og:title" content="Level Search">
<meta id="meta-desc" property="og:description" content="Search for Geometry Dash levels, and filter by length, difficulty, song + more!">
<meta id="meta-image" name="og:image" itemprop="image" content="https://gdbrowser.com/assets/coin.png">
@ -16,7 +16,7 @@
<div id="filters" class="popup">
<div id="filterStuff" class="brownBox bounce center supercenter" style="width: 101vh; height: 50%; padding-top: 0.3%; padding-bottom: 3.5%; padding-left: 1%">
<img class="gdButton" src="../assets/close.png" width="9%" style="position: absolute; top: -8.5%; left: -5.5vh" onclick="$('#filters').hide()">
<img class="gdButton" src="/assets/close.png" width="9%" style="position: absolute; top: -8.5%; left: -5.5vh" onclick="$('#filters').hide()">
<h1 style="margin-bottom: 4%">Advanced Options</h1><br>
<div><h1><input type="checkbox" id="box-featured" url="&featured"><label for="box-featured" class="gdcheckbox gdButton"></label>Featured</h1></div>
<div><h1><input type="checkbox" id="box-epic" url="&epic"><label for="box-epic" class="gdcheckbox gdButton"></label>Epic</h1></div>
@ -25,21 +25,21 @@
<div><h1><input type="checkbox" id="box-2player" url="&twoPlayer"><label for="box-2player" class="gdcheckbox gdButton"></label>2-Player</h1></div>
<div style="margin-bottom: 5%"><h1><input type="checkbox" id="box-coins" url="&coins"><label for="box-coins" class="gdcheckbox gdButton"></label>Coins</h1></div>
<h1 class="smallerer lessSpaced">Song</h1>
<img id="normalSong" class="gdButton inline gray" style="margin-right: 0.5%" src="../assets/song-normal.png" height="8%">
<img id="customSong" class="gdButton inline" style="margin-left: 0.5%" src="../assets/song-custom.png" height="8%">
<img id="normalSong" class="gdButton inline gray" style="margin-right: 0.5%" src="/assets/song-normal.png" height="8%">
<img id="customSong" class="gdButton inline" style="margin-left: 0.5%" src="/assets/song-custom.png" height="8%">
<br>
<input id="songID" type="number" placeholder="Custom Song ID" style="height: 15%; width: 70%; text-align: center; margin-top: 2%">
<div id="songSelect" style="width: 100%; display: none; margin-top: 3%; text-align: center">
<img style="width: 4%" id="songDown" class="gdButton inline valign songChange" jump="-1" src="../assets/whitearrow-left.png">
<img style="width: 4%" id="songDown" class="gdButton inline valign songChange" jump="-1" src="/assets/whitearrow-left.png">
<h1 class="inline valign smallerer center" id="songName" style="min-width: 60%"></h1>
<img style="width: 4%" id="songUp" class="gdButton inline valign songChange" jump="1" id="nextSong" src="../assets/whitearrow-right.png">
<img style="width: 4%" id="songUp" class="gdButton inline valign songChange" jump="1" id="nextSong" src="/assets/whitearrow-right.png">
</div>
</div>
</div>
<div id="customlist" class="popup">
<div class="brownBox bounce center supercenter" style="width: 100vh; height: 56%; padding-top: 0.3%; padding-bottom: 3.5%; padding-left: 1%">
<img class="gdButton" src="../assets/close.png" width="9%" style="position: absolute; top: -8.5%; left: -5.5vh" onclick="$('#customlist').hide()">
<img class="gdButton" src="/assets/close.png" width="9%" style="position: absolute; top: -8.5%; left: -5.5vh" onclick="$('#customlist').hide()">
<h1>Custom List</h1><br>
<p id="listInfo" style="margin-top: 1%; margin-bottom: 1.5%">Paste a <cy>comma-separated</cy> list of <ca>Level IDs</ca> to create a custom list!</p>
<textarea id="listLevels" placeholder="18025697, 23189196, 27786218, 27728679, 25706351" style="margin-bottom: 2%; font-size: 2.5vh"></textarea>
@ -53,24 +53,24 @@
<input id="pageSize" type="number" value="10" style="font-size: 4vh; height: 13%; width: 45%; text-align: center;">
</div>
<a id="listLink"><img src="../assets/btn-submit.png" type="submit" height=12.5%; class="disabled gdButton" style="margin-left: 1%" id="createList"></a>
<a id="listLink"><img src="/assets/btn-submit.png" type="submit" height=12.5%; class="disabled gdButton" style="margin-left: 1%" id="createList"></a>
</div>
</div>
<div style="position:absolute; bottom: 0%; left: 0%; width: 100%">
<img class="cornerPiece" src="../assets/corner.png" width=7%;>
<img class="cornerPiece" src="/assets/corner.png" width=7%;>
</div>
<div style="position:absolute; bottom: 0%; right: 0%; width: 100%; text-align: right;">
<img class="cornerPiece" src="../assets/corner.png" width=7%; style="transform: scaleX(-1); pointer-events: none">
<img class="cornerPiece" src="/assets/corner.png" width=7%; style="transform: scaleX(-1); pointer-events: none">
</div>
<div class="transparentBox center" style="width: 115vh; height: 9%; margin: 1.5% auto 1% auto; padding-bottom: 0.2%">
<div>
<input type="text" id="levelName" placeholder="Enter a level, user, or ID" maxlength=20>
<img search="0" src="../assets/search.png" id="searchBtn" width="20%" class="valign gdButton levelSearch" style="margin-left: 1%; margin-bottom: 2.2%">
<img id="userSearch" src="../assets/search-user.png" width="9.6%" class="valign gdButton" style="margin-left: 1%; margin-bottom: 2.2%">
<img search="0" src="/assets/search.png" id="searchBtn" width="20%" class="valign gdButton levelSearch" style="margin-left: 1%; margin-bottom: 2.2%">
<img id="userSearch" src="/assets/search-user.png" width="9.6%" class="valign gdButton" style="margin-left: 1%; margin-bottom: 2.2%">
</div>
</div>
@ -79,17 +79,17 @@
</div>
<div class="transparentBox center" style="width: 115vh; height: 35%; margin: 0.5% auto 1% auto; padding-top: 1.1%">
<img src="../assets/btn-top.png" height="27%" class="valign gdButton spaced levelSearch" search="mostdownloaded">
<img src="/assets/btn-top.png" height="27%" class="valign gdButton spaced levelSearch" search="mostdownloaded">
<span style="margin-right: 3%"></span>
<img src="../assets/btn-liked.png" height="27%" class="valign gdButton spaced levelSearch" search="mostliked">
<img src="/assets/btn-liked.png" height="27%" class="valign gdButton spaced levelSearch" search="mostliked">
<br>
<img src="../assets/btn-trending.png" height="27%" class="valign gdButton spaced levelSearch" search="trending">
<img src="../assets/btn-recent.png" height="27%" class="valign gdButton spaced levelSearch" search="recent" style="margin-left: 2%; margin-right: 2%">
<img src="../assets/btn-magic.png" height="27%" class="valign gdButton spaced levelSearch" search="magic">
<img src="/assets/btn-trending.png" height="27%" class="valign gdButton spaced levelSearch" search="trending">
<img src="/assets/btn-recent.png" height="27%" class="valign gdButton spaced levelSearch" search="recent" style="margin-left: 2%; margin-right: 2%">
<img src="/assets/btn-magic.png" height="27%" class="valign gdButton spaced levelSearch" search="magic">
<br>
<img src="../assets/btn-awarded.png" height="27%" class="valign gdButton levelSearch" search="awarded">
<img src="../assets/btn-featured.png" height="27%" class="valign gdButton levelSearch" search="featured" style="margin: 0% 2%">
<img src="../assets/btn-followed.png" height="27%" id="followedSearch" class="valign gdButton levelSearch" search="followed">
<img src="/assets/btn-awarded.png" height="27%" class="valign gdButton levelSearch" search="awarded">
<img src="/assets/btn-featured.png" height="27%" class="valign gdButton levelSearch" search="featured" style="margin: 0% 2%">
<img src="/assets/btn-followed.png" height="27%" id="followedSearch" class="valign gdButton levelSearch" search="followed">
</div>
<div class="center">
@ -97,56 +97,56 @@
</div>
<div id="difficulties" class="transparentBox center" style="width: 115vh; height: 12%; margin: 0.5% auto 1% auto; padding-top: 1%; padding-bottom: 1%;">
<div class="diffDiv gdButton" diff="-1"><img src="../assets/difficulties/unrated.png"><h3 class="mini">N/A</h3></div>
<div class="diffDiv gdButton" diff=1><img src="../assets/difficulties/easy.png"><h3 class="mini">Easy</h3></div>
<div class="diffDiv gdButton" diff=2><img src="../assets/difficulties/normal.png"><h3 class="mini">Normal</h3></div>
<div class="diffDiv gdButton" diff=3><img src="../assets/difficulties/hard.png"><h3 class="mini">Hard</h3></div>
<div class="diffDiv gdButton" diff=4><img src="../assets/difficulties/harder.png"><h3 class="mini">Harder</h3></div>
<div class="diffDiv gdButton" diff=5><img src="../assets/difficulties/insane.png"><h3 class="mini">Insane</h3></div>
<div class="diffDiv gdButton" diff="-1"><img src="/assets/difficulties/unrated.png"><h3 class="mini">N/A</h3></div>
<div class="diffDiv gdButton" diff=1><img src="/assets/difficulties/easy.png"><h3 class="mini">Easy</h3></div>
<div class="diffDiv gdButton" diff=2><img src="/assets/difficulties/normal.png"><h3 class="mini">Normal</h3></div>
<div class="diffDiv gdButton" diff=3><img src="/assets/difficulties/hard.png"><h3 class="mini">Hard</h3></div>
<div class="diffDiv gdButton" diff=4><img src="/assets/difficulties/harder.png"><h3 class="mini">Harder</h3></div>
<div class="diffDiv gdButton" diff=5><img src="/assets/difficulties/insane.png"><h3 class="mini">Insane</h3></div>
<div class="diffDiv gdButton" id="demonBtn" diff=-2><img src="../assets/difficulties/demon.png" style="width: 85%"><h3 class="mini">Demon</h3></div>
<div class="diffDiv gdButton" id="demonBtn" diff=-2><img src="/assets/difficulties/demon.png" style="width: 85%"><h3 class="mini">Demon</h3></div>
<!-- <div class="diffDiv gdButton" style="filter: brightness(100%)" id="demonBtn" diff=-2><img class="darkDiff" src="../assets/difficulties/demon.png" style="width: 85%"><h3 class="darkDiff mini">Demon</h3> -->
<!-- <img src="../assets/exclamation.png" style="position: absolute; width: 19%; left: 86%; bottom: 68%"></div> -->
<!-- <div class="diffDiv gdButton" style="filter: brightness(100%)" id="demonBtn" diff=-2><img class="darkDiff" src="/assets/difficulties/demon.png" style="width: 85%"><h3 class="darkDiff mini">Demon</h3> -->
<!-- <img src="/assets/exclamation.png" style="position: absolute; width: 19%; left: 86%; bottom: 68%"></div> -->
<div class="diffDiv gdButton" diff=-3><img src="../assets/difficulties/auto.png"><h3 class="mini">Auto</h3></div>
<div class="diffDiv gdButton" diff=-3><img src="/assets/difficulties/auto.png"><h3 class="mini">Auto</h3></div>
</div>
<div id="demons" class="transparentBox" style="display: none; width: 115vh; height: 12%; margin: 0.5% auto 1% auto; padding-top: 0.6%; padding-bottom: 1.4%;">
<div class="diffDiv gdButton demonDiff" diff=1 style="margin-left: 3.5%"><img src="../assets/difficulties/demon-easy.png" style="width: 90%"><h3 class="mini center">Easy</h3></div>
<div class="diffDiv gdButton demonDiff" diff=2><img src="../assets/difficulties/demon-medium.png" style="width: 90%"><h3 class="mini center smallTextWoo">Medium</h3></div>
<div class="diffDiv gdButton demonDiff" diff=3><img src="../assets/difficulties/demon-hard.png" style="width: 90%"><h3 class="mini center">Hard</h3></div>
<div class="diffDiv gdButton demonDiff" diff=4><img src="../assets/difficulties/demon-insane.png" style="width: 95%"><h3 class="mini center smallTextWoo">Insane</h3></div>
<div class="diffDiv gdButton demonDiff" diff=5><img src="../assets/difficulties/demon-extreme.png" style="width: 100%"><h3 class="mini center smallTextWoo">Extreme</h3></div>
<div class="diffDiv gdButton goBack" diff=-2 style="margin-left: 2.3%; filter: none"><img src="../assets/difficulties/demon.png" style="width: 90%"><h3 class="mini">Demon</h3></div>
<a id="demonList" style="display: none" href="./search/*?type=demonlist"><div class="gdButton diffDiv" style="filter: none"><img src="../assets/trophy2.png" style="width: 95%"><h3 class="yellow mini center">List</h3></div></a>
<div class="diffDiv gdButton demonDiff" diff=1 style="margin-left: 3.5%"><img src="/assets/difficulties/demon-easy.png" style="width: 90%"><h3 class="mini center">Easy</h3></div>
<div class="diffDiv gdButton demonDiff" diff=2><img src="/assets/difficulties/demon-medium.png" style="width: 90%"><h3 class="mini center smallTextWoo">Medium</h3></div>
<div class="diffDiv gdButton demonDiff" diff=3><img src="/assets/difficulties/demon-hard.png" style="width: 90%"><h3 class="mini center">Hard</h3></div>
<div class="diffDiv gdButton demonDiff" diff=4><img src="/assets/difficulties/demon-insane.png" style="width: 95%"><h3 class="mini center smallTextWoo">Insane</h3></div>
<div class="diffDiv gdButton demonDiff" diff=5><img src="/assets/difficulties/demon-extreme.png" style="width: 100%"><h3 class="mini center smallTextWoo">Extreme</h3></div>
<div class="diffDiv gdButton goBack" diff=-2 style="margin-left: 2.3%; filter: none"><img src="/assets/difficulties/demon.png" style="width: 90%"><h3 class="mini">Demon</h3></div>
<a id="demonList" style="display: none" href="./search/*?type=demonlist"><div class="gdButton diffDiv" style="filter: none"><img src="/assets/trophy2.png" style="width: 95%"><h3 class="yellow mini center">List</h3></div></a>
</div>
<div class="transparentBox center" style="width: 115vh; height: 6%; margin: 0.5% auto 1% auto; padding-top: 1%; padding-bottom: 0.5%;">
<!-- <div class="lengthDiv" style="pointer-events: none" len=0><h1 class="gdButton smaller" style="pointer-events: none"><img src="../assets/time.png" height="90%" style="pointer-events: none"></h1></div> -->
<!-- <div class="lengthDiv" style="pointer-events: none" len=0><h1 class="gdButton smaller" style="pointer-events: none"><img src="/assets/time.png" height="90%" style="pointer-events: none"></h1></div> -->
<div class="lengthDiv" len=0><h1 class="gdButton smaller">Tiny</h1></div>
<div class="lengthDiv" len=1><h1 class="gdButton smaller">Short</h1></div>
<div class="lengthDiv" len=2><h1 class="gdButton smaller">Medium</h1></div>
<div class="lengthDiv" len=3><h1 class="gdButton smaller">Long</h1></div>
<div class="lengthDiv" len=4><h1 class="gdButton smaller">XL</h1></div>
<div class="lengthDiv" id="starCheck"><img src="../assets/star.png" class="gdButton" height="90%"></div>
<div class="lengthDiv" id="starCheck"><img src="/assets/star.png" class="gdButton" height="90%"></div>
</div>
<div style="position:absolute; top: 2%; left: 1.5%; width: 10%; height: 25%; pointer-events: none">
<img class="gdButton yesClick" id="backButton" src="../assets/back.png" height="30%" onclick="backButton()">
<img class="gdButton yesClick" id="backButton" src="/assets/back.png" height="30%" onclick="backButton()">
</div>
<div style="position:absolute; top: 2%; right: 1.5%; width: 10%; text-align: right">
<img class="gdButton" style="margin-bottom: 12%" src="../assets/close.png" width="60%" onclick="clearFilters()">
<img class="gdButton" style="margin-bottom: 12%" id="showFilters" src="../assets/plus.png" width="60%" onclick="$('#filters').show()">
<img class="gdButton" src="../assets/listbutton.png" width="60%" onclick="$('#customlist').show()">
<img class="gdButton" style="margin-bottom: 12%" src="/assets/close.png" width="60%" onclick="clearFilters()">
<img class="gdButton" style="margin-bottom: 12%" id="showFilters" src="/assets/plus.png" width="60%" onclick="$('#filters').show()">
<img class="gdButton" src="/assets/listbutton.png" width="60%" onclick="$('#customlist').show()">
</div>
</div>
</body>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script type="text/javascript" src="../global.js?v=1"></script>
<script type="text/javascript" src="/misc/global.js?v=1"></script>
<script>
let filters = []
@ -174,7 +174,7 @@ $('.levelSearch').click(function() {
let difficulties = []
$('.diffDiv').each(function() {if ($(this).hasClass('selectedFilter')) difficulties.push($(this).attr('diff'))})
demonFilter = demonMode && difficulties[0] > 0
if (!difficulties.length) url += ""
else if (!demonFilter) url += "&diff=" + undupe(difficulties).join(",")
else url += "&diff=-2&demonFilter=" + difficulties[0]
@ -212,15 +212,15 @@ function getDiffFilters() {
}
function showDemonDiffs() {
$('#difficulties').hide();
$('#demons').show();
demonMode = true;
$('#difficulties').hide()
$('#demons').show()
demonMode = true
}
function hideDemonDiffs() {
$('#difficulties').show();
$('#demons').hide();
demonMode = false;
$('#difficulties').show()
$('#demons').hide()
demonMode = false
}
$('.diffDiv').click(function() {
@ -262,16 +262,16 @@ $('.lengthDiv').click(function() {
$(document).keydown(function(k) {
let searchFocus = $(':focus-visible').length == 1 && $(':focus-visible').first().attr('id') == "levelName"
if ((!$(':focus-visible').length || searchFocus) && k.which == 13) { // enter
if (!$('#customlist').is(':hidden')) k.preventDefault();
if (!$('#customlist').is(':hidden')) k.preventDefault()
else if ($('#filters').is(':hidden')) $('#searchBtn').trigger('click')
}
}
});
$('#pageSize').on('input blur', function (event) {
var x = +$(this).val(); var max = 250; var min = 1
if (event.type == "input") { if (x > max || x < min) $(this).addClass('red'); else $(this).removeClass('red')}
else {
$(this).val(Math.max(Math.min(Math.floor(x), max), min));
$(this).val(clamp(Math.floor(x), min, max))
$(this).removeClass('red')
}
$('#listLevels').trigger('input')
@ -279,7 +279,7 @@ $('#pageSize').on('input blur', function (event) {
let listMsg = $('#listInfo').html()
$('#listLevels, #listName').on('input blur', function (event) {
let levels = $('#listLevels').val().replace(/\n| /g, ",").split(",").map(x => x.replace(/[^0-9]/g, "")).filter(x => +x > 0 && +x < 100000000000)
let levels = $('#listLevels').val().replace(/\n| /g, ",").split(",").map(x => x.replace(/\D/g, "")).filter(x => +x > 0 && +x < 10**11)
levels = undupe(levels)
if (levels.length > 1 && levels.length <= 100) {
@ -376,11 +376,11 @@ else if (+savedFilters.song && +savedFilters.song > 0) $('#songID').val(savedFil
checkExtraFilters()
Fetch(`../api/music`).then(music => {
Fetch(`/api/music`).then(music => {
$('#songName').html("1: " + music[1][0])
$(document).on('click', '.songChange', function () {
$(document).on('click', '.songChange', function () {
officialSong += Number($(this).attr('jump'))
if (officialSong < 1) officialSong = 1
$('#songName').html(`${officialSong}: ${music[officialSong] ? music[officialSong][0] : officialSong == 69 ? "Nice" : "Unknown"}`)
@ -395,10 +395,10 @@ Fetch(`../api/music`).then(music => {
}
$(document).keydown(function(k) {
if (customSong) return;
if (customSong) return
if (k.which == 37) $('#songDown').trigger('click') // left
if (k.which == 39) $('#songUp').trigger('click') // right
});
})
if (onePointNine) {
$('#userSearch').hide()
@ -406,7 +406,7 @@ Fetch(`../api/music`).then(music => {
$('#levelName').css('width', '76%')
}
if (gdps) Fetch(`../api/gdps?current=1`).then(res => { if (res.demonList) $('#demonList').show() })
if (gdps) Fetch(`/api/gdps?current=1`).then(res => { if (res.demonList) $('#demonList').show() })
else $('#demonList').show()
})

View file

@ -1,9 +1,9 @@
<head>
<title>Gauntlets</title>
<meta charset="utf-8">
<link href="../assets/css/browser.css?v=1" type="text/css" rel="stylesheet">
<link href="/assets/css/browser.css?v=1" type="text/css" rel="stylesheet">
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-135255146-3"></script><script>window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);}gtag('js', new Date());gtag('config', 'UA-135255146-3');</script>
<link rel="icon" href="../assets/gauntlet.png">
<link rel="icon" href="/assets/gauntlet.png">
<meta id="meta-title" property="og:title" content="Gauntlets">
<meta id="meta-desc" property="og:description" content="Because RobTop wanted to leave behind the monstrosity that is Map Packs.">
<meta id="meta-image" name="og:image" itemprop="image" content="https://gdbrowser.com/assets/gauntlet.png">
@ -15,34 +15,34 @@
<div id="everything" class="center" style="width: 100%; height: 100%;">
<div style="position:absolute; top: 2%; left: -1.95%; width: 10%; height: 25%; pointer-events: none">
<img class="gdButton yesClick" id="backButton" src="../assets/back.png" height="30%" onclick="backButton()">
<img class="gdButton yesClick" id="backButton" src="/assets/back.png" height="30%" onclick="backButton()">
</div>
<div class="center" width="100%" style="margin-top: 2.5%; margin-bottom: 1%;">
<img src="../assets/gauntlets.png" width="50%">
<img src="/assets/gauntlets.png" width="50%">
</div>
<img id="loading" style="margin-top: 1%" class="spin noSelect" src="../assets/loading.png" height="12%">
<img id="loading" style="margin-top: 1%" class="spin noSelect" src="/assets/loading.png" height="12%">
<div id="gauntletList">
<br>
</div>
<br>
</div>
</body>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script type="text/javascript" src="../global.js?v=1"></script>
<script type="text/javascript" src="/misc/global.js?v=1"></script>
<script>
fetch('../api/gauntlets').then(res => res.json()).then(gauntlets => {
fetch('/api/gauntlets').then(res => res.json()).then(gauntlets => {
$('#loading').hide()
gauntlets.forEach((x, y) => {
$('#gauntletList').append(`
<div class="gauntlet">
<a href="../search/*?gauntlet=${x.id}">
<img src="../assets/gauntlets/${x.name.toLowerCase()}.png" height="300%"><br>
<img src="/assets/gauntlets/${x.name.toLowerCase()}.png" height="300%"><br>
<h3 class="gauntletText"">${x.name}<br>Gauntlet</h3></div></a>`)
})
});

View file

@ -1,9 +1,9 @@
<head>
<title>GD Multiverse Navigation Terminal</title>
<meta charset="utf-8">
<link href="../assets/css/browser.css?v=1" type="text/css" rel="stylesheet">
<link href="/assets/css/browser.css?v=1" type="text/css" rel="stylesheet">
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-135255146-3"></script><script>window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);}gtag('js', new Date());gtag('config', 'UA-135255146-3');</script>
<link rel="icon" href="../assets/unlock.png">
<link rel="icon" href="/assets/unlock.png">
<meta id="meta-title" property="og:title" content="GD Multiverse Navigation Terminal">
<meta id="meta-desc" property="og:description" content="That's uhh... that's just fancy talk for GDPS Browser. Select a popular GD private server and view its levels, creators, packs, leaderboards, and more!">
<meta id="meta-image" name="og:image" itemprop="image" content="https://gdbrowser.com/assets/unlock.png">
@ -26,43 +26,43 @@
Please note that I only add <cg>relatively large</cg> servers to the list.
Servers which are <cr>inactive</cr> or have <cr>few levels/members</cr> will not be accepted.
</p>
<img src="../assets/ok.png" width=15%; class="gdButton center" onclick="$('.popup').hide()">
<img src="/assets/ok.png" width=15%; class="gdButton center" onclick="$('.popup').hide()">
</div>
</div>
<div id="searchBox" class="supercenter dragscroll"; style="width: 127vh"></div>
<div class="epicbox supercenter gs" style="width: 126vh; height: 80%; pointer-events: none"></div>
<div class="center" style="position:absolute; top: 8%; left: 0%; right: 0%">
<h1 class="pre" id="header">GD Private Servers</h1>
</div>
</div>
<div style="position:absolute; top: 2%; left: 1.5%; width: 10%; height: 25%; pointer-events: none">
<img class="gdButton yesClick" id="backButton" src="../assets/back.png" height="30%" onclick="backButton()">
<img class="gdButton yesClick" id="backButton" src="/assets/back.png" height="30%" onclick="backButton()">
</div>
<div class="supercenter" id="loading" style="height: 10%; top: 47%">
<img class="spin noSelect" src="../assets/loading.png" height="105%">
<img class="spin noSelect" src="/assets/loading.png" height="105%">
</div>
<div style="position: absolute; left: 7%; top: 45%; height: 10%;">
<img class="gdButton" id="pageDown" src="../assets/arrow-left.png" style="display: none"; height="90%">
<img class="gdButton" id="pageDown" src="/assets/arrow-left.png" style="display: none"; height="90%">
</div>
<div style="position: absolute; right: 7%; top: 45%; height: 10%;">
<img class="gdButton" id="pageUp" src="../assets/arrow-right.png" style="display: none"; height="90%">
<img class="gdButton" id="pageUp" src="/assets/arrow-right.png" style="display: none"; height="90%">
</div>
<div style="position:absolute; top: 3%; right: 2%; text-align: right; width: 20%;">
<img id="plusButton" class="inline gdButton" src="../assets/plus.png" width="25%" onclick="$('#infoDiv').show()">
<img id="plusButton" class="inline gdButton" src="/assets/plus.png" width="25%" onclick="$('#infoDiv').show()">
</div>
</div>
</body>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script type="text/javascript" src="../global.js?v=1"></script>
<script type="text/javascript" src="/misc/global.js?v=1"></script>
<script type="text/javascript" src="../dragscroll.js"></script>
<script>
@ -71,7 +71,7 @@ let page = 1
let localhost = window.location.hostname == "localhost"
let host = window.location.host.split(".").slice(-2).join(".")
Fetch('../api/gdps').then(servers => {
Fetch('/api/gdps').then(servers => {
let currentServer = servers.find(x => x.id == gdps)
servers = [currentServer].concat(servers.filter(x => x.id != gdps)).filter(x => x)
@ -89,15 +89,15 @@ Fetch('../api/gdps').then(servers => {
serverPage.forEach(x => {
$('#searchBox').append(`<div class="searchresult" style="height: 19%; padding-top: 1.2%">
<h1 class="lessspaced blue" style="color: ${(gdps || "") == x.id ? "#00DDFF" : "white"}">${x.name}</h1>
<h1 class="lessspaced blue" style="color: ${(gdps || "") == x.id ? "#0DF" : "white"}">${x.name}</h1>
<h2 class="lessSpaced smaller inline gdButton"><a href="${x.authorLink}" target="_blank">By ${x.author}</a></h2>
<div class="center" style="position:absolute; height: 10%; width: 12.5%; left: 3%; transform:translateY(-160%)">
<a href="${x.link}" target="_blank"><img class="gdButton spaced gdpslogo" src="../assets/gdps/${x.id || "gd"}_icon.png" height="130%"></a>
<a href="${x.link}" target="_blank"><img class="gdButton spaced gdpslogo" src="/assets/gdps/${x.id || "gd"}_icon.png" height="130%"></a>
</div>
<div class="center" style="position:absolute; right: 7%; transform:translateY(-150%); height: 10%">
<a href="http://${x.id || ""}${x.id && localhost ? ".x" : ""}${x.id ? "." : ""}${host}"><img style="margin-bottom: 4.5%" class="valign gdButton" src="../assets/view.png" height="105%"></a>
<a href="http://${x.id || ""}${x.id && localhost ? ".x" : ""}${x.id ? "." : ""}${host}"><img style="margin-bottom: 4.5%" class="valign gdButton" src="/assets/view.png" height="105%"></a>
</div>
</div>`)
})

View file

@ -1,9 +1,9 @@
<head>
<title>Geometry Dash Browser!</title>
<meta charset="utf-8">
<link href="../assets/css/browser.css?v=1" type="text/css" rel="stylesheet">
<link href="/assets/css/browser.css?v=1" type="text/css" rel="stylesheet">
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-135255146-3"></script><script>window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);}gtag('js', new Date());gtag('config', 'UA-135255146-3');</script>
<link rel="icon" href="../assets/coin.png">
<link rel="icon" href="/assets/coin.png">
<meta id="meta-title" property="og:title" content="Geometry Dash Browser!">
<meta id="meta-desc" property="og:description" content="Browse all of Geometry Dash's online features, right from this handy little website! Levels, profiles, leaderboards, comments, and more!">
<meta id="meta-image" name="og:image" content="https://gdbrowser.com/assets/coin.png" itemprop="image">
@ -18,76 +18,76 @@
</div>
<div style="position:absolute; bottom: 0%; left: 0%; width: 100%; pointer-events: none">
<img class="cornerPiece" src="../assets/corner.png" width=7%;>
<img class="cornerPiece" src="/assets/corner.png" width=7%;>
</div>
<div style="position:absolute; top: 0%; left: 0%; width: 100%; pointer-events: none">
<img class="cornerPiece" src="../assets/corner.png" width=7%; style="transform: scaleY(-1)">
<img class="cornerPiece" src="/assets/corner.png" width=7%; style="transform: scaleY(-1)">
</div>
<div style="position:absolute; top: 1.7%; right: 2%; text-align: right; width: 10%;">
<img id="creditsButton" class="gdButton" src="../assets/credits.png" width="60%" onclick="loadCredits()">
<img id="creditsButton" class="gdButton" src="/assets/credits.png" width="60%" onclick="loadCredits()">
</div>
<div class="menu-achievements" style="position:absolute; top: 5.5%; left: 3%; width: 12%;">
<a href="../achievements"><img class="gdButton" src="../assets/achievements.png" width="40%"></a>
<a href="../achievements"><img class="gdButton" src="/assets/achievements.png" width="40%"></a>
</div>
<div class="menu-messages" style="position:absolute; top: -1.7%; left: 11%; text-align: left; width: 10%;">
<a href="../messages"><img class="iconRope" src="../assets/messagerope.png" width="40%"></a>
<a href="../messages"><img class="iconRope" src="/assets/messagerope.png" width="40%"></a>
</div>
<div style="position:absolute; top: -1.5%; right: 10%; text-align: right; width: 10%;">
<a href="../iconkit"><img class="iconRope" src="../assets/iconrope.png" width="40%"></a>
<a href="../iconkit"><img class="iconRope" src="/assets/iconrope.png" width="40%"></a>
</div>
<div id="dl" style="display: none; position:absolute; top: 15%; right: 0.5%; text-align: center; width: 17%">
<h1 class="smaller" style="margin-bottom: 1%">Note</h1>
<p style="font-size: 2.2vh; margin-top: 1.2%"><ca>Level downloading</ca> has been <cr>blocked</cr> by RobTop.
<cy>Level analysis, daily levels, and downloading extra info</cy> will <cg>not work</cg> until he chooses to unblock downloads.
These features still work <ca><a class="underline" target="_blank" href="https://github.com/GDColon/GDBrowser">locally</a></ca> and on <ca><a class="underline" href="../gdps">private servers</a></ca></p>
</div>
<div class="supercenter center" id="menuButtons" style="bottom: 5%;">
<table>
<tr class="menuButtonList">
<td><a tabindex="1" href="./search/*?type=saved"><img class="menubutton menu-saved" src="../assets/category-saved.png" title="Saved Levels"></a></td>
<td><a tabindex="1" href="./daily"><img class="menubutton menu-daily" src="../assets/category-daily.png" title="Daily Level"></a></td>
<td style="display: block" id="menu_weekly"><a tabindex="1" href="./weekly"><img class="menubutton menu-weekly" src="../assets/category-weekly.png" title="Weekly Demon"></a></td>
<td style="display: none" id="menu_featured"><a tabindex="1" href="./search/*?type=featured"><img class="menubutton menu-featured" src="../assets/category-featured.png" title="Featured"></a></td>
<td><a tabindex="1" href="./gauntlets"><img class="menubutton menu-gauntlets" src="../assets/category-gauntlets.png" title="Gauntlets"></a></td>
<td><a tabindex="1" href="./search/*?type=saved"><img class="menubutton menu-saved" src="/assets/category-saved.png" title="Saved Levels"></a></td>
<td><a tabindex="1" href="./daily"><img class="menubutton menu-daily" src="/assets/category-daily.png" title="Daily Level"></a></td>
<td style="display: block" id="menu_weekly"><a tabindex="1" href="./weekly"><img class="menubutton menu-weekly" src="/assets/category-weekly.png" title="Weekly Demon"></a></td>
<td style="display: none" id="menu_featured"><a tabindex="1" href="./search/*?type=featured"><img class="menubutton menu-featured" src="/assets/category-featured.png" title="Featured"></a></td>
<td><a tabindex="1" href="./gauntlets"><img class="menubutton menu-gauntlets" src="/assets/category-gauntlets.png" title="Gauntlets"></a></td>
</tr>
<tr class="menuButtonList">
<td><a tabindex="1" href="./leaderboard"><img class="menubutton menu-leaderboard" src="../assets/category-scores.png" title="Scores"></a></td>
<td><a tabindex="1" href="./leaderboard"><img class="menubutton menu-leaderboard" src="/assets/category-scores.png" title="Scores"></a></td>
<!-- <img src="./assets/exclamation.png" style="position: absolute; height: 18%; left: 3.5%; bottom: 23%; pointer-events: none; z-index: 50;"> -->
<td><a tabindex="1" href="./search/*?type=hof"><img class="menubutton menu-hof" src="../assets/category-hof.png" title="Hall Of Fame"></a></td>
<td><a tabindex="1" href="./mappacks"><img class="menubutton menu-mappacks" src="../assets/category-packs.png" title="Map Packs"></a></td>
<td><a tabindex="1" href="./search"><img class="menubutton menu-search" src="../assets/category-search.png" title="Search"></a></td>
<td><a tabindex="1" href="./search/*?type=hof"><img class="menubutton menu-hof" src="/assets/category-hof.png" title="Hall Of Fame"></a></td>
<td><a tabindex="1" href="./mappacks"><img class="menubutton menu-mappacks" src="/assets/category-packs.png" title="Map Packs"></a></td>
<td><a tabindex="1" href="./search"><img class="menubutton menu-search" src="/assets/category-search.png" title="Search"></a></td>
</tr>
</table>
<p style="margin-bottom: 2%">Website created by <a class="menuLink" href="https://twitter.com/TheRealGDColon" title="Colon">Colon</a> :<br>Pretty much everything other than that belongs to <a class="menuLink" href="http://robtopgames.com" title="RobTop Games">RobTop Games</a>.</p>
<p style="margin-top: 0%"><a class="menuLink" href="https://gdcolon.com/tools" title="GD Tools">GD Tools</a>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
<a class="menuLink" href="https://gdcolon.com" title="API">More Projects</a>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
<a class="menuLink" href="https://github.com/GDColon/GDBrowser" title="GitHub">GitHub</a>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
<a class="menuLink" href="https://store.steampowered.com/app/322170/Geometry_Dash/" title="Buy Geometry Dash!">Buy Geometry Dash!</a></p>
</div>
<div style="position:absolute; bottom: 17%; right: 7%; width: 9%; text-align: right; pointer-events: none">
<img src="../assets/privateservers.png" width=85%;">
<img src="/assets/privateservers.png" width=85%;">
</div>
<div style="position:absolute; bottom: 2.5%; right: 1.5%; text-align: right; width: 18%;">
<a href="../gdps" title="GD Private Servers"><img class="gdButton" src="../assets/basement.png" width="40%"></a>
<a href="../gdps" title="GD Private Servers"><img class="gdButton" src="/assets/basement.png" width="40%"></a>
</div>
<div class="center" width="100%" style="margin-top: 2%">
<img src="../assets/gdlogo.png" height="11.5%"><br>
<img id="browserlogo" src="../assets/browser.png" height="7%" style="margin: 0.5% 0% 0% 30%">
<img src="/assets/gdlogo.png" height="11.5%"><br>
<img id="browserlogo" src="/assets/browser.png" height="7%" style="margin: 0.5% 0% 0% 30%">
</div>
<div id="noDaily" style="display: none;">
@ -101,12 +101,12 @@
</body>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/6.2.2/browser/pixi.js"></script>
<script type="text/javascript" src="../iconkit/icon.js"></script>
<script type="text/javascript" src="../global.js?v=1"></script>
<script type="text/javascript" src="/iconkit/icon.js"></script>
<script type="text/javascript" src="/misc/global.js?v=1"></script>
<script>
let page = 1
$('#browserlogo').css('filter', `hue-rotate(${Math.floor(Math.random() * (330 - 60)) + 60}deg) saturate(${Math.floor(Math.random() * (150 - 100)) + 100}%)`)
$('#browserlogo').css('filter', `hue-rotate(${Math.floor(randRange(60, 330))}deg) saturate(${Math.floor(randRange(100, 150))}%)`)
let xButtonPos = '43%'
let lastPage
@ -126,7 +126,7 @@ function loadCredits() {
$('#credits').show()
if (page == lastPage) $('#closeCredits').css('height', '52%')
else $('#closeCredits').css('height', xButtonPos)
$('.creditsIcon:not(".creditLoaded"):visible').each(async function() { // only load icons when necessary
$('.creditsIcon:not(".creditLoaded"):visible').each(async function() { // only load icons when necessary
$(this).addClass('creditLoaded')
let profile = await Fetch(`./api/profile/${$(this).attr('ign')}?forceGD=1`).catch(e => {}) || {}
$(this).append(`<gdicon cacheID=${profile.playerID} iconID=${profile.icon} col1="${profile.col1}" col2="${profile.col2}" glow="${profile.glow}"></gdicon>`)
@ -140,19 +140,19 @@ Fetch(`./api/credits`).then(async res => {
lastPage = res.credits.length + 1
res.credits.forEach(async (x, y) => {
$('#credits').append(`<div id="credits${y+1}" class="subCredits" style="display: none;">
<img class="gdButton" src="../assets/arrow-left.png" style="${y == 0 ? "display: none; " : ""}position: absolute; top: 45%; right: 75%; width: 4.5%" tabindex="0" onclick="page -= 1; loadCredits()">
<img class="gdButton" src="/assets/arrow-left.png" style="${y == 0 ? "display: none; " : ""}position: absolute; top: 45%; right: 75%; width: 4.5%" tabindex="0" onclick="page -= 1; loadCredits()">
<div class="brownBox center supercenter" style="width: 80vh; height: 43%; padding-top: 1.5%; padding-bottom: 3.5%;">
<h1>${x.header}</h1>
<h2 style="margin-bottom: 1.5%; margin-top: 1.5%" class="gdButton biggerShadow"><a href="https://gdbrowser.com/u/${x.ign || x.name}" title=${x.name}>${x.name}</h2></a>
<div class="creditsIcon" ign="${x.ign || x.name}"></div>
<a target=_blank href="${x.youtube[0]}" title="YouTube"><img src="../assets/${x.youtube[1]}.png" width="11%" class="gdButton"></a>
<a target=_blank href="${x.twitter[0]}" title="Twitter"><img src="../assets/${x.twitter[1]}.png" width="11%" class="sideSpace gdButton"></a>
<a target=_blank href="${x.github[0]}" title="GitHub"><img src="../assets/${x.github[1]}.png" width="11%" class="sideSpace gdButton"></a>
<a target=_blank href="${x.youtube[0]}" title="YouTube"><img src="/assets/${x.youtube[1]}.png" width="11%" class="gdButton"></a>
<a target=_blank href="${x.twitter[0]}" title="Twitter"><img src="/assets/${x.twitter[1]}.png" width="11%" class="sideSpace gdButton"></a>
<a target=_blank href="${x.github[0]}" title="GitHub"><img src="/assets/${x.github[1]}.png" width="11%" class="sideSpace gdButton"></a>
<br>
</div>
<img class="gdButton" src="../assets/arrow-right.png" style="position: absolute; top: 45%; left: 75%; width: 4.5%" tabindex="0" onclick="page += 1; loadCredits()">
<img class="gdButton" src="/assets/arrow-right.png" style="position: absolute; top: 45%; left: 75%; width: 4.5%" tabindex="0" onclick="page += 1; loadCredits()">
</div>`)
})
@ -160,7 +160,7 @@ Fetch(`./api/credits`).then(async res => {
<div id="specialthanks" class="brownBox center supercenter" style="width: 80vh; height: 55%; padding-top: 1.5%; padding-bottom: 3.5%;">
<h1>Special Thanks!</h1><br>
</div>
<img class="gdButton" src="../assets/arrow-left.png" style="position: absolute; top: 45%; right: 75%; width: 4.5%" tabindex="0" onclick="page -= 1; loadCredits()">
<img class="gdButton" src="/assets/arrow-left.png" style="position: absolute; top: 45%; right: 75%; width: 4.5%" tabindex="0" onclick="page -= 1; loadCredits()">
</div>`)
res.specialThanks.forEach(async (x, y) => {
@ -173,7 +173,7 @@ Fetch(`./api/credits`).then(async res => {
$('#credits').append(`<div id="closeCredits" class="center supercenter" style="z-index: 10; width: 80vh; height: ${xButtonPos}%; pointer-events: none;">
<img class="closeWindow gdButton" src="../assets/close.png" width="14%" style="position: absolute; top: -24%; left: -7vh; pointer-events: all;" tabindex="0" onclick="$('#credits').hide(); page = 1;" title="Close"></div>`)
<img class="closeWindow gdButton" src="/assets/close.png" width="14%" style="position: absolute; top: -24%; left: -7vh; pointer-events: all;" tabindex="0" onclick="$('#credits').hide(); page = 1;" title="Close"></div>`)
$(document).keydown(function(k) {
@ -182,11 +182,11 @@ Fetch(`./api/credits`).then(async res => {
if (k.which == 37 && page > 1) { //left
page -= 1; loadCredits();
}
if (k.which == 39 && page < lastPage) { //right
page += 1; loadCredits();
}
});
});

View file

@ -1,15 +1,15 @@
<head>
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-135255146-3"></script><script>window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);}gtag('js', new Date());gtag('config', 'UA-135255146-3');</script>
<title>Online Icon Kit</title>
<link href="../assets/css/iconkit.css?v=3" type="text/css" rel="stylesheet">
<link href="/assets/css/iconkit.css?v=3" type="text/css" rel="stylesheet">
<meta name="viewport" content="width=1024">
<meta property="og:description" content="Build and save your very own Geometry Dash icons, right from the internet!">
<meta property="og:title" content="Geometry Dash Online Icon Kit">
<meta property="og:type" content="website">
<meta name="og:image" itemprop="image" content="https://gdbrowser.com/assets/icon.png">
<meta name="theme-color" content="#CCFF55">
<meta name="theme-color" content="#CF5">
<meta name="twitter:card" content="summary">
<link rel="icon" href="../assets/icon.png">
<link rel="icon" href="/assets/icon.png">
</link>
</head>
<body style="background: linear-gradient(rgb(139, 139, 139), rgb(100, 100, 100)) no-repeat center center fixed;" onbeforeunload="sessionStorage.setItem('prevUrl', window.location.href)">
@ -21,11 +21,11 @@
<p id="copyFrom" class="white" style="font-size: 24px; margin: 10px auto 5px auto"></p>
<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: 10px;">
<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()">
<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" data-nosnippet id="settings">
<div class="brownbox bounce center supercenter" style="height: 380px; width: 820px">
<h1 class="center gold" style="margin-top: 10px; margin-bottom: 20px;">Settings</h1>
@ -33,10 +33,10 @@
<div 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><h2>No UFO Dome</h2></div>
<div class="help" help="Sorts the colors by internal ID instead of their in-game order"><input type="checkbox" class="iconsetting" id="box-sort"><label for="box-sort" class="gdcheckbox gdButton"></label><h2>Unsort Colors</h2></div>
<div class="help" help="Allows robots to play spider animations and vice versa"><input type="checkbox" class="iconsetting" id="box-cursed"><label for="box-cursed" class="gdcheckbox gdButton"></label><h2>Cursed animations</h2></div>
</div>
</div>
<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()">
<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>
@ -45,14 +45,14 @@
<h1 class="center gold" style="margin-top: 12px">Enable 2.2 icons?</h1>
<p style="font-size: 26px; color: white; width: 620px; margin: 17px auto">The newest update for Geometry Dash Lite revealed 500 new icons across all forms. Enabling this setting will reveal all these icons, however they will be lower quality since no UHD textures were provided.</p>
<p style="font-size: 30px; color: yellow">THIS WILL REVEAL <u>EVERY</u> ICON.<br>PRESS CANCEL IF YOU DON'T WANT TO BE SPOILED!!!</p>
<img src="../assets/iconkitbuttons/btn-reveal.png" height=60px; style="margin-right: 33px" class="gdButton center" onclick="clickedSpoilerWarning = true; toggleSpoilers(); $('#spoilerwarning').hide()">
<img src="../assets/btn-cancel.png" height=60px; class="gdButton center" onclick="$('#spoilerwarning').hide()">
<img class="gdButton xButton" src="../assets/close.png" width="70px" onclick="$('#spoilerwarning').hide()">
<img src="/assets/iconkitbuttons/btn-reveal.png" height=60px; style="margin-right: 33px" class="gdButton center" onclick="clickedSpoilerWarning = true; toggleSpoilers(); $('#spoilerwarning').hide()">
<img src="/assets/btn-cancel.png" height=60px; class="gdButton center" onclick="$('#spoilerwarning').hide()">
<img class="gdButton xButton" src="/assets/close.png" width="70px" onclick="$('#spoilerwarning').hide()">
</div>
</div>
<img id="iconkitlogo" src="../assets/iconkit.png" style="margin: 7px 0px; height: 50px"><br>
<h2 style="margin: 5 auto 0 auto; display: none; height: 45px" id="howto"><span style='color: #aaaaaa'>(hover over an icon for info)</span></h2>
<img id="iconkitlogo" src="/assets/iconkit.png" style="margin: 7px 0px; height: 50px"><br>
<h2 style="margin: 5 auto 0 auto; display: none; height: 45px" id="howto"><span style='color: #aaa'>(hover over an icon for info)</span></h2>
<div id="iconbox">
<canvas id="result" style="transform: translateY(-35px)">
@ -60,14 +60,14 @@
<hr id="gdfloor">
<div id="menuButtons" style="height: 65px; margin: 0 0 8 0;">
<button class="blankButton menuButton" id="customColors" title="Settings" onclick="$('#settings').show()"><img src="../assets/iconkitbuttons/cog.png" width=60px></button>
<button class="blankButton menuButton" onclick="icon.psdExport()" title="Download layered PSD"><img src="../assets/iconkitbuttons/psd.png" width=60px></button>
<button class="blankButton menuButton" id="copyToClipboard" title="Copy to clipboard"><img src="../assets/iconkitbuttons/copy.png" width=60px></button>
<button class="blankButton menuButton" onclick="icon.pngExport()" title="Download icon"><img src="../assets/iconkitbuttons/save.png" width=60px></a></button>
<button class="blankButton menuButton" id="getUserIcon" title="Get player icon"><img src="../assets/iconkitbuttons/steal.png" width=60px></button>
<button class="blankButton menuButton" id="randomIcon" title="Random Icon"><img src="../assets/iconkitbuttons/shuffle.png" width=60px></button>
<button class="blankButton menuButton" id="unlockIcon" title="Unlock details"><img id="lock" src="../assets/iconkitbuttons/unlock.png" width=60px></button>
<button class="blankButton menuButton" onclick="clickedSpoilerWarning ? toggleSpoilers() : $('#spoilerwarning').show()" title="2.2 icons (spoilers!!!)"><img id="newIconBtn" src="../assets/iconkitbuttons/spoilers.png" width=60px></button>
<button class="blankButton menuButton" id="customColors" title="Settings" onclick="$('#settings').show()"><img src="/assets/iconkitbuttons/cog.png" width=60px></button>
<button class="blankButton menuButton" onclick="icon.psdExport()" title="Download layered PSD"><img src="/assets/iconkitbuttons/psd.png" width=60px></button>
<button class="blankButton menuButton" id="copyToClipboard" title="Copy to clipboard"><img src="/assets/iconkitbuttons/copy.png" width=60px></button>
<button class="blankButton menuButton" onclick="icon.pngExport()" title="Download icon"><img src="/assets/iconkitbuttons/save.png" width=60px></a></button>
<button class="blankButton menuButton" id="getUserIcon" title="Get player icon"><img src="/assets/iconkitbuttons/steal.png" width=60px></button>
<button class="blankButton menuButton" id="randomIcon" title="Random Icon"><img src="/assets/iconkitbuttons/shuffle.png" width=60px></button>
<button class="blankButton menuButton" id="unlockIcon" title="Unlock details"><img id="lock" src="/assets/iconkitbuttons/unlock.png" width=60px></button>
<button class="blankButton menuButton" onclick="clickedSpoilerWarning ? toggleSpoilers() : $('#spoilerwarning').show()" title="2.2 icons (spoilers!!!)"><img id="newIconBtn" src="/assets/iconkitbuttons/spoilers.png" width=60px></button>
</div>
<div id="iconTabs"></div><br>
@ -79,12 +79,12 @@
<div style="width: 1200px; margin: 0 auto; position: relative; right: 42px">
<div style="padding-top: 15px; width: 75px; vertical-align: top;" class="colTypes inline">
<button id="cc1" class="colorLabel blankButton" onclick="$('#cp1').trigger('click')" title="Primary Color"><img src="../assets/col1.png"></button><input type="color" id="cp1" class="colorPicker">
<button id="cc2" class="colorLabel blankButton" onclick="$('#cp2').trigger('click')" title="Secondary Color"><img src="../assets/col2.png"></button><input type="color" id="cp2" class="colorPicker">
<button id="cc1" class="colorLabel blankButton" onclick="$('#cp1').trigger('click')" title="Primary Color"><img src="/assets/col1.png"></button><input type="color" id="cp1" class="colorPicker">
<button id="cc2" class="colorLabel blankButton" onclick="$('#cp2').trigger('click')" title="Secondary Color"><img src="/assets/col2.png"></button><input type="color" id="cp2" class="colorPicker">
<div class="colorSplit" style="height: 12.5px"></div>
<button id="ccG" class="colorLabel blankButton" onclick="$('#cpG').trigger('click')" title="Glow Color"><img src="../assets/colG.png"></button><input type="color" id="cpG" class="colorPicker">
<button id="ccW" class="colorLabel blankButton" onclick="$('#cpW').trigger('click')" title="White Highlights" style="display: none"><img src="../assets/colW.png" style="background-color: rgb(255, 255, 255)";></button><input type="color" id="cpW" class="colorPicker">
<button id="ccU" class="colorLabel blankButton" onclick="$('#cpU').trigger('click')" title="UFO Dome" style="display: none"><img src="../assets/colU.png" style="background-color: rgb(255, 255, 255)";></button><input type="color" id="cpU" class="colorPicker">
<button id="ccG" class="colorLabel blankButton" onclick="$('#cpG').trigger('click')" title="Glow Color"><img src="/assets/colG.png"></button><input type="color" id="cpG" class="colorPicker">
<button id="ccW" class="colorLabel blankButton" onclick="$('#cpW').trigger('click')" title="White Highlights" style="display: none"><img src="/assets/colW.png" style="background-color: rgb(255, 255, 255)";></button><input type="color" id="cpW" class="colorPicker">
<button id="ccU" class="colorLabel blankButton" onclick="$('#cpU').trigger('click')" title="UFO Dome" style="display: none"><img src="/assets/colU.png" style="background-color: rgb(255, 255, 255)";></button><input type="color" id="cpU" class="colorPicker">
</div>
<div id="colors" class="inline iconKit">
<div id="col1"></div>
@ -116,27 +116,32 @@
</p>
<div class="hideIfSmall" style="position:absolute; top: 20px; left: 20px; width: 64px; height: 64px; pointer-events: none">
<img class="gdButton" style="pointer-events: all" id="backButton" src="../assets/back.png" height="100%" onclick="backButton()">
<img class="gdButton" style="pointer-events: all" id="backButton" src="/assets/back.png" height="100%" onclick="backButton()">
</div>
<div class="hideIfSmall" id="extraInfo" style="position:absolute; top: 20px; right: 15px"></div>
</div>
</body>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/6.2.2/browser/pixi.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/6.2.2/browser/pixi.min.js"></script>
<script type="text/javascript" src="./libs/ag-psd.js"></script>
<script type="text/javascript" src="./libs/pixi-ease.js"></script>
<script type="text/javascript" src="./libs/imagesloaded.js"></script>
<script type="text/javascript" src="./icon.js"></script>
<script type="text/javascript">
"use strict";
// these 3 fns are available in `global.js`
const clamp = (x, min, max) => x < min ? min : (x > max ? max : x)
const randRange = (min, max) => Math.random() * (max - min) + +min
const randInt = (min, max) => Math.floor(randRange(min, max))
// hi there hello! this code is really old, so it's shit. i should rewrite it some time omg
$('.hidden').show();
let mobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent)
$('.hidden').show()
// avoid possible name collision with isMobile.js (AJAX Node module)
let is_mobile = /Android|webOS|iPhone|iP[ao]d|BlackBerry/i.test(navigator.userAgent)
let currentForm = 'icon'
let glowbtnformCopy = 'icon'
const yOffsets = { ball: -10, ufo: 30, spider: 7, swing: -15 }
@ -149,7 +154,7 @@ let selectedCol2 = 3
let selectedColG = 3
let selectedColW = null
let selectedColU = null
let enableGlow = 0
let enableGlow = false
let enableSpoilers = false
let clickedSpoilerWarning = false
@ -164,25 +169,28 @@ let animationMultiplier = 1
let icon = null
let iconCanvas = document.getElementById('result');
let app = new PIXI.Application({ view: iconCanvas, width: 300, height: 300, backgroundAlpha: 0 });
let iconCanvas = document.getElementById('result')
let app = new PIXI.Application({ view: iconCanvas, width: 300, height: 300, backgroundAlpha: 0 })
if (mobile) $('#logo').attr('width', '80%');
if (is_mobile) $('#logo').attr('width', '80%')
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 colorBG(e, c, hex) {
let capitalize = str => str[0].toUpperCase() + str.substr(1)
function colorBG(e, c, hex) {
$(`#cc${e} img`).css('background-color', hex ? `#${c}` : `rgb(${c.r}, ${c.g}, ${c.b})`)
if (!hex) $(`#cp${e}`).val(toHexCode(rgbToDecimal(c.r, c.g, c.b)))
if (!hex) $(`#cp${e}`).val(toHexCode(rgb2Pac(c.r, c.g, c.b)))
}
function colorSplit() {
if ($("#colG").is(':visible') || $("#colW").is(':visible') || $("#colU").is(':visible')) $('.colorSplit').show()
else $('.colorSplit').hide()
function colorSplit() {
$('.colorSplit')[
$("#colG").is(':visible') ||
$("#colW").is(':visible') ||
$("#colU").is(':visible')
? "show" : "hide"
]()
}
function setColor(type, col) {
@ -215,10 +223,9 @@ function checkWhite() {
// check if animation selector should be visible
function checkAnimation() {
let animationData = iconData.robotAnimations.animations[selectedForm]
if (animationData && !$(`#robotAnimation[form="${selectedForm}"]`).is(":visible")) {
if (animationData && !$(`#robotAnimation[form="${selectedForm}"]`).is(":visible"))
appendAnimations(selectedForm, animationData)
}
else if (!animationData) $('#animationOptions').hide()
if (!animationData) $('#animationOptions').hide()
}
function animationSort(anim) {
@ -249,8 +256,8 @@ function setExtras() {
}
if ($('#colG').is(":visible") && (getCol(selectedColG) != getCol(selectedCol2))) extraInfo["Glow"] = extraColString(selectedColG)
if ($('#colW').is(":visible") && (getCol(selectedColW) != 0xffffff)) extraInfo["White"] = extraColString(selectedColW)
if ($('#colU').is(":visible") && (getCol(selectedColU) != 0xffffff)) extraInfo["UFO Dome"] = extraColString(selectedColU)
if ($('#colW').is(":visible") && (getCol(selectedColW) != WHITE)) extraInfo["White"] = extraColString(selectedColW)
if ($('#colU').is(":visible") && (getCol(selectedColU) != WHITE)) extraInfo["UFO Dome"] = extraColString(selectedColU)
let foundCredit = iconStuff.iconCredits.find(x => x.form == selectedForm && x.id == selectedIcon)
if (foundCredit) extraInfo["🎨 Artist"] = foundCredit.name
@ -265,8 +272,8 @@ function setExtras() {
function extraColString(col) {
let realCol = getCol(col)
let hexCol = toHexCode(realCol)
let foundGDCol = Object.entries(iconStuff.colors).find(x => rgbToDecimal(x[1]) == realCol)
let hexCol = toHexCode(realCol)
let foundGDCol = Object.entries(iconStuff.colors).find(x => rgb2Pac(x[1]) == realCol)
return foundGDCol ? `${foundGDCol[0]} (${hexCol})` : hexCol
}
@ -274,194 +281,198 @@ function getCol(id) {
return parseIconColor(id, iconStuff.colors)
}
function toHexCode(decimal) {
return "#" + decimal.toString(16).padStart(6, "0")
function toHexCode(pack) {
return "#" + pack.toString(16).padStart(6, "0")
}
let iconData = null
fetch('../api/icons').then(res => res.json()).then(sacredTexts => {
fetch('../api/iconkit').then(res => res.json()).then(iconKitData => {
fetch('/api/icons').then(res => res.json()).then(sacredTexts => {
fetch('/api/iconkit').then(res => res.json()).then(iconKitData => {
iconStuff = Object.assign(sacredTexts, iconKitData)
iconData = sacredTexts
iconStuff = Object.assign(sacredTexts, iconKitData)
iconData = sacredTexts
let forms = Object.keys(iconStuff.forms)
let forms = Object.keys(iconStuff.forms)
forms.forEach(form => {
let spoil = ["swing", "jetpack"].includes(form)
$("#iconTabs").append(`<button form="${form}"${spoil ? `isnew="true" style="display: none"` : ""} title="${iconStuff.forms[form].name}" class="blankButton iconTabButton"><img src="../assets/iconkitbuttons/${form}_off.png" style="width: 50px"></button>`)
$("#copyForms").append(`<button form="${form}"${spoil ? `isnew="true" style="display: none"` : ""} title="${iconStuff.forms[form].name}" class="blankButton copyForm"><img src="../assets/iconkitbuttons/${form}_off.png" style="width: 50px"></button>`)
})
$("#iconTabs").append(`<button title="Glow" class="blankButton glowToggle" id="glowbtn"><img id="glow" src="../assets/iconkitbuttons/streak_off.png" style="width: 50px"></button>`)
forms.forEach(form => {
let spoil = ["swing", "jetpack"].includes(form)
$("#iconTabs").append(`<button form="${form}"${spoil ? `isnew="true" style="display: none"` : ""} title="${iconStuff.forms[form].name}" class="blankButton iconTabButton"><img src="/assets/iconkitbuttons/${form}_off.png" style="width: 50px"></button>`)
$("#copyForms").append(`<button form="${form}"${spoil ? `isnew="true" style="display: none"` : ""} title="${iconStuff.forms[form].name}" class="blankButton copyForm"><img src="/assets/iconkitbuttons/${form}_off.png" style="width: 50px"></button>`)
})
$("#iconTabs").append(`<button title="Glow" class="blankButton glowToggle" id="glowbtn"><img id="glow" src="/assets/iconkitbuttons/streak_off.png" style="width: 50px"></button>`)
forms.forEach(form => {$("#iconKitParent").append(`<div id="${form}s" class="iconContainer"></div>`)})
forms.forEach(form => {$("#iconKitParent").append(`<div id="${form}s" class="iconContainer"></div>`)})
if (iconStuff.noCopy) $('#getUserIcon').remove()
else if (iconStuff.server) {
$('#copyFrom').html(`Copying from the <cy>${iconStuff.server}</cy> servers`)
$('#stealBox').css('height', '385px')
}
if (iconStuff.noCopy) $('#getUserIcon').remove()
else if (iconStuff.server) {
$('#copyFrom').html(`Copying from the <cy>${iconStuff.server}</cy> servers`)
$('#stealBox').css('height', '385px')
}
function generateIcon(cb) {
let noDome = selectedForm == "ufo" && iconSettings.includes("ufo")
let foundForm = parseIconForm(selectedForm)
function generateIcon(cb) {
//let noDome = selectedForm == "ufo" && iconSettings.includes("ufo")
let foundForm = parseIconForm(selectedForm)
loadIconLayers(foundForm, selectedIcon, function(l, sprites, isNew) {
let iconArgs = {app, form: foundForm, id: selectedIcon, col1: getCol(selectedCol1), col2: getCol(selectedCol2), glow: enableGlow > 0, new: isNew}
if (selectedCol2 != selectedColG) iconArgs.colG = getCol(selectedColG)
if (selectedColW) iconArgs.colW = getCol(selectedColW)
if (selectedColU) iconArgs.colU = getCol(selectedColU)
if (iconSettings.includes("ufo")) iconArgs.noUFODome = true
if (animationMultiplier != 1) iconArgs.animationSpeed = animationMultiplier
if (currentAnimation.form && (iconSettings.includes("cursed") || currentAnimation.form == selectedForm)) {
iconArgs.animation = currentAnimation.name
iconArgs.animationForm = currentAnimation.form
}
icon = new Icon(iconArgs)
icon.sprite.position.set(app.renderer.width / 2, (app.renderer.height / 2) + (yOffsets[selectedForm] || 0))
updateDetails()
checkWhite()
if (cb) cb()
})
}
function filterIcon(name) {
return iconStuff.previewIcons.concat(iconStuff.newPreviewIcons).filter(x => x.startsWith(name)).sort(function (a,b) {return a.replace(/[^0-9]/g, "") - b.replace(/[^0-9]/g, "");})
}
function appendIcon(form, formName) {
let imagesLoaded = 0
let totalLoaded = 0
let formContainer = $('#' + formName + 's')
let hasMini = form[0].endsWith("_0.png")
if (hasMini) form.shift()
form.forEach(function (i, p) {
let newOne = iconStuff.newPreviewIcons.includes(i)
formContainer.append(`<button num="${p + 1}" form="${formName}"${newOne ? ` isnew="true"${enableSpoilers ? "" : ` style="display: none"`}` : ""} class="blankButton iconButton" id="${formName}-${p + 1}"><img src="./${newOne ? "new" : ""}premade/${i}" title="${capitalize(formName)} ${p + 1}"></button>`)
})
if (hasMini) formContainer.append(`<button num="0" form="${formName}" class="blankButton iconButton" id="${formName}-0"><img src="./premade/${formName}_0.png" title="Mini ${formName}"></button>`)
formContainer.imagesLoaded(function() {}).progress(function() {
imagesLoaded += 1;
totalLoaded = imagesLoaded / formContainer.find('img').length * 100
$('#iconloading').css('width', `${totalLoaded}%`)
loadIconLayers(foundForm, selectedIcon, function(l, sprites, isNew) {
let iconArgs = {app, form: foundForm, id: selectedIcon, col1: getCol(selectedCol1), col2: getCol(selectedCol2), glow: enableGlow, new: isNew}
if (selectedCol2 != selectedColG) iconArgs.colG = getCol(selectedColG)
if (selectedColW) iconArgs.colW = getCol(selectedColW)
if (selectedColU) iconArgs.colU = getCol(selectedColU)
if (iconSettings.includes("ufo")) iconArgs.noUFODome = true
if (animationMultiplier != 1) iconArgs.animationSpeed = animationMultiplier
if (currentAnimation.form && (iconSettings.includes("cursed") || currentAnimation.form == selectedForm)) {
iconArgs.animation = currentAnimation.name
iconArgs.animationForm = currentAnimation.form
}
)}
function loadColors(devmode) {
let colTypes = [1, 2, "G", "W", "U"]
colTypes.forEach(x => $(`#col${x}`).html(""))
iconStuff.colorOrder.forEach(function (p, n) {
if (iconSettings.includes("sort")) p = n;
colTypes.forEach(c => {
let colRGB = iconStuff.colors[p]
$(`#col${c}`).append(`<button col=${p} colType=color${c} class="blankButton color${c} iconColor" title="Color ${p} (#${toHexCode(rgbToDecimal(colRGB))})" id="col${c}-${p}"><div style="background-color: rgb(${colRGB.r}, ${colRGB.g}, ${colRGB.b})"></button>`)
})
icon = new Icon(iconArgs)
icon.sprite.position.set(app.renderer.width / 2, (app.renderer.height / 2) + (yOffsets[selectedForm] || 0))
updateDetails()
checkWhite()
if (cb) cb()
})
}
function filterIcon(name) {
return (
iconStuff.previewIcons.concat(iconStuff.newPreviewIcons)
.filter(x => x.startsWith(name))
.sort( (a,b) => a.replace(/\D/g, "") - b.replace(/\D/g, "") )
)
}
function appendIcon(form, formName) {
let imagesLoaded = 0
let totalLoaded = 0
let formContainer = $(`#${formName}s`)
let hasMini = form[0].endsWith("_0.png")
if (hasMini) form.shift()
form.forEach((i, p) => {
let newOne = iconStuff.newPreviewIcons.includes(i)
formContainer.append(`<button num="${p + 1}" form="${formName}"${newOne ? ` isnew="true"${enableSpoilers ? "" : ` style="display: none"`}` : ""} class="blankButton iconButton" id="${formName}-${p + 1}"><img src="/iconkit/${newOne ? "new" : ""}premade/${i}" title="${capitalize(formName)} ${p + 1}"></button>`)
})
if (hasMini) formContainer.append(`<button num="0" form="${formName}" class="blankButton iconButton" id="${formName}-0"><img src="/iconkit/premade/${formName}_0.png" title="Mini ${formName}"></button>`)
formContainer.imagesLoaded(function() {}).progress(function() {
imagesLoaded++;
totalLoaded = imagesLoaded / formContainer.find('img').length * 100
$('#iconloading').css('width', `${totalLoaded}%`)
}
)}
function loadColors(devmode) {
let colTypes = [1, 2, "G", "W", "U"]
colTypes.forEach(x => $(`#col${x}`).html(""))
iconStuff.colorOrder.forEach(function (p, n) {
if (iconSettings.includes("sort")) p = n
colTypes.forEach(c => {
let colRGB = iconStuff.colors[p]
$(`#col${c}`).append(`<button col=${p} colType=color${c} class="blankButton color${c} iconColor" title="Color ${p} (#${toHexCode(rgb2Pac(colRGB))})" id="col${c}-${p}"><div style="background-color: rgb(${colRGB.r}, ${colRGB.g}, ${colRGB.b})"></button>`)
})
$('#col1').append("<span style='min-width: 10px'></span>")
}
})
$('#col1').append("<span style='min-width: 10px'></span>")
}
loadColors()
let icons = filterIcon('icon');
let sample = JSON.parse(iconStuff.sample);
enableGlow = sample[3] * 2;
[selectedIcon, selectedCol1, selectedCol2] = sample;
selectedColG = selectedCol2
loadColors()
let icons = filterIcon('icon')
$('body').imagesLoaded(function () {
appendIcon(icons, "icon")
$(`[num="${sample[0]}"][form="icon"]`).addClass('iconSelected');
let sample = JSON.parse(iconStuff.sample)
enableGlow = !!(sample[3] * 2);
[eselectedIcon, selectedCol1, selectedCol2] = sample
selectedColG = selectedCol2
$('body').imagesLoaded(function () {
appendIcon(icons, "icon")
$(`[num="${sample[0]}"][form="icon"]`).addClass('iconSelected');
})
$(`.color1[col="${sample[1]}"]`).addClass('iconSelected');
$(`.color2[col="${sample[2]}"]`).addClass('iconSelected');
$(`.colorG[col="${sample[2]}"]`).addClass('iconSelected');
$('.colorW[col="12"]').addClass('iconSelected');
$('.colorU[col="12"]').addClass('iconSelected')
colorBG(1, iconStuff.colors[sample[1]])
colorBG(2, iconStuff.colors[sample[2]])
colorBG('G', iconStuff.colors[sample[2]])
$('.colorLabel img').show()
generateIcon(() => { icon.glow = enableGlow = false } ) // disable glow after first generated
$(document).on('click', '.iconTabButton', function() {
let form = $(this).attr('form')
let formElement = `#${form}s`
currentForm = form
$('.iconTabButton').each(function() {
$(this).children().first().attr('src', $(this).children().first().attr('src').replace('_on', '_off'))
})
$(`.color1[col="${sample[1]}"]`).addClass('iconSelected');
$(`.color2[col="${sample[2]}"]`).addClass('iconSelected');
$(`.colorG[col="${sample[2]}"]`).addClass('iconSelected');
$('.colorW[col="12"]').addClass('iconSelected');
$('.colorU[col="12"]').addClass('iconSelected');
let img = $(this).children().first()
img.attr('src', img.attr('src').replace('_off', '_on'));
colorBG(1, iconStuff.colors[sample[1]])
colorBG(2, iconStuff.colors[sample[2]])
colorBG('G', iconStuff.colors[sample[2]])
$('#iconKitParent').each(function() { $(this).children().not('#iconprogressbar').hide() })
$('.colorLabel img').show()
if ($(formElement).html() == "") appendIcon(filterIcon(form), form)
generateIcon(() => { icon.glow = false; enableGlow = 0 } ) // disable glow after first generated
$(formElement).show()
})
$(document).on('click', '.iconTabButton', function () {
let form = $(this).attr('form')
let formElement = '#' + form + 's'
$('#iconTabs').find('.iconTabButton')
.first().children()
.first().attr(
'src',
$('.iconTabButton').first().children()
.first().attr('src').replace('_off', '_on')
)
currentForm = form
function setGlowAttr(glow) {
$("#glow").attr('src', $("#glow").attr('src').replace( ...(glow ? ['_off', '_on'] : ['_on', '_off']) ))
}
$('.iconTabButton').each(function(x, y) {
$(this).children().first().attr('src', $(this).children().first().attr('src').replace('_on', '_off'))
})
$("#randomIcon").click(function() {
let img = $(this).children().first()
img.attr('src', img.attr('src').replace('_off', '_on'));
let iconPool = iconStuff.previewIcons.concat(enableSpoilers ? iconStuff.newPreviewIcons : [])
let pickedIcon = iconPool[randInt(0, iconPool.length)].split(".", 1)[0].split("_")
let [randomForm, randomID] = pickedIcon
$('#iconKitParent').each(function(x, y) { $(this).children().not('#iconprogressbar').hide() })
let colorCount = Object.keys(iconStuff.colors).length
selectedForm = randomForm
selectedIcon = randomID
selectedCol1 = randInt(0, colorCount)
selectedCol2 = randInt(0, colorCount)
selectedColW = null
selectedColU = null
enableGlow = !randInt(0, 3) // 1 in 3 chance of glow
generateIcon()
if ($(formElement).html() == "") appendIcon(filterIcon(form), form)
$(formElement).show()
})
$('#glow').attr('src', '/assets/iconkitbuttons/streak_off.png')
$('#iconTabs').find('.iconTabButton').first().children().first().attr('src', $('.iconTabButton').first().children().first().attr('src').replace('_off', '_on'))
$(`.iconTabButton[form=${selectedForm}]`).trigger('click')
$(`#${selectedForm}-${selectedIcon}`).trigger('click')
$(`#col1-${selectedCol1}`).trigger('click')
$(`#col2-${selectedCol2}`).trigger('click')
$(`#colG-${selectedCol2}`).trigger('click')
$('#colW-12').trigger('click')
$('#colU-12').trigger('click')
setGlowAttr(enableGlow)
})
$("#randomIcon").click(function() {
let iconPool = iconStuff.previewIcons.concat(enableSpoilers ? iconStuff.newPreviewIcons : [])
let pickedIcon = iconPool[Math.floor(Math.random() * iconPool.length)].split(".")[0].split("_")
let [randomForm, randomID] = pickedIcon
let colorCount = Object.keys(iconStuff.colors).length
selectedForm = randomForm
selectedIcon = randomID
selectedCol1 = randInt(0, colorCount - 1)
selectedCol2 = randInt(0, colorCount - 1)
selectedColW = null
selectedColU = null
enableGlow = randInt(0, 2) == 1 ? 1 : 0 // 1 in 3 chance of glow
generateIcon()
$('#glow').attr('src', '../assets/iconkitbuttons/streak_off.png')
$(`.iconTabButton[form=${selectedForm}]`).trigger('click')
$(`#${selectedForm}-${selectedIcon}`).trigger('click')
$(`#col1-${selectedCol1}`).trigger('click')
$(`#col2-${selectedCol2}`).trigger('click')
$(`#colG-${selectedCol2}`).trigger('click')
$('#colW-12').trigger('click')
$('#colU-12').trigger('click')
if (enableGlow == 1) $("#glow").attr('src', $("#glow").attr('src').replace('_off', '_on'))
else $("#glow").attr('src', $("#glow").attr('src').replace('_on', '_off'))
})
$('#glowbtn').click(function () {
if (enableGlow) {
$("#glow").attr('src', $("#glow").attr('src').replace('_on', '_off'))
enableGlow = 0;
}
else {
$("#glow").attr('src', $("#glow").attr('src').replace('_off', '_on'))
enableGlow = 1;
}
icon.setGlow(enableGlow > 0, false)
$('#glowbtn').click(function() {
setGlowAttr(enableGlow)
enableGlow = !enableGlow
icon.setGlow(enableGlow, false)
updateDetails()
})
$(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')
@ -471,10 +482,10 @@ fetch('../api/iconkit').then(res => res.json()).then(iconKitData => {
$(document).on('click', '.iconButton', function () {
$(".iconButton").removeClass("iconSelected");
$(this).addClass('iconSelected');
$(this).addClass('iconSelected')
let oldForm = selectedForm
selectedIcon = $(this).attr('num');
selectedForm = $(this).attr('form');
selectedIcon = $(this).attr('num')
selectedForm = $(this).attr('form')
if (selectedForm == "ufo") { $('#colU').show(); $('#ccU').show() }
else { $('#colU').hide(); $('#ccU').hide(); $(`#colU-12`).trigger('click'); }
@ -488,99 +499,99 @@ fetch('../api/iconkit').then(res => res.json()).then(iconKitData => {
$(document).on('click', '.color1', function () {
$(".color1").removeClass("iconSelected");
$(this).addClass('iconSelected');
selectedCol1 = $(this).attr('col');
colorBG(1, iconStuff.colors[$(this).attr('col')]);
$(this).addClass('iconSelected')
selectedCol1 = $(this).attr('col')
colorBG(1, iconStuff.colors[$(this).attr('col')])
setColor("1", selectedCol1)
})
$(document).on('click', '.color2', function () {
$(".color2").removeClass("iconSelected");
$(this).addClass('iconSelected');
selectedCol2 = $(this).attr('col');
$(this).addClass('iconSelected')
selectedCol2 = $(this).attr('col')
colorBG(2, iconStuff.colors[$(this).attr('col')]);
$(`#colG-${$(this).attr('col')}`).trigger('click')
selectedColG = $(this).attr('col');
selectedColG = $(this).attr('col')
setColor("2", selectedCol2)
})
$(document).on('click', '.colorG', function () {
$(".colorG").removeClass("iconSelected");
$(this).addClass('iconSelected');
selectedColG = $(this).attr('col');
colorBG('G', iconStuff.colors[$(this).attr('col')]);
$(this).addClass('iconSelected')
selectedColG = $(this).attr('col')
colorBG('G', iconStuff.colors[$(this).attr('col')])
setColor("g", selectedColG)
})
$(document).on('click', '.colorW', function () {
$(".colorW").removeClass("iconSelected");
$(this).addClass('iconSelected');
selectedColW = $(this).attr('col');
$(this).addClass('iconSelected')
selectedColW = $(this).attr('col')
if (selectedColW == 12) selectedColW = null
colorBG('W', iconStuff.colors[$(this).attr('col')]);
colorBG('W', iconStuff.colors[$(this).attr('col')])
setColor("w", selectedColW)
})
$(document).on('click', '.colorU', function () {
$(".colorU").removeClass("iconSelected");
$(this).addClass('iconSelected');
selectedColU = $(this).attr('col');
$(this).addClass('iconSelected')
selectedColU = $(this).attr('col')
if (selectedColU == 12) selectedColU = null
colorBG('U', iconStuff.colors[$(this).attr('col')]);
colorBG('U', iconStuff.colors[$(this).attr('col')])
setColor("u", selectedColU)
})
$("#cp1").on('input change', function() {
colorBG(1, $(this).val(), true);
$(".color1").removeClass("iconSelected");
$(".color1").removeClass("iconSelected")
selectedCol1 = $('#cp1').val().slice(1)
setColor("1", selectedCol1)
})
$("#cp2").on('input change', function() {
colorBG(2, $(this).val(), true);
$(".color2").removeClass("iconSelected");
$(".color2").removeClass("iconSelected")
selectedCol2 = $('#cp2').val().slice(1)
setColor("2", selectedCol2)
})
$("#cpG").on('input change', function() {
colorBG('G', $(this).val(), true);
$(".colorG").removeClass("iconSelected");
$(".colorG").removeClass("iconSelected")
selectedColG = $('#cpG').val().slice(1)
setColor("g", selectedColG)
})
$("#cpW").on('input change', function() {
colorBG('W', $(this).val(), true);
$(".colorW").removeClass("iconSelected");
$(".colorW").removeClass("iconSelected")
selectedColW = $('#cpW').val().slice(1)
setColor("w", selectedColW)
})
$("#cpU").on('input change', function() {
colorBG('U', $(this).val(), true);
$(".colorU").removeClass("iconSelected");
$(".colorU").removeClass("iconSelected")
selectedColU = $('#cpU').val().slice(1)
setColor("u", selectedColU)
})
$("#getUserIcon").click(function() {
$(`.copyForm[form=${currentForm}]`).trigger('click')
$('#steal').show();
$('#playerName').focus()
$('#playerName').select()
$('#playerName').focus()
$('#playerName').select()
})
$('#copyToClipboard').click(function() {
if ($(this).hasClass('greyedOut')) return
icon.copyToClipboard()
let copyIcon = $(this).find('img')
$(this).addClass('greyedOut')
copyIcon.attr('src', '../assets/iconkitbuttons/copied.png')
copyIcon.attr('src', '/assets/iconkitbuttons/copied.png')
setTimeout(() => {
$(this).removeClass('greyedOut')
copyIcon.attr('src', '../assets/iconkitbuttons/copy.png')
copyIcon.attr('src', '/assets/iconkitbuttons/copy.png')
}, 420);
})
@ -595,12 +606,12 @@ fetch('../api/iconkit').then(res => res.json()).then(iconKitData => {
})
let hoverText = $('#helpText').html()
$(".help").hover(function() {
$(".help").hover(function() {
$(this).css('color', 'rgba(200, 255, 255)')
$('#helpText').html($(this).attr('help'))
$('#helpText').html($(this).attr('help'))
}, function() {
$(this).css('color', 'white')
$('#helpText').html(hoverText)
$('#helpText').html(hoverText)
})
$(document).on('change', '.iconsetting', function (e) {
@ -608,11 +619,11 @@ fetch('../api/iconkit').then(res => res.json()).then(iconKitData => {
$('.iconsetting:checkbox:checked').each((i, x) => { checkedSettings.push(x.id.split('-')[1]) })
iconSettings = checkedSettings
switch ($(this).attr('id').slice(4)) {
case "sort": loadColors(); break;
case "ufo": generateIcon(); break;
case "sort": loadColors(); break
case "ufo": generateIcon(); break
case "cursed":
$('#animationOptions').hide();
checkAnimation();
c('#animationOptions').hide();
checkAnimation();
$('#robotAnimation').trigger('change');
break;
}
@ -620,8 +631,8 @@ fetch('../api/iconkit').then(res => res.json()).then(iconKitData => {
})
$('#unlockIcon').click(function() {
if (!achievements.length) {
fetch('../api/achievements').then(res => { res.json().then(x => {
if (!achievements.length) {
fetch('/api/achievements').then(res => { res.json().then(x => {
achievements = x.achievements
shopIcons = iconStuff.shops
unlockMode = true
@ -635,19 +646,19 @@ fetch('../api/iconkit').then(res => res.json()).then(iconKitData => {
else { $('#lock').attr('src', $('#lock').attr('src').replace('_on.png', '.png')); $('#howto').hide() }
}
})
$(document).on('mouseover', '.iconButton, .color1, .color2', function () {
if (unlockMode && achievements.length) {
$(this).addClass('iconHover')
let form = $(this).attr('form') || $(this).attr('colType')
let iconNumber = $(this).attr('num') || $(this).attr('col')
$('#howto').html(getUnlockMethod(iconNumber, form) || `<span style='color: #aaaaaa'>(no info available)</span>`)
$('#howto').html(getUnlockMethod(iconNumber, form) || `<span style='color: #aaa'>(no info available)</span>`)
}
})
$(document).on('mouseleave', '.iconButton, .color1, .color2', function () {
$(this).removeClass('iconHover')
$('#howto').html("<span style='color: #aaaaaa'>(hover over an icon for info)</span>")
$('#howto').html("<span style='color: #aaa'>(hover over an icon for info)</span>")
})
})
})
@ -657,11 +668,11 @@ fetch('../api/iconkit').then(res => res.json()).then(iconKitData => {
if (!user || !user.length) return $("#steal").hide()
$(`.iconTabButton[form=${formCopy}]`).trigger('click')
$('#glow').attr('src', '../assets/iconkitbuttons/streak_off.png')
$('#glow').attr('src', '/assets/iconkitbuttons/streak_off.png')
$("#steal").hide()
enableGlow = 0
enableGlow = false
let info = await fetch('../api/profile/' + user).then(res => res.json()).catch(e => {})
let info = await fetch('/api/profile/' + user).then(res => res.json()).catch(e => {})
if (info == "-1") info = {}
$(`#${formCopy}-${Math.min(info[formCopy] || 1, $(`.iconButton[form=${formCopy}]`).length)}`).trigger('click')
@ -724,20 +735,21 @@ $('#animationSpeed').on('input', function() {
})
$('#animationSpeedBox').change(function() {
animationMultiplier = Number(Math.abs(Number($(this).val()) || 1).toFixed(2))
if (animationMultiplier > 99) animationMultiplier = 99
else if (animationMultiplier <= 0) animationMultiplier = 0.1
animationMultiplier = Number( Math.abs( Number( $(this).val() ) || 1 ).toFixed(2) )
animationMultiplier = clamp(animationMultiplier, 0, 99)
animationMultiplier ||= 0.1
$('#animationSpeed').val(animationMultiplier)
$('#animationSpeedBox').val(animationMultiplier)
if (icon.complex) icon.animationSpeed = animationMultiplier
})
$(document).keydown(function(k) {
if (k.keyCode == 13) { // enter
// standard and Numpad support
if (k.code.endsWith('Enter')) {
if ($("#steal").is(":visible")) $("#fetchUser").trigger('click')
else if ($(".popup").is(":visible")) return
}
if (k.keyCode == 27) { //esc
if (k.code == 'Escape') {
if ($(".popup").is(":visible")) return $('.popup').hide()
k.preventDefault()
$('#backButton').trigger('click')
@ -751,5 +763,4 @@ $(document).on('click', '.brownbox', function (e) {
$(document).on('click', '.popup', function () {
$('.popup').hide()
})
</script>
</script>

View file

@ -1,9 +1,9 @@
<head>
<title>Leaderboard</title>
<meta charset="utf-8">
<link href="../assets/css/browser.css?v=1" type="text/css" rel="stylesheet">
<link href="/assets/css/browser.css?v=1" type="text/css" rel="stylesheet">
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-135255146-3"></script><script>window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);}gtag('js', new Date());gtag('config', 'UA-135255146-3');</script>
<link rel="icon" href="../assets/trophy.png">
<link rel="icon" href="/assets/trophy.png">
<meta id="meta-title" property="og:title" content="Leaderboards">
<meta id="meta-desc" property="og:description" content="View Geometry Dash's leaderboards, plus an accurate and updated list of the top players.">
<meta id="meta-image" name="og:image" itemprop="image" content="https://gdbrowser.com/assets/trophy.png">
@ -15,74 +15,74 @@
<div id="everything" style="overflow: auto;">
<div id="scoreTabs">
<img src="../assets/tab-top-on.png" class="leaderboardTab" id="topTabOn" style="display: none">
<img src="../assets/tab-top-off.png" class="leaderboardTab leaderboardClick" id="topTabOff">
<img src="/assets/tab-top-on.png" class="leaderboardTab" id="topTabOn" style="display: none">
<img src="/assets/tab-top-off.png" class="leaderboardTab leaderboardClick" id="topTabOff">
<!-- for some GDPS'es -->
<img src="../assets/tab-weekly-on.png" class="sideSpaceC leaderboardTab" id="weeklyTabOn" style="display: none">
<img src="../assets/tab-weekly-off.png" class="sideSpaceC leaderboardTab leaderboardClick" id="weeklyTabOff" style="display: none">
<img src="/assets/tab-weekly-on.png" class="sideSpaceC leaderboardTab" id="weeklyTabOn" style="display: none">
<img src="/assets/tab-weekly-off.png" class="sideSpaceC leaderboardTab leaderboardClick" id="weeklyTabOff" style="display: none">
<img src="../assets/tab-accurate-on.png" class="sideSpaceC leaderboardTab" id="accurateTabOn" style="display: none">
<img src="../assets/tab-accurate-off.png" class="sideSpaceC leaderboardTab leaderboardClick" id="accurateTabOff">
<img src="/assets/tab-accurate-on.png" class="sideSpaceC leaderboardTab" id="accurateTabOn" style="display: none">
<img src="/assets/tab-accurate-off.png" class="sideSpaceC leaderboardTab leaderboardClick" id="accurateTabOff">
<img src="../assets/tab-creators-on.png" class="sideSpaceC leaderboardTab" id="creatorTabOn" style="display: none">
<img src="../assets/tab-creators-off.png" class="sideSpaceC leaderboardTab leaderboardClick" id="creatorTabOff">
<img src="/assets/tab-creators-on.png" class="sideSpaceC leaderboardTab" id="creatorTabOn" style="display: none">
<img src="/assets/tab-creators-off.png" class="sideSpaceC leaderboardTab leaderboardClick" id="creatorTabOff">
</div>
<div class="popup" id="infoDiv">
<div class="fancybox bounce center supercenter" style="width: 80vh">
<h2 class="smaller center" style="font-size: 5.5vh">Leaderboard Info</h2>
<p class="bigger center" id="infoText" style="line-height: 5vh; margin-top: 1.5vh"></p>
<img src="../assets/ok.png" width=20%; class="gdButton center" onclick="$('.popup').hide()">
<img src="/assets/ok.png" width=20%; class="gdButton center" onclick="$('.popup').hide()">
</div>
</div>
<div style="position:absolute; bottom: 0%; left: 0%; width: 100%">
<img class="cornerPiece noClick" src="../assets/corner.png" width=7%;>
<img class="cornerPiece noClick" src="/assets/corner.png" width=7%;>
</div>
<div style="position:absolute; bottom: 0%; right: 0%; width: 100%; text-align: right;">
<a title="Boomlings Leaderboard?????" href="../boomlings"><img id="boomling" style="position: absolute; width: 6%; top: 2%; right: 1%; display: none"></a>
<img class="cornerPiece noClick" src="../assets/corner.png" width=7%; style="transform: scaleX(-1)">
<img class="cornerPiece noClick" src="/assets/corner.png" width=7%; style="transform: scaleX(-1)">
</div>
<div id="searchBox" class="supercenter dragscroll">
<div style="height: 4.5%"></div>
</div>
<div class="leaderboardBox supercenter gs" style="width: 120vh; height: 80%; pointer-events: none">
<div id="relativeUser" class="sortDiv" style="position: relative; left: 100%; transform: translateX(4.5vh); top: 12%; width: 0.1%">
<img class="gdButton" id="findRelative" style="margin-bottom: 1vh" title="Global Search" src="../assets/magnify.png" height="11%">
<img class="gdButton" id="clearRelative" style="margin-bottom: 1vh; display: none" title="Clear Global Search" src="../assets/unmagnify.png" height="11%">
<img class="gdButton" id="findRelative" style="margin-bottom: 1vh" title="Global Search" src="/assets/magnify.png" height="11%">
<img class="gdButton" id="clearRelative" style="margin-bottom: 1vh; display: none" title="Clear Global Search" src="/assets/unmagnify.png" height="11%">
</div>
<div class="sortDiv" style="display: none; position: relative; left: 100%; transform: translateX(4.5vh); top: 12%; width: 0.1%">
<img class="gdButton" id="modSort" style="margin-bottom: 1vh" title="Moderators" src="../assets/sort-mod.png" height="11%">
<img class="gdButton" id="weeklyStats" style="margin-bottom: 1vh" title="Weekly Stats" src="../assets/sort-week.png" height="11%">
<img class="gdButton" id="modSort" style="margin-bottom: 1vh" title="Moderators" src="/assets/sort-mod.png" height="11%">
<img class="gdButton" id="weeklyStats" style="margin-bottom: 1vh" title="Weekly Stats" src="/assets/sort-week.png" height="11%">
</div>
<div class="sortDiv" style="display: none; position: relative; right: 10.5%; top: 0%; width: 0.1%; transform: translateY(-33.3%)" id="statSort">
<img class="gdButton sortButton" style="margin-bottom: 1vh" sort="stars" title="Most stars" src="../assets/sort-stars-on.png" height="11%" id="starSort">
<img class="gdButton sortButton" style="margin-bottom: 1vh" sort="diamonds" title="Most diamonds" src="../assets/sort-diamonds.png" height="11%">
<img class="gdButton sortButton" style="margin-bottom: 1vh" sort="coins" title="Most coins" src="../assets/sort-coins.png" height="11%">
<img class="gdButton sortButton" style="margin-bottom: 1vh" sort="demons" title="Most demons" src="../assets/sort-demons.png" height="11%">
<img class="gdButton sortButton" style="margin-bottom: 1vh; display: none" sort="cp" title="Most creator points" src="../assets/sort-cp.png" height="11%" id="cpSort">
<img class="gdButton sortButton" style="margin-bottom: 1vh" sort="stars" title="Most stars" src="/assets/sort-stars-on.png" height="11%" id="starSort">
<img class="gdButton sortButton" style="margin-bottom: 1vh" sort="diamonds" title="Most diamonds" src="/assets/sort-diamonds.png" height="11%">
<img class="gdButton sortButton" style="margin-bottom: 1vh" sort="coins" title="Most coins" src="/assets/sort-coins.png" height="11%">
<img class="gdButton sortButton" style="margin-bottom: 1vh" sort="demons" title="Most demons" src="/assets/sort-demons.png" height="11%">
<img class="gdButton sortButton" style="margin-bottom: 1vh; display: none" sort="cp" title="Most creator points" src="/assets/sort-cp.png" height="11%" id="cpSort">
</div>
</div>
<div style="position:absolute; top: 2%; left: 1.5%; width: 10%; height: 25%; pointer-events: none">
<img class="gdButton yesClick" id="backButton" src="../assets/back.png" height="30%" onclick="backButton()">
<img class="gdButton yesClick" id="backButton" src="/assets/back.png" height="30%" onclick="backButton()">
</div>
<div style="position:absolute; top: 2.5%; right: 2%; width: 10%; text-align: right;">
<img class="gdButton" src="../assets/smallinfo.png" width="32%" onclick="$('#infoDiv').show()">
<img class="gdButton" src="/assets/smallinfo.png" width="32%" onclick="$('#infoDiv').show()">
</div>
<div id="discordLinks" style="display: none; position:absolute; top: 12%; right: 1.5%; width: 21%; text-align: right;">
<a id="discord" target="_blank" title="Official leaderboard Discord!" href="https://discord.gg/leaderboard"><img class="gdButton" src="../assets/discord.png" width="20%"></a>
<a id="altDiscord" target="_blank" title="Accurate leaderboard Discord!" style="display: none; filter: hue-rotate(300deg)" href="https://discord.gg/Uz7pd4d"><img class="gdButton" src="../assets/discord.png" width="20%"></a>
<a id="discord" target="_blank" title="Official leaderboard Discord!" href="https://discord.gg/leaderboard"><img class="gdButton" src="/assets/discord.png" width="20%"></a>
<a id="altDiscord" target="_blank" title="Accurate leaderboard Discord!" style="display: none; filter: hue-rotate(300deg)" href="https://discord.gg/Uz7pd4d"><img class="gdButton" src="/assets/discord.png" width="20%"></a>
</div>
<div class="supercenter" id="loading" style="height: 10%; top: 47%; display: none;">
<img class="spin noSelect" src="../assets/loading.png" height="105%">
<img class="spin noSelect" src="/assets/loading.png" height="105%">
</div>
<div class="popup" id="userSearch">
@ -90,10 +90,10 @@
<h2 class="smaller center" style="font-size: 5.5vh; margin-top: 1%">User Search</h2>
<p>Enter the <cy>username</cy> of a player to find their position in the <ca>global leaderboard</ca>.</p>
<input type="text" id="relativeName" placeholder="Username" style="height: 8vh; width: 90%; text-align: center; margin-top: 0.5%; margin-bottom: 5%"><br>
<img src="../assets/btn-cancel.png" class="postButton gdButton center" style="width: 32%; margin-right: 1%" onclick="$('#userSearch').hide()">
<img src="../assets/btn-submit.png" class="postButton gdButton center" style="width: 32%; margin-left: 1%" id="relativeSearch">
<img src="/assets/btn-cancel.png" class="postButton gdButton center" style="width: 32%; margin-right: 1%" onclick="$('#userSearch').hide()">
<img src="/assets/btn-submit.png" class="postButton gdButton center" style="width: 32%; margin-left: 1%" id="relativeSearch">
<p id="relativeStatus" style="display: none"></p>
<img class="closeWindow gdButton" src="../assets/close.png" width="13%" style="position: absolute; top: -13.5%; left: -6vh" onclick="$('#userSearch').hide()">
<img class="closeWindow gdButton" src="/assets/close.png" width="13%" style="position: absolute; top: -13.5%; left: -6vh" onclick="$('#userSearch').hide()">
</div>
</div>
</div>
@ -101,35 +101,36 @@
</body>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/6.2.2/browser/pixi.js"></script>
<script type="text/javascript" src="../iconkit/icon.js"></script>
<script type="text/javascript" src="../global.js?v=1"></script>
<script type="text/javascript" src="../dragscroll.js"></script>
<script type="text/javascript" src="/iconkit/icon.js"></script>
<script type="text/javascript" src="/misc/global.js?v=1"></script>
<script type="text/javascript" src="/misc/dragscroll.js"></script>
<script>
"use strict";
let type
let sort = "stars"
let modMode = false
let weekly = false
let showWeek = localStorage.weeklyStats == "1"
let trophies = [1, 3, 10, 25, 50, 75, 100]
let trophies = [1, 3, 10, 25, 50, 75, 100] // it seems this can be optimized into a const Uint8Array
let boomColors = ["red", "orange", "yellow", "green", "teal", "blue", "pink"]
let top250Text =
let top250Text =
`The <cg>Stars</cg> leaderboard contains the <cg>top 100 players</cg>, sorted by <cy>star</cy> value. It was formerly <co>inaccurate</co> but should be much more <cb>reliable</cb> now.`
let topGDPSText =
let topGDPSText =
`The <cg>Stars</cg> leaderboard contains the <cg>top players</cg>, sorted by <cy>star</cy> value.`
let weeklyText =
let weeklyText =
`The <cg>Weekly</cg> leaderboard displays the players who have gained the most <cy>stars</cy> in the <cb>past week</cb>. It was officially <co>removed</co> in update 2.0, but lives on in some GDPS'es.`
let accurateText =
`The <cg>Accurate Leaderboard</cg> is an <cy>externally managed</cy> leaderboard which aims to provide <ca>detailed</ca> and hacker-proof stats on top players. It also once provided a way to view an <cg>accurate</cg> list of players with the most <cy>stars</cy> when the official leaderboards were <ca>frozen</ca>. It is managed by <cb>XShadowWizardX, Pepper360, Octeract</cb>, and many many other helpers.`
let creatorText =
let creatorText =
`The <cg>Creators Leaderboard</cg> is sorted by <cg>creator points</cg>, rather than stars. A player's <cg>creator points</cg> (CP) is calculated by counting their number of <cy>star rated</cy> levels, plus an extra point for every level that has been <cb>featured</cb>, plus an additional point for <co>epic rated</co> levels.`
if (showWeek) $('#weeklyStats').attr('src', '../assets/sort-week-on.png')
if (showWeek) $('#weeklyStats').attr('src', '/assets/sort-week-on.png')
function infoText(text, altDiscord) {
$('#infoText').html(text)
@ -147,7 +148,7 @@ function leaderboard(val, leaderboardParams, scrollTo) {
$('#clearRelative').hide()
$('#loading').show()
Fetch("../api/leaderboard?" + (leaderboardParams || `count=250&${val}&type=${sort}${modMode ? "&mod=1" : ""}`)).then(res => {
Fetch("/api/leaderboard?" + (leaderboardParams || `count=250&${val}&type=${sort}${modMode ? "&mod=1" : ""}`)).then(res => {
if (gdps && !didGDPSStuff) {
didGDPSStuff = true
@ -155,7 +156,7 @@ function leaderboard(val, leaderboardParams, scrollTo) {
$('#accurateTabOn').remove()
$('#accurateTabOff').remove()
Fetch('../api/gdps?current=1').then(ps => {
Fetch('/api/gdps?current=1').then(ps => {
if (weekly) return
else if (ps.weeklyLeaderboard) { $('#weeklyTabOff').show(); weekly = true }
else $('#scoreTabs').css('margin-left', '-29vh')
@ -174,14 +175,14 @@ function leaderboard(val, leaderboardParams, scrollTo) {
$('#searchBox').html(`<div style="height: 4.5%"></div>`)
$('.ranking').remove()
if (modMode && sort == "cp") res = res.sort(function(a, b){return b.cp - a.cp});
if (modMode && sort == "cp") res = res.sort(function(a, b){return b.cp - a.cp})
let wk = type == "weekly"
if ((leaderboardParams ? true : val == type) && res != -1 && res.length) res.forEach((x, y) => {
if ((!!leaderboardParams || val == type) && res != -1 && res.length) res.forEach((x, y) => {
let wp = x.weeklyProgress || {}
let cosmetics = x.cosmetics || {}
let bgCol = cosmetics.bgColor
let bgString = bgCol ? ` style="background-color: rgb(${bgCol.join()})"` : ""
@ -194,32 +195,32 @@ function leaderboard(val, leaderboardParams, scrollTo) {
$('#searchBox').append(`<div class="searchresult leaderboardSlot"${bgString}>
<div class="center ranking">
${x.icon.icon == -1 && type == "accurate" ? `<img class="spaced" src="./assets/trophies/${trophies.findIndex(z => y+1 <= z) + 1}.png" height="150%" style="margin-bottom: 0%; transform:scale(1.1)">` :
${x.icon.icon == -1 && type == "accurate" ? `<img class="spaced" src="./assets/trophies/${trophies.findIndex(z => y+1 <= z) + 1}.png" height="150%" style="margin-bottom: 0%; transform:scale(1.1)">` :
`<gdicon dontload="true" class="leaderboardIcon" iconID=${x.icon.icon} cacheID=${x.playerID} iconForm="${x.icon.form}" col1="${x.icon.col1}" col2="${x.icon.col2}" glow="${x.icon.glow}"></gdicon>`}
<h2 class="slightlySmaller" style="transform: scale(${1 - (Math.max(0, String(x.rank).length - 1) * 0.1)})">${x.rank}</h2>
</div>
<div class="leaderboardSide">
<div class="leaderboardStars">
${x.moderator ? `<img title="${x.moderator == 2 ? "Elder " : ""}Moderator" src="../assets/mod${x.moderator == 2 ? "-elder" : ""}.png" style="width: 9%; cursor: help; padding-right: 1.6%; transform: translateY(0.7vh)">` : ""}
<h2 class="leaderboardName small inline gdButton" style="margin-top: 1.5%${nameString || (x.moderator == 2 ? "; color: #FF9977;" : "")}"><a href="${onePointNine ? `../search/${x.playerID}?user` : `../u/${x.accountID}.`}" accountID="${x.accountID}">${x.username}</a></h2>
<h3 class="inline${x.stars >= 100000 ? " yellow" : ""}" style="margin-left: 4%; margin-top: 2%; font-size: 4.5vh${type == "weekly" ? "; display: none" : ""};">${x.stars} <img class="help valign" src="../assets/star.png"style="width: 4vh; transform: translate(-25%, -10%);" title="Stars"></h3>
${x.moderator ? `<img title="${x.moderator == 2 ? "Elder " : ""}Moderator" src="/assets/mod${x.moderator == 2 ? "-elder" : ""}.png" style="width: 9%; cursor: help; padding-right: 1.6%; transform: translateY(0.7vh)">` : ""}
<h2 class="leaderboardName small inline gdButton" style="margin-top: 1.5%${nameString || (x.moderator == 2 ? "; color: #F97;" : "")}"><a href="${onePointNine ? `../search/${x.playerID}?user` : `../u/${x.accountID}.`}" accountID="${x.accountID}">${x.username}</a></h2>
<h3 class="inline${x.stars >= 100000 ? " yellow" : ""}" style="margin-left: 4%; margin-top: 2%; font-size: 4.5vh${type == "weekly" ? "; display: none" : ""};">${x.stars} <img class="help valign" src="/assets/star.png"style="width: 4vh; transform: translate(-25%, -10%);" title="Stars"></h3>
</div>
<h3 class="lessSpaced leaderboardStats">
${type != "weekly" ? "" : `<span${x.stars >= 1000 ? " class='yellow'" : ""}>+${x.stars}</span> <img class="help valign" src="../assets/star.png" title="Star Gain">`}
${wk || onePointNine ? "" : `<span${x.diamonds >= 65535 ? ` class='blue'>` : ">"}${x.diamonds}</span> <img class="help valign" src="../assets/diamond.png" title="Diamonds">`}
${wk ? "&nbsp;" : `<span${x.coins >= 149 ? " class='yellow'" : ""}>${x.coins}</span> <img class="help valign" src="../assets/coin.png" title="Secret Coins">`}
${wk || onePointNine ? "" : `<span${x.userCoins >= 10000 ? " class='brightblue'" : ""}>${x.userCoins}</span> <img class="help valign" src="../assets/silvercoin.png" title="User Coins">`}
${wk ? "" : `<span${x.demons >= 1000 ? " class='brightred'" : ""}>${x.demons}</span> <img class="help valign" src="../assets/demon.png" title="Demons">`}
${x.cp <= 0 ? "" : `<span${x.cp >= 100 ? " class='yellow'" : ""}>${x.cp}</span> <img class="help valign" src="../assets/cp.png" title="Creator Points">`}
${type != "weekly" ? "" : `<span${x.stars >= 1000 ? " class='yellow'" : ""}>+${x.stars}</span> <img class="help valign" src="/assets/star.png" title="Star Gain">`}
${wk || onePointNine ? "" : `<span${x.diamonds >= 65535 ? ` class='blue'>` : ">"}${x.diamonds}</span> <img class="help valign" src="/assets/diamond.png" title="Diamonds">`}
${wk ? "&nbsp;" : `<span${x.coins >= 149 ? " class='yellow'" : ""}>${x.coins}</span> <img class="help valign" src="/assets/coin.png" title="Secret Coins">`}
${wk || onePointNine ? "" : `<span${x.userCoins >= 10000 ? " class='brightblue'" : ""}>${x.userCoins}</span> <img class="help valign" src="/assets/silvercoin.png" title="User Coins">`}
${wk ? "" : `<span${x.demons >= 1000 ? " class='brightred'" : ""}>${x.demons}</span> <img class="help valign" src="/assets/demon.png" title="Demons">`}
${x.cp <= 0 ? "" : `<span${x.cp >= 100 ? " class='yellow'" : ""}>${x.cp}</span> <img class="help valign" src="/assets/cp.png" title="Creator Points">`}
</h3>
<h3 class="lessSpaced leaderboardStats weeklyStuff"}>
<span${wp.diamonds >= 250 ? " class='blue'" : ""}>${wp.diamonds >= 0 ? "+" : ""}${wp.diamonds}</span> <img class="help valign" src="../assets/diamond.png" title="Diamond Gain">
<span${wp.stars >= 1000 ? " class='yellow'" : ""}>${wp.stars >= 0 ? "+" : ""}${wp.stars}</span> <img class="help valign" src="../assets/star.png" title="Star Gain">
<span${wp.userCoins >= 250 ? " class='brightblue'" : ""}>${wp.userCoins >= 0 ? "+" : ""}${wp.userCoins}</span> <img class="help valign" src="../assets/silvercoin.png" title="User Coin Gain">
<span${wp.demons >= 25 ? " class='brightred'" : ""}>${wp.demons >= 0 ? "+" : ""}${wp.demons}</span> <img class="help valign" src="../assets/demon.png" title="Demon Gain">
<span${wp.diamonds >= 250 ? " class='blue'" : ""}>${wp.diamonds >= 0 ? "+" : ""}${wp.diamonds}</span> <img class="help valign" src="/assets/diamond.png" title="Diamond Gain">
<span${wp.stars >= 1000 ? " class='yellow'" : ""}>${wp.stars >= 0 ? "+" : ""}${wp.stars}</span> <img class="help valign" src="/assets/star.png" title="Star Gain">
<span${wp.userCoins >= 250 ? " class='brightblue'" : ""}>${wp.userCoins >= 0 ? "+" : ""}${wp.userCoins}</span> <img class="help valign" src="/assets/silvercoin.png" title="User Coin Gain">
<span${wp.demons >= 25 ? " class='brightred'" : ""}>${wp.demons >= 0 ? "+" : ""}${wp.demons}</span> <img class="help valign" src="/assets/demon.png" title="Demon Gain">
</h3>
</div>
@ -249,7 +250,7 @@ function leaderboard(val, leaderboardParams, scrollTo) {
}).catch(e => {console.log(e); $('#loading').hide();})
}
// $('#boomling').attr('src', `../assets/boomlings/${boomColors[Math.floor(Math.random() * boomColors.length)]}.png`)
// $('#boomling').attr('src', `../assets/boomlings/${boomColors[randInt(0, boomColors.length)]}.png`)
$(document).on('click', '.sortButton', function () {
if ($('#loading').is(":visible")) return
@ -261,7 +262,7 @@ $(document).on('click', '.sortButton', function () {
})
$('#topTabOff').click(function() {
if (type == "top") return;
if (type == "top") return
type = "top"
leaderboard(type)
$('.leaderboardTab').hide();
@ -274,7 +275,7 @@ $('#topTabOff').click(function() {
})
$('#accurateTabOff').click(function() {
if (type == "accurate") return;
if (type == "accurate") return
type = "accurate"
leaderboard(type)
$('.leaderboardTab').hide();
@ -287,7 +288,7 @@ $('#accurateTabOff').click(function() {
})
$('#weeklyTabOff').click(function() {
if (type == "weekly" || !gdps) return;
if (type == "weekly" || !gdps) return
type = "weekly"
leaderboard(type)
$('.leaderboardTab').hide();
@ -299,7 +300,7 @@ $('#weeklyTabOff').click(function() {
})
$('#creatorTabOff').click(function() {
if (type == "creator") return;
if (type == "creator") return
type = "creator"
leaderboard(type)
$('.leaderboardTab').hide();
@ -313,7 +314,7 @@ $('#creatorTabOff').click(function() {
$('#modSort').click(function() {
modMode = !modMode
$(this).attr('src', `../assets/sort-mod${modMode ? "-on" : ""}.png`)
if (modMode) {
if (modMode) {
$('#cpSort').show();
$('#statSort').css('transform', 'translateY(-26.7%')
}
@ -348,7 +349,7 @@ $('#relativeSearch').click(function() {
let relativeUsername = $('#relativeName').val()
if (relativeLoading || !relativeUsername) return
relativeLoading = true
Fetch("../api/profile/" + relativeUsername).then(foundUser => {
Fetch("/api/profile/" + relativeUsername).then(foundUser => {
if (foundUser && foundUser.accountID && foundUser.rank) {
leaderboard(null, "type=relative&accountID=" + foundUser.accountID, foundUser.accountID)
$('#userSearch').hide()

View file

@ -1,9 +1,9 @@
<head>
<title>[[NAME]] ([[ID]])</title>
<meta charset="utf-8">
<link href="../assets/css/browser.css?v=1" type="text/css" rel="stylesheet">
<link href="/assets/css/browser.css?v=1" type="text/css" rel="stylesheet">
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-135255146-3"></script><script>window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);}gtag('js', new Date());gtag('config', 'UA-135255146-3');</script>
<link rel="icon" href="../assets/difficulties/[[DIFFICULTYFACE]].png">
<link rel="icon" href="/assets/difficulties/[[DIFFICULTYFACE]].png">
<meta id="meta-title" property="og:title" content="[[NAME]] by [[AUTHOR]]">
<meta id="meta-desc" property="og:description" content="ID: [[ID]] | Stars: [[STARS]] | Difficulty: [[DIFFICULTY]] | Downloads: [[DOWNLOADS]] | Likes: [[LIKES]] | Length: [[LENGTH]] | Song: [[SONGNAME]] ([[SONGID]])">
<meta id="meta-image" name="og:image" itemprop="image" content="https://gdbrowser.com/assets/difficulties/[[DIFFICULTYFACE]].png">
@ -24,7 +24,7 @@
<br>GD Version: <cy>[[GAMEVERSION]]</cy>
[[OBJECTINFO]][[REQUESTED]]
</p>
<img src="../assets/ok.png" width=20%; class="gdButton center closeWindow">
<img src="/assets/ok.png" width=20%; class="gdButton center closeWindow">
</div>
</div>
@ -34,7 +34,7 @@
<p class="bigger center" style="line-height: 5vh; margin-top: 1.5vh;">
<cy class="pre">[[NAME]]</cy> has been added to your <a class="youCanClickThis2" style="color:lime" href="../search/levels?type=saved">saved levels</a> list.
</p>
<img src="../assets/ok.png" width=20%; class="gdButton center" onclick="savedLevel()">
<img src="/assets/ok.png" width=20%; class="gdButton center" onclick="savedLevel()">
</div>
</div>
@ -44,8 +44,8 @@
<p class="bigger center" style="line-height: 5vh; margin-top: 1.5vh;">
Are you sure you want to <cr>delete</cr> this level from your <a class="youCanClickThis2"style="color:lime" href="../search/levels?type=saved">saved levels </a>list?
</p>
<img src="../assets/btn-no.png" height=25%; class="gdButton center closeWindow">
<img src="../assets/btn-yes.png" height=25%; class="gdButton center sideSpaceB" onclick="deleteLevel()">
<img src="/assets/btn-no.png" height=25%; class="gdButton center closeWindow">
<img src="/assets/btn-yes.png" height=25%; class="gdButton center sideSpaceB" onclick="deleteLevel()">
</div>
</div>
@ -55,15 +55,15 @@
<p class="bigger center" style="line-height: 5vh; margin-top: 1.5vh;">
<cy>Level analysis</cy> is currently <cr>blocked</cr> by <ca>RobTop</ca>. We don't know when or if it will be re-enabled.<br><a class="youCanClickThis" style="color:aqua" href="./analyze/[[ID]]">(click to try anyways)</a>
</p>
<img src="../assets/ok.png" width=20%; class="gdButton center" onclick="$('.popup').hide()">
<img src="/assets/ok.png" width=20%; class="gdButton center" onclick="$('.popup').hide()">
</div>
</div>
<!-- <div class="popup" id="likeDiv">
<div class="brownbox bounce center supercenter" style="height: 75%; width: 100vh">
<h1 class="smaller center" style="font-size: 5.5vh">Vote</h1>
<img src="../assets/smashLike.png" id="likebtn" class="inline gdButton likeButton">
<img src="../assets/smashDislike.png" id="dislikebtn" class="inline gdButton likeButton youAreNotTheOne">
<img src="/assets/smashLike.png" id="likebtn" class="inline gdButton likeButton">
<img src="/assets/smashDislike.png" id="dislikebtn" class="inline gdButton likeButton youAreNotTheOne">
<form action="nothing lol">
<h3 class="center">GD Username</h3>
<input type="text" name="gdbrowser" id="like-username" maxlength="50" style="height: 8vh; width: 90%; text-align: center; margin-top: 0.5%">
@ -73,17 +73,17 @@
<div style="min-height: 18%; max-height: 18%">
<p id="message" style="padding: 0% 10%; margin-top: 2.5%"></p>
</div>
<img src="../assets/btn-cancel.png" height=10%; class="postButton gdButton center" style="margin-right: 1%" onclick="$('#likeDiv').hide(); $('#likebtn').trigger('click');">
<img src="../assets/btn-submit.png" type="submit" height=10%; class="postButton gdButton center" style="margin-left: 1%" id="submitVote">
<img src="/assets/btn-cancel.png" height=10%; class="postButton gdButton center" style="margin-right: 1%" onclick="$('#likeDiv').hide(); $('#likebtn').trigger('click');">
<img src="/assets/btn-submit.png" type="submit" height=10%; class="postButton gdButton center" style="margin-left: 1%" id="submitVote">
</div>
</div> -->
<div style="position:absolute; bottom: 0%; left: 0%; width: 100%">
<img class="cornerPiece" src="../assets/corner.png" width=7%;>
<img class="cornerPiece" src="/assets/corner.png" width=7%;>
</div>
<div style="position:absolute; bottom: 0%; right: 0%; width: 100%; text-align: right;">
<img class="cornerPiece" src="../assets/corner.png" width=7%; style="transform: scaleX(-1)">
<img class="cornerPiece" src="/assets/corner.png" width=7%; style="transform: scaleX(-1)">
</div>
<div class="center" style="width: 70%; margin: 1% auto">
@ -93,31 +93,31 @@
<div class="center" style="position:absolute; top: 9%; left: 0%; right: 0%; height: 5%;">
<h2 class="pre inline slightlySmaller normalCursor gdButton" id="authorName"><a class="linkButton" id="authorLink" href="../u/[[ACCOUNTID]].">By [[AUTHOR]]</a></h2>
<h2 class="inline slightlySmaller normalCursor sideSpaceC">
<img class="inline valign" id="copiedBadge" style="display: none; height: 60%; cursor:help" src="../assets/copied.png" title="Level is a copy or a collaboration">
<img class="inline valign" id="largeBadge" style="display: none; height: 60%; margin-left: -7%; cursor:help" src="../assets/large.png" title="Contains more than 40,000 objects">
<img class="inline valign" id="2pBadge" style="display: none; height: 60%; margin-left: -7%; cursor:help" src="../assets/twoPlayer.png" title="Two player level">
<img class="inline valign" id="copiedBadge" style="display: none; height: 60%; cursor:help" src="/assets/copied.png" title="Level is a copy or a collaboration">
<img class="inline valign" id="largeBadge" style="display: none; height: 60%; margin-left: -7%; cursor:help" src="/assets/large.png" title="Contains more than 40,000 objects">
<img class="inline valign" id="2pBadge" style="display: none; height: 60%; margin-left: -7%; cursor:help" src="/assets/twoPlayer.png" title="Two player level">
</h2><br>
<img class="inline spaced dailyLevel" id="dailyIcon" style="height:90%; display: none; margin-right: 0.5%; margin-top: 0.4%; vertical-align: middle;">
<h1 class="inline smallerer spaced dailyLevel" style="display: none; margin: 0 0 0 0">#[[DAILYNUMBER]]<span style="font-size: 2.5vh; vertical-align: middle;" class="h3Size" id="dailyTime"></span></h1>
</div>
<div class="center valign" style="position:absolute; top: 10.5%; left: 27%; transform:scale(0.8); height: 100%; width: 20%">
<img class="spaced" src="../assets/difficulties/[[DIFFICULTYFACE]].png" height="15%" style="margin-bottom: 0%">
<img class="spaced" src="/assets/difficulties/[[DIFFICULTYFACE]].png" height="15%" style="margin-bottom: 0%">
<h1 class="smaller" id="difficultytext" style="transform:scale(0.9);">[[DIFFICULTY]]</h1>
<h1 class="smaller inline stars" style="transform:scale(0.9)">[[STARS]]</h1> <img class="inline stars" src="../assets/star.png" height=4%; style="transform:translateY(-12%)"><br class="stars">
<h1 class="smaller inline diamonds" style="transform:scale(0.9)">[[DIAMONDS]]</h1> <img class="inline diamonds" src="../assets/diamond.png" height=4%; style="transform:translateY(-12%)">
<h1 class="smaller inline demonList" style="transform:scale(0.9); display: none;">#[[DEMONLIST]]</h1> <img class="inline demonList" src="../assets/demon.png" height=4.5%; style="transform:translateY(-7%); display: none; margin-left: 1.5%">
<h1 class="smaller inline stars" style="transform:scale(0.9)">[[STARS]]</h1> <img class="inline stars" src="/assets/star.png" height=4%; style="transform:translateY(-12%)"><br class="stars">
<h1 class="smaller inline diamonds" style="transform:scale(0.9)">[[DIAMONDS]]</h1> <img class="inline diamonds" src="/assets/diamond.png" height=4%; style="transform:translateY(-12%)">
<h1 class="smaller inline demonList" style="transform:scale(0.9); display: none;">#[[DEMONLIST]]</h1> <img class="inline demonList" src="/assets/demon.png" height=4.5%; style="transform:translateY(-7%); display: none; margin-left: 1.5%">
<div id="coins" style="margin-top: 3%"></div>
</div>
<div style="position:absolute; top: 20%; right: 0%; height: 38%; width: 40%; line-height: 8.5vh">
<img class="valign inline spaced" src="../assets/download.png" height=15% style="margin-right: 2%">
<img class="valign inline spaced" src="/assets/download.png" height=15% style="margin-right: 2%">
<h1 class="valign inline smaller spaced">[[DOWNLOADS]]</h1><br>
<img id="likeImg" class="valign inline spaced" src="../assets/like.png" height=15% style="margin-right: 2%">
<img id="likeImg" class="valign inline spaced" src="/assets/like.png" height=15% style="margin-right: 2%">
<h1 class="valign inline smaller spaced">[[LIKES]]</h1><br>
<img class="valign inline spaced" src="../assets/time.png" height=15% style="margin-right: 2%">
<img class="valign inline spaced" src="/assets/time.png" height=15% style="margin-right: 2%">
<h1 class="valign inline smaller spaced">[[LENGTH]]</h1><br>
<img class="valign inline spaced orbs" src="../assets/orbs.png" height=15% style="margin-right: 2%">
<img class="valign inline spaced orbs" src="/assets/orbs.png" height=15% style="margin-right: 2%">
<h1 class="valign inline smaller spaced orbs">[[ORBS]]</h1>
</div>
@ -129,16 +129,16 @@
<div style="margin-left: 0.5%">
<h1 class="pre slightlySmaller" id="songname">[[SONGNAME]]</h1>
<h2 class="pre smaller">By: [[SONGAUTHOR]]<!--
--><img id="scout" title="Artist is scouted" style="display: none; margin-left: 1.5%; filter: hue-rotate(-55deg);" class="help artistIcon valign" src="../assets/check.png" width="5%"><!--
--><img id="whitelist" title="Artist is whitelisted" style="display: none" class="help artistIcon valign" src="../assets/check.png" width="5%"><!--
--> <a class="songLink" href="https://[[SONGAUTHOR]].newgrounds.com" target="_blank"><img id="moreSongs" class="gdButton valign" src="../assets/more.png" width="12%"></a></h2>
<img id="checkSong" style="display: none; margin-top: 1%" title="Check song verification" class="gdButton valign" src="../assets/btn-check.png" width="16%">
--><img id="scout" title="Artist is scouted" style="display: none; margin-left: 1.5%; filter: hue-rotate(-55deg);" class="help artistIcon valign" src="/assets/check.png" width="5%"><!--
--><img id="whitelist" title="Artist is whitelisted" style="display: none" class="help artistIcon valign" src="/assets/check.png" width="5%"><!--
--> <a class="songLink" href="https://[[SONGAUTHOR]].newgrounds.com" target="_blank"><img id="moreSongs" class="gdButton valign" src="/assets/more.png" width="12%"></a></h2>
<img id="checkSong" style="display: none; margin-top: 1%" title="Check song verification" class="gdButton valign" src="/assets/btn-check.png" width="16%">
<h3 id="songLoading" style="display: none; margin-top: 0.5%;">Loading...</h3>
<h3 id="songAllowed" style="display: none; margin-top: 0.5%; color: lime">Song is allowed for use</h3>
<h3 id="songNotAllowed" style="display: none; margin-top: 0.5%; color: red">Song is not allowed for use</h3>
<h3 id="artistInfo" style="display: none; margin-top: 0.5%"></h3>
</div>
<a class="songLink" href="https://www.newgrounds.com/audio/listen/[[SONGID]]" target="_blank"><img class="gdButton sideButton" title="Play Song" id="playSong" src="../assets/playsong.png" style="position:absolute; right: 1%; top: 50%; width: 11%; height: auto;"></a>
<a class="songLink" href="https://www.newgrounds.com/audio/listen/[[SONGID]]" target="_blank"><img class="gdButton sideButton" title="Play Song" id="playSong" src="/assets/playsong.png" style="position:absolute; right: 1%; top: 50%; width: 11%; height: auto;"></a>
</div>
<div class="center" style="position:absolute; bottom: 5%; left: 0; right: 0">
@ -146,20 +146,20 @@
</div>
<div class="center noClick" style="position:absolute; top: 24%; right: 0%; min-width: 100%">
<img class="gdButton yesClick" id="playButton" src="../assets/play.png" width="11%">
<img class="gdButton yesClick" id="playButton" src="/assets/play.png" width="11%">
</div>
<div class="levelButtons" style="position:absolute; top: 46%; right: 3.5%; transform: translateY(-50%); height: 75%;">
<img class="gdButton sideButton" title="Save level" id="saveButton" src="../assets/plus.png" onclick="$('#saveDiv').show(); saveLevel()"><br>
<img class="gdButton sideButton" title="Level Info" id="infoButton" src="../assets/info.png" onclick="$('#infoDiv').show()"><br>
<!-- <img class="gdButton sideButton" title="Rate Level" id="likeButton" src="../assets/vote.png" onclick="$('#likeDiv').show()"><br> -->
<a href="./analyze/[[ID]]" id="analyzeLink"><img id="analyzeBtn" title="Analyze Level" class="gdButton sideButton" src="../assets/edit.png"></a><br>
<a href="./comments/[[ID]]"><img class="gdButton sideButton" title="View Comments" src="../assets/comment.png"></a><br>
<a href="./leaderboard/[[ID]]"><img id="leaderboardbtn" title="View Leaderboard" class="gdButton sideButton" src="../assets/leaderboard.png"></a><br>
<img class="gdButton sideButton" title="Save level" id="saveButton" src="/assets/plus.png" onclick="$('#saveDiv').show(); saveLevel()"><br>
<img class="gdButton sideButton" title="Level Info" id="infoButton" src="/assets/info.png" onclick="$('#infoDiv').show()"><br>
<!-- <img class="gdButton sideButton" title="Rate Level" id="likeButton" src="/assets/vote.png" onclick="$('#likeDiv').show()"><br> -->
<a href="./analyze/[[ID]]" id="analyzeLink"><img id="analyzeBtn" title="Analyze Level" class="gdButton sideButton" src="/assets/edit.png"></a><br>
<a href="./comments/[[ID]]"><img class="gdButton sideButton" title="View Comments" src="/assets/comment.png"></a><br>
<a href="./leaderboard/[[ID]]"><img id="leaderboardbtn" title="View Leaderboard" class="gdButton sideButton" src="/assets/leaderboard.png"></a><br>
</div>
<div style="position:absolute; top: 2%; left: 1.5%; width: 10%; height: 25%; pointer-events: none">
<img class="gdButton yesClick" id="backButton" src="../assets/back.png" height="30%" onclick="backButton()">
<img class="gdButton yesClick" id="backButton" src="/assets/back.png" height="30%" onclick="backButton()">
</div>
<div id="copied" style="display: none;">
<div class="copied center noClick" style="position:absolute; top: 36%; left: 50%; transform: translate(-50%,-50%); width: 90vh">
@ -171,7 +171,7 @@
</body>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script type="text/javascript" src="../global.js?v=1"></script>
<script type="text/javascript" src="/misc/global.js?v=1"></script>
<script>
let messageText = 'Your <cy>Geometry Dash password</cy> will <cg>not be stored</cg> anywhere on the site, both <ca>locally and server-side.</ca> You can view the code used for liking a level <a class="menuLink" target="_blank" href="https://github.com/GDColon/GDBrowser/blob/master/api/post/like.js">here</a>.'
@ -193,22 +193,22 @@ if (window.location.href.endsWith('?download')) $('#infoDiv').show()
"Get a life", "...", "bruh moment", "Etched into thy's memory!", "h", "I feel physical pain", "Play it in GD!", "[[ID]]", "go away", "Every copy costs 2 cents!", "Un-copied!", "Copied++", "C O P I E D", "help me please", "Open GD to play the level!", "pretend you're playing it", "Anotha one!"]
let copies = 0
let animated = false;
let freeze = false;
let animated = false
let freeze = false
let dailyTime = Number('[[NEXTDAILY]]') || null
$('#playButton').click(function () {
if (!($('#copied').is(':animated')) && !animated) {
animated = true;
animated = true
copies += 1
$('#copied').stop().fadeIn(200).delay(500).fadeOut(500, function() {
animated = false
if (copies > 1) $('#copiedText').text(copyMessages[Math.floor(Math.random()*(copies > 4 ? copyMessages.length : 6))].replace("[C]", copies))
if (copies > 1) $('#copiedText').text(copyMessages[randInt(0, copies > 4 ? copyMessages.length : 6)].replace("[C]", copies))
})
}
var temp = $("<input>");
$("body").append(temp);
temp.val('[[ID]]').select();
$("body").append(temp)
temp.val('[[ID]]').select()
document.execCommand("copy"); temp.remove()
})
$('.closeWindow').click(function () { if (!freeze) $(".popup").attr('style', 'display: none;') })
@ -238,7 +238,7 @@ else {
.replace('[[TIME1]]', "")
.replace('[[TIME2]]', "")
.replace('[[UPLOAD]]', "")
.replace('[[UPDATE]]', "") +
.replace('[[UPDATE]]', "") +
`<br><a id="additional" class="youCanClickThis" href="/[[ID]]?download"><ca>Download additional info</ca></a>`
)}
@ -251,14 +251,13 @@ if ([[DIAMONDS]] == 0 || !'[[DEMONLIST]]'.startsWith("[")) $('.diamonds').hide()
if (!'[[DEMONLIST]]'.startsWith("[")) {
$('.demonList').show()
$('#leaderboardbtn').attr('src', '../assets/demonleaderboard.png').parent().attr('href', '../demon/[[DEMONLIST]]')
$('#leaderboardbtn').attr('src', '/assets/demonleaderboard.png').parent().attr('href', '../demon/[[DEMONLIST]]')
}
else $('.demonList').remove()
if (!'[[DAILYNUMBER]]'.startsWith("[")) {
$('#dailyIcon').attr('src', `../assets/crown-${'[[WEEKLY]]'.startsWith("[") ? "daily" : "weekly"}.png`)
$('.dailyLevel').show()
if (dailyTime) {
$('#dailyTime').html(` (${colonize(dailyTime, true)})`)
setInterval(() => {
github-advanced-security[bot] commented 2022-07-03 13:52:58 -04:00 (Migrated from github.com)
Review

External links without noopener/noreferrer are a potential security risk.

Show more details

## Potentially unsafe external link External links without noopener/noreferrer are a potential security risk. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/75)
Rudxain commented 2022-07-03 14:09:29 -04:00 (Migrated from github.com)
Review

Is this because of using "/" instead of "../"?

Is this because of using "/" instead of "../"?
@ -282,10 +281,10 @@ if (!"[[GDPS]]".startsWith("[")) {
}
$('#leaderboardbtn').hide()
$('#checkSong').remove()
}
}
if ("[[SONGAUTHOR]]" == "Unknown" || "[[INVALIDSONG]]" == "true") $('.songLink').hide()
if ("[[DISLIKED]]" == "true") $('#likeImg').attr('src', '../assets/dislike.png').css('transform', 'translateY(20%)')
if ("[[DISLIKED]]" == "true") $('#likeImg').attr('src', '/assets/dislike.png').css('transform', 'translateY(20%)')
if ($("#additional").hasClass('downloadDisabled')) {
$('#analyzeLink').removeAttr('href')
@ -295,9 +294,9 @@ if ($("#additional").hasClass('downloadDisabled')) {
}
let coinColor = [[VERIFIEDCOINS]] ? "silvercoin" : "browncoin"
if ([[COINS]] > 0) $("#coins").append(`<img src="../assets/${coinColor}.png" height="5%">`)
if ([[COINS]] > 1) $("#coins").append(`<img class="squeeze" src="../assets/${coinColor}.png" height="5%">`)
if ([[COINS]] > 2) $("#coins").append(`<img class="squeeze" src="../assets/${coinColor}.png" height="5%">`)
if ([[COINS]] > 0) $("#coins").append(`<img src="/assets/${coinColor}.png" height="5%">`)
if ([[COINS]] > 1) $("#coins").append(`<img class="squeeze" src="/assets/${coinColor}.png" height="5%">`)
if ([[COINS]] > 2) $("#coins").append(`<img class="squeeze" src="/assets/${coinColor}.png" height="5%">`)
if ("[[GDPS]]".startsWith("1.9/")) $("#authorLink").attr('href', '/search/[[PLAYERID]]?user')
@ -319,43 +318,43 @@ $(window).on('load', function() {
if (overflow > 3) overflow = 3
$('#songname').addClass('smaller').css('font-size', (6 - (overflow)) + 'vh')
}
});
})
let savedLevels = JSON.parse(localStorage.getItem('saved') || '[]');
let deleteMode = false;
if (savedLevels.includes('[[ID]]')) $('#saveButton').attr('src', '../assets/delete.png').attr('onclick', '$("#deleteDiv").show()')
let savedLevels = JSON.parse(localStorage.getItem('saved') || '[]')
let deleteMode = false
if (savedLevels.includes('[[ID]]')) $('#saveButton').attr('src', '/assets/delete.png').attr('onclick', '$("#deleteDiv").show()')
function saveLevel() {
savedLevels.push('[[ID]]');
savedLevels.push('[[ID]]')
localStorage.setItem('saved', JSON.stringify(savedLevels));
}
function savedLevel() {
$('#saveButton').attr('src', '../assets/delete.png').attr('onclick', '$("#deleteDiv").show()')
$('#saveButton').attr('src', '/assets/delete.png').attr('onclick', '$("#deleteDiv").show()')
$('.popup').hide()
}
function deleteLevel() {
savedLevels = savedLevels.filter(function(el) {return el != '[[ID]]'})
localStorage.setItem('saved', JSON.stringify(savedLevels));
$('#saveButton').attr('src', '../assets/plus.png').attr('onclick', '$("#saveDiv").show(); saveLevel()')
$('#saveButton').attr('src', '/assets/plus.png').attr('onclick', '$("#saveDiv").show(); saveLevel()')
$('.popup').hide()
}
$('#checkSong').click(function() {
$('#checkSong').hide()
$('#songLoading').show()
fetch(`../api/song/[[SONGID]]`).then(res => res.json()).then(info => {
fetch(`/api/song/[[SONGID]]`).then(res => res.json()).then(info => {
$('#songLoading').hide()
$(info && info != -1 ? '#songAllowed' : '#songNotAllowed').show().addClass('songStatus')
// if (info.error) return
// if (!info.artist.scouted) {
// $('#scout').attr('src', '../assets/x.png')
// $('#scout').attr('src', '/assets/x.png')
// $('#scout').attr('title', $('#scout').attr('title').replace('is ', 'is NOT '))
// $('#scout').css('filter', 'saturate(0.4)')
// }
// if (!info.artist.whitelisted) {
// $('#whitelist').attr('src', '../assets/x.png')
// $('#whitelist').attr('src', '/assets/x.png')
// $('#whitelist').attr('title', $('#whitelist').attr('title').replace('is ', 'is NOT '))
// }
// $('#scout').show(); $('#whitelist').show()
@ -375,7 +374,7 @@ $('.artistIcon').hover(function() {
// let like;
// let likedLevels = localStorage.likedLevels ? JSON.parse(localStorage.likedLevels) : []
// if (likedLevels.includes('[[ID]]')) $('#likeButton').attr('src', '../assets/voted.png').removeClass('gdButton').prop("onclick", null)
// if (likedLevels.includes('[[ID]]')) $('#likeButton').attr('src', '/assets/voted.png').removeClass('gdButton').prop("onclick", null)
// $('#likebtn').click(function() {
// $('#likebtn').removeClass('youAreNotTheOne')
@ -405,7 +404,7 @@ $('.artistIcon').hover(function() {
// $('.postbutton').hide()
// allowEsc = false
// fetch(`../api/profile/${username}`).then(res => res.json()).then(res => {
// fetch(`/api/profile/${username}`).then(res => res.json()).then(res => {
// if (!res || res == "-1") {$('.postbutton').show(); return $('#message').text("The username you provided doesn't exist!")}
// else accountID = res.accountID
@ -414,7 +413,7 @@ $('.artistIcon').hover(function() {
// likedLevels.push('[[ID]]')
// localStorage.setItem('likedLevels', JSON.stringify(likedLevels))
// location.reload()
// })
// })
// .fail(e => {$('.postbutton').show();$('#message').text(e.responseText.includes("DOCTYPE") ? "Something went wrong..." : e.responseText)})
// })
// })

View file

@ -1,9 +1,9 @@
<head>
<title>Leaderboard</title>
<meta charset="utf-8">
<link href="../assets/css/browser.css?v=1" type="text/css" rel="stylesheet">
<link href="/assets/css/browser.css?v=1" type="text/css" rel="stylesheet">
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-135255146-3"></script><script>window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);}gtag('js', new Date());gtag('config', 'UA-135255146-3');</script>
<link rel="icon" href="../assets/trophy.png">
<link rel="icon" href="/assets/trophy.png">
<meta id="meta-title" property="og:title" content="Level Leaderboard">
<meta id="meta-desc" property="og:description" content="View the leaderboard of a Geometry Dash level!">
<meta id="meta-image" name="og:image" itemprop="image" content="https://gdbrowser.com/assets/trophy.png">
@ -15,17 +15,17 @@
<div id="everything" style="overflow: auto;">
<div style="position:absolute; bottom: 0%; left: 0%; width: 100%">
<img class="cornerPiece" src="../assets/corner.png" width=7%;>
<img class="cornerPiece" src="/assets/corner.png" width=7%;>
</div>
<div style="position:absolute; bottom: 0%; right: 0%; width: 100%; text-align: right;">
<img class="cornerPiece" src="../assets/corner.png" width=7%; style="transform: scaleX(-1)">
<img class="cornerPiece" src="/assets/corner.png" width=7%; style="transform: scaleX(-1)">
</div>
<div id="searchBox" class="supercenter dragscroll"; style="width: 124vh">
<div style="height: 4.5%"></div>
</div>
<div class="epicbox supercenter gs" style="width: 126vh; height: 80%; pointer-events: none"></div>
<div class="center" style="position:absolute; top: 8%; left: 0%; right: 0%">
@ -33,13 +33,13 @@
</div>
<div style="position:absolute; top: 2%; left: 1.5%; width: 10%; height: 25%; pointer-events: none">
<img class="gdButton yesClick" id="backButton" src="../assets/back.png" height="30%" onclick="backButton()">
<img class="gdButton yesClick" id="backButton" src="/assets/back.png" height="30%" onclick="backButton()">
</div>
<div class="supercenter" id="loading" style="height: 10%; top: 47%; display: none;">
<img class="spin noSelect" src="../assets/loading.png" height="105%">
<img class="spin noSelect" src="/assets/loading.png" height="105%">
</div>
<div id="error" class="supercenter" style="height: 20%; top: 47%; display: none;">
<h1 class="center">No scores available...</h1>
<h3 class="center" style="margin-top: 2%">Either this leaderboard is empty, or the scores weren't able to be obtained from the GD servers.</h3>
@ -47,11 +47,11 @@
</div>
<div class="supercenter" style="left: 87%; top: 24%; height: 10%">
<img class="gdButton darken" id="topMode" src="../assets/leaderboard-top.png" height="90%">
<img class="gdButton darken" id="topMode" src="/assets/leaderboard-top.png" height="90%">
</div>
<div class="supercenter" style="left: 87%; top: 35%; height: 10%">
<img class="gdButton" id="weekMode" src="../assets/leaderboard-week.png" height="90%">
<img class="gdButton" id="weekMode" src="/assets/leaderboard-week.png" height="90%">
</div>
</div>
@ -59,12 +59,12 @@
</body>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/6.2.2/browser/pixi.js"></script>
<script type="text/javascript" src="../iconkit/icon.js"></script>
<script type="text/javascript" src="../global.js?v=1"></script>
<script type="text/javascript" src="../dragscroll.js"></script>
<script type="text/javascript" src="/iconkit/icon.js"></script>
<script type="text/javascript" src="/misc/global.js?v=1"></script>
<script type="text/javascript" src="/misc/dragscroll.js"></script>
<script>
let loading = false;
let loading = false
let lvlID = Math.round(window.location.pathname.split('/')[2])
if (!lvlID || lvlID > 99999999 || lvlID < -99999999) window.location.href = window.location.href.replace("leaderboard", "search")
@ -78,7 +78,7 @@ function leaderboard() {
loading = true;
$('#loading').show()
fetch(`../api/level/${lvlID}`).then(lvl => lvl.json()).then(lvl => {
fetch(`/api/level/${lvlID}`).then(lvl => lvl.json()).then(lvl => {
if (lvl == "-1") return $('#header').html("Nonexistent level " + lvlID)
document.title = "Leaderboards for " + lvl.name
@ -86,9 +86,9 @@ function leaderboard() {
$('#meta-title').attr('content', "Leaderboards for " + lvl.name)
$('#meta-desc').attr('content', 'View the leaderboard for ' + lvl.name + ' by ' + lvl.author + '!')
})
fetch(`../api/leaderboardLevel/${lvlID}?count=200${weekly ? "&week" : ""}`).then(res => res.json()).then(res => {
fetch(`/api/leaderboardLevel/${lvlID}?count=200${weekly ? "&week" : ""}`).then(res => res.json()).then(res => {
if (!res || res.error || res == "-1") {
loading = false;
@ -101,12 +101,12 @@ function leaderboard() {
res.forEach((x, y) => {
$('#searchBox').append(`<div class="searchresult leaderboardSlot levelboardSlot" style="align-items: center; padding-left: 1vh; height: 15%; width: 100%; position: relative">
<h2 class="center" style="width: 12%; margin: 0% 0% 0% 0.5%; transform: scale(${1 - (Math.max(0, String(x.rank).length - 1) * 0.2)}">${x.rank}</h2>
<gdicon dontload="true" iconID=${x.icon.icon} cacheID=${x.playerID} iconForm="${x.icon.form}" col1="${x.icon.col1}" col2="${x.icon.col2}" glow="${x.icon.glow}" style="width: 7%; margin-bottom: 1%" imgStyle="width: 100%"></gdicon>
<h2 class="small gdButton" style="font-size: 6.5vh; margin-right: 3%; margin-left: 3%"><a href="../u/${x.accountID}.">${x.username}</a></h2>
<h3 class="lessSpaced" style="margin-top: 1.3%; margin-right: 2%">${x.percent}%</h3>
${'<div style="width: 2%"><img class="valign" src="../assets/silvercoin.png" style="height: 33%"></div>'.repeat(x.coins)}
${'<div style="width: 2%"><img class="valign" src="/assets/silvercoin.png" style="height: 33%"></div>'.repeat(x.coins)}
<div class="center" style="text-align: right; position:absolute; right: 1.25%; height: 10%; width: 12.5%; top: 100%;">
<p class="commentDate">${x.date}</p>
@ -117,17 +117,17 @@ function leaderboard() {
$('#searchBox').append('<div style="height: 4.5%"></div>')
loading = false;
$('#loading').hide();
$('#loading').hide()
lazyLoadIcons()
})
}
let weekly = false;
let weekly = false
leaderboard()
$('#topMode').click(function() {
if (!weekly || loading) return;
if (!weekly || loading) return
weekly = false
leaderboard()
$('#weekMode').removeClass('darken')
@ -135,12 +135,12 @@ function leaderboard() {
})
$('#weekMode').click(function() {
if (weekly || loading) return;
if (weekly || loading) return
weekly = true
leaderboard()
$('#topMode').removeClass('darken')
$('#weekMode').addClass('darken')
});
})
function lazyLoadIcons() {
let newIconFound = false

View file

@ -1,9 +1,9 @@
<head>
<title>Map Packs</title>
<meta charset="utf-8">
<link href="../assets/css/browser.css?v=1" type="text/css" rel="stylesheet">
<link href="/assets/css/browser.css?v=1" type="text/css" rel="stylesheet">
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-135255146-3"></script><script>window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);}gtag('js', new Date());gtag('config', 'UA-135255146-3');</script>
<link rel="icon" href="../assets/folder.png">
<link rel="icon" href="/assets/folder.png">
<meta id="meta-title" property="og:title" content="Map Packs">
<meta id="meta-desc" property="og:description" content="Collections of terrible Geometry Dash levels that people only play because of the absurd number of icons they unlock.">
<meta id="meta-image" name="og:image" itemprop="image" content="https://gdbrowser.com/assets/folder.png">
@ -18,32 +18,32 @@
<h1 style="transform:scale(1.2)">Map Packs</h1>
</div>
<img id="loading" style="margin-top: 1%" class="spin noSelect" src="../assets/loading.png" height="12%">
<img id="loading" style="margin-top: 1%" class="spin noSelect" src="/assets/loading.png" height="12%">
<div id="packList">
<br>
</div>
<div style="position:absolute; top: 2%; left: -1.95%; width: 10%; height: 25%; pointer-events: none">
<img class="gdButton yesClick" id="backButton" src="../assets/back.png" height="30%" onclick="backButton()">
</div>
<img class="gdButton yesClick" id="backButton" src="/assets/back.png" height="30%" onclick="backButton()">
</div>
<br>
</div>
</body>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script type="text/javascript" src="../global.js?v=1"></script>
<script type="text/javascript" src="/misc/global.js?v=1"></script>
<script>
fetch('../api/mappacks').then(res => res.json()).then(packs => {
fetch('/api/mappacks').then(res => res.json()).then(packs => {
$('#loading').hide()
packs.forEach(x => {
$('#packList').append(`
<div class="mappack">
<a href="../search/${x.levels.join(",")}?list&header=${encodeURIComponent(x.name)}">
<img src="../assets/difficulties/${x.difficulty}.png" width="42%"><br>
<img src="/assets/difficulties/${x.difficulty}.png" width="42%"><br>
<h3 class="gauntletText"">${x.name.replace("Pack", "<br>Pack")}<br>
<span style="color: rgb(${x.textColor})">${x.stars}</span><img class="valign" src="../assets/star.png" style="cursor: help; width: 14%; margin-left: 2%; transform: translateY(-10%);" title="Stars">
<span style="color: rgb(${x.barColor})">${x.coins}</span><img class="valign" src="../assets/coin.png" style="cursor: help; width: 14%; margin-left: 2%; transform: translateY(-10%);" title="Secret Coins">
<span style="color: rgb(${x.textColor})">${x.stars}</span><img class="valign" src="/assets/star.png" style="cursor: help; width: 14%; margin-left: 2%; transform: translateY(-10%);" title="Stars">
<span style="color: rgb(${x.barColor})">${x.coins}</span><img class="valign" src="/assets/coin.png" style="cursor: help; width: 14%; margin-left: 2%; transform: translateY(-10%);" title="Secret Coins">
</h3></div></a>`)
})
});

View file

@ -1,10 +1,10 @@
<head>
<title>Messages</title>
<meta charset="utf-8">
<link href="../assets/css/browser.css?v=1" type="text/css" rel="stylesheet">
<link href="/assets/css/browser.css?v=1" type="text/css" rel="stylesheet">
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-135255146-3"></script>
<script>window.dataLayer = window.dataLayer || []; function gtag() { dataLayer.push(arguments); } gtag('js', new Date()); gtag('config', 'UA-135255146-3');</script>
<link rel="icon" href="../assets/messages.png">
<link rel="icon" href="/assets/messages.png">
<meta id="meta-title" property="og:title" content="Messages">
<meta id="meta-desc" property="og:description" content="Read, write, and manage your Geometry Dash messages!">
<meta id="meta-image" name="og:image" itemprop="image" content="https://gdbrowser.com/assets/messages.png">
@ -29,9 +29,9 @@
<div style="margin-bottom: 3%">
<p id="message" style="padding: 0% 10%; margin-top: 1.5%"></p>
</div>
<img src="../assets/btn-cancel.png" height=11%; class="gdButton postbutton center"
<img src="/assets/btn-cancel.png" height=11%; class="gdButton postbutton center"
style="margin-right: 1%" onclick="backButton()">
<img src="../assets/btn-submit.png" type="submit" height=11%; class="gdButton postbutton center"
<img src="/assets/btn-submit.png" type="submit" height=11%; class="gdButton postbutton center"
style="margin-left: 1%" id="logIn">
</div>
</div>
@ -40,7 +40,7 @@
<div class="brownbox center supercenter" style="height: 65%; width: 110vh">
<div style="position:absolute; top: 25%; width: 100%">
<p id="msgCount" style="font-size: 3.5vh; margin-bottom: 5%"></p>
<img class="spin noSelect" id="loadingicon" src="../assets/loading.png" width="10%">
<img class="spin noSelect" id="loadingicon" src="/assets/loading.png" width="10%">
</div>
</div>
</div>
@ -52,25 +52,25 @@
</div>
<div style="text-align: left">
<img class="gdButton inline" id="purge" src="../assets/trash-square.png" width="7%">
<img class="gdButton inline" id="purge" src="/assets/trash-square.png" width="7%">
<div id="selectCount" style="display: none; pointer-events: none;">
<h3 class="center" style="font-size: 3vh; margin-top: 12%"></h3>
</div>
<img class="gdButton inline" id="selectAll" src="../assets/select-all.png" width="15%"
<img class="gdButton inline" id="selectAll" src="/assets/select-all.png" width="15%"
style="margin: 0% 0% 0.5% 2%">
<img class="gdButton inline" id="selectNone" src="../assets/select-none.png" width="15%"
<img class="gdButton inline" id="selectNone" src="/assets/select-none.png" width="15%"
style="display: none; margin: 0% 0% 0.5% 2%">
</div>
<img src="../assets/refresh.png" style="height: 13%;position: absolute;right: 0%;bottom: 0%;" onclick="page = 0; getMessages()" class="gdButton center sideSpaceB">
<img src="/assets/refresh.png" style="height: 13%;position: absolute;right: 0%;bottom: 0%;" onclick="page = 0; getMessages()" class="gdButton center sideSpaceB">
<div style="position: absolute; left: 0.5%; top: 45%; height: 11%;">
<img class="gdButton" style="display: none" id="pageDown" src="../assets/arrow-left.png"
<img class="gdButton" style="display: none" id="pageDown" src="/assets/arrow-left.png"
height="90%">
</div>
<div style="position: absolute; right: 0.5%; top: 45%; height: 11%;">
<img class="gdButton" style="display: none" id="pageUp" src="../assets/arrow-right.png"
<img class="gdButton" style="display: none" id="pageUp" src="/assets/arrow-right.png"
height="90%">
</div>
@ -84,19 +84,19 @@
<p class="bigger center" style="line-height: 6vh; margin-top: 1.5vh;">
Are you sure you want to <cr>delete</cr> this message?
</p>
<img src="../assets/btn-cancel-green.png" height=29%; class="gdButton center" onclick="$('#confirmDelete').hide()">
<img src="../assets/btn-delete.png" height=29%; id="deleteCurrentMessage" class="gdButton center sideSpaceB">
<img src="/assets/btn-cancel-green.png" height=29%; class="gdButton center" onclick="$('#confirmDelete').hide()">
<img src="/assets/btn-delete.png" height=29%; id="deleteCurrentMessage" class="gdButton center sideSpaceB">
</div>
<div id="deleting" style="display: none">
<h2 class="smaller center" style="font-size: 5.5vh">Delete</h2>
<p class="bigger center" style="line-height: 6vh; margin-top: 1.5vh;">Deleting message...</p>
<img src="../assets/loading.png" class="spin noSelect" style="height: 35%; margin-top: -2%">
<img src="/assets/loading.png" class="spin noSelect" style="height: 35%; margin-top: -2%">
</div>
<div id="delete-error" style="display: none;">
<img class="gdButton" src="../assets/close.png" style="width: 15%; position: absolute; top: -17.5%; left: -5.5vh" onclick="$('#delete-error').hide(); $('#confirmDelete').hide(); $('#preDelete').show()">
<img src="../assets/exclamation.png" style="height: 40%">
<img class="gdButton" src="/assets/close.png" style="width: 15%; position: absolute; top: -17.5%; left: -5.5vh" onclick="$('#delete-error').hide(); $('#confirmDelete').hide(); $('#preDelete').show()">
<img src="/assets/exclamation.png" style="height: 40%">
<p class="bigger" style="margin-bottom: 0%; margin-top: 2.5%">Something went wrong!</p>
<p id="delError" style="font-size: 2.4vh; margin-top: 1%"></p>
</div>
@ -111,9 +111,9 @@
Are you sure you want to <cr>delete</cr> <span class="selectedAmount"
style="color: yelow"></span>?
</p>
<img src="../assets/btn-cancel-green.png" height=29%; class="gdButton center"
<img src="/assets/btn-cancel-green.png" height=29%; class="gdButton center"
onclick="$('#bulkDelete').hide()">
<img src="../assets/btn-delete.png" height=29%; id="bulkDeleteMessages"
<img src="/assets/btn-delete.png" height=29%; id="bulkDeleteMessages"
class="gdButton center sideSpaceB">
</div>
@ -121,12 +121,12 @@
<h2 class="smaller center" style="font-size: 5.5vh">Delete</h2>
<p class="bigger center" style="line-height: 6vh; margin-top: 1.5vh;">Deleting <span
class="selectedAmount"></span>...</p>
<img src="../assets/loading.png" class="spin noSelect" style="height: 35%; margin-top: -2%">
<img src="/assets/loading.png" class="spin noSelect" style="height: 35%; margin-top: -2%">
</div>
<div id="bd-error" style="display: none;">
<img class="gdButton" src="../assets/close.png" style="width: 15%; position: absolute; top: -17.5%; left: -5.5vh" onclick="$('#bd-error').hide(); $('#bulkDelete').hide(); $('#preBulkDelete').show()">
<img src="../assets/exclamation.png" style="height: 40%">
<img class="gdButton" src="/assets/close.png" style="width: 15%; position: absolute; top: -17.5%; left: -5.5vh" onclick="$('#bd-error').hide(); $('#bulkDelete').hide(); $('#preBulkDelete').show()">
<img src="/assets/exclamation.png" style="height: 40%">
<p class="bigger" style="margin-bottom: 0%; margin-top: 2.5%">Something went wrong!</p>
<p id="bdError" style="font-size: 2.4vh; margin-top: 1%"></p>
</div>
@ -136,16 +136,16 @@
<div id="selectedMessage" class="popup">
<div class="bounce center supercenter" style="height: 70%; width: 115vh">
<div class="bluebox center supercenter" style="width: 100%; height: 100%">
<img class="gdButton" src="../assets/close.png" width="9%" style="position: absolute; top: -7.5%; left: -6.5vh" onclick="$('#selectedMessage').hide()">
<img class="gdButton" src="/assets/close.png" width="9%" style="position: absolute; top: -7.5%; left: -6.5vh" onclick="$('#selectedMessage').hide()">
<h1 id="messageSubject" class="smaller center" style="font-size: 5.5vh; min-height: 9%; margin-top: 1%"></h1>
<h3 id="messageAuthor" class="gold center gauntletText gdButton"></h3>
<div class="transparentBox center" id="theMfMessage">
<img id="messageLoad" src="../assets/loading.png" class="spin noSelect" style="width: 10%; margin-top: 15%">
<img id="messageLoad" src="/assets/loading.png" class="spin noSelect" style="width: 10%; margin-top: 15%">
<p id="messageBody"></p>
</div>
<div id="messageOptions">
<img class="gdButton" style="width: 8%" title="Reply" src="../assets/reply.png" id="replyButton">
<img class="gdButton" style="width: 8%" title="Delete" src="../assets/trash.png" id="deleteButton" onclick="$('#confirmDelete').show()">
<img class="gdButton" style="width: 8%" title="Reply" src="/assets/reply.png" id="replyButton">
<img class="gdButton" style="width: 8%" title="Delete" src="/assets/trash.png" id="deleteButton" onclick="$('#confirmDelete').show()">
</div>
</div>
</div>
@ -163,10 +163,10 @@
placeholder="Message"></textarea>
</div>
<p id="messageStatus" style="margin: 1% 0% 1.5% 0%; min-height: 5%;"></p>
<img src="../assets/btn-cancel.png" height=10%; class="gdButton center"
<img src="/assets/btn-cancel.png" height=10%; class="gdButton center"
onclick="$('textarea').val(''); $('#sendMessage').hide()">
<img src="../assets/btn-submit.png" height=10%; id="postMessage" class="gdButton center sideSpaceB">
<img src="../assets/trash.png" style="height: 10%; position: absolute; right: 0%"
<img src="/assets/btn-submit.png" height=10%; id="postMessage" class="gdButton center sideSpaceB">
<img src="/assets/trash.png" style="height: 10%; position: absolute; right: 0%"
onclick="$('#postContent').val('');" class="gdButton center sideSpaceB">
</div>
</div>
@ -176,19 +176,19 @@
<div class="bluebox bounce center supercenter" style="height: 27%; width: 55vh">
<div id="reply-loading">
<img src="../assets/loading.png" class="spin noSelect" style="height: 40%; margin-top: 7.5%">
<img src="/assets/loading.png" class="spin noSelect" style="height: 40%; margin-top: 7.5%">
<p class="bigger">Sending...</p>
</div>
<div id="reply-sent" style="display: none;">
<img class="gdButton" src="../assets/close.png" style="width: 15%; position: absolute; top: -17.5%; left: -5.5vh" onclick="$('.popup').hide()">
<img src="../assets/check.png" style="height: 40%; margin-top: 7.5%">
<img class="gdButton" src="/assets/close.png" style="width: 15%; position: absolute; top: -17.5%; left: -5.5vh" onclick="$('.popup').hide()">
<img src="/assets/check.png" style="height: 40%; margin-top: 7.5%">
<p class="bigger">Message sent!</p>
</div>
<div id="reply-error" style="display: none;">
<img class="gdButton" src="../assets/close.png" style="width: 15%; position: absolute; top: -17.5%; left: -5.5vh" onclick="$('.popup').hide()">
<img src="../assets/exclamation.png" style="height: 40%; margin-top: 7.5%">
<img class="gdButton" src="/assets/close.png" style="width: 15%; position: absolute; top: -17.5%; left: -5.5vh" onclick="$('.popup').hide()">
<img src="/assets/exclamation.png" style="height: 40%; margin-top: 7.5%">
<p class="bigger" style="margin-bottom: 0%;">Something went wrong!</p>
<p style="font-size: 2.4vh; margin-top: 1%">Does the recipient have messages enabled?</p>
</div>
@ -197,15 +197,15 @@
</div>
<div style="position:absolute; bottom: 0%; left: 0%; width: 100%; pointer-events: none;">
<img class="cornerPiece" src="../assets/corner.png" width=7%;>
<img class="cornerPiece" src="/assets/corner.png" width=7%;>
</div>
<div style="position:absolute; bottom: 0%; right: 0%; width: 100%; text-align: right; pointer-events: none;">
<img class="cornerPiece" src="../assets/corner.png" width=7%; style="transform: scaleX(-1)">
<img class="cornerPiece" src="/assets/corner.png" width=7%; style="transform: scaleX(-1)">
</div>
<div style="position:absolute; top: 2%; left: 1.5%; width: 10%; height: 25%; pointer-events: none">
<img class="gdButton yesClick" id="backButton" src="../assets/back.png" height="30%" onclick="backButton()">
<img class="gdButton yesClick" id="backButton" src="/assets/back.png" height="30%" onclick="backButton()">
</div>
</div>
@ -213,18 +213,18 @@
</body>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script type="text/javascript" src="../global.js?v=1"></script>
<script type="text/javascript" src="/misc/global.js?v=1"></script>
<script>
let accountID;
let password;
let page = 0;
let messageID = 0;
let playerID = 0;
let messages = [];
let messageStatus = {};
let cache = {};
let loading = false;
let accountID
let password
let page = 0
let messageID = 0
let playerID = 0
let messages = []
let messageStatus = {}
let cache = {}
let loading = false
let messageText = 'Your <cy>Geometry Dash password</cy> will <cg>not be stored</cg> anywhere on the site, both <ca>locally and server-side.</ca> For security, it will be <cy>forgotten</cy> when you exit this page.'
$('#message').html(messageText)
@ -254,7 +254,7 @@
<label for="message-${y}" class="gdcheckbox gdButton"></label>
</div>${/*
<div class="xButton hitbox">
<img class="gdButton" style="width: 8%" src="../assets/xbutton.png">
<img class="gdButton" style="width: 8%" src="/assets/xbutton.png">
</div>*/""}
</div>`)
})
@ -272,7 +272,7 @@
$('#message').text("Logging in...")
$('.postbutton').hide()
fetch(`../api/profile/${username}`).then(res => res.json()).then(res => {
fetch(`/api/profile/${username}`).then(res => res.json()).then(res => {
if (!res || res == "-1") { $('.postbutton').show(); return $('#message').text("The username you provided doesn't exist!") }
else accountID = res.accountID
@ -292,7 +292,7 @@
let targetUser = window.location.search.match(/(\?|&)sendTo=(.+)/)
if (targetUser) {
targetUser = decodeURIComponent(targetUser[2])
fetch(`../api/profile/${targetUser}`).then(res => res.json()).then(res => {
fetch(`/api/profile/${targetUser}`).then(res => res.json()).then(res => {
if (res == "-1" || !res) return;
$('#replyAuthor').html(`<a href="../u/${res.accountID}." target="_blank">To: ${res.username}</a>`)
messageStatus[res.accountID] = [res.messages, res.username]
@ -321,7 +321,7 @@
$('#selectCount').hide()
$('#selectAll').show()
$('#selectNone').hide()
$('#msgList').html('<img src="../assets/loading.png" class="spin noSelect" style="margin-top: 20%; height: 20%;">')
$('#msgList').html('<img src="/assets/loading.png" class="spin noSelect" style="margin-top: 20%; height: 20%;">')
$.post("../messages", { password, accountID, page })
.done(msgs => {
messages = msgs
@ -384,7 +384,7 @@
$('#deleteButton').show()
}
if (!messageStatus[msg.accountID]) fetch(`../api/profile/${msg.author}`).then(res => res.json()).then(res => {
if (!messageStatus[msg.accountID]) fetch(`/api/profile/${msg.author}`).then(res => res.json()).then(res => {
messageStatus[msg.accountID] = [res.messages, msg.author]
loadMsg()
})
@ -392,10 +392,10 @@
loadMsg()
}
})
.fail(e => {
.fail(e => {
$('#messageAuthor').html('&nbsp;')
$('#messageSubject').html('&nbsp;')
$('#messageLoad').hide()
$('#messageLoad').hide()
$('#messageBody').html(e.responseText).show()
$('#theMfMessage').attr('style', 'background-color: rgba(0, 0, 0, 0)')
})
@ -416,7 +416,7 @@
$('#preDelete').show()
$('#deleting').hide()
})
.fail(e => {
.fail(e => {
$('#deleting').hide()
$('#delete-error').show()
$('#delError').html(e.responseText)
@ -450,7 +450,7 @@
$('#bulkDeleting').hide()
$('#preBulkDelete').show()
})
.fail(e => {
.fail(e => {
$('#bulkDeleting').hide()
$('#bd-error').show()
$('#bdError').html(e.responseText)
@ -458,7 +458,7 @@
})
$('#replyButton').click(function() {
if (!messageStatus[playerID]) return;
if (!messageStatus[playerID]) return
let status = messageStatus[playerID][0]
let name = messageStatus[playerID][1]
$('#postMessage').removeClass('grayscale')
@ -475,7 +475,7 @@
$('#postMessage').click(function () {
let subject = $('#postSubject').val()
let message = $('#postContent').val()
if (!subject || !message || !messageStatus[playerID] || messageStatus[playerID][0] == "off") return;
if (!subject || !message || !messageStatus[playerID] || messageStatus[playerID][0] == "off") return
allowEsc = false
$('#reply-loading').show()
$('#reply-sent').hide()
@ -488,7 +488,7 @@
$('#reply-sent').show()
allowEsc = true
})
.fail(e => {
.fail(e => {
$('#reply-loading').hide()
$('#reply-error').show()
allowEsc = true
@ -518,10 +518,10 @@
});
$(document).keydown(function (k) {
if (loading) return;
if (loading) return
if ($('#access').is(':visible')) {
if (k.which == 13) $('#logIn').trigger('click') //enter
if (k.which == 13) $('#logIn').trigger('click') //enter
else return;
}

View file

@ -1,9 +1,9 @@
<head>
<title>Geometry Dash Browser!</title>
<meta charset="utf-8">
<link href="../assets/css/browser.css?v=1" type="text/css" rel="stylesheet">
<link href="/assets/css/browser.css?v=1" type="text/css" rel="stylesheet">
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-135255146-3"></script><script>window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);}gtag('js', new Date());gtag('config', 'UA-135255146-3');</script>
<link rel="icon" href="../assets/keymaster-head.png">
<link rel="icon" href="/assets/keymaster-head.png">
<meta id="meta-title" property="og:title" content="Geometry Dash Browser!">
<meta id="meta-desc" property="og:description" content="Browse all of Geometry Dash's online features, right from this handy little website! Levels, profiles, leaderboards, comments, and more!">
<meta id="meta-image" name="og:image" itemprop="image" content="https://gdbrowser.com/assets/coin.png">
@ -15,7 +15,7 @@
<div id="everything" class="center" style="overflow: hidden">
<h2 style="margin-top: 2%; margin-bottom: 2.2%">RobTop's Purgatory</h2>
<h1 id="msg" class="smaller" style="margin-bottom: 1.2%; white-space: normal;">&nbsp;</h1>
<img id="glubfub" src="../assets/keymaster.png" height="25%" class="gdButton">
<img id="glubfub" src="/assets/keymaster.png" height="25%" class="gdButton">
<div id="footer" style="position: absolute; left: 1%; bottom: 0%; text-align: left">
@ -27,33 +27,33 @@
</div>
<div style="position:absolute; top: 2.8%; left: 0.5%; width: 4.5%; text-align: right;">
<a href="../?home"><img class="gdButton" src="../assets/arrow-left.png" width=80%;"></a>
<a href="../?home"><img class="gdButton" src="/assets/arrow-left.png" width=80%;"></a>
</div>
<div style="position:absolute; bottom: 3%; right: 3%; width: 10%; text-align: right;">
<a href="../gdps"><img class="gdButton" src="../assets/basement.png" width=85%;"></a>
<a href="../gdps"><img class="gdButton" src="/assets/basement.png" width=85%;"></a>
</div>
<div style="position:absolute; bottom: 22%; right: 7%; width: 10%; text-align: right; pointer-events: none">
<img src="../assets/privateservers.png" width=85%;">
<img src="/assets/privateservers.png" width=85%;">
</div>
<div style="position:absolute; top: 15.5%; right: 7.5%; width: 10%; text-align: right; pointer-events: none">
<img src="../assets/leaderboardarrow.png" width=85%;">
<img src="/assets/leaderboardarrow.png" width=85%;">
</div>
<div style="position:absolute; top: 2.2%; right: 2.5%; text-align: right; width: 11%;">
<a href="../leaderboard"><img class="gdButton" src="../assets/leaderboard.png" width="55%"></a>
<a href="../leaderboard"><img class="gdButton" src="/assets/leaderboard.png" width="55%"></a>
</div>
<div style="position:absolute; top: -1.5%; right: 11%; text-align: right; width: 10%;">
<a href="../iconkit"><img class="iconRope" src="../assets/iconrope.png" width="40%"></a>
</div>
<a href="../iconkit"><img class="iconRope" src="/assets/iconrope.png" width="40%"></a>
</div>
</div>
</body>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script type="text/javascript" src="../global.js?v=1"></script>
<script type="text/javascript" src="/misc/global.js?v=1"></script>
<script>
let line = 0

View file

@ -1,7 +1,7 @@
<head>
<title>[[USERNAME]]'s Profile</title>
<meta charset="utf-8">
<link href="../assets/css/browser.css?v=1" type="text/css" rel="stylesheet">
<link href="/assets/css/browser.css?v=1" type="text/css" rel="stylesheet">
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-135255146-3"></script><script>window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);}gtag('js', new Date());gtag('config', 'UA-135255146-3');</script>
<link rel="icon" href="">
<meta id="meta-title" property="og:title" content="[[USERNAME]]'s profile">
@ -26,16 +26,16 @@
<div style="min-height: 20%; max-height: 20%">
<p id="message" style="padding: 0% 10%; margin-top: 2%"></p>
</div>
<img src="../assets/btn-cancel.png" height=11%; class="postButton gdButton center" style="margin-right: 1%" onclick="$('#leavePost').hide(); $('textarea').val('')">
<img src="../assets/btn-submit.png" type="submit" height=11%; class="postButton gdButton center" style="margin-left: 1%" id="submitComment">
<img src="/assets/btn-cancel.png" height=11%; class="postButton gdButton center" style="margin-right: 1%" onclick="$('#leavePost').hide(); $('textarea').val('')">
<img src="/assets/btn-submit.png" type="submit" height=11%; class="postButton gdButton center" style="margin-left: 1%" id="submitComment">
</div>
</div>
<div class="popup" id="likeComment">
<div class="brownbox bounce center supercenter" style="height: 75%; width: 100vh">
<h1 class="smaller center" style="font-size: 5.5vh">Vote</h1>
<img src="../assets/smashLike.png" id="likebtn" class="inline gdButton likeButton"><!--
--><img src="../assets/smashDislike.png" id="dislikebtn" class="inline gdButton likeButton youAreNotTheOne">
<img src="/assets/smashLike.png" id="likebtn" class="inline gdButton likeButton"><!--
--><img src="/assets/smashDislike.png" id="dislikebtn" class="inline gdButton likeButton youAreNotTheOne">
<form action="nothing lol">
<h3 class="center">GD Username</h3>
<input type="text" name="gdbrowser" id="like-username" maxlength="50" style="height: 8vh; width: 90%; text-align: center; margin-top: 0.5%">
@ -45,11 +45,11 @@
<div style="min-height: 18%; max-height: 18%">
<p id="likeMessage" style="padding: 0% 10%; margin-top: 2.5%"></p>
</div>
<img src="../assets/btn-cancel.png" height=10%; class="postButton gdButton center" style="margin-right: 1%" onclick="$('#likeComment').hide(); $('#likebtn').trigger('click');">
<img src="../assets/btn-submit.png" type="submit" height=10%; class="postButton gdButton center" style="margin-left: 1%" id="submitVote">
<img src="/assets/btn-cancel.png" height=10%; class="postButton gdButton center" style="margin-right: 1%" onclick="$('#likeComment').hide(); $('#likebtn').trigger('click');">
<img src="/assets/btn-submit.png" type="submit" height=10%; class="postButton gdButton center" style="margin-left: 1%" id="submitVote">
</div>
</div>
<div class="popup" id="settingsDiv">
<div class="fancybox bounce center supercenter">
<h2 class="smaller center" style="font-size: 5.5vh">User Info</h2>
@ -58,43 +58,43 @@
Private Messages: [[DMS]]<br>
Comment History: [[COMMENTS]]<br>
</p>
<img src="../assets/ok.png" width=20%; class="gdButton center" onclick="$('#settingsDiv').hide()">
<img src="/assets/ok.png" width=20%; class="gdButton center" onclick="$('#settingsDiv').hide()">
</div>
</div>
<div style="position:absolute; bottom: 0%; left: 0%; width: 100%">
<img class="cornerPiece" src="../assets/corner.png" width=7%;>
<img class="cornerPiece" src="/assets/corner.png" width=7%;>
</div>
<div style="position:absolute; bottom: 0%; right: 0%; width: 100%; text-align: right;">
<img class="cornerPiece" src="../assets/corner.png" width=7%; style="transform: scaleX(-1)">
<img class="cornerPiece" src="/assets/corner.png" width=7%; style="transform: scaleX(-1)">
</div>
<div style="position:absolute; top: 2%; left: 1.5%; width: 10%; height: 25%; pointer-events: none">
<img class="gdButton yesClick" id="backButton" src="../assets/back.png" height="30%" onclick="backButton()">
<img class="gdButton yesClick" id="backButton" src="/assets/back.png" height="30%" onclick="backButton()">
</div>
<div class="brownBox center supercenter" style="width: 135vh; height: 82%; margin-top: -0.7%">
<div id="globalrank0" style="position: absolute; left: 0.3%; top: 1.5%; width: 18%; text-align: left">
<p style="margin: 0;"><img src="../assets/trophy.png" class="inline valign help profileTrophy" width="22%" title="Global Rank"> [[RANK]]</p>
<p style="margin: 0;"><img src="/assets/trophy.png" class="inline valign help profileTrophy" width="22%" title="Global Rank"> [[RANK]]</p>
</div>
<h1 class="veryBig inline" style="width: inherit">
<img class="inline valign" id="modBadge1" style="display: none; height: 7%; cursor:help" src="../assets/mod.png" title="[[USERNAME]] is a moderator!"><!--
--><img class="inline valign" id="modBadge2" style="display: none; height: 7%; cursor:help" src="../assets/mod-elder.png" title="[[USERNAME]] is an elder moderator!"><!--
--><img class="inline valign" id="modBadge3" style="display: none; height: 7%; cursor:help" src="../assets/mod-extra.png" title="[[USERNAME]] is a custom moderator! (tier [[MODERATOR]])"><!--
<img class="inline valign" id="modBadge1" style="display: none; height: 7%; cursor:help" src="/assets/mod.png" title="[[USERNAME]] is a moderator!"><!--
--><img class="inline valign" id="modBadge2" style="display: none; height: 7%; cursor:help" src="/assets/mod-elder.png" title="[[USERNAME]] is an elder moderator!"><!--
--><img class="inline valign" id="modBadge3" style="display: none; height: 7%; cursor:help" src="/assets/mod-extra.png" title="[[USERNAME]] is a custom moderator! (tier [[MODERATOR]])"><!--
--><span style="margin-left: 1%">[[USERNAME]]</span>
</h1>
<hr style="margin-bottom: 2%" class="profilePostHide">
<h3 id="collectibles" class="profilePostHide">
<span id="stars">[[STARS]]</span> <img class="help valign" src="../assets/star.png" title="Stars">
<span id="diamonds">[[DIAMONDS]]</span> <img class="help valign" src="../assets/diamond.png" title="Diamonds">
<span id="coins">[[COINS]]</span> <img class="help valign" src="../assets/coin.png" title="Secret Coins">
<span id="usercoins">[[USERCOINS]]</span> <img class="help valign"src="../assets/silvercoin.png" title="User Coins">
<span id="demons">[[DEMONS]]</span> <img class="help valign"src="../assets/demon.png" title="Demons">
<span id="creatorpoints" style="display: none"><span id="cp">[[CP]]</span> <img class="help valign" src="../assets/cp.png" title="Creator Points"></span>
<span id="stars">[[STARS]]</span> <img class="help valign" src="/assets/star.png" title="Stars">
<span id="diamonds">[[DIAMONDS]]</span> <img class="help valign" src="/assets/diamond.png" title="Diamonds">
<span id="coins">[[COINS]]</span> <img class="help valign" src="/assets/coin.png" title="Secret Coins">
<span id="usercoins">[[USERCOINS]]</span> <img class="help valign"src="/assets/silvercoin.png" title="User Coins">
<span id="demons">[[DEMONS]]</span> <img class="help valign"src="/assets/demon.png" title="Demons">
<span id="creatorpoints" style="display: none"><span id="cp">[[CP]]</span> <img class="help valign" src="/assets/cp.png" title="Creator Points"></span>
</h3>
<div class="lightBox center profilePostHide" id="iconsDiv" style="margin: 2% auto; width: 105vh">
@ -105,45 +105,45 @@
<gdicon iconID=[[WAVE]] iconForm="wave" col1="[[COL1]]" col2="[[COL2]]" glow="[[GLOW]]" imgStyle="height: 7%"></gdicon>
<gdicon iconID=[[ROBOT]] iconForm="robot" col1="[[COL1]]" col2="[[COL2]]" glow="[[GLOW]]"></gdicon>
<gdicon iconID=[[SPIDER]] iconForm="spider" col1="[[COL1]]" col2="[[COL2]]" glow="[[GLOW]]"></gdicon>
<img src="../assets/deatheffects/[[DEATHEFFECT]].png" title="Death Effect [[DEATHEFFECT]]" id="deatheffect">
<img src="/assets/deatheffects/[[DEATHEFFECT]].png" title="Death Effect [[DEATHEFFECT]]" id="deatheffect">
</div>
<div class="lightBox center dragscroll" id="statusDiv" normalHeight="36vh" compactHeight="69vh" style="margin: 2% auto; width: 105vh; height: 36vh; background-color: #BE6F3F">
</div>
<div class="center profilePostHide" style="margin: 1.5% auto 2.5% auto;">
<a id="msgA" target="_blank"><img src="../assets/messages.png" height="10%" id="msgButton" class="sideSpace gdButton" onclick="$('#settingsDiv').show()"></a>
<img src="../assets/friends.png" height="10%" id="friendButton" class="sideSpace gdButton" onclick="$('#settingsDiv').show()">
<a id="msgA" target="_blank"><img src="/assets/messages.png" height="10%" id="msgButton" class="sideSpace gdButton" onclick="$('#settingsDiv').show()"></a>
<img src="/assets/friends.png" height="10%" id="friendButton" class="sideSpace gdButton" onclick="$('#settingsDiv').show()">
</div>
<div style="position: absolute; bottom: 0%; left: 12%;" class="profilePostHide">
<p style="text-align: left; font-size: 2.2vh; color: rgba(0, 0, 0, 0.5)">Account ID: [[ACCOUNTID]]<br>Player ID: [[PLAYERID]]</p>
</div>
<img src="../assets/follow-off.png" id="followOff" class="gdButton profilePostHide" style="position: absolute; left: 4.5%; bottom: 1%; width: 6%">
<img src="../assets/follow-on.png" id="followOn" class="gdButton profilePostHide" style="position: absolute; left: 4.5%; bottom: 1%; width: 6%; display: none">
<a id="commentA"><img src="../assets/comments.png" class="gdButton" id="commentButton" style="position: absolute; right: 0.5%; bottom: 50%; width: 6%" onclick="$('#settingsDiv').show()"></a>
<img src="../assets/expanded-off.png" class="gdButton" id="compactMode" style="position: absolute; left: 2%; bottom: 45%; width: 6%">
<img src="/assets/follow-off.png" id="followOff" class="gdButton profilePostHide" style="position: absolute; left: 4.5%; bottom: 1%; width: 6%">
<img src="/assets/follow-on.png" id="followOn" class="gdButton profilePostHide" style="position: absolute; left: 4.5%; bottom: 1%; width: 6%; display: none">
<a href="../search/[[USERNAME]]?user"><img src="../assets/levels.png" class="gdButton" style="position: absolute; right: 0.5%; bottom: 1%; width: 6%"></a>
<a id="commentA"><img src="/assets/comments.png" class="gdButton" id="commentButton" style="position: absolute; right: 0.5%; bottom: 50%; width: 6%" onclick="$('#settingsDiv').show()"></a>
<img src="/assets/expanded-off.png" class="gdButton" id="compactMode" style="position: absolute; left: 2%; bottom: 45%; width: 6%">
<a href="../search/[[USERNAME]]?user"><img src="/assets/levels.png" class="gdButton" style="position: absolute; right: 0.5%; bottom: 1%; width: 6%"></a>
<div style="position: absolute; right: 0.5%; top: 0%; width: 6%">
<a id="youtube" style="display: none" target="_blank" href="https://youtube.com/channel/[[YOUTUBE]]"><img src="../assets/youtube.png" class="gdButton socialButton"></a>
<a id="twitter" style="display: none" target="_blank" href="https://twitter.com/[[TWITTER]]"><img src="../assets/twitter.png" class="gdButton socialButton"></a>
<a id="twitch" style="display: none" target="_blank" href="https://twitch.tv/[[TWITCH]]"><img src="../assets/twitch.png" class="gdButton socialButton"></a>
<a id="youtube" style="display: none" target="_blank" href="https://youtube.com/channel/[[YOUTUBE]]"><img src="/assets/youtube.png" class="gdButton socialButton"></a>
<a id="twitter" style="display: none" target="_blank" href="https://twitter.com/[[TWITTER]]"><img src="/assets/twitter.png" class="gdButton socialButton"></a>
<a id="twitch" style="display: none" target="_blank" href="https://twitch.tv/[[TWITCH]]"><img src="/assets/twitch.png" class="gdButton socialButton"></a>
</div>
<div class="supercenter" style="left: 5%; top: 65%; height: 10%">
<img class="gdButton" id="pageDown" style="display: none" src="../assets/arrow-left.png" height="95%" onclick="page -= 1; appendComments()">
<img class="gdButton" id="pageDown" style="display: none" src="/assets/arrow-left.png" height="95%" onclick="page -= 1; appendComments()">
</div>
<div class="supercenter" style="left: 95%; top: 65%; height: 10%;">
<img class="gdButton" id="pageUp" src="../assets/arrow-right.png" height="95%" onclick="page += 1; appendComments()">
<img class="gdButton" id="pageUp" src="/assets/arrow-right.png" height="95%" onclick="page += 1; appendComments()">
</div>
<div id="postButton" style="position:absolute; bottom: 0%; left: 0%; width: 14%; text-align: left; transform: translate(-35%, 40%);">
<img class="gdButton" src="../assets/comment.png" width="60%" onclick="$('#content').trigger('input'); $('#leavePost').show();">
<img class="gdButton" src="/assets/comment.png" width="60%" onclick="$('#content').trigger('input'); $('#leavePost').show();">
</div>
</div>
@ -153,9 +153,9 @@
</body>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/6.2.2/browser/pixi.js"></script>
<script type="text/javascript" src="../iconkit/icon.js"></script>
<script type="text/javascript" src="../global.js?v=1"></script>
<script type="text/javascript" src="../dragscroll.js"></script>
<script type="text/javascript" src="/iconkit/icon.js"></script>
<script type="text/javascript" src="/misc/global.js?v=1"></script>
<script type="text/javascript" src="/misc/dragscroll.js"></script>
<script>
@ -170,7 +170,7 @@ renderIcons()
// set favicon
$('#mainIcon').on('DOMNodeInserted', 'img', function () {
$("link[rel='icon']").attr("href", $(this).attr("src"));
});
})
let messageText = 'Your <cy>Geometry Dash password</cy> will <cg>not be stored</cg> anywhere on the site, both <ca>locally and server-side.</ca> You can view the code used for profile posts <a class="menuLink" target="_blank" href="https://github.com/GDColon/GDBrowser/blob/master/api/post/postProfileComment.js">here</a>.'
$('#message').html(messageText)
@ -211,14 +211,14 @@ if (`[[TWITTER]]` != "null") $('#twitter').show()
if (`[[TWITCH]]` != "null") $('#twitch').show()
let numRank = parseInt("[[RANK]]")
if (numRank < 2) $(".profileTrophy").attr("src","../assets/trophies/1.png")
else if (numRank <= 10) $(".profileTrophy").attr("src","../assets/trophies/2.png")
else if (numRank <= 50 ) $(".profileTrophy").attr("src","../assets/trophies/3.png")
else if (numRank <= 100) $(".profileTrophy").attr("src","../assets/trophies/4.png")
else if (numRank <= 200) $(".profileTrophy").attr("src","../assets/trophies/5.png")
else if (numRank <= 500) $(".profileTrophy").attr("src","../assets/trophies/6.png")
else if (numRank <= 1000) $(".profileTrophy").attr("src","../assets/trophies/7.png")
else $(".profileTrophy").attr("src","../assets/trophies/0.png")
if (numRank < 2) $(".profileTrophy").attr("src","/assets/trophies/1.png")
else if (numRank <= 10) $(".profileTrophy").attr("src","/assets/trophies/2.png")
else if (numRank <= 50 ) $(".profileTrophy").attr("src","/assets/trophies/3.png")
else if (numRank <= 100) $(".profileTrophy").attr("src","/assets/trophies/4.png")
else if (numRank <= 200) $(".profileTrophy").attr("src","/assets/trophies/5.png")
else if (numRank <= 500) $(".profileTrophy").attr("src","/assets/trophies/6.png")
else if (numRank <= 1000) $(".profileTrophy").attr("src","/assets/trophies/7.png")
else $(".profileTrophy").attr("src","/assets/trophies/0.png")
let messages = "[[MESSAGES]]"
let commenthistory = "[[COMMENTHISTORY]]"
@ -227,10 +227,10 @@ let reqMode = [[FRIENDREQUESTS]] ? "<cg>Enabled</cg>" : "<cr>Disabled</cr>"
let dmMode = messages == "all" ? "<cg>Public</cg>" : messages == "friends" ? "<cy>Friends Only</cy>" : "<cr>Disabled</cr>"
let commentMode = commenthistory == "all" ? "<cg>Public</cg>" : commenthistory == "friends" ? "<cy>Friends Only</cy>" : "<cr>Disabled</cr>"
if (commenthistory == "friends") $('#commentButton').attr('src', '../assets/comments-yellow.png')
else if (commenthistory == "off") $('#commentButton').attr('src', '../assets/comments-grey.png')
if (commenthistory == "friends") $('#commentButton').attr('src', '/assets/comments-yellow.png')
else if (commenthistory == "off") $('#commentButton').attr('src', '/assets/comments-grey.png')
else {
$('#commentButton').attr('src', '../assets/comments.png').attr('onclick', '')
$('#commentButton').attr('src', '/assets/comments.png').attr('onclick', '')
$('#commentA').attr('href', '../comments/[[USERNAME]]')
}
@ -239,10 +239,10 @@ if (messages == "all") {
$('#msgA').attr('href', '../messages?sendTo=[[USERNAME]]')
}
if (messages == "friends") $('#msgButton').attr('src', '../assets/messages-yellow.png')
else if (messages == "off") $('#msgButton').attr('src', '../assets/messages-grey.png')
if (messages == "friends") $('#msgButton').attr('src', '/assets/messages-yellow.png')
else if (messages == "off") $('#msgButton').attr('src', '/assets/messages-grey.png')
if (![[FRIENDREQUESTS]]) $('#friendButton').attr('src', '../assets/friends-grey.png')
if (![[FRIENDREQUESTS]]) $('#friendButton').attr('src', '/assets/friends-grey.png')
$('#userInfo').html($('#userInfo').html()
.replace("[[REQS]]", reqMode)
@ -250,15 +250,15 @@ $('#userInfo').html($('#userInfo').html()
.replace("[[COMMENTS]]", commentMode) + (messages == "friends" ? "<br style='line-height: 69%'><a target='_blank' style='color: lime' class='youCanClickThis2' href='../messages?sendTo=[[USERNAME]]'>Send message</a><br>(if friended)" : ""))
function appendComments() {
if (loadingComments) return;
if (loadingComments) return
else loadingComments = true;
$('#statusDiv').html(`<div class="supercenter" id="loading" style="height: 12%; top: 62%;"><img class="spin noSelect" src="../assets/loading.png" height="105%"></div>`)
$('#statusDiv').html(`<div class="supercenter" id="loading" style="height: 12%; top: 62%;"><img class="spin noSelect" src="/assets/loading.png" height="105%"></div>`)
if (page == 0) $('#pageDown').hide()
else $('#pageDown').show()
Fetch(`../api/comments/[[ACCOUNTID]]?type=profile&page=${page}`).then(res => {
Fetch(`/api/comments/[[ACCOUNTID]]?type=profile&page=${page}`).then(res => {
if (res.length != 10) $('#pageUp').hide()
else $('#pageUp').show()
@ -277,7 +277,7 @@ function appendComments() {
</div>
<p class="commentDate">${x.date}</p>
<div class="commentLikes">
<img id="likeImg" class="likeComment gdButton inline" commentID="${x.ID}" ${x.likes < 0 ? "style='transform: translateY(25%)'" : ""} src="../assets/${x.likes < 0 ? "dis" : ""}like.png" height=20% style="margin-right: 0.4%">
<img id="likeImg" class="likeComment gdButton inline" commentID="${x.ID}" ${x.likes < 0 ? "style='transform: translateY(25%)'" : ""} src="/assets/${x.likes < 0 ? "dis" : ""}like.png" height=20% style="margin-right: 0.4%">
<h3 class="inline">${x.likes}</h3><br>
</div>
</div>`)
@ -325,9 +325,9 @@ $('#submitComment').click(function () {
.fail(e => { allowEsc = true; $('.postbutton').show(); $('#message').text(e.responseText.includes("DOCTYPE") ? "Something went wrong..." : e.responseText) })
})
let commentID = 0;
let likeCount, likeImg;
let likedComments;
let commentID = 0
let likeCount, likeImg
let likedComments
let like = true;
$('#likebtn').click(function() {
@ -346,7 +346,7 @@ $(document).on('click', '.likeComment', function(cmnt) {
commentID = $(this).attr('commentID')
likedComments = localStorage.likedComments ? JSON.parse(localStorage.likedComments) : []
if (likedComments.includes(commentID)) return;
if (likedComments.includes(commentID)) return
lvID = $(this).attr('levelID') || 0
likeImg = $(this).find('img')
@ -356,7 +356,7 @@ $(document).on('click', '.likeComment', function(cmnt) {
$('#submitVote').click(function() {
if (likedComments.includes(commentID)) return $('#likeMessage').text("You've already liked/disliked this comment!");
if (likedComments.includes(commentID)) return $('#likeMessage').text("You've already liked/disliked this comment!")
let ID = commentID
let username = $('#like-username').val()
@ -364,14 +364,14 @@ $('#submitVote').click(function() {
let extraID = lvID || window.location.pathname.split('/')[2]
let accountID = 0
let likeType = like ? "1" : "0"
if (!ID || !username || !password || loadingComments) return $('#postComment').hide()
$('#likeMessage').text(like ? "Liking..." : "Disliking... :(")
$('.postbutton').hide()
allowEsc = false
Fetch(`../api/profile/${username}`).then(res => {
Fetch(`/api/profile/${username}`).then(res => {
if (!res || res == "-1") {allowEsc = true; $('.postbutton').show(); return $('#likeMessage').text("The username you provided doesn't exist!")}
else accountID = res.accountID
@ -379,8 +379,8 @@ $('#submitVote').click(function() {
.done(x => {
let newCount = parseInt(likeCount.text()) + (like ? 1 : -1)
likeCount.text(newCount)
if (newCount < 0) likeImg.attr('src', '../assets/dislike.png').css('transform', compact ? 'translateY(15%)' : 'translateY(25%)')
else likeImg.attr('src', '../assets/like.png').removeAttr('style')
if (newCount < 0) likeImg.attr('src', '/assets/dislike.png').css('transform', compact ? 'translateY(15%)' : 'translateY(25%)')
else likeImg.attr('src', '/assets/like.png').removeAttr('style')
$('#likeComment').hide()
$('#likebtn').trigger('click')
$('.postbutton').show()
@ -388,7 +388,7 @@ $('#submitVote').click(function() {
allowEsc = true
likedComments.push(commentID)
localStorage.setItem('likedComments', JSON.stringify(likedComments))
})
})
.fail(e => {allowEsc = true; $('.postbutton').show();$('#likeMessage').text(e.responseText.includes("DOCTYPE") ? "Something went wrong..." : e.responseText)})
})
})
@ -400,14 +400,14 @@ $('#compactMode').click(function() {
$('.profilePostHide').hide()
$('.statusIcon').show()
$('#statusDiv').css('height', $('#statusDiv').attr('compactHeight'))
$(this).attr('src', "../assets/expanded-on.png")
$(this).attr('src', "/assets/expanded-on.png")
}
else {
$('.profilePostHide').show()
$('.statusIcon').hide()
$('.statusIcon').hide()
$('#statusDiv').css('height', $('#statusDiv').attr('normalHeight'))
$(this).attr('src', "../assets/expanded-off.png")
$(this).attr('src', "/assets/expanded-off.png")
}
})
@ -416,12 +416,12 @@ $('#leavePost').on("change keyup keydown paste click", "textarea", function () {
})
$(document).keydown(function(k) {
if ($('#content').is(':visible')) {
if (k.which == 13) k.preventDefault() //enter
}
if (loadingComments || $('.popup').is(":visible")) return;
if (loadingComments || $('.popup').is(":visible")) return
if (k.which == 37 && $('#pageDown').is(":visible")) { //left
$('#pageDown').trigger('click')
@ -431,7 +431,7 @@ $(document).keydown(function(k) {
$('#pageUp').trigger('click')
}
})
})
</script>

View file

@ -1,9 +1,9 @@
<head>
<title id="tabTitle">Level Search</title>
<meta charset="utf-8">
<link href="../assets/css/browser.css?v=1" type="text/css" rel="stylesheet">
<link href="/assets/css/browser.css?v=1" type="text/css" rel="stylesheet">
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-135255146-3"></script><script>window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);}gtag('js', new Date());gtag('config', 'UA-135255146-3');</script>
<link rel="icon" href="../assets/coin.png">
<link rel="icon" href="/assets/coin.png">
<meta id="meta-title" property="og:title" content="Level Search">
<meta id="meta-desc" property="og:description" content="Search for Geometry Dash levels, and filter by length, difficulty, song + more!">
<meta id="meta-image" name="og:image" itemprop="image" content="../coin.png">
@ -18,15 +18,15 @@
<div class="brownbox bounce center supercenter" style="width: 60vh; height: 34%">
<h2 class="smaller center" style="font-size: 5.5vh; margin-top: 1%">Jump to Page</h2>
<input type="number" id="pageSelect" placeholder="1"><br>
<img src="../assets/ok.png" height=20%; id="pageJump" class="gdButton center closeWindow">
<img class="closeWindow gdButton" src="../assets/close.png" height="25%" style="position: absolute; top: -13.5%; left: -6vh">
<img src="/assets/ok.png" height=20%; id="pageJump" class="gdButton center closeWindow">
<img class="closeWindow gdButton" src="/assets/close.png" height="25%" style="position: absolute; top: -13.5%; left: -6vh">
<div class="supercenter" style="left: 25%; top: 43%; height: 10%;">
<img class="gdButton" src="../assets/whitearrow-left.png" height="160%" onclick="$('#pageSelect').val(parseInt($('#pageSelect').val() || 0) - 1); $('#pageSelect').trigger('input');">
<img class="gdButton" src="/assets/whitearrow-left.png" height="160%" onclick="$('#pageSelect').val(parseInt($('#pageSelect').val() || 0) - 1); $('#pageSelect').trigger('input');">
</div>
<div class="supercenter" style="left: 75%; top: 43%; height: 10%;">
<img class="gdButton" src="../assets/whitearrow-right.png" height="160%" onclick="$('#pageSelect').val(parseInt($('#pageSelect').val() || 0) + 1); $('#pageSelect').trigger('input');">
<img class="gdButton" src="/assets/whitearrow-right.png" height="160%" onclick="$('#pageSelect').val(parseInt($('#pageSelect').val() || 0) + 1); $('#pageSelect').trigger('input');">
</div>
</div>
</div>
@ -37,8 +37,8 @@
<p class="bigger center" style="line-height: 5vh; margin-top: 1.5vh;">
Delete all saved online levels?<br><cy>Levels will be cleared from your browser.</cy>
</p>
<img src="../assets/btn-cancel-green.png" height=25%; class="gdButton center closeWindow">
<img src="../assets/btn-delete.png" height=25%; id="purgeSaved" class="gdButton center sideSpaceB">
<img src="/assets/btn-cancel-green.png" height=25%; class="gdButton center closeWindow">
<img src="/assets/btn-delete.png" height=25%; id="purgeSaved" class="gdButton center sideSpaceB">
</div>
</div>
@ -49,22 +49,22 @@
A random level cannot be picked with your current <cy>search filters!</cy>
This is because there is no way to tell how many results were found, due to the GD servers inaccurately saying there's <cg>9999</cg>.
</p>
<img src="../assets/ok.png" width=20%; class="gdButton center closeWindow">
<img src="/assets/ok.png" width=20%; class="gdButton center closeWindow">
</div>
</div>
<div style="position:absolute; bottom: 0%; left: 0%; width: 100%">
<img class="cornerPiece" src="../assets/corner.png" width=7%;>
<img class="cornerPiece" src="/assets/corner.png" width=7%;>
</div>
<div style="position:absolute; bottom: 0%; right: 0%; width: 100%; text-align: right;">
<img class="cornerPiece" src="../assets/corner.png" width=7%; style="transform: scaleX(-1)">
<img class="cornerPiece" src="/assets/corner.png" width=7%; style="transform: scaleX(-1)">
</div>
<div id="searchBox" class="supercenter dragscroll">
<div style="height: 4.5%"></div>
</div>
<div class="epicbox supercenter gs" style="width: 120vh; height: 80%; pointer-events: none;"></div>
<div class="center" style="position:absolute; top: 8%; left: 0%; right: 0%">
@ -76,61 +76,62 @@
</div>
<div title="Jump to page" style="text-align: right; position:absolute; top: 7.5%; right: 2%; height: 12%;">
<img src="../assets/magnify.png" height="60%" class="gdButton" style="margin-top: 5%" onclick="$('#pageDiv').show(); $('#pageSelect').focus().select()">
<img src="/assets/magnify.png" height="60%" class="gdButton" style="margin-top: 5%" onclick="$('#pageDiv').show(); $('#pageSelect').focus().select()">
</div>
<div id="shuffle" title="Random level" style="display: none; text-align: right; position:absolute; top: 7.5%; right: 6.5%; height: 12%;">
<img src="../assets/random.png" height="60%" class="gdButton" style="margin-top: 5%">
<img src="/assets/random.png" height="60%" class="gdButton" style="margin-top: 5%">
</div>
<div id="lastPage" title="Last page" style="display: none; text-align: right; position:absolute; top: 7.5%; right: 11%; height: 11%;">
<img src="../assets/double-arrow.png" height="60%" class="gdButton" style="margin-top: 5%">
<img src="/assets/double-arrow.png" height="60%" class="gdButton" style="margin-top: 5%">
</div>
<div style="position:absolute; top: 2%; left: 1.5%; width: 10%; height: 25%; pointer-events: none">
<img class="gdButton yesClick" id="backButton" src="../assets/back.png" height="30%" onclick="backButton()">
<img class="gdButton yesClick" id="backButton" src="/assets/back.png" height="30%" onclick="backButton()">
</div>
<div id="purge" style="position:absolute; bottom: 1%; right: -3%; width: 10%; display:none;">
<img class="gdButton" src="../assets/delete.png" width="60%" onclick="$('#purgeDiv').show()">
<img class="gdButton" src="/assets/delete.png" width="60%" onclick="$('#purgeDiv').show()">
</div>
<div style="position:absolute; bottom: 2%; right: 1%; text-align: right; width: 15%;">
<img class="gdButton" src="../assets/refresh.png" width="40%" id="refreshPage"></a>
<img class="gdButton" src="/assets/refresh.png" width="40%" id="refreshPage"></a>
</div>
<div style="position:absolute; bottom: 2%; right: 8.5%; text-align: right; width: 15%; display: none" id="gdWorld">
<a title="Geometry Dash World" href="/search/*?type=gdw"><img class="gdButton" src="../assets/gdw_circle.png" width="40%"></a>
<a title="Geometry Dash World" href="/search/*?type=gdw"><img class="gdButton" src="/assets/gdw_circle.png" width="40%"></a>
</div>
<div style="position:absolute; bottom: 2%; right: 8.5%; text-align: right; width: 15%; display: none" id="normalGD">
<a title="Back to Geometry Dash" href="/search/*?type=featured"><img class="gdButton" src="../assets/gd_circle.png" width="40%"></a>
<a title="Back to Geometry Dash" href="/search/*?type=featured"><img class="gdButton" src="/assets/gd_circle.png" width="40%"></a>
</div>
<div style="position: absolute; left: 7%; top: 45%; height: 10%;">
<img class="gdButton" id="pageDown" style="display: none"; src="../assets/arrow-left.png" height="90%">
<img class="gdButton" id="pageDown" style="display: none"; src="/assets/arrow-left.png" height="90%">
</div>
<div style="position: absolute; right: 7%; top: 45%; height: 10%;">
<img class="gdButton" id="pageUp" style="display: none"; src="../assets/arrow-right.png" height="90%">
<img class="gdButton" id="pageUp" style="display: none"; src="/assets/arrow-right.png" height="90%">
</div>
<div class="supercenter" id="loading" style="height: 10%; top: 47%; display: none;">
<img class="spin noSelect" src="../assets/loading.png" height="105%">
<img class="spin noSelect" src="/assets/loading.png" height="105%">
</div>
</div>
</body>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script type="text/javascript" src="../global.js?v=1"></script>
<script type="text/javascript" src="./global.js?v=1"></script>
<script type="text/javascript" src="../dragscroll.js"></script>
<script>
"use strict";
$('#pageDown').hide()
$('#pageUp').hide()
let accID;
let accID
let path = location.pathname.replace('/search/', "")
let url = new URL(window.location.href)
let gauntlet = url.searchParams.get('gauntlet')
@ -140,7 +141,7 @@ let list = url.searchParams.get('list')
let count = url.searchParams.get('count')
let header = url.searchParams.get('header')
let demonList = ["demonList", "demonlist"].some(x => typeof url.searchParams.get(x) == "string" || type == x)
let loading = false;
let loading = false
let gauntlets = ["Fire", "Ice", "Poison", "Shadow", "Lava", "Bonus", "Chaos", "Demon", "Time", "Crystal", "Magic", "Spike", "Monster", "Doom", "Death"]
let page = Math.max(1, url.searchParams.get('page')) - 1
@ -152,24 +153,26 @@ let superSearch = ['*', '*?type=mostliked', '*?type=mostdownloaded', '*?type=rec
let pageCache = {}
let demonListLink = "https://pointercrate.com/"
let searchFilters = `../api/search/${type == 'saved' ? JSON.parse(localStorage.getItem('saved') || '[]').reverse().toString() : accID || path}?page=[PAGE]${count ? "" : "&count=10"}${window.location.search.replace(/\?/g, "&").replace("page", "nope")}`
let searchFilters = `/api/search/${type == 'saved' ? JSON.parse(localStorage.getItem('saved') || '[]').reverse().toString() : accID || path}?page=[PAGE]${count ? "" : "&count=10"}${window.location.search.replace(/\?/g, "&").replace("page", "nope")}`
function clean(text) {return (text || "").toString().replace(/&/g, "&#38;").replace(/</g, "&#60;").replace(/>/g, "&#62;").replace(/=/g, "&#61;").replace(/"/g, "&#34;").replace(/'/g, "&#39;")}
let clean = text => (text || '').toString().replace(/./gs, c => (
{'&': '&#38;', '<': '&#60;', '>': '&#62;', '=': '&#61;', '"': '&#34;', "'": '&#39;'}[c] || c
))
if (type == "followed") {
let followed = localStorage.followed ? JSON.parse(localStorage.followed) : []
searchFilters += ("&creators=" + followed.join())
searchFilters += ("&creators=" + followed.join())
}
let hostMatch = window.location.host.match(/\./g)
if (hostMatch && hostMatch.length > 1) { // gdps check
if (hostMatch?.length > 1) { // gdps check
$('#gdWorld').remove()
$('#normalGD').remove()
}
function Append(firstLoad, noCache) {
loading = true;
loading = true
if (!firstLoad) $('#pagenum').text(`Page ${(page + 1)}${pages ? ` of ${pages}` : ""}`)
$('#searchBox').html('<div style="height: 4.5%"></div>')
$('#pageSelect').val(page + 1)
@ -207,7 +210,7 @@ function Append(firstLoad, noCache) {
if (demonList) {
demonListLink = res[0].demonList
res = res.sort(function(a, b){return a.demonPosition - b.demonPosition});
res = res.sort((a, b) => a.demonPosition - b.demonPosition);
}
res.forEach((x, y) => {
@ -229,35 +232,35 @@ function Append(firstLoad, noCache) {
<h2 class="pre smaller inline gdButton help ${hasAuthor ? "" : "green unregistered"}" title="Account ID: ${x.accountID}\nPlayer ID: ${x.playerID}"><!--
-->${hasAuthor && !onePointNine ? `<a style="margin-right: 0.66vh" href="../u/${x.accountID}.">By ${x.author || "-"}</a>` : `<a ${userSearch ? "" : `href="../search/${x.playerID}?user"`}>By ${x.author || "-"}</a>`}</h2><!--
--><h2 class="inline" style="margin-left: 1.5%; transform:translateY(30%)"> ${x.copiedID == '0' ? "" : `<a target="_blank" href="../${x.copiedID}"><!--
--><img class="gdButton valign sideSpaceD" title="Original: ${x.copiedID}" src="../assets/copied.png" height="12%"></a>`}<!--
-->${x.large ? `<img class="help valign sideSpaceD" title="${x.objects}${x.objects == 65535 ? "+" : ""} objects" src="../assets/large.png" height="12%">` : ''}<!--
-->${x.twoPlayer ? `<img class="help valign sideSpaceD" title="Two player level" src="../assets/twoPlayer.png" height="12%">` : ''}
--><img class="gdButton valign sideSpaceD" title="Original: ${x.copiedID}" src="/assets/copied.png" height="12%"></a>`}<!--
-->${x.large ? `<img class="help valign sideSpaceD" title="${x.objects}${x.objects == 65535 ? "+" : ""} objects" src="/assets/large.png" height="12%">` : ''}<!--
-->${x.twoPlayer ? `<img class="help valign sideSpaceD" title="Two player level" src="/assets/twoPlayer.png" height="12%">` : ''}
</h2>
<h3 class="lessSpaced help ${noLink ? "" : 'gdButton '}pre ${songColor}" title="${filteredSong} by ${x.songAuthor} (${x.songID})" style="overflow: hidden; max-height: 19%; width: fit-content; padding: 1% 1% 0% 0%">${noLink ? filteredSong : `<a target="_blank" style="width: fit-content" href="https://www.newgrounds.com/audio/listen/${x.songID}">${filteredSong}</a>`}</h3>
<h3 class="lessSpaced" style="width: fit-content" title="">
<img class="help valign rightSpace" title="Length" src="../assets/time.png" height="14%">${x.length}
<img class="help valign rightSpace" title="Downloads" src="../assets/download.png" height="14%">${x.downloads}
<img class="help valign rightSpace" title="Likes" src="../assets/${x.disliked ? 'dis' : ''}like.png" height="14%">${x.likes}
${x.orbs != 0 ? `<img class="help valign rightSpace" title="Mana Orbs" src="../assets/orbs.png" height="14%">${x.orbs}` : ""}
<img class="help valign rightSpace" title="Length" src="/assets/time.png" height="14%">${x.length}
<img class="help valign rightSpace" title="Downloads" src="/assets/download.png" height="14%">${x.downloads}
<img class="help valign rightSpace" title="Likes" src="/assets/${x.disliked ? 'dis' : ''}like.png" height="14%">${x.likes}
${x.orbs != 0 ? `<img class="help valign rightSpace" title="Mana Orbs" src="/assets/orbs.png" height="14%">${x.orbs}` : ""}
</h3>
<div class="center" style="position:absolute; top: ${6.5 + (y * 33.5) + (x.coins == 0 ? 2.5 : 0)}%; left: 4.4%; transform:scale(0.82); height: 10%; width: 12.5%;">
<img class="help spaced" id="dFace" title="${x.difficulty}${x.epic ? " (Epic)" : x.featured ? " (Featured)" : ""}" src="../assets/difficulties/${x.difficultyFace}.png" height="150%" style="margin-bottom: 0%; ${x.epic ? 'transform:scale(1.2)' : x.featured ? 'transform:scale(1.1)' : ''}">
<img class="help spaced" id="dFace" title="${x.difficulty}${x.epic ? " (Epic)" : x.featured ? " (Featured)" : ""}" src="/assets/difficulties/${x.difficultyFace}.png" height="150%" style="margin-bottom: 0%; ${x.epic ? 'transform:scale(1.2)' : x.featured ? 'transform:scale(1.1)' : ''}">
<h3 title="">${x.difficulty.includes('Demon') ? "Demon" : x.difficulty}</h3>
${x.stars != 0 && !demonList ? `<h3 class="help" title="${x.stars} star${x.stars == 1 ? "" : "s"}${x.starsRequested ? ` (${x.starsRequested} requested)` : ""}">${x.stars}<img class="valign sideSpaceB" src="../assets/star.png" height="35%" style="transform:translateY(-8%)"></h3>` : ""}
${x.stars != 0 && !demonList ? `<h3 class="help" title="${x.stars} star${x.stars == 1 ? "" : "s"}${x.starsRequested ? ` (${x.starsRequested} requested)` : ""}">${x.stars}<img class="valign sideSpaceB" src="/assets/star.png" height="35%" style="transform:translateY(-8%)"></h3>` : ""}
${demonList ? `<h3 class="help yellow" title="Ranked #${x.demonPosition} on the Demon List">#${x.demonPosition}</h3>` : ""}
<div id="coins" style="margin-top: 3%" title="${x.coins} user coin${x.coins == 1 ? "" : "s"} (${x.verifiedCoins ? "" : "un"}verified)">
${x.coins > 0 ? `<img src="../assets/${x.verifiedCoins ? 'silver' : 'brown'}coin.png" height="50%" class="help">` : ""}
${x.coins > 1 ? `<img src="../assets/${x.verifiedCoins ? 'silver' : 'brown'}coin.png" height="50%" class="help squeezeB">` : ""}
${x.coins > 2 ? `<img src="../assets/${x.verifiedCoins ? 'silver' : 'brown'}coin.png" height="50%" class="help squeezeB">` : ""}
${x.coins > 0 ? `<img src="/assets/${x.verifiedCoins ? 'silver' : 'brown'}coin.png" height="50%" class="help">` : ""}
${x.coins > 1 ? `<img src="/assets/${x.verifiedCoins ? 'silver' : 'brown'}coin.png" height="50%" class="help squeezeB">` : ""}
${x.coins > 2 ? `<img src="/assets/${x.verifiedCoins ? 'silver' : 'brown'}coin.png" height="50%" class="help squeezeB">` : ""}
</div>
</div>
<div class="center" style="position:absolute; right: 7%; transform:translateY(-${demonList ? 19.5 : 16.25}vh); height: 10%">
<a title="View level" href="../${x.id}""><img style="margin-bottom: 4.5%" class="valign gdButton" src="../assets/view.png" height="105%"></a>
${demonList ? `<br><a title="View leaderboard" href="../demon/${x.demonPosition}""><img class="valign gdButton" src="../assets/trophyButton.png" height="110%"></a>
<a title="View on Pointercrate" href="${demonListLink}demonlist/${x.demonPosition}" target=_blank><img class="valign gdButton" src="../assets/demonButton.png" height="110%"></a>` : "" }
<a title="View level" href="../${x.id}""><img style="margin-bottom: 4.5%" class="valign gdButton" src="/assets/view.png" height="105%"></a>
${demonList ? `<br><a title="View leaderboard" href="../demon/${x.demonPosition}""><img class="valign gdButton" src="/assets/trophyButton.png" height="110%"></a>
<a title="View on Pointercrate" href="${demonListLink}demonlist/${x.demonPosition}" target=_blank><img class="valign gdButton" src="/assets/demonButton.png" height="110%"></a>` : "" }
<p title="Level ID" style="text-align: right; color: rgba(0, 0, 0, 0.4); font-size: 2.2vh; transform: translate(2.8vh, ${demonList ? -1.8 : 2.5}vh)">#${x.id}</p>
</div>
</div>`)
@ -336,31 +339,26 @@ if (!$('#header').text() && typeof userMode != "string") {
$('.closeWindow').click(function() {$(".popup").attr('style', 'display: none;')})
$('#purgeSaved').click(function() {
localStorage.removeItem('saved');
localStorage.removeItem('saved')
location.reload()
})
var max = 9999
var min = 1
function onPageSel(that) {
let x = $(that).val()
if (x != "") $(that).val(clamp(Math.floor(x), 1, 9999))
}
$('#pageSelect').on('input', function () {
var x = $(this).val();
if ($(this).val() != "") $(this).val(Math.max(Math.min(Math.floor(x), max), min));
});
$('#pageSelect').on('blur', function () {
var x = $(this).val();
if ($(this).val() != "") $(this).val(Math.max(Math.min(Math.floor(x), max), min));
});
$('#pageSelect').on('input', function() {onPageSel(this)})
$('#pageSelect').on('blur', function() {onPageSel(this)})
$('#shuffle').click(function() {
if (superSearch) {
$('#searchBox').html('<div style="height: 4.5%"></div>')
$('#loading').show()
fetch("../api/search/*?page=0&type=recent").then(res => res.json()).then(recent => {
fetch("/api/search/*?page=0&type=recent").then(res => res.json()).then(recent => {
let mostRecent = recent[0].id
function fetchRandom() {
fetch(`../api/level/${Math.floor(Math.random() * (mostRecent)) + 1}`).then(res => res.json()).then(res => {
fetch(`/api/level/${randInt(0, mostRecent) + 1}`).then(res => res.json()).then(res => {
if (res == "-1" || !res.id) return fetchRandom()
else window.location.href = "../" + res.id
})
@ -371,7 +369,7 @@ $('#shuffle').click(function() {
else if (pages) {
let random = {}
let pageCount = +count || 10
randomResult = Math.floor(Math.random() * (results)) + 1
randomResult = randInt(0, results) + 1
randomPage = Math.ceil(randomResult / pageCount)
randomIndex = randomResult % pageCount
if (randomIndex == 0) randomIndex = pageCount
@ -385,11 +383,11 @@ $('#shuffle').click(function() {
})
$(document).keydown(function(k) {
if (loading) return;
if (loading) return
if ($('#pageDiv').is(':visible')) {
if (k.which == 13) $('#pageJump').trigger('click') //enter
else return;
if (k.which == 13) $('#pageJump').trigger('click') //enter
else return
}
if (k.which == 37 && $('#pageDown').is(":visible")) $('#pageDown').trigger('click') // left

View file

@ -51,4 +51,4 @@
{ "form": "spider", "id": 7, "type": "treasureRoom", "keys": 1 },
{ "form": "spider", "id": 10, "type": "gauntlet", "gauntlet": "Demon" },
{ "form": "spider", "id": 17, "type": "treasureRoom", "keys": 5 }
]
]

View file

@ -1,3 +1,4 @@
"use strict";
const WHITE = 0xffffff
const colorNames = { "1": "Color 1", "2": "Color 2", "g": "Glow", "w": "White", "u": "UFO Dome" }
const formNames = { "player": "icon", "player_ball": "ball", "bird": "ufo", "dart": "wave" }
@ -5,10 +6,19 @@ const loader = PIXI.Loader.shared
const loadedNewIcons = {}
let positionMultiplier = 4
const TAU = Math.PI * 2
// by default, converts degrees to rads
const toRadians = (angle, scale = 360) => TAU / scale * angle
// default rad to deg
const fromRadians = (rad, scale = 360) => rad / (TAU / scale)
// `scale` is the num of subdivisions in a turn. More info: https://en.wikipedia.org/wiki/Turn_(angle)
// `scale = 400` corresponds to gradians, `256` to byte radians, and `100` to percentage of a turn
const positionMultiplier = 4
function positionPart(part, partIndex, layer, formName, isNew, isGlow) {
layer.position.x += (part.pos[0] * positionMultiplier * (isNew ? 0.5 : 1))
layer.position.y -= (part.pos[1] * positionMultiplier * (isNew ? 0.5 : 1))
let truePosMultiplier = positionMultiplier / (isNew ? 2 : 1)
layer.position.x += (part.pos[0] * truePosMultiplier)
layer.position.y -= (part.pos[1] * truePosMultiplier)
layer.scale.x = part.scale[0]
layer.scale.y = part.scale[1]
if (part.flipped[0]) layer.scale.x *= -1
@ -20,17 +30,18 @@ function positionPart(part, partIndex, layer, formName, isNew, isGlow) {
let tintInfo = iconData.robotAnimations.info[formName].tints
let foundTint = tintInfo[partIndex]
if (foundTint > 0) {
let darkenFilter = new PIXI.filters.ColorMatrixFilter();
let darkenFilter = new PIXI.filters.ColorMatrixFilter()
darkenFilter.brightness(0)
darkenFilter.alpha = (255 - foundTint) / 255
darkenFilter.alpha = 1 - foundTint / 255
layer.filters = [darkenFilter]
}
}
}
function validNum(val, defaultVal) {
function sanitizeNum(val, defaultVal) {
let colVal = +val
return isNaN(colVal) ? defaultVal : colVal
// yes, it also checks for NaN
return isFinite(colVal) ? colVal : defaultVal
}
function getGlowColor(colors) {
@ -39,17 +50,17 @@ function getGlowColor(colors) {
return glowCol
}
function validateIconID(id, form) {
let realID = Math.min(iconData.newIconCounts[form], Math.abs(validNum(id, 1)))
function sanitizeIconID(id, form) {
let realID = Math.min(iconData.newIconCounts[form], Math.abs(sanitizeNum(id, 1)))
if (realID == 0 && !["player", "player_ball"].includes(form)) realID = 1
return realID
}
function parseIconColor(col) {
if (!col) return WHITE
else if (typeof col == "string" && col.length >= 6) return parseInt(col, 16)
if (typeof col == "string" && col.length >= 6) return parseInt(col, 16)
let rgb = iconData.colors[col]
return rgb ? rgbToDecimal(rgb) : WHITE;
return rgb ? rgb2Pac(rgb) : WHITE
}
function parseIconForm(form) {
@ -58,7 +69,7 @@ function parseIconForm(form) {
}
function loadIconLayers(form, id, cb) {
let iconStr = `${form}_${padZero(validateIconID(id, form))}`
let iconStr = `${form}_${padZero(sanitizeIconID(id, form))}`
let texturesToLoad = Object.keys(iconData.gameSheet).filter(x => x.startsWith(iconStr + "_"))
if (loadedNewIcons[texturesToLoad[0]]) return cb(loader, loader.resources, true)
@ -67,7 +78,10 @@ function loadIconLayers(form, id, cb) {
if (iconData.newIcons.includes(iconStr)) return loadNewIcon(iconStr, cb)
}
loader.add(texturesToLoad.filter(x => !loader.resources[x]).map(x => ({ name: x, url: `/iconkit/icons/${x}` })))
loader.add(texturesToLoad
.filter(x => !loader.resources[x])
.map(x => ({ name: x, url: `/iconkit/icons/${x}` }))
)
loader.load(cb) // no params
}
@ -80,11 +94,11 @@ function loadNewIcon(iconStr, cb) {
loader.add({ name: sheetName, url: `/iconkit/newicons/${iconStr}-hd.png` })
loader.load((l, resources) => {
let texture = resources[sheetName].texture
Object.keys(data).forEach(x => {
let bounds = data[x]
Object.keys(data).forEach(k => {
let bounds = data[k]
let textureRect = new PIXI.Rectangle(bounds.pos[0], bounds.pos[1], bounds.size[0], bounds.size[1])
let partTexture = new PIXI.Texture(texture, textureRect)
loadedNewIcons[x] = partTexture
loadedNewIcons[k] = partTexture
})
cb(l, resources, true)
})
@ -104,23 +118,23 @@ function parseNewPlist(data) {
iconData.gameSheet[frameName] = {}
positionData[frameName] = {}
for (let n=0; n < frameData.length; n += 2) {
for (let n = 0; n < frameData.length; n += 2) {
let keyName = frameData[n].innerHTML
let keyData = frameData[n + 1].innerHTML
if (["spriteOffset", "spriteSize", "spriteSourceSize"].includes(keyName)) {
iconData.gameSheet[frameName][keyName] = parseWeirdArray(keyData)
switch (keyName) {
case "spriteOffset": case "spriteSize": case "spriteSourceSize":
iconData.gameSheet[frameName][keyName] = parseWeirdArray(keyData)
break
case "textureRotated":
isRotated = frameData[n + 1].outerHTML.includes("true")
iconData.gameSheet[frameName][keyName] = isRotated
break
case "textureRect":
let textureArr = keyData.slice(1, -1).split("},{").map(parseWeirdArray)
positionData[frameName].pos = textureArr[0]
positionData[frameName].size = textureArr[1]
break
}
else if (keyName == "textureRotated") {
isRotated = frameData[n + 1].outerHTML.includes("true")
iconData.gameSheet[frameName][keyName] = isRotated
}
else if (keyName == "textureRect") {
let textureArr = keyData.slice(1, -1).split("},{").map(x => parseWeirdArray(x))
positionData[frameName].pos = textureArr[0]
positionData[frameName].size = textureArr[1]
}
}
if (isRotated) positionData[frameName].size.reverse()
@ -129,35 +143,37 @@ function parseNewPlist(data) {
return positionData
}
function parseWeirdArray(data) {
return data.replace(/[^0-9,-]/g, "").split(",").map(x => +x)
}
let parseWeirdArray = data => data.replace(/[^\d,-]/g, "").split(",").map(x => +x)
function padZero(num) {
let numStr = num.toString()
if (num < 10) numStr = "0" + numStr
return numStr
}
let padZero = num => num.toString().padStart(2, "0")
function rgbToDecimal(rgb) {
return (rgb.r << 16) + (rgb.g << 8) + rgb.b;
}
/*
name explanation:
`Number`s are not decimals, because they are not `String`s, and the internal representation is binary.
This means "rgbToDecimal" is a misleading name.
Converting independent color components into one number is called "color packing".
So I thougth it would be funny to rename `rgbToPacked` into `rgb2Pac`.
Alternative names could be `rgbPacker` or `packRGB`, IDK
- said by @Rudxain
*/
let rgb2Pac = rgb => (rgb.r << 16) | (rgb.g << 8) | rgb.b
class Icon {
constructor(data={}, cb) {
this.app = data.app
this.sprite = new PIXI.Container();
this.sprite = new PIXI.Container()
this.form = data.form || "player"
this.id = validateIconID(data.id, this.form)
this.id = sanitizeIconID(data.id, this.form)
this.new = !!data.new
this.colors = {
"1": validNum(data.col1, 0xafafaf), // primary
"2": validNum(data.col2, WHITE), // secondary
"g": validNum(data.colG, validNum(+data.colg, null)), // glow
"w": validNum(data.colW, validNum(+data.colw, WHITE)), // white
"u": validNum(data.colU, validNum(+data.colu, WHITE)), // ufo
"1": sanitizeNum(data.col1, 0xafafaf), // primary
"2": sanitizeNum(data.col2, WHITE), // secondary
"g": sanitizeNum(data.colG, sanitizeNum(data.colg, null)), // glow
"w": sanitizeNum(data.colW, sanitizeNum(data.colw, WHITE)), // white
"u": sanitizeNum(data.colU, sanitizeNum(data.colu, WHITE)), // ufo
}
this.glow = !!data.glow
this.layers = []
this.glowLayers = []
@ -178,22 +194,24 @@ class Icon {
let idlePosition = this.getAnimation(data.animation, data.animationForm).frames[0]
idlePosition.forEach((x, y) => {
x.name = iconData.robotAnimations.info[this.form].names[y]
let part = new IconPart(this.form, this.id, this.colors, false, { part: x, skipGlow: true, new: this.new })
positionPart(x, y, part.sprite, this.form, this.new)
let glowPart = new IconPart(this.form, this.id, this.colors, true, { part: x, onlyGlow: true, new: this.new })
positionPart(x, y, glowPart.sprite, this.form, this.new, true)
glowPart.sprite.visible = this.glow
this.glowLayers.push(glowPart)
this.layers.push(part)
this.sprite.addChild(part.sprite)
})
let fullGlow = new PIXI.Container();
let fullGlow = new PIXI.Container()
this.glowLayers.forEach(x => fullGlow.addChild(x.sprite))
this.sprite.addChildAt(fullGlow, 0)
if (typeof Ease !== "undefined") this.ease = new Ease.Ease()
if (typeof Ease != "undefined") this.ease = new Ease.Ease()
this.animationSpeed = Math.abs(Number(data.animationSpeed) || 1)
if (data.animation) this.setAnimation(data.animation, data.animationForm)
}
@ -209,14 +227,16 @@ class Icon {
getAllLayers() {
let allLayers = [];
(this.complex ? this.glowLayers : []).concat(this.layers).forEach(x => x.sections.forEach(s => allLayers.push(s)))
(this.complex ? this.glowLayers : [])
.concat(this.layers)
.forEach(x => x.sections.forEach(s => allLayers.push(s)))
return allLayers
}
setColor(colorType, newColor, extra={}) {
let colorStr = String(colorType).toLowerCase()
if (!colorType || !Object.keys(this.colors).includes(colorStr)) return
else this.colors[colorStr] = newColor
if (!colorType || !this.colors.hasOwnProperty(colorStr)) return
this.colors[colorStr] = newColor
let newGlow = getGlowColor(this.colors)
this.getAllLayers().forEach(x => {
if (colorType != "g" && x.colorType == colorStr) x.setColor(newColor)
@ -228,13 +248,9 @@ class Icon {
}
}
formName() {
return formNames[this.form] || this.form
}
formName() { return formNames[this.form] || this.form }
isGlowing() {
return this.glowLayers[0].sprite.visible
}
isGlowing() { return this.glowLayers[0].sprite.visible }
setGlow(toggle) {
this.glow = !!toggle
@ -258,7 +274,7 @@ class Icon {
animData.frames[this.animationFrame].forEach((newPart, index) => {
let section = this.layers[index]
let glowSection = this.glowLayers[index]
let truePosMultiplier = this.new ? positionMultiplier * 0.5 : positionMultiplier
let truePosMultiplier = positionMultiplier / (this.new ? 2 : 1)
if (!section) return
// gd is weird with negative rotations
@ -270,16 +286,16 @@ class Icon {
y: newPart.pos[1] * truePosMultiplier * -1,
scaleX: newPart.scale[0],
scaleY: newPart.scale[1],
rotation: realRot * (Math.PI / 180) // radians
rotation: toRadians(realRot)
}
if (newPart.flipped[0]) movementData.scaleX *= -1
if (newPart.flipped[1]) movementData.scaleY *= -1
let bothSections = [section, glowSection]
bothSections.forEach((x, y) => {
let easing = this.ease.add(x.sprite, movementData, { duration: duration || 1, ease: 'linear' })
let easing = this.ease.add(x.sprite, movementData, { duration: duration || 1, ease: "linear" })
let continueAfterEase = animData.frames.length > 1 && y == 0 && index == 0 && animName == this.animationName
if (continueAfterEase) easing.on('complete', () => {
if (continueAfterEase) easing.on("complete", () => {
this.animationFrame++
if (this.animationFrame >= animData.frames.length) {
if (animData.info.loop) this.animationFrame = 0
@ -294,7 +310,7 @@ class Icon {
// find actual icon size by reading pixel data (otherwise there's whitespace and shit)
if (this.new) this.sprite.scale.set(1)
let spriteSize = [Math.round(this.sprite.width), Math.round(this.sprite.height)]
let pixels = this.app.renderer.plugins.extract.pixels(this.sprite);
let pixels = this.app.renderer.plugins.extract.pixels(this.sprite)
let xRange = [spriteSize[0], 0]
let yRange = [spriteSize[1], 0]
@ -316,7 +332,7 @@ class Icon {
// this took hours to figure out. i fucking hate my life
xRange[1]++
yRange[1]++
let realWidth = xRange[1] - xRange[0]
let realHeight = yRange[1] - yRange[0]
@ -336,35 +352,35 @@ class Icon {
toDataURL(dataType="image/png") {
this.autocrop()
this.app.renderer.render(this.app.stage);
let b64data = this.app.view.toDataURL(dataType);
this.app.renderer.render(this.app.stage)
let b64data = this.app.view.toDataURL(dataType)
this.revertCrop()
return b64data
}
pngExport() {
let b64data = this.toDataURL()
let downloader = document.createElement('a');
let downloader = document.createElement("a")
downloader.href = b64data
downloader.setAttribute("download", `${this.formName()}_${this.id}.png`);
document.body.appendChild(downloader);
downloader.click();
document.body.removeChild(downloader);
downloader.setAttribute("download", `${this.formName()}_${this.id}.png`)
document.body.appendChild(downloader)
downloader.click()
document.body.removeChild(downloader)
}
copyToClipboard() {
this.autocrop()
this.app.renderer.render(app.stage);
this.app.renderer.render(app.stage)
this.app.view.toBlob(blob => {
let item = new ClipboardItem({ "image/png": blob });
navigator.clipboard.write([item]);
});
let item = new ClipboardItem({ "image/png": blob })
navigator.clipboard.write([item])
})
this.revertCrop()
}
psdExport() {
if (typeof agPsd === "undefined") throw new Error("ag-psd not imported!")
if (typeof agPsd == "undefined") throw new Error("ag-psd not imported!")
let glowing = this.isGlowing()
this.setGlow(true)
@ -376,10 +392,10 @@ class Icon {
function addPSDLayer(layer, parent, sprite) {
allLayers.forEach(x => x.sprite.alpha = 0)
layer.sprite.alpha = 255
let layerChild = { name: layer.colorName, canvas: renderer.plugins.extract.canvas(sprite) }
if (layer.colorType == "g") {
if (parent.part) layerChild.name = parent.part.name + " glow"
if (parent.part) layerChild.name = `${parent.part.name} glow`
else layerChild.blendMode = "linear dodge"
if (!complex && !glowing) layerChild.hidden = true
}
@ -404,13 +420,13 @@ class Icon {
allLayers.forEach(x => x.sprite.alpha = 255)
let output = agPsd.writePsd(psd)
let blob = new Blob([output]);
let downloader = document.createElement('a');
downloader.href = URL.createObjectURL(blob);
downloader.setAttribute("download", `${this.formName()}_${this.id}.psd`);
document.body.appendChild(downloader);
downloader.click();
document.body.removeChild(downloader);
let blob = new Blob([output])
let downloader = document.createElement("a")
downloader.href = URL.createObjectURL(blob)
downloader.setAttribute("download", `${this.formName()}_${this.id}.psd`)
document.body.appendChild(downloader)
downloader.click()
document.body.removeChild(downloader)
this.setGlow(glowing)
}
}
@ -421,11 +437,11 @@ class IconPart {
if (colors[1] == 0 && !misc.skipGlow) glow = true // add glow if p1 is black
let iconPath = `${form}_${padZero(id)}`
let partString = misc.part ? "_" + padZero(misc.part.part) : ""
let partString = misc.part ? `_${padZero(misc.part.part)}` : ""
let sections = {}
if (misc.part) this.part = misc.part
this.sprite = new PIXI.Container();
this.sprite = new PIXI.Container()
this.sections = []
if (!misc.skipGlow) {
@ -448,11 +464,22 @@ class IconPart {
}
}
let layerOrder = ["glow", "ufo", "col2", "col1", "white"].map(x => sections[x]).filter(x => x)
layerOrder.forEach(x => {
let layerOrder = ["glow", "ufo", "col2", "col1", "white"]
for (let x of layerOrder) {
x = sections[x]
if (!x) continue
this.sections.push(x)
this.sprite.addChild(x.sprite)
})
}
/* alternative:
layerOrder.map(x => sections[x])
.filter(x => x)
.forEach(x => {
this.sections.push(x)
this.sprite.addChild(x.sprite)
})
*/
}
}
@ -479,7 +506,7 @@ class IconLayer {
}
setColor(color) {
this.color = validNum(color, WHITE)
this.color = sanitizeNum(color, WHITE)
this.sprite.tint = this.color
}
}
}

View file

@ -1,4 +1,4 @@
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.agPsd = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
(function(f){if(typeof exports=="object"&&typeof module!="undefined"){module.exports=f()}else if(typeof define=="function"&&define.amd){define([],f)}else{var g;if(typeof window!="undefined"){g=window}else if(typeof global!="undefined"){g=global}else if(typeof self!="undefined"){g=self}else{g=this}g.agPsd = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.readAbr = void 0;
@ -1189,7 +1189,7 @@ addHandler('Anno', function (target) { return target.annotations !== undefined;
var sound = annotation.type === 'sound';
if (sound && !(annotation.data instanceof Uint8Array))
throw new Error('Sound annotation data should be Uint8Array');
if (!sound && typeof annotation.data !== 'string')
if (!sound && typeof annotation.data != 'string')
throw new Error('Text annotation data should be string');
var lengthOffset = writer.offset;
psdWriter_1.writeUint32(writer, 0); // length
@ -3049,16 +3049,16 @@ function getTypeByKey(key, value, root) {
return ('Wdth' in value) ? 'Objc' : (('units' in value) ? 'UntF' : 'doub');
}
else if (key === 'Type') {
return typeof value === 'string' ? 'enum' : 'long';
return typeof value == 'string' ? 'enum' : 'long';
}
else if (key === 'AntA') {
return typeof value === 'string' ? 'enum' : 'bool';
return typeof value == 'string' ? 'enum' : 'bool';
}
else if (key === 'Hrzn' || key === 'Vrtc' || key === 'Top ' || key === 'Left' || key === 'Btom' || key === 'Rght') {
return typeof value === 'number' ? 'doub' : 'UntF';
return typeof value == 'number' ? 'doub' : 'UntF';
}
else if (key === 'Vrsn') {
return typeof value === 'number' ? 'long' : 'Objc';
return typeof value == 'number' ? 'long' : 'Objc';
}
else if (key === 'Rd ' || key === 'Grn ' || key === 'Bl ') {
return root === 'artd' ? 'long' : 'doub';
@ -3390,7 +3390,7 @@ function writeReferenceStructure(writer, _key, items) {
for (var i = 0; i < items.length; i++) {
var value = items[i];
var type = 'unknown';
if (typeof value === 'string') {
if (typeof value == 'string') {
if (/^[a-z]+\.[a-z]+$/i.test(value)) {
type = 'Enmr';
}
@ -3485,7 +3485,7 @@ function parseUnits(_a) {
exports.parseUnits = parseUnits;
function parseUnitsOrNumber(value, units) {
if (units === void 0) { units = 'Pixels'; }
if (typeof value === 'number')
if (typeof value == 'number')
return { value: value, units: units };
return parseUnits(value);
}
@ -3508,10 +3508,10 @@ exports.unitsPercent = unitsPercent;
function unitsValue(x, key) {
if (x == null)
return { units: 'Pixels', value: 0 };
if (typeof x !== 'object')
if (typeof x != 'object')
throw new Error("Invalid value: " + JSON.stringify(x) + " (key: " + key + ") (should have value and units)");
var units = x.units, value = x.value;
if (typeof value !== 'number')
if (typeof value != 'number')
throw new Error("Invalid value in " + JSON.stringify(x) + " (key: " + key + ")");
if (units !== 'Pixels' && units !== 'Millimeters' && units !== 'Points' && units !== 'None' &&
units !== 'Picas' && units !== 'Inches' && units !== 'Centimeters' && units !== 'Density') {
@ -4009,7 +4009,7 @@ function parseEngineData(data) {
if (!stack.length)
throw new Error('Invalid data');
var top = stack[stack.length - 1];
if (typeof top === 'string') {
if (typeof top == 'string') {
stack[stack.length - 2][top] = value;
pop();
}
@ -4024,7 +4024,7 @@ function parseEngineData(data) {
if (!stack.length)
pushContainer({});
var top = stack[stack.length - 1];
if (top && typeof top === 'string') {
if (top && typeof top == 'string') {
if (name === 'nil') {
pushValue(null);
}
@ -4032,7 +4032,7 @@ function parseEngineData(data) {
pushValue("/" + name);
}
}
else if (top && typeof top === 'object') {
else if (top && typeof top == 'object') {
stack.push(name);
}
else {
@ -4196,15 +4196,15 @@ function serializeEngineData(data, condensed) {
writePrefix();
writeString(condensed ? '/nil' : 'null');
}
else if (typeof value === 'number') {
else if (typeof value == 'number') {
writePrefix();
writeString(serializeNumber(value, key));
}
else if (typeof value === 'boolean') {
else if (typeof value == 'boolean') {
writePrefix();
writeString(value ? 'true' : 'false');
}
else if (typeof value === 'string') {
else if (typeof value == 'string') {
writePrefix();
if ((key === '99' || key === '98') && value.charAt(0) === '/') {
writeString(value);
@ -4223,7 +4223,7 @@ function serializeEngineData(data, condensed) {
}
else if (Array.isArray(value)) {
writePrefix();
if (value.every(function (x) { return typeof x === 'number'; })) {
if (value.every(function (x) { return typeof x == 'number'; })) {
writeString('[');
var intArray = intArrays.indexOf(key) !== -1;
for (var _i = 0, value_1 = value; _i < value_1.length; _i++) {
@ -4247,7 +4247,7 @@ function serializeEngineData(data, condensed) {
writeString(']');
}
}
else if (typeof value === 'object') {
else if (typeof value == 'object') {
if (inProperty && !condensed)
writeString('\n');
writeIndent();
@ -4266,7 +4266,7 @@ function serializeEngineData(data, condensed) {
return undefined;
}
if (condensed) {
if (typeof data === 'object') {
if (typeof data == 'object') {
for (var _i = 0, _a = getKeys(data); _i < _a.length; _i++) {
var key = _a[_i];
writeProperty(key, data[key]);
@ -4560,7 +4560,7 @@ var createImageData = function (width, height) {
return tempCanvas.getContext('2d').createImageData(width, height);
};
exports.createImageData = createImageData;
if (typeof document !== 'undefined') {
if (typeof document != 'undefined') {
exports.createCanvas = function (width, height) {
var canvas = document.createElement('canvas');
canvas.width = width;
@ -5225,7 +5225,7 @@ function writePsdUint8Array(psd, options) {
}
exports.writePsdUint8Array = writePsdUint8Array;
function writePsdBuffer(psd, options) {
if (typeof Buffer === 'undefined') {
if (typeof Buffer == 'undefined') {
throw new Error('Buffer not supported on this platform');
}
return Buffer.from(writePsdUint8Array(psd, options));
@ -7050,7 +7050,7 @@ function deduplicateValues(base, runs, keys) {
if (Array.isArray(value)) {
identical = runs.every(function (r) { return arraysEqual(r.style[key], value); });
}
else if (typeof value === 'object') {
else if (typeof value == 'object') {
identical = runs.every(function (r) { return objectsEqual(r.style[key], value); });
}
else {
@ -7068,7 +7068,7 @@ function deduplicateValues(base, runs, keys) {
if (Array.isArray(value)) {
same = arraysEqual(r.style[key], value);
}
else if (typeof value === 'object') {
else if (typeof value == 'object') {
same = objectsEqual(r.style[key], value);
}
else {
@ -7514,7 +7514,7 @@ function decodeString(value) {
throw Error("Lone surrogate U+" + code.toString(16).toUpperCase() + " is not a scalar value");
}
}
else if ((byte1 & 0xf8) === 0xf0) {
else if ((byte1 & 0xf8) == 0xf0) {
var byte2 = continuationByte(value, i++);
var byte3 = continuationByte(value, i++);
var byte4 = continuationByte(value, i++);
@ -7539,7 +7539,7 @@ exports.decodeString = decodeString;
},{}],15:[function(require,module,exports){
'use strict'
"use strict";
exports.byteLength = byteLength
exports.toByteArray = toByteArray
@ -7547,7 +7547,7 @@ exports.fromByteArray = fromByteArray
var lookup = []
var revLookup = []
var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array
var Arr = typeof Uint8Array != 'undefined' ? Uint8Array : Array
var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
for (var i = 0, len = code.length; i < len; ++i) {
@ -7700,7 +7700,7 @@ function fromByteArray (uint8) {
*/
/* eslint-disable no-proto */
'use strict'
"use strict";
var base64 = require('base64-js')
var ieee754 = require('ieee754')
@ -7728,8 +7728,8 @@ exports.kMaxLength = K_MAX_LENGTH
*/
Buffer.TYPED_ARRAY_SUPPORT = typedArraySupport()
if (!Buffer.TYPED_ARRAY_SUPPORT && typeof console !== 'undefined' &&
typeof console.error === 'function') {
if (!Buffer.TYPED_ARRAY_SUPPORT && typeof console != 'undefined' &&
typeof console.error == 'function') {
console.error(
'This browser lacks typed array (Uint8Array) support which is required by ' +
'`buffer` v5.x. Use `buffer` v4.x if you require old browser support.'
@ -7785,8 +7785,8 @@ function createBuffer (length) {
function Buffer (arg, encodingOrOffset, length) {
// Common case.
if (typeof arg === 'number') {
if (typeof encodingOrOffset === 'string') {
if (typeof arg == 'number') {
if (typeof encodingOrOffset == 'string') {
throw new TypeError(
'The "string" argument must be of type string. Received type number'
)
@ -7797,7 +7797,7 @@ function Buffer (arg, encodingOrOffset, length) {
}
// Fix subarray() in ES2016. See: https://github.com/feross/buffer/pull/97
if (typeof Symbol !== 'undefined' && Symbol.species != null &&
if (typeof Symbol != 'undefined' && Symbol.species != null &&
Buffer[Symbol.species] === Buffer) {
Object.defineProperty(Buffer, Symbol.species, {
value: null,
@ -7810,7 +7810,7 @@ if (typeof Symbol !== 'undefined' && Symbol.species != null &&
Buffer.poolSize = 8192 // not used by this implementation
function from (value, encodingOrOffset, length) {
if (typeof value === 'string') {
if (typeof value == 'string') {
return fromString(value, encodingOrOffset)
}
@ -7830,7 +7830,7 @@ function from (value, encodingOrOffset, length) {
return fromArrayBuffer(value, encodingOrOffset, length)
}
if (typeof value === 'number') {
if (typeof value == 'number') {
throw new TypeError(
'The "value" argument must not be of type number. Received type number'
)
@ -7844,8 +7844,8 @@ function from (value, encodingOrOffset, length) {
var b = fromObject(value)
if (b) return b
if (typeof Symbol !== 'undefined' && Symbol.toPrimitive != null &&
typeof value[Symbol.toPrimitive] === 'function') {
if (typeof Symbol != 'undefined' && Symbol.toPrimitive != null &&
typeof value[Symbol.toPrimitive] == 'function') {
return Buffer.from(
value[Symbol.toPrimitive]('string'), encodingOrOffset, length
)
@ -7875,7 +7875,7 @@ Buffer.prototype.__proto__ = Uint8Array.prototype
Buffer.__proto__ = Uint8Array
function assertSize (size) {
if (typeof size !== 'number') {
if (typeof size != 'number') {
throw new TypeError('"size" argument must be of type number')
} else if (size < 0) {
throw new RangeError('The value "' + size + '" is invalid for option "size"')
@ -7884,18 +7884,14 @@ function assertSize (size) {
function alloc (size, fill, encoding) {
assertSize(size)
if (size <= 0) {
return createBuffer(size)
}
if (fill !== undefined) {
return (size <= 0 || fill === undefined
? createBuffer(size)
:
// Only pay attention to encoding if it's a string. This
// prevents accidentally sending in a number that would
// be interpretted as a start offset.
return typeof encoding === 'string'
? createBuffer(size).fill(fill, encoding)
: createBuffer(size).fill(fill)
}
return createBuffer(size)
createBuffer(size).fill(fill, typeof encoding == 'string' ? encoding : undefined)
)
}
/**
@ -7925,7 +7921,7 @@ Buffer.allocUnsafeSlow = function (size) {
}
function fromString (string, encoding) {
if (typeof encoding !== 'string' || encoding === '') {
if (typeof encoding != 'string' || encoding === '') {
encoding = 'utf8'
}
@ -7994,7 +7990,7 @@ function fromObject (obj) {
}
if (obj.length !== undefined) {
if (typeof obj.length !== 'number' || numberIsNaN(obj.length)) {
if (typeof obj.length != 'number' || numberIsNaN(obj.length)) {
return createBuffer(0)
}
return fromArrayLike(obj)
@ -8113,7 +8109,7 @@ function byteLength (string, encoding) {
if (ArrayBuffer.isView(string) || isInstance(string, ArrayBuffer)) {
return string.byteLength
}
if (typeof string !== 'string') {
if (typeof string != 'string') {
throw new TypeError(
'The "string" argument must be one of type string, Buffer, or ArrayBuffer. ' +
'Received type ' + typeof string
@ -8378,7 +8374,7 @@ function bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) {
if (buffer.length === 0) return -1
// Normalize byteOffset
if (typeof byteOffset === 'string') {
if (typeof byteOffset == 'string') {
encoding = byteOffset
byteOffset = 0
} else if (byteOffset > 0x7fffffff) {
@ -8403,7 +8399,7 @@ function bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) {
}
// Normalize val
if (typeof val === 'string') {
if (typeof val == 'string') {
val = Buffer.from(val, encoding)
}
@ -8414,9 +8410,9 @@ function bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) {
return -1
}
return arrayIndexOf(buffer, val, byteOffset, encoding, dir)
} else if (typeof val === 'number') {
} else if (typeof val == 'number') {
val = val & 0xFF // Search for a byte value [0-255]
if (typeof Uint8Array.prototype.indexOf === 'function') {
if (typeof Uint8Array.prototype.indexOf == 'function') {
if (dir) {
return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset)
} else {
@ -8549,7 +8545,7 @@ Buffer.prototype.write = function write (string, offset, length, encoding) {
length = this.length
offset = 0
// Buffer#write(string, encoding)
} else if (length === undefined && typeof offset === 'string') {
} else if (length === undefined && typeof offset == 'string') {
encoding = offset
length = this.length
offset = 0
@ -9228,7 +9224,7 @@ Buffer.prototype.copy = function copy (target, targetStart, start, end) {
var len = end - start
if (this === target && typeof Uint8Array.prototype.copyWithin === 'function') {
if (this === target && typeof Uint8Array.prototype.copyWithin == 'function') {
// Use built-in when available, missing from IE11
this.copyWithin(targetStart, start, end)
} else if (this === target && start < targetStart && targetStart < end) {
@ -9253,19 +9249,19 @@ Buffer.prototype.copy = function copy (target, targetStart, start, end) {
// buffer.fill(string[, offset[, end]][, encoding])
Buffer.prototype.fill = function fill (val, start, end, encoding) {
// Handle string cases:
if (typeof val === 'string') {
if (typeof start === 'string') {
if (typeof val == 'string') {
if (typeof start == 'string') {
encoding = start
start = 0
end = this.length
} else if (typeof end === 'string') {
} else if (typeof end == 'string') {
encoding = end
end = this.length
}
if (encoding !== undefined && typeof encoding !== 'string') {
if (encoding !== undefined && typeof encoding != 'string') {
throw new TypeError('encoding must be a string')
}
if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) {
if (typeof encoding == 'string' && !Buffer.isEncoding(encoding)) {
throw new TypeError('Unknown encoding: ' + encoding)
}
if (val.length === 1) {
@ -9276,7 +9272,7 @@ Buffer.prototype.fill = function fill (val, start, end, encoding) {
val = code
}
}
} else if (typeof val === 'number') {
} else if (typeof val == 'number') {
val = val & 255
}
@ -9295,7 +9291,7 @@ Buffer.prototype.fill = function fill (val, start, end, encoding) {
if (!val) val = 0
var i
if (typeof val === 'number') {
if (typeof val == 'number') {
for (i = start; i < end; ++i) {
this[i] = val
}
@ -9319,25 +9315,22 @@ Buffer.prototype.fill = function fill (val, start, end, encoding) {
// HELPER FUNCTIONS
// ================
var INVALID_BASE64_RE = /[^+/0-9A-Za-z-_]/g
var INVALID_BASE64_RE = /[^+/\dA-Z-_]/gi
function base64clean (str) {
// Node takes equal signs as end of the Base64 encoding
str = str.split('=')[0]
str = str.split('=', 1)[0]
// Node strips out invalid characters like \n and \t from the string, base64-js does not
str = str.trim().replace(INVALID_BASE64_RE, '')
// Node converts strings with length < 2 to ''
if (str.length < 2) return ''
// Node allows for non-padded base64 strings (missing trailing ===), base64-js does not
while (str.length % 4 !== 0) {
str = str + '='
}
while (str.length % 4 !== 0) str += '='
return str
}
function toHex (n) {
if (n < 16) return '0' + n.toString(16)
return n.toString(16)
return (n < 16 ? '0' : '') + n.toString(16)
}
function utf8ToBytes (string, units) {

File diff suppressed because one or more lines are too long

340
index.js
View file

@ -1,30 +1,33 @@
const express = require('express');
const request = require('request');
const compression = require('compression');
const timeout = require('connect-timeout');
const rateLimit = require("express-rate-limit");
const fs = require("fs");
const app = express();
"use strict";
const express = require('express')
const request = require('request')
const compression = require('compression')
const timeout = require('connect-timeout')
const rateLimit = require("express-rate-limit")
const fs = require("fs")
const app = express()
let serverList = require('./servers.json')
let pinnedServers = serverList.filter(x => x.pinned)
let notPinnedServers = serverList.filter(x => !x.pinned).sort((a, b) => a.name.localeCompare(b.name))
const serverList = require('./servers.json')
const pinnedServers = []
const notPinnedServers = []
serverList.forEach(x => (x.pinned ? pinnedServers : notPinnedServers).push(x))
notPinnedServers.sort((a, b) => a.name.localeCompare(b.name))
app.servers = pinnedServers.concat(notPinnedServers)
app.safeServers = JSON.parse(JSON.stringify(app.servers)) // clone
app.safeServers.forEach(x => { delete x.endpoint; delete x.substitutions; delete x.overrides; delete x.disabled })
app.safeServers = JSON.parse(JSON.stringify(app.servers)) // deep clone
app.safeServers.forEach(x => ['endpoint', 'substitutions', 'overrides', 'disabled'].forEach(k => delete x[k]))
app.config = require('./settings.js')
let rlMessage = "Rate limited ¯\\_(ツ)_/¯<br><br>Please do not spam my servers with a crazy amount of requests. It slows things down on my end and stresses RobTop's servers just as much." +
" If you really want to send a zillion requests for whatever reason, please download the GDBrowser repository locally - or even just send the request directly to the GD servers.<br><br>" +
const rlMessage = "Rate limited ¯\\_(ツ)_/¯<br><br>Please do not spam my servers with a crazy amount of requests. It slows things down on my end and stresses RobTop's servers just as much. "+
"If you really want to send a zillion requests for whatever reason, please download the GDBrowser repository locally - or even just send the request directly to the GD servers.<br><br>"+
"This kind of spam usually leads to GDBrowser getting IP banned by RobTop, and every time that happens I have to start making the rate limit even stricter. Please don't be the reason for that.<br><br>"
const RL = rateLimit({
windowMs: app.config.rateLimiting ? 5 * 60 * 1000 : 0,
max: app.config.rateLimiting ? 100 : 0, // max requests per 5 minutes
message: rlMessage,
keyGenerator: function(req) { return req.headers['x-real-ip'] || req.headers['x-forwarded-for'] },
skip: function(req) { return ((req.url.includes("api/level") && !req.query.hasOwnProperty("download")) ? true : false) }
keyGenerator: req => req.headers['x-real-ip'] || req.headers['x-forwarded-for'],
skip: req => req.url.includes("api/level") && !req.query.hasOwnProperty("download")
})
const RL2 = rateLimit({
@ -34,8 +37,8 @@ const RL2 = rateLimit({
keyGenerator: function(req) { return req.headers['x-real-ip'] || req.headers['x-forwarded-for'] }
})
let XOR = require('./classes/XOR.js');
let achievements = require('./misc/achievements.json')
const XOR = require('./classes/XOR.js')
const achievements = require('./misc/achievements.json')
let achievementTypes = require('./misc/achievementTypes.json')
let music = require('./misc/music.json')
let assetPage = fs.readFileSync('./html/assets.html', 'utf8')
@ -45,25 +48,27 @@ app.lastSuccess = {}
app.actuallyWorked = {}
app.servers.forEach(x => {
app.accountCache[x.id || "gd"] = {}
app.lastSuccess[x.id || "gd"] = Date.now()
x = x.id || "gd"
app.accountCache[x] = {}
app.lastSuccess[x] = Date.now()
})
app.mainEndpoint = app.servers.find(x => !x.id).endpoint // boomlings.com unless changed in fork
app.set('json spaces', 2)
app.use(compression());
app.use(express.json());
app.use(express.urlencoded({extended: true}));
app.use(timeout('20s'));
app.use(compression())
app.use(express.json())
app.use(express.urlencoded({extended: true}))
app.use(timeout('20s'))
app.use(async function(req, res, next) {
let subdomains = req.subdomains.map(x => x.toLowerCase())
if (!subdomains.length) subdomains = [""]
req.server = app.servers.find(x => subdomains.includes(x.id.toLowerCase()))
if (subdomains.length > 1 || !req.server) return res.redirect("http://" + req.get('host').split(".").slice(subdomains.length).join(".") + req.originalUrl)
if (subdomains.length > 1 || !req.server)
return res.redirect("http://" + req.get('host').split(".").slice(subdomains.length).join(".") + req.originalUrl)
// will expand this in the future :wink:
// will expand this in the future :wink: 😉
res.sendError = function(errorCode=500) {
res.status(errorCode).send("-1")
}
@ -80,14 +85,17 @@ app.use(async function(req, res, next) {
if (req.query.online > 0) req.offline = false
req.gdParams = function(obj={}, substitute=true) {
Object.keys(app.config.params).forEach(x => { if (!obj[x]) obj[x] = app.config.params[x] })
Object.keys(req.server.extraParams || {}).forEach(x => { if (!obj[x]) obj[x] = req.server.extraParams[x] })
Object.keys(app.config.params).forEach(k => obj[k] ||= app.config.params[k])
Object.keys(req.server.extraParams || {}).forEach(k => obj[k] ||= req.server.extraParams[k])
let ip = req.headers['x-real-ip'] || req.headers['x-forwarded-for']
let params = {form: obj, headers: app.config.ipForwarding && ip ? {'x-forwarded-for': ip, 'x-real-ip': ip} : {}}
if (substitute) { // GDPS substitutions in settings.js
for (let ss in req.server.substitutions) {
if (params.form[ss]) { params.form[req.server.substitutions[ss]] = params.form[ss]; delete params.form[ss] }
if (params.form[ss]) {
params.form[req.server.substitutions[ss]] = params.form[ss]
delete params.form[ss]
}
}
}
return params
@ -95,16 +103,16 @@ app.use(async function(req, res, next) {
req.gdRequest = function(target, params={}, cb=function(){}) {
if (!target) return cb(true)
target = req.server.overrides ? (req.server.overrides[target] || target) : target
target = (req.server.overrides && req.server.overrides[target]) || target
let parameters = params.headers ? params : req.gdParams(params)
let endpoint = req.endpoint
if (params.forceGD || (params.form && params.form.forceGD)) endpoint = "http://www.boomlings.com/database/"
let {endpoint} = req
if (params.forceGD || (params.form?.forceGD))
endpoint = "http://www.boomlings.com/database/"
request.post(endpoint + target + '.php', parameters, function(err, res, body) {
let error = err
if (!error && (err || !body || body.match(/^-\d$/) || body.startsWith("error") || body.startsWith("<"))) {
error = {serverError: true, response: body}
}
return cb(error, res, body)
if (!err && (!body || /(^-\d$)|^error|^</.test(body)))
err = {serverError: true, response: body}
return cb(err, res, body)
})
}
@ -116,20 +124,25 @@ fs.readdirSync('./api').filter(x => !x.includes(".")).forEach(x => directories.p
app.trackSuccess = function(id) {
app.lastSuccess[id] = Date.now()
if (!app.actuallyWorked[id]) app.actuallyWorked[id] = true
app.actuallyWorked[id] ||= true
}
app.timeSince = function(id, time) {
if (!time) time = app.lastSuccess[id]
let secsPassed = Math.floor((Date.now() - time) / 1000)
let minsPassed = Math.floor(secsPassed / 60)
secsPassed -= 60 * minsPassed;
secsPassed -= 60 * minsPassed
return `${app.actuallyWorked[id] ? "" : "~"}${minsPassed}m ${secsPassed}s`
}
app.userCache = function(id, accountID, playerID, name) {
if (!accountID || accountID == "0" || (name && name.toLowerCase() == "robtop" && accountID != "71") || !app.config.cacheAccountIDs) return
if ( // "IDK how to format this nicely" @Rudxain
!accountID || accountID == "0" ||
(name?.toLowerCase() == "robtop" && accountID != "71") ||
!app.config.cacheAccountIDs
)
return
if (!playerID) return app.accountCache[id][accountID.toLowerCase()]
let cacheStuff = [accountID, playerID, name]
app.accountCache[id][name.toLowerCase()] = cacheStuff
@ -138,7 +151,10 @@ app.userCache = function(id, accountID, playerID, name) {
app.run = {}
directories.forEach(d => {
fs.readdirSync('./api/' + d).forEach(x => {if (x.includes('.')) app.run[x.split('.')[0]] = require('./api/' + d + "/" + x) })
fs.readdirSync('./api/' + d)
.forEach(x => {
if (x.includes('.')) app.run[x.split('.', 1)[0]] = require(`./api/${d}/${x}`)
})
})
app.xor = new XOR()
@ -162,26 +178,29 @@ catch(e) {
}
app.parseResponse = function (responseBody, splitter=":") {
if (!responseBody || responseBody == "-1") return {};
if (!responseBody || responseBody == "-1") return {}
if (responseBody.startsWith("\nWarning:")) responseBody = responseBody.split("\n").slice(2).join("\n").trim() // GDPS'es are wild
if (responseBody.startsWith("<br />")) responseBody = responseBody.split("<br />").slice(2).join("<br />").trim() // Seriously screw this
let response = responseBody.split('#')[0].split(splitter);
let res = {};
for (let i = 0; i < response.length; i += 2) {
res[response[i]] = response[i + 1]}
return res
let response = responseBody.split('#', 1)[0].split(splitter)
let res = {}
for (let i = 0; i < response.length; i += 2)
res[response[i]] = response[i + 1]
return res
}
//xss bad
app.clean = function(text) {if (!text || typeof text != "string") return text; else return text.replace(/&/g, "&#38;").replace(/</g, "&#60;").replace(/>/g, "&#62;").replace(/=/g, "&#61;").replace(/"/g, "&#34;").replace(/'/g, "&#39;")}
app.clean = text => {
const escChar = c => ({"&": "&#38;", "<": "&#60;", ">": "&#62;", "=": "&#61;", '"': "&#34;", "'": "&#39;"}[c] || c)
return !text || typeof text != "string" ? text : text.replace(/./gs, escChar)
}
// ASSETS
app.use('/assets', express.static(__dirname + '/assets', {maxAge: "7d"}));
app.use('/assets/css', express.static(__dirname + '/assets/css'));
app.use('/assets', express.static(__dirname + '/assets', {maxAge: "7d"}))
app.use('/assets/css', express.static(__dirname + '/assets/css'))
app.use('/iconkit', express.static(__dirname + '/iconkit'));
app.get("/global.js", function(req, res) { res.status(200).sendFile(__dirname + "/misc/global.js") })
app.use('/iconkit', express.static(__dirname + '/iconkit'))
app.get("/misc/global.js", function(req, res) { res.status(200).sendFile(__dirname + "/misc/global.js") })
app.get("/dragscroll.js", function(req, res) { res.status(200).sendFile(__dirname + "/misc/dragscroll.js") })
app.get("/assets/:dir*?", function(req, res) {
@ -208,116 +227,151 @@ app.get("/assets/:dir*?", function(req, res) {
// POST REQUESTS
app.post("/like", RL, function(req, res) { app.run.like(app, req, res) })
app.post("/postComment", RL, function(req, res) { app.run.postComment(app, req, res) })
app.post("/postProfileComment", RL, function(req, res) { app.run.postProfileComment(app, req, res) })
function doPOST(name, key, noRL, wtf) {
const args = ["/" + name]
if (!noRL) args.push(RL)
app.post(...args, function(req, res) { app.run[ key || name ](app, req, res, wtf ? true : undefined) })
}
doPOST("like")
doPOST("postComment")
doPOST("postProfileComment")
doPOST("messages", "getMessages")
doPOST("messages/:id", "fetchMessage")
doPOST("deleteMessage")
doPOST("sendMessage")
doPOST("accurateLeaderboard", "accurate", true, true)
doPOST("analyzeLevel", "analyze", true)
app.post("/messages", RL, function(req, res) { app.run.getMessages(app, req, res) })
app.post("/messages/:id", RL, function(req, res) { app.run.fetchMessage(app, req, res) })
app.post("/deleteMessage", RL, function(req, res) { app.run.deleteMessage(app, req, res) })
app.post("/sendMessage", RL, function(req, res) { app.run.sendMessage(app, req, res) })
app.post("/accurateLeaderboard", function(req, res) { app.run.accurate(app, req, res, true) })
app.post("/analyzeLevel", function(req, res) { app.run.analyze(app, req, res) })
// HTML
let onePointNineDisabled = ['daily', 'weekly', 'gauntlets', 'messages']
let downloadDisabled = ['daily', 'weekly']
let onePointNineDisabled = ['daily', 'weekly', 'gauntlets', 'messages'] // using `concat` won't shorten this
let gdpsHide = ['achievements', 'messages']
app.get("/", function(req, res) {
if (req.query.hasOwnProperty("offline") || (req.offline && !req.query.hasOwnProperty("home"))) res.status(200).sendFile(__dirname + "/html/offline.html")
app.get("/", function(req, res) {
if (req.query.hasOwnProperty("offline") || (req.offline && !req.query.hasOwnProperty("home")))
res.status(200).sendFile(__dirname + "/html/offline.html")
else {
fs.readFile('./html/home.html', 'utf8', function (err, data) {
let html = data;
let html = data
if (req.isGDPS) {
html = html.replace('"levelBG"', '"levelBG purpleBG"')
.replace(/Geometry Dash Browser!/g, req.server.name + " Browser!")
.replace("/assets/gdlogo", `/assets/gdps/${req.id}_logo`)
.replace("coin.png\" itemprop", `gdps/${req.id}_icon.png" itemprop`)
.replace(/coin\.png/g, `${req.server.onePointNine ? "blue" : "silver"}coin.png`)
html = html
.replace('"levelBG"', '"levelBG purpleBG"')
.replace(/Geometry Dash Browser!/g, req.server.name + " Browser!")
.replace("/assets/gdlogo", `/assets/gdps/${req.id}_logo`)
.replace("coin.png\" itemprop", `gdps/${req.id}_icon.png" itemprop`)
.replace(/coin\.png/g, `${req.server.onePointNine ? "blue" : "silver"}coin.png`)
gdpsHide.forEach(x => { html = html.replace(`menu-${x}`, 'changeDaWorld') })
}
if (req.onePointNine) onePointNineDisabled.forEach(x => { html = html.replace(`menu-${x}`, 'menuDisabled') })
if (req.server.disabled) req.server.disabled.forEach(x => { html = html.replace(`menu-${x}`, 'menuDisabled') })
const htmlReplacer = x => { html = html.replace(`menu-${x}`, 'menuDisabled') }
if (req.onePointNine) onePointNineDisabled.forEach(htmlReplacer)
if (req.server.disabled) req.server.disabled.forEach(htmlReplacer)
if (req.server.downloadsDisabled && process.platform == "linux") {
downloadDisabled.forEach(x => { html = html.replace(`menu-${x}`, 'menuDisabled') })
html = html.replace('id="dl" style="display: none', 'style="display: block')
.replace('No active <span id="noLevel">daily</span> level!', '[Blocked by RobTop]')
downloadDisabled.forEach(htmlReplacer)
html = html
.replace('id="dl" style="display: none', 'style="display: block')
.replace('No active <span id="noLevel">daily</span> level!', '[Blocked by RobTop]')
}
if (html.includes('menuDisabled" src="../assets/category-weekly')) { // if weekly disabled, replace with featured
html = html.replace('block" id="menu_weekly', 'none" id="menu_weekly')
.replace('none" id="menu_featured', 'block" id="menu_featured')
if (html.includes('menuDisabled" src="/assets/category-weekly')) { // if weekly disabled, replace with featured
html = html
.replace('block" id="menu_weekly', 'none" id="menu_weekly')
.replace('none" id="menu_featured', 'block" id="menu_featured')
}
return res.status(200).send(html)
})
}
})
})
function sendHTML(dir, name) {
app.get("/" + dir, function(req, res) { res.status(200).sendFile(`${__dirname}/html/${name || dir}.html`) })
}
sendHTML("achievements")
sendHTML("analyze/:id", "analyze")
sendHTML("api")
sendHTML("boomlings")
sendHTML("comments/:id", "comments")
sendHTML("demon/:id", "demon")
sendHTML("gauntlets")
sendHTML("gdps")
sendHTML("iconkit")
sendHTML("leaderboard")
sendHTML("leaderboard/:text", "levelboard")
sendHTML("mappacks")
sendHTML("messages")
sendHTML("search", "filters")
sendHTML("search/:text", "search")
app.get("/achievements", function(req, res) { res.status(200).sendFile(__dirname + "/html/achievements.html") })
app.get("/analyze/:id", function(req, res) { res.status(200).sendFile(__dirname + "/html/analyze.html") })
app.get("/api", function(req, res) { res.status(200).sendFile(__dirname + "/html/api.html") })
app.get("/boomlings", function(req, res) { res.status(200).sendFile(__dirname + "/html/boomlings.html") })
app.get("/comments/:id", function(req, res) { res.status(200).sendFile(__dirname + "/html/comments.html") })
app.get("/demon/:id", function(req, res) { res.status(200).sendFile(__dirname + "/html/demon.html") })
app.get("/gauntlets", function(req, res) { res.status(200).sendFile(__dirname + "/html/gauntlets.html") })
app.get("/gdps", function(req, res) { res.status(200).sendFile(__dirname + "/html/gdps.html") })
app.get("/iconkit", function(req, res) { res.status(200).sendFile(__dirname + "/html/iconkit.html") })
app.get("/leaderboard", function(req, res) { res.status(200).sendFile(__dirname + "/html/leaderboard.html") })
app.get("/leaderboard/:text", function(req, res) { res.status(200).sendFile(__dirname + "/html/levelboard.html") })
app.get("/mappacks", function(req, res) { res.status(200).sendFile(__dirname + "/html/mappacks.html") })
app.get("/messages", function(req, res) { res.status(200).sendFile(__dirname + "/html/messages.html") })
app.get("/search", function(req, res) { res.status(200).sendFile(__dirname + "/html/filters.html") })
app.get("/search/:text", function(req, res) { res.status(200).sendFile(__dirname + "/html/search.html") })
// API
app.get("/api/analyze/:id", RL, function(req, res) { app.run.level(app, req, res, true, true) })
app.get("/api/boomlings", function(req, res) { app.run.boomlings(app, req, res) })
app.get("/api/comments/:id", RL2, function(req, res) { app.run.comments(app, req, res) })
function doGET(name, key, RL, wtf1, wtf2) {
const args = ["/api/" + name]
if (RL !== null && RL !== undefined) args.push(RL)
app.post(...args, function(req, res) { app.run[ key || name ](app, req, res, wtf1 ? true : undefined, wtf2 ? true : undefined) })
}
doGET("analyze/:id", "level", RL, true, true)
doGET("boomlings", "", null)
doGET("comments/:id", "comments", RL2)
app.get("/api/credits", function(req, res) { res.status(200).send(require('./misc/credits.json')) })
app.get("/api/gauntlets", function(req, res) { app.run.gauntlets(app, req, res) })
doGET("gauntlets")
app.get("/api/leaderboard", function(req, res) { app.run[req.query.hasOwnProperty("accurate") ? "accurate" : "scores"](app, req, res) })
app.get("/api/leaderboardLevel/:id", RL2, function(req, res) { app.run.leaderboardLevel(app, req, res) })
app.get("/api/level/:id", RL, function(req, res) { app.run.level(app, req, res, true) })
app.get("/api/mappacks", function(req, res) { app.run.mappacks(app, req, res) })
app.get("/api/profile/:id", RL2, function(req, res) { app.run.profile(app, req, res, true) })
app.get("/api/search/:text", RL2, function(req, res) { app.run.search(app, req, res) })
app.get("/api/song/:song", function(req, res){ app.run.song(app, req, res) })
doGET("leaderboardLevel/:id", "leaderboardLevel", RL2)
doGET("level/:id", "level", RL, true)
doGET("mappacks")
doGET("profile/:id", "profile", RL2, true)
doGET("search/:text", "search", RL2)
doGET("song/:song", "song")
// REDIRECTS
app.get("/icon", function(req, res) { res.redirect('/iconkit') })
app.get("/obj/:text", function(req, res) { res.redirect('/obj/' + req.params.text) })
app.get("/leaderboards/:id", function(req, res) { res.redirect('/leaderboard/' + req.params.id) })
app.get("/profile/:id", function(req, res) { res.redirect('/u/' + req.params.id) })
app.get("/p/:id", function(req, res) { res.redirect('/u/' + req.params.id) })
app.get("/l/:id", function(req, res) { res.redirect('/leaderboard/' + req.params.id) })
app.get("/a/:id", function(req, res) { res.redirect('/analyze/' + req.params.id) })
app.get("/c/:id", function(req, res) { res.redirect('/comments/' + req.params.id) })
app.get("/d/:id", function(req, res) { res.redirect('/demon/' + req.params.id) })
function doRedir(name, dir, key = 'id') {
app.get('/' + name, function(req, res) { res.redirect('/' + dir + (key && '/' + req.params[key]) ) })
}
doRedir('icon', 'iconkit', '')
doRedir('obj/:text', 'obj', 'text')
doRedir('leaderboards/:id', 'leaderboard')
doRedir('profile/:id', 'u')
doRedir('p/:id', 'u')
doRedir('l/:id', 'leaderboard')
doRedir('a/:id', 'analyze')
doRedir('c/:id', 'comments')
doRedir('d/:id', 'demon')
// API AND HTML
app.get("/u/:id", function(req, res) { app.run.profile(app, req, res) })
app.get("/:id", function(req, res) { app.run.level(app, req, res) })
doPOST("u/:id", "profile", true)
doPOST(":id", "level", true)
// MISC
app.get("/api/userCache", function(req, res) { res.status(200).send(app.accountCache) })
app.get("/api/achievements", function(req, res) { res.status(200).send({achievements, types: achievementTypes, shopIcons: sacredTexts.shops, colors: sacredTexts.colors }) })
app.get("/api/achievements", function(req, res) {
res.status(200).send(
{achievements, types: achievementTypes, shopIcons: sacredTexts.shops, colors: sacredTexts.colors}
)
})
app.get("/api/music", function(req, res) { res.status(200).send(music) })
app.get("/api/gdps", function(req, res) {res.status(200).send(req.query.hasOwnProperty("current") ? app.safeServers.find(x => req.server.id == x.id) : app.safeServers) })
app.get("/api/gdps", function(req, res) {
res.status(200).send(
req.query.hasOwnProperty("current")
? app.safeServers.find(x => req.server.id == x.id)
: app.safeServers
)
})
// important icon stuff
let sacredTexts = {}
fs.readdirSync('./iconkit/sacredtexts').forEach(x => {
sacredTexts[x.split(".")[0]] = require("./iconkit/sacredtexts/" + x)
sacredTexts[x.split(".", 1)[0]] = require("./iconkit/sacredtexts/" + x)
})
let previewIcons = fs.readdirSync('./iconkit/premade')
@ -326,7 +380,7 @@ let newPreviewIcons = fs.readdirSync('./iconkit/newpremade')
let previewCounts = {}
previewIcons.forEach(x => {
if (x.endsWith("_0.png")) return
let iconType = sacredTexts.forms[x.split("_")[0]].form
let iconType = sacredTexts.forms[x.split("_", 1)[0]].form
if (!previewCounts[iconType]) previewCounts[iconType] = 1
else previewCounts[iconType]++
})
@ -337,15 +391,15 @@ sacredTexts.newIcons = []
let newIconCounts = {}
newIcons.forEach(x => {
if (x.endsWith(".plist")) {
sacredTexts.newIcons.push(x.split("-")[0])
let formName = x.split(/_\d/g)[0]
sacredTexts.newIcons.push(x.split("-", 1)[0])
let formName = x.split(/_\d/g, 1)[0]
if (!newIconCounts[formName]) newIconCounts[formName] = 1
else newIconCounts[formName]++
}
})
sacredTexts.newIconCounts = newIconCounts
github-advanced-security[bot] commented 2022-07-03 13:42:41 -04:00 (Migrated from github.com)
Review

Missing rate limiting

This route handler performs a file system access, but is not rate-limited.

Show more details

## Missing rate limiting This route handler performs [a file system access](1), but is not rate-limited. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/71)
app.get('/api/icons', function(req, res) {
app.get('/api/icons', function(req, res) {
res.status(200).send(sacredTexts);
});
@ -353,37 +407,37 @@ app.get('/api/icons', function(req, res) {
let iconKitFiles = {}
let sampleIcons = require('./misc/sampleIcons.json')
fs.readdirSync('./iconkit/extradata').forEach(x => {
iconKitFiles[x.split(".")[0]] = require("./iconkit/extradata/" + x)
iconKitFiles[x.split(".", 1)[0]] = require("./iconkit/extradata/" + x)
})
iconKitFiles.previewIcons = previewIcons
iconKitFiles.newPreviewIcons = newPreviewIcons
Object.assign(iconKitFiles, {previewIcons, newPreviewIcons})
app.get('/api/iconkit', function(req, res) {
let sample = [JSON.stringify(sampleIcons[Math.floor(Math.random() * sampleIcons.length)].slice(1))]
app.get('/api/iconkit', function(req, res) {
let sample = [JSON.stringify(sampleIcons[Math.random() * sampleIcons.length >>>0].slice(1))]
let iconserver = req.isGDPS ? req.server.name : undefined
res.status(200).send(Object.assign(iconKitFiles, {sample, server: iconserver, noCopy: req.onePointNine || req.offline}));
});
})
app.get('/icon/:text', function(req, res) {
let iconID = Number(req.query.icon || 1)
let iconForm = sacredTexts.forms[req.query.form] ? req.query.form : "icon"
let iconPath = `${iconForm}_${iconID}.png`
let fileExists = iconKitFiles.previewIcons.includes(iconPath)
if (fileExists) return res.status(200).sendFile(`./iconkit/premade/${iconPath}`, {root: __dirname })
else return res.status(200).sendFile(`./iconkit/premade/${iconForm}_01.png`, {root: __dirname})
return res.status(200).sendFile(`./iconkit/premade/${fileExists ? iconPath : iconForm + '_01.png'}`, {root: __dirname})
})
app.get('*', function(req, res) {
if (req.path.startsWith('/api') || req.path.startsWith("/iconkit")) res.status(404).send('-1')
else res.redirect('/search/404%20')
});
app.use(function (err, req, res, next) {
if (err && err.message == "Response timeout") res.status(504).send('Internal server error! (Timed out)')
if (/^\/(api|iconkit)/.test(req.path))
res.status(404).send('-1')
else
res.redirect('/search/404%20')
})
process.on('uncaughtException', (e) => { console.log(e) });
process.on('unhandledRejection', (e, p) => { console.log(e) });
app.use(function (err, req, res) {
if (err?.message == "Response timeout") res.status(504).send('Internal server error! (Timed out)')
})
app.listen(app.config.port, () => console.log(`Site online! (port ${app.config.port})`))
process.on('uncaughtException', e => console.log(e))
process.on('unhandledRejection', e => console.log(e))
app.listen(app.config.port, () => console.log(`Site online! (port ${app.config.port})`))

View file

@ -1,30 +1,33 @@
"use strict";
function somethingSelected() {
return typeof window.getSelection == 'function' && window.getSelection().toString() != "";
}
const remover = / |\n|\t/g;
const remover = /[ \n\t]/g; //should it be /\s/g ?
$('.dragscroll').each(function(_, el) {
let previouslyMouseDown = false;
let previouslyMouseDown = false
el.addEventListener('mousemove', function(e) {
if (e.buttons != 1) {
if (previouslyMouseDown) {
el.style.removeProperty('user-select');
el.style.removeProperty('-webkit-user-select');
el.style.removeProperty('user-select')
el.style.removeProperty('-webkit-user-select')
previouslyMouseDown = false;
}
return;
}
if (somethingSelected())
return;
if (somethingSelected()) return
if (!previouslyMouseDown) {
for (let el of e.target.childNodes) {
if (el.nodeType === Node.TEXT_NODE && el.textContent.replace(remover, '').length)
return;
}
el.style['user-select'] = 'none';
el.style['-webkit-user-select'] = 'none';
if ([...e.target.childNodes].some(
el => el.nodeType === Node.TEXT_NODE
&&
el.textContent.replace(remover, '').length
)
) return
el.style['user-select'] = 'none'
el.style['-webkit-user-select'] = 'none'
previouslyMouseDown = true;
}
//el.scrollLeft -= e.movementX;
//el.scrollLeft -= e.movementX
el.scrollTop -= e.movementY;
}, {passive: true});
});

View file

@ -1,3 +1,4 @@
"use strict";
$('body').append(`
<div data-nosnippet id="tooSmall" class="brownbox center supercenter" style="display: none; width: 80%">
<h1>Yikes!</h1>
@ -9,28 +10,36 @@ $('body').append(`
`)
$(window).resize(function () {
if (window.innerHeight > window.innerWidth - 75) {
$('#everything').hide();
$('#tooSmall').show();
}
$(window).resize(function() {
// these alternatives may be helpful: https://stackoverflow.com/a/4917796
let isPortrait = window.innerHeight > window.innerWidth - 75
$('#everything')[isPortrait ? 'hide' : 'show']()
$('#tooSmall')[isPortrait ? 'show' : 'hide']()
})
else {
$('#everything').show();
$('#tooSmall').hide()
}
});
// supports Numbers, BigInts, and Strings!
const clamp = (x, min, max) => x < min ? min : (x > max ? max : x) // interval [min, max]
const randRange = (min, max) => Math.random() * (max - min) + +min // prevent string concat
// interval [min, max)
const randInt = (min, max) => Math.floor(randRange(min, max))
//fn, to always get an updated answer
let isDownloadURL = () => window.location.href.endsWith('?download')
function saveUrl() {
if (window.location.href.endsWith('?download')) return;
sessionStorage.setItem('prevUrl', window.location.href);
if ( !isDownloadURL() ) sessionStorage.setItem('prevUrl', window.location.href)
}
function backButton() {
if (window.history.length > 1 && document.referrer.startsWith(window.location.origin)){
if (window.location.href.endsWith('?download') && sessionStorage.getItem('prevUrl') === window.location.href.replace('?download', '')) window.history.go(-2);
else window.history.back()
}
if (window.history.length > 1 && document.referrer.startsWith(window.location.origin)) {
let steps = (
isDownloadURL() &&
sessionStorage.getItem('prevUrl') === window.location.href.replace('?download', '')
? -2 : -1
)
window.history.go(steps)
}
else window.location.href = "../../../../../"
}
@ -42,23 +51,23 @@ function Fetch(link) {
fetch(link).then(resp => {
if (!resp.ok) return rej(resp)
gdps = resp.headers.get('gdps')
if (gdps && gdps.startsWith('1.9/')) { onePointNine = true; gdps = gdps.slice(4) }
if (gdps?.startsWith('1.9/')) { onePointNine = true; gdps = gdps.slice(4) }
resp.json().then(res)
}).catch(rej)
})
}
let allowEsc = true;
let popupEsc = true;
let allowEsc = true
let popupEsc = true
$(document).keydown(function(k) {
if (k.keyCode == 27) { //esc
if (!allowEsc) return
k.preventDefault()
if (popupEsc && $('.popup').is(":visible")) $('.popup').hide();
else $('#backButton').trigger('click')
}
});
if (k.code != 'Escape' || !allowEsc) return
k.preventDefault()
if (popupEsc && $('.popup').is(":visible"))
$('.popup').hide()
else
$('#backButton').trigger('click')
})
let iconData = null
let iconCanvas = null
@ -72,9 +81,9 @@ async function renderIcons() {
if (overrideLoader) return
let iconsToRender = $('gdicon:not([rendered], [dontload])')
if (iconsToRender.length < 1) return
if (!iconData) iconData = await Fetch("../api/icons")
if (!iconCanvas) iconCanvas = document.createElement('canvas')
if (!iconRenderer) iconRenderer = new PIXI.Application({ view: iconCanvas, width: 300, height: 300, backgroundAlpha: 0});
iconData ||= await Fetch("/api/icons")
iconCanvas ||= document.createElement('canvas')
iconRenderer ||= new PIXI.Application({ view: iconCanvas, width: 300, height: 300, backgroundAlpha: 0})
if (loader.loading) return overrideLoader = true
buildIcon(iconsToRender, 0)
}
@ -121,37 +130,37 @@ function finishIcon(currentIcon, name, data) {
}
// reset scroll
while ($(this).scrollTop() != 0) {
while ($(this).scrollTop() != 0)
$(this).scrollTop(0);
}
$(document).ready(function() {
$(window).trigger('resize');
});
// Adds all necessary elements into the tab index (all buttons and links that aren't natively focusable)
const inaccessibleLinkSelector = "*:not(a) > img.gdButton, .leaderboardTab, .gdcheckbox, .diffDiv, .lengthDiv";
document.querySelectorAll(inaccessibleLinkSelector).forEach(elem => {
elem.setAttribute('tabindex', 0);
$(window).trigger('resize')
})
document.getElementById('backButton')?.setAttribute('tabindex', 1); // Prioritize back button, first element to be focused
// Adds all necessary elements into the tab index (all buttons and links that aren't natively focusable)
const inaccessibleLinkSelector = "*:not(a) > img.gdButton, .leaderboardTab, .gdcheckbox, .diffDiv, .lengthDiv"
document.querySelectorAll(inaccessibleLinkSelector)
.forEach(elem => { elem.setAttribute('tabindex', 0) })
document.getElementById('backButton')?.setAttribute('tabindex', 1) // Prioritize back button, first element to be focused
// Event listener to run a .click() function if
window.addEventListener("keydown", e => {
if(e.key !== 'Enter') return;
window.addEventListener("keydown", k => {
// standard and Numpad support
if ( !k.code.endsWith('Enter') ) return
const active = document.activeElement;
const isUnsupportedLink = active.hasAttribute('tabindex'); // Only click on links that aren't already natively supported to prevent double clicking
if(isUnsupportedLink) active.click();
const active = document.activeElement
const isUnsupportedLink = active.hasAttribute('tabindex') // Only click on links that aren't already natively supported to prevent double clicking
if(isUnsupportedLink) active.click()
})
// stolen from stackoverflow
$.fn.isInViewport = function () {
let elementTop = $(this).offset().top;
let elementBottom = elementTop + $(this).outerHeight();
let viewportTop = $(window).scrollTop();
let viewportBottom = viewportTop + $(window).height();
return elementBottom > viewportTop && elementTop < viewportBottom;
};
let elementTop = $(this).offset().top
let elementBottom = elementTop + $(this).outerHeight()
let viewportTop = $(window).scrollTop()
let viewportBottom = viewportTop + $(window).height()
return elementBottom > viewportTop && elementTop < viewportBottom
}

View file

@ -1,25 +1,26 @@
"use strict";
let path = "../extra/"
let files = ["AchievementsDesc", "AchievementsDescMD", null, "AchievementsDescSZ"]
let gameNames = ["gd", "meltdown", "world", "subzero"]
let achString = "geometry.ach."
const files = ["", "MD", null, "SZ"]
const gameNames = ["gd", "meltdown", "world", "subzero"]
const achString = "geometry.ach."
let rewardTypes = { color: "color1", icon: "cube", bird: "ufo", dart: "wave", special: "trail", death: "deathEffect" }
let games = { "md": "meltdown", "world.": "world", "subzero.": "subzero" }
const plist = require('plist');
const fs = require('fs');
const plist = require('plist')
const fs = require('fs')
let achArray = []
files.forEach((file, fileNum) => {
if (!file) return
let data = plist.parse(fs.readFileSync(path + file + '.plist', 'utf8'));
if (file === null) return
file = "AchievementsDesc" + file
f
console.log(`Converting ${file}.plist...`)
for (let key in data) {
if (!achArray.find(x => x.trueID == key)) {
let fileData = data[key];
let fileData = data[key]
let reward = fileData.icon ? fileData.icon.split("_") : []
let achObj = {
id: key.slice(achString.length),

View file

@ -1,16 +1,19 @@
github-advanced-security[bot] commented 2022-06-16 20:39:24 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '}}'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '}}'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/69)
github-advanced-security[bot] commented 2022-06-16 20:39:25 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '{{'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '{{'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/70)
Rudxain commented 2022-06-16 21:04:57 -04:00 (Migrated from github.com)
Review

I realized replaceAll is better there

I realized `replaceAll` is better there
github-advanced-security[bot] commented 2022-06-16 20:39:24 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '}}'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '}}'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/69)
github-advanced-security[bot] commented 2022-06-16 20:39:25 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '{{'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '{{'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/70)
Rudxain commented 2022-06-16 21:04:57 -04:00 (Migrated from github.com)
Review

I realized replaceAll is better there

I realized `replaceAll` is better there
let gdPath = 'C:\\Program Files (x86)\\Steam\\steamapps\\common\\Geometry Dash\\Resources\\'
github-advanced-security[bot] commented 2022-06-16 20:39:24 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '}}'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '}}'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/69)
github-advanced-security[bot] commented 2022-06-16 20:39:25 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '{{'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '{{'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/70)
Rudxain commented 2022-06-16 21:04:57 -04:00 (Migrated from github.com)
Review

I realized replaceAll is better there

I realized `replaceAll` is better there
"use strict";
github-advanced-security[bot] commented 2022-06-16 20:39:24 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '}}'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '}}'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/69)
github-advanced-security[bot] commented 2022-06-16 20:39:25 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '{{'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '{{'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/70)
Rudxain commented 2022-06-16 21:04:57 -04:00 (Migrated from github.com)
Review

I realized replaceAll is better there

I realized `replaceAll` is better there
const gdPath = 'C:\\Program Files (x86)\\Steam\\steamapps\\common\\Geometry Dash\\Resources\\'
github-advanced-security[bot] commented 2022-06-16 20:39:24 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '}}'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '}}'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/69)
github-advanced-security[bot] commented 2022-06-16 20:39:25 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '{{'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '{{'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/70)
Rudxain commented 2022-06-16 21:04:57 -04:00 (Migrated from github.com)
Review

I realized replaceAll is better there

I realized `replaceAll` is better there
const plist = require('plist');
github-advanced-security[bot] commented 2022-06-16 20:39:24 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '}}'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '}}'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/69)
github-advanced-security[bot] commented 2022-06-16 20:39:25 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '{{'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '{{'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/70)
Rudxain commented 2022-06-16 21:04:57 -04:00 (Migrated from github.com)
Review

I realized replaceAll is better there

I realized `replaceAll` is better there
const fs = require('fs');
github-advanced-security[bot] commented 2022-06-16 20:39:24 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '}}'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '}}'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/69)
github-advanced-security[bot] commented 2022-06-16 20:39:25 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '{{'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '{{'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/70)
Rudxain commented 2022-06-16 21:04:57 -04:00 (Migrated from github.com)
Review

I realized replaceAll is better there

I realized `replaceAll` is better there
const plist = require('plist')
github-advanced-security[bot] commented 2022-06-16 20:39:24 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '}}'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '}}'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/69)
github-advanced-security[bot] commented 2022-06-16 20:39:25 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '{{'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '{{'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/70)
Rudxain commented 2022-06-16 21:04:57 -04:00 (Migrated from github.com)
Review

I realized replaceAll is better there

I realized `replaceAll` is better there
const fs = require('fs')
github-advanced-security[bot] commented 2022-06-16 20:39:24 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '}}'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '}}'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/69)
github-advanced-security[bot] commented 2022-06-16 20:39:25 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '{{'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '{{'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/70)
Rudxain commented 2022-06-16 21:04:57 -04:00 (Migrated from github.com)
Review

I realized replaceAll is better there

I realized `replaceAll` is better there
const forms = require('./forms.json')
const data = plist.parse(fs.readFileSync(gdPath + 'GJ_GameSheet02-uhd.plist', 'utf8'));
github-advanced-security[bot] commented 2022-06-16 20:39:24 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '}}'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '}}'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/69)
github-advanced-security[bot] commented 2022-06-16 20:39:25 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '{{'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '{{'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/70)
Rudxain commented 2022-06-16 21:04:57 -04:00 (Migrated from github.com)
Review

I realized replaceAll is better there

I realized `replaceAll` is better there
const glowSheet = plist.parse(fs.readFileSync(gdPath + 'GJ_GameSheetGlow-uhd.plist', 'utf8'));
github-advanced-security[bot] commented 2022-06-16 20:39:24 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '}}'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '}}'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/69)
github-advanced-security[bot] commented 2022-06-16 20:39:25 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '{{'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '{{'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/70)
Rudxain commented 2022-06-16 21:04:57 -04:00 (Migrated from github.com)
Review

I realized replaceAll is better there

I realized `replaceAll` is better there
const data = plist.parse(fs.readFileSync(gdPath + 'GJ_GameSheet02-uhd.plist', 'utf8'))
github-advanced-security[bot] commented 2022-06-16 20:39:24 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '}}'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '}}'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/69)
github-advanced-security[bot] commented 2022-06-16 20:39:25 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '{{'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '{{'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/70)
Rudxain commented 2022-06-16 21:04:57 -04:00 (Migrated from github.com)
Review

I realized replaceAll is better there

I realized `replaceAll` is better there
const glowSheet = plist.parse(fs.readFileSync(gdPath + 'GJ_GameSheetGlow-uhd.plist', 'utf8'))
github-advanced-security[bot] commented 2022-06-16 20:39:24 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '}}'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '}}'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/69)
github-advanced-security[bot] commented 2022-06-16 20:39:25 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '{{'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '{{'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/70)
Rudxain commented 2022-06-16 21:04:57 -04:00 (Migrated from github.com)
Review

I realized replaceAll is better there

I realized `replaceAll` is better there
let formList = Object.values(forms).map(x => x.form)
let frames = {}
function addIcons(data) {
Object.keys(data).filter(x => formList.includes(x.split("_")[0])).forEach(x => frames[x] = data[x])
github-advanced-security[bot] commented 2022-06-16 20:39:24 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '}}'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '}}'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/69)
github-advanced-security[bot] commented 2022-06-16 20:39:25 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '{{'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '{{'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/70)
Rudxain commented 2022-06-16 21:04:57 -04:00 (Migrated from github.com)
Review

I realized replaceAll is better there

I realized `replaceAll` is better there
Object.keys(data)
github-advanced-security[bot] commented 2022-06-16 20:39:24 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '}}'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '}}'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/69)
github-advanced-security[bot] commented 2022-06-16 20:39:25 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '{{'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '{{'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/70)
Rudxain commented 2022-06-16 21:04:57 -04:00 (Migrated from github.com)
Review

I realized replaceAll is better there

I realized `replaceAll` is better there
.filter(k => formList.includes(k.split("_", 1)[0]))
github-advanced-security[bot] commented 2022-06-16 20:39:24 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '}}'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '}}'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/69)
github-advanced-security[bot] commented 2022-06-16 20:39:25 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '{{'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '{{'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/70)
Rudxain commented 2022-06-16 21:04:57 -04:00 (Migrated from github.com)
Review

I realized replaceAll is better there

I realized `replaceAll` is better there
.forEach(k => frames[k] = data[k])
github-advanced-security[bot] commented 2022-06-16 20:39:24 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '}}'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '}}'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/69)
github-advanced-security[bot] commented 2022-06-16 20:39:25 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '{{'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '{{'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/70)
Rudxain commented 2022-06-16 21:04:57 -04:00 (Migrated from github.com)
Review

I realized replaceAll is better there

I realized `replaceAll` is better there
}
addIcons(data.frames)
@ -18,13 +21,13 @@ addIcons(glowSheet.frames)
github-advanced-security[bot] commented 2022-06-16 20:39:24 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '}}'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '}}'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/69)
github-advanced-security[bot] commented 2022-06-16 20:39:25 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '{{'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '{{'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/70)
Rudxain commented 2022-06-16 21:04:57 -04:00 (Migrated from github.com)
Review

I realized replaceAll is better there

I realized `replaceAll` is better there
github-advanced-security[bot] commented 2022-06-16 20:39:24 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '}}'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '}}'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/69)
github-advanced-security[bot] commented 2022-06-16 20:39:25 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '{{'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '{{'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/70)
Rudxain commented 2022-06-16 21:04:57 -04:00 (Migrated from github.com)
Review

I realized replaceAll is better there

I realized `replaceAll` is better there
for (let key in frames) {
if (key.startsWith(".")) delete frames[key]
else { let fileData = frames[key];
github-advanced-security[bot] commented 2022-06-16 20:39:24 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '}}'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '}}'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/69)
github-advanced-security[bot] commented 2022-06-16 20:39:25 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '{{'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '{{'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/70)
Rudxain commented 2022-06-16 21:04:57 -04:00 (Migrated from github.com)
Review

I realized replaceAll is better there

I realized `replaceAll` is better there
for (let innerKey in fileData) {
github-advanced-security[bot] commented 2022-06-16 20:39:24 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '}}'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '}}'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/69)
github-advanced-security[bot] commented 2022-06-16 20:39:25 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '{{'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '{{'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/70)
Rudxain commented 2022-06-16 21:04:57 -04:00 (Migrated from github.com)
Review

I realized replaceAll is better there

I realized `replaceAll` is better there
if (typeof fileData[innerKey]) {
github-advanced-security[bot] commented 2022-06-16 20:39:24 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '}}'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '}}'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/69)
github-advanced-security[bot] commented 2022-06-16 20:39:25 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '{{'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '{{'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/70)
Rudxain commented 2022-06-16 21:04:57 -04:00 (Migrated from github.com)
Review

I realized replaceAll is better there

I realized `replaceAll` is better there
else {
github-advanced-security[bot] commented 2022-06-16 20:39:24 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '}}'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '}}'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/69)
github-advanced-security[bot] commented 2022-06-16 20:39:25 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '{{'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '{{'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/70)
Rudxain commented 2022-06-16 21:04:57 -04:00 (Migrated from github.com)
Review

I realized replaceAll is better there

I realized `replaceAll` is better there
let fileData = frames[key]
github-advanced-security[bot] commented 2022-06-16 20:39:24 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '}}'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '}}'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/69)
github-advanced-security[bot] commented 2022-06-16 20:39:25 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '{{'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '{{'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/70)
Rudxain commented 2022-06-16 21:04:57 -04:00 (Migrated from github.com)
Review

I realized replaceAll is better there

I realized `replaceAll` is better there
for (let innerKey in fileData) {
github-advanced-security[bot] commented 2022-06-16 20:39:24 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '}}'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '}}'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/69)
github-advanced-security[bot] commented 2022-06-16 20:39:25 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '{{'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '{{'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/70)
Rudxain commented 2022-06-16 21:04:57 -04:00 (Migrated from github.com)
Review

I realized replaceAll is better there

I realized `replaceAll` is better there
if (!["spriteSize", "spriteOffset"].includes(innerKey)) delete fileData[innerKey] // remove useless stuff
else fileData[innerKey] = JSON.parse(fileData[innerKey].replace(/{/g, '[').replace(/}/g, ']'));
github-advanced-security[bot] commented 2022-06-16 20:39:24 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '}}'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '}}'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/69)
github-advanced-security[bot] commented 2022-06-16 20:39:25 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '{{'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '{{'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/70)
Rudxain commented 2022-06-16 21:04:57 -04:00 (Migrated from github.com)
Review

I realized replaceAll is better there

I realized `replaceAll` is better there
else fileData[innerKey] = JSON.parse(fileData[innerKey].replaceAll('{', '[').replaceAll('}', ']'))
github-advanced-security[bot] commented 2022-06-16 20:39:24 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '}}'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '}}'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/69)
github-advanced-security[bot] commented 2022-06-16 20:39:25 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '{{'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '{{'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/70)
Rudxain commented 2022-06-16 21:04:57 -04:00 (Migrated from github.com)
Review

I realized replaceAll is better there

I realized `replaceAll` is better there
}
}
}}
github-advanced-security[bot] commented 2022-06-16 20:39:24 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '}}'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '}}'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/69)
github-advanced-security[bot] commented 2022-06-16 20:39:25 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '{{'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '{{'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/70)
Rudxain commented 2022-06-16 21:04:57 -04:00 (Migrated from github.com)
Review

I realized replaceAll is better there

I realized `replaceAll` is better there
fs.writeFileSync('./parsed/gameSheet.json', JSON.stringify(frames, null, 2).replace(/\[\n.+?(-?\d+),\n.+?(-?\d+)\n.+]/g, "[$1, $2]")); // regex to make it easier to read
github-advanced-security[bot] commented 2022-06-16 20:39:24 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '}}'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '}}'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/69)
github-advanced-security[bot] commented 2022-06-16 20:39:25 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '{{'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '{{'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/70)
Rudxain commented 2022-06-16 21:04:57 -04:00 (Migrated from github.com)
Review

I realized replaceAll is better there

I realized `replaceAll` is better there
console.log("Successfully parsed!")
github-advanced-security[bot] commented 2022-06-16 20:39:24 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '}}'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '}}'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/69)
github-advanced-security[bot] commented 2022-06-16 20:39:25 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '{{'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '{{'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/70)
Rudxain commented 2022-06-16 21:04:57 -04:00 (Migrated from github.com)
Review

I realized replaceAll is better there

I realized `replaceAll` is better there
}
github-advanced-security[bot] commented 2022-06-16 20:39:24 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '}}'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '}}'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/69)
github-advanced-security[bot] commented 2022-06-16 20:39:25 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '{{'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '{{'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/70)
Rudxain commented 2022-06-16 21:04:57 -04:00 (Migrated from github.com)
Review

I realized replaceAll is better there

I realized `replaceAll` is better there
fs.writeFileSync('./parsed/gameSheet.json', JSON.stringify(frames, null, 2).replace(/\[\n.+?(-?\d+),\n.+?(-?\d+)\n.+]/g, "[$1, $2]")); // regex to make it easier to read
github-advanced-security[bot] commented 2022-06-16 20:39:24 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '}}'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '}}'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/69)
github-advanced-security[bot] commented 2022-06-16 20:39:25 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '{{'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '{{'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/70)
Rudxain commented 2022-06-16 21:04:57 -04:00 (Migrated from github.com)
Review

I realized replaceAll is better there

I realized `replaceAll` is better there
console.log("Successfully parsed!")
github-advanced-security[bot] commented 2022-06-16 20:39:24 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '}}'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '}}'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/69)
github-advanced-security[bot] commented 2022-06-16 20:39:25 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '{{'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '{{'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/70)
Rudxain commented 2022-06-16 21:04:57 -04:00 (Migrated from github.com)
Review

I realized replaceAll is better there

I realized `replaceAll` is better there

github-advanced-security[bot] commented 2022-06-16 20:39:24 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '}}'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '}}'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/69)
github-advanced-security[bot] commented 2022-06-16 20:39:25 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '{{'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '{{'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/70)
Rudxain commented 2022-06-16 21:04:57 -04:00 (Migrated from github.com)
Review

I realized replaceAll is better there

I realized `replaceAll` is better there
github-advanced-security[bot] commented 2022-06-16 20:39:24 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '}}'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '}}'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/69)
github-advanced-security[bot] commented 2022-06-16 20:39:25 -04:00 (Migrated from github.com)
Review

Incomplete string escaping or encoding

This replaces only the first occurrence of '{{'.

Show more details

## Incomplete string escaping or encoding This replaces only the first occurrence of '{{'. [Show more details](https://github.com/GDColon/GDBrowser/security/code-scanning/70)
Rudxain commented 2022-06-16 21:04:57 -04:00 (Migrated from github.com)
Review

I realized replaceAll is better there

I realized `replaceAll` is better there

View file

@ -1,5 +1,6 @@
const plist = require('plist');
const fs = require('fs');
"use strict";
const plist = require('plist')
const fs = require('fs')
const gdPath = 'C:\\Program Files (x86)\\Steam\\steamapps\\common\\Geometry Dash\\Resources\\'
const animationPlists = ["Robot_AnimDesc.plist", "Spider_AnimDesc.plist"]
const objectDefinitions = "objectDefinitions.plist"
@ -29,16 +30,16 @@ plistData.forEach(x => {
Object.keys(x.animationContainer).forEach(a => fullAnimationData[a] = x.animationContainer[a])
})
let animations = { "robot": {}, "spider": {} }
let animations = { robot: {}, spider: {} }
for (let animation in fullAnimationData) {
let animationName = animation.split(".")[0].split("_")
let animationName = animation.split(".", 1)[0].split("_")
let animationForm = animationName.shift()
let animationIndex = Number(animationName.pop()) - 1
animationName = animationName.join("_")
let animationList = Object.values(fullAnimationData[animation])
let formName = animation.split("_")[0].toLowerCase()
let formName = animation.split("_", 1)[0].toLowerCase()
let animationData = animationList.map(anim => {
let textureInfo = anim.texture.split("_")
let flips = parseSet(anim.flipped)
@ -55,17 +56,19 @@ for (let animation in fullAnimationData) {
if (!animations[formName][animationName]) {
let timingDefs = timings[animationForm].animations[animationName] || {}
let animationInfo = { duration: cleanFloat(Number(timingDefs.delay || 0.05) * 1000) }
if (timingDefs.looped == '1' || (!timingDefs.looped && animationName.includes("loop"))) animationInfo.loop = true
if (timingDefs.singleFrame) animationInfo.single = true
if (timingDefs.looped == '1' || (!timingDefs.looped && animationName.includes("loop")))
animationInfo.loop = true
if (timingDefs.singleFrame)
animationInfo.single = true
animations[formName][animationName] = { info: animationInfo, frames: [] }
}
animations[formName][animationName].frames[animationIndex] = animationData
}
let cleanJSON = JSON.stringify({info, animations}, null, 2)
.replace(/: \[\n\s+([0-9a-z.-]+),\n\s+([0-9a-z.-]+)\n\s+],/g, ": [$1, $2],") // keep sets on one line
.replace(/],\n(\s+)"(.+?)": \[\n/g, '],\n\n$1"$2": [\n') // blank line between animations
.replace(' "animations"', '\n "animations"') // blank line before animation list
.replace(/: \[\n\s+([\da-z.-]+),\n\s+([\da-z.-]+)\n\s+],/g, ": [$1, $2],") // keep sets on one line
.replace(/],\n(\s+)"(.+?)": \[\n/g, '],\n\n$1"$2": [\n') // blank line between animations
.replace(' "animations"', '\n "animations"') // blank line before animation list
fs.writeFileSync('./parsed/robotAnimations.json', cleanJSON); // regex to make it easier to read
fs.writeFileSync('./parsed/robotAnimations.json', cleanJSON); // regex to make it easier to read
console.log("Successfully parsed!")

View file

@ -17,7 +17,6 @@
[ "Cool", 37, 20, 17, 1 ],
[ "Cyclic", 30, 3, 12, 0 ],
[ "DanZmeN", 104, 34, 12, 1 ],
[ "DanZmeN", 104, 34, 12, 1 ],
[ "envylol", 73, 20, 1, 1],
Rudxain commented 2022-07-03 14:34:33 -04:00 (Migrated from github.com)
Review

DanZmeN was duped lol

DanZmeN was duped lol
[ "EVW", 28, 12, 9, 0 ],
[ "Flub", 25, 3, 12, 1 ],

View file

@ -2,10 +2,10 @@
// Feel free to enable/disable stuff here for smoother local use, free of rate limits
module.exports = {
port: 2000, // Port to host website on
params: { // Always send this stuff to the servers
params: { // Always send this stuff to the servers
secret: 'Wmfd2893gb7',
gameVersion: '21',
binaryVersion: '35',