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

View file

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

View file

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

View file

@ -1,3 +1,4 @@
"use strict";
let cache = {} let cache = {}
let gauntletNames = ["Fire", "Ice", "Poison", "Shadow", "Lava", "Bonus", "Chaos", "Demon", "Time", "Crystal", "Magic", "Spike", "Monster", "Doom", "Death"] 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() if (req.offline) return res.sendError()
let cached = cache[req.id] 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) { req.gdRequest('getGJGauntlets21', {}, function (err, resp, body) {
if (err) return res.sendError() 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(",") })) 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) res.send(gauntletList)
}) })
} }

View file

@ -1,5 +1,6 @@
const {GoogleSpreadsheet} = require('google-spreadsheet'); "use strict";
const sheet = new GoogleSpreadsheet('1ADIJvAkL0XHGBDhO7PP9aQOuK3mPIKB2cVPbshuBBHc'); // accurate leaderboard spreadsheet const {GoogleSpreadsheet} = require('google-spreadsheet')
const sheet = new GoogleSpreadsheet('1ADIJvAkL0XHGBDhO7PP9aQOuK3mPIKB2cVPbshuBBHc') // accurate leaderboard spreadsheet
let indexes = ["stars", "coins", "demons", "diamonds"] 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) => { module.exports = async (app, req, res, post) => {
// Accurate leaderboard returns 418 because private servers do not use. // Accurate leaderboard returns 418 because private servers do not use.
if (req.isGDPS) return res.status(418).send("-2") if (req.isGDPS) return res.status(418).send("-2")
if (!app.sheetsKey) return res.status(500).send([]) if (!app.sheetsKey) return res.status(500).send([])
let gdMode = post || req.query.hasOwnProperty("gd") const gdMode = post || req.query.hasOwnProperty("gd")
let modMode = !gdMode && req.query.hasOwnProperty("mod") const modMode = !gdMode && req.query.hasOwnProperty("mod")
let cache = caches[gdMode ? 2 : modMode ? 1 : 0] let cache = caches[gdMode ? 2 : modMode ? 1 : 0]
let type = req.query.type ? req.query.type.toLowerCase() : 'stars' let type = req.query.type ? req.query.type.toLowerCase() : 'stars'
if (type == "usercoins") type = "coins" if (type == "usercoins") type = "coins"
if (!indexes.includes(type)) type = "stars" 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 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.useApiKey(app.sheetsKey)
sheet.loadInfo().then(async () => { sheet.loadInfo().then(async () => {
let tab = sheet.sheetsById[1555821000] let tab = sheet.sheetsById[1555821000]
await tab.loadCells('A2:H2') await tab.loadCells('A2:H2')
let cellIndex = indexes.findIndex(x => type == x) let cellIndex = indexes.indexOf(type)
if (modMode) cellIndex += indexes.length if (modMode) cellIndex += indexes.length
let cell = tab.getCell(1, cellIndex).value 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() } if (!cell || typeof cell != "string" || cell.startsWith("GoogleSpreadsheetFormulaError")) {
let leaderboard = JSON.parse(cell.replace(/~( |$)/g, "")) console.log("Spreadsheet Error:"); console.log(cell); return res.sendError()
}
let gdFormatting = "" let leaderboard = JSON.parse(cell.replace(/~( |$)/g, ""))
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 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') const request = require('request')
module.exports = async (app, req, res) => { module.exports = async (app, req, res) => {
// Accurate leaderboard returns 418 because Private servers do not use. // Accurate leaderboard returns 418 because Private servers do not use.
if (req.isGDPS) return res.status(418).send("0") if (req.isGDPS) return res.status(418).send("0")
request.post('http://robtopgames.com/Boomlings/get_scores.php', { request.post(
form : { secret: app.config.params.secret || "Wmfd2893gb7", name: "Player" } }, function(err, resp, body) { '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") 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), score: +scores.slice(3, 10),
boomling: +visuals.slice(5, 7), boomling: +visuals.slice(5, 7),
boomlingLevel: +visuals.slice(2, 4), 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), unknownVisual: +visuals.slice(0, 2),
unknownScore: +scores.slice(0, 1), 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.boomling || user.boomling > 66 || user.boomling < 0) user.boomling = 0
if (!user.boomlingLevel || user.boomlingLevel > 25 || user.boomlingLevel < 1) user.boomlingLevel = 25 if (!user.boomlingLevel || user.boomlingLevel > 25 || user.boomlingLevel < 1) user.boomlingLevel = 25
users.push(user) users.push(user)
}) })
return res.send(users) 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) => { module.exports = async (app, req, res) => {
if (req.offline) return res.sendError() if (req.offline) return res.sendError()
let amount = 100; let amount = 100
let count = req.query.count ? parseInt(req.query.count) : null let count = req.query.count ? parseInt(req.query.count) : null
if (count && count > 0) { if (count && count > 0) amount = Math.min(count, 200)
if (count > 200) amount = 200
else amount = count;
}
let params = { let params = {
levelID: req.params.id, levelID: req.params.id,
accountID: app.id, accountID: app.id,
gjp: app.gjp, gjp: app.gjp,
type: req.query.hasOwnProperty("week") ? "2" : "1", 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)}) 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]) scores = body.split('|').map(x => app.parseResponse(x)).filter(x => x[1])
if (!scores.length) return res.status(500).send([]) if (!scores.length) return res.status(500).send([])
else app.trackSuccess(req.id) app.trackSuccess(req.id)
scores.forEach(x => { scores.forEach(x => {
let keys = Object.keys(x) let keys = Object.keys(x)
x.rank = x[6] x.rank = x[6]
x.username = x[1] x.username = x[1]
x.percent = +x[3] x.percent = +x[3]
x.coins = +x[13] x.coins = +x[13]
x.playerID = x[2] x.playerID = x[2]
x.accountID = x[16] x.accountID = x[16]
x.date = x[42] + req.timestampSuffix x.date = x[42] + req.timestampSuffix
x.icon = { x.icon = {
form: ['icon', 'ship', 'ball', 'ufo', 'wave', 'robot', 'spider'][+x[14]], form: ['icon', 'ship', 'ball', 'ufo', 'wave', 'robot', 'spider'][+x[14]],
icon: +x[9], icon: +x[9],
col1: +x[10], col1: +x[10],
col2: +x[11], col2: +x[11],
glow: +x[15] > 1, glow: +x[15] > 1,
col1RGB: colors[x[10]] || colors["0"], col1RGB: colors[x[10]] || colors["0"],
col2RGB: colors[x[11]] || colors["3"] col2RGB: colors[x[11]] || colors["3"]
} }
keys.forEach(k => delete x[k]) keys.forEach(k => delete x[k])
app.userCache(req.id, x.accountID, x.playerID, x.username) 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') const Player = require('../../classes/Player.js')
module.exports = async (app, req, res) => { module.exports = async (app, req, res) => {
if (req.offline) return res.sendError() if (req.offline) return res.sendError()
let amount = 100; let amount = 100
let count = req.query.count ? parseInt(req.query.count) : null let count = req.query.count ? parseInt(req.query.count) : 0
if (count && count > 0) { if (count > 0) amount = Math.min(count, 10000)
if (count > 10000) amount = 10000
else amount = count;
}
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" let isInQuery = (...args) => args.some(x => req.query.hasOwnProperty(x) || req.query.type == x)
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
}
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() req.gdRequest('getGJScores20', params, function(err, resp, body) {
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)) if (err) return res.sendError()
scores.forEach(x => app.userCache(req.id, x.accountID, x.playerID, x.username)) scores = body.split('|').map(x => app.parseResponse(x)).filter(x => x[1])
return res.send(scores.slice(0, amount)) 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 request = require('request')
const fs = require('fs') const fs = require('fs')
const Level = require('../classes/Level.js') const Level = require('../classes/Level.js')
@ -5,17 +6,14 @@ const Level = require('../classes/Level.js')
module.exports = async (app, req, res, api, analyze) => { module.exports = async (app, req, res, api, analyze) => {
function rejectLevel() { function rejectLevel() {
if (!api) return res.redirect('search/' + req.params.id) return !api ? res.redirect('search/' + req.params.id) : res.sendError()
else return res.sendError()
} }
if (req.offline) return rejectLevel() if (req.offline) return rejectLevel()
let levelID = req.params.id let levelID = req.params.id
if (levelID == "daily") return app.run.download(app, req, res, api, 'daily', analyze) if (levelID == "daily" || levelID == "weekly") return app.run.download(app, req, res, api, levelID, analyze)
else if (levelID == "weekly") return app.run.download(app, req, res, api, 'weekly', analyze) else if (/\D/.test(levelID)) return rejectLevel()
else if (levelID.match(/[^0-9]/)) return rejectLevel()
else levelID = levelID.replace(/[^0-9]/g, "")
if (analyze || req.query.hasOwnProperty("download")) return app.run.download(app, req, res, api, levelID, analyze) 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() if (err || body.startsWith("##")) return rejectLevel()
let preRes = body.split('#')[0].split('|', 10) // "revolutionary name XD" @Rudxain
let author = body.split('#')[1].split('|')[0].split(':') const bodySplit = body.split('#')
let song = '~' + body.split('#')[2]; let preRes = bodySplit[0].split('|', 10)
let author = bodySplit[1].split('|', 1)[0].split(':')
let song = '~' + bodySplit[2]
song = app.parseResponse(song, '~|~') song = app.parseResponse(song, '~|~')
let levelInfo = app.parseResponse(preRes.find(x => x.startsWith(`1:${levelID}`)) || preRes[0]) 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) if (api) return res.send(level)
else return fs.readFile('./html/level.html', 'utf8', function (err, data) { 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 let filteredSong = level.songName.replace(/[^ -~]/g, "") // strip off unsupported characters
level.songName = filteredSong || level.songName level.songName = filteredSong || level.songName
let variables = Object.keys(level) 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 difficulties = ["auto", "easy", "normal", "hard", "harder", "insane", "demon", "demon-easy", "demon-medium", "demon-insane", "demon-extreme"]
let cache = {} let cache = {}
module.exports = async (app, req, res) => { module.exports = async (app, req, res) => {
if (req.offline) return res.sendError() if (req.offline) return res.sendError()
let cached = cache[req.id] 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 params = { count: 250, page: 0 }
let packs = [] let packs = []
@ -15,7 +17,7 @@ module.exports = async (app, req, res) => {
if (err) return res.sendError() 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) packs = packs.concat(newPacks)
// not all GDPS'es support the count param, which means recursion time!!! // not all GDPS'es support the count param, which means recursion time!!!
@ -23,7 +25,7 @@ module.exports = async (app, req, res) => {
params.page++ params.page++
return mapPackLoop() return mapPackLoop()
} }
let mappacks = packs.map(x => ({ // "packs.map()" laugh now please let mappacks = packs.map(x => ({ // "packs.map()" laugh now please
id: +x[1], id: +x[1],
name: x[2], name: x[2],

View file

@ -1,9 +1,11 @@
"use strict";
module.exports = async (app, req, res) => { 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.accountID) return send("No account ID provided!")
if (!req.body.password) return res.status(400).send("No password provided!") if (!req.body.password) return send("No password provided!")
let params = { let params = {
accountID: req.body.accountID, accountID: req.body.accountID,
@ -13,11 +15,11 @@ module.exports = async (app, req, res) => {
req.gdRequest('getGJUserInfo20', params, function (err, resp, body) { 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.`) 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.`)
else app.trackSuccess(req.id) app.trackSuccess(req.id)
let count = app.parseResponse(body)[38] let count = app.parseResponse(body)[38]
if (!count) return res.status(400).send("Error fetching unread messages!") if (!count) return send("Error fetching unread messages!")
else res.send(count) res.send(count)
}) })
} }

View file

@ -1,23 +1,26 @@
"use strict";
module.exports = async (app, req, res) => { 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.accountID) return send("No account ID provided!")
if (!req.body.password) return res.status(400).send("No password provided!") if (!req.body.password) return send("No password provided!")
if (!req.body.id) return res.status(400).send("No message ID(s) provided!") if (!req.body.id) return send("No message ID(s) provided!")
let params = { let params = {
accountID: req.body.accountID, accountID: req.body.accountID,
gjp: app.xor.encrypt(req.body.password, 37526), 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, 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) { 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.`) 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.`)
else res.send(`${deleted == 1 ? "1 message" : `${deleted} messages`} deleted!`) res.send(`${deleted} message${deleted == 1 ? "" : "s"} deleted!`)
app.trackSuccess(req.id) app.trackSuccess(req.id)
}) })

View file

@ -1,9 +1,11 @@
"use strict";
module.exports = async (app, req, res) => { 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.accountID) return send("No account ID provided!")
if (!req.body.password) return res.status(400).send("No password provided!") if (!req.body.password) return send("No password provided!")
let params = req.gdParams({ let params = req.gdParams({
accountID: req.body.accountID, accountID: req.body.accountID,
@ -13,24 +15,25 @@ module.exports = async (app, req, res) => {
req.gdRequest('downloadGJMessage20', params, function (err, resp, body) { 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.`) 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.`)
else app.trackSuccess(req.id) app.trackSuccess(req.id)
let x = app.parseResponse(body) let x = app.parseResponse(body)
let msg = {} let subject = Buffer.from(x[4], "base64").toString().replace(/^Re: ☆/, "Re: ")
msg.id = x[1]; let msg = {
msg.playerID = x[3] id: x[1],
msg.accountID = x[2] playerID: x[3],
msg.author = x[6] accountID: x[2],
msg.subject = Buffer.from(x[4], "base64").toString().replace(/^Re: ☆/, "Re: ") author: x[6],
msg.content = app.xor.decrypt(x[5], 14251) subject,
msg.date = x[7] + req.timestampSuffix content: app.xor.decrypt(x[5], 14251),
if (msg.subject.endsWith("☆") || msg.subject.startsWith("☆")) { date: x[7] + req.timestampSuffix
if (msg.subject.endsWith("☆")) msg.subject = msg.subject.slice(0, -1) }
else msg.subject = msg.subject.slice(1) if (/^☆|☆$/.test(subject)) {
msg.browserColor = true msg.subject = subject.slice(...(subject.endsWith("☆") ? [0, -1] : [1]))
} msg.browserColor = true
}
return res.send(msg) return res.send(msg)
}) })

View file

@ -1,10 +1,12 @@
"use strict";
module.exports = async (app, req, res) => { 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.count) return app.run.countMessages(app, req, res)
if (!req.body.accountID) return res.status(400).send("No account ID provided!") if (!req.body.accountID) return send("No account ID provided!")
if (!req.body.password) return res.status(400).send("No password provided!") if (!req.body.password) return send("No password provided!")
let params = req.gdParams({ let params = req.gdParams({
accountID: req.body.accountID, accountID: req.body.accountID,
@ -15,26 +17,26 @@ module.exports = async (app, req, res) => {
req.gdRequest('getGJMessages20', params, function (err, resp, body) { 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) else app.trackSuccess(req.id)
let messages = body.split("|").map(msg => app.parseResponse(msg)) let messages = body.split("|").map(msg => app.parseResponse(msg))
let messageArray = [] let messageArray = []
messages.forEach(x => { messages.forEach(x => {
let msg = {} let subject = Buffer.from(x[4], "base64").toString().replace(/^Re: ☆/, "Re: ")
let msg = {
msg.id = x[1]; id: x[1],
msg.playerID = x[3] playerID: x[3],
msg.accountID = x[2] accountID: x[2],
msg.author = x[6] author: x[6],
msg.subject = Buffer.from(x[4], "base64").toString().replace(/^Re: ☆/, "Re: ") subject,
msg.date = x[7] + req.timestampSuffix date: x[7] + req.timestampSuffix,
msg.unread = x[8] != "1" unread: x[8] != "1"
if (msg.subject.endsWith("☆") || msg.subject.startsWith("☆")) { }
if (msg.subject.endsWith("☆")) msg.subject = msg.subject.slice(0, -1) if (/^☆|☆$/.test(subject)) {
else msg.subject = msg.subject.slice(1) msg.subject = subject.slice(...(subject.endsWith("☆") ? [0, -1] : [1]))
msg.browserColor = true msg.browserColor = true
} }
app.userCache(req.id, msg.accountID, msg.playerID, msg.author) app.userCache(req.id, msg.accountID, msg.playerID, msg.author)
messageArray.push(msg) messageArray.push(msg)

View file

@ -1,25 +1,33 @@
"use strict";
module.exports = async (app, req, res) => { 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.targetID) return send("No target ID provided!")
if (!req.body.message) return res.status(400).send("No message provided!") if (!req.body.message) return send("No message provided!")
if (!req.body.accountID) return res.status(400).send("No account ID provided!") if (!req.body.accountID) return send("No account ID provided!")
if (!req.body.password) return res.status(400).send("No password 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 body = app.xor.encrypt(req.body.message.slice(0, 300), 14251)
let params = req.gdParams({ let params = req.gdParams({
accountID: req.body.accountID, accountID: req.body.accountID,
gjp: app.xor.encrypt(req.body.password, 37526), gjp: app.xor.encrypt(req.body.password, 37526),
toAccountID: req.body.targetID, toAccountID: req.body.targetID,
subject, body, subject, body
}) })
req.gdRequest('uploadGJMessage20', params, function (err, resp, 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.`) if (body != 1) return send(
else res.send('Message sent!') `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) app.trackSuccess(req.id)
}) })

View file

@ -1,17 +1,31 @@
"use strict";
const crypto = require('crypto') 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) => { 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 = { let params = {
udid: '0', udid: '0',
uuid: '0', uuid: '0',
@ -25,14 +39,15 @@ module.exports = async (app, req, res) => {
params.special = req.body.extraID.toString() params.special = req.body.extraID.toString()
params.type = req.body.type.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 = sha1(chk)
chk = app.xor.encrypt(chk, 58281) chk = app.xor.encrypt(chk, 58281)
params.chk = chk params.chk = chk
req.gdRequest('likeGJItem211', params, function (err, resp, body) { 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) 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)") 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') 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 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) { function getTime(time) {
let seconds = Math.ceil(time / 1000); let seconds = Math.ceil(time / 1000)
seconds = seconds % 60; seconds %= 60
return seconds} return seconds
}
module.exports = async (app, req, res) => { 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.comment) return send("No comment provided!")
if (!req.body.username) return res.status(400).send("No username provided!") if (!req.body.username) return send("No username provided!")
if (!req.body.levelID) return res.status(400).send("No level ID provided!") if (!req.body.levelID) return send("No level ID provided!")
if (!req.body.accountID) return res.status(400).send("No account ID provided!") if (!req.body.accountID) return send("No account ID provided!")
if (!req.body.password) return res.status(400).send("No password 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 } 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.gjp = app.xor.encrypt(req.body.password, 37526)
params.levelID = req.body.levelID.toString() params.levelID = req.body.levelID.toString()
params.accountID = req.body.accountID.toString() params.accountID = req.body.accountID.toString()
@ -40,15 +55,22 @@ module.exports = async (app, req, res) => {
params.chk = chk params.chk = chk
req.gdRequest('uploadGJComment21', params, function (err, resp, body) { 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")) { if (body.startsWith("temp")) {
let banStuff = body.split("_") 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}`) res.send(`Comment posted to level ${params.levelID} with ID ${body}`)
app.trackSuccess(req.id) app.trackSuccess(req.id)
rateLimit[req.body.username] = Date.now(); rateLimit[req.body.username] = Date.now()
setTimeout(() => {delete rateLimit[req.body.username]; }, cooldown); setTimeout(() => {delete rateLimit[req.body.username]}, cooldown);
}) })
} }

View file

@ -1,20 +1,32 @@
"use strict";
const crypto = require('crypto') 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) => { 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.comment) return send("No comment provided!")
if (!req.body.username) return res.status(400).send("No username provided!") if (!req.body.username) return send("No username provided!")
if (!req.body.accountID) return res.status(400).send("No account ID provided!") if (!req.body.accountID) return send("No account ID provided!")
if (!req.body.password) return res.status(400).send("No password 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' } 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.gjp = app.xor.encrypt(req.body.password, 37526)
params.accountID = req.body.accountID.toString() params.accountID = req.body.accountID.toString()
params.userName = req.body.username params.userName = req.body.username
@ -25,10 +37,10 @@ module.exports = async (app, req, res) => {
params.chk = chk params.chk = chk
req.gdRequest('uploadGJAccComment20', params, function (err, resp, body) { 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")) { else if (body.startsWith("temp")) {
let banStuff = body.split("_") 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) else app.trackSuccess(req.id)
res.send(`Comment posted to ${params.userName} with ID ${body}`) res.send(`Comment posted to ${params.userName} with ID ${body}`)

View file

@ -1,13 +1,15 @@
"use strict";
const fs = require('fs') const fs = require('fs')
const Player = require('../classes/Player.js') const Player = require('../classes/Player.js')
module.exports = async (app, req, res, api, getLevels) => { module.exports = async (app, req, res, api, getLevels) => {
function rejectLevel() {
if (req.offline) { // this variant has an extra "/"
if (!api) return res.redirect('/search/' + req.params.id) return !api ? res.redirect('/search/' + req.params.id) : res.sendError()
else return res.sendError()
} }
if (req.offline) return rejectLevel()
let username = getLevels || req.params.id let username = getLevels || req.params.id
let probablyID let probablyID
if (username.endsWith(".") && req.isGDPS) { 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 accountMode = !req.query.hasOwnProperty("player") && Number(req.params.id)
let foundID = app.userCache(req.id, username) let foundID = app.userCache(req.id, username)
let skipRequest = accountMode || foundID || probablyID 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) // 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] if (foundID) searchResult = foundID[0]
else if (accountMode || err1 || b1 == '-1' || b1.startsWith("<") || !b1) searchResult = probablyID ? username : req.params.id 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 else { // GDPS's return multiple users, GD no longer does this
let userResults = b1.split("|").map(x => app.parseResponse(x)) let userResults = b1.split("|").map(x => app.parseResponse(x))
searchResult = userResults.find(x => x[1].toLowerCase() == username.toLowerCase() || x[2] == username) || "" searchResult = userResults.find(x => x[1].toLowerCase() == username.toLowerCase() || x[2] == username) || ""
if (searchResult) searchResult = searchResult[16] searchResult &&= searchResult[16]
} }
if (getLevels) { if (getLevels) {
@ -40,20 +42,17 @@ module.exports = async (app, req, res, api, getLevels) => {
let account = app.parseResponse(body || "") let account = app.parseResponse(body || "")
let dumbGDPSError = req.isGDPS && (!account[16] || account[1].toLowerCase() == "undefined") let dumbGDPSError = req.isGDPS && (!account[16] || account[1].toLowerCase() == "undefined")
if (err2 || dumbGDPSError) { if (err2 || dumbGDPSError) return rejectLevel()
if (!api) return res.redirect('/search/' + req.params.id)
else return res.sendError()
}
if (!foundID) app.userCache(req.id, account[16], account[2], account[1]) 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) if (api) return res.send(userData)
else fs.readFile('./html/profile.html', 'utf8', function(err, data) { else fs.readFile('./html/profile.html', 'utf8', function(err, data) {
let html = data; let html = data
let variables = Object.keys(userData) let variables = Object.keys(userData)
variables.forEach(x => { variables.forEach(x => {
let regex = new RegExp(`\\[\\[${x.toUpperCase()}\\]\\]`, "g") let regex = new RegExp(`\\[\\[${x.toUpperCase()}\\]\\]`, "g")
@ -61,7 +60,6 @@ module.exports = async (app, req, res, api, getLevels) => {
}) })
return res.send(html) return res.send(html)
}) })
})
}) })
} })
}

View file

@ -1,94 +1,92 @@
"use strict";
const request = require('request') const request = require('request')
const music = require('../misc/music.json')
const Level = require('../classes/Level.js') const Level = require('../classes/Level.js')
let demonList = {} let demonList = {}
module.exports = async (app, req, res) => { 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 = query.hasOwnProperty("demonlist") || query.hasOwnProperty("demonList") || query.type == "demonlist" || query.type == "demonList"
let demonMode = req.query.hasOwnProperty("demonlist") || req.query.hasOwnProperty("demonList") || req.query.type == "demonlist" || req.query.type == "demonList"
if (demonMode) { if (demonMode) {
if (!req.server.demonList) return res.sendError(400) if (!req.server.demonList) return res.sendError(400)
let dList = demonList[req.id] let dList = demonList[req.id]
if (!dList || !dList.list.length || dList.lastUpdated + 600000 < Date.now()) { // 10 minute cache 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() 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() 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) return app.run.search(app, req, res)
}) })
}) })
} }
} }
let amount = 10; let amount = 10
let count = req.isGDPS ? 10 : +req.query.count let count = req.isGDPS ? 10 : +query.count
if (count && count > 0) { if (count && count > 0) amount = Math.min(count, 500)
if (count > 500) amount = 500
else amount = count;
}
let filters = { let filters = {
str: req.params.text, str: req.params.text,
diff: req.query.diff, diff: query.diff,
demonFilter: req.query.demonFilter, demonFilter: query.demonFilter,
page: req.query.page || 0, page: query.page || 0,
gauntlet: req.query.gauntlet || 0, gauntlet: query.gauntlet || 0,
len: req.query.length, len: query.length,
song: req.query.songID, song: query.songID,
followed: req.query.creators, followed: query.creators,
featured: req.query.hasOwnProperty("featured") ? 1 : 0, featured: query.hasOwnProperty("featured") ? 1 : 0,
originalOnly: req.query.hasOwnProperty("original") ? 1 : 0, originalOnly: query.hasOwnProperty("original") ? 1 : 0,
twoPlayer: req.query.hasOwnProperty("twoPlayer") ? 1 : 0, twoPlayer: query.hasOwnProperty("twoPlayer") ? 1 : 0,
coins: req.query.hasOwnProperty("coins") ? 1 : 0, coins: query.hasOwnProperty("coins") ? 1 : 0,
epic: req.query.hasOwnProperty("epic") ? 1 : 0, epic: query.hasOwnProperty("epic") ? 1 : 0,
star: req.query.hasOwnProperty("starred") ? 1 : 0, star: query.hasOwnProperty("starred") ? 1 : 0,
noStar: req.query.hasOwnProperty("noStar") ? 1 : 0, noStar: query.hasOwnProperty("noStar") ? 1 : 0,
customSong: req.query.hasOwnProperty("customSong") ? 1 : 0, customSong: query.hasOwnProperty("customSong") ? 1 : 0,
type: req.query.type || 0, type: query.type || 0,
count: amount count: amount
} }
if (req.query.type) { if (query.type) {
let filterCheck = req.query.type.toLowerCase() let filterCheck = query.type.toLowerCase()
switch(filterCheck) { let typeMap = {
case 'mostdownloaded': filters.type = 1; break; 'mostdownloaded': 1, 'mostliked': 2,
case 'mostliked': filters.type = 2; break; 'trending': 3, 'recent': 4,
case 'trending': filters.type = 3; break; 'featured': 6, 'magic': 7,
case 'recent': filters.type = 4; break; 'awarded': 11, 'starred': 11,
case 'featured': filters.type = 6; break; 'halloffame': 16, 'hof': 16,
case 'magic': filters.type = 7; break; 'gdw': 17, 'gdworld': 17
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 (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) let accountCheck = app.userCache(req.id, filters.str)
filters.type = 5 filters.type = 5
if (accountCheck) filters.str = accountCheck[1] 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 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.type = 10
filters.str = demonMode ? demonList[req.id].list : filters.str.split(",") filters.str = demonMode ? demonList[req.id].list : filters.str.split(",")
listSize = filters.str.length listSize = filters.str.length
filters.str = filters.str.slice(filters.page*amount, filters.page*amount + amount) filters.str = filters.str.slice(filters.page*amount, filters.page*amount + amount)
if (!filters.str.length) return res.sendError(400) 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 filters.page = 0
} }
@ -103,25 +101,25 @@ module.exports = async (app, req, res) => {
let authorList = {} let authorList = {}
let songList = {} let songList = {}
let authors = splitBody[1].split('|') 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']}) songs.forEach(x => {songList[x['~1']] = x['2']})
authors.forEach(x => { authors.forEach(x => {
if (x.startsWith('~')) return if (x.startsWith('~')) return
let arr = x.split(':') let arr = x.split(':')
authorList[arr[0]] = [arr[1], arr[2]]}) authorList[arr[0]] = [arr[1], arr[2]]
})
let levelArray = preRes.map(x => app.parseResponse(x)).filter(x => x[1]) let levelArray = preRes.map(x => app.parseResponse(x)).filter(x => x[1])
let parsedLevels = [] let parsedLevels = []
levelArray.forEach((x, y) => { levelArray.forEach((x, y) => {
let songSearch = songs.find(y => y['~1'] == x[35]) || [] let songSearch = songs.find(y => y['~1'] == x[35]) || []
let level = new Level(x, req.server).getSongInfo(songSearch) let level = new Level(x, req.server).getSongInfo(songSearch)
if (!level.id) return if (!level.id) return
level.author = authorList[x[6]] ? authorList[x[6]][0] : "-"; level.author = authorList[x[6]] ? authorList[x[6]][0] : "-"
level.accountID = authorList[x[6]] ? authorList[x[6]][1] : "0"; level.accountID = authorList[x[6]] ? authorList[x[6]][1] : "0"
if (demonMode) { if (demonMode) {
if (!y) level.demonList = req.server.demonList 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 //this is broken if you're not on page 0, blame robtop
if (filters.page == 0 && y == 0 && splitBody[3]) { if (filters.page == 0 && y == 0 && splitBody[3]) {
let pages = splitBody[3].split(":"); let pages = splitBody[3].split(":")
if (filters.gauntlet) { // gauntlet page stuff if (filters.gauntlet) { // gauntlet page stuff
level.results = levelArray.length level.results = levelArray.length
level.pages = 1 level.pages = 1
} }
else if (filters.type == 10) { // custom page stuff else if (filters.type == 10) { // custom page stuff
level.results = listSize level.results = listSize
level.pages = +Math.ceil(listSize / (amount || 10)) level.pages = Math.ceil(listSize / (amount || 10))
} }
else { // normal page stuff else { // normal page stuff
level.results = +pages[0]; level.results = +pages[0]
level.pages = +pages[0] == 9999 ? 1000 : +Math.ceil(pages[0] / amount); 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) => { module.exports = async (app, req, res) => {
if (req.offline) return res.sendError() if (req.offline) return res.sendError()
let songID = req.params.song let songID = req.params.song
req.gdRequest('getGJSongInfo', {songID: songID}, function(err, resp, body) { req.gdRequest('getGJSongInfo', {songID: songID}, function(err, resp, body) {
if (err) return res.sendError(400) return err
return res.send(!body.startsWith("-") && body.length > 10) ? res.sendError(400)
: res.send(!body.startsWith("-") && body.length > 10)
}) })
} }

View file

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

View file

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

View file

@ -14,7 +14,7 @@
} }
body { body {
background-color: 555555; background-color: #555;
} }
p { p {
@ -144,9 +144,9 @@ input:focus, select:focus, textarea:focus, button:focus {
} }
.supercenter { .supercenter {
position: absolute; position: absolute;
top: 50%; top: 50%;
left: 50%; left: 50%;
transform: translate(-50%,-50%); transform: translate(-50%,-50%);
} }
@ -194,7 +194,7 @@ input:focus, select:focus, textarea:focus, button:focus {
padding: 10 10 10 10; padding: 10 10 10 10;
overflow: auto; overflow: auto;
white-space: nowrap; white-space: nowrap;
border-radius: 8px; border-radius: 8px;
} }
#symbols p { #symbols p {
@ -238,8 +238,8 @@ input:focus, select:focus, textarea:focus, button:focus {
#generate { #generate {
font-family: "Roboto"; font-family: "Roboto";
border: rgba(0, 0, 0, 0); border: rgba(0, 0, 0, 0);
background-color: #88FF33; background-color: #8F3;
box-shadow: 2px 2px 3px #66AA22; box-shadow: 2px 2px 3px #6A2;
border-radius: 10px; border-radius: 10px;
color: black; color: black;
padding: 10px 15px 10px 15px; padding: 10px 15px 10px 15px;
@ -259,8 +259,8 @@ input:focus, select:focus, textarea:focus, button:focus {
.miniButton { .miniButton {
font-family: "Roboto"; font-family: "Roboto";
border: rgba(0, 0, 0, 0); border: rgba(0, 0, 0, 0);
background-color: #88FF33; background-color: #8F3;
box-shadow: 1.5px 1.5px 2px #66AA22; box-shadow: 1.5px 1.5px 2px #6A2;
border-radius: 10px; border-radius: 10px;
color: black; color: black;
padding: 7px 9px 7px 9px; padding: 7px 9px 7px 9px;
@ -375,11 +375,11 @@ input:focus, select:focus, textarea:focus, button:focus {
#colors::-webkit-scrollbar, #iconKitParent::-webkit-scrollbar { #colors::-webkit-scrollbar, #iconKitParent::-webkit-scrollbar {
width: 9px; width: 9px;
height: 10px; height: 10px;
background: rgba(0, 0, 0, 0.5); background: rgba(0, 0, 0, 0.5);
} }
#colors::-webkit-scrollbar-thumb, #iconKitParent::-webkit-scrollbar-thumb { #colors::-webkit-scrollbar-thumb, #iconKitParent::-webkit-scrollbar-thumb {
background: rgb(185, 185, 185); background: rgb(185, 185, 185);
} }
#iconKitParent { #iconKitParent {
@ -394,7 +394,7 @@ input:focus, select:focus, textarea:focus, button:focus {
.iconColor div { .iconColor div {
width: 50px; width: 50px;
height: 50px; height: 50px;
} }
#gdfloor { #gdfloor {
@ -423,13 +423,13 @@ input:focus, select:focus, textarea:focus, button:focus {
} }
#iconprogressbar { #iconprogressbar {
background: rgba(0, 0, 0, 0.5); background: rgba(0, 0, 0, 0.5);
height: 7px; height: 7px;
margin-bottom: 12px; margin-bottom: 12px;
} }
#iconloading { #iconloading {
background: rgba(255, 255, 255, 0.5); background: rgba(255, 255, 255, 0.5);
height: 7px; height: 7px;
width: 0%; width: 0%;
} }
@ -447,8 +447,8 @@ body::-webkit-scrollbar {
} }
.popup { .popup {
position: fixed; position: fixed;
display: none; display: none;
width: 100%; width: 100%;
height: 100%; height: 100%;
top: 0; left: 0; right: 0; bottom: 0; top: 0; left: 0; right: 0; bottom: 0;
@ -459,7 +459,7 @@ body::-webkit-scrollbar {
.brownBox { .brownBox {
border: 17px solid rgba(0, 0, 0, 0); border: 17px solid rgba(0, 0, 0, 0);
border-radius: 25px; border-radius: 25px;
background-color: #995533; background-color: #953;
border-image: url('../../assets/brownbox.png') 10% round; 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); } } @-moz-keyframes spin { 100% { -moz-transform: rotate(360deg); } }
@-webkit-keyframes spin { 100% { -webkit-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"); "use strict";
const music = require(__dirname + "/../misc/music.json"); const XOR = require("./XOR")
const music = require("../misc/music.json")
let orbs = [0, 0, 50, 75, 125, 175, 225, 275, 350, 425, 500] let orbs = [0, 0, 50, 75, 125, 175, 225, 275, 350, 425, 500]
let length = ['Tiny', 'Short', 'Medium', 'Long', 'XL'] 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 difficulty = { 0: 'Unrated', 10: 'Easy', 20: 'Normal', 30: 'Hard', 40: 'Harder', 50: 'Insane' }
let demonTypes = { 3: "Easy", 4: "Medium", 5: "Insane", 6: "Extreme" } let demonTypes = { 3: "Easy", 4: "Medium", 5: "Insane", 6: "Extreme" }
let dailyLimit = 100000
class Level { class Level {
constructor(levelInfo, server, download, author = []) { constructor(levelInfo, server, download, author = []) {
this.name = levelInfo[2] || "-"; this.name = levelInfo[2] || "-"
this.id = levelInfo[1] || 0; this.id = levelInfo[1] || 0
this.description = Buffer.from((levelInfo[3] || ""), "base64").toString() || "(No description provided)"; this.description = Buffer.from((levelInfo[3] || ""), "base64").toString() || "(No description provided)"
this.author = author[1] || "-" this.author = author[1] || "-"
this.playerID = levelInfo[6] || 0 this.playerID = levelInfo[6] || 0
this.accountID = author[2] || 0 this.accountID = author[2] || 0
@ -29,8 +32,8 @@ class Level {
if (levelInfo[29]) this.updated = levelInfo[29] + (server.timestampSuffix || "") if (levelInfo[29]) this.updated = levelInfo[29] + (server.timestampSuffix || "")
if (levelInfo[46]) this.editorTime = +levelInfo[46] || 0 if (levelInfo[46]) this.editorTime = +levelInfo[46] || 0
if (levelInfo[47]) this.totalEditorTime = +levelInfo[47] || 0 if (levelInfo[47]) this.totalEditorTime = +levelInfo[47] || 0
if (levelInfo[27]) this.password = levelInfo[27]; if (levelInfo[27]) this.password = levelInfo[27]
this.version = +levelInfo[5] || 0; this.version = +levelInfo[5] || 0
this.copiedID = levelInfo[30] || "0" this.copiedID = levelInfo[30] || "0"
this.twoPlayer = levelInfo[31] > 0 this.twoPlayer = levelInfo[31] > 0
this.officialSong = +levelInfo[35] ? 0 : parseInt(levelInfo[12]) + 1 this.officialSong = +levelInfo[35] ? 0 : parseInt(levelInfo[12]) + 1
@ -39,21 +42,24 @@ class Level {
this.verifiedCoins = levelInfo[38] > 0 this.verifiedCoins = levelInfo[38] > 0
this.starsRequested = +levelInfo[39] || 0 this.starsRequested = +levelInfo[39] || 0
this.ldm = levelInfo[40] > 0 this.ldm = levelInfo[40] > 0
if (+levelInfo[41] > 100000) this.weekly = true if (+levelInfo[41] > dailyLimit) 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]) {
this.dailyNumber = (+levelInfo[41] > dailyLimit ? +levelInfo[41] - dailyLimit : +levelInfo[41])
this.nextDaily = null
this.nextDailyTimestamp = null
}
this.objects = +levelInfo[45] || 0 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) this.cp = Number((this.stars > 0) + this.featured + this.epic)
if (levelInfo[17] > 0) this.difficulty = (demonTypes[levelInfo[43]] || "Hard") + " Demon" if (levelInfo[17] > 0) this.difficulty = (demonTypes[levelInfo[43]] || "Hard") + " Demon"
if (levelInfo[25] > 0) this.difficulty = 'Auto' 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) { if (this.password && this.password != 0) {
let xor = new XOR(); let xor = new XOR()
let pass = xor.decrypt(this.password, 26364); let pass = xor.decrypt(this.password, 26364)
if (pass.length > 1) this.password = pass.slice(1); this.password = pass.length > 1 ? pass.slice(1) : pass
else this.password = pass;
} }
if (server.onePointNine) { if (server.onePointNine) {
@ -83,9 +89,9 @@ class Level {
this.songSize = "0MB" this.songSize = "0MB"
this.songID = "Level " + this.officialSong this.songID = "Level " + this.officialSong
} }
return this 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 { class Player {
constructor(account) { 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 { 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))) } xor(str, key) {
encrypt(str, key = 37526) { return Buffer.from(this.xor(str, key)).toString('base64').replace(/./gs, c => ({'/': '_', '+': '-'}[c] || c)); } key = key.toString()
decrypt(str, key = 37526) { return this.xor(Buffer.from(str.replace(/./gs, c => ({'/': '_', '+': '-'}[c] || c)), 'base64').toString(), key) } 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"> <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%"> <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%"> <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="forms"></div>
<div id="typeLabel"> <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%"> <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%"> <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="types"></div>
<div id="gameLabel"> <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%"> <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%"> <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"> <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/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"> <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 id="searchBox" class="supercenter dragscroll" style="width: 128vh">
<div style="height: 4.5%"></div> <div style="height: 4.5%"></div>
</div> </div>
<div class="epicbox supercenter gs" style="width: 126vh; height: 80%; pointer-events: none"></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%"> <div class="center" style="position:absolute; top: 8%; left: 0%; right: 0%">
@ -79,10 +79,10 @@
</body> </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/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 type="text/javascript" src="./dragscroll.js"></script>
<script> <script>
"use strict";
let disabledFilters = {reward: [], type: [], game: []} let disabledFilters = {reward: [], type: [], game: []}
let forms = ["icon", "ship", "ball", "ufo", "wave", "robot", "spider"] 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"} 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>`) $('#searchBox').html(`<div style="height: 4.5%"></div>`)
achievements.forEach(x => { 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}"` 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")) { else if (x.rewardType.startsWith("color")) {
let col = colors[x.rewardID] 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%">`) $('#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 {achievements, colors} = ach
colors = ach.colors
append() append()
function label(labelName) { function label(labelName) {
@ -148,23 +147,23 @@ function label(labelName) {
$(labelFilter).click(function() { $(labelFilter).click(function() {
let filters = $(this).attr('filter').split(" ") let filters = $(this).attr('filter').split(" ")
let args
if (!$(this).hasClass('achDeselected')) { if (!$(this).hasClass('achDeselected')) {
$(this).addClass('achDeselected') $(this).addClass('achDeselected')
filters.forEach(x => disabledFilters[labelName].push(x)) filters.forEach(x => disabledFilters[labelName].push(x))
if (labelName == "reward") $(this).attr('src', $(this).attr('src').replace("_on", "_off")) args = ['_on', '_off']
} }
else { else {
$(this).removeClass('achDeselected') $(this).removeClass('achDeselected')
filters.forEach(x => disabledFilters[labelName] = disabledFilters[labelName].filter(y => y != x)) 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") const labels = ['reward', 'type', 'game']
label("type") labels.forEach(label)
label("game")
$(document).on('click', '.selectNone', function() { $(`.${$(this).attr('filter')}:not(.achDeselected)`).trigger('click'); $('#selectNone').hide(); $('#selectAll').show() }) $(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() }) $(document).on('click', '.selectAll', function() { $(`.${$(this).attr('filter')}.achDeselected`).trigger('click'); $('#selectNone').show(); $('#selectAll').hide() })
@ -173,23 +172,20 @@ $('#submitFilters').click(function() {
$('.popup').hide() $('.popup').hide()
if (!$('.rewardFilter:not(.achDeselected)').length) $('#rewardLabel .selectAll').trigger('click') labels.forEach(x => {
if (!$('.typeFilter:not(.achDeselected)').length) $('#typeLabel .selectAll').trigger('click') if (!$(`.${x}Filter:not(.achDeselected)`).length) $(`#${x}Label .selectAll`).trigger('click')
if (!$('.gameFilter:not(.achDeselected)').length) $('#gameLabel .selectAll').trigger('click') })
achievements = ach.achievements achievements = ach.achievements
.filter(x => !disabledFilters.reward.includes(x.rewardType)) .filter(x => !disabledFilters.reward.includes(x.rewardType))
.filter(x => !disabledFilters.type.some(y => x.id.startsWith(y))) .filter(x => !disabledFilters.type.some(y => x.id.startsWith(y)))
.filter(x => !disabledFilters.game.includes(x.game)) .filter(x => !disabledFilters.game.includes(x.game))
append() append()
}) })
$('#check').click(function() { $('#check').click(() => {
completed = !completed $('#check').attr('src', `../assets/check-o${completed = !completed ? 'n' : 'ff'}.png`)
if (completed) $('#check').attr('src', '../assets/check-on.png')
else $('#check').attr('src', '../assets/check-off.png')
append(false) append(false)
}) })
})) }))
</script> </script>

View file

@ -3,11 +3,12 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta property="og:type" content="website"> <meta property="og:type" content="website">
<meta id="meta-title" property="og:title" content="Level Analysis"> <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 id="meta-image" name="og:image" itemprop="image" content="https://gdbrowser.com/assets/cp.png">
<meta name="twitter:card" content="summary"> <meta name="twitter:card" content="summary">
<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> <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"> <link rel="icon" href="../assets/cp.png">
</head> </head>
@ -100,25 +101,27 @@
</div> </div>
</body> </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/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> <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;")} 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 disabledPortals = []
let altTriggerSort = false let altTriggerSort = false
let formPortals = ['cube', 'ship', 'ball', 'ufo', 'wave', 'robot', 'spider'] const PORTALS = {
let speedPortals = ['-1x', '1x', '2x', '3x', '4x'] form: ['cube', 'ship', 'ball', 'ufo', 'wave', 'robot', 'spider'],
let sizePortals = ['mini', 'big'] speed: ['-1x', '1x', '2x', '3x', '4x'],
let dualPortals = ['dual', 'single'] size: ['mini', 'big'],
let mirrorPortals = ['mirrorOn', 'mirrorOff'] 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) { if (!res.level) {
switch(res) { 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 -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 -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; 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 popupEsc = false
@ -129,8 +132,8 @@ fetch(`../api${window.location.pathname}`).then(res => res.json()).then(res => {
$('#objectCount').text(commafy(res.objects) + " objects") $('#objectCount').text(commafy(res.objects) + " objects")
document.title = "Analysis of " + res.level.name document.title = "Analysis of " + res.level.name
$('#meta-title').attr('content', "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-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 let hdPercent = (res.highDetail / res.objects) * 100
@ -216,7 +219,7 @@ function appendTriggerGroups() {
}) })
miscList.forEach(x => { 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>`) 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')) else disabledPortals.push($(this).attr('portal'))
portals = res.portals.split(", ").map(x => x.split(" ")) portals = res.portals.split(", ").map(x => x.split(" "))
if (disabledPortals.includes('form')) portals = portals.filter(x => !formPortals.includes(x[0])) for (let P in PORTALS)
if (disabledPortals.includes('speed')) portals = portals.filter(x => !speedPortals.includes(x[0])) if (disabledPortals.includes(P)) portals = portals.filter(x => !PORTALS[P].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]))
if (disabledPortals.includes('dupe')) { if (disabledPortals.includes('dupe')) {
portals.reverse().forEach((x, y) => {if (portals[y+1] && portals[y+1][0] == x[0]) portals[y][0] = null;}) 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.s = Number(hsv.s).toFixed(2)
hsv.v = Number(hsv.v).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(` $('#colorStuff').html(`
<h2 class="slightlySmaller">${isNaN(col.channel) ? col.channel : "Color " + col.channel}</h2> <h2 class="slightlySmaller">${isNaN(col.channel) ? col.channel : "Color " + col.channel}</h2>
<div class="colorSection"> <div class="colorSection">
@ -343,6 +344,5 @@ function appendTriggerGroups() {
$('#loadingDiv').hide() $('#loadingDiv').hide()
$('#analysisDiv').show() $('#analysisDiv').show()
}); })
</script> </script>

View file

@ -3,11 +3,11 @@
<head> <head>
<title>GD Level Browser API</title> <title>GD Level Browser API</title>
<meta charset="utf-8"> <meta charset="utf-8">
<link rel="icon" href="../assets/difficulties/auto.png"> <link rel="icon" href="/assets/difficulties/auto.png">
<link href="../assets/css/api.css" type="text/css" rel="stylesheet"> <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> <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"> <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-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-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"> <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 // smooth scrolling through anchors
document.querySelectorAll('a[href^="#"]').forEach(anchor => { document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function(e) { anchor.addEventListener('click', function(e) {
e.preventDefault(); e.preventDefault()
document.querySelector(this.getAttribute('href')).scrollIntoView({ document.querySelector(this.getAttribute('href')).scrollIntoView({
behavior: 'smooth' behavior: 'smooth'
}); });
}); });
}); });
// menu button // menu button
document.getElementById('menu-btn').onclick = function(){ 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'); document.getElementById('menu-btn').classList.toggle('active');
} }
for(let i = 0; i < document.getElementsByClassName('header-link').length; i++){ for(let i = 0; i < document.getElementsByClassName('header-link').length; i++){
document.getElementsByClassName('header-link')[i].onclick = function(){ 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'); document.getElementById('menu-btn').classList.toggle('active');
} }
} }

View file

@ -3,11 +3,11 @@
<head> <head>
<title>GD Level Browser API</title> <title>GD Level Browser API</title>
<meta charset="utf-8"> <meta charset="utf-8">
<link rel="icon" href="../assets/difficulties/auto.png"> <link rel="icon" href="/assets/difficulties/auto.png">
<link href="../assets/css/api.css" type="text/css" rel="stylesheet"> <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> <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"> <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-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-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"> <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-wrap">
<div class="main-header"> <div class="main-header">
<div class="header-drawer" id="menu-btn"> <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>
<div class="header-links hid"> <div class="header-links hid">
<a class="header-link" href="#intro">Introduction</a> <a class="header-link" href="#intro">Introduction</a>
@ -37,24 +37,24 @@
<div class="category-content"> <div class="category-content">
<a class="header-link" href="#profile">Profiles</a> <a class="header-link" href="#profile">Profiles</a>
<a class="header-link" href="#leaderboard">Leaderboards</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> </div>
<div class="header-category"> <div class="header-category">
<div class="category-name">Account</div> <div class="category-name">Account</div>
<div class="category-content"> <div class="category-content">
<a class="header-link" href="#commenting">Commenting</a> <a class="header-link" href="#commenting">Commenting</a>
<a class="header-link" href="#profileposting">Profile Posting</a> <a class="header-link" href="#profileposting">Profile Posting</a>
<a class="header-link" href="#liking">Liking</a> <a class="header-link" href="#liking">Liking</a>
<a class="header-link" href="#messages">Messages</a> <a class="header-link" href="#messages">Messages</a>
</div> </div>
</div> </div>
<div class="header-category"> <div class="header-category">
<div class="category-name">Misc</div> <div class="category-name">Misc</div>
<div class="category-content"> <div class="category-content">
<a class="header-link" href="#analyze">Level Analysis</a> <a class="header-link" href="#analyze">Level Analysis</a>
<a class="header-link" href="#artist">Song Verification</a> <a class="header-link" href="#artist">Song Verification</a>
<a class="header-link" href="#icons">Icons</a> <a class="header-link" href="#icons">Icons</a>
</div> </div>
</div> </div>
</div> </div>
@ -186,7 +186,7 @@
<main> <main>
<div id="profile" class="anchor"></div> <div id="profile" class="anchor"></div>
<div class="main-block"> <div class="main-block">
<h1>Profiles</h1> <h1>Profiles</h1>
<p>/api/profile/username-or-id</p> <p>/api/profile/username-or-id</p>
@ -244,7 +244,7 @@
<main> <main>
<div id="search" class="anchor"></div> <div id="search" class="anchor"></div>
<div class="main-block"> <div class="main-block">
<h1>Searching</h1> <h1>Searching</h1>
<p>/api/search/search-query</p> <p>/api/search/search-query</p>
@ -540,10 +540,10 @@
<div class="main-block"> <div class="main-block">
<h1>Commenting (usually broken)</h1> <h1>Commenting (usually broken)</h1>
<p>POST: /postComment</p> <p>POST: /postComment</p>
<p>Leaves a comment on a level. This one is a POST request!</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> <p>*Commenting has a rate limit of 15 seconds</p>
<br> <br>
<p class="reveal" onclick="revealSection('#params-commenting')"><b>Parameters (6)</b></p> <p class="reveal" onclick="revealSection('#params-commenting')"><b>Parameters (6)</b></p>
<div class="subdiv" id="params-commenting"> <div class="subdiv" id="params-commenting">
@ -555,7 +555,7 @@
<p>percent: The percent shown on the comment (optional)</p> <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> <p>color: If the comment should have a special pink color on GDBrowser (optional)</p>
</div> </div>
<br> <br>
<p class="reveal" onclick="revealSection('#request-commenting')"><b>Example</b></p> <p class="reveal" onclick="revealSection('#request-commenting')"><b>Example</b></p>
@ -571,7 +571,7 @@
<br> <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> <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> </div>
<div class="seperator"></div> <div class="seperator"></div>
</main> </main>
@ -581,9 +581,9 @@
<div class="main-block"> <div class="main-block">
<h1>Profile Posting (usually broken)</h1> <h1>Profile Posting (usually broken)</h1>
<p>POST: /postProfileComment</p> <p>POST: /postProfileComment</p>
<p>Leaves a profile post. This one is a POST request!</p> <p>Leaves a profile post. This one is a POST request!</p>
<br> <br>
<p class="reveal" onclick="revealSection('#params-profileposting')"><b>Parameters (5)</b></p> <p class="reveal" onclick="revealSection('#params-profileposting')"><b>Parameters (5)</b></p>
<div class="subdiv" id="params-profileposting"> <div class="subdiv" id="params-profileposting">
@ -593,7 +593,7 @@
<p>password: Your password (as plain text)</p> <p>password: Your password (as plain text)</p>
<p>color: If the comment should have a special pink color on GDBrowser (optional)</p> <p>color: If the comment should have a special pink color on GDBrowser (optional)</p>
</div> </div>
<br> <br>
<p class="reveal" onclick="revealSection('#request-profileposting')"><b>Example</b></p> <p class="reveal" onclick="revealSection('#request-profileposting')"><b>Example</b></p>
@ -607,7 +607,7 @@
<br> <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> <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> </div>
<div class="seperator"></div> <div class="seperator"></div>
</main> </main>
@ -618,9 +618,9 @@
<div class="main-block"> <div class="main-block">
<h1>Liking (usually broken)</h1> <h1>Liking (usually broken)</h1>
<p>POST: /like</p> <p>POST: /like</p>
<p>Likes/dislikes level, comment, or post. This one is a POST request!</p> <p>Likes/dislikes level, comment, or post. This one is a POST request!</p>
<br> <br>
<p class="reveal" onclick="revealSection('#params-liking')"><b>Parameters (6)</b></p> <p class="reveal" onclick="revealSection('#params-liking')"><b>Parameters (6)</b></p>
<div class="subdiv" id="params-liking"> <div class="subdiv" id="params-liking">
@ -631,7 +631,7 @@
<p>accountID: Your account ID</p> <p>accountID: Your account ID</p>
<p>password: Your password (as plain text)</p> <p>password: Your password (as plain text)</p>
</div> </div>
<br> <br>
<p class="reveal" onclick="revealSection('#request-liking')"><b>Example</b></p> <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> <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> 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> </div>
<div class="seperator"></div> <div class="seperator"></div>
</main> </main>
@ -665,7 +665,7 @@
<b>/sendMessage</b> (sends a message)</p> <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> <p>I decided to put all 4 of these requests in one section because they're fairly similar ¯\_(ツ)_/¯</p>
<br> <br>
<p class="reveal" onclick="revealSection('#params-msg')"><b>Parameters</b></p> <p class="reveal" onclick="revealSection('#params-msg')"><b>Parameters</b></p>
<div class="subdiv" id="params-msg"> <div class="subdiv" id="params-msg">
@ -687,7 +687,7 @@
<p>message: The content of the message, max 300 characters</p> <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> <p>color: If the message should have a special pink color on GDBrowser (optional)</p>
</div> </div>
<br> <br>
<p class="reveal" onclick="revealSection('#request-msg')"><b>Example</b></p> <p class="reveal" onclick="revealSection('#request-msg')"><b>Example</b></p>
@ -699,7 +699,7 @@
&accountID=106255<br> &accountID=106255<br>
&password=KitsuneColon333<br> &password=KitsuneColon333<br>
<br> <br>
<p>Read message with ID of 177013:</p> <p>Read message with ID of 177013:</p>
<p>POST /messages/177013<br> <p>POST /messages/177013<br>
?accountID=106255<br> ?accountID=106255<br>
@ -712,7 +712,7 @@
&id=177013<br> &id=177013<br>
&password=KitsuneColon333<br> &password=KitsuneColon333<br>
<br> <br>
<p>Send "Hello!" to Tubular9:</p> <p>Send "Hello!" to Tubular9:</p>
<p>POST /sendMessage<br> <p>POST /sendMessage<br>
?accountID=106255<br> ?accountID=106255<br>
@ -877,22 +877,22 @@
// smooth scrolling through anchors // smooth scrolling through anchors
document.querySelectorAll('a[href^="#"]').forEach(anchor => { document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function(e) { anchor.addEventListener('click', function(e) {
e.preventDefault(); e.preventDefault()
document.querySelector(this.getAttribute('href')).scrollIntoView({ document.querySelector(this.getAttribute('href')).scrollIntoView({
behavior: 'smooth' behavior: 'smooth'
}); });
}); });
}); });
// menu button // menu button
document.getElementById('menu-btn').onclick = function(){ 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'); document.getElementById('menu-btn').classList.toggle('active');
} }
for(let i = 0; i < document.getElementsByClassName('header-link').length; i++){ for(let i = 0; i < document.getElementsByClassName('header-link').length; i++){
document.getElementsByClassName('header-link')[i].onclick = function(){ 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'); document.getElementById('menu-btn').classList.toggle('active');
} }
} }

View file

@ -3,13 +3,13 @@
<meta charset="utf-8"> <meta charset="utf-8">
<script async src="https://www.googletagmanager.com/gtag/js?id=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> <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 name="robots" content="noindex">
<meta id="meta-desc" property="og:description" content='i only upload high quality geometry dash asset rips'> <meta id="meta-desc" property="og:description" content='i only upload high quality geometry dash asset rips'>
<style> <style>
body { body {
background-image: linear-gradient(#2b2b2b, #171717) !important; background-image: linear-gradient(#2b2b2b, #171717) !important
margin-left: 12px; margin-left: 12px;
} }
@ -21,16 +21,16 @@
} }
h1, p { h1, p {
color: white; color: white
font-family: aller, helvetica, arial; font-family: aller, helvetica, arial
font-weight: normal; font-weight: normal;
} }
.name { .name {
width: 300px; width: 300px
display: inline-block; display: inline-block
margin: 0 5 4 5; margin: 0 5 4 5
font-family: aller, helvetica, arial; font-family: aller, helvetica, arial
font-size: 20px; font-size: 20px;
} }
</style> </style>

View file

@ -1,10 +1,10 @@
<head> <head>
<title>Boomlings Leaderboard</title> <title>Boomlings Leaderboard</title>
<meta charset="utf-8"> <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 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>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-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-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"> <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 id="borderbox" class="supercenter dragscroll"></div>
<div class="supercenter" id="loading" style="height: 10%; top: 47%; display: none;"> <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>
<div style="position:absolute; top: 2%; left: 1.5%; width: 10%; height: 25%; pointer-events: none"> <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>
<div style="position:absolute; bottom: 0%; left: 0%; width: 100%"> <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>
<div style="position:absolute; bottom: 0%; right: 0%; width: 100%; text-align: right;"> <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>
</div> </div>
@ -42,14 +42,14 @@
</body> </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/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="//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="/misc/global.js?v=1"></script>
<script type="text/javascript" src="../dragscroll.js"></script> <script type="text/javascript" src="/misc/dragscroll.js"></script>
<script> <script>
$('#boomerbox').html('') $('#boomerbox').html('')
$('#loading').show() $('#loading').show()
fetch(`../api/boomlings`).then(res => res.json()).then(res => { fetch(`/api/boomlings`).then(res => res.json()).then(res => {
if (res == "0") res = [] if (res == "0") res = []
$('#boomerbox').html('') $('#boomerbox').html('')
@ -59,19 +59,19 @@
$('#boomerbox').append(`<div class="inline leaderboardSlot"> $('#boomerbox').append(`<div class="inline leaderboardSlot">
<div class="flex" style="width: 7vh; height: 100%"><h1 class="rankNumber" style="width: inherit">${x.rank}</h1></div> <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> <p class="flex username" style="width: 48%">${x.name}</p>
<div class="flex" style="width: 22%"> <div class="flex" style="width: 22%">
<h1 class="level">Level ${x.level}<br><span class="score">${x.score}</span> <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[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[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[2]}.png">
</h1> </h1>
</div> </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>`) </div>`)
}) })

View file

@ -1,19 +1,19 @@
<head> <head>
<title>Coming Soon...</title> <title>Coming Soon...</title>
<meta charset="utf-8"> <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">
<link rel="icon" href="../assets/coin.png"> <link rel="icon" href="/assets/coin.png">
</head> </head>
<body class="levelBG"> <body class="levelBG">
<div class="supercenter" style="height: 70%;"> <div class="supercenter" style="height: 70%;">
<h1>Coming soon!</h1> <h1>Coming soon!</h1>
<br> <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>
<br> <br>
<div class="gdbutton"> <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>
</div> </div>
</body> </body>

View file

@ -1,9 +1,9 @@
<head> <head>
<title>Comments</title> <title>Comments</title>
<meta charset="utf-8"> <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> <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-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-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"> <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%"> <div style="min-height: 16%; max-height: 16%">
<p id="message" style="padding: 0% 10%; margin-top: 1.5%"></p> <p id="message" style="padding: 0% 10%; margin-top: 1.5%"></p>
</div> </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-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-submit.png" type="submit" height=10%; class="postButton gdButton center" style="margin-left: 1%" id="submitComment">
</div> </div>
</div> </div>
<div class="popup" id="likeComment"> <div class="popup" id="likeComment">
<div class="brownbox bounce center supercenter" style="height: 75%; width: 100vh"> <div class="brownbox bounce center supercenter" style="height: 75%; width: 100vh">
<h1 class="smaller center" style="font-size: 5.5vh">Vote</h1> <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/smashLike.png" id="likebtn" class="inline gdButton likeButton"><!--
--><img src="../assets/smashDislike.png" id="dislikebtn" class="inline gdButton likeButton youAreNotTheOne"> --><img src="/assets/smashDislike.png" id="dislikebtn" class="inline gdButton likeButton youAreNotTheOne">
<form action="nothing lol"> <form action="nothing lol">
<h3 class="center">GD Username</h3> <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%"> <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%"> <div style="min-height: 18%; max-height: 18%">
<p id="likeMessage" style="padding: 0% 10%; margin-top: 2.5%"></p> <p id="likeMessage" style="padding: 0% 10%; margin-top: 2.5%"></p>
</div> </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-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-submit.png" type="submit" height=10%; class="postButton gdButton center" style="margin-left: 1%" id="submitVote">
</div> </div>
</div> </div>
<div style="position:absolute; bottom: 0%; left: 0%; width: 100%"> <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>
<div style="position:absolute; bottom: 0%; right: 0%; width: 100%; text-align: right;"> <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>
<div style="text-align: right; position:absolute; top: 1%; right: 1.25%"> <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="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%"> <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>
<div class="supercenter" id="firstPage" style="left: 97%; top: 27.5%; height: 8.5%; display: none;"> <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>
<div class="supercenter" id="refreshButton" style="left: 97%; top: 27.5%; height: 10%"> <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>
<div class="supercenter" style="left: 3%; top: 15%; height: 10%"> <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>
<div class="supercenter" style="left: 3%; top: 25.5%; height: 10%"> <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>
<div class="supercenter" style="left: 3%; top: 36%; height: 10%"> <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>
<div class="supercenter" style="left: 3%; top: 63%; height: 11%"> <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>
<div class="supercenter" id="pageDown" style="left: 3%; top: 50%; height: 10%; display: none;"> <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>
<div class="supercenter" id="pageUp" style="left: 97%; top: 50%; height: 10%;"> <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>
<div class="supercenter" style="top: 4.8%; height: 10%; width: 100%;"> <div class="supercenter" style="top: 4.8%; height: 10%; width: 100%;">
@ -114,15 +114,15 @@
<h2 class="smallGold inline" id="levelVersion"></h2> <h2 class="smallGold inline" id="levelVersion"></h2>
<a id="levelLink"><h2 class="smallGold inline gdButton" id="levelID" style="margin-left: 6vh"></h2></a> <a id="levelLink"><h2 class="smallGold inline gdButton" id="levelID" style="margin-left: 6vh"></h2></a>
</div> </div>
<div id="leaveComment" style="display: none; position:absolute;bottom: 0%;right: 0%;width: 14%;text-align: right;transform: translate(30%, 40%);"> <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> </div>
<div style="position:absolute; top: 2%; left: 1.5%; width: 10%; height: 25%; pointer-events: none"> <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>
</div> </div>
@ -130,9 +130,9 @@
</body> </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/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="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="/iconkit/icon.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 type="text/javascript" src="/misc/dragscroll.js"></script>
<script> <script>
let {mode, compact} = JSON.parse(localStorage.getItem('commentPreset') || '{"mode": "top", "compact": true}') let {mode, compact} = JSON.parse(localStorage.getItem('commentPreset') || '{"mode": "top", "compact": true}')
@ -150,13 +150,13 @@ let auto = false
let interval = null let interval = null
let commentCache = {} 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 (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) else lvlID = Math.round(+lvlID)
if (mode == "top") { mode = "top"; $('#topSort').attr('src', "../assets/sort-likes-on.png") } if (mode == "top") { mode = "top"; $('#topSort').attr('src', "/assets/sort-likes-on.png") }
else $('#timeSort').attr('src', "../assets/sort-time-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;")} 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() if (!lvl.name) $('#leaveComment').hide()
$('#levelAuthor').text("By " + (lvl.author || "-")) $('#levelAuthor').text("By " + (lvl.author || "-"))
$('#levelID').text("ID: " + lvlID) $('#levelID').text("ID: " + lvlID)
if (lvl.accountID && lvl.author != "-") if (lvl.accountID && lvl.author != "-")
if (lvl.id) $('#levelLink').attr('href', '../' + lvl.id) if (lvl.id) $('#levelLink').attr('href', '../' + lvl.id)
else $('#levelID').removeClass("gdButton") else $('#levelID').removeClass("gdButton")
$('#levelVersion').text("Version: " + (lvl.version || 0)) $('#levelVersion').text("Version: " + (lvl.version || 0))
@ -211,10 +211,10 @@ function buildComments(lvl) {
function appendComments(auto, noCache) { function appendComments(auto, noCache) {
if (loadingComments) return; if (loadingComments) return
else loadingComments = true; 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) { if (page == 0) {
$('#pageDown').hide() $('#pageDown').hide()
@ -230,13 +230,12 @@ else {
} }
if (!noCache && commentCache[page]) addComments(commentCache[page]) 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) => { fetch(
if (res.status === 204) return []; `/api${!history ? window.location.pathname : "/comments/" + lvl.playerID}?count=${compact && !auto ? 20 : 10}&page=${page}${history ? "&type=commentHistory" : ""}&${mode}`
return res.json(); ).then(res => res.status === 204 ? [] : res.json()).then(addComments)
}).then(addComments)
function addComments(res) { function addComments(res) {
if (history && lvl.commentHistory != "all") $('#pageUp').hide() if (history && lvl.commentHistory != "all") $('#pageUp').hide()
if (res == -1 || (history && lvl.commentHistory != "all")) { 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> <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}.`}> <a href=../${x.accountID == "0" ? `search/${x.playerID}?user` : `../u/${x.accountID}.`}>
<h2 class="inline gdButton ${x.accountID == "0" ? "green unregistered" : ""}">${userName}</h2></a> <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> <p class="commentPercent inline">${x.percent ? x.percent + "%" : ""}</p>
</div> </div>
@ -287,14 +286,14 @@ function addComments(res) {
<div class="commentLikes"> <div class="commentLikes">
${history ? `<h3 style="margin-right: 1.5vh; pointer-events: all;" class="gold inline"><a href="../${x.levelID}">(${x.levelID})</a></h3>` : ""} ${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}"` : ""}"> <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> </div>
<h3 id="likes-${x.ID}" class="inline">${x.likes}</h3> <h3 id="likes-${x.ID}" class="inline">${x.likes}</h3>
</div> </div>
</div>` </div>`
////// COMPACT MODE ////// ////// COMPACT MODE //////
: ` : `
<div class="commentBG compactBG ${bgCol}"> <div class="commentBG compactBG ${bgCol}">
<div class="comment compact" commentID="${x.ID}"> <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> <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}.`}> <a href=../${x.accountID == "0" ? `search/${x.playerID}?user` : `../u/${x.accountID}.`}>
<h2 class="inline gdButton ${x.accountID == "0" ? "green unregistered" : ""}">${userName}</h2></a> <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> <p class="commentPercent inline">${x.percent ? x.percent + "%" : ""}</p>
</div> </div>
@ -314,7 +313,7 @@ function addComments(res) {
<div class="commentLikes"> <div class="commentLikes">
${history ? `<h3 style="margin-right: 0.5vh; pointer-events: all;" class="gold inline"><a href="../${x.levelID}">(${x.levelID})</a></h3>` : ""} ${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}"` : ""}> <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> </div>
<h3 id="likes-${x.ID}" class="inline">${x.likes}</h3> <h3 id="likes-${x.ID}" class="inline">${x.likes}</h3>
</div> </div>
@ -331,7 +330,7 @@ function addComments(res) {
$(this).css('font-size', (3.5 - (overflow)) + 'vh') $(this).css('font-size', (3.5 - (overflow)) + 'vh')
} }
}); })
renderIcons() renderIcons()
$('#loading').hide() $('#loading').hide()
@ -356,39 +355,39 @@ function resetSort() {
} }
$('#topSort').click(function() { $('#topSort').click(function() {
if (mode == "top" || loadingComments) return; if (mode == "top" || loadingComments) return
resetSort() resetSort()
mode = "top"; mode = "top";
$('#timeSort').attr('src', "../assets/sort-time.png") $('#timeSort').attr('src', "/assets/sort-time.png")
$('#topSort').attr('src', "../assets/sort-likes-on.png") $('#topSort').attr('src', "/assets/sort-likes-on.png")
appendComments() appendComments()
}) })
$('#timeSort').click(function() { $('#timeSort').click(function() {
if (mode == "time" || loadingComments) return; if (mode == "time" || loadingComments) return
resetSort() resetSort()
mode = "time"; mode = "time";
$('#timeSort').attr('src', "../assets/sort-time-on.png") $('#timeSort').attr('src', "/assets/sort-time-on.png")
$('#topSort').attr('src', "../assets/sort-likes.png") $('#topSort').attr('src', "/assets/sort-likes.png")
appendComments() appendComments()
}) })
$('#compactMode').click(function() { $('#compactMode').click(function() {
if (loadingComments) return; if (loadingComments) return
compact = !compact compact = !compact
lastPage = 0; castPage = 0;
page = 0; page = 0;
$('#compactMode').attr('src', `../assets/compact-${compact ? "on" : "off"}.png`) $('#compactMode').attr('src', `../assets/compact-${compact ? "on" : "off"}.png`)
appendComments() appendComments()
}) })
$('#autoMode').click(function() { $('#autoMode').click(function() {
if (loadingComments) return; if (loadingComments) return
auto = !auto auto = !auto
mode = "time" mode = "time"
page = 0; page = 0;
$('#timeSort').attr('src', "../assets/sort-time-on.png") $('#timeSort').attr('src', "/assets/sort-time-on.png")
$('#topSort').attr('src', "../assets/sort-likes.png") $('#topSort').attr('src', "/assets/sort-likes.png")
if (auto) { if (auto) {
document.title = "[LIVE] " + document.title document.title = "[LIVE] " + document.title
@ -407,8 +406,8 @@ $('#autoMode').click(function() {
$(document).on('click', '.refreshBtn', function () { $(document).on('click', '.refreshBtn', function () {
if (loadingComments) return if (loadingComments) return
lastPage = 0; lastPage = 0
page = 0; page = 0
appendComments(false, true) appendComments(false, true)
}) })
@ -430,7 +429,7 @@ $('#submitComment').click(function() {
$('.postbutton').hide() $('.postbutton').hide()
allowEsc = false 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!")} if (!res || res == "-1") {allowEsc = true; $('.postbutton').show(); return $('#message').text("The username you provided doesn't exist!")}
else accountID = res.accountID else accountID = res.accountID
@ -440,13 +439,13 @@ $('#submitComment').click(function() {
$('#postComment').hide() $('#postComment').hide()
$('.postbutton').show() $('.postbutton').show()
$('#message').html(messageText) $('#message').html(messageText)
$('#timeSort').attr('src', "../assets/sort-time-on.png") $('#timeSort').attr('src', "/assets/sort-time-on.png")
$('#topSort').attr('src', "../assets/sort-likes.png") $('#topSort').attr('src', "/assets/sort-likes.png")
allowEsc = true allowEsc = true
mode = "time" mode = "time"
page = 0 page = 0
appendComments() appendComments()
}) })
.fail(e => {allowEsc = true; $('.postbutton').show();$('#message').text(e.responseText.includes("DOCTYPE") ? "Something went wrong..." : e.responseText)}) .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 commentID = 0
let lvID = 0 let lvID = 0
let likeCount, likeImg; let likeCount, likeImg
let likedComments; let likedComments;
$(document).on('click', '.likeComment', function(cmnt) { $(document).on('click', '.likeComment', function(cmnt) {
@ -473,7 +472,7 @@ $(document).on('click', '.likeComment', function(cmnt) {
commentID = $(this).attr('commentID') commentID = $(this).attr('commentID')
likedComments = localStorage.likedComments ? JSON.parse(localStorage.likedComments) : [] likedComments = localStorage.likedComments ? JSON.parse(localStorage.likedComments) : []
if (likedComments.includes(commentID)) return; if (likedComments.includes(commentID)) return
lvID = $(this).attr('levelID') || 0 lvID = $(this).attr('levelID') || 0
likeImg = $(this).find('img') likeImg = $(this).find('img')
@ -483,7 +482,7 @@ $(document).on('click', '.likeComment', function(cmnt) {
$('#submitVote').click(function() { $('#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 ID = commentID
let username = $('#like-username').val() let username = $('#like-username').val()
@ -491,14 +490,14 @@ $('#submitVote').click(function() {
let extraID = lvID || window.location.pathname.split('/')[2] let extraID = lvID || window.location.pathname.split('/')[2]
let accountID = 0 let accountID = 0
let likeType = like ? "1" : "0" let likeType = like ? "1" : "0"
if (!ID || !username || !password || loadingComments) return $('#postComment').hide() if (!ID || !username || !password || loadingComments) return $('#postComment').hide()
$('#likeMessage').text(like ? "Liking..." : "Disliking... :(") $('#likeMessage').text(like ? "Liking..." : "Disliking... :(")
$('.postbutton').hide() $('.postbutton').hide()
allowEsc = false 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!")} if (!res || res == "-1") {allowEsc = true; $('.postbutton').show(); return $('#likeMessage').text("The username you provided doesn't exist!")}
else accountID = res.accountID else accountID = res.accountID
@ -506,8 +505,8 @@ $('#submitVote').click(function() {
.done(x => { .done(x => {
let newCount = parseInt(likeCount.text()) + (like ? 1 : -1) let newCount = parseInt(likeCount.text()) + (like ? 1 : -1)
likeCount.text(newCount) likeCount.text(newCount)
if (newCount < 0) likeImg.attr('src', '../assets/dislike.png').css('transform', compact ? 'translateY(15%)' : 'translateY(25%)') 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') else likeImg.attr('src', '/assets/like.png').removeAttr('style')
$('#likeComment').hide() $('#likeComment').hide()
$('#likebtn').trigger('click') $('#likebtn').trigger('click')
$('.postbutton').show() $('.postbutton').show()
@ -515,7 +514,7 @@ $('#submitVote').click(function() {
allowEsc = true allowEsc = true
likedComments.push(commentID) likedComments.push(commentID)
localStorage.setItem('likedComments', JSON.stringify(likedComments)) localStorage.setItem('likedComments', JSON.stringify(likedComments))
}) })
.fail(e => {allowEsc = true; $('.postbutton').show();$('#likeMessage').text(e.responseText.includes("DOCTYPE") ? "Something went wrong..." : e.responseText)}) .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 (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 if (k.which == 37 && $('#pageDown').is(":visible")) { //left
$('#pageDown').trigger('click') $('#pageDown').trigger('click')
} }
if (k.which == 39 && $('#pageUp').is(":visible")) { //right if (k.which == 39 && $('#pageUp').is(":visible")) { //right
$('#pageUp').trigger('click') $('#pageUp').trigger('click')
} }
@ -548,4 +547,3 @@ $(document).keydown(function(k) {
}; };
</script> </script>

View file

@ -1,9 +1,9 @@
<head> <head>
<title>Demon Leaderboard</title> <title>Demon Leaderboard</title>
<meta charset="utf-8"> <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> <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-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-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"> <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"> <p class="bigger center" style="margin-top: 1vh">
Usernames may <cy>differ</cy> from what is used in GD Usernames may <cy>differ</cy> from what is used in GD
</p> </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> </div>
<div style="position:absolute; bottom: 0%; left: 0%; width: 100%"> <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>
<div style="position:absolute; bottom: 0%; right: 0%; width: 100%; text-align: right;"> <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>
<div id="searchBox" class="supercenter dragscroll"; style="width: 124vh"> <div id="searchBox" class="supercenter dragscroll"; style="width: 124vh">
<div style="height: 4.5%"></div> <div style="height: 4.5%"></div>
</div> </div>
<div class="epicbox supercenter gs" style="width: 126vh; height: 80%; pointer-events: none"></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%"> <div class="center" style="position:absolute; top: 8%; left: 0%; right: 0%">
@ -47,35 +47,35 @@
</div> </div>
<div style="position: absolute; left: 7%; top: 45%; height: 10%;"> <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>
<div style="position: absolute; right: 7%; top: 45%; height: 10%;"> <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>
<div style="position:absolute; top: 2%; left: 1.5%; width: 10%; height: 25%; pointer-events: none"> <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>
<div class="supercenter" id="loading" style="height: 10%; top: 47%"> <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>
<div style="position:absolute; top: 3%; right: 2%; text-align: right; width: 20%;"> <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> <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()"> <img id="creditsButton" class="inline gdButton" src="/assets/credits.png" width="25%" onclick="$('#infoDiv').show()">
</div> </div>
<div style="position:absolute; bottom: 5.5%; right: 3.5%; text-align: right; width: 18%;"> <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>
</div> </div>
</body> </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/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 type="text/javascript" src="../dragscroll.js"></script>
<script> <script>
@ -83,7 +83,7 @@ let max = 250
let trophies = [1, 5, 10, 25, 50, 100, max] let trophies = [1, 5, 10, 25, 50, 100, max]
let demonID = Math.round(window.location.pathname.split('/')[2]) 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}`) if (demonID > 1) $('#pageDown').attr('href', `./${demonID - 1}`)
else $('#pageDown').hide() else $('#pageDown').hide()
@ -91,8 +91,8 @@ else $('#pageDown').hide()
if (demonID < max) $('#pageUp').attr('href', `./${demonID + 1}`) if (demonID < max) $('#pageUp').attr('href', `./${demonID + 1}`)
else $('#pageUp').hide() else $('#pageUp').hide()
Fetch(`../api/gdps?current=1`).then(server => { Fetch(`/api/gdps?current=1`).then(server => {
if (illegal || !server.demonList) return $('#loading').hide(); 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 => { 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" if (x.video && x.video.includes("youtube.com")) videoIcon = "youtube"
else if (x.video && x.video.includes("twitch.tv")) videoIcon = "twitch" 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> <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> <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> <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%;"> <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}"> <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> </a>
</div> </div>

View file

@ -1,9 +1,9 @@
<head> <head>
<title>Level Search</title> <title>Level Search</title>
<meta charset="utf-8"> <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> <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-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-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"> <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="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%"> <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> <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-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> <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><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> <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> <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="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="customSong" class="gdButton inline" style="margin-left: 0.5%" src="/assets/song-custom.png" height="8%">
<br> <br>
<input id="songID" type="number" placeholder="Custom Song ID" style="height: 15%; width: 70%; text-align: center; margin-top: 2%"> <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"> <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> <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>
</div> </div>
<div id="customlist" class="popup"> <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%"> <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> <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> <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> <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;"> <input id="pageSize" type="number" value="10" style="font-size: 4vh; height: 13%; width: 45%; text-align: center;">
</div> </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> </div>
<div style="position:absolute; bottom: 0%; left: 0%; width: 100%"> <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>
<div style="position:absolute; bottom: 0%; right: 0%; width: 100%; text-align: right;"> <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>
<div class="transparentBox center" style="width: 115vh; height: 9%; margin: 1.5% auto 1% auto; padding-bottom: 0.2%"> <div class="transparentBox center" style="width: 115vh; height: 9%; margin: 1.5% auto 1% auto; padding-bottom: 0.2%">
<div> <div>
<input type="text" id="levelName" placeholder="Enter a level, user, or ID" maxlength=20> <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 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 id="userSearch" src="/assets/search-user.png" width="9.6%" class="valign gdButton" style="margin-left: 1%; margin-bottom: 2.2%">
</div> </div>
</div> </div>
@ -79,17 +79,17 @@
</div> </div>
<div class="transparentBox center" style="width: 115vh; height: 35%; margin: 0.5% auto 1% auto; padding-top: 1.1%"> <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> <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> <br>
<img src="../assets/btn-trending.png" height="27%" class="valign gdButton spaced levelSearch" search="trending"> <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-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-magic.png" height="27%" class="valign gdButton spaced levelSearch" search="magic">
<br> <br>
<img src="../assets/btn-awarded.png" height="27%" class="valign gdButton levelSearch" search="awarded"> <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-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-followed.png" height="27%" id="followedSearch" class="valign gdButton levelSearch" search="followed">
</div> </div>
<div class="center"> <div class="center">
@ -97,56 +97,56 @@
</div> </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 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/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=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=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=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=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=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> --> <!-- <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> --> <!-- <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>
<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 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=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=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=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=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 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> <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> <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>
<div class="transparentBox center" style="width: 115vh; height: 6%; margin: 0.5% auto 1% auto; padding-top: 1%; padding-bottom: 0.5%;"> <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=0><h1 class="gdButton smaller">Tiny</h1></div>
<div class="lengthDiv" len=1><h1 class="gdButton smaller">Short</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=2><h1 class="gdButton smaller">Medium</h1></div>
<div class="lengthDiv" len=3><h1 class="gdButton smaller">Long</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" 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>
<div style="position:absolute; top: 2%; left: 1.5%; width: 10%; height: 25%; pointer-events: none"> <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>
<div style="position:absolute; top: 2%; right: 1.5%; width: 10%; text-align: right"> <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%" 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" 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" src="/assets/listbutton.png" width="60%" onclick="$('#customlist').show()">
</div> </div>
</div> </div>
</body> </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/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> <script>
let filters = [] let filters = []
@ -174,7 +174,7 @@ $('.levelSearch').click(function() {
let difficulties = [] let difficulties = []
$('.diffDiv').each(function() {if ($(this).hasClass('selectedFilter')) difficulties.push($(this).attr('diff'))}) $('.diffDiv').each(function() {if ($(this).hasClass('selectedFilter')) difficulties.push($(this).attr('diff'))})
demonFilter = demonMode && difficulties[0] > 0 demonFilter = demonMode && difficulties[0] > 0
if (!difficulties.length) url += "" if (!difficulties.length) url += ""
else if (!demonFilter) url += "&diff=" + undupe(difficulties).join(",") else if (!demonFilter) url += "&diff=" + undupe(difficulties).join(",")
else url += "&diff=-2&demonFilter=" + difficulties[0] else url += "&diff=-2&demonFilter=" + difficulties[0]
@ -212,15 +212,15 @@ function getDiffFilters() {
} }
function showDemonDiffs() { function showDemonDiffs() {
$('#difficulties').hide(); $('#difficulties').hide()
$('#demons').show(); $('#demons').show()
demonMode = true; demonMode = true
} }
function hideDemonDiffs() { function hideDemonDiffs() {
$('#difficulties').show(); $('#difficulties').show()
$('#demons').hide(); $('#demons').hide()
demonMode = false; demonMode = false
} }
$('.diffDiv').click(function() { $('.diffDiv').click(function() {
@ -262,16 +262,16 @@ $('.lengthDiv').click(function() {
$(document).keydown(function(k) { $(document).keydown(function(k) {
let searchFocus = $(':focus-visible').length == 1 && $(':focus-visible').first().attr('id') == "levelName" let searchFocus = $(':focus-visible').length == 1 && $(':focus-visible').first().attr('id') == "levelName"
if ((!$(':focus-visible').length || searchFocus) && k.which == 13) { // enter 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') else if ($('#filters').is(':hidden')) $('#searchBtn').trigger('click')
} }
}); });
$('#pageSize').on('input blur', function (event) { $('#pageSize').on('input blur', function (event) {
var x = +$(this).val(); var max = 250; var min = 1 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')} if (event.type == "input") { if (x > max || x < min) $(this).addClass('red'); else $(this).removeClass('red')}
else { else {
$(this).val(Math.max(Math.min(Math.floor(x), max), min)); $(this).val(clamp(Math.floor(x), min, max))
$(this).removeClass('red') $(this).removeClass('red')
} }
$('#listLevels').trigger('input') $('#listLevels').trigger('input')
@ -279,7 +279,7 @@ $('#pageSize').on('input blur', function (event) {
let listMsg = $('#listInfo').html() let listMsg = $('#listInfo').html()
$('#listLevels, #listName').on('input blur', function (event) { $('#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) levels = undupe(levels)
if (levels.length > 1 && levels.length <= 100) { if (levels.length > 1 && levels.length <= 100) {
@ -376,11 +376,11 @@ else if (+savedFilters.song && +savedFilters.song > 0) $('#songID').val(savedFil
checkExtraFilters() checkExtraFilters()
Fetch(`../api/music`).then(music => { Fetch(`/api/music`).then(music => {
$('#songName').html("1: " + music[1][0]) $('#songName').html("1: " + music[1][0])
$(document).on('click', '.songChange', function () { $(document).on('click', '.songChange', function () {
officialSong += Number($(this).attr('jump')) officialSong += Number($(this).attr('jump'))
if (officialSong < 1) officialSong = 1 if (officialSong < 1) officialSong = 1
$('#songName').html(`${officialSong}: ${music[officialSong] ? music[officialSong][0] : officialSong == 69 ? "Nice" : "Unknown"}`) $('#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) { $(document).keydown(function(k) {
if (customSong) return; if (customSong) return
if (k.which == 37) $('#songDown').trigger('click') // left if (k.which == 37) $('#songDown').trigger('click') // left
if (k.which == 39) $('#songUp').trigger('click') // right if (k.which == 39) $('#songUp').trigger('click') // right
}); })
if (onePointNine) { if (onePointNine) {
$('#userSearch').hide() $('#userSearch').hide()
@ -406,7 +406,7 @@ Fetch(`../api/music`).then(music => {
$('#levelName').css('width', '76%') $('#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() else $('#demonList').show()
}) })

View file

@ -1,9 +1,9 @@
<head> <head>
<title>Gauntlets</title> <title>Gauntlets</title>
<meta charset="utf-8"> <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> <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-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-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"> <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 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"> <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>
<div class="center" width="100%" style="margin-top: 2.5%; margin-bottom: 1%;"> <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> </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"> <div id="gauntletList">
<br> <br>
</div> </div>
<br> <br>
</div> </div>
</body> </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/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> <script>
fetch('../api/gauntlets').then(res => res.json()).then(gauntlets => { fetch('/api/gauntlets').then(res => res.json()).then(gauntlets => {
$('#loading').hide() $('#loading').hide()
gauntlets.forEach((x, y) => { gauntlets.forEach((x, y) => {
$('#gauntletList').append(` $('#gauntletList').append(`
<div class="gauntlet"> <div class="gauntlet">
<a href="../search/*?gauntlet=${x.id}"> <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>`) <h3 class="gauntletText"">${x.name}<br>Gauntlet</h3></div></a>`)
}) })
}); });

View file

@ -1,9 +1,9 @@
<head> <head>
<title>GD Multiverse Navigation Terminal</title> <title>GD Multiverse Navigation Terminal</title>
<meta charset="utf-8"> <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> <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-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-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"> <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. 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. Servers which are <cr>inactive</cr> or have <cr>few levels/members</cr> will not be accepted.
</p> </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> </div>
<div id="searchBox" class="supercenter dragscroll"; style="width: 127vh"></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="epicbox supercenter gs" style="width: 126vh; height: 80%; pointer-events: none"></div>
<div class="center" style="position:absolute; top: 8%; left: 0%; right: 0%"> <div class="center" style="position:absolute; top: 8%; left: 0%; right: 0%">
<h1 class="pre" id="header">GD Private Servers</h1> <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"> <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>
<div class="supercenter" id="loading" style="height: 10%; top: 47%"> <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>
<div style="position: absolute; left: 7%; top: 45%; height: 10%;"> <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>
<div style="position: absolute; right: 7%; top: 45%; height: 10%;"> <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>
<div style="position:absolute; top: 3%; right: 2%; text-align: right; width: 20%;"> <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>
</div> </div>
</body> </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/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 type="text/javascript" src="../dragscroll.js"></script>
<script> <script>
@ -71,7 +71,7 @@ let page = 1
let localhost = window.location.hostname == "localhost" let localhost = window.location.hostname == "localhost"
let host = window.location.host.split(".").slice(-2).join(".") 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) let currentServer = servers.find(x => x.id == gdps)
servers = [currentServer].concat(servers.filter(x => x.id != gdps)).filter(x => x) servers = [currentServer].concat(servers.filter(x => x.id != gdps)).filter(x => x)
@ -89,15 +89,15 @@ Fetch('../api/gdps').then(servers => {
serverPage.forEach(x => { serverPage.forEach(x => {
$('#searchBox').append(`<div class="searchresult" style="height: 19%; padding-top: 1.2%"> $('#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> <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%)"> <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>
<div class="center" style="position:absolute; right: 7%; transform:translateY(-150%); height: 10%"> <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>
</div>`) </div>`)
}) })

View file

@ -1,9 +1,9 @@
<head> <head>
<title>Geometry Dash Browser!</title> <title>Geometry Dash Browser!</title>
<meta charset="utf-8"> <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> <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-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-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"> <meta id="meta-image" name="og:image" content="https://gdbrowser.com/assets/coin.png" itemprop="image">
@ -18,76 +18,76 @@
</div> </div>
<div style="position:absolute; bottom: 0%; left: 0%; width: 100%; pointer-events: none"> <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>
<div style="position:absolute; top: 0%; left: 0%; width: 100%; pointer-events: none"> <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>
<div style="position:absolute; top: 1.7%; right: 2%; text-align: right; width: 10%;"> <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>
<div class="menu-achievements" style="position:absolute; top: 5.5%; left: 3%; width: 12%;"> <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>
<div class="menu-messages" style="position:absolute; top: -1.7%; left: 11%; text-align: left; width: 10%;"> <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>
<div style="position:absolute; top: -1.5%; right: 10%; text-align: right; width: 10%;"> <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>
<div id="dl" style="display: none; position:absolute; top: 15%; right: 0.5%; text-align: center; width: 17%"> <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> <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. <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. <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> 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>
<div class="supercenter center" id="menuButtons" style="bottom: 5%;"> <div class="supercenter center" id="menuButtons" style="bottom: 5%;">
<table> <table>
<tr class="menuButtonList"> <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="./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><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: 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 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="./gauntlets"><img class="menubutton menu-gauntlets" src="/assets/category-gauntlets.png" title="Gauntlets"></a></td>
</tr> </tr>
<tr class="menuButtonList"> <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;"> --> <!-- <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="./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="./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"><img class="menubutton menu-search" src="/assets/category-search.png" title="Search"></a></td>
</tr> </tr>
</table> </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-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> <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> <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> <a class="menuLink" href="https://github.com/GDColon/GDBrowser" title="GitHub">GitHub</a>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &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> <a class="menuLink" href="https://store.steampowered.com/app/322170/Geometry_Dash/" title="Buy Geometry Dash!">Buy Geometry Dash!</a></p>
</div> </div>
<div style="position:absolute; bottom: 17%; right: 7%; width: 9%; text-align: right; pointer-events: none"> <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>
<div style="position:absolute; bottom: 2.5%; right: 1.5%; text-align: right; width: 18%;"> <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>
<div class="center" width="100%" style="margin-top: 2%"> <div class="center" width="100%" style="margin-top: 2%">
<img src="../assets/gdlogo.png" height="11.5%"><br> <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 id="browserlogo" src="/assets/browser.png" height="7%" style="margin: 0.5% 0% 0% 30%">
</div> </div>
<div id="noDaily" style="display: none;"> <div id="noDaily" style="display: none;">
@ -101,12 +101,12 @@
</body> </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/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="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="/iconkit/icon.js"></script>
<script type="text/javascript" src="../global.js?v=1"></script> <script type="text/javascript" src="/misc/global.js?v=1"></script>
<script> <script>
let page = 1 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 xButtonPos = '43%'
let lastPage let lastPage
@ -126,7 +126,7 @@ function loadCredits() {
$('#credits').show() $('#credits').show()
if (page == lastPage) $('#closeCredits').css('height', '52%') if (page == lastPage) $('#closeCredits').css('height', '52%')
else $('#closeCredits').css('height', xButtonPos) 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') $(this).addClass('creditLoaded')
let profile = await Fetch(`./api/profile/${$(this).attr('ign')}?forceGD=1`).catch(e => {}) || {} 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>`) $(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 lastPage = res.credits.length + 1
res.credits.forEach(async (x, y) => { res.credits.forEach(async (x, y) => {
$('#credits').append(`<div id="credits${y+1}" class="subCredits" style="display: none;"> $('#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%;"> <div class="brownBox center supercenter" style="width: 80vh; height: 43%; padding-top: 1.5%; padding-bottom: 3.5%;">
<h1>${x.header}</h1> <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> <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> <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.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.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.github[0]}" title="GitHub"><img src="/assets/${x.github[1]}.png" width="11%" class="sideSpace gdButton"></a>
<br> <br>
</div> </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>`) </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%;"> <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> <h1>Special Thanks!</h1><br>
</div> </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>`) </div>`)
res.specialThanks.forEach(async (x, y) => { 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;"> $('#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) { $(document).keydown(function(k) {
@ -182,11 +182,11 @@ Fetch(`./api/credits`).then(async res => {
if (k.which == 37 && page > 1) { //left if (k.which == 37 && page > 1) { //left
page -= 1; loadCredits(); page -= 1; loadCredits();
} }
if (k.which == 39 && page < lastPage) { //right if (k.which == 39 && page < lastPage) { //right
page += 1; loadCredits(); page += 1; loadCredits();
} }
}); });
}); });

View file

@ -1,15 +1,15 @@
<head> <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> <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> <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 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: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:title" content="Geometry Dash Online Icon Kit">
<meta property="og:type" content="website"> <meta property="og:type" content="website">
<meta name="og:image" itemprop="image" content="https://gdbrowser.com/assets/icon.png"> <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"> <meta name="twitter:card" content="summary">
<link rel="icon" href="../assets/icon.png"> <link rel="icon" href="/assets/icon.png">
</link> </link>
</head> </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)"> <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> <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;"> <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> <div id="copyForms"></div>
<img src="../assets/ok.png" height=55px; class="postButton gdButton center" style="margin-top: 30px" id="fetchUser"> <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 class="gdButton xButton" src="/assets/close.png" width="70px" onclick="$('#steal').hide()">
</div> </div>
</div> </div>
<div class="popup" data-nosnippet id="settings"> <div class="popup" data-nosnippet id="settings">
<div class="brownbox bounce center supercenter" style="height: 380px; width: 820px"> <div class="brownbox bounce center supercenter" style="height: 380px; width: 820px">
<h1 class="center gold" style="margin-top: 10px; margin-bottom: 20px;">Settings</h1> <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="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="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 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> <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 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 class="gdButton xButton" src="/assets/close.png" width="70px" onclick="$('#settings').hide()">
</div> </div>
</div> </div>
@ -45,14 +45,14 @@
<h1 class="center gold" style="margin-top: 12px">Enable 2.2 icons?</h1> <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: 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> <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/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 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 class="gdButton xButton" src="/assets/close.png" width="70px" onclick="$('#spoilerwarning').hide()">
</div> </div>
</div> </div>
<img id="iconkitlogo" src="../assets/iconkit.png" style="margin: 7px 0px; height: 50px"><br> <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> <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"> <div id="iconbox">
<canvas id="result" style="transform: translateY(-35px)"> <canvas id="result" style="transform: translateY(-35px)">
@ -60,14 +60,14 @@
<hr id="gdfloor"> <hr id="gdfloor">
<div id="menuButtons" style="height: 65px; margin: 0 0 8 0;"> <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" 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" 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" 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" 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="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="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" 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" onclick="clickedSpoilerWarning ? toggleSpoilers() : $('#spoilerwarning').show()" title="2.2 icons (spoilers!!!)"><img id="newIconBtn" src="/assets/iconkitbuttons/spoilers.png" width=60px></button>
</div> </div>
<div id="iconTabs"></div><br> <div id="iconTabs"></div><br>
@ -79,12 +79,12 @@
<div style="width: 1200px; margin: 0 auto; position: relative; right: 42px"> <div style="width: 1200px; margin: 0 auto; position: relative; right: 42px">
<div style="padding-top: 15px; width: 75px; vertical-align: top;" class="colTypes inline"> <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="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="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> <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="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="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="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>
<div id="colors" class="inline iconKit"> <div id="colors" class="inline iconKit">
<div id="col1"></div> <div id="col1"></div>
@ -116,27 +116,32 @@
</p> </p>
<div class="hideIfSmall" style="position:absolute; top: 20px; left: 20px; width: 64px; height: 64px; pointer-events: none"> <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>
<div class="hideIfSmall" id="extraInfo" style="position:absolute; top: 20px; right: 15px"></div> <div class="hideIfSmall" id="extraInfo" style="position:absolute; top: 20px; right: 15px"></div>
</div> </div>
</body> </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/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://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/ag-psd.js"></script>
<script type="text/javascript" src="./libs/pixi-ease.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="./libs/imagesloaded.js"></script>
<script type="text/javascript" src="./icon.js"></script> <script type="text/javascript" src="./icon.js"></script>
<script type="text/javascript"> <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 // hi there hello! this code is really old, so it's shit. i should rewrite it some time omg
$('.hidden').show(); $('.hidden').show()
// avoid possible name collision with isMobile.js (AJAX Node module)
let mobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent) let is_mobile = /Android|webOS|iPhone|iP[ao]d|BlackBerry/i.test(navigator.userAgent)
let currentForm = 'icon' let currentForm = 'icon'
let glowbtnformCopy = 'icon' let glowbtnformCopy = 'icon'
const yOffsets = { ball: -10, ufo: 30, spider: 7, swing: -15 } const yOffsets = { ball: -10, ufo: 30, spider: 7, swing: -15 }
@ -149,7 +154,7 @@ let selectedCol2 = 3
let selectedColG = 3 let selectedColG = 3
let selectedColW = null let selectedColW = null
let selectedColU = null let selectedColU = null
let enableGlow = 0 let enableGlow = false
let enableSpoilers = false let enableSpoilers = false
let clickedSpoilerWarning = false let clickedSpoilerWarning = false
@ -164,25 +169,28 @@ let animationMultiplier = 1
let icon = null let icon = null
let iconCanvas = document.getElementById('result'); let iconCanvas = document.getElementById('result')
let app = new PIXI.Application({ view: iconCanvas, width: 300, height: 300, backgroundAlpha: 0 }); 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(",") let iconSettings = (localStorage.iconkit || "").split(",")
iconSettings.forEach(x => { iconSettings.forEach(x => {
$(`#box-${x}`).prop('checked', true) $(`#box-${x}`).prop('checked', true)
}) })
function capitalize(str) { return str[0].toUpperCase() + str.substr(1) } let capitalize = str => str[0].toUpperCase() + str.substr(1)
function randInt(min, max) { return Math.floor(Math.random() * (max - min + 1) ) + min } function colorBG(e, c, hex) {
function colorBG(e, c, hex) {
$(`#cc${e} img`).css('background-color', hex ? `#${c}` : `rgb(${c.r}, ${c.g}, ${c.b})`) $(`#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() { function colorSplit() {
if ($("#colG").is(':visible') || $("#colW").is(':visible') || $("#colU").is(':visible')) $('.colorSplit').show() $('.colorSplit')[
else $('.colorSplit').hide() $("#colG").is(':visible') ||
$("#colW").is(':visible') ||
$("#colU").is(':visible')
? "show" : "hide"
]()
} }
function setColor(type, col) { function setColor(type, col) {
@ -215,10 +223,9 @@ function checkWhite() {
// check if animation selector should be visible // check if animation selector should be visible
function checkAnimation() { function checkAnimation() {
let animationData = iconData.robotAnimations.animations[selectedForm] let animationData = iconData.robotAnimations.animations[selectedForm]
if (animationData && !$(`#robotAnimation[form="${selectedForm}"]`).is(":visible")) { if (animationData && !$(`#robotAnimation[form="${selectedForm}"]`).is(":visible"))
appendAnimations(selectedForm, animationData) appendAnimations(selectedForm, animationData)
} if (!animationData) $('#animationOptions').hide()
else if (!animationData) $('#animationOptions').hide()
} }
function animationSort(anim) { function animationSort(anim) {
@ -249,8 +256,8 @@ function setExtras() {
} }
if ($('#colG').is(":visible") && (getCol(selectedColG) != getCol(selectedCol2))) extraInfo["Glow"] = extraColString(selectedColG) if ($('#colG').is(":visible") && (getCol(selectedColG) != getCol(selectedCol2))) extraInfo["Glow"] = extraColString(selectedColG)
if ($('#colW').is(":visible") && (getCol(selectedColW) != 0xffffff)) extraInfo["White"] = extraColString(selectedColW) if ($('#colW').is(":visible") && (getCol(selectedColW) != WHITE)) extraInfo["White"] = extraColString(selectedColW)
if ($('#colU').is(":visible") && (getCol(selectedColU) != 0xffffff)) extraInfo["UFO Dome"] = extraColString(selectedColU) if ($('#colU').is(":visible") && (getCol(selectedColU) != WHITE)) extraInfo["UFO Dome"] = extraColString(selectedColU)
let foundCredit = iconStuff.iconCredits.find(x => x.form == selectedForm && x.id == selectedIcon) let foundCredit = iconStuff.iconCredits.find(x => x.form == selectedForm && x.id == selectedIcon)
if (foundCredit) extraInfo["🎨 Artist"] = foundCredit.name if (foundCredit) extraInfo["🎨 Artist"] = foundCredit.name
@ -265,8 +272,8 @@ function setExtras() {
function extraColString(col) { function extraColString(col) {
let realCol = getCol(col) let realCol = getCol(col)
let hexCol = toHexCode(realCol) let hexCol = toHexCode(realCol)
let foundGDCol = Object.entries(iconStuff.colors).find(x => rgbToDecimal(x[1]) == realCol) let foundGDCol = Object.entries(iconStuff.colors).find(x => rgb2Pac(x[1]) == realCol)
return foundGDCol ? `${foundGDCol[0]} (${hexCol})` : hexCol return foundGDCol ? `${foundGDCol[0]} (${hexCol})` : hexCol
} }
@ -274,194 +281,198 @@ function getCol(id) {
return parseIconColor(id, iconStuff.colors) return parseIconColor(id, iconStuff.colors)
} }
function toHexCode(decimal) { function toHexCode(pack) {
return "#" + decimal.toString(16).padStart(6, "0") return "#" + pack.toString(16).padStart(6, "0")
} }
let iconData = null let iconData = null
fetch('../api/icons').then(res => res.json()).then(sacredTexts => { fetch('/api/icons').then(res => res.json()).then(sacredTexts => {
fetch('../api/iconkit').then(res => res.json()).then(iconKitData => { fetch('/api/iconkit').then(res => res.json()).then(iconKitData => {
iconStuff = Object.assign(sacredTexts, iconKitData) iconStuff = Object.assign(sacredTexts, iconKitData)
iconData = sacredTexts iconData = sacredTexts
let forms = Object.keys(iconStuff.forms) let forms = Object.keys(iconStuff.forms)
forms.forEach(form => { forms.forEach(form => {
let spoil = ["swing", "jetpack"].includes(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>`) $("#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>`) $("#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>`) $("#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() if (iconStuff.noCopy) $('#getUserIcon').remove()
else if (iconStuff.server) { else if (iconStuff.server) {
$('#copyFrom').html(`Copying from the <cy>${iconStuff.server}</cy> servers`) $('#copyFrom').html(`Copying from the <cy>${iconStuff.server}</cy> servers`)
$('#stealBox').css('height', '385px') $('#stealBox').css('height', '385px')
} }
function generateIcon(cb) { function generateIcon(cb) {
let noDome = selectedForm == "ufo" && iconSettings.includes("ufo") //let noDome = selectedForm == "ufo" && iconSettings.includes("ufo")
let foundForm = parseIconForm(selectedForm) let foundForm = parseIconForm(selectedForm)
loadIconLayers(foundForm, selectedIcon, function(l, sprites, isNew) { 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} 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 (selectedCol2 != selectedColG) iconArgs.colG = getCol(selectedColG)
if (selectedColW) iconArgs.colW = getCol(selectedColW) if (selectedColW) iconArgs.colW = getCol(selectedColW)
if (selectedColU) iconArgs.colU = getCol(selectedColU) if (selectedColU) iconArgs.colU = getCol(selectedColU)
if (iconSettings.includes("ufo")) iconArgs.noUFODome = true if (iconSettings.includes("ufo")) iconArgs.noUFODome = true
if (animationMultiplier != 1) iconArgs.animationSpeed = animationMultiplier if (animationMultiplier != 1) iconArgs.animationSpeed = animationMultiplier
if (currentAnimation.form && (iconSettings.includes("cursed") || currentAnimation.form == selectedForm)) { if (currentAnimation.form && (iconSettings.includes("cursed") || currentAnimation.form == selectedForm)) {
iconArgs.animation = currentAnimation.name iconArgs.animation = currentAnimation.name
iconArgs.animationForm = currentAnimation.form 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}%`)
} }
)}
function loadColors(devmode) { icon = new Icon(iconArgs)
let colTypes = [1, 2, "G", "W", "U"] icon.sprite.position.set(app.renderer.width / 2, (app.renderer.height / 2) + (yOffsets[selectedForm] || 0))
colTypes.forEach(x => $(`#col${x}`).html("")) updateDetails()
iconStuff.colorOrder.forEach(function (p, n) { checkWhite()
if (iconSettings.includes("sort")) p = n;
colTypes.forEach(c => { if (cb) cb()
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>`) }
})
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() loadColors()
let icons = filterIcon('icon'); let icons = filterIcon('icon')
let sample = JSON.parse(iconStuff.sample);
enableGlow = sample[3] * 2;
[selectedIcon, selectedCol1, selectedCol2] = sample;
selectedColG = selectedCol2
$('body').imagesLoaded(function () { let sample = JSON.parse(iconStuff.sample)
appendIcon(icons, "icon") enableGlow = !!(sample[3] * 2);
$(`[num="${sample[0]}"][form="icon"]`).addClass('iconSelected'); [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'); let img = $(this).children().first()
$(`.color2[col="${sample[2]}"]`).addClass('iconSelected'); img.attr('src', img.attr('src').replace('_off', '_on'));
$(`.colorG[col="${sample[2]}"]`).addClass('iconSelected');
$('.colorW[col="12"]').addClass('iconSelected');
$('.colorU[col="12"]').addClass('iconSelected');
colorBG(1, iconStuff.colors[sample[1]]) $('#iconKitParent').each(function() { $(this).children().not('#iconprogressbar').hide() })
colorBG(2, iconStuff.colors[sample[2]])
colorBG('G', iconStuff.colors[sample[2]])
$('.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 () { $('#iconTabs').find('.iconTabButton')
let form = $(this).attr('form') .first().children()
let formElement = '#' + form + 's' .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) { $("#randomIcon").click(function() {
$(this).children().first().attr('src', $(this).children().first().attr('src').replace('_on', '_off'))
})
let img = $(this).children().first() let iconPool = iconStuff.previewIcons.concat(enableSpoilers ? iconStuff.newPreviewIcons : [])
img.attr('src', img.attr('src').replace('_off', '_on')); 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) $('#glow').attr('src', '/assets/iconkitbuttons/streak_off.png')
$(formElement).show()
})
$('#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() { $('#glowbtn').click(function() {
setGlowAttr(enableGlow)
let iconPool = iconStuff.previewIcons.concat(enableSpoilers ? iconStuff.newPreviewIcons : []) enableGlow = !enableGlow
let pickedIcon = iconPool[Math.floor(Math.random() * iconPool.length)].split(".")[0].split("_") icon.setGlow(enableGlow, false)
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)
updateDetails() updateDetails()
}) })
$(document).on('click', '.copyForm', function () { $(document).on('click', '.copyForm', function () {
$('.copyForm').each(function(x, y) {$(this).children().first().attr('src', $(this).children().first().attr('src').replace('_on', '_off'))}) $('.copyForm').each(function(x, y) {$(this).children().first().attr('src', $(this).children().first().attr('src').replace('_on', '_off'))})
formCopy = $(this).attr('form') formCopy = $(this).attr('form')
@ -471,10 +482,10 @@ fetch('../api/iconkit').then(res => res.json()).then(iconKitData => {
$(document).on('click', '.iconButton', function () { $(document).on('click', '.iconButton', function () {
$(".iconButton").removeClass("iconSelected"); $(".iconButton").removeClass("iconSelected");
$(this).addClass('iconSelected'); $(this).addClass('iconSelected')
let oldForm = selectedForm let oldForm = selectedForm
selectedIcon = $(this).attr('num'); selectedIcon = $(this).attr('num')
selectedForm = $(this).attr('form'); selectedForm = $(this).attr('form')
if (selectedForm == "ufo") { $('#colU').show(); $('#ccU').show() } if (selectedForm == "ufo") { $('#colU').show(); $('#ccU').show() }
else { $('#colU').hide(); $('#ccU').hide(); $(`#colU-12`).trigger('click'); } 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 () { $(document).on('click', '.color1', function () {
$(".color1").removeClass("iconSelected"); $(".color1").removeClass("iconSelected");
$(this).addClass('iconSelected'); $(this).addClass('iconSelected')
selectedCol1 = $(this).attr('col'); selectedCol1 = $(this).attr('col')
colorBG(1, iconStuff.colors[$(this).attr('col')]); colorBG(1, iconStuff.colors[$(this).attr('col')])
setColor("1", selectedCol1) setColor("1", selectedCol1)
}) })
$(document).on('click', '.color2', function () { $(document).on('click', '.color2', function () {
$(".color2").removeClass("iconSelected"); $(".color2").removeClass("iconSelected");
$(this).addClass('iconSelected'); $(this).addClass('iconSelected')
selectedCol2 = $(this).attr('col'); selectedCol2 = $(this).attr('col')
colorBG(2, iconStuff.colors[$(this).attr('col')]); colorBG(2, iconStuff.colors[$(this).attr('col')]);
$(`#colG-${$(this).attr('col')}`).trigger('click') $(`#colG-${$(this).attr('col')}`).trigger('click')
selectedColG = $(this).attr('col'); selectedColG = $(this).attr('col')
setColor("2", selectedCol2) setColor("2", selectedCol2)
}) })
$(document).on('click', '.colorG', function () { $(document).on('click', '.colorG', function () {
$(".colorG").removeClass("iconSelected"); $(".colorG").removeClass("iconSelected");
$(this).addClass('iconSelected'); $(this).addClass('iconSelected')
selectedColG = $(this).attr('col'); selectedColG = $(this).attr('col')
colorBG('G', iconStuff.colors[$(this).attr('col')]); colorBG('G', iconStuff.colors[$(this).attr('col')])
setColor("g", selectedColG) setColor("g", selectedColG)
}) })
$(document).on('click', '.colorW', function () { $(document).on('click', '.colorW', function () {
$(".colorW").removeClass("iconSelected"); $(".colorW").removeClass("iconSelected");
$(this).addClass('iconSelected'); $(this).addClass('iconSelected')
selectedColW = $(this).attr('col'); selectedColW = $(this).attr('col')
if (selectedColW == 12) selectedColW = null if (selectedColW == 12) selectedColW = null
colorBG('W', iconStuff.colors[$(this).attr('col')]); colorBG('W', iconStuff.colors[$(this).attr('col')])
setColor("w", selectedColW) setColor("w", selectedColW)
}) })
$(document).on('click', '.colorU', function () { $(document).on('click', '.colorU', function () {
$(".colorU").removeClass("iconSelected"); $(".colorU").removeClass("iconSelected");
$(this).addClass('iconSelected'); $(this).addClass('iconSelected')
selectedColU = $(this).attr('col'); selectedColU = $(this).attr('col')
if (selectedColU == 12) selectedColU = null if (selectedColU == 12) selectedColU = null
colorBG('U', iconStuff.colors[$(this).attr('col')]); colorBG('U', iconStuff.colors[$(this).attr('col')])
setColor("u", selectedColU) setColor("u", selectedColU)
}) })
$("#cp1").on('input change', function() { $("#cp1").on('input change', function() {
colorBG(1, $(this).val(), true); colorBG(1, $(this).val(), true);
$(".color1").removeClass("iconSelected"); $(".color1").removeClass("iconSelected")
selectedCol1 = $('#cp1').val().slice(1) selectedCol1 = $('#cp1').val().slice(1)
setColor("1", selectedCol1) setColor("1", selectedCol1)
}) })
$("#cp2").on('input change', function() { $("#cp2").on('input change', function() {
colorBG(2, $(this).val(), true); colorBG(2, $(this).val(), true);
$(".color2").removeClass("iconSelected"); $(".color2").removeClass("iconSelected")
selectedCol2 = $('#cp2').val().slice(1) selectedCol2 = $('#cp2').val().slice(1)
setColor("2", selectedCol2) setColor("2", selectedCol2)
}) })
$("#cpG").on('input change', function() { $("#cpG").on('input change', function() {
colorBG('G', $(this).val(), true); colorBG('G', $(this).val(), true);
$(".colorG").removeClass("iconSelected"); $(".colorG").removeClass("iconSelected")
selectedColG = $('#cpG').val().slice(1) selectedColG = $('#cpG').val().slice(1)
setColor("g", selectedColG) setColor("g", selectedColG)
}) })
$("#cpW").on('input change', function() { $("#cpW").on('input change', function() {
colorBG('W', $(this).val(), true); colorBG('W', $(this).val(), true);
$(".colorW").removeClass("iconSelected"); $(".colorW").removeClass("iconSelected")
selectedColW = $('#cpW').val().slice(1) selectedColW = $('#cpW').val().slice(1)
setColor("w", selectedColW) setColor("w", selectedColW)
}) })
$("#cpU").on('input change', function() { $("#cpU").on('input change', function() {
colorBG('U', $(this).val(), true); colorBG('U', $(this).val(), true);
$(".colorU").removeClass("iconSelected"); $(".colorU").removeClass("iconSelected")
selectedColU = $('#cpU').val().slice(1) selectedColU = $('#cpU').val().slice(1)
setColor("u", selectedColU) setColor("u", selectedColU)
}) })
$("#getUserIcon").click(function() { $("#getUserIcon").click(function() {
$(`.copyForm[form=${currentForm}]`).trigger('click') $(`.copyForm[form=${currentForm}]`).trigger('click')
$('#steal').show(); $('#steal').show();
$('#playerName').focus() $('#playerName').focus()
$('#playerName').select() $('#playerName').select()
}) })
$('#copyToClipboard').click(function() { $('#copyToClipboard').click(function() {
if ($(this).hasClass('greyedOut')) return if ($(this).hasClass('greyedOut')) return
icon.copyToClipboard() icon.copyToClipboard()
let copyIcon = $(this).find('img') let copyIcon = $(this).find('img')
$(this).addClass('greyedOut') $(this).addClass('greyedOut')
copyIcon.attr('src', '../assets/iconkitbuttons/copied.png') copyIcon.attr('src', '/assets/iconkitbuttons/copied.png')
setTimeout(() => { setTimeout(() => {
$(this).removeClass('greyedOut') $(this).removeClass('greyedOut')
copyIcon.attr('src', '../assets/iconkitbuttons/copy.png') copyIcon.attr('src', '/assets/iconkitbuttons/copy.png')
}, 420); }, 420);
}) })
@ -595,12 +606,12 @@ fetch('../api/iconkit').then(res => res.json()).then(iconKitData => {
}) })
let hoverText = $('#helpText').html() let hoverText = $('#helpText').html()
$(".help").hover(function() { $(".help").hover(function() {
$(this).css('color', 'rgba(200, 255, 255)') $(this).css('color', 'rgba(200, 255, 255)')
$('#helpText').html($(this).attr('help')) $('#helpText').html($(this).attr('help'))
}, function() { }, function() {
$(this).css('color', 'white') $(this).css('color', 'white')
$('#helpText').html(hoverText) $('#helpText').html(hoverText)
}) })
$(document).on('change', '.iconsetting', function (e) { $(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]) }) $('.iconsetting:checkbox:checked').each((i, x) => { checkedSettings.push(x.id.split('-')[1]) })
iconSettings = checkedSettings iconSettings = checkedSettings
switch ($(this).attr('id').slice(4)) { switch ($(this).attr('id').slice(4)) {
case "sort": loadColors(); break; case "sort": loadColors(); break
case "ufo": generateIcon(); break; case "ufo": generateIcon(); break
case "cursed": case "cursed":
$('#animationOptions').hide(); c('#animationOptions').hide();
checkAnimation(); checkAnimation();
$('#robotAnimation').trigger('change'); $('#robotAnimation').trigger('change');
break; break;
} }
@ -620,8 +631,8 @@ fetch('../api/iconkit').then(res => res.json()).then(iconKitData => {
}) })
$('#unlockIcon').click(function() { $('#unlockIcon').click(function() {
if (!achievements.length) { if (!achievements.length) {
fetch('../api/achievements').then(res => { res.json().then(x => { fetch('/api/achievements').then(res => { res.json().then(x => {
achievements = x.achievements achievements = x.achievements
shopIcons = iconStuff.shops shopIcons = iconStuff.shops
unlockMode = true 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() } else { $('#lock').attr('src', $('#lock').attr('src').replace('_on.png', '.png')); $('#howto').hide() }
} }
}) })
$(document).on('mouseover', '.iconButton, .color1, .color2', function () { $(document).on('mouseover', '.iconButton, .color1, .color2', function () {
if (unlockMode && achievements.length) { if (unlockMode && achievements.length) {
$(this).addClass('iconHover') $(this).addClass('iconHover')
let form = $(this).attr('form') || $(this).attr('colType') let form = $(this).attr('form') || $(this).attr('colType')
let iconNumber = $(this).attr('num') || $(this).attr('col') 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 () { $(document).on('mouseleave', '.iconButton, .color1, .color2', function () {
$(this).removeClass('iconHover') $(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() if (!user || !user.length) return $("#steal").hide()
$(`.iconTabButton[form=${formCopy}]`).trigger('click') $(`.iconTabButton[form=${formCopy}]`).trigger('click')
$('#glow').attr('src', '../assets/iconkitbuttons/streak_off.png') $('#glow').attr('src', '/assets/iconkitbuttons/streak_off.png')
$("#steal").hide() $("#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 = {} if (info == "-1") info = {}
$(`#${formCopy}-${Math.min(info[formCopy] || 1, $(`.iconButton[form=${formCopy}]`).length)}`).trigger('click') $(`#${formCopy}-${Math.min(info[formCopy] || 1, $(`.iconButton[form=${formCopy}]`).length)}`).trigger('click')
@ -724,20 +735,21 @@ $('#animationSpeed').on('input', function() {
}) })
$('#animationSpeedBox').change(function() { $('#animationSpeedBox').change(function() {
animationMultiplier = Number(Math.abs(Number($(this).val()) || 1).toFixed(2)) animationMultiplier = Number( Math.abs( Number( $(this).val() ) || 1 ).toFixed(2) )
if (animationMultiplier > 99) animationMultiplier = 99 animationMultiplier = clamp(animationMultiplier, 0, 99)
else if (animationMultiplier <= 0) animationMultiplier = 0.1 animationMultiplier ||= 0.1
$('#animationSpeed').val(animationMultiplier) $('#animationSpeed').val(animationMultiplier)
$('#animationSpeedBox').val(animationMultiplier) $('#animationSpeedBox').val(animationMultiplier)
if (icon.complex) icon.animationSpeed = animationMultiplier if (icon.complex) icon.animationSpeed = animationMultiplier
}) })
$(document).keydown(function(k) { $(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') if ($("#steal").is(":visible")) $("#fetchUser").trigger('click')
else if ($(".popup").is(":visible")) return else if ($(".popup").is(":visible")) return
} }
if (k.keyCode == 27) { //esc if (k.code == 'Escape') {
if ($(".popup").is(":visible")) return $('.popup').hide() if ($(".popup").is(":visible")) return $('.popup').hide()
k.preventDefault() k.preventDefault()
$('#backButton').trigger('click') $('#backButton').trigger('click')
@ -751,5 +763,4 @@ $(document).on('click', '.brownbox', function (e) {
$(document).on('click', '.popup', function () { $(document).on('click', '.popup', function () {
$('.popup').hide() $('.popup').hide()
}) })
</script>
</script>

View file

@ -1,9 +1,9 @@
<head> <head>
<title>Leaderboard</title> <title>Leaderboard</title>
<meta charset="utf-8"> <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> <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-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-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"> <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="everything" style="overflow: auto;">
<div id="scoreTabs"> <div id="scoreTabs">
<img src="../assets/tab-top-on.png" class="leaderboardTab" id="topTabOn" style="display: none"> <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-off.png" class="leaderboardTab leaderboardClick" id="topTabOff">
<!-- for some GDPS'es --> <!-- for some GDPS'es -->
<img src="../assets/tab-weekly-on.png" class="sideSpaceC leaderboardTab" id="weeklyTabOn" 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-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-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-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-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-off.png" class="sideSpaceC leaderboardTab leaderboardClick" id="creatorTabOff">
</div> </div>
<div class="popup" id="infoDiv"> <div class="popup" id="infoDiv">
<div class="fancybox bounce center supercenter" style="width: 80vh"> <div class="fancybox bounce center supercenter" style="width: 80vh">
<h2 class="smaller center" style="font-size: 5.5vh">Leaderboard Info</h2> <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> <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> </div>
<div style="position:absolute; bottom: 0%; left: 0%; width: 100%"> <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>
<div style="position:absolute; bottom: 0%; right: 0%; width: 100%; text-align: right;"> <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> <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>
<div id="searchBox" class="supercenter dragscroll"> <div id="searchBox" class="supercenter dragscroll">
<div style="height: 4.5%"></div> <div style="height: 4.5%"></div>
</div> </div>
<div class="leaderboardBox supercenter gs" style="width: 120vh; height: 80%; pointer-events: none"> <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%"> <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="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="clearRelative" style="margin-bottom: 1vh; display: none" title="Clear Global Search" src="/assets/unmagnify.png" height="11%">
</div> </div>
<div class="sortDiv" style="display: none; position: relative; left: 100%; transform: translateX(4.5vh); top: 12%; width: 0.1%"> <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="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="weeklyStats" style="margin-bottom: 1vh" title="Weekly Stats" src="/assets/sort-week.png" height="11%">
</div> </div>
<div class="sortDiv" style="display: none; position: relative; right: 10.5%; top: 0%; width: 0.1%; transform: translateY(-33.3%)" id="statSort"> <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="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="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="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" 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; display: none" sort="cp" title="Most creator points" src="/assets/sort-cp.png" height="11%" id="cpSort">
</div> </div>
</div> </div>
<div style="position:absolute; top: 2%; left: 1.5%; width: 10%; height: 25%; pointer-events: none"> <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>
<div style="position:absolute; top: 2.5%; right: 2%; width: 10%; text-align: right;"> <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>
<div id="discordLinks" style="display: none; position:absolute; top: 12%; right: 1.5%; width: 21%; text-align: right;"> <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="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="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>
<div class="supercenter" id="loading" style="height: 10%; top: 47%; display: none;"> <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>
<div class="popup" id="userSearch"> <div class="popup" id="userSearch">
@ -90,10 +90,10 @@
<h2 class="smaller center" style="font-size: 5.5vh; margin-top: 1%">User Search</h2> <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> <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> <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-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-submit.png" class="postButton gdButton center" style="width: 32%; margin-left: 1%" id="relativeSearch">
<p id="relativeStatus" style="display: none"></p> <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> </div>
</div> </div>
@ -101,35 +101,36 @@
</body> </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/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="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="/iconkit/icon.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 type="text/javascript" src="/misc/dragscroll.js"></script>
<script> <script>
"use strict";
let type let type
let sort = "stars" let sort = "stars"
let modMode = false let modMode = false
let weekly = false let weekly = false
let showWeek = localStorage.weeklyStats == "1" 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 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.` `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.` `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.` `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 = 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.` `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.` `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) { function infoText(text, altDiscord) {
$('#infoText').html(text) $('#infoText').html(text)
@ -147,7 +148,7 @@ function leaderboard(val, leaderboardParams, scrollTo) {
$('#clearRelative').hide() $('#clearRelative').hide()
$('#loading').show() $('#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) { if (gdps && !didGDPSStuff) {
didGDPSStuff = true didGDPSStuff = true
@ -155,7 +156,7 @@ function leaderboard(val, leaderboardParams, scrollTo) {
$('#accurateTabOn').remove() $('#accurateTabOn').remove()
$('#accurateTabOff').remove() $('#accurateTabOff').remove()
Fetch('../api/gdps?current=1').then(ps => { Fetch('/api/gdps?current=1').then(ps => {
if (weekly) return if (weekly) return
else if (ps.weeklyLeaderboard) { $('#weeklyTabOff').show(); weekly = true } else if (ps.weeklyLeaderboard) { $('#weeklyTabOff').show(); weekly = true }
else $('#scoreTabs').css('margin-left', '-29vh') else $('#scoreTabs').css('margin-left', '-29vh')
@ -174,14 +175,14 @@ function leaderboard(val, leaderboardParams, scrollTo) {
$('#searchBox').html(`<div style="height: 4.5%"></div>`) $('#searchBox').html(`<div style="height: 4.5%"></div>`)
$('.ranking').remove() $('.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" 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 wp = x.weeklyProgress || {}
let cosmetics = x.cosmetics || {} let cosmetics = x.cosmetics || {}
let bgCol = cosmetics.bgColor let bgCol = cosmetics.bgColor
let bgString = bgCol ? ` style="background-color: rgb(${bgCol.join()})"` : "" 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}> $('#searchBox').append(`<div class="searchresult leaderboardSlot"${bgString}>
<div class="center ranking"> <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>`} `<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> <h2 class="slightlySmaller" style="transform: scale(${1 - (Math.max(0, String(x.rank).length - 1) * 0.1)})">${x.rank}</h2>
</div> </div>
<div class="leaderboardSide"> <div class="leaderboardSide">
<div class="leaderboardStars"> <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)">` : ""} ${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> <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> <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> </div>
<h3 class="lessSpaced leaderboardStats"> <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">`} ${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 || 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 ? "&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 || 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">`} ${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">`} ${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>
<h3 class="lessSpaced leaderboardStats weeklyStuff"}> <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.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.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.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.demons >= 25 ? " class='brightred'" : ""}>${wp.demons >= 0 ? "+" : ""}${wp.demons}</span> <img class="help valign" src="/assets/demon.png" title="Demon Gain">
</h3> </h3>
</div> </div>
@ -249,7 +250,7 @@ function leaderboard(val, leaderboardParams, scrollTo) {
}).catch(e => {console.log(e); $('#loading').hide();}) }).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 () { $(document).on('click', '.sortButton', function () {
if ($('#loading').is(":visible")) return if ($('#loading').is(":visible")) return
@ -261,7 +262,7 @@ $(document).on('click', '.sortButton', function () {
}) })
$('#topTabOff').click(function() { $('#topTabOff').click(function() {
if (type == "top") return; if (type == "top") return
type = "top" type = "top"
leaderboard(type) leaderboard(type)
$('.leaderboardTab').hide(); $('.leaderboardTab').hide();
@ -274,7 +275,7 @@ $('#topTabOff').click(function() {
}) })
$('#accurateTabOff').click(function() { $('#accurateTabOff').click(function() {
if (type == "accurate") return; if (type == "accurate") return
type = "accurate" type = "accurate"
leaderboard(type) leaderboard(type)
$('.leaderboardTab').hide(); $('.leaderboardTab').hide();
@ -287,7 +288,7 @@ $('#accurateTabOff').click(function() {
}) })
$('#weeklyTabOff').click(function() { $('#weeklyTabOff').click(function() {
if (type == "weekly" || !gdps) return; if (type == "weekly" || !gdps) return
type = "weekly" type = "weekly"
leaderboard(type) leaderboard(type)
$('.leaderboardTab').hide(); $('.leaderboardTab').hide();
@ -299,7 +300,7 @@ $('#weeklyTabOff').click(function() {
}) })
$('#creatorTabOff').click(function() { $('#creatorTabOff').click(function() {
if (type == "creator") return; if (type == "creator") return
type = "creator" type = "creator"
leaderboard(type) leaderboard(type)
$('.leaderboardTab').hide(); $('.leaderboardTab').hide();
@ -313,7 +314,7 @@ $('#creatorTabOff').click(function() {
$('#modSort').click(function() { $('#modSort').click(function() {
modMode = !modMode modMode = !modMode
$(this).attr('src', `../assets/sort-mod${modMode ? "-on" : ""}.png`) $(this).attr('src', `../assets/sort-mod${modMode ? "-on" : ""}.png`)
if (modMode) { if (modMode) {
$('#cpSort').show(); $('#cpSort').show();
$('#statSort').css('transform', 'translateY(-26.7%') $('#statSort').css('transform', 'translateY(-26.7%')
} }
@ -348,7 +349,7 @@ $('#relativeSearch').click(function() {
let relativeUsername = $('#relativeName').val() let relativeUsername = $('#relativeName').val()
if (relativeLoading || !relativeUsername) return if (relativeLoading || !relativeUsername) return
relativeLoading = true relativeLoading = true
Fetch("../api/profile/" + relativeUsername).then(foundUser => { Fetch("/api/profile/" + relativeUsername).then(foundUser => {
if (foundUser && foundUser.accountID && foundUser.rank) { if (foundUser && foundUser.accountID && foundUser.rank) {
leaderboard(null, "type=relative&accountID=" + foundUser.accountID, foundUser.accountID) leaderboard(null, "type=relative&accountID=" + foundUser.accountID, foundUser.accountID)
$('#userSearch').hide() $('#userSearch').hide()

View file

@ -1,9 +1,9 @@
<head> <head>
<title>[[NAME]] ([[ID]])</title> <title>[[NAME]] ([[ID]])</title>
<meta charset="utf-8"> <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> <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-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-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"> <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> <br>GD Version: <cy>[[GAMEVERSION]]</cy>
[[OBJECTINFO]][[REQUESTED]] [[OBJECTINFO]][[REQUESTED]]
</p> </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> </div>
@ -34,7 +34,7 @@
<p class="bigger center" style="line-height: 5vh; margin-top: 1.5vh;"> <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. <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> </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>
</div> </div>
@ -44,8 +44,8 @@
<p class="bigger center" style="line-height: 5vh; margin-top: 1.5vh;"> <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? 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> </p>
<img src="../assets/btn-no.png" height=25%; class="gdButton center closeWindow"> <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-yes.png" height=25%; class="gdButton center sideSpaceB" onclick="deleteLevel()">
</div> </div>
</div> </div>
@ -55,15 +55,15 @@
<p class="bigger center" style="line-height: 5vh; margin-top: 1.5vh;"> <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> <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> </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> </div>
<!-- <div class="popup" id="likeDiv"> <!-- <div class="popup" id="likeDiv">
<div class="brownbox bounce center supercenter" style="height: 75%; width: 100vh"> <div class="brownbox bounce center supercenter" style="height: 75%; width: 100vh">
<h1 class="smaller center" style="font-size: 5.5vh">Vote</h1> <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/smashLike.png" id="likebtn" class="inline gdButton likeButton">
<img src="../assets/smashDislike.png" id="dislikebtn" class="inline gdButton likeButton youAreNotTheOne"> <img src="/assets/smashDislike.png" id="dislikebtn" class="inline gdButton likeButton youAreNotTheOne">
<form action="nothing lol"> <form action="nothing lol">
<h3 class="center">GD Username</h3> <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%"> <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%"> <div style="min-height: 18%; max-height: 18%">
<p id="message" style="padding: 0% 10%; margin-top: 2.5%"></p> <p id="message" style="padding: 0% 10%; margin-top: 2.5%"></p>
</div> </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-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-submit.png" type="submit" height=10%; class="postButton gdButton center" style="margin-left: 1%" id="submitVote">
</div> </div>
</div> --> </div> -->
<div style="position:absolute; bottom: 0%; left: 0%; width: 100%"> <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>
<div style="position:absolute; bottom: 0%; right: 0%; width: 100%; text-align: right;"> <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>
<div class="center" style="width: 70%; margin: 1% auto"> <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%;"> <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="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"> <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="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="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="2pBadge" style="display: none; height: 60%; margin-left: -7%; cursor:help" src="/assets/twoPlayer.png" title="Two player level">
</h2><br> </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;"> <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> <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>
<div class="center valign" style="position:absolute; top: 10.5%; left: 27%; transform:scale(0.8); height: 100%; width: 20%"> <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" 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 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 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 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 id="coins" style="margin-top: 3%"></div>
</div> </div>
<div style="position:absolute; top: 20%; right: 0%; height: 38%; width: 40%; line-height: 8.5vh"> <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> <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> <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> <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> <h1 class="valign inline smaller spaced orbs">[[ORBS]]</h1>
</div> </div>
@ -129,16 +129,16 @@
<div style="margin-left: 0.5%"> <div style="margin-left: 0.5%">
<h1 class="pre slightlySmaller" id="songname">[[SONGNAME]]</h1> <h1 class="pre slightlySmaller" id="songname">[[SONGNAME]]</h1>
<h2 class="pre smaller">By: [[SONGAUTHOR]]<!-- <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="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%"><!-- --><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> --> <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="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="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="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="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> <h3 id="artistInfo" style="display: none; margin-top: 0.5%"></h3>
</div> </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>
<div class="center" style="position:absolute; bottom: 5%; left: 0; right: 0"> <div class="center" style="position:absolute; bottom: 5%; left: 0; right: 0">
@ -146,20 +146,20 @@
</div> </div>
<div class="center noClick" style="position:absolute; top: 24%; right: 0%; min-width: 100%"> <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>
<div class="levelButtons" style="position:absolute; top: 46%; right: 3.5%; transform: translateY(-50%); height: 75%;"> <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="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="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> --> <!-- <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="./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="./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> <a href="./leaderboard/[[ID]]"><img id="leaderboardbtn" title="View Leaderboard" class="gdButton sideButton" src="/assets/leaderboard.png"></a><br>
</div> </div>
<div style="position:absolute; top: 2%; left: 1.5%; width: 10%; height: 25%; pointer-events: none"> <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>
<div id="copied" style="display: none;"> <div id="copied" style="display: none;">
<div class="copied center noClick" style="position:absolute; top: 36%; left: 50%; transform: translate(-50%,-50%); width: 90vh"> <div class="copied center noClick" style="position:absolute; top: 36%; left: 50%; transform: translate(-50%,-50%); width: 90vh">
@ -171,7 +171,7 @@
</body> </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/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> <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>.' 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!"] "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 copies = 0
let animated = false; let animated = false
let freeze = false; let freeze = false
let dailyTime = Number('[[NEXTDAILY]]') || null let dailyTime = Number('[[NEXTDAILY]]') || null
$('#playButton').click(function () { $('#playButton').click(function () {
if (!($('#copied').is(':animated')) && !animated) { if (!($('#copied').is(':animated')) && !animated) {
animated = true; animated = true
copies += 1 copies += 1
$('#copied').stop().fadeIn(200).delay(500).fadeOut(500, function() { $('#copied').stop().fadeIn(200).delay(500).fadeOut(500, function() {
animated = false 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>"); var temp = $("<input>");
$("body").append(temp); $("body").append(temp)
temp.val('[[ID]]').select(); temp.val('[[ID]]').select()
document.execCommand("copy"); temp.remove() document.execCommand("copy"); temp.remove()
}) })
$('.closeWindow').click(function () { if (!freeze) $(".popup").attr('style', 'display: none;') }) $('.closeWindow').click(function () { if (!freeze) $(".popup").attr('style', 'display: none;') })
@ -238,7 +238,7 @@ else {
.replace('[[TIME1]]', "") .replace('[[TIME1]]', "")
.replace('[[TIME2]]', "") .replace('[[TIME2]]', "")
.replace('[[UPLOAD]]', "") .replace('[[UPLOAD]]', "")
.replace('[[UPDATE]]', "") + .replace('[[UPDATE]]', "") +
`<br><a id="additional" class="youCanClickThis" href="/[[ID]]?download"><ca>Download additional info</ca></a>` `<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("[")) { if (!'[[DEMONLIST]]'.startsWith("[")) {
$('.demonList').show() $('.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() else $('.demonList').remove()
if (!'[[DAILYNUMBER]]'.startsWith("[")) { if (!'[[DAILYNUMBER]]'.startsWith("[")) {
$('#dailyIcon').attr('src', `../assets/crown-${'[[WEEKLY]]'.startsWith("[") ? "daily" : "weekly"}.png`) $('#dailyIcon').attr('src', `../assets/crown-${'[[WEEKLY]]'.startsWith("[") ? "daily" : "weekly"}.png`)
$('.dailyLevel').show() $('.dailyLevel').show()
if (dailyTime) { if (dailyTime) {
$('#dailyTime').html(` (${colonize(dailyTime, true)})`) $('#dailyTime').html(` (${colonize(dailyTime, true)})`)
setInterval(() => { 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() $('#leaderboardbtn').hide()
$('#checkSong').remove() $('#checkSong').remove()
} }
if ("[[SONGAUTHOR]]" == "Unknown" || "[[INVALIDSONG]]" == "true") $('.songLink').hide() 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')) { if ($("#additional").hasClass('downloadDisabled')) {
$('#analyzeLink').removeAttr('href') $('#analyzeLink').removeAttr('href')
@ -295,9 +294,9 @@ if ($("#additional").hasClass('downloadDisabled')) {
} }
let coinColor = [[VERIFIEDCOINS]] ? "silvercoin" : "browncoin" let coinColor = [[VERIFIEDCOINS]] ? "silvercoin" : "browncoin"
if ([[COINS]] > 0) $("#coins").append(`<img 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]] > 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]] > 2) $("#coins").append(`<img class="squeeze" src="/assets/${coinColor}.png" height="5%">`)
if ("[[GDPS]]".startsWith("1.9/")) $("#authorLink").attr('href', '/search/[[PLAYERID]]?user') if ("[[GDPS]]".startsWith("1.9/")) $("#authorLink").attr('href', '/search/[[PLAYERID]]?user')
@ -319,43 +318,43 @@ $(window).on('load', function() {
if (overflow > 3) overflow = 3 if (overflow > 3) overflow = 3
$('#songname').addClass('smaller').css('font-size', (6 - (overflow)) + 'vh') $('#songname').addClass('smaller').css('font-size', (6 - (overflow)) + 'vh')
} }
}); })
let savedLevels = JSON.parse(localStorage.getItem('saved') || '[]'); let savedLevels = JSON.parse(localStorage.getItem('saved') || '[]')
let deleteMode = false; let deleteMode = false
if (savedLevels.includes('[[ID]]')) $('#saveButton').attr('src', '../assets/delete.png').attr('onclick', '$("#deleteDiv").show()') if (savedLevels.includes('[[ID]]')) $('#saveButton').attr('src', '/assets/delete.png').attr('onclick', '$("#deleteDiv").show()')
function saveLevel() { function saveLevel() {
savedLevels.push('[[ID]]'); savedLevels.push('[[ID]]')
localStorage.setItem('saved', JSON.stringify(savedLevels)); localStorage.setItem('saved', JSON.stringify(savedLevels));
} }
function savedLevel() { function savedLevel() {
$('#saveButton').attr('src', '../assets/delete.png').attr('onclick', '$("#deleteDiv").show()') $('#saveButton').attr('src', '/assets/delete.png').attr('onclick', '$("#deleteDiv").show()')
$('.popup').hide() $('.popup').hide()
} }
function deleteLevel() { function deleteLevel() {
savedLevels = savedLevels.filter(function(el) {return el != '[[ID]]'}) savedLevels = savedLevels.filter(function(el) {return el != '[[ID]]'})
localStorage.setItem('saved', JSON.stringify(savedLevels)); 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() $('.popup').hide()
} }
$('#checkSong').click(function() { $('#checkSong').click(function() {
$('#checkSong').hide() $('#checkSong').hide()
$('#songLoading').show() $('#songLoading').show()
fetch(`../api/song/[[SONGID]]`).then(res => res.json()).then(info => { fetch(`/api/song/[[SONGID]]`).then(res => res.json()).then(info => {
$('#songLoading').hide() $('#songLoading').hide()
$(info && info != -1 ? '#songAllowed' : '#songNotAllowed').show().addClass('songStatus') $(info && info != -1 ? '#songAllowed' : '#songNotAllowed').show().addClass('songStatus')
// if (info.error) return // if (info.error) return
// if (!info.artist.scouted) { // 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').attr('title', $('#scout').attr('title').replace('is ', 'is NOT '))
// $('#scout').css('filter', 'saturate(0.4)') // $('#scout').css('filter', 'saturate(0.4)')
// } // }
// if (!info.artist.whitelisted) { // 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 ')) // $('#whitelist').attr('title', $('#whitelist').attr('title').replace('is ', 'is NOT '))
// } // }
// $('#scout').show(); $('#whitelist').show() // $('#scout').show(); $('#whitelist').show()
@ -375,7 +374,7 @@ $('.artistIcon').hover(function() {
// let like; // let like;
// let likedLevels = localStorage.likedLevels ? JSON.parse(localStorage.likedLevels) : [] // 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').click(function() {
// $('#likebtn').removeClass('youAreNotTheOne') // $('#likebtn').removeClass('youAreNotTheOne')
@ -405,7 +404,7 @@ $('.artistIcon').hover(function() {
// $('.postbutton').hide() // $('.postbutton').hide()
// allowEsc = false // 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!")} // if (!res || res == "-1") {$('.postbutton').show(); return $('#message').text("The username you provided doesn't exist!")}
// else accountID = res.accountID // else accountID = res.accountID
@ -414,7 +413,7 @@ $('.artistIcon').hover(function() {
// likedLevels.push('[[ID]]') // likedLevels.push('[[ID]]')
// localStorage.setItem('likedLevels', JSON.stringify(likedLevels)) // localStorage.setItem('likedLevels', JSON.stringify(likedLevels))
// location.reload() // location.reload()
// }) // })
// .fail(e => {$('.postbutton').show();$('#message').text(e.responseText.includes("DOCTYPE") ? "Something went wrong..." : e.responseText)}) // .fail(e => {$('.postbutton').show();$('#message').text(e.responseText.includes("DOCTYPE") ? "Something went wrong..." : e.responseText)})
// }) // })
// }) // })

View file

@ -1,9 +1,9 @@
<head> <head>
<title>Leaderboard</title> <title>Leaderboard</title>
<meta charset="utf-8"> <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> <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-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-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"> <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 id="everything" style="overflow: auto;">
<div style="position:absolute; bottom: 0%; left: 0%; width: 100%"> <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>
<div style="position:absolute; bottom: 0%; right: 0%; width: 100%; text-align: right;"> <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>
<div id="searchBox" class="supercenter dragscroll"; style="width: 124vh"> <div id="searchBox" class="supercenter dragscroll"; style="width: 124vh">
<div style="height: 4.5%"></div> <div style="height: 4.5%"></div>
</div> </div>
<div class="epicbox supercenter gs" style="width: 126vh; height: 80%; pointer-events: none"></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%"> <div class="center" style="position:absolute; top: 8%; left: 0%; right: 0%">
@ -33,13 +33,13 @@
</div> </div>
<div style="position:absolute; top: 2%; left: 1.5%; width: 10%; height: 25%; pointer-events: none"> <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>
<div class="supercenter" id="loading" style="height: 10%; top: 47%; display: none;"> <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>
<div id="error" class="supercenter" style="height: 20%; top: 47%; display: none;"> <div id="error" class="supercenter" style="height: 20%; top: 47%; display: none;">
<h1 class="center">No scores available...</h1> <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> <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>
<div class="supercenter" style="left: 87%; top: 24%; height: 10%"> <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>
<div class="supercenter" style="left: 87%; top: 35%; height: 10%"> <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>
</div> </div>
@ -59,12 +59,12 @@
</body> </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/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="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="/iconkit/icon.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 type="text/javascript" src="/misc/dragscroll.js"></script>
<script> <script>
let loading = false; let loading = false
let lvlID = Math.round(window.location.pathname.split('/')[2]) let lvlID = Math.round(window.location.pathname.split('/')[2])
if (!lvlID || lvlID > 99999999 || lvlID < -99999999) window.location.href = window.location.href.replace("leaderboard", "search") if (!lvlID || lvlID > 99999999 || lvlID < -99999999) window.location.href = window.location.href.replace("leaderboard", "search")
@ -78,7 +78,7 @@ function leaderboard() {
loading = true; loading = true;
$('#loading').show() $('#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) if (lvl == "-1") return $('#header').html("Nonexistent level " + lvlID)
document.title = "Leaderboards for " + lvl.name document.title = "Leaderboards for " + lvl.name
@ -86,9 +86,9 @@ function leaderboard() {
$('#meta-title').attr('content', "Leaderboards for " + lvl.name) $('#meta-title').attr('content', "Leaderboards for " + lvl.name)
$('#meta-desc').attr('content', 'View the leaderboard for ' + lvl.name + ' by ' + lvl.author + '!') $('#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") { if (!res || res.error || res == "-1") {
loading = false; loading = false;
@ -101,12 +101,12 @@ function leaderboard() {
res.forEach((x, y) => { res.forEach((x, y) => {
$('#searchBox').append(`<div class="searchresult leaderboardSlot levelboardSlot" style="align-items: center; padding-left: 1vh; height: 15%; width: 100%; position: relative"> $('#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> <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> <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> <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> <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%;"> <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> <p class="commentDate">${x.date}</p>
@ -117,17 +117,17 @@ function leaderboard() {
$('#searchBox').append('<div style="height: 4.5%"></div>') $('#searchBox').append('<div style="height: 4.5%"></div>')
loading = false; loading = false;
$('#loading').hide(); $('#loading').hide()
lazyLoadIcons() lazyLoadIcons()
}) })
} }
let weekly = false; let weekly = false
leaderboard() leaderboard()
$('#topMode').click(function() { $('#topMode').click(function() {
if (!weekly || loading) return; if (!weekly || loading) return
weekly = false weekly = false
leaderboard() leaderboard()
$('#weekMode').removeClass('darken') $('#weekMode').removeClass('darken')
@ -135,12 +135,12 @@ function leaderboard() {
}) })
$('#weekMode').click(function() { $('#weekMode').click(function() {
if (weekly || loading) return; if (weekly || loading) return
weekly = true weekly = true
leaderboard() leaderboard()
$('#topMode').removeClass('darken') $('#topMode').removeClass('darken')
$('#weekMode').addClass('darken') $('#weekMode').addClass('darken')
}); })
function lazyLoadIcons() { function lazyLoadIcons() {
let newIconFound = false let newIconFound = false

View file

@ -1,9 +1,9 @@
<head> <head>
<title>Map Packs</title> <title>Map Packs</title>
<meta charset="utf-8"> <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> <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-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-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"> <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> <h1 style="transform:scale(1.2)">Map Packs</h1>
</div> </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"> <div id="packList">
<br> <br>
</div> </div>
<div style="position:absolute; top: 2%; left: -1.95%; width: 10%; height: 25%; pointer-events: none"> <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>
<br> <br>
</div> </div>
</body> </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/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> <script>
fetch('../api/mappacks').then(res => res.json()).then(packs => { fetch('/api/mappacks').then(res => res.json()).then(packs => {
$('#loading').hide() $('#loading').hide()
packs.forEach(x => { packs.forEach(x => {
$('#packList').append(` $('#packList').append(`
<div class="mappack"> <div class="mappack">
<a href="../search/${x.levels.join(",")}?list&header=${encodeURIComponent(x.name)}"> <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> <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.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.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>`) </h3></div></a>`)
}) })
}); });

View file

@ -1,10 +1,10 @@
<head> <head>
<title>Messages</title> <title>Messages</title>
<meta charset="utf-8"> <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 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>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-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-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"> <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%"> <div style="margin-bottom: 3%">
<p id="message" style="padding: 0% 10%; margin-top: 1.5%"></p> <p id="message" style="padding: 0% 10%; margin-top: 1.5%"></p>
</div> </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()"> 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"> style="margin-left: 1%" id="logIn">
</div> </div>
</div> </div>
@ -40,7 +40,7 @@
<div class="brownbox center supercenter" style="height: 65%; width: 110vh"> <div class="brownbox center supercenter" style="height: 65%; width: 110vh">
<div style="position:absolute; top: 25%; width: 100%"> <div style="position:absolute; top: 25%; width: 100%">
<p id="msgCount" style="font-size: 3.5vh; margin-bottom: 5%"></p> <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> </div>
</div> </div>
@ -52,25 +52,25 @@
</div> </div>
<div style="text-align: left"> <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;"> <div id="selectCount" style="display: none; pointer-events: none;">
<h3 class="center" style="font-size: 3vh; margin-top: 12%"></h3> <h3 class="center" style="font-size: 3vh; margin-top: 12%"></h3>
</div> </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%"> 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%"> style="display: none; margin: 0% 0% 0.5% 2%">
</div> </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%;"> <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%"> height="90%">
</div> </div>
<div style="position: absolute; right: 0.5%; top: 45%; height: 11%;"> <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%"> height="90%">
</div> </div>
@ -84,19 +84,19 @@
<p class="bigger center" style="line-height: 6vh; margin-top: 1.5vh;"> <p class="bigger center" style="line-height: 6vh; margin-top: 1.5vh;">
Are you sure you want to <cr>delete</cr> this message? Are you sure you want to <cr>delete</cr> this message?
</p> </p>
<img src="../assets/btn-cancel-green.png" height=29%; class="gdButton center" onclick="$('#confirmDelete').hide()"> <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-delete.png" height=29%; id="deleteCurrentMessage" class="gdButton center sideSpaceB">
</div> </div>
<div id="deleting" style="display: none"> <div id="deleting" style="display: none">
<h2 class="smaller center" style="font-size: 5.5vh">Delete</h2> <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> <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>
<div id="delete-error" style="display: none;"> <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 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 src="/assets/exclamation.png" style="height: 40%">
<p class="bigger" style="margin-bottom: 0%; margin-top: 2.5%">Something went wrong!</p> <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> <p id="delError" style="font-size: 2.4vh; margin-top: 1%"></p>
</div> </div>
@ -111,9 +111,9 @@
Are you sure you want to <cr>delete</cr> <span class="selectedAmount" Are you sure you want to <cr>delete</cr> <span class="selectedAmount"
style="color: yelow"></span>? style="color: yelow"></span>?
</p> </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()"> 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"> class="gdButton center sideSpaceB">
</div> </div>
@ -121,12 +121,12 @@
<h2 class="smaller center" style="font-size: 5.5vh">Delete</h2> <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 <p class="bigger center" style="line-height: 6vh; margin-top: 1.5vh;">Deleting <span
class="selectedAmount"></span>...</p> 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>
<div id="bd-error" style="display: none;"> <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 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 src="/assets/exclamation.png" style="height: 40%">
<p class="bigger" style="margin-bottom: 0%; margin-top: 2.5%">Something went wrong!</p> <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> <p id="bdError" style="font-size: 2.4vh; margin-top: 1%"></p>
</div> </div>
@ -136,16 +136,16 @@
<div id="selectedMessage" class="popup"> <div id="selectedMessage" class="popup">
<div class="bounce center supercenter" style="height: 70%; width: 115vh"> <div class="bounce center supercenter" style="height: 70%; width: 115vh">
<div class="bluebox center supercenter" style="width: 100%; height: 100%"> <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> <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> <h3 id="messageAuthor" class="gold center gauntletText gdButton"></h3>
<div class="transparentBox center" id="theMfMessage"> <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> <p id="messageBody"></p>
</div> </div>
<div id="messageOptions"> <div id="messageOptions">
<img class="gdButton" style="width: 8%" title="Reply" src="../assets/reply.png" id="replyButton"> <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="Delete" src="/assets/trash.png" id="deleteButton" onclick="$('#confirmDelete').show()">
</div> </div>
</div> </div>
</div> </div>
@ -163,10 +163,10 @@
placeholder="Message"></textarea> placeholder="Message"></textarea>
</div> </div>
<p id="messageStatus" style="margin: 1% 0% 1.5% 0%; min-height: 5%;"></p> <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()"> onclick="$('textarea').val(''); $('#sendMessage').hide()">
<img src="../assets/btn-submit.png" height=10%; id="postMessage" class="gdButton center sideSpaceB"> <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/trash.png" style="height: 10%; position: absolute; right: 0%"
onclick="$('#postContent').val('');" class="gdButton center sideSpaceB"> onclick="$('#postContent').val('');" class="gdButton center sideSpaceB">
</div> </div>
</div> </div>
@ -176,19 +176,19 @@
<div class="bluebox bounce center supercenter" style="height: 27%; width: 55vh"> <div class="bluebox bounce center supercenter" style="height: 27%; width: 55vh">
<div id="reply-loading"> <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> <p class="bigger">Sending...</p>
</div> </div>
<div id="reply-sent" style="display: none;"> <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 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 src="/assets/check.png" style="height: 40%; margin-top: 7.5%">
<p class="bigger">Message sent!</p> <p class="bigger">Message sent!</p>
</div> </div>
<div id="reply-error" style="display: none;"> <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 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 src="/assets/exclamation.png" style="height: 40%; margin-top: 7.5%">
<p class="bigger" style="margin-bottom: 0%;">Something went wrong!</p> <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> <p style="font-size: 2.4vh; margin-top: 1%">Does the recipient have messages enabled?</p>
</div> </div>
@ -197,15 +197,15 @@
</div> </div>
<div style="position:absolute; bottom: 0%; left: 0%; width: 100%; pointer-events: none;"> <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>
<div style="position:absolute; bottom: 0%; right: 0%; width: 100%; text-align: right; pointer-events: none;"> <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>
<div style="position:absolute; top: 2%; left: 1.5%; width: 10%; height: 25%; pointer-events: none"> <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>
</div> </div>
@ -213,18 +213,18 @@
</body> </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/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> <script>
let accountID; let accountID
let password; let password
let page = 0; let page = 0
let messageID = 0; let messageID = 0
let playerID = 0; let playerID = 0
let messages = []; let messages = []
let messageStatus = {}; let messageStatus = {}
let cache = {}; let cache = {}
let loading = false; 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.' 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) $('#message').html(messageText)
@ -254,7 +254,7 @@
<label for="message-${y}" class="gdcheckbox gdButton"></label> <label for="message-${y}" class="gdcheckbox gdButton"></label>
</div>${/* </div>${/*
<div class="xButton hitbox"> <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>*/""}
</div>`) </div>`)
}) })
@ -272,7 +272,7 @@
$('#message').text("Logging in...") $('#message').text("Logging in...")
$('.postbutton').hide() $('.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!") } if (!res || res == "-1") { $('.postbutton').show(); return $('#message').text("The username you provided doesn't exist!") }
else accountID = res.accountID else accountID = res.accountID
@ -292,7 +292,7 @@
let targetUser = window.location.search.match(/(\?|&)sendTo=(.+)/) let targetUser = window.location.search.match(/(\?|&)sendTo=(.+)/)
if (targetUser) { if (targetUser) {
targetUser = decodeURIComponent(targetUser[2]) 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; if (res == "-1" || !res) return;
$('#replyAuthor').html(`<a href="../u/${res.accountID}." target="_blank">To: ${res.username}</a>`) $('#replyAuthor').html(`<a href="../u/${res.accountID}." target="_blank">To: ${res.username}</a>`)
messageStatus[res.accountID] = [res.messages, res.username] messageStatus[res.accountID] = [res.messages, res.username]
@ -321,7 +321,7 @@
$('#selectCount').hide() $('#selectCount').hide()
$('#selectAll').show() $('#selectAll').show()
$('#selectNone').hide() $('#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 }) $.post("../messages", { password, accountID, page })
.done(msgs => { .done(msgs => {
messages = msgs messages = msgs
@ -384,7 +384,7 @@
$('#deleteButton').show() $('#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] messageStatus[msg.accountID] = [res.messages, msg.author]
loadMsg() loadMsg()
}) })
@ -392,10 +392,10 @@
loadMsg() loadMsg()
} }
}) })
.fail(e => { .fail(e => {
$('#messageAuthor').html('&nbsp;') $('#messageAuthor').html('&nbsp;')
$('#messageSubject').html('&nbsp;') $('#messageSubject').html('&nbsp;')
$('#messageLoad').hide() $('#messageLoad').hide()
$('#messageBody').html(e.responseText).show() $('#messageBody').html(e.responseText).show()
$('#theMfMessage').attr('style', 'background-color: rgba(0, 0, 0, 0)') $('#theMfMessage').attr('style', 'background-color: rgba(0, 0, 0, 0)')
}) })
@ -416,7 +416,7 @@
$('#preDelete').show() $('#preDelete').show()
$('#deleting').hide() $('#deleting').hide()
}) })
.fail(e => { .fail(e => {
$('#deleting').hide() $('#deleting').hide()
$('#delete-error').show() $('#delete-error').show()
$('#delError').html(e.responseText) $('#delError').html(e.responseText)
@ -450,7 +450,7 @@
$('#bulkDeleting').hide() $('#bulkDeleting').hide()
$('#preBulkDelete').show() $('#preBulkDelete').show()
}) })
.fail(e => { .fail(e => {
$('#bulkDeleting').hide() $('#bulkDeleting').hide()
$('#bd-error').show() $('#bd-error').show()
$('#bdError').html(e.responseText) $('#bdError').html(e.responseText)
@ -458,7 +458,7 @@
}) })
$('#replyButton').click(function() { $('#replyButton').click(function() {
if (!messageStatus[playerID]) return; if (!messageStatus[playerID]) return
let status = messageStatus[playerID][0] let status = messageStatus[playerID][0]
let name = messageStatus[playerID][1] let name = messageStatus[playerID][1]
$('#postMessage').removeClass('grayscale') $('#postMessage').removeClass('grayscale')
@ -475,7 +475,7 @@
$('#postMessage').click(function () { $('#postMessage').click(function () {
let subject = $('#postSubject').val() let subject = $('#postSubject').val()
let message = $('#postContent').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 allowEsc = false
$('#reply-loading').show() $('#reply-loading').show()
$('#reply-sent').hide() $('#reply-sent').hide()
@ -488,7 +488,7 @@
$('#reply-sent').show() $('#reply-sent').show()
allowEsc = true allowEsc = true
}) })
.fail(e => { .fail(e => {
$('#reply-loading').hide() $('#reply-loading').hide()
$('#reply-error').show() $('#reply-error').show()
allowEsc = true allowEsc = true
@ -518,10 +518,10 @@
}); });
$(document).keydown(function (k) { $(document).keydown(function (k) {
if (loading) return; if (loading) return
if ($('#access').is(':visible')) { if ($('#access').is(':visible')) {
if (k.which == 13) $('#logIn').trigger('click') //enter if (k.which == 13) $('#logIn').trigger('click') //enter
else return; else return;
} }

View file

@ -1,9 +1,9 @@
<head> <head>
<title>Geometry Dash Browser!</title> <title>Geometry Dash Browser!</title>
<meta charset="utf-8"> <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> <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-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-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"> <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"> <div id="everything" class="center" style="overflow: hidden">
<h2 style="margin-top: 2%; margin-bottom: 2.2%">RobTop's Purgatory</h2> <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> <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"> <div id="footer" style="position: absolute; left: 1%; bottom: 0%; text-align: left">
@ -27,33 +27,33 @@
</div> </div>
<div style="position:absolute; top: 2.8%; left: 0.5%; width: 4.5%; text-align: right;"> <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>
<div style="position:absolute; bottom: 3%; right: 3%; width: 10%; text-align: right;"> <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>
<div style="position:absolute; bottom: 22%; right: 7%; width: 10%; text-align: right; pointer-events: none"> <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>
<div style="position:absolute; top: 15.5%; right: 7.5%; width: 10%; text-align: right; pointer-events: none"> <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>
<div style="position:absolute; top: 2.2%; right: 2.5%; text-align: right; width: 11%;"> <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>
<div style="position:absolute; top: -1.5%; right: 11%; text-align: right; width: 10%;"> <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> <a href="../iconkit"><img class="iconRope" src="/assets/iconrope.png" width="40%"></a>
</div> </div>
</div> </div>
</body> </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/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> <script>
let line = 0 let line = 0

View file

@ -1,7 +1,7 @@
<head> <head>
<title>[[USERNAME]]'s Profile</title> <title>[[USERNAME]]'s Profile</title>
<meta charset="utf-8"> <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> <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=""> <link rel="icon" href="">
<meta id="meta-title" property="og:title" content="[[USERNAME]]'s profile"> <meta id="meta-title" property="og:title" content="[[USERNAME]]'s profile">
@ -26,16 +26,16 @@
<div style="min-height: 20%; max-height: 20%"> <div style="min-height: 20%; max-height: 20%">
<p id="message" style="padding: 0% 10%; margin-top: 2%"></p> <p id="message" style="padding: 0% 10%; margin-top: 2%"></p>
</div> </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-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-submit.png" type="submit" height=11%; class="postButton gdButton center" style="margin-left: 1%" id="submitComment">
</div> </div>
</div> </div>
<div class="popup" id="likeComment"> <div class="popup" id="likeComment">
<div class="brownbox bounce center supercenter" style="height: 75%; width: 100vh"> <div class="brownbox bounce center supercenter" style="height: 75%; width: 100vh">
<h1 class="smaller center" style="font-size: 5.5vh">Vote</h1> <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/smashLike.png" id="likebtn" class="inline gdButton likeButton"><!--
--><img src="../assets/smashDislike.png" id="dislikebtn" class="inline gdButton likeButton youAreNotTheOne"> --><img src="/assets/smashDislike.png" id="dislikebtn" class="inline gdButton likeButton youAreNotTheOne">
<form action="nothing lol"> <form action="nothing lol">
<h3 class="center">GD Username</h3> <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%"> <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%"> <div style="min-height: 18%; max-height: 18%">
<p id="likeMessage" style="padding: 0% 10%; margin-top: 2.5%"></p> <p id="likeMessage" style="padding: 0% 10%; margin-top: 2.5%"></p>
</div> </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-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-submit.png" type="submit" height=10%; class="postButton gdButton center" style="margin-left: 1%" id="submitVote">
</div> </div>
</div> </div>
<div class="popup" id="settingsDiv"> <div class="popup" id="settingsDiv">
<div class="fancybox bounce center supercenter"> <div class="fancybox bounce center supercenter">
<h2 class="smaller center" style="font-size: 5.5vh">User Info</h2> <h2 class="smaller center" style="font-size: 5.5vh">User Info</h2>
@ -58,43 +58,43 @@
Private Messages: [[DMS]]<br> Private Messages: [[DMS]]<br>
Comment History: [[COMMENTS]]<br> Comment History: [[COMMENTS]]<br>
</p> </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> </div>
<div style="position:absolute; bottom: 0%; left: 0%; width: 100%"> <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>
<div style="position:absolute; bottom: 0%; right: 0%; width: 100%; text-align: right;"> <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>
<div style="position:absolute; top: 2%; left: 1.5%; width: 10%; height: 25%; pointer-events: none"> <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>
<div class="brownBox center supercenter" style="width: 135vh; height: 82%; margin-top: -0.7%"> <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"> <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> </div>
<h1 class="veryBig inline" style="width: inherit"> <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="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="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="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> --><span style="margin-left: 1%">[[USERNAME]]</span>
</h1> </h1>
<hr style="margin-bottom: 2%" class="profilePostHide"> <hr style="margin-bottom: 2%" class="profilePostHide">
<h3 id="collectibles" class="profilePostHide"> <h3 id="collectibles" class="profilePostHide">
<span id="stars">[[STARS]]</span> <img class="help valign" src="../assets/star.png" title="Stars"> <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="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="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="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="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="creatorpoints" style="display: none"><span id="cp">[[CP]]</span> <img class="help valign" src="/assets/cp.png" title="Creator Points"></span>
</h3> </h3>
<div class="lightBox center profilePostHide" id="iconsDiv" style="margin: 2% auto; width: 105vh"> <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=[[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=[[ROBOT]] iconForm="robot" col1="[[COL1]]" col2="[[COL2]]" glow="[[GLOW]]"></gdicon>
<gdicon iconID=[[SPIDER]] iconForm="spider" 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>
<div class="lightBox center dragscroll" id="statusDiv" normalHeight="36vh" compactHeight="69vh" style="margin: 2% auto; width: 105vh; height: 36vh; background-color: #BE6F3F"> <div class="lightBox center dragscroll" id="statusDiv" normalHeight="36vh" compactHeight="69vh" style="margin: 2% auto; width: 105vh; height: 36vh; background-color: #BE6F3F">
</div> </div>
<div class="center profilePostHide" style="margin: 1.5% auto 2.5% auto;"> <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> <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()"> <img src="/assets/friends.png" height="10%" id="friendButton" class="sideSpace gdButton" onclick="$('#settingsDiv').show()">
</div> </div>
<div style="position: absolute; bottom: 0%; left: 12%;" class="profilePostHide"> <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> <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> </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-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"> <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%">
<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%"> <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="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="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="twitch" style="display: none" target="_blank" href="https://twitch.tv/[[TWITCH]]"><img src="/assets/twitch.png" class="gdButton socialButton"></a>
</div> </div>
<div class="supercenter" style="left: 5%; top: 65%; height: 10%"> <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>
<div class="supercenter" style="left: 95%; top: 65%; height: 10%;"> <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>
<div id="postButton" style="position:absolute; bottom: 0%; left: 0%; width: 14%; text-align: left; transform: translate(-35%, 40%);"> <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>
</div> </div>
@ -153,9 +153,9 @@
</body> </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/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="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="/iconkit/icon.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 type="text/javascript" src="/misc/dragscroll.js"></script>
<script> <script>
@ -170,7 +170,7 @@ renderIcons()
// set favicon // set favicon
$('#mainIcon').on('DOMNodeInserted', 'img', function () { $('#mainIcon').on('DOMNodeInserted', 'img', function () {
$("link[rel='icon']").attr("href", $(this).attr("src")); $("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>.' 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) $('#message').html(messageText)
@ -211,14 +211,14 @@ if (`[[TWITTER]]` != "null") $('#twitter').show()
if (`[[TWITCH]]` != "null") $('#twitch').show() if (`[[TWITCH]]` != "null") $('#twitch').show()
let numRank = parseInt("[[RANK]]") let numRank = parseInt("[[RANK]]")
if (numRank < 2) $(".profileTrophy").attr("src","../assets/trophies/1.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 <= 10) $(".profileTrophy").attr("src","/assets/trophies/2.png")
else if (numRank <= 50 ) $(".profileTrophy").attr("src","../assets/trophies/3.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 <= 100) $(".profileTrophy").attr("src","/assets/trophies/4.png")
else if (numRank <= 200) $(".profileTrophy").attr("src","../assets/trophies/5.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 <= 500) $(".profileTrophy").attr("src","/assets/trophies/6.png")
else if (numRank <= 1000) $(".profileTrophy").attr("src","../assets/trophies/7.png") else if (numRank <= 1000) $(".profileTrophy").attr("src","/assets/trophies/7.png")
else $(".profileTrophy").attr("src","../assets/trophies/0.png") else $(".profileTrophy").attr("src","/assets/trophies/0.png")
let messages = "[[MESSAGES]]" let messages = "[[MESSAGES]]"
let commenthistory = "[[COMMENTHISTORY]]" 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 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>" 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') if (commenthistory == "friends") $('#commentButton').attr('src', '/assets/comments-yellow.png')
else if (commenthistory == "off") $('#commentButton').attr('src', '../assets/comments-grey.png') else if (commenthistory == "off") $('#commentButton').attr('src', '/assets/comments-grey.png')
else { else {
$('#commentButton').attr('src', '../assets/comments.png').attr('onclick', '') $('#commentButton').attr('src', '/assets/comments.png').attr('onclick', '')
$('#commentA').attr('href', '../comments/[[USERNAME]]') $('#commentA').attr('href', '../comments/[[USERNAME]]')
} }
@ -239,10 +239,10 @@ if (messages == "all") {
$('#msgA').attr('href', '../messages?sendTo=[[USERNAME]]') $('#msgA').attr('href', '../messages?sendTo=[[USERNAME]]')
} }
if (messages == "friends") $('#msgButton').attr('src', '../assets/messages-yellow.png') if (messages == "friends") $('#msgButton').attr('src', '/assets/messages-yellow.png')
else if (messages == "off") $('#msgButton').attr('src', '../assets/messages-grey.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() $('#userInfo').html($('#userInfo').html()
.replace("[[REQS]]", reqMode) .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)" : "")) .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() { function appendComments() {
if (loadingComments) return; if (loadingComments) return
else loadingComments = true; 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() if (page == 0) $('#pageDown').hide()
else $('#pageDown').show() 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() if (res.length != 10) $('#pageUp').hide()
else $('#pageUp').show() else $('#pageUp').show()
@ -277,7 +277,7 @@ function appendComments() {
</div> </div>
<p class="commentDate">${x.date}</p> <p class="commentDate">${x.date}</p>
<div class="commentLikes"> <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> <h3 class="inline">${x.likes}</h3><br>
</div> </div>
</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) }) .fail(e => { allowEsc = true; $('.postbutton').show(); $('#message').text(e.responseText.includes("DOCTYPE") ? "Something went wrong..." : e.responseText) })
}) })
let commentID = 0; let commentID = 0
let likeCount, likeImg; let likeCount, likeImg
let likedComments; let likedComments
let like = true; let like = true;
$('#likebtn').click(function() { $('#likebtn').click(function() {
@ -346,7 +346,7 @@ $(document).on('click', '.likeComment', function(cmnt) {
commentID = $(this).attr('commentID') commentID = $(this).attr('commentID')
likedComments = localStorage.likedComments ? JSON.parse(localStorage.likedComments) : [] likedComments = localStorage.likedComments ? JSON.parse(localStorage.likedComments) : []
if (likedComments.includes(commentID)) return; if (likedComments.includes(commentID)) return
lvID = $(this).attr('levelID') || 0 lvID = $(this).attr('levelID') || 0
likeImg = $(this).find('img') likeImg = $(this).find('img')
@ -356,7 +356,7 @@ $(document).on('click', '.likeComment', function(cmnt) {
$('#submitVote').click(function() { $('#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 ID = commentID
let username = $('#like-username').val() let username = $('#like-username').val()
@ -364,14 +364,14 @@ $('#submitVote').click(function() {
let extraID = lvID || window.location.pathname.split('/')[2] let extraID = lvID || window.location.pathname.split('/')[2]
let accountID = 0 let accountID = 0
let likeType = like ? "1" : "0" let likeType = like ? "1" : "0"
if (!ID || !username || !password || loadingComments) return $('#postComment').hide() if (!ID || !username || !password || loadingComments) return $('#postComment').hide()
$('#likeMessage').text(like ? "Liking..." : "Disliking... :(") $('#likeMessage').text(like ? "Liking..." : "Disliking... :(")
$('.postbutton').hide() $('.postbutton').hide()
allowEsc = false 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!")} if (!res || res == "-1") {allowEsc = true; $('.postbutton').show(); return $('#likeMessage').text("The username you provided doesn't exist!")}
else accountID = res.accountID else accountID = res.accountID
@ -379,8 +379,8 @@ $('#submitVote').click(function() {
.done(x => { .done(x => {
let newCount = parseInt(likeCount.text()) + (like ? 1 : -1) let newCount = parseInt(likeCount.text()) + (like ? 1 : -1)
likeCount.text(newCount) likeCount.text(newCount)
if (newCount < 0) likeImg.attr('src', '../assets/dislike.png').css('transform', compact ? 'translateY(15%)' : 'translateY(25%)') 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') else likeImg.attr('src', '/assets/like.png').removeAttr('style')
$('#likeComment').hide() $('#likeComment').hide()
$('#likebtn').trigger('click') $('#likebtn').trigger('click')
$('.postbutton').show() $('.postbutton').show()
@ -388,7 +388,7 @@ $('#submitVote').click(function() {
allowEsc = true allowEsc = true
likedComments.push(commentID) likedComments.push(commentID)
localStorage.setItem('likedComments', JSON.stringify(likedComments)) localStorage.setItem('likedComments', JSON.stringify(likedComments))
}) })
.fail(e => {allowEsc = true; $('.postbutton').show();$('#likeMessage').text(e.responseText.includes("DOCTYPE") ? "Something went wrong..." : e.responseText)}) .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() $('.profilePostHide').hide()
$('.statusIcon').show() $('.statusIcon').show()
$('#statusDiv').css('height', $('#statusDiv').attr('compactHeight')) $('#statusDiv').css('height', $('#statusDiv').attr('compactHeight'))
$(this).attr('src', "../assets/expanded-on.png") $(this).attr('src', "/assets/expanded-on.png")
} }
else { else {
$('.profilePostHide').show() $('.profilePostHide').show()
$('.statusIcon').hide() $('.statusIcon').hide()
$('#statusDiv').css('height', $('#statusDiv').attr('normalHeight')) $('#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) { $(document).keydown(function(k) {
if ($('#content').is(':visible')) { if ($('#content').is(':visible')) {
if (k.which == 13) k.preventDefault() //enter 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 if (k.which == 37 && $('#pageDown').is(":visible")) { //left
$('#pageDown').trigger('click') $('#pageDown').trigger('click')
@ -431,7 +431,7 @@ $(document).keydown(function(k) {
$('#pageUp').trigger('click') $('#pageUp').trigger('click')
} }
}) })
</script> </script>

View file

@ -1,9 +1,9 @@
<head> <head>
<title id="tabTitle">Level Search</title> <title id="tabTitle">Level Search</title>
<meta charset="utf-8"> <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> <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-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-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"> <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%"> <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> <h2 class="smaller center" style="font-size: 5.5vh; margin-top: 1%">Jump to Page</h2>
<input type="number" id="pageSelect" placeholder="1"><br> <input type="number" id="pageSelect" placeholder="1"><br>
<img src="../assets/ok.png" height=20%; id="pageJump" class="gdButton center closeWindow"> <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 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%;"> <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>
<div class="supercenter" style="left: 75%; top: 43%; height: 10%;"> <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> </div>
</div> </div>
@ -37,8 +37,8 @@
<p class="bigger center" style="line-height: 5vh; margin-top: 1.5vh;"> <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> Delete all saved online levels?<br><cy>Levels will be cleared from your browser.</cy>
</p> </p>
<img src="../assets/btn-cancel-green.png" height=25%; class="gdButton center closeWindow"> <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-delete.png" height=25%; id="purgeSaved" class="gdButton center sideSpaceB">
</div> </div>
</div> </div>
@ -49,22 +49,22 @@
A random level cannot be picked with your current <cy>search filters!</cy> 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>. 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> </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> </div>
<div style="position:absolute; bottom: 0%; left: 0%; width: 100%"> <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>
<div style="position:absolute; bottom: 0%; right: 0%; width: 100%; text-align: right;"> <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>
<div id="searchBox" class="supercenter dragscroll"> <div id="searchBox" class="supercenter dragscroll">
<div style="height: 4.5%"></div> <div style="height: 4.5%"></div>
</div> </div>
<div class="epicbox supercenter gs" style="width: 120vh; height: 80%; pointer-events: none;"></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%"> <div class="center" style="position:absolute; top: 8%; left: 0%; right: 0%">
@ -76,61 +76,62 @@
</div> </div>
<div title="Jump to page" style="text-align: right; position:absolute; top: 7.5%; right: 2%; height: 12%;"> <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>
<div id="shuffle" title="Random level" style="display: none; text-align: right; position:absolute; top: 7.5%; right: 6.5%; height: 12%;"> <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>
<div id="lastPage" title="Last page" style="display: none; text-align: right; position:absolute; top: 7.5%; right: 11%; height: 11%;"> <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>
<div style="position:absolute; top: 2%; left: 1.5%; width: 10%; height: 25%; pointer-events: none"> <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>
<div id="purge" style="position:absolute; bottom: 1%; right: -3%; width: 10%; display:none;"> <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>
<div style="position:absolute; bottom: 2%; right: 1%; text-align: right; width: 15%;"> <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>
<div style="position:absolute; bottom: 2%; right: 8.5%; text-align: right; width: 15%; display: none" id="gdWorld"> <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>
<div style="position:absolute; bottom: 2%; right: 8.5%; text-align: right; width: 15%; display: none" id="normalGD"> <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>
<div style="position: absolute; left: 7%; top: 45%; height: 10%;"> <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>
<div style="position: absolute; right: 7%; top: 45%; height: 10%;"> <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>
<div class="supercenter" id="loading" style="height: 10%; top: 47%; display: none;"> <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>
</div> </div>
</body> </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/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 type="text/javascript" src="../dragscroll.js"></script>
<script> <script>
"use strict";
$('#pageDown').hide() $('#pageDown').hide()
$('#pageUp').hide() $('#pageUp').hide()
let accID; let accID
let path = location.pathname.replace('/search/', "") let path = location.pathname.replace('/search/', "")
let url = new URL(window.location.href) let url = new URL(window.location.href)
let gauntlet = url.searchParams.get('gauntlet') let gauntlet = url.searchParams.get('gauntlet')
@ -140,7 +141,7 @@ let list = url.searchParams.get('list')
let count = url.searchParams.get('count') let count = url.searchParams.get('count')
let header = url.searchParams.get('header') let header = url.searchParams.get('header')
let demonList = ["demonList", "demonlist"].some(x => typeof url.searchParams.get(x) == "string" || type == x) 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 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 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 pageCache = {}
let demonListLink = "https://pointercrate.com/" 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") { if (type == "followed") {
let followed = localStorage.followed ? JSON.parse(localStorage.followed) : [] let followed = localStorage.followed ? JSON.parse(localStorage.followed) : []
searchFilters += ("&creators=" + followed.join()) searchFilters += ("&creators=" + followed.join())
} }
let hostMatch = window.location.host.match(/\./g) let hostMatch = window.location.host.match(/\./g)
if (hostMatch && hostMatch.length > 1) { // gdps check if (hostMatch?.length > 1) { // gdps check
$('#gdWorld').remove() $('#gdWorld').remove()
$('#normalGD').remove() $('#normalGD').remove()
} }
function Append(firstLoad, noCache) { function Append(firstLoad, noCache) {
loading = true; loading = true
if (!firstLoad) $('#pagenum').text(`Page ${(page + 1)}${pages ? ` of ${pages}` : ""}`) if (!firstLoad) $('#pagenum').text(`Page ${(page + 1)}${pages ? ` of ${pages}` : ""}`)
$('#searchBox').html('<div style="height: 4.5%"></div>') $('#searchBox').html('<div style="height: 4.5%"></div>')
$('#pageSelect').val(page + 1) $('#pageSelect').val(page + 1)
@ -207,7 +210,7 @@ function Append(firstLoad, noCache) {
if (demonList) { if (demonList) {
demonListLink = res[0].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) => { 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}"><!-- <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><!-- -->${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}"><!-- --><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>`}<!-- --><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.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%">` : ''} -->${x.twoPlayer ? `<img class="help valign sideSpaceD" title="Two player level" src="/assets/twoPlayer.png" height="12%">` : ''}
</h2> </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 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=""> <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="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="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} <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}` : ""} ${x.orbs != 0 ? `<img class="help valign rightSpace" title="Mana Orbs" src="/assets/orbs.png" height="14%">${x.orbs}` : ""}
</h3> </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%;"> <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> <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>` : ""} ${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)"> <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 > 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 > 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 > 2 ? `<img src="/assets/${x.verifiedCoins ? 'silver' : 'brown'}coin.png" height="50%" class="help squeezeB">` : ""}
</div> </div>
</div> </div>
<div class="center" style="position:absolute; right: 7%; transform:translateY(-${demonList ? 19.5 : 16.25}vh); height: 10%"> <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> <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> ${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 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> <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>
</div>`) </div>`)
@ -336,31 +339,26 @@ if (!$('#header').text() && typeof userMode != "string") {
$('.closeWindow').click(function() {$(".popup").attr('style', 'display: none;')}) $('.closeWindow').click(function() {$(".popup").attr('style', 'display: none;')})
$('#purgeSaved').click(function() { $('#purgeSaved').click(function() {
localStorage.removeItem('saved'); localStorage.removeItem('saved')
location.reload() location.reload()
}) })
var max = 9999 function onPageSel(that) {
var min = 1 let x = $(that).val()
if (x != "") $(that).val(clamp(Math.floor(x), 1, 9999))
}
$('#pageSelect').on('input', function () { $('#pageSelect').on('input', function() {onPageSel(this)})
var x = $(this).val(); $('#pageSelect').on('blur', function() {onPageSel(this)})
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));
});
$('#shuffle').click(function() { $('#shuffle').click(function() {
if (superSearch) { if (superSearch) {
$('#searchBox').html('<div style="height: 4.5%"></div>') $('#searchBox').html('<div style="height: 4.5%"></div>')
$('#loading').show() $('#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 let mostRecent = recent[0].id
function fetchRandom() { 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() if (res == "-1" || !res.id) return fetchRandom()
else window.location.href = "../" + res.id else window.location.href = "../" + res.id
}) })
@ -371,7 +369,7 @@ $('#shuffle').click(function() {
else if (pages) { else if (pages) {
let random = {} let random = {}
let pageCount = +count || 10 let pageCount = +count || 10
randomResult = Math.floor(Math.random() * (results)) + 1 randomResult = randInt(0, results) + 1
randomPage = Math.ceil(randomResult / pageCount) randomPage = Math.ceil(randomResult / pageCount)
randomIndex = randomResult % pageCount randomIndex = randomResult % pageCount
if (randomIndex == 0) randomIndex = pageCount if (randomIndex == 0) randomIndex = pageCount
@ -385,11 +383,11 @@ $('#shuffle').click(function() {
}) })
$(document).keydown(function(k) { $(document).keydown(function(k) {
if (loading) return; if (loading) return
if ($('#pageDiv').is(':visible')) { if ($('#pageDiv').is(':visible')) {
if (k.which == 13) $('#pageJump').trigger('click') //enter if (k.which == 13) $('#pageJump').trigger('click') //enter
else return; else return
} }
if (k.which == 37 && $('#pageDown').is(":visible")) $('#pageDown').trigger('click') // left 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": 7, "type": "treasureRoom", "keys": 1 },
{ "form": "spider", "id": 10, "type": "gauntlet", "gauntlet": "Demon" }, { "form": "spider", "id": 10, "type": "gauntlet", "gauntlet": "Demon" },
{ "form": "spider", "id": 17, "type": "treasureRoom", "keys": 5 } { "form": "spider", "id": 17, "type": "treasureRoom", "keys": 5 }
] ]

View file

@ -1,3 +1,4 @@
"use strict";
const WHITE = 0xffffff const WHITE = 0xffffff
const colorNames = { "1": "Color 1", "2": "Color 2", "g": "Glow", "w": "White", "u": "UFO Dome" } 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" } const formNames = { "player": "icon", "player_ball": "ball", "bird": "ufo", "dart": "wave" }
@ -5,10 +6,19 @@ const loader = PIXI.Loader.shared
const loadedNewIcons = {} 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) { function positionPart(part, partIndex, layer, formName, isNew, isGlow) {
layer.position.x += (part.pos[0] * positionMultiplier * (isNew ? 0.5 : 1)) let truePosMultiplier = positionMultiplier / (isNew ? 2 : 1)
layer.position.y -= (part.pos[1] * positionMultiplier * (isNew ? 0.5 : 1)) layer.position.x += (part.pos[0] * truePosMultiplier)
layer.position.y -= (part.pos[1] * truePosMultiplier)
layer.scale.x = part.scale[0] layer.scale.x = part.scale[0]
layer.scale.y = part.scale[1] layer.scale.y = part.scale[1]
if (part.flipped[0]) layer.scale.x *= -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 tintInfo = iconData.robotAnimations.info[formName].tints
let foundTint = tintInfo[partIndex] let foundTint = tintInfo[partIndex]
if (foundTint > 0) { if (foundTint > 0) {
let darkenFilter = new PIXI.filters.ColorMatrixFilter(); let darkenFilter = new PIXI.filters.ColorMatrixFilter()
darkenFilter.brightness(0) darkenFilter.brightness(0)
darkenFilter.alpha = (255 - foundTint) / 255 darkenFilter.alpha = 1 - foundTint / 255
layer.filters = [darkenFilter] layer.filters = [darkenFilter]
} }
} }
} }
function validNum(val, defaultVal) { function sanitizeNum(val, defaultVal) {
let colVal = +val let colVal = +val
return isNaN(colVal) ? defaultVal : colVal // yes, it also checks for NaN
return isFinite(colVal) ? colVal : defaultVal
} }
function getGlowColor(colors) { function getGlowColor(colors) {
@ -39,17 +50,17 @@ function getGlowColor(colors) {
return glowCol return glowCol
} }
function validateIconID(id, form) { function sanitizeIconID(id, form) {
let realID = Math.min(iconData.newIconCounts[form], Math.abs(validNum(id, 1))) let realID = Math.min(iconData.newIconCounts[form], Math.abs(sanitizeNum(id, 1)))
if (realID == 0 && !["player", "player_ball"].includes(form)) realID = 1 if (realID == 0 && !["player", "player_ball"].includes(form)) realID = 1
return realID return realID
} }
function parseIconColor(col) { function parseIconColor(col) {
if (!col) return WHITE 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] let rgb = iconData.colors[col]
return rgb ? rgbToDecimal(rgb) : WHITE; return rgb ? rgb2Pac(rgb) : WHITE
} }
function parseIconForm(form) { function parseIconForm(form) {
@ -58,7 +69,7 @@ function parseIconForm(form) {
} }
function loadIconLayers(form, id, cb) { 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 + "_")) let texturesToLoad = Object.keys(iconData.gameSheet).filter(x => x.startsWith(iconStr + "_"))
if (loadedNewIcons[texturesToLoad[0]]) return cb(loader, loader.resources, true) 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) 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 loader.load(cb) // no params
} }
@ -80,11 +94,11 @@ function loadNewIcon(iconStr, cb) {
loader.add({ name: sheetName, url: `/iconkit/newicons/${iconStr}-hd.png` }) loader.add({ name: sheetName, url: `/iconkit/newicons/${iconStr}-hd.png` })
loader.load((l, resources) => { loader.load((l, resources) => {
let texture = resources[sheetName].texture let texture = resources[sheetName].texture
Object.keys(data).forEach(x => { Object.keys(data).forEach(k => {
let bounds = data[x] let bounds = data[k]
let textureRect = new PIXI.Rectangle(bounds.pos[0], bounds.pos[1], bounds.size[0], bounds.size[1]) let textureRect = new PIXI.Rectangle(bounds.pos[0], bounds.pos[1], bounds.size[0], bounds.size[1])
let partTexture = new PIXI.Texture(texture, textureRect) let partTexture = new PIXI.Texture(texture, textureRect)
loadedNewIcons[x] = partTexture loadedNewIcons[k] = partTexture
}) })
cb(l, resources, true) cb(l, resources, true)
}) })
@ -104,23 +118,23 @@ function parseNewPlist(data) {
iconData.gameSheet[frameName] = {} iconData.gameSheet[frameName] = {}
positionData[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 keyName = frameData[n].innerHTML
let keyData = frameData[n + 1].innerHTML let keyData = frameData[n + 1].innerHTML
if (["spriteOffset", "spriteSize", "spriteSourceSize"].includes(keyName)) { switch (keyName) {
iconData.gameSheet[frameName][keyName] = parseWeirdArray(keyData) 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() if (isRotated) positionData[frameName].size.reverse()
@ -129,35 +143,37 @@ function parseNewPlist(data) {
return positionData return positionData
} }
function parseWeirdArray(data) { let parseWeirdArray = data => data.replace(/[^\d,-]/g, "").split(",").map(x => +x)
return data.replace(/[^0-9,-]/g, "").split(",").map(x => +x)
}
function padZero(num) { let padZero = num => num.toString().padStart(2, "0")
let numStr = num.toString()
if (num < 10) numStr = "0" + numStr
return numStr
}
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 { class Icon {
constructor(data={}, cb) { constructor(data={}, cb) {
this.app = data.app this.app = data.app
this.sprite = new PIXI.Container(); this.sprite = new PIXI.Container()
this.form = data.form || "player" this.form = data.form || "player"
this.id = validateIconID(data.id, this.form) this.id = sanitizeIconID(data.id, this.form)
this.new = !!data.new this.new = !!data.new
this.colors = { this.colors = {
"1": validNum(data.col1, 0xafafaf), // primary "1": sanitizeNum(data.col1, 0xafafaf), // primary
"2": validNum(data.col2, WHITE), // secondary "2": sanitizeNum(data.col2, WHITE), // secondary
"g": validNum(data.colG, validNum(+data.colg, null)), // glow "g": sanitizeNum(data.colG, sanitizeNum(data.colg, null)), // glow
"w": validNum(data.colW, validNum(+data.colw, WHITE)), // white "w": sanitizeNum(data.colW, sanitizeNum(data.colw, WHITE)), // white
"u": validNum(data.colU, validNum(+data.colu, WHITE)), // ufo "u": sanitizeNum(data.colU, sanitizeNum(data.colu, WHITE)), // ufo
} }
this.glow = !!data.glow this.glow = !!data.glow
this.layers = [] this.layers = []
this.glowLayers = [] this.glowLayers = []
@ -178,22 +194,24 @@ class Icon {
let idlePosition = this.getAnimation(data.animation, data.animationForm).frames[0] let idlePosition = this.getAnimation(data.animation, data.animationForm).frames[0]
idlePosition.forEach((x, y) => { idlePosition.forEach((x, y) => {
x.name = iconData.robotAnimations.info[this.form].names[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 }) 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) 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 }) 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) positionPart(x, y, glowPart.sprite, this.form, this.new, true)
glowPart.sprite.visible = this.glow glowPart.sprite.visible = this.glow
this.glowLayers.push(glowPart) this.glowLayers.push(glowPart)
this.layers.push(part) this.layers.push(part)
this.sprite.addChild(part.sprite) this.sprite.addChild(part.sprite)
}) })
let fullGlow = new PIXI.Container(); let fullGlow = new PIXI.Container()
this.glowLayers.forEach(x => fullGlow.addChild(x.sprite)) this.glowLayers.forEach(x => fullGlow.addChild(x.sprite))
this.sprite.addChildAt(fullGlow, 0) 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) this.animationSpeed = Math.abs(Number(data.animationSpeed) || 1)
if (data.animation) this.setAnimation(data.animation, data.animationForm) if (data.animation) this.setAnimation(data.animation, data.animationForm)
} }
@ -209,14 +227,16 @@ class Icon {
getAllLayers() { getAllLayers() {
let allLayers = []; 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 return allLayers
} }
setColor(colorType, newColor, extra={}) { setColor(colorType, newColor, extra={}) {
let colorStr = String(colorType).toLowerCase() let colorStr = String(colorType).toLowerCase()
if (!colorType || !Object.keys(this.colors).includes(colorStr)) return if (!colorType || !this.colors.hasOwnProperty(colorStr)) return
else this.colors[colorStr] = newColor this.colors[colorStr] = newColor
let newGlow = getGlowColor(this.colors) let newGlow = getGlowColor(this.colors)
this.getAllLayers().forEach(x => { this.getAllLayers().forEach(x => {
if (colorType != "g" && x.colorType == colorStr) x.setColor(newColor) if (colorType != "g" && x.colorType == colorStr) x.setColor(newColor)
@ -228,13 +248,9 @@ class Icon {
} }
} }
formName() { formName() { return formNames[this.form] || this.form }
return formNames[this.form] || this.form
}
isGlowing() { isGlowing() { return this.glowLayers[0].sprite.visible }
return this.glowLayers[0].sprite.visible
}
setGlow(toggle) { setGlow(toggle) {
this.glow = !!toggle this.glow = !!toggle
@ -258,7 +274,7 @@ class Icon {
animData.frames[this.animationFrame].forEach((newPart, index) => { animData.frames[this.animationFrame].forEach((newPart, index) => {
let section = this.layers[index] let section = this.layers[index]
let glowSection = this.glowLayers[index] let glowSection = this.glowLayers[index]
let truePosMultiplier = this.new ? positionMultiplier * 0.5 : positionMultiplier let truePosMultiplier = positionMultiplier / (this.new ? 2 : 1)
if (!section) return if (!section) return
// gd is weird with negative rotations // gd is weird with negative rotations
@ -270,16 +286,16 @@ class Icon {
y: newPart.pos[1] * truePosMultiplier * -1, y: newPart.pos[1] * truePosMultiplier * -1,
scaleX: newPart.scale[0], scaleX: newPart.scale[0],
scaleY: newPart.scale[1], scaleY: newPart.scale[1],
rotation: realRot * (Math.PI / 180) // radians rotation: toRadians(realRot)
} }
if (newPart.flipped[0]) movementData.scaleX *= -1 if (newPart.flipped[0]) movementData.scaleX *= -1
if (newPart.flipped[1]) movementData.scaleY *= -1 if (newPart.flipped[1]) movementData.scaleY *= -1
let bothSections = [section, glowSection] let bothSections = [section, glowSection]
bothSections.forEach((x, y) => { 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 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++ this.animationFrame++
if (this.animationFrame >= animData.frames.length) { if (this.animationFrame >= animData.frames.length) {
if (animData.info.loop) this.animationFrame = 0 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) // find actual icon size by reading pixel data (otherwise there's whitespace and shit)
if (this.new) this.sprite.scale.set(1) if (this.new) this.sprite.scale.set(1)
let spriteSize = [Math.round(this.sprite.width), Math.round(this.sprite.height)] 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 xRange = [spriteSize[0], 0]
let yRange = [spriteSize[1], 0] let yRange = [spriteSize[1], 0]
@ -316,7 +332,7 @@ class Icon {
// this took hours to figure out. i fucking hate my life // this took hours to figure out. i fucking hate my life
xRange[1]++ xRange[1]++
yRange[1]++ yRange[1]++
let realWidth = xRange[1] - xRange[0] let realWidth = xRange[1] - xRange[0]
let realHeight = yRange[1] - yRange[0] let realHeight = yRange[1] - yRange[0]
@ -336,35 +352,35 @@ class Icon {
toDataURL(dataType="image/png") { toDataURL(dataType="image/png") {
this.autocrop() this.autocrop()
this.app.renderer.render(this.app.stage); this.app.renderer.render(this.app.stage)
let b64data = this.app.view.toDataURL(dataType); let b64data = this.app.view.toDataURL(dataType)
this.revertCrop() this.revertCrop()
return b64data return b64data
} }
pngExport() { pngExport() {
let b64data = this.toDataURL() let b64data = this.toDataURL()
let downloader = document.createElement('a'); let downloader = document.createElement("a")
downloader.href = b64data downloader.href = b64data
downloader.setAttribute("download", `${this.formName()}_${this.id}.png`); downloader.setAttribute("download", `${this.formName()}_${this.id}.png`)
document.body.appendChild(downloader); document.body.appendChild(downloader)
downloader.click(); downloader.click()
document.body.removeChild(downloader); document.body.removeChild(downloader)
} }
copyToClipboard() { copyToClipboard() {
this.autocrop() this.autocrop()
this.app.renderer.render(app.stage); this.app.renderer.render(app.stage)
this.app.view.toBlob(blob => { this.app.view.toBlob(blob => {
let item = new ClipboardItem({ "image/png": blob }); let item = new ClipboardItem({ "image/png": blob })
navigator.clipboard.write([item]); navigator.clipboard.write([item])
}); })
this.revertCrop() this.revertCrop()
} }
psdExport() { 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() let glowing = this.isGlowing()
this.setGlow(true) this.setGlow(true)
@ -376,10 +392,10 @@ class Icon {
function addPSDLayer(layer, parent, sprite) { function addPSDLayer(layer, parent, sprite) {
allLayers.forEach(x => x.sprite.alpha = 0) allLayers.forEach(x => x.sprite.alpha = 0)
layer.sprite.alpha = 255 layer.sprite.alpha = 255
let layerChild = { name: layer.colorName, canvas: renderer.plugins.extract.canvas(sprite) } let layerChild = { name: layer.colorName, canvas: renderer.plugins.extract.canvas(sprite) }
if (layer.colorType == "g") { 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" else layerChild.blendMode = "linear dodge"
if (!complex && !glowing) layerChild.hidden = true if (!complex && !glowing) layerChild.hidden = true
} }
@ -404,13 +420,13 @@ class Icon {
allLayers.forEach(x => x.sprite.alpha = 255) allLayers.forEach(x => x.sprite.alpha = 255)
let output = agPsd.writePsd(psd) let output = agPsd.writePsd(psd)
let blob = new Blob([output]); let blob = new Blob([output])
let downloader = document.createElement('a'); let downloader = document.createElement("a")
downloader.href = URL.createObjectURL(blob); downloader.href = URL.createObjectURL(blob)
downloader.setAttribute("download", `${this.formName()}_${this.id}.psd`); downloader.setAttribute("download", `${this.formName()}_${this.id}.psd`)
document.body.appendChild(downloader); document.body.appendChild(downloader)
downloader.click(); downloader.click()
document.body.removeChild(downloader); document.body.removeChild(downloader)
this.setGlow(glowing) this.setGlow(glowing)
} }
} }
@ -421,11 +437,11 @@ class IconPart {
if (colors[1] == 0 && !misc.skipGlow) glow = true // add glow if p1 is black if (colors[1] == 0 && !misc.skipGlow) glow = true // add glow if p1 is black
let iconPath = `${form}_${padZero(id)}` let iconPath = `${form}_${padZero(id)}`
let partString = misc.part ? "_" + padZero(misc.part.part) : "" let partString = misc.part ? `_${padZero(misc.part.part)}` : ""
let sections = {} let sections = {}
if (misc.part) this.part = misc.part if (misc.part) this.part = misc.part
this.sprite = new PIXI.Container(); this.sprite = new PIXI.Container()
this.sections = [] this.sections = []
if (!misc.skipGlow) { if (!misc.skipGlow) {
@ -448,11 +464,22 @@ class IconPart {
} }
} }
let layerOrder = ["glow", "ufo", "col2", "col1", "white"].map(x => sections[x]).filter(x => x) let layerOrder = ["glow", "ufo", "col2", "col1", "white"]
layerOrder.forEach(x => { for (let x of layerOrder) {
x = sections[x]
if (!x) continue
this.sections.push(x) this.sections.push(x)
this.sprite.addChild(x.sprite) 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) { setColor(color) {
this.color = validNum(color, WHITE) this.color = sanitizeNum(color, WHITE)
this.sprite.tint = this.color 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"; "use strict";
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.readAbr = void 0; exports.readAbr = void 0;
@ -1189,7 +1189,7 @@ addHandler('Anno', function (target) { return target.annotations !== undefined;
var sound = annotation.type === 'sound'; var sound = annotation.type === 'sound';
if (sound && !(annotation.data instanceof Uint8Array)) if (sound && !(annotation.data instanceof Uint8Array))
throw new Error('Sound annotation data should be 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'); throw new Error('Text annotation data should be string');
var lengthOffset = writer.offset; var lengthOffset = writer.offset;
psdWriter_1.writeUint32(writer, 0); // length 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'); return ('Wdth' in value) ? 'Objc' : (('units' in value) ? 'UntF' : 'doub');
} }
else if (key === 'Type') { else if (key === 'Type') {
return typeof value === 'string' ? 'enum' : 'long'; return typeof value == 'string' ? 'enum' : 'long';
} }
else if (key === 'AntA') { 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') { 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') { else if (key === 'Vrsn') {
return typeof value === 'number' ? 'long' : 'Objc'; return typeof value == 'number' ? 'long' : 'Objc';
} }
else if (key === 'Rd ' || key === 'Grn ' || key === 'Bl ') { else if (key === 'Rd ' || key === 'Grn ' || key === 'Bl ') {
return root === 'artd' ? 'long' : 'doub'; return root === 'artd' ? 'long' : 'doub';
@ -3390,7 +3390,7 @@ function writeReferenceStructure(writer, _key, items) {
for (var i = 0; i < items.length; i++) { for (var i = 0; i < items.length; i++) {
var value = items[i]; var value = items[i];
var type = 'unknown'; var type = 'unknown';
if (typeof value === 'string') { if (typeof value == 'string') {
if (/^[a-z]+\.[a-z]+$/i.test(value)) { if (/^[a-z]+\.[a-z]+$/i.test(value)) {
type = 'Enmr'; type = 'Enmr';
} }
@ -3485,7 +3485,7 @@ function parseUnits(_a) {
exports.parseUnits = parseUnits; exports.parseUnits = parseUnits;
function parseUnitsOrNumber(value, units) { function parseUnitsOrNumber(value, units) {
if (units === void 0) { units = 'Pixels'; } if (units === void 0) { units = 'Pixels'; }
if (typeof value === 'number') if (typeof value == 'number')
return { value: value, units: units }; return { value: value, units: units };
return parseUnits(value); return parseUnits(value);
} }
@ -3508,10 +3508,10 @@ exports.unitsPercent = unitsPercent;
function unitsValue(x, key) { function unitsValue(x, key) {
if (x == null) if (x == null)
return { units: 'Pixels', value: 0 }; 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)"); throw new Error("Invalid value: " + JSON.stringify(x) + " (key: " + key + ") (should have value and units)");
var units = x.units, value = x.value; 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 + ")"); throw new Error("Invalid value in " + JSON.stringify(x) + " (key: " + key + ")");
if (units !== 'Pixels' && units !== 'Millimeters' && units !== 'Points' && units !== 'None' && if (units !== 'Pixels' && units !== 'Millimeters' && units !== 'Points' && units !== 'None' &&
units !== 'Picas' && units !== 'Inches' && units !== 'Centimeters' && units !== 'Density') { units !== 'Picas' && units !== 'Inches' && units !== 'Centimeters' && units !== 'Density') {
@ -4009,7 +4009,7 @@ function parseEngineData(data) {
if (!stack.length) if (!stack.length)
throw new Error('Invalid data'); throw new Error('Invalid data');
var top = stack[stack.length - 1]; var top = stack[stack.length - 1];
if (typeof top === 'string') { if (typeof top == 'string') {
stack[stack.length - 2][top] = value; stack[stack.length - 2][top] = value;
pop(); pop();
} }
@ -4024,7 +4024,7 @@ function parseEngineData(data) {
if (!stack.length) if (!stack.length)
pushContainer({}); pushContainer({});
var top = stack[stack.length - 1]; var top = stack[stack.length - 1];
if (top && typeof top === 'string') { if (top && typeof top == 'string') {
if (name === 'nil') { if (name === 'nil') {
pushValue(null); pushValue(null);
} }
@ -4032,7 +4032,7 @@ function parseEngineData(data) {
pushValue("/" + name); pushValue("/" + name);
} }
} }
else if (top && typeof top === 'object') { else if (top && typeof top == 'object') {
stack.push(name); stack.push(name);
} }
else { else {
@ -4196,15 +4196,15 @@ function serializeEngineData(data, condensed) {
writePrefix(); writePrefix();
writeString(condensed ? '/nil' : 'null'); writeString(condensed ? '/nil' : 'null');
} }
else if (typeof value === 'number') { else if (typeof value == 'number') {
writePrefix(); writePrefix();
writeString(serializeNumber(value, key)); writeString(serializeNumber(value, key));
} }
else if (typeof value === 'boolean') { else if (typeof value == 'boolean') {
writePrefix(); writePrefix();
writeString(value ? 'true' : 'false'); writeString(value ? 'true' : 'false');
} }
else if (typeof value === 'string') { else if (typeof value == 'string') {
writePrefix(); writePrefix();
if ((key === '99' || key === '98') && value.charAt(0) === '/') { if ((key === '99' || key === '98') && value.charAt(0) === '/') {
writeString(value); writeString(value);
@ -4223,7 +4223,7 @@ function serializeEngineData(data, condensed) {
} }
else if (Array.isArray(value)) { else if (Array.isArray(value)) {
writePrefix(); writePrefix();
if (value.every(function (x) { return typeof x === 'number'; })) { if (value.every(function (x) { return typeof x == 'number'; })) {
writeString('['); writeString('[');
var intArray = intArrays.indexOf(key) !== -1; var intArray = intArrays.indexOf(key) !== -1;
for (var _i = 0, value_1 = value; _i < value_1.length; _i++) { for (var _i = 0, value_1 = value; _i < value_1.length; _i++) {
@ -4247,7 +4247,7 @@ function serializeEngineData(data, condensed) {
writeString(']'); writeString(']');
} }
} }
else if (typeof value === 'object') { else if (typeof value == 'object') {
if (inProperty && !condensed) if (inProperty && !condensed)
writeString('\n'); writeString('\n');
writeIndent(); writeIndent();
@ -4266,7 +4266,7 @@ function serializeEngineData(data, condensed) {
return undefined; return undefined;
} }
if (condensed) { if (condensed) {
if (typeof data === 'object') { if (typeof data == 'object') {
for (var _i = 0, _a = getKeys(data); _i < _a.length; _i++) { for (var _i = 0, _a = getKeys(data); _i < _a.length; _i++) {
var key = _a[_i]; var key = _a[_i];
writeProperty(key, data[key]); writeProperty(key, data[key]);
@ -4560,7 +4560,7 @@ var createImageData = function (width, height) {
return tempCanvas.getContext('2d').createImageData(width, height); return tempCanvas.getContext('2d').createImageData(width, height);
}; };
exports.createImageData = createImageData; exports.createImageData = createImageData;
if (typeof document !== 'undefined') { if (typeof document != 'undefined') {
exports.createCanvas = function (width, height) { exports.createCanvas = function (width, height) {
var canvas = document.createElement('canvas'); var canvas = document.createElement('canvas');
canvas.width = width; canvas.width = width;
@ -5225,7 +5225,7 @@ function writePsdUint8Array(psd, options) {
} }
exports.writePsdUint8Array = writePsdUint8Array; exports.writePsdUint8Array = writePsdUint8Array;
function writePsdBuffer(psd, options) { function writePsdBuffer(psd, options) {
if (typeof Buffer === 'undefined') { if (typeof Buffer == 'undefined') {
throw new Error('Buffer not supported on this platform'); throw new Error('Buffer not supported on this platform');
} }
return Buffer.from(writePsdUint8Array(psd, options)); return Buffer.from(writePsdUint8Array(psd, options));
@ -7050,7 +7050,7 @@ function deduplicateValues(base, runs, keys) {
if (Array.isArray(value)) { if (Array.isArray(value)) {
identical = runs.every(function (r) { return arraysEqual(r.style[key], 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); }); identical = runs.every(function (r) { return objectsEqual(r.style[key], value); });
} }
else { else {
@ -7068,7 +7068,7 @@ function deduplicateValues(base, runs, keys) {
if (Array.isArray(value)) { if (Array.isArray(value)) {
same = arraysEqual(r.style[key], value); same = arraysEqual(r.style[key], value);
} }
else if (typeof value === 'object') { else if (typeof value == 'object') {
same = objectsEqual(r.style[key], value); same = objectsEqual(r.style[key], value);
} }
else { else {
@ -7514,7 +7514,7 @@ function decodeString(value) {
throw Error("Lone surrogate U+" + code.toString(16).toUpperCase() + " is not a scalar 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 byte2 = continuationByte(value, i++);
var byte3 = continuationByte(value, i++); var byte3 = continuationByte(value, i++);
var byte4 = continuationByte(value, i++); var byte4 = continuationByte(value, i++);
@ -7539,7 +7539,7 @@ exports.decodeString = decodeString;
},{}],15:[function(require,module,exports){ },{}],15:[function(require,module,exports){
'use strict' "use strict";
exports.byteLength = byteLength exports.byteLength = byteLength
exports.toByteArray = toByteArray exports.toByteArray = toByteArray
@ -7547,7 +7547,7 @@ exports.fromByteArray = fromByteArray
var lookup = [] var lookup = []
var revLookup = [] var revLookup = []
var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array var Arr = typeof Uint8Array != 'undefined' ? Uint8Array : Array
var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
for (var i = 0, len = code.length; i < len; ++i) { for (var i = 0, len = code.length; i < len; ++i) {
@ -7700,7 +7700,7 @@ function fromByteArray (uint8) {
*/ */
/* eslint-disable no-proto */ /* eslint-disable no-proto */
'use strict' "use strict";
var base64 = require('base64-js') var base64 = require('base64-js')
var ieee754 = require('ieee754') var ieee754 = require('ieee754')
@ -7728,8 +7728,8 @@ exports.kMaxLength = K_MAX_LENGTH
*/ */
Buffer.TYPED_ARRAY_SUPPORT = typedArraySupport() Buffer.TYPED_ARRAY_SUPPORT = typedArraySupport()
if (!Buffer.TYPED_ARRAY_SUPPORT && typeof console !== 'undefined' && if (!Buffer.TYPED_ARRAY_SUPPORT && typeof console != 'undefined' &&
typeof console.error === 'function') { typeof console.error == 'function') {
console.error( console.error(
'This browser lacks typed array (Uint8Array) support which is required by ' + 'This browser lacks typed array (Uint8Array) support which is required by ' +
'`buffer` v5.x. Use `buffer` v4.x if you require old browser support.' '`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) { function Buffer (arg, encodingOrOffset, length) {
// Common case. // Common case.
if (typeof arg === 'number') { if (typeof arg == 'number') {
if (typeof encodingOrOffset === 'string') { if (typeof encodingOrOffset == 'string') {
throw new TypeError( throw new TypeError(
'The "string" argument must be of type string. Received type number' '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 // 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) { Buffer[Symbol.species] === Buffer) {
Object.defineProperty(Buffer, Symbol.species, { Object.defineProperty(Buffer, Symbol.species, {
value: null, value: null,
@ -7810,7 +7810,7 @@ if (typeof Symbol !== 'undefined' && Symbol.species != null &&
Buffer.poolSize = 8192 // not used by this implementation Buffer.poolSize = 8192 // not used by this implementation
function from (value, encodingOrOffset, length) { function from (value, encodingOrOffset, length) {
if (typeof value === 'string') { if (typeof value == 'string') {
return fromString(value, encodingOrOffset) return fromString(value, encodingOrOffset)
} }
@ -7830,7 +7830,7 @@ function from (value, encodingOrOffset, length) {
return fromArrayBuffer(value, encodingOrOffset, length) return fromArrayBuffer(value, encodingOrOffset, length)
} }
if (typeof value === 'number') { if (typeof value == 'number') {
throw new TypeError( throw new TypeError(
'The "value" argument must not be of type number. Received type number' '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) var b = fromObject(value)
if (b) return b if (b) return b
if (typeof Symbol !== 'undefined' && Symbol.toPrimitive != null && if (typeof Symbol != 'undefined' && Symbol.toPrimitive != null &&
typeof value[Symbol.toPrimitive] === 'function') { typeof value[Symbol.toPrimitive] == 'function') {
return Buffer.from( return Buffer.from(
value[Symbol.toPrimitive]('string'), encodingOrOffset, length value[Symbol.toPrimitive]('string'), encodingOrOffset, length
) )
@ -7875,7 +7875,7 @@ Buffer.prototype.__proto__ = Uint8Array.prototype
Buffer.__proto__ = Uint8Array Buffer.__proto__ = Uint8Array
function assertSize (size) { function assertSize (size) {
if (typeof size !== 'number') { if (typeof size != 'number') {
throw new TypeError('"size" argument must be of type number') throw new TypeError('"size" argument must be of type number')
} else if (size < 0) { } else if (size < 0) {
throw new RangeError('The value "' + size + '" is invalid for option "size"') throw new RangeError('The value "' + size + '" is invalid for option "size"')
@ -7884,18 +7884,14 @@ function assertSize (size) {
function alloc (size, fill, encoding) { function alloc (size, fill, encoding) {
assertSize(size) assertSize(size)
if (size <= 0) { return (size <= 0 || fill === undefined
return createBuffer(size) ? createBuffer(size)
} :
if (fill !== undefined) {
// Only pay attention to encoding if it's a string. This // Only pay attention to encoding if it's a string. This
// prevents accidentally sending in a number that would // prevents accidentally sending in a number that would
// be interpretted as a start offset. // be interpretted as a start offset.
return typeof encoding === 'string' createBuffer(size).fill(fill, typeof encoding == 'string' ? encoding : undefined)
? createBuffer(size).fill(fill, encoding) )
: createBuffer(size).fill(fill)
}
return createBuffer(size)
} }
/** /**
@ -7925,7 +7921,7 @@ Buffer.allocUnsafeSlow = function (size) {
} }
function fromString (string, encoding) { function fromString (string, encoding) {
if (typeof encoding !== 'string' || encoding === '') { if (typeof encoding != 'string' || encoding === '') {
encoding = 'utf8' encoding = 'utf8'
} }
@ -7994,7 +7990,7 @@ function fromObject (obj) {
} }
if (obj.length !== undefined) { if (obj.length !== undefined) {
if (typeof obj.length !== 'number' || numberIsNaN(obj.length)) { if (typeof obj.length != 'number' || numberIsNaN(obj.length)) {
return createBuffer(0) return createBuffer(0)
} }
return fromArrayLike(obj) return fromArrayLike(obj)
@ -8113,7 +8109,7 @@ function byteLength (string, encoding) {
if (ArrayBuffer.isView(string) || isInstance(string, ArrayBuffer)) { if (ArrayBuffer.isView(string) || isInstance(string, ArrayBuffer)) {
return string.byteLength return string.byteLength
} }
if (typeof string !== 'string') { if (typeof string != 'string') {
throw new TypeError( throw new TypeError(
'The "string" argument must be one of type string, Buffer, or ArrayBuffer. ' + 'The "string" argument must be one of type string, Buffer, or ArrayBuffer. ' +
'Received type ' + typeof string 'Received type ' + typeof string
@ -8378,7 +8374,7 @@ function bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) {
if (buffer.length === 0) return -1 if (buffer.length === 0) return -1
// Normalize byteOffset // Normalize byteOffset
if (typeof byteOffset === 'string') { if (typeof byteOffset == 'string') {
encoding = byteOffset encoding = byteOffset
byteOffset = 0 byteOffset = 0
} else if (byteOffset > 0x7fffffff) { } else if (byteOffset > 0x7fffffff) {
@ -8403,7 +8399,7 @@ function bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) {
} }
// Normalize val // Normalize val
if (typeof val === 'string') { if (typeof val == 'string') {
val = Buffer.from(val, encoding) val = Buffer.from(val, encoding)
} }
@ -8414,9 +8410,9 @@ function bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) {
return -1 return -1
} }
return arrayIndexOf(buffer, val, byteOffset, encoding, dir) 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] val = val & 0xFF // Search for a byte value [0-255]
if (typeof Uint8Array.prototype.indexOf === 'function') { if (typeof Uint8Array.prototype.indexOf == 'function') {
if (dir) { if (dir) {
return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset) return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset)
} else { } else {
@ -8549,7 +8545,7 @@ Buffer.prototype.write = function write (string, offset, length, encoding) {
length = this.length length = this.length
offset = 0 offset = 0
// Buffer#write(string, encoding) // Buffer#write(string, encoding)
} else if (length === undefined && typeof offset === 'string') { } else if (length === undefined && typeof offset == 'string') {
encoding = offset encoding = offset
length = this.length length = this.length
offset = 0 offset = 0
@ -9228,7 +9224,7 @@ Buffer.prototype.copy = function copy (target, targetStart, start, end) {
var len = end - start 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 // Use built-in when available, missing from IE11
this.copyWithin(targetStart, start, end) this.copyWithin(targetStart, start, end)
} else if (this === target && start < targetStart && targetStart < 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.fill(string[, offset[, end]][, encoding])
Buffer.prototype.fill = function fill (val, start, end, encoding) { Buffer.prototype.fill = function fill (val, start, end, encoding) {
// Handle string cases: // Handle string cases:
if (typeof val === 'string') { if (typeof val == 'string') {
if (typeof start === 'string') { if (typeof start == 'string') {
encoding = start encoding = start
start = 0 start = 0
end = this.length end = this.length
} else if (typeof end === 'string') { } else if (typeof end == 'string') {
encoding = end encoding = end
end = this.length end = this.length
} }
if (encoding !== undefined && typeof encoding !== 'string') { if (encoding !== undefined && typeof encoding != 'string') {
throw new TypeError('encoding must be a 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) throw new TypeError('Unknown encoding: ' + encoding)
} }
if (val.length === 1) { if (val.length === 1) {
@ -9276,7 +9272,7 @@ Buffer.prototype.fill = function fill (val, start, end, encoding) {
val = code val = code
} }
} }
} else if (typeof val === 'number') { } else if (typeof val == 'number') {
val = val & 255 val = val & 255
} }
@ -9295,7 +9291,7 @@ Buffer.prototype.fill = function fill (val, start, end, encoding) {
if (!val) val = 0 if (!val) val = 0
var i var i
if (typeof val === 'number') { if (typeof val == 'number') {
for (i = start; i < end; ++i) { for (i = start; i < end; ++i) {
this[i] = val this[i] = val
} }
@ -9319,25 +9315,22 @@ Buffer.prototype.fill = function fill (val, start, end, encoding) {
// HELPER FUNCTIONS // HELPER FUNCTIONS
// ================ // ================
var INVALID_BASE64_RE = /[^+/0-9A-Za-z-_]/g var INVALID_BASE64_RE = /[^+/\dA-Z-_]/gi
function base64clean (str) { function base64clean (str) {
// Node takes equal signs as end of the Base64 encoding // 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 // Node strips out invalid characters like \n and \t from the string, base64-js does not
str = str.trim().replace(INVALID_BASE64_RE, '') str = str.trim().replace(INVALID_BASE64_RE, '')
// Node converts strings with length < 2 to '' // Node converts strings with length < 2 to ''
if (str.length < 2) return '' if (str.length < 2) return ''
// Node allows for non-padded base64 strings (missing trailing ===), base64-js does not // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not
while (str.length % 4 !== 0) { while (str.length % 4 !== 0) str += '='
str = str + '='
}
return str return str
} }
function toHex (n) { function toHex (n) {
if (n < 16) return '0' + n.toString(16) return (n < 16 ? '0' : '') + n.toString(16)
return n.toString(16)
} }
function utf8ToBytes (string, units) { 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'); "use strict";
const request = require('request'); const express = require('express')
const compression = require('compression'); const request = require('request')
const timeout = require('connect-timeout'); const compression = require('compression')
const rateLimit = require("express-rate-limit"); const timeout = require('connect-timeout')
const fs = require("fs"); const rateLimit = require("express-rate-limit")
const app = express(); const fs = require("fs")
const app = express()
let serverList = require('./servers.json') const serverList = require('./servers.json')
let pinnedServers = serverList.filter(x => x.pinned) const pinnedServers = []
let notPinnedServers = serverList.filter(x => !x.pinned).sort((a, b) => a.name.localeCompare(b.name)) 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.servers = pinnedServers.concat(notPinnedServers)
app.safeServers = JSON.parse(JSON.stringify(app.servers)) // clone app.safeServers = JSON.parse(JSON.stringify(app.servers)) // deep clone
app.safeServers.forEach(x => { delete x.endpoint; delete x.substitutions; delete x.overrides; delete x.disabled }) app.safeServers.forEach(x => ['endpoint', 'substitutions', 'overrides', 'disabled'].forEach(k => delete x[k]))
app.config = require('./settings.js') 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." + 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>" + "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>" "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({ const RL = rateLimit({
windowMs: app.config.rateLimiting ? 5 * 60 * 1000 : 0, windowMs: app.config.rateLimiting ? 5 * 60 * 1000 : 0,
max: app.config.rateLimiting ? 100 : 0, // max requests per 5 minutes max: app.config.rateLimiting ? 100 : 0, // max requests per 5 minutes
message: rlMessage, message: rlMessage,
keyGenerator: function(req) { return req.headers['x-real-ip'] || req.headers['x-forwarded-for'] }, keyGenerator: req => 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) } skip: req => req.url.includes("api/level") && !req.query.hasOwnProperty("download")
}) })
const RL2 = rateLimit({ const RL2 = rateLimit({
@ -34,8 +37,8 @@ const RL2 = rateLimit({
keyGenerator: function(req) { return req.headers['x-real-ip'] || req.headers['x-forwarded-for'] } keyGenerator: function(req) { return req.headers['x-real-ip'] || req.headers['x-forwarded-for'] }
}) })
let XOR = require('./classes/XOR.js'); const XOR = require('./classes/XOR.js')
let achievements = require('./misc/achievements.json') const achievements = require('./misc/achievements.json')
let achievementTypes = require('./misc/achievementTypes.json') let achievementTypes = require('./misc/achievementTypes.json')
let music = require('./misc/music.json') let music = require('./misc/music.json')
let assetPage = fs.readFileSync('./html/assets.html', 'utf8') let assetPage = fs.readFileSync('./html/assets.html', 'utf8')
@ -45,25 +48,27 @@ app.lastSuccess = {}
app.actuallyWorked = {} app.actuallyWorked = {}
app.servers.forEach(x => { app.servers.forEach(x => {
app.accountCache[x.id || "gd"] = {} x = x.id || "gd"
app.lastSuccess[x.id || "gd"] = Date.now() app.accountCache[x] = {}
app.lastSuccess[x] = Date.now()
}) })
app.mainEndpoint = app.servers.find(x => !x.id).endpoint // boomlings.com unless changed in fork app.mainEndpoint = app.servers.find(x => !x.id).endpoint // boomlings.com unless changed in fork
app.set('json spaces', 2) app.set('json spaces', 2)
app.use(compression()); app.use(compression())
app.use(express.json()); app.use(express.json())
app.use(express.urlencoded({extended: true})); app.use(express.urlencoded({extended: true}))
app.use(timeout('20s')); app.use(timeout('20s'))
app.use(async function(req, res, next) { app.use(async function(req, res, next) {
let subdomains = req.subdomains.map(x => x.toLowerCase()) let subdomains = req.subdomains.map(x => x.toLowerCase())
if (!subdomains.length) subdomains = [""] if (!subdomains.length) subdomains = [""]
req.server = app.servers.find(x => subdomains.includes(x.id.toLowerCase())) 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.sendError = function(errorCode=500) {
res.status(errorCode).send("-1") res.status(errorCode).send("-1")
} }
@ -80,14 +85,17 @@ app.use(async function(req, res, next) {
if (req.query.online > 0) req.offline = false if (req.query.online > 0) req.offline = false
req.gdParams = function(obj={}, substitute=true) { req.gdParams = function(obj={}, substitute=true) {
Object.keys(app.config.params).forEach(x => { if (!obj[x]) obj[x] = app.config.params[x] }) Object.keys(app.config.params).forEach(k => obj[k] ||= app.config.params[k])
Object.keys(req.server.extraParams || {}).forEach(x => { if (!obj[x]) obj[x] = req.server.extraParams[x] }) 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 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} : {}} let params = {form: obj, headers: app.config.ipForwarding && ip ? {'x-forwarded-for': ip, 'x-real-ip': ip} : {}}
if (substitute) { // GDPS substitutions in settings.js if (substitute) { // GDPS substitutions in settings.js
for (let ss in req.server.substitutions) { 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 return params
@ -95,16 +103,16 @@ app.use(async function(req, res, next) {
req.gdRequest = function(target, params={}, cb=function(){}) { req.gdRequest = function(target, params={}, cb=function(){}) {
if (!target) return cb(true) 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 parameters = params.headers ? params : req.gdParams(params)
let endpoint = req.endpoint let {endpoint} = req
if (params.forceGD || (params.form && params.form.forceGD)) endpoint = "http://www.boomlings.com/database/" if (params.forceGD || (params.form?.forceGD))
endpoint = "http://www.boomlings.com/database/"
request.post(endpoint + target + '.php', parameters, function(err, res, body) { request.post(endpoint + target + '.php', parameters, function(err, res, body) {
let error = err if (!err && (!body || /(^-\d$)|^error|^</.test(body)))
if (!error && (err || !body || body.match(/^-\d$/) || body.startsWith("error") || body.startsWith("<"))) { err = {serverError: true, response: body}
error = {serverError: true, response: body}
} return cb(err, res, body)
return cb(error, res, body)
}) })
} }
@ -116,20 +124,25 @@ fs.readdirSync('./api').filter(x => !x.includes(".")).forEach(x => directories.p
app.trackSuccess = function(id) { app.trackSuccess = function(id) {
app.lastSuccess[id] = Date.now() app.lastSuccess[id] = Date.now()
if (!app.actuallyWorked[id]) app.actuallyWorked[id] = true app.actuallyWorked[id] ||= true
} }
app.timeSince = function(id, time) { app.timeSince = function(id, time) {
if (!time) time = app.lastSuccess[id] if (!time) time = app.lastSuccess[id]
let secsPassed = Math.floor((Date.now() - time) / 1000) let secsPassed = Math.floor((Date.now() - time) / 1000)
let minsPassed = Math.floor(secsPassed / 60) let minsPassed = Math.floor(secsPassed / 60)
secsPassed -= 60 * minsPassed; secsPassed -= 60 * minsPassed
return `${app.actuallyWorked[id] ? "" : "~"}${minsPassed}m ${secsPassed}s` return `${app.actuallyWorked[id] ? "" : "~"}${minsPassed}m ${secsPassed}s`
} }
app.userCache = function(id, accountID, playerID, name) { 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()] if (!playerID) return app.accountCache[id][accountID.toLowerCase()]
let cacheStuff = [accountID, playerID, name] let cacheStuff = [accountID, playerID, name]
app.accountCache[id][name.toLowerCase()] = cacheStuff app.accountCache[id][name.toLowerCase()] = cacheStuff
@ -138,7 +151,10 @@ app.userCache = function(id, accountID, playerID, name) {
app.run = {} app.run = {}
directories.forEach(d => { 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() app.xor = new XOR()
@ -162,26 +178,29 @@ catch(e) {
} }
app.parseResponse = function (responseBody, splitter=":") { 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("\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 if (responseBody.startsWith("<br />")) responseBody = responseBody.split("<br />").slice(2).join("<br />").trim() // Seriously screw this
let response = responseBody.split('#')[0].split(splitter); let response = responseBody.split('#', 1)[0].split(splitter)
let res = {}; let res = {}
for (let i = 0; i < response.length; i += 2) { for (let i = 0; i < response.length; i += 2)
res[response[i]] = response[i + 1]} res[response[i]] = response[i + 1]
return res return res
} }
//xss bad //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 // ASSETS
app.use('/assets', express.static(__dirname + '/assets', {maxAge: "7d"})); app.use('/assets', express.static(__dirname + '/assets', {maxAge: "7d"}))
app.use('/assets/css', express.static(__dirname + '/assets/css')); app.use('/assets/css', express.static(__dirname + '/assets/css'))
app.use('/iconkit', express.static(__dirname + '/iconkit')); app.use('/iconkit', express.static(__dirname + '/iconkit'))
app.get("/global.js", function(req, res) { res.status(200).sendFile(__dirname + "/misc/global.js") }) 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("/dragscroll.js", function(req, res) { res.status(200).sendFile(__dirname + "/misc/dragscroll.js") })
app.get("/assets/:dir*?", function(req, res) { app.get("/assets/:dir*?", function(req, res) {
@ -208,116 +227,151 @@ app.get("/assets/:dir*?", function(req, res) {
// POST REQUESTS // POST REQUESTS
app.post("/like", RL, function(req, res) { app.run.like(app, req, res) }) function doPOST(name, key, noRL, wtf) {
app.post("/postComment", RL, function(req, res) { app.run.postComment(app, req, res) }) const args = ["/" + name]
app.post("/postProfileComment", RL, function(req, res) { app.run.postProfileComment(app, req, res) }) 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 // HTML
let onePointNineDisabled = ['daily', 'weekly', 'gauntlets', 'messages']
let downloadDisabled = ['daily', 'weekly'] let downloadDisabled = ['daily', 'weekly']
let onePointNineDisabled = ['daily', 'weekly', 'gauntlets', 'messages'] // using `concat` won't shorten this
let gdpsHide = ['achievements', 'messages'] let gdpsHide = ['achievements', 'messages']
app.get("/", function(req, res) { app.get("/", function(req, res) {
if (req.query.hasOwnProperty("offline") || (req.offline && !req.query.hasOwnProperty("home"))) res.status(200).sendFile(__dirname + "/html/offline.html") if (req.query.hasOwnProperty("offline") || (req.offline && !req.query.hasOwnProperty("home")))
res.status(200).sendFile(__dirname + "/html/offline.html")
else { else {
fs.readFile('./html/home.html', 'utf8', function (err, data) { fs.readFile('./html/home.html', 'utf8', function (err, data) {
let html = data; let html = data
if (req.isGDPS) { if (req.isGDPS) {
html = html.replace('"levelBG"', '"levelBG purpleBG"') html = html
.replace(/Geometry Dash Browser!/g, req.server.name + " Browser!") .replace('"levelBG"', '"levelBG purpleBG"')
.replace("/assets/gdlogo", `/assets/gdps/${req.id}_logo`) .replace(/Geometry Dash Browser!/g, req.server.name + " Browser!")
.replace("coin.png\" itemprop", `gdps/${req.id}_icon.png" itemprop`) .replace("/assets/gdlogo", `/assets/gdps/${req.id}_logo`)
.replace(/coin\.png/g, `${req.server.onePointNine ? "blue" : "silver"}coin.png`) .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') }) gdpsHide.forEach(x => { html = html.replace(`menu-${x}`, 'changeDaWorld') })
} }
if (req.onePointNine) onePointNineDisabled.forEach(x => { html = html.replace(`menu-${x}`, 'menuDisabled') }) const htmlReplacer = x => { html = html.replace(`menu-${x}`, 'menuDisabled') }
if (req.server.disabled) req.server.disabled.forEach(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") { if (req.server.downloadsDisabled && process.platform == "linux") {
downloadDisabled.forEach(x => { html = html.replace(`menu-${x}`, 'menuDisabled') }) downloadDisabled.forEach(htmlReplacer)
html = html.replace('id="dl" style="display: none', 'style="display: block') html = html
.replace('No active <span id="noLevel">daily</span> level!', '[Blocked by RobTop]') .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 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') html = html
.replace('none" id="menu_featured', 'block" id="menu_featured') .replace('block" id="menu_weekly', 'none" id="menu_weekly')
.replace('none" id="menu_featured', 'block" id="menu_featured')
} }
return res.status(200).send(html) 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 // 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) }) function doGET(name, key, RL, wtf1, wtf2) {
app.get("/api/comments/:id", RL2, function(req, res) { app.run.comments(app, req, res) }) 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/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/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) }) doGET("leaderboardLevel/:id", "leaderboardLevel", RL2)
app.get("/api/level/:id", RL, function(req, res) { app.run.level(app, req, res, true) }) doGET("level/:id", "level", RL, true)
app.get("/api/mappacks", function(req, res) { app.run.mappacks(app, req, res) }) doGET("mappacks")
app.get("/api/profile/:id", RL2, function(req, res) { app.run.profile(app, req, res, true) }) doGET("profile/:id", "profile", RL2, true)
app.get("/api/search/:text", RL2, function(req, res) { app.run.search(app, req, res) }) doGET("search/:text", "search", RL2)
app.get("/api/song/:song", function(req, res){ app.run.song(app, req, res) }) doGET("song/:song", "song")
// REDIRECTS // REDIRECTS
app.get("/icon", function(req, res) { res.redirect('/iconkit') }) function doRedir(name, dir, key = 'id') {
app.get("/obj/:text", function(req, res) { res.redirect('/obj/' + req.params.text) }) app.get('/' + name, function(req, res) { res.redirect('/' + dir + (key && '/' + req.params[key]) ) })
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) }) doRedir('icon', 'iconkit', '')
app.get("/p/:id", function(req, res) { res.redirect('/u/' + req.params.id) }) doRedir('obj/:text', 'obj', 'text')
app.get("/l/:id", function(req, res) { res.redirect('/leaderboard/' + req.params.id) }) doRedir('leaderboards/:id', 'leaderboard')
app.get("/a/:id", function(req, res) { res.redirect('/analyze/' + req.params.id) }) doRedir('profile/:id', 'u')
app.get("/c/:id", function(req, res) { res.redirect('/comments/' + req.params.id) }) doRedir('p/:id', 'u')
app.get("/d/:id", function(req, res) { res.redirect('/demon/' + req.params.id) }) doRedir('l/:id', 'leaderboard')
doRedir('a/:id', 'analyze')
doRedir('c/:id', 'comments')
doRedir('d/:id', 'demon')
// API AND HTML // 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 // MISC
app.get("/api/userCache", function(req, res) { res.status(200).send(app.accountCache) }) 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/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 // important icon stuff
let sacredTexts = {} let sacredTexts = {}
fs.readdirSync('./iconkit/sacredtexts').forEach(x => { 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') let previewIcons = fs.readdirSync('./iconkit/premade')
@ -326,7 +380,7 @@ let newPreviewIcons = fs.readdirSync('./iconkit/newpremade')
let previewCounts = {} let previewCounts = {}
previewIcons.forEach(x => { previewIcons.forEach(x => {
if (x.endsWith("_0.png")) return 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 if (!previewCounts[iconType]) previewCounts[iconType] = 1
else previewCounts[iconType]++ else previewCounts[iconType]++
}) })
@ -337,15 +391,15 @@ sacredTexts.newIcons = []
let newIconCounts = {} let newIconCounts = {}
newIcons.forEach(x => { newIcons.forEach(x => {
if (x.endsWith(".plist")) { if (x.endsWith(".plist")) {
sacredTexts.newIcons.push(x.split("-")[0]) sacredTexts.newIcons.push(x.split("-", 1)[0])
let formName = x.split(/_\d/g)[0] let formName = x.split(/_\d/g, 1)[0]
if (!newIconCounts[formName]) newIconCounts[formName] = 1 if (!newIconCounts[formName]) newIconCounts[formName] = 1
else newIconCounts[formName]++ else newIconCounts[formName]++
} }
}) })
sacredTexts.newIconCounts = newIconCounts 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); res.status(200).send(sacredTexts);
}); });
@ -353,37 +407,37 @@ app.get('/api/icons', function(req, res) {
let iconKitFiles = {} let iconKitFiles = {}
let sampleIcons = require('./misc/sampleIcons.json') let sampleIcons = require('./misc/sampleIcons.json')
fs.readdirSync('./iconkit/extradata').forEach(x => { 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 Object.assign(iconKitFiles, {previewIcons, newPreviewIcons})
iconKitFiles.newPreviewIcons = newPreviewIcons
app.get('/api/iconkit', function(req, res) { app.get('/api/iconkit', function(req, res) {
let sample = [JSON.stringify(sampleIcons[Math.floor(Math.random() * sampleIcons.length)].slice(1))] let sample = [JSON.stringify(sampleIcons[Math.random() * sampleIcons.length >>>0].slice(1))]
let iconserver = req.isGDPS ? req.server.name : undefined let iconserver = req.isGDPS ? req.server.name : undefined
res.status(200).send(Object.assign(iconKitFiles, {sample, server: iconserver, noCopy: req.onePointNine || req.offline})); res.status(200).send(Object.assign(iconKitFiles, {sample, server: iconserver, noCopy: req.onePointNine || req.offline}));
}); })
app.get('/icon/:text', function(req, res) { app.get('/icon/:text', function(req, res) {
let iconID = Number(req.query.icon || 1) let iconID = Number(req.query.icon || 1)
let iconForm = sacredTexts.forms[req.query.form] ? req.query.form : "icon" let iconForm = sacredTexts.forms[req.query.form] ? req.query.form : "icon"
let iconPath = `${iconForm}_${iconID}.png` let iconPath = `${iconForm}_${iconID}.png`
let fileExists = iconKitFiles.previewIcons.includes(iconPath) let fileExists = iconKitFiles.previewIcons.includes(iconPath)
if (fileExists) return res.status(200).sendFile(`./iconkit/premade/${iconPath}`, {root: __dirname }) return res.status(200).sendFile(`./iconkit/premade/${fileExists ? iconPath : iconForm + '_01.png'}`, {root: __dirname})
else return res.status(200).sendFile(`./iconkit/premade/${iconForm}_01.png`, {root: __dirname})
}) })
app.get('*', function(req, res) { app.get('*', function(req, res) {
if (req.path.startsWith('/api') || req.path.startsWith("/iconkit")) res.status(404).send('-1') if (/^\/(api|iconkit)/.test(req.path))
else res.redirect('/search/404%20') 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)')
}) })
process.on('uncaughtException', (e) => { console.log(e) }); app.use(function (err, req, res) {
process.on('unhandledRejection', (e, p) => { console.log(e) }); 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() { function somethingSelected() {
return typeof window.getSelection == 'function' && window.getSelection().toString() != ""; 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) { $('.dragscroll').each(function(_, el) {
let previouslyMouseDown = false; let previouslyMouseDown = false
el.addEventListener('mousemove', function(e) { el.addEventListener('mousemove', function(e) {
if (e.buttons != 1) { if (e.buttons != 1) {
if (previouslyMouseDown) { if (previouslyMouseDown) {
el.style.removeProperty('user-select'); el.style.removeProperty('user-select')
el.style.removeProperty('-webkit-user-select'); el.style.removeProperty('-webkit-user-select')
previouslyMouseDown = false; previouslyMouseDown = false;
} }
return; return;
} }
if (somethingSelected()) if (somethingSelected()) return
return;
if (!previouslyMouseDown) { if (!previouslyMouseDown) {
for (let el of e.target.childNodes) { if ([...e.target.childNodes].some(
if (el.nodeType === Node.TEXT_NODE && el.textContent.replace(remover, '').length) el => el.nodeType === Node.TEXT_NODE
return; &&
} el.textContent.replace(remover, '').length
el.style['user-select'] = 'none'; )
el.style['-webkit-user-select'] = 'none'; ) return
el.style['user-select'] = 'none'
el.style['-webkit-user-select'] = 'none'
previouslyMouseDown = true; previouslyMouseDown = true;
} }
//el.scrollLeft -= e.movementX; //el.scrollLeft -= e.movementX
el.scrollTop -= e.movementY; el.scrollTop -= e.movementY;
}, {passive: true}); }, {passive: true});
}); });

View file

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

View file

@ -1,25 +1,26 @@
"use strict";
let path = "../extra/" let path = "../extra/"
let files = ["AchievementsDesc", "AchievementsDescMD", null, "AchievementsDescSZ"] const files = ["", "MD", null, "SZ"]
let gameNames = ["gd", "meltdown", "world", "subzero"] const gameNames = ["gd", "meltdown", "world", "subzero"]
let achString = "geometry.ach." const achString = "geometry.ach."
let rewardTypes = { color: "color1", icon: "cube", bird: "ufo", dart: "wave", special: "trail", death: "deathEffect" } let rewardTypes = { color: "color1", icon: "cube", bird: "ufo", dart: "wave", special: "trail", death: "deathEffect" }
let games = { "md": "meltdown", "world.": "world", "subzero.": "subzero" } let games = { "md": "meltdown", "world.": "world", "subzero.": "subzero" }
const plist = require('plist'); const plist = require('plist')
const fs = require('fs'); const fs = require('fs')
let achArray = [] let achArray = []
files.forEach((file, fileNum) => { files.forEach((file, fileNum) => {
if (!file) return if (file === null) return
let data = plist.parse(fs.readFileSync(path + file + '.plist', 'utf8')); file = "AchievementsDesc" + file
f
console.log(`Converting ${file}.plist...`) console.log(`Converting ${file}.plist...`)
for (let key in data) { for (let key in data) {
if (!achArray.find(x => x.trueID == key)) { if (!achArray.find(x => x.trueID == key)) {
let fileData = data[key]; let fileData = data[key]
let reward = fileData.icon ? fileData.icon.split("_") : [] let reward = fileData.icon ? fileData.icon.split("_") : []
let achObj = { let achObj = {
id: key.slice(achString.length), 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\\' "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
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'); 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
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'); 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
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 forms = require('./forms.json')
const data = plist.parse(fs.readFileSync(gdPath + 'GJ_GameSheet02-uhd.plist', 'utf8')); 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
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')); 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
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 formList = Object.values(forms).map(x => x.form)
let frames = {} let frames = {}
function addIcons(data) { function addIcons(data) {
Object.keys(data).filter(x => formList.includes(x.split("_")[0])).forEach(x => frames[x] = data[x]) 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
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) 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) { for (let key in frames) {
if (key.startsWith(".")) delete frames[key] if (key.startsWith(".")) delete frames[key]
else { let fileData = frames[key]; 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
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) { 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
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]) { 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
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 if (!["spriteSize", "spriteOffset"].includes(innerKey)) delete fileData[innerKey] // remove useless stuff
else fileData[innerKey] = JSON.parse(fileData[innerKey].replace(/{/g, '[').replace(/}/g, ']')); 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
} }
} }
}} }
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 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
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!") 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
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'); "use strict";
const fs = require('fs'); const plist = require('plist')
const fs = require('fs')
const gdPath = 'C:\\Program Files (x86)\\Steam\\steamapps\\common\\Geometry Dash\\Resources\\' const gdPath = 'C:\\Program Files (x86)\\Steam\\steamapps\\common\\Geometry Dash\\Resources\\'
const animationPlists = ["Robot_AnimDesc.plist", "Spider_AnimDesc.plist"] const animationPlists = ["Robot_AnimDesc.plist", "Spider_AnimDesc.plist"]
const objectDefinitions = "objectDefinitions.plist" const objectDefinitions = "objectDefinitions.plist"
@ -29,16 +30,16 @@ plistData.forEach(x => {
Object.keys(x.animationContainer).forEach(a => fullAnimationData[a] = x.animationContainer[a]) Object.keys(x.animationContainer).forEach(a => fullAnimationData[a] = x.animationContainer[a])
}) })
let animations = { "robot": {}, "spider": {} } let animations = { robot: {}, spider: {} }
for (let animation in fullAnimationData) { for (let animation in fullAnimationData) {
let animationName = animation.split(".")[0].split("_") let animationName = animation.split(".", 1)[0].split("_")
let animationForm = animationName.shift() let animationForm = animationName.shift()
let animationIndex = Number(animationName.pop()) - 1 let animationIndex = Number(animationName.pop()) - 1
animationName = animationName.join("_") animationName = animationName.join("_")
let animationList = Object.values(fullAnimationData[animation]) 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 animationData = animationList.map(anim => {
let textureInfo = anim.texture.split("_") let textureInfo = anim.texture.split("_")
let flips = parseSet(anim.flipped) let flips = parseSet(anim.flipped)
@ -55,17 +56,19 @@ for (let animation in fullAnimationData) {
if (!animations[formName][animationName]) { if (!animations[formName][animationName]) {
let timingDefs = timings[animationForm].animations[animationName] || {} let timingDefs = timings[animationForm].animations[animationName] || {}
let animationInfo = { duration: cleanFloat(Number(timingDefs.delay || 0.05) * 1000) } let animationInfo = { duration: cleanFloat(Number(timingDefs.delay || 0.05) * 1000) }
if (timingDefs.looped == '1' || (!timingDefs.looped && animationName.includes("loop"))) animationInfo.loop = true if (timingDefs.looped == '1' || (!timingDefs.looped && animationName.includes("loop")))
if (timingDefs.singleFrame) animationInfo.single = true animationInfo.loop = true
if (timingDefs.singleFrame)
animationInfo.single = true
animations[formName][animationName] = { info: animationInfo, frames: [] } animations[formName][animationName] = { info: animationInfo, frames: [] }
} }
animations[formName][animationName].frames[animationIndex] = animationData animations[formName][animationName].frames[animationIndex] = animationData
} }
let cleanJSON = JSON.stringify({info, animations}, null, 2) 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+([\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(/],\n(\s+)"(.+?)": \[\n/g, '],\n\n$1"$2": [\n') // blank line between animations
.replace(' "animations"', '\n "animations"') // blank line before animation list .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!") console.log("Successfully parsed!")

View file

@ -17,7 +17,6 @@
[ "Cool", 37, 20, 17, 1 ], [ "Cool", 37, 20, 17, 1 ],
[ "Cyclic", 30, 3, 12, 0 ], [ "Cyclic", 30, 3, 12, 0 ],
[ "DanZmeN", 104, 34, 12, 1 ], [ "DanZmeN", 104, 34, 12, 1 ],
[ "DanZmeN", 104, 34, 12, 1 ],
[ "envylol", 73, 20, 1, 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 ], [ "EVW", 28, 12, 9, 0 ],
[ "Flub", 25, 3, 12, 1 ], [ "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 // Feel free to enable/disable stuff here for smoother local use, free of rate limits
module.exports = { module.exports = {
port: 2000, // Port to host website on 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', secret: 'Wmfd2893gb7',
gameVersion: '21', gameVersion: '21',
binaryVersion: '35', binaryVersion: '35',