Roblox / index.html
ewr23r2r's picture
also make By: Roblox tetx underlined and clickable on item detail page. and make sur ethat if user creats a item on uplaod catalog iem popup dont close it when they upload - Initial Deployment
2db41a2 verified
<html>
<head>
<base href="https://roblox.com/">
<meta name="referrer" content="no-referrer-when-downgrade">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Roblox</title>
<link rel="icon" type="image/png" href="/favicon.png"/>
<link rel="icon" type="image/png" href="https://i.imgur.com/iPnIO41.png"/>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;700&amp;display=swap" rel="stylesheet">
<style>
.input-field {
width: 100%;
padding: 8px;
margin-bottom: 10px;
border: 1px solid #5F6569;
border-radius: 5px;
background-color: #232527;
color: #FFFFFF;
font-family: 'Montserrat', 'Gotham SSm A', 'Gotham SSm B', Arial, sans-serif;
}
.input-field:focus {
outline: none;
border-color: #00A2FF;
}
textarea.input-field {
resize: vertical;
min-height: 100px;
}
body {
font-family: 'Montserrat', 'Gotham SSm A', 'Gotham SSm B', Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #232527;
color: #ffffff;
}
.profile-stats {
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 20px;
margin-top: 20px;
}
.profile-stat {
text-align: center;
background-color: #393B3D;
border-radius: 10px;
padding: 15px;
width: 150px;
}
.profile-stat h3 {
margin: 0;
font-size: 14px;
color: #B8B8B8;
}
.profile-stat p {
margin: 5px 0 0;
font-size: 18px;
font-weight: bold;
}
.header {
background-color: #191B1D;
color: #fff;
padding: 10px;
display: flex;
justify-content: space-between;
align-items: center;
}
.logo {
font-size: 24px;
font-weight: bold;
color: #ffffff;
}
.nav {
display: flex;
}
.nav a {
color: #fff;
text-decoration: none;
margin-left: 20px;
cursor: pointer;
padding-bottom: 5px;
position: relative;
font-weight: bold;
}
.nav a.active::after {
content: '';
position: absolute;
left: 0;
bottom: -2px;
width: 100%;
height: 2px;
background-color: #00A2FF;
}
.content {
padding: 20px;
}
.profile,
.robux-page {
background-color: #393B3D;
border-radius: 10px;
padding: 20px;
margin-bottom: 20px;
}
.profile-header {
display: flex;
flex-direction: column;
align-items: center;
}
.profile-header h2 {
font-size: 28px;
margin-bottom: 10px;
}
.admin-options {
display: flex;
gap: 10px;
margin-top: 10px;
}
.catalog-items,
.inventory-items {
display: flex;
flex-wrap: wrap;
gap: 20px;
justify-content: center;
}
.catalog-item,
.inventory-item {
background-color: #393B3D;
border-radius: 10px;
padding: 15px;
width: 200px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
position: relative;
cursor: pointer;
}
.catalog-item img,
.inventory-item img {
width: 100%;
height: 180px;
object-fit: cover;
border-radius: 5px;
margin-bottom: 10px;
border: 2px solid #323436;
background-color: #323436;
}
.catalog-item-details,
.inventory-item-details {
text-align: center;
}
.catalog-item-details h3,
.inventory-item-details h3 {
margin: 5px 0;
font-size: 19px;
color: #ffffff;
}
.catalog-item-details p,
.inventory-item-details p {
margin: 5px 0;
font-size: 16px;
color: #B8B8B8;
}
.catalog-item-details button {
display: block;
margin: 10px auto;
}
.admin-panel {
background-color: #393B3D;
border-radius: 10px;
padding: 20px;
}
.button {
background-color: #00B06F;
color: #fff;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
margin: 5px;
font-family: 'Montserrat', 'Gotham SSm A', 'Gotham SSm B', Arial, sans-serif;
font-size: 14px;
}
.button:disabled {
background-color: #5F6569;
cursor: not-allowed;
}
select {
background-color: #393B3D;
color: #ffffff;
border: 1px solid #5F6569;
padding: 8px;
border-radius: 5px;
font-family: 'Montserrat', 'Gotham SSm A', 'Gotham SSm B', Arial, sans-serif;
font-size: 14px;
cursor: pointer;
}
select:focus {
outline: none;
border-color: #00A2FF;
}
.pagination button {
background-color: #393B3D;
color: #ffffff;
border: 1px solid #5F6569;
padding: 5px 10px;
border-radius: 3px;
font-family: 'Montserrat', 'Gotham SSm A', 'Gotham SSm B', Arial, sans-serif;
font-size: 14px;
cursor: pointer;
}
.pagination button:disabled {
background-color: #232527;
color: #5F6569;
cursor: not-allowed;
}
.pagination span {
color: #ffffff;
font-family: 'Montserrat', 'Gotham SSm A', 'Gotham SSm B', Arial, sans-serif;
font-size: 14px;
margin: 0 10px;
}
.popup {
display: none;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: #393B3D;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
z-index: 1000;
max-height: 80vh;
overflow-y: auto;
}
#buy-confirmation-popup {
z-index: 1001; /* Higher than other popups */
}
#item-listings-popup, #purchase-history-popup {
width: 400px;
height: auto;
max-height: 500px;
}
.clickable {
color: #00A2FF;
cursor: pointer;
text-decoration: underline;
}
.serial-number {
position: absolute;
top: 5px;
left: 5px;
background-color: rgba(0, 0, 0, 0.7);
color: #fff;
padding: 2px 6px;
border-radius: 3px;
font-size: 12px;
}
.buy-listing-confirmation {
text-align: center;
padding: 20px;
}
.buy-listing-confirmation img {
width: 100px;
height: 100px;
object-fit: cover;
margin: 10px auto;
display: block;
}
.buy-listing-confirmation .button-group {
display: flex;
justify-content: center;
gap: 10px;
margin-top: 15px;
}
.purchase-history {
max-height: 200px;
overflow-y: auto;
margin-bottom: 20px;
padding: 10px;
background-color: #2C2F33;
border-radius: 5px;
}
.purchase-history-item {
padding: 8px;
margin: 5px 0;
background-color: #232527;
border-radius: 5px;
}
#item-listings h4 {
margin: 10px 0;
padding-bottom: 5px;
border-bottom: 1px solid #393B3D;
}
#item-listings-popup .clickable,
#item-listings-popup .button {
margin: 5px;
}
#item-listings-popup h3,
#item-listings-popup .clickable,
#item-listings-popup .button {
display: inline-block;
vertical-align: middle;
}
.listing {
background-color: #2C2F33;
padding: 10px;
margin: 5px 0;
border-radius: 5px;
display: flex;
justify-content: space-between;
align-items: center;
}
.item-quantity {
position: absolute;
top: 5px;
right: 5px;
background-color: rgba(0, 0, 0, 0.7);
color: #fff;
padding: 2px 6px;
border-radius: 10px;
font-size: 12px;
}
.limited-icon {
width: 75px;
height: 75px;
position: absolute;
top: 120px;
left: 50%;
transform: translateX(-50%);
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.limited-icon.unlimited {
width: 120px;
height: 265px;
background-image: url('https://i.imgur.com/T5kWRJJ.png');
top: 56px;
margin-left: -39px;
}
.limited-icon.limited {
width: 120px;
height: 25px;
background-image: url('https://i.imgur.com/Bh3j647.png');
top: 175px;
margin-left: -50px;
}
.robux-icon {
width: 16px;
height: 16px;
display: inline-block;
vertical-align: middle;
margin-right: 5px;
background-image: url('https://devforum-uploads.s3.dualstack.us-east-2.amazonaws.com/uploads/original/4X/e/d/f/edfae9388da4cd8496b885a8a2df613372500d9c.png');
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.catalog-item .robux-icon,
.inventory-item .robux-icon {
width: 24px;
height: 24px;
margin-right: 8px;
}
.catalog-item-details p,
.inventory-item-details p {
font-size: 18px;
font-weight: bold;
}
#catalog h2 {
text-align: center;
font-size: 24px;
text-decoration: underline;
margin-bottom: 20px;
}
.success-banner {
position: fixed;
top: 50px;
left: 0;
width: 100%;
background-color: #4CAF50;
color: white;
text-align: center;
padding: 20px;
z-index: 1001;
display: none;
}
#buy-confirmation-popup,
#sell-confirmation-popup {
text-align: center;
}
#buy-confirmation-popup img {
margin: 10px auto;
width: 100px;
height: 100px;
object-fit: cover;
}
#buy-confirmation-popup p {
margin: 10px 0;
text-align: center;
}
#buy-confirmation-popup button {
margin: 5px;
min-width: 100px;
}
.loading-screen {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.7);
z-index: 2000;
justify-content: center;
align-items: center;
}
.loading-icon {
width: 50px;
height: 50px;
border: 5px solid #f3f3f3;
border-top: 5px solid #3498db;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.profile-inventory {
margin-top: 20px;
max-height: 600px;
overflow-y: auto;
}
.profile-inventory h3 {
margin-bottom: 10px;
}
.profile-inventory-item {
background-color: #2C2F33;
border-radius: 5px;
padding: 10px;
margin-bottom: 10px;
display: flex;
justify-content: space-between;
align-items: center;
}
.profile-inventory-item-details {
display: flex;
align-items: center;
}
.profile-inventory-item-details img {
width: 50px;
height: 50px;
object-fit: cover;
border-radius: 5px;
margin-right: 10px;
}
.profile-inventory-item-info h4 {
margin: 0;
font-size: 16px;
}
.profile-inventory-item-info p {
margin: 5px 0 0;
font-size: 14px;
color: #B8B8B8;
}
#profile-inventory-pagination {
display: none;
}
#users-list p {
cursor: pointer;
padding: 10px;
font-size: 24px;
text-align: center;
font-weight: bold;
color: white;
}
#users-list p:hover {
background-color: #5F6569;
}
#users-list p.active {
background-color: #00A2FF;
font-weight: bold;
}
.pagination {
display: flex;
justify-content: center;
align-items: center;
margin-top: 20px;
}
.pagination button {
margin: 0 5px;
}
.timer-text {
position: absolute;
top: 5px;
left: 5px;
background-color: #ff4444;
padding: 4px;
border-radius: 4px;
width: 24px;
height: 24px;
background-image: url('https://i.imgur.com/m4Caqes.png');
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.new-text {
position: absolute;
top: 5px;
left: 5px;
background-color: #F78904;
color: white;
padding: 2px 5px;
border-radius: 3px;
font-size: 14px;
cursor: pointer;
transition: opacity 0.3s ease;
}
.timer-text.with-new {
left: 55px;
}
.sort-options {
margin-bottom: 10px;
text-align: center;
}
.profile-inventory .catalog-item {
background-color: #232527;
}
.confirm-button {
background-color: #ffffff;
color: #000000;
}
.cancel-button {
background-color: #808080;
color: #ffffff;
}
.new-text {
cursor: pointer;
transition: opacity 0.3s ease;
}
.new-text:hover {
opacity: 0.8;
}
.stock-text {
color: #00A2FF;
font-weight: bold;
margin: 5px 0;
}
.trade-value.invalid {
color: red;
}
.trade-status {
text-align: center;
margin: 10px 0;
font-weight: bold;
}
.trade-status.invalid {
color: red;
}
.trade-status.valid {
color: green;
}
.trade-page {
padding: 20px;
}
.trade-container {
display: flex;
justify-content: space-between;
gap: 20px;
}
.trade-side {
flex: 1;
background-color: #393B3D;
padding: 20px;
border-radius: 10px;
}
.trade-items {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 10px;
margin-top: 10px;
max-height: 400px;
overflow-y: auto;
}
.trade-item {
position: relative;
background-color: #232527;
border-radius: 5px;
padding: 10px;
cursor: pointer;
transition: background-color 0.2s;
}
.trade-item:hover {
background-color: #2C2F33;
}
.trade-item.selected {
border: 2px solid #00A2FF;
}
.trade-item img {
width: 100%;
height: 100px;
object-fit: cover;
border-radius: 5px;
}
.trade-item-quantity {
position: absolute;
bottom: 5px;
right: 5px;
background-color: rgba(0, 0, 0, 0.7);
color: #fff;
padding: 2px 6px;
border-radius: 10px;
font-size: 12px;
}
.trade-partners {
margin-bottom: 20px;
}
.trade-buttons {
margin-top: 20px;
text-align: center;
}
.trade-value {
text-align: center;
margin: 10px 0;
font-size: 18px;
font-weight: bold;
}
.trade-buttons .button:disabled {
background-color: #5F6569;
cursor: not-allowed;
}
.completed-trades {
margin-top: 20px;
padding: 20px;
background-color: #393B3D;
border-radius: 10px;
}
.completed-trade {
background-color: #232527;
padding: 15px;
margin-bottom: 10px;
border-radius: 5px;
}
.trade-details {
display: flex;
justify-content: space-between;
gap: 20px;
}
.trade-side-details {
flex: 1;
}
.trade-timestamp {
color: #B8B8B8;
font-size: 14px;
margin-top: 5px;
}
.trade-items-list {
margin-top: 10px;
}
.trade-item-entry {
display: flex;
align-items: center;
gap: 10px;
margin: 5px 0;
font-size: 14px;
background-color: #2C2F33;
border-radius: 5px;
padding: 5px;
}
.trade-item-entry img {
width: 30px;
height: 30px;
object-fit: cover;
border-radius: 4px;
}
.pending-trades {
margin: 20px 0;
padding: 20px;
background-color: #393B3D;
border-radius: 10px;
}
.pending-trade {
background-color: #232527;
padding: 15px;
margin-bottom: 10px;
border-radius: 5px;
}
.trade-actions {
margin-top: 10px;
display: flex;
gap: 10px;
justify-content: flex-end;
}
.trade-rap-total {
color: #00A2FF;
font-weight: bold;
margin: 5px 0;
font-size: 16px;
}
.trade-side-details h5 {
margin-bottom: 10px;
padding-bottom: 5px;
border-bottom: 1px solid #393B3D;
}
#users-list-page p {
cursor: pointer;
padding: 10px;
font-size: 24px;
text-align: center;
font-weight: bold;
color: white;
}
#users-list-page p:hover {
background-color: #5F6569;
}
.profile-username-display {
font-size: 28px;
font-weight: bold;
color: white;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
}
.verified-badge {
width: 20px;
height: 20px;
vertical-align: middle;
}
.declined-trades {
margin-top: 20px;
padding: 20px;
background-color: #393B3D;
border-radius: 10px;
}
.declined-trades .completed-trade {
background-color: #232527;
padding: 15px;
margin-bottom: 10px;
border-radius: 5px;
opacity: 0.8;
}
.declined-trades h3 {
color: #ff4444;
margin-bottom: 15px;
}
#catalog-search {
padding-right: 35px;
}
#catalog-search + div svg {
color: #B8B8B8;
transition: color 0.2s;
}
#catalog-search + div:hover svg {
color: #ffffff;
}
.transaction-list {
max-height: 400px;
overflow-y: auto;
margin: 20px 0;
padding: 10px;
background-color: #2C2F33;
border-radius: 10px;
}
.transaction-item {
background-color: #393B3D;
padding: 15px;
margin: 10px 0;
border-radius: 5px;
}
.transaction-item p {
margin: 5px 0;
color: #FFFFFF;
}
.total-spent {
font-size: 18px;
font-weight: bold;
margin: 15px 0;
color: #00A2FF;
}
.robux-page h2 {
font-size: 32px;
margin-bottom: 20px;
display: flex;
align-items: center;
gap: 10px;
}
.robux-page h3 {
font-size: 24px;
margin: 20px 0;
color: #FFFFFF;
}
.robux-page .button {
margin: 10px;
min-width: 150px;
}
#detail-limited-icon.unlimited {
width: 120px;
height: 265px;
background-image: url('https://i.imgur.com/T5kWRJJ.png');
position: absolute;
bottom: 10px;
left: 10px;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
#detail-limited-icon.limited {
width: 120px;
height: 25px;
background-image: url('https://i.imgur.com/Bh3j647.png');
position: absolute;
bottom: 10px;
left: 10px;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.detail-rap-text {
color: #B8B8B8;
font-size: 14px;
margin-top: 5px;
}
.equipped-items-container {
margin-top: 20px;
padding: 15px;
background-color: #393B3D;
border-radius: 10px;
text-align: center;
}
.equipped-items-title {
font-size: 20px;
font-weight: bold;
margin-bottom: 15px;
text-align: center;
}
.equipped-items-grid {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 10px;
justify-content: center;
margin: 0 auto;
max-width: 90%;
}
#profile-equipped-items {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 10px;
}
.equipped-item {
background-color: #232527;
border-radius: 5px;
padding: 15px;
text-align: center;
width: 180px;
}
.equipped-item img {
width: 150px;
height: 150px;
object-fit: cover;
border-radius: 5px;
margin-bottom: 8px;
}
.equipped-item-name {
font-weight: bold;
font-size: 16px;
margin-bottom: 8px;
white-space: normal;
overflow: visible;
text-overflow: clip;
}
.equipped-item-rap, .equipped-item-price {
font-size: 14px;
color: #B8B8B8;
}
.put-on-button, .take-off-button {
display: block;
margin: 5px auto;
padding: 5px 10px;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 12px;
}
.put-on-button {
background-color: #00B06F;
color: white;
}
.take-off-button {
background-color: #ff4444;
color: white;
}
.user-bio {
margin-top: 10px;
padding: 15px;
background-color: #232527;
border-radius: 10px;
font-style: italic;
text-align: center;
position: relative;
}
.edit-bio-button {
position: absolute;
top: 5px;
right: 5px;
background-color: #393B3D;
color: white;
border: none;
border-radius: 5px;
padding: 3px 8px;
font-size: 12px;
cursor: pointer;
}
.inventory-toggle {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 15px;
}
.toggle-switch {
position: relative;
display: inline-block;
width: 60px;
height: 30px;
margin: 0 10px;
}
.toggle-switch input {
opacity: 0;
width: 0;
height: 0;
}
.toggle-slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: .4s;
border-radius: 30px;
}
.toggle-slider:before {
position: absolute;
content: "";
height: 22px;
width: 22px;
left: 4px;
bottom: 4px;
background-color: white;
transition: .4s;
border-radius: 50%;
}
input:checked + .toggle-slider {
background-color: #00A2FF;
}
input:checked + .toggle-slider:before {
transform: translateX(30px);
}
.admin-badge {
width: 20px;
height: 20px;
vertical-align: middle;
margin-right: 5px;
}
.leaderboard-row-private {
background-color: #232527 !important;
}
.leaderboard-row-public {
background-color: #2C2F33 !important;
}
.leaderboard-row .robux-icon {
width: 18px;
height: 18px;
margin-right: 5px;
}
.leaderboard-row {
font-size: 18px;
}
#announcement-banner > div {
outline: none !important;
border: none !important;
box-sizing: border-box;
}
.sortable-list {
max-height: 400px;
overflow-y: auto;
margin-bottom: 15px;
}
.sortable-user-item {
padding: 10px 15px;
background-color: #2C2F33;
border-radius: 5px;
margin-bottom: 8px;
cursor: pointer;
user-select: none;
transition: background-color 0.2s;
}
.sortable-user-item:hover {
background-color: #393B3D;
}
.sortable-user-item.dragging {
opacity: 0.7;
background-color: #1F2022;
}
</style>
</head>
<body>
<div class="loading-screen" id="loading-screen">
<div class="loading-icon"></div>
</div>
<div class="success-banner" id="success-banner">
Successful purchase!
</div>
<div class="header">
<div class="logo"><img src="https://i.imgur.com/Z39wYb8.png" alt="Roblox Logo" style="height: 25px;"></div>
<div id="current-user-display"></div>
<div class="nav">
<a onclick="showProfile(); return false;">Profile</a>
<a onclick="showCatalog(); return false;">Catalog</a>
<a onclick="showAdmin(); return false;" id="admin-link" style="display:none;">Admin</a>
<a onclick="showInventory(); return false;">Inventory</a>
<a onclick="showRobuxPage(); return false;"><span class="robux-icon"></span><span id="robux-amount" style="font-weight: bold;">0</span></a>
<a onclick="showTrade(); return false;">Trade</a>
<a onclick="showLeaderboard(); return false;">Leaderboard</a>
<a onclick="showUsers(); return false;">Switch Account</a>
</div>
</div>
<div id="announcement-banner" style="display:none; background-color: transparent; padding: 0; margin: 0;"></div>
<div class="content">
<div id="profile" class="profile">
<div class="profile-header">
<h2 id="profile-username"></h2>
</div>
<div class="user-bio" style="margin: 10px 0 20px 0;">
<p id="user-bio-text">"I have no bio."</p>
<button class="edit-bio-button" onclick="showEditBioPopup('')" style="display: none;">Edit Bio</button>
</div>
<div class="profile-stats">
<div class="profile-stat">
<h3>Join Date</h3>
<p id="join-date"></p>
</div>
<div class="profile-stat">
<h3>Place Visits</h3>
<p id="place-visits">0</p>
</div>
<div class="profile-stat">
<h3>Friends</h3>
<p id="friends">0</p>
</div>
<div class="profile-stat">
<h3>Followers</h3>
<p id="followers">0</p>
</div>
<div class="profile-stat">
<h3>Following</h3>
<p id="following">0</p>
</div>
<div class="profile-stat">
<h3>RAP</h3>
<p id="rap">0</p>
</div>
</div>
<div class="equipped-items-container">
<div class="equipped-items-title">Equipped Items</div>
<div id="profile-equipped-items" class="equipped-items-grid"></div>
</div>
<div class="profile-inventory">
<h3 style="text-align: center; font-weight: bold; font-size: 24px; text-decoration: underline;">Inventory</h3>
<div id="profile-inventory-items" class="catalog-items"></div>
<div class="pagination" id="profile-inventory-pagination"></div>
</div>
</div>
<div id="catalog" style="display:none;">
<h2>Catalog</h2>
<div class="sort-options">
<div style="display: flex; align-items: center; justify-content: center; gap: 10px; margin-bottom: 10px;">
<div style="position: relative; display: inline-block;">
<input type="text" id="catalog-search" class="input-field" placeholder="Search items..." style="width: 200px; padding-right: 35px;">
<div onclick="performSearch()" style="position: absolute; right: 10px; top: 50%; transform: translateY(-50%); cursor: pointer;">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
<path d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001c.03.04.062.078.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z"/>
</svg>
</div>
</div>
<select id="catalog-sort">
<option value="newest">Newest Items</option>
<option value="price-asc">Price: Low to High</option>
<option value="price-desc">Price: High to Low</option>
<option value="oldest">Oldest Items</option>
</select>
</div>
</div>
<div id="catalog-items" class="catalog-items"></div>
<div class="pagination" id="catalog-pagination"></div>
</div>
<div id="admin" style="display:none;">
<h2>Admin Panel</h2>
<button class="button" onclick="showUploadPopup()">Upload Catalog Item</button>
<button class="button" onclick="showDatabase()">Database</button>
<button class="button" onclick="showAddUserPopup()">Add User</button>
<button class="button" onclick="showAnnouncementPopup()">Announcement</button>
<button class="button" onclick="showUserSortingPopup()">Sort Users</button>
<button class="button" onclick="showHiddenItems()">View Hidden Items</button>
<div id="current-announcements" style="margin-top: 20px;"></div>
</div>
<div id="inventory" style="display:none;">
<h2>Inventory</h2>
<div class="inventory-toggle">
<span>Private Inventory</span>
<label class="toggle-switch">
<input type="checkbox" id="private-inventory-toggle" onchange="togglePrivateInventory()">
<span class="toggle-slider"></span>
</label>
</div>
<div id="inventory-items" class="catalog-items"></div>
<div class="pagination" id="inventory-pagination"></div>
</div>
<div id="robux-page" class="robux-page" style="display:none;">
<h2><span class="robux-icon"></span><span id="robux-amount-page"></span></h2>
<h3>Transaction History</h3>
<div class="total-spent">Total Spent: <span id="total-spent">0</span> Robux</div>
<div id="transaction-list" class="transaction-list"></div>
<button class="button" onclick="addRobux(400)">Add 400 Robux</button>
<button class="button" onclick="addRobux(800)">Add 800 Robux</button>
<button class="button" onclick="addRobux(1700)">Add 1700 Robux</button>
<button class="button" onclick="addRobux(4500)">Add 4500 Robux</button>
<button class="button" onclick="addRobux(10000)">Add 10000 Robux</button>
<button class="button" onclick="addRobux(22500)">Add 22500 Robux</button>
</div>
<div id="trade" class="trade-page" style="display:none;">
<h2>Trade</h2>
<div class="trade-partners">
<select id="trade-partner" class="input-field">
<option value>Select Trade Partner</option>
</select>
</div>
<div class="trade-status" id="trade-status"></div>
<div class="trade-container">
<div class="trade-side">
<h3>Your Offers</h3>
<div class="trade-value">Total Value: <span id="your-trade-value">0</span></div>
<div id="your-trade-items" class="trade-items"></div>
</div>
<div class="trade-side">
<h3>Their Offers</h3>
<div class="trade-value">Total Value: <span id="their-trade-value">0</span></div>
<div id="their-trade-items" class="trade-items"></div>
</div>
</div>
<div class="trade-buttons">
<button class="button" onclick="sendTrade()" id="send-trade-btn">Send Trade</button>
<button class="button" onclick="clearTrade()">Clear Trade</button>
</div>
<div class="completed-trades">
<div id="completed-trades-list"></div>
</div>
<div class="pending-trades"></div>
<div class="declined-trades"></div>
</div>
<div id="leaderboard" class="users-page" style="display:none;">
<h2>Leaderboard</h2>
<div id="leaderboard-list"></div>
</div>
</div>
<div id="item-detail-page" style="display:none;" class="content">
<button class="button" onclick="backToCatalog()" style="margin-bottom: 20px;">&larr; Back to Catalog</button>
<div style="display: flex; padding: 20px; background-color: #393B3D; border-radius: 10px;">
<div style="flex: 1; position: relative;">
<img id="detail-item-image" src="" alt="" style="width: 100%; max-width: 500px; border-radius: 10px;">
<div id="detail-limited-icon" class="limited-icon" style="position: absolute; top: 379px; left: 139px; width: 200px; height: 211px;"></div>
</div>
<div style="flex: 1; padding-left: 30px;">
<h2 id="detail-item-name" style="font-size: 32px; font-weight: bold; margin-bottom: 10px;"></h2>
<p style="margin-bottom: 20px;">By: Roblox <img src="https://en.help.roblox.com/hc/article_attachments/7997146649876" alt="Verified" style="width: 16px; height: 16px; vertical-align: middle;"></p>
<p style="margin-bottom: 20px;"><strong>Description: </strong><span id="detail-item-description" style="color: #B8B8B8;"></span></p>
<div id="detail-item-price" style="font-size: 24px; font-weight: bold;"></div>
<div id="detail-item-buttons"></div>
</div>
</div>
</div>
<div class="overlay" id="overlay"></div>
<div class="popup" id="upload-popup">
<h3>Upload Catalog Item</h3>
<input type="text" id="item-name" placeholder="Item Name" class="input-field">
<input type="text" id="item-image" placeholder="Image URL" class="input-field" oninput="previewUploadImage()" value="https://i.imgur.com/uXWWQIs.png">
<img id="upload-preview-image" style="max-width: 200px; max-height: 200px; display: none; margin: 10px auto;">
<input type="number" id="item-price" placeholder="Price" class="input-field">
<input type="number" id="item-rap" placeholder="RAP" class="input-field">
<input type="number" id="item-stock" placeholder="Stock (Optional)" class="input-field">
<input type="number" id="item-max-purchase" placeholder="Max Purchase Amount Per User" class="input-field">
<select id="item-limited-status">
<option value="none">Not Limited</option>
<option value="limited">Limited</option>
<option value="unlimited">Unlimited</option>
</select>
<select id="item-timer-status">
<option value="none">None</option>
<option value="timer">Timer</option>
</select>
<div id="timer-fields" style="display: none; margin: 10px 0;">
<div style="display: flex; gap: 10px;">
<input type="number" id="item-timer-hours" placeholder="Hours" class="input-field" min="0" max="720">
<input type="number" id="item-timer-minutes" placeholder="Minutes" class="input-field" min="0" max="59">
<input type="number" id="item-timer-seconds" placeholder="Seconds" class="input-field" min="0" max="59">
</div>
</div>
<div style="margin: 10px 0;">
<label><input type="checkbox" id="item-offsale"> Off Sale</label>
</div>
<div style="margin: 10px 0;">
<label><input type="checkbox" id="item-hidden"> Hidden from Catalog</label>
</div>
<textarea id="item-description" placeholder="Item Description" class="input-field" style="min-height: 100px;"></textarea>
<button class="button" onclick="confirmUpload()">Upload</button>
<button class="button" onclick="closePopup('upload-popup')">Cancel</button>
</div>
<div class="popup" id="database-popup">
<h3>Database</h3>
<input type="text" id="database-search" class="search-bar" placeholder="Search in database...">
<textarea id="database-content" rows="20" cols="50"></textarea>
<button class="button" onclick="saveDatabase()">Save Changes</button>
<button class="button" onclick="closePopup('database-popup')">Close</button>
</div>
<div class="popup" id="users-popup">
<h3>Users</h3>
<div id="users-list"></div>
<button class="button" onclick="closePopup('users-popup')">Close</button>
</div>
<div class="popup" id="add-user-popup">
<h3>Add User</h3>
<input type="text" id="new-username" placeholder="Username" class="input-field">
<button class="button" onclick="addUser()">Add User</button>
<button class="button" onclick="closePopup('add-user-popup')">Cancel</button>
</div>
<div class="popup" id="buy-confirmation-popup">
<h3>Confirm Purchase</h3>
<img id="buy-confirmation-image" src alt="Item Image" style="max-width: 100px; max-height: 100px; display: block; margin: 10px auto;">
<p>Would you like to buy the item <span id="buy-item-name"></span> from Roblox for <span id="buy-item-price"></span>?</p>
<button class="button confirm-button" onclick="confirmPurchase()">Confirm</button>
<button class="button cancel-button" onclick="cancelPurchase()">Cancel</button>
<p>Your balance after this transaction will be <span class="robux-icon"></span><span id="robux-after-purchase"></span>.</p>
</div>
<div class="popup" id="edit-item-popup">
<h3>Edit Catalog Item</h3>
<input type="text" id="edit-item-name" readonly class="input-field">
<input type="text" id="edit-item-name-new" placeholder="New Item Name" class="input-field">
<input type="text" id="edit-item-image" placeholder="New Image URL" class="input-field" oninput="previewEditImage()">
<img id="edit-preview-image" style="max-width: 200px; max-height: 200px; display: none; margin: 10px auto;">
<input type="number" id="edit-item-price" placeholder="New Price" class="input-field">
<input type="number" id="edit-item-rap" placeholder="New RAP" class="input-field">
<input type="number" id="edit-item-max-purchase" placeholder="Max Purchase Amount Per User" class="input-field">
<select id="edit-item-limited-status">
<option value="none">Not Limited</option>
<option value="limited">Limited</option>
<option value="unlimited">Unlimited</option>
</select>
<select id="edit-item-timer-status">
<option value="none">None</option>
<option value="timer">Timer</option>
</select>
<div id="edit-timer-fields" style="display: none; margin: 10px 0;">
<div style="display: flex; gap: 10px;">
<input type="number" id="edit-item-timer-hours" placeholder="Hours" class="input-field" min="0" max="720">
<input type="number" id="edit-item-timer-minutes" placeholder="Minutes" class="input-field" min="0" max="59">
<input type="number" id="edit-item-timer-seconds" placeholder="Seconds" class="input-field" min="0" max="59">
</div>
</div>
<div style="margin: 10px 0;">
<label><input type="checkbox" id="edit-item-offsale"> Off Sale</label>
</div>
<div style="margin: 10px 0;">
<label><input type="checkbox" id="edit-item-hidden"> Hidden from Catalog</label>
</div>
<textarea id="edit-item-description" placeholder="Item Description" class="input-field" style="min-height: 100px;"></textarea>
<div style="margin: 10px 0;">
<label><input type="checkbox" id="edit-item-new-badge"> Give New Badge</label>
</div>
<button class="button" onclick="saveItemChanges()">Save Changes</button>
<button class="button" onclick="closePopup('edit-item-popup')">Cancel</button>
</div>
<div class="popup" id="sell-confirmation-popup">
<h3>Confirm Sale</h3>
<img id="sell-confirmation-image" src alt="Item Image" style="max-width: 100px; max-height: 100px; display: block; margin: 10px auto;">
<p>Are you sure you want to sell this item?</p>
<p>Your Robux after this sale: <span id="robux-after-sale"></span></p>
<button class="button confirm-button" onclick="confirmSale()">Confirm</button>
<button class="button cancel-button" onclick="cancelSale()">Cancel</button>
</div>
<div class="popup" id="upload-confirmation-popup">
<h3>Confirm Upload</h3>
<p>Are you sure you want to upload this item?</p>
<button class="button" onclick="uploadItem()">Confirm</button>
<button class="button" onclick="closePopup('upload-confirmation-popup')">Cancel</button>
</div>
<div class="popup" id="delete-confirmation-popup">
<h3>Confirm Delete</h3>
<p>Are you sure you want to delete this item?</p>
<p>This action cannot be undone and will refund all users who purchased this item.</p>
<button class="button" onclick="confirmDeleteItem()">Yes, Delete</button>
<button class="button" onclick="closePopup('delete-confirmation-popup')">Cancel</button>
</div>
<div class="popup" id="delete-user-confirmation-popup">
<h3>Confirm Delete User</h3>
<p>Are you sure you want to delete this user?</p>
<p>This action cannot be undone.</p>
<button class="button" onclick="confirmDeleteUser()">Yes, Delete</button>
<button class="button" onclick="closePopup('delete-user-confirmation-popup')">Cancel</button>
</div>
<div class="popup" id="item-listings-popup">
<h3>Item Listings</h3>
<p class="clickable" onclick="showPurchaseHistory()">Purchase History</p>
<button class="button" onclick="closePopup('item-listings-popup')">Close</button>
<div id="item-listings"></div>
</div>
<div class="popup" id="purchase-history-popup">
<h3>Purchase History</h3>
<div id="purchase-history"></div>
<button class="button" onclick="closePopup('purchase-history-popup')">Close</button>
</div>
<div class="popup" id="sell-item-popup">
<h3>List Item for Sale</h3>
<div>
<h4>Select Quantity to Sell:</h4>
<select id="quantity-select" class="input-field"></select>
</div>
<input type="number" id="listing-price" class="input-field" placeholder="Price" max="999999999" oninput="updateSellPrice(this.value)">
<p id="seller-earnings" style="color: #B8B8B8; margin: 5px 0;"></p>
<button class="button" onclick="confirmCreateListing()">Create Listing</button>
<button class="button" onclick="closePopup('sell-item-popup')">Cancel</button>
</div>
<div class="popup" id="listing-confirmation-popup"></div>
<div id="owners-popup" class="popup">
<h3>Item Owners</h3>
<div id="owners-list"></div>
<button class="button" onclick="closePopup('owners-popup')">Close</button>
</div>
<div class="popup" id="give-item-popup">
<h3>Give Item</h3>
<select id="recipient-select" class="input-field">
<option value="">Select User</option>
</select>
<input type="number" id="give-quantity" class="input-field" placeholder="Quantity" min="1" value="1">
<button class="button" onclick="confirmGiveItem()">Give Item</button>
<button class="button" onclick="closePopup('give-item-popup')">Cancel</button>
</div>
<div class="popup" id="confirm-give-popup">
<h3>Confirm Give Item</h3>
<p>Are you sure you want to give this item to <span id="recipient-name"></span>?</p>
<button class="button" onclick="giveItem()">Confirm</button>
<button class="button" onclick="closePopup('confirm-give-popup')">Cancel</button>
</div>
<div class="popup" id="edit-bio-popup">
<h3>Edit Bio</h3>
<textarea id="bio-text" class="input-field" rows="5" style="width: 100%"></textarea>
<button class="button" onclick="saveBio()">Save</button>
<button class="button" onclick="closePopup('edit-bio-popup')">Cancel</button>
</div>
<div class="popup" id="remove-item-confirmation-popup">
<h3>Confirm Remove Item</h3>
<p>Are you sure you want to remove <span class="remove-item-name"></span> from <span class="remove-item-username"></span>'s inventory?</p>
<button class="button" onclick="confirmRemoveItem()">Yes, Remove</button>
<button class="button" onclick="closePopup('remove-item-confirmation-popup')">Cancel</button>
</div>
<div class="popup" id="announcement-popup">
<h3>Create Announcement</h3>
<textarea id="announcement-text" class="input-field" placeholder="Announcement text"></textarea>
<select id="announcement-color" class="input-field">
<option value="#ff4444">Red</option>
<option value="#00B06F">Green</option>
<option value="#00A2FF">Blue</option>
<option value="#F78904">Orange</option>
<option value="#9b59b6">Purple</option>
<option value="#e74c3c">Dark Red</option>
<option value="#2ecc71">Dark Green</option>
<option value="#3498db">Light Blue</option>
<option value="#e67e22">Dark Orange</option>
<option value="#16a085">Teal</option>
</select>
<button class="button" onclick="addAnnouncement()">Add Announcement</button>
<button class="button" onclick="closePopup('announcement-popup')">Cancel</button>
</div>
<div class="popup" id="hidden-items-popup">
<h3>Hidden Items</h3>
<div id="hidden-items-list" class="catalog-items" style="max-height: 500px; overflow-y: auto;"></div>
<button class="button" onclick="closePopup('hidden-items-popup')">Close</button>
</div>
<div class="popup" id="edit-announcement-popup">
<h3>Edit Announcement</h3>
<textarea id="edit-announcement-text" class="input-field" placeholder="Edit announcement text"></textarea>
<select id="edit-announcement-color" class="input-field">
<option value="#ff4444">Red</option>
<option value="#00B06F">Green</option>
<option value="#00A2FF">Blue</option>
<option value="#F78904">Orange</option>
<option value="#9b59b6">Purple</option>
<option value="#e74c3c">Dark Red</option>
<option value="#2ecc71">Dark Green</option>
<option value="#3498db">Light Blue</option>
<option value="#e67e22">Dark Orange</option>
<option value="#16a085">Teal</option>
</select>
<button class="button" onclick="saveEditedAnnouncement()">Save Changes</button>
<button class="button" onclick="closePopup('edit-announcement-popup')">Cancel</button>
</div>
<div class="popup" id="delete-user-confirmation-popup">
<div class="popup-content">
<h2>Delete User</h2>
<p>Are you sure you want to delete the user <span class="username"></span>?</p>
<div class="popup-actions">
<button class="button" onclick="closePopup('delete-user-confirmation-popup')">Cancel</button>
<button class="button" onclick="deleteUser()">Yes, Delete</button>
</div>
</div>
</div>
<div class="popup" id="edit-user-popup">
<h3>Edit User</h3>
<label for="edit-username">Username:</label>
<input type="text" id="edit-username" class="input-field" placeholder="Username">
<label for="edit-join-date">Join Date:</label>
<input type="text" id="edit-join-date" class="input-field" placeholder="Join Date">
<label for="edit-place-visits">Place Visits:</label>
<input type="number" id="edit-place-visits" class="input-field" placeholder="Place Visits">
<label for="edit-friends">Friends:</label>
<input type="number" id="edit-friends" class="input-field" placeholder="Friends">
<label for="edit-followers">Followers:</label>
<input type="number" id="edit-followers" class="input-field" placeholder="Followers">
<label for="edit-following">Following:</label>
<input type="number" id="edit-following" class="input-field" placeholder="Following">
<label for="edit-rap">RAP:</label>
<input type="number" id="edit-rap" class="input-field" placeholder="RAP">
<label for="edit-robux">Robux:</label>
<input type="number" id="edit-robux" class="input-field" placeholder="Robux">
<button class="button" onclick="saveUserChanges()">Save Changes</button>
<button class="button" onclick="closePopup('edit-user-popup')">Cancel</button>
</div>
<div class="popup" id="user-sorting-popup">
<h3>Sort Users (Drag and Drop)</h3>
<div id="sortable-users-list" class="sortable-list"></div>
<button class="button" onclick="saveUserOrder()">Save Order</button>
<button class="button" onclick="closePopup('user-sorting-popup')">Cancel</button>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script>
let currentUser = null;
let currentProfileInventoryPage = 1;
let users = [];
let catalogItems = [];
let itemToBuy = null;
let itemToSell = null;
let itemToDelete = null;
let currentCatalogPage = 1;
let currentInventoryPage = 1;
const itemsPerPage = 14;
let pendingTrades = [];
let completedTrades = [];
let declinedTrades = [];
let itemListings = [];
let purchaseHistory = [];
const MAX_EQUIPPED_ITEMS = 10;
let itemToGive = null;
let recipientUsername = null;
let itemDetailSource = 'catalog'; // Possible values: 'catalog', 'profile'
function toggleEquipItem(itemName) {
if (!currentUser) return;
if (!currentUser.equippedItems) {
currentUser.equippedItems = [];
}
const isEquipped = currentUser.equippedItems.includes(itemName);
if (isEquipped) {
// Take off the item
currentUser.equippedItems = currentUser.equippedItems.filter(name => name !== itemName);
} else {
// Put on the item
if (currentUser.equippedItems.length >= MAX_EQUIPPED_ITEMS) {
alert(`You can only have ${MAX_EQUIPPED_ITEMS} equipped items at a time.`);
return;
}
currentUser.equippedItems.push(itemName);
}
saveToLocalStorage();
updateInventory();
updateEquippedItems();
// If on profile page, update the profile equipped items too
if ($('#profile').is(':visible')) {
updateProfileEquippedItems(currentUser);
}
}
function updateEquippedItems() {
if (!currentUser) return;
if (!currentUser.equippedItems) {
currentUser.equippedItems = [];
}
}
function updateProfileEquippedItems(user) {
if (!user) return;
if (!user.equippedItems) {
user.equippedItems = [];
}
let equippedHtml = '';
if (user.equippedItems.length > 0) {
for (let itemName of user.equippedItems) {
const item = user.inventory.find(i => i.name === itemName);
if (item) {
const catalogItem = catalogItems.find(i => i.name === itemName);
const hasRap = catalogItem && catalogItem.rap > 0;
const rapHtml = hasRap ? `<div class="equipped-item-rap">RAP: ${formatNumber(catalogItem.rap)}</div>` : '';
let priceHtml = '';
if (catalogItem) {
if (catalogItem.offSale) {
priceHtml = '<div class="equipped-item-price" style="font-weight: bold; color: #ff4444;">Offsale</div>';
} else if (catalogItem.limitedStatus !== 'none') {
const listings = itemListings.filter(l => l.itemName === itemName)
.sort((a, b) => a.price - b.price);
const lowestListing = listings[0];
if (!lowestListing) {
priceHtml = '<div class="equipped-item-price" style="font-weight: bold;">No Resellers</div>';
} else {
priceHtml = `<div class="equipped-item-price" style="font-weight: bold;">Price: <span class="robux-icon"></span>${formatNumber(lowestListing.price)}</div>`;
}
} else if (catalogItem.price === 0) {
priceHtml = '<div class="equipped-item-price" style="font-weight: bold;">Free</div>';
} else {
priceHtml = `<div class="equipped-item-price" style="font-weight: bold;">Price: <span class="robux-icon"></span>${formatNumber(catalogItem.price)}</div>`;
}
}
equippedHtml += `
<div class="equipped-item">
<img src="${item.image}" alt="${item.name}">
<div class="equipped-item-name">${item.name}</div>
${rapHtml}
${priceHtml}
</div>
`;
}
}
} else {
equippedHtml = '<p style="grid-column: span 5; text-align: center; color: #808080;">No equipped items</p>';
}
$('#profile-equipped-items').html(equippedHtml);
}
function loadFromLocalStorage() {
let data = localStorage.getItem('robloxSimData');
if (data) {
let parsedData = JSON.parse(data);
users = parsedData.users;
catalogItems = parsedData.catalogItems;
completedTrades = parsedData.completedTrades || [];
pendingTrades = parsedData.pendingTrades || [];
declinedTrades = parsedData.declinedTrades || [];
itemListings = parsedData.itemListings || [];
purchaseHistory = parsedData.purchaseHistory || [];
// Initialize equipped items if not present
users.forEach(user => {
if (!user.equippedItems) {
user.equippedItems = [];
}
if (!user.bio) {
user.bio = "I have no bio.";
}
if (user.privateInventory === undefined) {
user.privateInventory = false;
}
if (!user.isAdmin) {
user.isAdmin = false;
}
});
// Remove apostrophes from all item names
catalogItems.forEach(item => {
if (item.name && item.name.includes("'")) {
item.name = item.name.replace(/'/g, "");
}
});
// Update user inventory items to match catalog items without apostrophes
users.forEach(user => {
if (user.inventory) {
user.inventory.forEach(item => {
if (item.name && item.name.includes("'")) {
item.name = item.name.replace(/'/g, "");
}
});
}
});
} else {
users = [{
username: 'Roblox',
joinDate: '1/1/2023',
placeVisits: 0,
friends: 0,
followers: 0,
following: 0,
rap: 0,
robux: 0,
inventory: [],
transactions: [],
equippedItems: [],
bio: "I have no bio.",
isAdmin: true
}];
completedTrades = [];
pendingTrades = [];
declinedTrades = [];
}
}
function verifyUser(username) {
let user = users.find(u => u.username === username);
if (user) {
user.verified = !user.verified;
saveToLocalStorage();
showUserProfile(username);
showSuccessBanner(user.verified ? 'User verified!' : 'User unverified!');
}
}
function resetUsername(username) {
let user = users.find(u => u.username === username);
if (user && user.username !== 'Roblox') {
let newUsername = prompt('Enter new username:');
if (newUsername && newUsername !== 'Roblox' && !users.some(u => u.username === newUsername)) {
pendingTrades.forEach(trade => {
if (trade.sender === user.username) trade.sender = newUsername;
if (trade.receiver === user.username) trade.receiver = newUsername;
});
completedTrades.forEach(trade => {
if (trade.sender === user.username) trade.sender = newUsername;
if (trade.receiver === user.username) trade.receiver = newUsername;
});
catalogItems.forEach(item => {
if (item.newViewedBy) {
let index = item.newViewedBy.indexOf(user.username);
if (index !== -1) {
item.newViewedBy[index] = newUsername;
}
}
});
user.username = newUsername;
saveToLocalStorage();
showUserProfile(newUsername);
showSuccessBanner('Username reset successfully!');
} else {
alert('Invalid username or username already exists.');
}
}
}
function banUser(username) {
let user = users.find(u => u.username === username);
if (user && user.username !== 'Roblox') {
user.banned = !user.banned;
if (user.banned) {
pendingTrades = pendingTrades.filter(trade => trade.sender !== user.username && trade.receiver !== user.username);
}
saveToLocalStorage();
showUserProfile(username);
showSuccessBanner(user.banned ? 'User banned!' : 'User unbanned!');
}
}
function makeAdmin(username) {
let user = users.find(u => u.username === username);
if (user) {
user.isAdmin = !user.isAdmin;
saveToLocalStorage();
showUserProfile(username);
showSuccessBanner(user.isAdmin ? 'User is now an admin!' : 'User is no longer an admin!');
}
}
function showUserProfile(username) {
$('.profile-inventory').scrollTop(0);
let user = users.find(u => u.username === username);
if (user) {
let verifiedBadge = user.verified ? ' <img src="https://en.help.roblox.com/hc/article_attachments/7997146649876" alt="Verified" class="verified-badge">' : '';
let adminBadge = user.isAdmin ? ' <img src="https://i.imgur.com/WRPVUAh.png" alt="Admin" class="admin-badge">' : '';
$('#profile-username').html(`
<div class="profile-username-display" style="color: ${user.banned ? 'red' : 'white'}">
${adminBadge}${user.username}${verifiedBadge}
</div>
`);
$('#profile-username').css('color', user.banned ? 'red' : '');
$('#join-date').text(user.joinDate);
$('#place-visits').text(formatNumber(user.placeVisits));
$('#friends').text(formatNumber(user.friends));
$('#followers').text(formatNumber(user.followers));
$('#following').text(formatNumber(user.following));
let totalRap = 0;
if (user.inventory) {
totalRap = user.inventory.reduce((sum, item) => {
let catalogItem = catalogItems.find(i => i.name === item.name);
let itemRap = catalogItem ? catalogItem.rap : item.rap;
return sum + itemRap * (item.quantity || 1);
}, 0);
}
$('#rap').text(formatNumber(totalRap));
// Update bio text
$('#user-bio-text').text(`"${user.bio || "I have no bio."}"`);
$('.edit-bio-button').css('display', currentUser && (currentUser.username === user.username || currentUser.username === 'Roblox') ? 'block' : 'none');
$('.edit-bio-button').attr('onclick', `showEditBioPopup('${user.username}')`);
updateProfileEquippedItems(user);
updateProfileInventory(user);
showProfile();
if (currentUser.username === 'Roblox') {
$('.admin-options').remove();
let adminOptionsHtml = `
<div class="admin-options">
<button class="button" onclick="verifyUser('${user.username}')">${user.verified ? 'Unverify' : 'Verify'}</button>
<button class="button" onclick="resetUsername('${user.username}')">Reset Username</button>
<button class="button" onclick="banUser('${user.username}')">${user.banned ? 'Unban' : 'Ban'}</button>
<button class="button" onclick="showDeleteUserConfirmation('${user.username}')">Delete</button>
<button class="button" onclick="makeAdmin('${user.username}')">${user.isAdmin ? 'Remove Admin' : 'Make Admin'}</button>
<button class="button" onclick="showEditUserPopup('${user.username}')">Edit</button>
</div>`;
$('.profile-header').append(adminOptionsHtml);
} else {
$('.admin-options').remove();
}
}
}
function showProfile() {
$('#profile').show();
$('#catalog').hide();
$('#admin').hide();
$('#inventory').hide();
$('#robux-page').hide();
$('#users-page').hide();
$('#trade').hide();
$('#leaderboard').hide();
setActiveNavItem('profile');
}
function showCatalog() {
$('#profile').hide();
$('#catalog').show();
$('#admin').hide();
$('#inventory').hide();
$('#robux-page').hide();
$('#users-page').hide();
$('#trade').hide();
$('#leaderboard').hide();
updateCatalog();
setActiveNavItem('catalog');
}
function showAdmin() {
$('#profile').hide();
$('#catalog').hide();
$('#admin').show();
$('#inventory').hide();
$('#robux-page').hide();
$('#users-page').hide();
$('#trade').hide();
$('#leaderboard').hide();
setActiveNavItem('admin');
}
function showInventory() {
$('#profile').hide();
$('#catalog').hide();
$('#admin').hide();
$('#inventory').show();
$('#robux-page').hide();
$('#users-page').hide();
$('#trade').hide();
$('#leaderboard').hide();
updateInventory();
setActiveNavItem('inventory');
}
function showRobuxPage() {
$('#profile').hide();
$('#catalog').hide();
$('#admin').hide();
$('#inventory').hide();
$('#robux-page').show();
$('#trade').hide();
$('#leaderboard').hide();
updateRobuxPage();
setActiveNavItem('robux');
}
function showTrade() {
$('#profile').hide();
$('#catalog').hide();
$('#admin').hide();
$('#inventory').hide();
$('#robux-page').hide();
$('#trade').show();
$('#leaderboard').hide();
updateTradePartners();
updateTradeItems();
updatePendingTrades();
updateCompletedTrades();
updateDeclinedTrades();
setActiveNavItem('trade');
}
function showLeaderboard() {
$('#profile').hide();
$('#catalog').hide();
$('#admin').hide();
$('#inventory').hide();
$('#robux-page').hide();
$('#trade').hide();
$('#leaderboard').show();
updateLeaderboard();
setActiveNavItem('leaderboard');
}
function updateTradePartners() {
let html = '<option value="">Select Trade Partner</option>';
users.forEach(user => {
if (user.username !== currentUser.username && !user.banned && !user.privateInventory) {
html += `<option value="${user.username}">${user.username}</option>`;
}
});
$('#trade-partner').html(html);
}
let selectedItems = {
yours: [],
theirs: []
};
function updateTradeItems() {
if (!currentUser) return;
let yourHtml = '';
let theirHtml = '';
let partner = users.find(u => u.username === $('#trade-partner').val());
if (!partner) return;
let yourSortedItems = currentUser.inventory ? [...currentUser.inventory].sort((a, b) => b.price - a.price) : [];
let theirSortedItems = partner && partner.inventory ? [...partner.inventory].sort((a, b) => b.price - a.price) : [];
if (yourSortedItems.length) {
yourSortedItems.forEach(item => {
if (item.rap > 0) {
let catalogItem = catalogItems.find(i => i.name === item.name);
let currentRap = catalogItem ? catalogItem.rap : item.rap;
let currentQuantity = selectedItems.yours.filter(i => i === item.name).length;
let maxQuantity = item.quantity || 1;
let selected = currentQuantity > 0 ? 'selected' : '';
yourHtml += `
<div class="trade-item ${selected}" onclick="toggleTradeItem('yours', &quot;${escapeHtml(item.name)}&quot;)">
<img src="${item.image}" alt="${item.name}">
<p>${item.name}</p>
<p>RAP per item: ${formatNumber(currentRap)}</p>
<p>You own: ${maxQuantity}</p>
<div class="trade-item-quantity">
<button onclick="changeQuantity('yours', &quot;${escapeHtml(item.name)}&quot;, -1)" ${currentQuantity === 0 ? 'disabled' : ''}>-</button>
<span>${currentQuantity}</span>
<button onclick="changeQuantity('yours', &quot;${escapeHtml(item.name)}&quot;, 1)" ${currentQuantity >= maxQuantity ? 'disabled' : ''}>+</button>
</div>
</div>
`;
}
});
}
if (partner && theirSortedItems.length) {
theirSortedItems.forEach(item => {
if (item.rap > 0) {
let catalogItem = catalogItems.find(i => i.name === item.name);
let currentRap = catalogItem ? catalogItem.rap : item.rap;
let currentQuantity = selectedItems.theirs.filter(i => i === item.name).length;
let maxQuantity = item.quantity || 1;
let selected = currentQuantity > 0 ? 'selected' : '';
theirHtml += `
<div class="trade-item ${selected}" onclick="toggleTradeItem('theirs', &quot;${escapeHtml(item.name)}&quot;)">
<img src="${item.image}" alt="${item.name}">
<p>${item.name}</p>
<p>RAP per item: ${formatNumber(currentRap)}</p>
<p>They own: ${maxQuantity}</p>
<div class="trade-item-quantity">
<button onclick="changeQuantity('theirs', &quot;${escapeHtml(item.name)}&quot;, -1)" ${currentQuantity === 0 ? 'disabled' : ''}>-</button>
<span>${currentQuantity}</span>
<button onclick="changeQuantity('theirs', &quot;${escapeHtml(item.name)}&quot;, 1)" ${currentQuantity >= maxQuantity ? 'disabled' : ''}>+</button>
</div>
</div>
`;
}
});
}
$('#your-trade-items').html(yourHtml || '<p>No tradeable items found</p>');
$('#their-trade-items').html(theirHtml || '<p>No tradeable items found</p>');
$('#trade-status').text('');
$('#send-trade-btn').prop('disabled', currentUser.banned);
$('.trade-buttons button').prop('disabled', currentUser.banned);
updateTradeValues();
}
function updateTradeValues() {
let yourTotalValue = selectedItems.yours.reduce((total, itemName) => {
let userItem = currentUser.inventory.find(i => i.name === itemName);
let catalogItem = catalogItems.find(i => i.name === itemName);
let itemRap = catalogItem ? catalogItem.rap : userItem.rap;
return total + itemRap;
}, 0);
let partner = users.find(u => u.username === $('#trade-partner').val());
let theirTotalValue = selectedItems.theirs.reduce((total, itemName) => {
let partnerItem = partner?.inventory.find(i => i.name === itemName);
let catalogItem = catalogItems.find(i => i.name === itemName);
let itemRap = catalogItem ? catalogItem.rap : (partnerItem ? partnerItem.rap : 0);
return total + itemRap;
}, 0);
$('#your-trade-value').text(formatNumber(yourTotalValue));
$('#their-trade-value').text(formatNumber(theirTotalValue));
}
function toggleTradeItem(side, itemName) {
let partner = users.find(u => u.username === $('#trade-partner').val());
let inventory = side === 'yours' ? currentUser.inventory : partner.inventory;
let item = inventory.find(i => i.name === itemName);
if (!item || item.rap === 0) return;
let currentQuantity = selectedItems[side].filter(i => i === itemName).length;
let maxQuantity = item.quantity || 1;
if (currentQuantity >= maxQuantity) {
selectedItems[side] = selectedItems[side].filter(i => i !== itemName);
} else {
selectedItems[side].push(itemName);
}
updateTradeItems();
updateTradeValues();
}
function changeQuantity(side, itemName, change) {
event.stopPropagation();
let partner = users.find(u => u.username === $('#trade-partner').val());
let inventory = side === 'yours' ? currentUser.inventory : partner.inventory;
let item = inventory.find(i => i.name === itemName);
if (!item || item.rap === 0) return;
let currentQuantity = selectedItems[side].filter(i => i === itemName).length;
let maxQuantity = item.quantity || 1;
if (change > 0 && currentQuantity < maxQuantity) {
selectedItems[side].push(itemName);
} else if (change < 0 && currentQuantity > 0) {
let index = selectedItems[side].lastIndexOf(itemName);
if (index !== -1) {
selectedItems[side].splice(index, 1);
}
}
updateTradeItems();
updateTradeValues();
}
function clearTrade() {
if (currentUser && currentUser.banned) return;
selectedItems = {
yours: [],
theirs: []
};
updateTradeItems();
updateTradeValues();
}
function sendTrade() {
if (!currentUser || currentUser.banned) return;
let partner = users.find(u => u.username === $('#trade-partner').val());
if (!partner) return;
let existingTrade = pendingTrades.find(t => t.sender === currentUser.username && t.receiver === partner.username || t.sender === partner.username && t.receiver === currentUser.username);
if (existingTrade) {
pendingTrades = pendingTrades.filter(t => t.id !== existingTrade.id);
}
showLoadingScreen();
setTimeout(() => {
let newTrade = {
id: Date.now(),
sender: currentUser.username,
receiver: partner.username,
senderItems: selectedItems.yours.map(itemName => {
let item = currentUser.inventory.find(i => i.name === itemName);
return {
name: itemName,
rap: item.rap
};
}),
receiverItems: selectedItems.theirs.map(itemName => {
let item = partner.inventory.find(i => i.name === itemName);
return {
name: itemName,
rap: item.rap
};
}),
timestamp: new Date().toISOString(),
status: 'pending'
};
pendingTrades.push(newTrade);
saveToLocalStorage();
hideLoadingScreen();
showSuccessBanner('Trade request sent!');
clearTrade();
updateTradeItems();
updatePendingTrades();
}, 1200);
}
function editTrade(tradeId) {
let trade = pendingTrades.find(t => t.id === tradeId);
if (!trade) return;
$('#trade-partner').val(trade.sender === currentUser.username ? trade.receiver : trade.sender);
selectedItems = {
yours: [],
theirs: []
};
if (trade.receiver === currentUser.username) {
trade.receiverItems.forEach(item => {
selectedItems.yours.push(item.name);
});
trade.senderItems.forEach(item => {
selectedItems.theirs.push(item.name);
});
} else {
trade.senderItems.forEach(item => {
selectedItems.yours.push(item.name);
});
trade.receiverItems.forEach(item => {
selectedItems.theirs.push(item.name);
});
}
pendingTrades = pendingTrades.filter(t => t.id !== tradeId);
saveToLocalStorage();
updateTradeItems();
updatePendingTrades();
}
function updateUserRAP(user) {
if (typeof user.rap === 'number') return;
if (!user.inventory) return;
user.rap = user.inventory.reduce((sum, item) => {
let catalogItem = catalogItems.find(i => i.name === item.name);
let itemRap = catalogItem ? catalogItem.rap : item.rap;
return sum + itemRap * (item.quantity || 1);
}, 0);
}
function updateDeclinedTrades() {
let relevantDeclinedTrades = declinedTrades.filter(trade => trade.sender === currentUser.username || trade.receiver === currentUser.username).sort((a, b) => new Date(b.declinedAt) - new Date(a.declinedAt));
let html = '<h3>Declined Trades</h3>';
relevantDeclinedTrades.forEach(trade => {
let senderItems = [];
let receiverItems = [];
trade.senderItems.forEach(item => {
if (!senderItems[item.name]) {
let catalogItem = catalogItems.find(i => i.name === item.name);
let currentRap = catalogItem ? catalogItem.rap : item.rap;
senderItems[item.name] = {
name: item.name,
rap: currentRap,
count: 1
};
} else {
senderItems[item.name].count++;
}
});
trade.receiverItems.forEach(item => {
if (!receiverItems[item.name]) {
let catalogItem = catalogItems.find(i => i.name === item.name);
let currentRap = catalogItem ? catalogItem.rap : item.rap;
receiverItems[item.name] = {
name: item.name,
rap: currentRap,
count: 1
};
} else {
receiverItems[item.name].count++;
}
});
let senderItemsList = Object.values(senderItems);
let receiverItemsList = Object.values(receiverItems);
let senderTotalRap = senderItemsList.reduce((sum, item) => sum + item.rap * item.count, 0);
let receiverTotalRap = receiverItemsList.reduce((sum, item) => sum + item.rap * item.count, 0);
// Determine if the current user is the sender or receiver
const isSender = trade.sender === currentUser.username;
const senderLabel = isSender ? `${trade.sender} (GIVING)` : `${trade.sender} (GETTING)`;
const receiverLabel = isSender ? `${trade.receiver} (GETTING)` : `${trade.receiver} (GIVING)`;
html += `
<div class="completed-trade" style="border-left: 3px solid #ff4444;">
<div class="trade-details">
<div class="trade-side-details">
<h5>${senderLabel}'s Offer (Total RAP: ${formatNumber(senderTotalRap)})</h5>
<div class="trade-items-list">
${senderItemsList.map(item => `
<div class="trade-item-entry">
<img src="${catalogItems.find(i => i.name === item.name)?.image || ''}" alt="${item.name}">
<span>${item.name} x${item.count} (Each Item Value: ${formatNumber(item.rap)})</span>
</div>
`).join('')}
</div>
</div>
<div class="trade-side-details">
<h5>${receiverLabel}'s Offer (Total RAP: ${formatNumber(receiverTotalRap)})</h5>
<div class="trade-items-list">
${receiverItemsList.map(item => `
<div class="trade-item-entry">
<img src="${catalogItems.find(i => i.name === item.name)?.image || ''}" alt="${item.name}">
<span>${item.name} x${item.count} (Each Item Value: ${formatNumber(item.rap)})</span>
</div>
`).join('')}
</div>
</div>
</div>
<div class="trade-timestamp">Declined by ${trade.declinedBy} on ${new Date(trade.declinedAt).toLocaleString()}</div>
</div>
`;
});
let declinedTradesContainer = document.createElement('div');
declinedTradesContainer.className = 'declined-trades';
declinedTradesContainer.innerHTML = html;
$('.declined-trades').remove();
$('.completed-trades').after(declinedTradesContainer);
}
function groupItems(items) {
let grouped = {};
items.forEach(item => {
if (!grouped[item.name]) {
grouped[item.name] = {
name: item.name,
rap: item.rap,
count: 1
};
} else {
grouped[item.name].count++;
}
});
return Object.values(grouped);
}
function updatePendingTrades() {
let relevantTrades = pendingTrades.filter(trade => trade.sender === currentUser.username || trade.receiver === currentUser.username).sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp));
let html = '<div class="pending-trades"><h3>Pending Trades</h3>';
relevantTrades.forEach(trade => {
let groupedSenderItems = {};
let groupedReceiverItems = {};
trade.senderItems.forEach(item => {
let catalogItem = catalogItems.find(i => i.name === item.name);
let currentRap = catalogItem ? catalogItem.rap : item.rap;
if (!groupedSenderItems[item.name]) {
groupedSenderItems[item.name] = {
name: item.name,
rap: currentRap,
count: 1
};
} else {
groupedSenderItems[item.name].count++;
}
});
trade.receiverItems.forEach(item => {
let catalogItem = catalogItems.find(i => i.name === item.name);
let currentRap = catalogItem ? catalogItem.rap : item.rap;
if (!groupedReceiverItems[item.name]) {
groupedReceiverItems[item.name] = {
name: item.name,
rap: currentRap,
count: 1
};
} else {
groupedReceiverItems[item.name].count++;
}
});
let senderItems = Object.values(groupedSenderItems);
let receiverItems = Object.values(groupedReceiverItems);
let senderTotalRap = senderItems.reduce((sum, item) => sum + item.rap * item.count, 0);
let receiverTotalRap = receiverItems.reduce((sum, item) => sum + item.rap * item.count, 0);
// Determine if the current user is the sender or receiver
const isSender = trade.sender === currentUser.username;
const senderLabel = isSender ? `${trade.sender} (GIVING)` : `${trade.sender} (GETTING)`;
const receiverLabel = isSender ? `${trade.receiver} (GETTING)` : `${trade.receiver} (GIVING)`;
html += `
<div class="pending-trade">
<div class="trade-details">
<div class="trade-side-details">
<h5>${senderLabel}'s Offer (Total RAP: ${formatNumber(senderTotalRap)})</h5>
<div class="trade-items-list">
${senderItems.map(item => `
<div class="trade-item-entry">
<img src="${catalogItems.find(i => i.name === item.name)?.image || ''}" alt="${item.name}">
<span>${item.name} x${item.count} (Each Item Value: ${formatNumber(item.rap)})</span>
</div>
`).join('')}
</div>
</div>
<div class="trade-side-details">
<h5>${receiverLabel}'s Offer (Total RAP: ${formatNumber(receiverTotalRap)})</h5>
<div class="trade-items-list">
${receiverItems.map(item => `
<div class="trade-item-entry">
<img src="${catalogItems.find(i => i.name === item.name)?.image || ''}" alt="${item.name}">
<span>${item.name} x${item.count} (Each Item Value: ${formatNumber(item.rap)})</span>
</div>
`).join('')}
</div>
</div>
</div>
<div class="trade-timestamp">Sent: ${new Date(trade.timestamp).toLocaleString()}</div>
<div class="trade-actions">
<button class="button" onclick="editTrade(${trade.id})">Edit</button>
${trade.receiver === currentUser.username ? `
<button class="button" onclick="acceptTrade(${trade.id})">Accept</button>
` : ''}
<button class="button" onclick="declineTrade(${trade.id})">${trade.receiver === currentUser.username ? 'Decline' : 'Cancel'}</button>
</div>
</div>
`;
});
html += '</div>';
$('.pending-trades').remove();
$('.completed-trades').before(html);
}
function groupTradeItems(items) {
let grouped = {};
items.forEach(item => {
if (!grouped[item.name]) {
grouped[item.name] = {
name: item.name,
rap: item.rap,
count: 1
};
} else {
grouped[item.name].count++;
}
});
return Object.values(grouped);
}
function updateCompletedTrades() {
let relevantCompletedTrades = completedTrades.filter(trade => trade.sender === currentUser.username || trade.receiver === currentUser.username).sort((a, b) => new Date(b.completedAt) - new Date(a.completedAt));
let html = '<h3>Completed Trades</h3>';
relevantCompletedTrades.forEach(trade => {
let senderItems = groupTradeItems(trade.senderItems.map(item => {
let catalogItem = catalogItems.find(i => i.name === item.name);
return {
name: item.name,
rap: catalogItem ? catalogItem.rap : item.rap
};
}));
let receiverItems = groupTradeItems(trade.receiverItems.map(item => {
let catalogItem = catalogItems.find(i => i.name === item.name);
return {
name: item.name,
rap: catalogItem ? catalogItem.rap : item.rap
};
}));
let senderTotalRap = senderItems.reduce((sum, item) => sum + item.rap * item.count, 0);
let receiverTotalRap = receiverItems.reduce((sum, item) => sum + item.rap * item.count, 0);
// Determine if the current user is the sender or receiver
const isSender = trade.sender === currentUser.username;
const senderLabel = isSender ? `${trade.sender} (GIVING)` : `${trade.sender} (GETTING)`;
const receiverLabel = isSender ? `${trade.receiver} (GETTING)` : `${trade.receiver} (GIVING)`;
html += `
<div class="completed-trade">
<div class="trade-details">
<div class="trade-side-details">
<h5>${senderLabel}'s Offer (Total RAP: ${formatNumber(senderTotalRap)})</h5>
<div class="trade-items-list">
${senderItems.map(item => `
<div class="trade-item-entry">
<img src="${catalogItems.find(i => i.name === item.name)?.image || ''}" alt="${item.name}">
<span>${item.name} x${item.count} (Each Item Value: ${formatNumber(item.rap)})</span>
</div>
`).join('')}
</div>
</div>
<div class="trade-side-details">
<h5>${receiverLabel}'s Offer (Total RAP: ${formatNumber(receiverTotalRap)})</h5>
<div class="trade-items-list">
${receiverItems.map(item => `
<div class="trade-item-entry">
<img src="${catalogItems.find(i => i.name === item.name)?.image || ''}" alt="${item.name}">
<span>${item.name} x${item.count} (Each Item Value: ${formatNumber(item.rap)})</span>
</div>
`).join('')}
</div>
</div>
</div>
<div class="trade-timestamp">Completed: ${new Date(trade.completedAt).toLocaleString()}</div>
</div>
`;
});
$('#completed-trades-list').html(html);
}
function updateUsersPage() {
let usersHtml = '';
for (let user of users) {
usersHtml += `<p class="${user.username === currentUser.username ? 'current-user' : ''}" onclick="showUserProfile('${user.username}')">${user.username}</p>`;
}
$('#users-list-page').html(usersHtml);
}
function updateProfileInventory(user) {
let inventoryHtml = '';
// Check if inventory is private and viewer is not owner or Roblox
const isPrivate = user.privateInventory && currentUser.username !== user.username && currentUser.username !== 'Roblox';
if (isPrivate) {
inventoryHtml = '<p style="text-align: center; font-size: 20px; color: #B8B8B8; margin-top: 50px;">This user\'s inventory is private</p>';
$('#profile-inventory-items').html(inventoryHtml);
$('#profile-inventory-pagination').hide();
// Set RAP display to "Private"
$('#rap').text('Private');
return;
}
if (user.inventory) {
let expandedInventory = [];
user.inventory.forEach(item => {
for (let i = 0; i < (item.quantity || 1); i++) {
expandedInventory.push(item);
}
});
let sortedInventory = expandedInventory.sort((a, b) => {
// Remove the equipped items sorting logic
let catalogItemA = catalogItems.find(i => i.name === a.name);
let catalogItemB = catalogItems.find(i => i.name === b.name);
let isLimitedA = catalogItemA && (catalogItemA.limitedStatus === 'limited' || catalogItemA.limitedStatus === 'unlimited');
let isLimitedB = catalogItemB && (catalogItemB.limitedStatus === 'limited' || catalogItemB.limitedStatus === 'unlimited');
let isOffSaleA = catalogItemA && catalogItemA.offSale;
let isOffSaleB = catalogItemB && catalogItemB.offSale;
if (isLimitedA && !isLimitedB) return -1;
if (!isLimitedA && isLimitedB) return 1;
if (isLimitedA && isLimitedB) {
return (catalogItemB.rap || 0) - (catalogItemA.rap || 0);
}
if (isOffSaleA && !isOffSaleB) return 1;
if (!isOffSaleA && isOffSaleB) return -1;
return (b.price || 0) - (a.price || 0);
});
for (let item of sortedInventory) {
let limitedIcon = '';
if (item.limitedStatus === 'unlimited') {
limitedIcon = '<div class="limited-icon unlimited"></div>';
} else if (item.limitedStatus === 'limited') {
limitedIcon = '<div class="limited-icon limited"></div>';
}
let catalogItem = catalogItems.find(i => i.name === item.name);
let isOffSale = catalogItem && catalogItem.offSale;
const listings = itemListings.filter(l => l.itemName === item.name)
.sort((a, b) => a.price - b.price);
const lowestListing = listings[0];
const priceDisplay = (catalogItem.limitedStatus !== 'none' && !lowestListing) ?
'<p style="color: #B8B8B8; font-weight: bold;">No Resellers</p>' :
(lowestListing ?
`<p><span class="robux-icon"></span>${formatNumber(lowestListing.price)}</p>` :
(catalogItem.price === 0 ?
'<p><span class="robux-icon"></span>Free</p>' :
`<p><span class="robux-icon"></span>${formatNumber(catalogItem.price)}</p>`));
let removeButton = '';
if (currentUser.username === 'Roblox') {
removeButton = `<button class="button" style="background-color: #ff4444; margin-top: 5px;" onclick="event.stopPropagation(); showRemoveItemConfirmation('${user.username}', '${escapeHtml(item.name)}')">Remove</button>`;
}
inventoryHtml += `
<div class="catalog-item" onclick="showItemDetail(&quot;${escapeHtml(item.name)}&quot;);" style="cursor: pointer;">
<img src="${item.image}" alt="${item.name}">
${limitedIcon}
<div class="catalog-item-details">
<h3>${item.name}</h3>
${isOffSale ? '<p style="color: #ff4444;">Offsale</p>' : ''}
${(item.limitedStatus !== 'none' && !item.offSale) ? `<p>RAP: ${formatNumber(catalogItem.rap)}</p>` : ''}
<p>By: Roblox <img src="https://en.help.roblox.com/hc/article_attachments/7997146649876" alt="Verified" style="width: 16px; height: 16px; vertical-align: middle;"></p>
${removeButton}
</div>
</div>
`;
}
$('#profile-inventory-items').html(inventoryHtml || '<p>No items in inventory</p>');
updateProfileInventoryPagination(Math.ceil(sortedInventory.length / itemsPerPage), currentProfileInventoryPage);
}
}
function updateProfileInventoryPagination(totalPages, currentPage) {
let paginationHtml = `
<button onclick="updateProfileInventoryPage(${Math.max(1, currentPage - 1)})" ${currentPage === 1 ? 'disabled' : ''}>&larr;</button>
<span>${currentPage} / ${totalPages}</span>
<button onclick="updateProfileInventoryPage(${Math.min(totalPages, currentPage + 1)})" ${currentPage === totalPages ? 'disabled' : ''}>&rarr;</button>
`;
$('#profile-inventory-pagination').html(paginationHtml);
}
function updateProfileInventoryPage(page) {
currentProfileInventoryPage = page;
let user = users.find(u => u.username === currentUser.username);
if (user) {
let inventory = user.inventory;
if (inventory) {
let expandedInventory = [];
inventory.forEach(item => {
for (let i = 0; i < (item.quantity || 1); i++) {
expandedInventory.push(item);
}
});
let sortedInventory = expandedInventory.sort((a, b) => {
// Remove the equipped items sorting logic
let catalogItemA = catalogItems.find(i => i.name === a.name);
let catalogItemB = catalogItems.find(i => i.name === b.name);
let isLimitedA = catalogItemA && (catalogItemA.limitedStatus === 'limited' || catalogItemA.limitedStatus === 'unlimited');
let isLimitedB = catalogItemB && (catalogItemB.limitedStatus === 'limited' || catalogItemB.limitedStatus === 'unlimited');
let isOffSaleA = catalogItemA && catalogItemA.offSale;
let isOffSaleB = catalogItemB && catalogItemB.offSale;
if (isLimitedA && !isLimitedB) return -1;
if (!isLimitedA && isLimitedB) return 1;
if (isLimitedA && isLimitedB) {
return (catalogItemB.rap || 0) - (catalogItemA.rap || 0);
}
if (isOffSaleA && !isOffSaleB) return 1;
if (!isOffSaleA && isOffSaleB) return -1;
return (b.price || 0) - (a.price || 0);
});
let startIndex = (currentProfileInventoryPage - 1) * itemsPerPage;
let endIndex = startIndex + itemsPerPage;
let pageItems = sortedInventory.slice(startIndex, endIndex);
let inventoryHtml = '';
for (let item of pageItems) {
if (!item) continue;
let limitedIcon = '';
if (item.limitedStatus === 'unlimited') {
limitedIcon = '<div class="limited-icon unlimited"></div>';
} else if (item.limitedStatus === 'limited') {
limitedIcon = '<div class="limited-icon limited"></div>';
}
let catalogItem = catalogItems.find(i => i.name === item.name) || {};
let isOffSale = catalogItem && catalogItem.offSale;
const listings = itemListings.filter(l => l.itemName === item.name)
.sort((a, b) => a.price - b.price);
const lowestListing = listings[0];
const priceDisplay = (catalogItem.limitedStatus !== 'none' && !lowestListing) ?
'<p style="color: #B8B8B8; font-weight: bold;">No Resellers</p>' :
(lowestListing ?
`<p><span class="robux-icon"></span>${formatNumber(lowestListing.price)}</p>` :
(catalogItem.price === 0 ?
'<p><span class="robux-icon"></span>Free</p>' :
`<p><span class="robux-icon"></span>${formatNumber(catalogItem.price)}</p>`));
let userQuantity = currentUser.inventory?.find(i => i.name === item.name)?.quantity || 0;
let userQuantityDisplay = userQuantity > 0 ? `<div class="item-quantity">x${userQuantity}</div>` : '';
let buyButtonDisabled = item.offSale || currentUser.banned;
if (!item.limitedStatus && !item.stock && item.timerStatus !== 'timer' && item.quantity > 0) {
buyButtonDisabled = true;
}
if (item.maxPurchase !== null && userQuantity >= item.maxPurchase) {
buyButtonDisabled = true;
}
let canSell = currentUser && !currentUser.banned &&
currentUser.inventory && currentUser.inventory.some(i => i.name === item.name) &&
(item.limitedStatus === 'limited' || item.limitedStatus === 'unlimited');
let purchasesDisplay = (item.limitedStatus === 'limited' || item.limitedStatus === 'unlimited') ?
`<p style="color: #B8B8B8;">Purchases: ${formatNumber(item.purchases || 0)}</p>` : '';
inventoryHtml += `
<div class="catalog-item" onclick="showItemDetail(&quot;${escapeHtml(item.name)}&quot;);" style="cursor: pointer;">
<img src="${item.image}" alt="${item.name}">
${limitedIcon}
${userQuantityDisplay}
<div class="catalog-item-details">
<h3>${item.name}</h3>
${isOffSale ? '<p style="color: #ff4444;">Offsale</p>' : ''}
${(item.limitedStatus !== 'none' && !item.offSale) ? `<p>RAP: ${formatNumber(catalogItem.rap)}</p>` : ''}
${purchasesDisplay}
<p>By: Roblox <img src="https://en.help.roblox.com/hc/article_attachments/7997146649876" alt="Verified" style="width: 16px; height: 16px; vertical-align: middle;"></p>
</div>
</div>
`;
}
$('#profile-inventory-items').html(inventoryHtml || '<p>No items in inventory</p>');
}
}
}
function setActiveNavItem(page) {
$('.nav a').removeClass('active');
if (page === 'trade') {
$('.nav a[onclick*="showTrade"]').addClass('active');
} else if (page === 'leaderboard') {
$('.nav a[onclick*="showLeaderboard"]').addClass('active');
} else {
$(`.nav a[onclick*="show${page.charAt(0).toUpperCase() + page.slice(1)}"]`).addClass('active');
}
}
function updateProfile() {
if (!currentUser) return;
showUserProfile(currentUser.username);
updateRobuxDisplay();
}
function updateRobuxDisplay() {
if (!currentUser) return;
let robuxAmount = currentUser.robux;
let displayAmount = robuxAmount < 10000 ? formatNumber(robuxAmount) : abbreviateNumber(robuxAmount);
$('#robux-amount').html('<strong>' + displayAmount + '</strong>');
$('#robux-amount-page').text(formatNumber(robuxAmount));
}
function abbreviateNumber(number) {
if (number >= 1000000000000) {
let trillions = number / 1000000000000;
if (trillions >= 100) {
return Math.floor(trillions) + 'T+';
}
return (Math.floor(trillions * 10) / 10).toString().replace('.0', '') + 'T+';
} else if (number >= 1000000000) {
let billions = number / 1000000000;
if (billions >= 100) {
return Math.floor(billions) + 'B+';
} else if (billions >= 10) {
return (Math.floor(billions * 10) / 10).toString().replace('.0', '') + 'B+';
} else {
return (Math.floor(billions * 10) / 10).toString().replace('.0', '') + 'B+';
}
} else if (number >= 1000000) {
return (Math.floor(number / 1000000 * 10) / 10).toString().replace('.0', '') + 'M+';
} else if (number >= 10000) {
return (Math.floor(number / 1000 * 10) / 10).toString().replace('.0', '') + 'K+';
}
return formatNumber(number);
}
function formatNumber(number) {
return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
function updateRobuxPage() {
if (!currentUser) return;
updateRobuxDisplay();
let transactionHtml = '';
let totalSpent = 0;
if (currentUser.transactions) {
for (let transaction of currentUser.transactions.slice().reverse()) {
transactionHtml += `
<div class="transaction-item">
<p>${transaction.type}: ${transaction.item} - <span class="robux-icon"></span>${formatNumber(transaction.amount)}</p>
<p>Date: ${transaction.date}</p>
</div>
`;
if (transaction.type === 'Purchase') {
totalSpent += transaction.amount;
}
}
}
$('#transaction-list').html(transactionHtml);
$('#total-spent').text(formatNumber(totalSpent));
}
function updateCatalog() {
let searchTerm = $('#catalog-search').val().toLowerCase().trim();
let sortOption = $('#catalog-sort').val();
let sortedCatalogItems = [...catalogItems].filter(item =>
// Only show hidden items when searching for them
((!item.hidden || searchTerm) &&
(!searchTerm || item.name.toLowerCase().includes(searchTerm)))
);
sortedCatalogItems.sort((a, b) => {
if (a.timerStatus === 'timer' && b.timerStatus !== 'timer') return -1;
if (a.timerStatus !== 'timer' && b.timerStatus === 'timer') return 1;
if (a.isNew && (!a.newViewedBy || !a.newViewedBy.includes(currentUser.username))) return -1;
if (b.isNew && (!b.newViewedBy || !b.newViewedBy.includes(currentUser.username))) return 1;
// Sort by the selected option
switch(sortOption) {
case 'newest':
return new Date(b.createdAt || 0) - new Date(a.createdAt || 0);
case 'oldest':
return new Date(a.createdAt || 0) - new Date(b.createdAt || 0);
case 'price-asc':
let aListingsAsc = itemListings.filter(l => l.itemName === a.name)
.sort((a, b) => a.price - b.price);
let bListingsAsc = itemListings.filter(l => l.itemName === b.name)
.sort((a, b) => a.price - b.price);
let aPriceAsc = !a.offSale && (a.limitedStatus === 'none' ? a.price : (aListingsAsc[0]?.price || null));
let bPriceAsc = !b.offSale && (b.limitedStatus === 'none' ? b.price : (bListingsAsc[0]?.price || null));
if (aPriceAsc !== null && bPriceAsc === null) return -1;
if (aPriceAsc === null && bPriceAsc !== null) return 1;
if (aPriceAsc !== null && bPriceAsc !== null) return aPriceAsc - bPriceAsc;
break;
case 'price-desc':
let aListingsDesc = itemListings.filter(l => l.itemName === a.name)
.sort((a, b) => a.price - b.price);
let bListingsDesc = itemListings.filter(l => l.itemName === b.name)
.sort((a, b) => a.price - b.price);
let aPriceDesc = !a.offSale && (a.limitedStatus === 'none' ? a.price : (aListingsDesc[0]?.price || null));
let bPriceDesc = !b.offSale && (b.limitedStatus === 'none' ? b.price : (bListingsDesc[0]?.price || null));
if (aPriceDesc !== null && bPriceDesc === null) return -1;
if (aPriceDesc === null && bPriceDesc !== null) return 1;
if (aPriceDesc !== null && bPriceDesc !== null) return bPriceDesc - aPriceDesc;
break;
}
let isOffSaleA = a.offSale;
let isOffSaleB = b.offSale;
if (isOffSaleA && !isOffSaleB) return 1;
if (!isOffSaleA && isOffSaleB) return -1;
// Default fallback sorting
if (a.rap > b.rap) return -1;
if (a.rap < b.rap) return 1;
return 0;
});
let startIndex = (currentCatalogPage - 1) * itemsPerPage;
let endIndex = startIndex + itemsPerPage;
let pageItems = sortedCatalogItems.slice(startIndex, endIndex);
let catalogHtml = '';
for (let item of pageItems) {
const listings = itemListings.filter(l => l.itemName === item.name)
.sort((a, b) => a.price - b.price);
let priceDisplay;
if (item.offSale) {
priceDisplay = '<p style="color: #ff4444;">Offsale</p>';
} else if (item.limitedStatus !== 'none') {
const lowestListing = listings[0];
priceDisplay = !lowestListing ?
'<p style="color: #B8B8B8; font-weight: bold;">No Resellers</p>' :
`<p><span class="robux-icon"></span>${formatNumber(lowestListing.price)}</p>`;
} else {
priceDisplay = item.price === 0 ?
'<p><span class="robux-icon"></span>Free</p>' :
`<p><span class="robux-icon"></span>${formatNumber(item.price)}</p>`;
}
let limitedIcon = '';
if (item.limitedStatus === 'unlimited') {
limitedIcon = '<div class="limited-icon unlimited"></div>';
} else if (item.limitedStatus === 'limited') {
limitedIcon = '<div class="limited-icon limited"></div>';
}
let isNewForCurrentUser = item.isNew && (!item.newViewedBy || !item.newViewedBy.includes(currentUser.username));
let hasTimer = item.timerStatus === 'timer';
let stockText = '';
if (item.stock !== undefined && !item.rap) {
if (item.stock > 0) {
stockText = `<p style="color: #808080;">Stock: ${formatNumber(item.stock)}</p>`;
}
}
let userQuantity = currentUser.inventory?.find(i => i.name === item.name)?.quantity || 0;
let userQuantityDisplay = userQuantity > 0 ? `<div class="item-quantity">x${userQuantity}</div>` : '';
let buyButtonDisabled = item.offSale || currentUser.banned;
if (!item.limitedStatus && !item.stock && item.timerStatus !== 'timer' && item.quantity > 0) {
buyButtonDisabled = true;
}
if (item.maxPurchase !== null && userQuantity >= item.maxPurchase) {
buyButtonDisabled = true;
}
let canSell = currentUser && !currentUser.banned &&
currentUser.inventory && currentUser.inventory.some(i => i.name === item.name) &&
(item.limitedStatus === 'limited' || item.limitedStatus === 'unlimited');
let purchasesDisplay = (item.limitedStatus === 'limited' || item.limitedStatus === 'unlimited') ?
`<p style="color: #B8B8B8;">Purchases: ${formatNumber(item.purchases || 0)}</p>` : '';
catalogHtml += `
<div class="catalog-item" onclick="showItemDetail(&quot;${escapeHtml(item.name)}&quot;);" style="cursor: pointer;">
<img src="${item.image}" alt="${item.name}">
${limitedIcon}
${userQuantityDisplay}
<div class="catalog-item-details">
<h3>${item.name}</h3>
${priceDisplay}
${(item.limitedStatus !== 'none' && !item.offSale) ? `<p>RAP: ${formatNumber(item.rap)}</p>` : ''}
${purchasesDisplay}
<p><span class="clickable" style="text-decoration: underline; color: inherit;" onclick="event.stopPropagation(); showUserProfile('Roblox');">By: Roblox</span> <img src="https://en.help.roblox.com/hc/article_attachments/7997146649876" alt="Verified" style="width: 16px; height: 16px; vertical-align: middle;"></p>
${stockText}
</div>
${isNewForCurrentUser ? `<div class="new-text" onclick="markItemAsViewed(event, &quot;${escapeHtml(item.name)}&quot;)">New</div>` : ''}
${hasTimer && (!item.stock || item.stock > 0) ? '<div class="timer-text' + (isNewForCurrentUser ? ' with-new' : '') + '"></div>' : ''}
</div>
`;
}
$('#catalog-items').html(catalogHtml);
updatePaginationWithArrows(Math.ceil(sortedCatalogItems.length / itemsPerPage), currentCatalogPage, 'catalog-pagination', changeCatalogPage);
}
function markItemAsViewed(event, itemName) {
event.stopPropagation();
let item = catalogItems.find(i => i.name === itemName);
if (item && (!item.newViewedBy || !item.newViewedBy.includes(currentUser.username))) {
if (!item.newViewedBy) item.newViewedBy = [];
item.newViewedBy.push(currentUser.username);
saveToLocalStorage();
updateCatalog();
}
}
function updatePaginationWithArrows(totalPages, currentPage, paginationId, changePageFunction) {
let paginationHtml = `
<button onclick="${changePageFunction.name}(${Math.max(1, currentPage - 1)})" ${currentPage === 1 ? 'disabled' : ''}>&larr;</button>
<span>${currentPage} / ${totalPages}</span>
<button onclick="${changePageFunction.name}(${Math.min(totalPages, currentPage + 1)})" ${currentPage === totalPages ? 'disabled' : ''}>&rarr;</button>
`;
$(`#${paginationId}`).html(paginationHtml);
}
function changeCatalogPage(page) {
currentCatalogPage = page;
updateCatalog();
}
function showUploadPopup() {
$('#upload-popup').show();
$('#overlay').show();
}
function confirmUpload() {
$('#upload-popup').hide();
$('#upload-confirmation-popup').show();
$('#overlay').show();
}
function uploadItem() {
let itemName = $('#item-name').val().trim();
if (!itemName) {
alert('Please enter an item name');
return;
}
let safeItemName = itemName.replace(/'/g, "");
let stock = parseInt($('#item-stock').val());
let maxPurchase = parseInt($('#item-max-purchase').val()) || null;
let isOffsale = $('#item-offsale').is(':checked');
let isHidden = $('#item-hidden').is(':checked');
let timerStatus = $('#item-timer-status').val();
let newItem = {
name: safeItemName,
image: $('#upload-preview-image').attr('src'), // Ensure the image URL is set correctly
price: parseInt($('#item-price').val()),
rap: parseInt($('#item-rap').val()) || 0,
offSale: isOffsale,
hidden: isHidden,
limitedStatus: $('#item-limited-status').val(),
timerStatus: timerStatus,
maxPurchase: maxPurchase,
isNew: !isOffsale,
createdAt: new Date().toISOString(),
newViewedBy: isOffsale ? users.map(u => u.username) : [],
description: $('#item-description').val().trim() || 'No description available.',
purchases: 0
};
// Add timer information if timer is enabled
if (timerStatus === 'timer') {
const hours = parseInt($('#item-timer-hours').val()) || 0;
const minutes = parseInt($('#item-timer-minutes').val()) || 0;
const seconds = parseInt($('#item-timer-seconds').val()) || 0;
// Only set timer if time is specified
if (hours > 0 || minutes > 0 || seconds > 0) {
const timerDuration = (hours * 60 * 60 + minutes * 60 + seconds) * 1000;
newItem.timerEndTime = Date.now() + timerDuration;
}
}
if (!isNaN(stock) && stock > 0) {
newItem.stock = stock;
newItem.originalStock = stock;
}
catalogItems.push(newItem);
let robloxUser = users.find(u => u.username === 'Roblox');
if (robloxUser) {
if (!robloxUser.inventory) robloxUser.inventory = [];
robloxUser.inventory.push({
...newItem,
quantity: 1
});
robloxUser.rap = robloxUser.inventory.reduce((sum, item) => {
let catalogItem = catalogItems.find(i => i.name === item.name);
let itemRap = catalogItem ? catalogItem.rap : item.rap;
return sum + itemRap * (item.quantity || 1);
}, 0);
}
$('#item-name').val('');
$('#item-image').val('');
$('#item-price').val('');
$('#item-rap').val('');
$('#item-stock').val('');
$('#item-max-purchase').val('');
$('#item-limited-status').val('none');
$('#item-timer-status').val('none');
$('#item-offsale').prop('checked', false);
$('#item-hidden').prop('checked', false);
$('#item-description').val('');
// Reset timer fields
$('#item-timer-hours').val('');
$('#item-timer-minutes').val('');
$('#item-timer-seconds').val('');
$('#timer-fields').hide();
closePopup('upload-confirmation-popup');
showCatalog();
updateProfile();
saveToLocalStorage();
}
function showBuyConfirmation(itemName) {
if (!currentUser) return;
itemToBuy = catalogItems.find(i => i.name === itemName);
if (!itemToBuy || itemToBuy.offSale || currentUser.banned) return;
if (itemToBuy.limitedStatus === 'limited' || itemToBuy.limitedStatus === 'unlimited') {
let listingsHtml = '<h4>Current Listings</h4>';
const listings = itemListings.filter(l => l.itemName === itemName)
.sort((a, b) => a.price - b.price);
if (listings.length > 0) {
listingsHtml += `
${listings.map(listing => `
<div class="listing">
<span>
<span class="clickable" onclick="showUserProfile('${listing.seller}'); closePopup('item-listings-popup')"> ${listing.seller}</span> -
<span class="robux-icon"></span>${formatNumber(listing.price)}
</span>
<div>
${listing.seller !== currentUser.username ?
`<button class="button" onclick="showListingConfirmation('${listing.id}')">Buy</button>` :
`<button class="button" onclick="deleteListing('${listing.id}')">Delete</button>`}
</div>
</div>
`).join('')}
`;
} else {
listingsHtml += '<p style="color: #B8B8B8; font-weight: bold;">No Resellers</p>';
}
listingsHtml += '<h4>Purchase History</h4><div class="purchase-history">';
const itemHistory = purchaseHistory
.filter(purchase => purchase.itemName === itemName)
.slice()
.reverse();
if (itemHistory.length > 0) {
listingsHtml += `
${itemHistory.map(purchase => `
<div class="purchase-history-item">
Sold for <span class="robux-icon"></span>${formatNumber(purchase.price)} at ${new Date(purchase.date).toLocaleDateString()}
By <span class="clickable" onclick="showUserProfile('${purchase.seller}'); closePopup('item-listings-popup')"> ${purchase.seller}</span> to
<span class="clickable" onclick="showUserProfile('${purchase.buyer}'); closePopup('item-listings-popup')"> ${purchase.buyer}</span>
</div>
`).join('')}
`;
} else {
listingsHtml += '<p>No purchase history for this item</p>';
}
listingsHtml += '</div>';
$('#item-listings').html(listingsHtml);
$('#item-listings-popup').show();
$('#overlay').show();
// Scroll to top
$('#item-listings').scrollTop(0);
} else {
// Rest of existing non-limited item purchase logic...
if (itemToBuy.stock !== undefined && itemToBuy.stock <= 0) {
alert('Item is out of stock!');
return;
}
let existingItem = currentUser.inventory?.find(i => i.name === itemToBuy.name);
if (existingItem && !itemToBuy.limitedStatus && itemToBuy.timerStatus !== 'timer') {
alert('You can only own one copy of this item.');
return;
}
if (itemToBuy.maxPurchase !== null && existingItem && existingItem.quantity >= itemToBuy.maxPurchase) {
alert(`You can only own ${itemToBuy.maxPurchase} of this item.`);
return;
}
$('#buy-confirmation-image').attr('src', itemToBuy.image);
$('#buy-item-name').text(itemToBuy.name);
$('#buy-item-price').html(`<span class="robux-icon"></span>${formatNumber(itemToBuy.price)}`);
let balanceAfterPurchase = currentUser.robux - itemToBuy.price;
$('#robux-after-purchase').text(formatNumber(balanceAfterPurchase));
const forceEnable = itemToBuy.stock === 1;
$('.confirm-button').prop('disabled', balanceAfterPurchase < 0 && !forceEnable);
if (balanceAfterPurchase < 0 && !forceEnable) {
$('.confirm-button').css('background-color', '#808080');
} else {
$('.confirm-button').css('background-color', '#ffffff');
}
$('#buy-confirmation-popup').show();
$('#overlay').show();
}
}
function cancelPurchase() {
closePopup('buy-confirmation-popup');
}
function confirmPurchase() {
if (!currentUser || !itemToBuy || currentUser.banned) return;
showLoadingScreen();
setTimeout(() => {
let catalogItem = catalogItems.find(i => i.name === itemToBuy.name);
if (catalogItem.stock !== undefined) {
if (catalogItem.stock <= 0) {
hideLoadingScreen();
showSuccessBanner('Item is out of stock!');
closePopup('buy-confirmation-popup');
return;
}
catalogItem.stock--;
// Update stock text on item detail page if it's visible
if ($('#item-detail-page').is(':visible')) {
let stockText = '';
if (catalogItem.stock !== undefined && !catalogItem.rap) {
if (catalogItem.stock > 0) {
stockText = `<p style="color: #808080; margin-top: 10px;">Stock: ${formatNumber(catalogItem.stock)}</p>`;
}
}
// Find and update the stock text in the price display
let priceHtml = $('#detail-item-price').html();
priceHtml = priceHtml.replace(/<p style="color: #808080; margin-top: 10px;">Stock: .*?<\/p>/, '');
priceHtml += stockText;
$('#detail-item-price').html(priceHtml);
}
if (catalogItem.stock === 0) {
catalogItem.limitedStatus = 'unlimited';
catalogItem.rap = 0; // Reset RAP when going back on sale
// Clear any existing listings since it's a new limited item
itemListings = itemListings.filter(listing => listing.itemName !== itemToBuy.name);
}
// Force enable confirm button if purchasing the last stock
if (catalogItem.stock === 0 || catalogItem.originalStock === 1) {
$('.confirm-button').prop('disabled', false);
$('.confirm-button').css('background-color', '#ffffff');
}
}
if (currentUser.robux < itemToBuy.price) {
hideLoadingScreen();
showSuccessBanner('Not enough Robux!');
closePopup('buy-confirmation-popup');
return;
}
let existingItem = currentUser.inventory?.find(i => i.name === itemToBuy.name);
if (existingItem && !itemToBuy.limitedStatus && itemToBuy.timerStatus !== 'timer') {
hideLoadingScreen();
showSuccessBanner('You can only own one copy of this item.');
closePopup('buy-confirmation-popup');
return;
}
if (itemToBuy.maxPurchase !== null && existingItem && existingItem.quantity >= itemToBuy.maxPurchase) {
hideLoadingScreen();
showSuccessBanner(`You can only own ${itemToBuy.maxPurchase} of this item.`);
closePopup('buy-confirmation-popup');
return;
}
// Deduct Robux from buyer
currentUser.robux -= itemToBuy.price;
// Give Roblox account the robux from the purchase
let robloxUser = users.find(u => u.username === 'Roblox');
if (robloxUser && (!itemToBuy.limitedStatus || !itemToBuy.rap)) {
if (!robloxUser.robux) robloxUser.robux = 0;
robloxUser.robux += itemToBuy.price;
// Update Roblox's transactions
if (!robloxUser.transactions) robloxUser.transactions = [];
robloxUser.transactions.push({
type: 'Sale',
item: itemToBuy.name,
amount: itemToBuy.price,
date: new Date().toLocaleDateString('en-US')
});
}
if (!currentUser.inventory) currentUser.inventory = [];
// Update inventory and quantity
if (existingItem) {
existingItem.quantity = (existingItem.quantity || 1) + 1;
} else {
currentUser.inventory.push({
...itemToBuy,
quantity: 1
});
}
// Update "You own" text on item detail page immediately
if ($('#item-detail-page').is(':visible')) {
let userQuantity = currentUser.inventory.find(i => i.name === itemToBuy.name)?.quantity || 0;
let priceHtml = $('#detail-item-price').html();
// Remove existing "You own" text if it exists
priceHtml = priceHtml.replace(/<p class="detail-rap-text" style="color: #00A2FF;">You own: <strong>\d+<\/strong><\/p>/, '');
// Add updated "You own" text
priceHtml += `<p class="detail-rap-text" style="color: #00A2FF;">You own: <strong>${userQuantity}</strong></p>`;
$('#detail-item-price').html(priceHtml);
// Update buy button state
if (itemToBuy.maxPurchase !== null && userQuantity >= itemToBuy.maxPurchase) {
$('#detail-buy-button').prop('disabled', true);
}
}
if (!currentUser.transactions) currentUser.transactions = [];
currentUser.transactions.push({
type: 'Purchase',
item: itemToBuy.name,
amount: itemToBuy.price,
date: new Date().toLocaleDateString('en-US')
});
itemToBuy.purchases = (itemToBuy.purchases || 0) + 1;
saveToLocalStorage();
hideLoadingScreen();
closePopup('buy-confirmation-popup');
showSuccessBanner('Successful purchase!');
updateRobuxDisplay();
}, 1200);
}
function showSellConfirmation(itemName) {
if (!currentUser) return;
itemToSell = currentUser.inventory.find(i => i.name === itemName);
let catalogItem = catalogItems.find(i => i.name === itemName);
if (catalogItem && catalogItem.offSale) return;
if (itemToSell.rap === 0) {
alert("This item cannot be sold as it has 0 RAP.");
return;
}
let robuxAfterSale = currentUser.robux + (catalogItem ? catalogItem.price : itemToSell.price);
$('#robux-after-sale').text(formatNumber(robuxAfterSale));
$('#sell-confirmation-image').attr('src', itemToSell.image);
$('#sell-confirmation-popup').show();
$('#overlay').show();
}
function confirmSale() {
if (!currentUser || !itemToSell || itemToSell.rap === 0) return;
showLoadingScreen();
setTimeout(() => {
let catalogItem = catalogItems.find(i => i.name === itemToSell.name);
let salePrice = catalogItem ? catalogItem.price : itemToSell.price;
currentUser.robux += salePrice;
let inventoryItem = currentUser.inventory.find(i => i.name === itemToSell.name);
if (inventoryItem.quantity && inventoryItem.quantity > 1) {
inventoryItem.quantity--;
} else {
currentUser.inventory = currentUser.inventory.filter(i => i.name !== itemToSell.name);
}
currentUser.rap = currentUser.inventory.reduce((sum, item) => {
let catalogItem = catalogItems.find(i => i.name === item.name);
let itemRap = catalogItem ? catalogItem.rap : item.rap;
return sum + itemRap * (item.quantity || 1);
}, 0);
if (!currentUser.transactions) currentUser.transactions = [];
currentUser.transactions.push({
type: 'Sale',
item: itemToSell.name,
amount: salePrice,
date: new Date().toLocaleDateString('en-US')
});
updateInventory();
updateRobuxDisplay();
saveToLocalStorage();
hideLoadingScreen();
showSuccessBanner('Successful sale!');
closePopup('sell-confirmation-popup');
showInventory();
}, 1200);
}
function cancelSale() {
closePopup('sell-confirmation-popup');
}
function showDatabase() {
$('#database-content').val(JSON.stringify({
users,
catalogItems
}, null, 2));
$('#database-popup').show();
$('#overlay').show();
}
function saveDatabase() {
try {
let data = JSON.parse($('#database-content').val());
if (!data || typeof data !== 'object') {
throw new Error('Invalid database format');
}
// Ensure required properties exist and are arrays
users = Array.isArray(data.users) ? data.users : [];
catalogItems = Array.isArray(data.catalogItems) ? data.catalogItems : [];
// Initialize missing properties
users.forEach(user => {
if (!user.inventory) user.inventory = [];
if (!user.transactions) user.transactions = [];
if (typeof user.robux !== 'number') user.robux = 0;
});
saveToLocalStorage();
closePopup('database-popup');
showSuccessBanner('Database saved successfully!');
// Only update UI if we have a current user
if (currentUser) {
// Find the current user in the new users array
currentUser = users.find(u => u.username === currentUser.username) || users[0];
updateCatalog();
updateProfile();
updateInventory();
}
} catch (error) {
console.error('Database save error:', error);
alert('Error saving database: ' + error.message);
}
}
function showUsers() {
let usersHtml = '';
for (let user of users) {
usersHtml += `<p class="${user.username === currentUser.username ? 'current-user' : ''}" onclick="switchUser('${user.username}')">${user.username}</p>`;
}
$('#users-list').html(usersHtml);
$('#users-popup').show();
$('#overlay').show();
}
function switchUser(username) {
currentUser = users.find(u => u.username === username);
updateProfile();
updateInventory();
updateRobuxDisplay();
$('#admin-link').hide();
if (username === 'Roblox' || (currentUser && currentUser.isAdmin)) {
$('#admin-link').show();
}
$('#current-user-display').text(`Current User: ${username}`);
closePopup('users-popup');
showProfile();
$('.profile-inventory').scrollTop(0);
currentCatalogPage = 1;
currentInventoryPage = 1;
updateCatalog();
$('#item-detail-page').hide();
$('#catalog').hide();
}
function showAddUserPopup() {
$('#add-user-popup').show();
$('#overlay').show();
}
function addUser() {
let newUsername = $('#new-username').val();
if (newUsername && !users.some(u => u.username === newUsername)) {
let newUser = {
username: newUsername,
joinDate: new Date().toLocaleDateString('en-US'),
placeVisits: 0,
friends: 0,
followers: 0,
following: 0,
rap: 0,
robux: 0,
inventory: [],
transactions: []
};
users.push(newUser);
catalogItems.forEach(item => {
if (!item.newViewedBy) {
item.newViewedBy = [];
}
item.newViewedBy.push(newUsername);
});
saveToLocalStorage();
closePopup('add-user-popup');
showSuccessBanner('User added successfully!');
} else {
alert('Invalid username or username already exists.');
}
}
function showEditItemPopup(itemName) {
let item = catalogItems.find(i => i.name === itemName);
if (item) {
$('#edit-item-name').val(item.name);
$('#edit-item-name-new').val(item.name);
$('#edit-preview-image').attr('src', item.image).show();
$('#edit-preview-image').data('imageUrl', item.image);
$('#edit-item-image').val(item.image);
$('#edit-item-price').val(item.price);
$('#edit-item-rap').val(item.rap);
$('#edit-item-max-purchase').val(item.maxPurchase || '');
$('#edit-item-limited-status').val(item.limitedStatus || 'none');
$('#edit-item-timer-status').val(item.timerStatus || 'none');
$('#edit-item-hidden').prop('checked', item.hidden || false);
$('#edit-item-offsale').prop('checked', item.offSale || false);
$('#edit-item-description').val(item.description || '');
// Show/hide timer fields based on timer status
if (item.timerStatus === 'timer') {
$('#edit-timer-fields').show();
// If there's a timerEndTime, calculate and display the remaining time
if (item.timerEndTime) {
const timeLeft = Math.max(0, item.timerEndTime - Date.now());
const hours = Math.floor(timeLeft / (1000 * 60 * 60));
const minutes = Math.floor((timeLeft % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((timeLeft % (1000 * 60)) / 1000);
$('#edit-item-timer-hours').val(hours);
$('#edit-item-timer-minutes').val(minutes);
$('#edit-item-timer-seconds').val(seconds);
} else {
// Default values if no timer end time is set but timer is enabled
$('#edit-item-timer-hours').val(24);
$('#edit-item-timer-minutes').val(0);
$('#edit-item-timer-seconds').val(0);
}
} else {
$('#edit-timer-fields').hide();
// Clear timer fields when not in use
$('#edit-item-timer-hours').val('');
$('#edit-item-timer-minutes').val('');
$('#edit-item-timer-seconds').val('');
}
// Force trigger the change event to ensure UI is updated
$('#edit-item-timer-status').trigger('change');
$('#edit-item-popup').show();
$('#overlay').show();
}
}
async function saveItemChanges() {
let oldItemName = $('#edit-item-name').val();
let newItemName = $('#edit-item-name-new').val().trim();
if (!newItemName) {
alert('Please enter a valid item name');
return;
}
let safeNewItemName = newItemName.replace(/'/g, "");
let item = catalogItems.find(i => i.name === oldItemName);
if (item) {
const newImageUrl = $('#edit-item-image').val();
if (newImageUrl) {
try {
const response = await fetch(newImageUrl);
const blob = await response.blob();
const permanentUrl = await window.websim.upload(blob);
$('#edit-preview-image').data('imageUrl', permanentUrl);
} catch (err) {
console.error('Failed to upload image:', err);
// If image upload fails, ensure we keep using the original image URL
if (!$('#edit-preview-image').data('imageUrl')) {
$('#edit-preview-image').data('imageUrl', item.image);
}
}
} else {
// If no new image URL provided, use the original image
$('#edit-preview-image').data('imageUrl', item.image);
}
let oldRap = item.rap;
item.name = safeNewItemName;
item.image = $('#edit-preview-image').data('imageUrl');
item.price = parseInt($('#edit-item-price').val());
item.rap = parseInt($('#edit-item-rap').val()) || 0;
item.limitedStatus = $('#edit-item-limited-status').val();
const timerStatus = $('#edit-item-timer-status').val();
item.timerStatus = timerStatus;
// Handle timer settings
if (timerStatus === 'timer') {
const hours = parseInt($('#edit-item-timer-hours').val()) || 0;
const minutes = parseInt($('#edit-item-timer-minutes').val()) || 0;
const seconds = parseInt($('#edit-item-timer-seconds').val()) || 0;
// Only show timer badge if time is specified
if (hours > 0 || minutes > 0 || seconds > 0) {
const timerDuration = (hours * 60 * 60 + minutes * 60 + seconds) * 1000;
item.timerEndTime = Date.now() + timerDuration;
} else {
delete item.timerEndTime;
}
} else {
// If timer is disabled, remove timer end time
delete item.timerEndTime;
}
item.maxPurchase = parseInt($('#edit-item-max-purchase').val()) || null;
item.description = $('#edit-item-description').val();
item.hidden = $('#edit-item-hidden').is(':checked');
item.offSale = $('#edit-item-offsale').is(':checked');
// If new badge is checked, update the item's createdAt timestamp to current time
if ($('#edit-item-new-badge').is(':checked')) {
item.isNew = true;
item.newViewedBy = [];
item.createdAt = new Date().toISOString(); // Update timestamp to current time
}
for (let user of users) {
let userItem = user.inventory.find(i => i.name === oldItemName);
if (userItem) {
userItem.name = safeNewItemName;
userItem.image = item.image;
userItem.price = item.price;
userItem.limitedStatus = item.limitedStatus;
userItem.rap = item.rap;
userItem.offSale = item.offSale;
}
}
for (let user of users) {
user.rap = user.inventory.reduce((sum, item) => {
let catalogItem = catalogItems.find(i => i.name === item.name);
let itemRap = catalogItem ? catalogItem.rap : item.rap;
return sum + itemRap * (item.quantity || 1);
}, 0);
}
let catalogIndex = catalogItems.findIndex(i => i.name === oldItemName);
if (catalogIndex !== -1) {
catalogItems[catalogIndex] = item;
}
saveToLocalStorage();
closePopup('edit-item-popup');
showSuccessBanner('Item updated successfully!');
updateCatalog();
if (currentUser.username !== 'Roblox') {
updateInventory();
updateProfile();
}
}
if ($('#edit-item-new-badge').is(':checked')) {
item.isNew = true;
item.newViewedBy = [];
}
}
function showDeleteConfirmation(itemName) {
itemToDelete = catalogItems.find(i => i.name === itemName);
$('#delete-confirmation-popup').show();
$('#overlay').show();
}
function confirmDeleteItem() {
if (!itemToDelete) return;
catalogItems = catalogItems.filter(i => i.name !== itemToDelete.name);
for (let user of users) {
let userItem = user.inventory.find(i => i.name === itemToDelete.name);
if (userItem) {
user.robux += itemToDelete.price * (userItem.quantity || 1);
if (typeof user.rap !== 'number') {
user.rap -= itemToDelete.rap * (userItem.quantity || 1);
}
user.inventory = user.inventory.filter(i => i.name !== itemToDelete.name);
if (!user.transactions) user.transactions = [];
user.transactions.push({
type: 'Refund',
item: itemToDelete.name,
amount: itemToDelete.price * (userItem.quantity || 1),
date: new Date().toLocaleDateString('en-US')
});
}
}
saveToLocalStorage();
closePopup('delete-confirmation-popup');
showSuccessBanner('Item deleted and refunds processed!');
updateCatalog();
updateInventory();
updateProfile();
updateRobuxDisplay();
}
function showDeleteUserConfirmation(username) {
$('#delete-user-confirmation-popup .username').text(username);
$('#delete-user-confirmation-popup').data('username', username);
$('#delete-user-confirmation-popup').show();
$('#overlay').show();
}
function deleteUser() {
const username = $('#delete-user-confirmation-popup').data('username');
if (!username) return;
showLoadingScreen();
setTimeout(() => {
users = users.filter(u => u.username !== username);
saveToLocalStorage();
closePopup('delete-user-confirmation-popup');
showSuccessBanner('User deleted successfully!');
showProfile();
updateCatalog();
updateInventory();
updateRobuxPage();
}, 1200);
}
function acceptTrade(tradeId) {
let trade = pendingTrades.find(t => t.id === tradeId);
if (!trade || currentUser.banned) return;
showLoadingScreen();
setTimeout(() => {
// Remove the trade from pending trades
pendingTrades = pendingTrades.filter(t => t.id !== tradeId);
// Save a copy for declinedTrades tracking
const tradeCopy = JSON.parse(JSON.stringify(trade));
// Get the sender and receiver users
let sender = users.find(u => u.username === trade.sender);
let receiver = users.find(u => u.username === trade.receiver);
if (!sender || !receiver) {
hideLoadingScreen();
return;
}
const transferItems = (fromUser, toUser, items) => {
for (let item of items) {
let fromItem = fromUser.inventory.find(i => i.name === item.name);
if (fromItem?.quantity > 1) {
fromItem.quantity--;
} else {
fromUser.inventory = fromUser.inventory.filter(i => i.name !== item.name);
}
// Add to recipient
let toItem = toUser.inventory.find(i => i.name === item.name);
if (toItem) {
toItem.quantity = (toItem.quantity || 1) + 1;
} else {
toUser.inventory.push({
name: item.name,
image: catalogItems.find(ci => ci.name === item.name)?.image || '',
quantity: 1,
rap: item.rap,
limitedStatus: 'limited'
});
}
// Remove from equipped items if needed
if (fromUser.equippedItems && fromUser.equippedItems.includes(item.name)) {
fromUser.equippedItems = fromUser.equippedItems.filter(i => i !== item.name);
}
}
};
transferItems(sender, receiver, trade.senderItems);
transferItems(receiver, sender, trade.receiverItems);
// Set completedAt timestamp
trade.completedAt = new Date().toISOString();
// Add to completed trades
completedTrades.push(trade);
saveToLocalStorage();
hideLoadingScreen();
showSuccessBanner('Trade completed!');
updateTradeItems();
updatePendingTrades();
updateCompletedTrades();
updateProfile();
updateInventory();
}, 1200);
}
function declineTrade(tradeId) {
let trade = pendingTrades.find(t => t.id === tradeId);
if (!trade) return;
pendingTrades = pendingTrades.filter(t => t.id !== tradeId);
trade.status = 'declined';
trade.declinedAt = new Date().toISOString();
trade.declinedBy = currentUser.username;
declinedTrades.unshift(trade);
saveToLocalStorage();
showSuccessBanner('Trade declined!');
updatePendingTrades();
updateDeclinedTrades();
}
function toggleOffSale(itemName) {
let item = catalogItems.find(i => i.name === itemName);
if (item) {
item.offSale = !item.offSale;
if (item.offSale) {
item.timerStatus = 'none';
} else {
item.isNew = true;
item.newViewedBy = [];
if (item.limitedStatus === 'limited' || item.limitedStatus === 'unlimited') {
item.rap = 0; // Reset RAP when going back on sale
// Clear any existing listings since it's a new limited item
itemListings = itemListings.filter(listing => listing.itemName !== item.name);
}
}
saveToLocalStorage();
showItemDetail(itemName); // Update the detail page instead of going back to catalog
}
}
function showLoadingScreen() {
$('#loading-screen').css('display', 'flex');
}
function hideLoadingScreen() {
$('#loading-screen').css('display', 'none');
}
let bannerTimeout;
function showSuccessBanner(message) {
clearTimeout(bannerTimeout);
$('#success-banner').stop(true, true).hide();
$('#success-banner').text(message);
$('#success-banner').fadeIn();
bannerTimeout = setTimeout(() => {
$('#success-banner').fadeOut();
}, 2000);
}
function closePopup(popupId) {
$(`#${popupId}`).hide();
$('#overlay').hide();
}
function addRobux(amount) {
if (currentUser && !currentUser.banned) {
currentUser.robux += amount;
updateRobuxDisplay();
saveToLocalStorage();
}
}
function saveToLocalStorage() {
localStorage.setItem('robloxSimData', JSON.stringify({
users,
catalogItems,
completedTrades,
pendingTrades,
declinedTrades,
itemListings,
purchaseHistory
}));
}
function recalculateRAP(item, salePrice) {
const difference = salePrice - item.rap;
const adjustment = Math.floor(difference / 10);
return item.rap + adjustment;
}
function createListing(itemName) {
const userItem = currentUser.inventory.find(i => i.name === itemName);
if (!userItem) {
alert('You do not own this item');
return;
}
const catalogItem = catalogItems.find(i => i.name === itemName);
const currentRap = catalogItem ? catalogItem.rap : userItem.rap;
// Get current listings for this item by this user
const existingListings = itemListings.filter(l =>
l.itemName === itemName && l.seller === currentUser.username
);
const listedQuantities = existingListings.map(l => l.quantity);
const quantityHtml = `
<div>
<h4>Select Item to Sell:</h4>
<h5>Current RAP: ${formatNumber(currentRap)}</h5>
<select id="quantity-select" class="input-field">
${Array.from({length: userItem.quantity || 1}, (_, i) => i + 1)
.map(num => `
<option value="${num}"
${listedQuantities.includes(num) ? 'style="color: #808080;" disabled' : ''}>
Item #${num}${listedQuantities.includes(num) ? ' (Listed)' : ''}
</option>
`).join('')}
</select>
</div>
`;
$('#sell-item-popup').html(`
${quantityHtml}
<input type="number" id="listing-price" class="input-field" placeholder="Price" max="999999999"
oninput="updateSellPrice(this.value)">
<p id="seller-earnings" style="color: #B8B8B8; margin: 5px 0;"></p>
<button class="button" onclick="confirmCreateListing()">Create Listing</button>
<button class="button" onclick="closePopup('sell-item-popup')">Cancel</button>
`);
$('#sell-item-popup').show();
$('#overlay').show();
$('#sell-item-popup').data('itemName', itemName);
}
function updateSellPrice(price) {
const earnings = Math.floor(price * 0.9); // 10% fee
$('#seller-earnings').text(`You will receive: ${formatNumber(earnings)} Robux after 10% marketplace fee`);
}
function confirmCreateListing() {
const itemName = $('#sell-item-popup').data('itemName');
const quantity = parseInt($('#quantity-select').val());
const price = parseInt($('#listing-price').val());
if (!quantity || quantity < 1) {
alert('Please select a quantity');
return;
}
if (!price || price < 1) {
alert('Please enter a valid price');
return;
}
if (price > 999999999) {
alert('Maximum price is 999,999,999 Robux');
return;
}
showLoadingScreen();
setTimeout(() => {
const listing = {
id: Date.now().toString(),
itemName,
quantity,
price,
seller: currentUser.username,
createdAt: new Date().toISOString()
};
itemListings.push(listing);
saveToLocalStorage();
hideLoadingScreen();
closePopup('sell-item-popup');
showSuccessBanner('Item listed successfully!');
updateCatalog();
updateInventory();
}, 1200);
}
function showPurchaseHistory() {
const itemName = $('#sell-item-popup').data('itemName');
let historyHtml = '';
// Filter purchase history for just this item
const itemHistory = purchaseHistory
.filter(purchase => purchase.itemName === itemName)
.slice()
.reverse();
if (itemHistory.length > 0) {
historyHtml += `
${itemHistory.map(purchase => `
<div class="purchase-history-item">
Sold for <span class="robux-icon"></span>${formatNumber(purchase.price)} at ${new Date(purchase.date).toLocaleDateString()}
By <span class="clickable" onclick="showUserProfile('${purchase.seller}'); closePopup('item-listings-popup')"> ${purchase.seller}</span> to
<span class="clickable" onclick="showUserProfile('${purchase.buyer}'); closePopup('item-listings-popup')"> ${purchase.buyer}</span>
</div>
`).join('')}
`;
} else {
historyHtml += '<p>No purchase history for this item</p>';
}
$('#purchase-history').html(historyHtml);
$('#purchase-history-popup').show();
$('#overlay').show();
// Scroll to top
$('#purchase-history').scrollTop(0);
}
function showOwners(itemName) {
let ownersList = '';
users.forEach(user => {
// Skip users with private inventory unless currentUser is Roblox
if (user.privateInventory && currentUser.username !== 'Roblox') return;
const userItem = user.inventory?.find(i => i.name === itemName);
if (userItem && userItem.quantity > 0) {
const verified = user.verified ? ' <img src="https://en.help.roblox.com/hc/article_attachments/7997146649876" alt="Verified" class="verified-badge">' : '';
ownersList += `<p style="padding: 8px; margin: 5px 0; background-color: #232527; border-radius: 5px;">
<span class="clickable" onclick="showUserProfile('${user.username}'); closePopup('owners-popup')"> ${user.username}${verified}</span>: x${userItem.quantity}
</p>`;
}
});
if (!ownersList) {
ownersList = '<p>No users currently own this item</p>';
}
$('#owners-list').html(ownersList);
$('#owners-popup').show();
$('#overlay').show();
}
function updateInventory() {
if (!currentUser || !currentUser.inventory) return;
// Update the toggle state
$('#private-inventory-toggle').prop('checked', currentUser.privateInventory || false);
if (!currentUser.equippedItems) {
currentUser.equippedItems = [];
}
let sortedInventory = [...currentUser.inventory].sort((a, b) => {
// If item is equipped, put it first
const aEquipped = currentUser.equippedItems.includes(a.name);
const bEquipped = currentUser.equippedItems.includes(b.name);
if (aEquipped && !bEquipped) return -1;
if (!aEquipped && bEquipped) return 1;
let catalogItemA = catalogItems.find(i => i.name === a.name);
let catalogItemB = catalogItems.find(i => i.name === b.name);
let isLimitedA = catalogItemA && (catalogItemA.limitedStatus === 'limited' || catalogItemA.limitedStatus === 'unlimited');
let isLimitedB = catalogItemB && (catalogItemB.limitedStatus === 'limited' || catalogItemB.limitedStatus === 'unlimited');
let isOffSaleA = catalogItemA && catalogItemA.offSale;
let isOffSaleB = catalogItemB && catalogItemB.offSale;
if (isLimitedA && !isLimitedB) return -1;
if (!isLimitedA && isLimitedB) return 1;
if (isLimitedA && isLimitedB) {
return (catalogItemB.rap || 0) - (catalogItemA.rap || 0);
}
if (isOffSaleA && !isOffSaleB) return 1;
if (!isOffSaleA && isOffSaleB) return -1;
return (b.price || 0) - (a.price || 0);
});
let startIndex = (currentInventoryPage - 1) * itemsPerPage;
let endIndex = startIndex + itemsPerPage;
let pageItems = sortedInventory.slice(startIndex, endIndex);
let inventoryHtml = '';
for (let item of pageItems) {
if (!item) continue;
let limitedIcon = '';
if (item.limitedStatus === 'unlimited') {
limitedIcon = '<div class="limited-icon unlimited"></div>';
} else if (item.limitedStatus === 'limited') {
limitedIcon = '<div class="limited-icon limited"></div>';
}
let catalogItem = catalogItems.find(i => i.name === item.name) || {};
let isOffSale = catalogItem && catalogItem.offSale;
const listings = itemListings.filter(l => l.itemName === item.name)
.sort((a, b) => a.price - b.price);
const lowestListing = listings[0];
const priceDisplay = (catalogItem.limitedStatus !== 'none' && !lowestListing) ?
'<p style="color: #B8B8B8; font-weight: bold;">No Resellers</p>' :
(lowestListing ?
`<p><span class="robux-icon"></span>${formatNumber(lowestListing.price)}</p>` :
(catalogItem.price === 0 ?
'<p><span class="robux-icon"></span>Free</p>' :
`<p><span class="robux-icon"></span>${formatNumber(catalogItem.price)}</p>`));
const isEquipped = currentUser.equippedItems.includes(item.name);
const equipButton = isEquipped ?
`<button class="take-off-button" onclick="event.stopPropagation(); toggleEquipItem('${escapeHtml(item.name)}')">Take Off</button>` :
`<button class="put-on-button" onclick="event.stopPropagation(); toggleEquipItem('${escapeHtml(item.name)}')">Put On</button>`;
inventoryHtml += `
<div class="catalog-item">
<img src="${item.image || ''}" alt="${item.name || 'Item'}">
${limitedIcon}
<div class="catalog-item-details">
<h3>${item.name || 'Unnamed Item'}</h3>
${isOffSale ? '<p style="color: #ff4444;">Offsale</p>' : ''}
${(item.limitedStatus !== 'none' && !item.offSale) ? `<p>RAP: ${formatNumber(catalogItem.rap || 0)}</p>` : ''}
${priceDisplay}
<p><span class="clickable" style="text-decoration: underline;" onclick="event.stopPropagation(); showUserProfile('Roblox');">By: Roblox</span> <img src="https://en.help.roblox.com/hc/article_attachments/7997146649876" alt="Verified" style="width: 16px; height: 16px; vertical-align: middle;"></p>
${equipButton}
</div>
</div>
`;
}
$('#inventory-items').html(inventoryHtml || '<p>No items in inventory</p>');
updatePaginationWithArrows(Math.ceil(sortedInventory.length / itemsPerPage), currentInventoryPage, 'inventory-pagination', changeInventoryPage);
}
function updatePaginationWithArrows(totalPages, currentPage, paginationId, changePageFunction) {
let paginationHtml = `
<button onclick="${changePageFunction.name}(${Math.max(1, currentPage - 1)})" ${currentPage === 1 ? 'disabled' : ''}>&larr;</button>
<span>${currentPage} / ${totalPages}</span>
<button onclick="${changePageFunction.name}(${Math.min(totalPages, currentPage + 1)})" ${currentPage === totalPages ? 'disabled' : ''}>&rarr;</button>
`;
$(`#${paginationId}`).html(paginationHtml);
}
function changeInventoryPage(page) {
currentInventoryPage = page;
updateInventory();
}
function togglePrivateInventory() {
if (!currentUser) return;
currentUser.privateInventory = $('#private-inventory-toggle').is(':checked');
saveToLocalStorage();
updateInventory();
// If currently viewing the user's profile, refresh it
if ($('#profile').is(':visible') && $('#profile-username').text().trim() === currentUser.username) {
showUserProfile(currentUser.username);
}
}
function showRemoveItemConfirmation(username, itemName) {
$('#remove-item-confirmation-popup .remove-item-username').text(username);
$('#remove-item-confirmation-popup .remove-item-name').text(itemName);
$('#remove-item-confirmation-popup').data('username', username);
$('#remove-item-confirmation-popup').data('itemName', itemName);
$('#remove-item-confirmation-popup').show();
$('#overlay').show();
}
function confirmRemoveItem() {
const username = $('#remove-item-confirmation-popup').data('username');
const itemName = $('#remove-item-confirmation-popup').data('itemName');
const user = users.find(u => u.username === username);
if (!user) return;
showLoadingScreen();
setTimeout(() => {
// Find the item in the user's inventory
const inventoryItem = user.inventory.find(i => i.name === itemName);
if (inventoryItem) {
if (inventoryItem.quantity > 1) {
inventoryItem.quantity--;
} else {
user.inventory = user.inventory.filter(i => i.name !== itemName);
}
// Remove from equipped items if it was equipped
if (user.equippedItems && user.equippedItems.includes(itemName)) {
user.equippedItems = user.equippedItems.filter(name => name !== itemName);
}
// Update RAP
user.rap = user.inventory.reduce((sum, item) => {
const catalogItem = catalogItems.find(i => i.name === item.name);
const itemRap = catalogItem ? catalogItem.rap : item.rap;
return sum + itemRap * (item.quantity || 1);
}, 0);
saveToLocalStorage();
hideLoadingScreen();
closePopup('remove-item-confirmation-popup');
showSuccessBanner(`Item removed from ${username}'s inventory!`);
// Refresh the profile view
showUserProfile(username);
}
}, 800);
}
function escapeHtml(str) {
if (!str) return '';
const div = document.createElement('div');
div.textContent = str;
return div.innerHTML;
}
function showGiveItemPopup(itemName) {
if (currentUser.username !== 'Roblox') return;
itemToGive = catalogItems.find(i => i.name === itemName);
if (!itemToGive) return;
let usersHtml = '<option value="">Select User</option>';
users.forEach(user => {
if (user.username !== 'Roblox') {
usersHtml += `<option value="${user.username}">${user.username}</option>`;
}
});
$('#recipient-select').html(usersHtml);
$('#give-quantity').val(1);
$('#give-item-popup').show();
$('#overlay').show();
}
function confirmGiveItem() {
recipientUsername = $('#recipient-select').val();
if (!recipientUsername) {
alert('Please select a user');
return;
}
$('#recipient-name').text(recipientUsername);
$('#give-item-popup').hide();
$('#confirm-give-popup').show();
}
function giveItem() {
if (!itemToGive || !recipientUsername) return;
const quantity = parseInt($('#give-quantity').val()) || 1;
showLoadingScreen();
setTimeout(() => {
const recipient = users.find(u => u.username === recipientUsername);
if (!recipient) return;
if (!recipient.inventory) recipient.inventory = [];
let existingItem = recipient.inventory.find(i => i.name === itemToGive.name);
if (existingItem) {
existingItem.quantity = (existingItem.quantity || 1) + quantity;
} else {
recipient.inventory.push({
...itemToGive,
quantity: quantity
});
}
saveToLocalStorage();
hideLoadingScreen();
closePopup('confirm-give-popup');
showSuccessBanner(`${quantity} item(s) given to ${recipientUsername}!`);
}, 1200);
}
function saveBio() {
if (!currentUser) return;
let bioText = $('#bio-text').val().trim();
let username = $('#edit-bio-popup').data('username');
let user = users.find(u => u.username === username);
if (user) {
user.bio = bioText;
saveToLocalStorage();
closePopup('edit-bio-popup');
showUserProfile(username);
showSuccessBanner('Bio updated successfully!');
}
}
function showEditBioPopup(username) {
let user = users.find(u => u.username === username);
if (user) {
$('#bio-text').val(user.bio || '');
$('#edit-bio-popup').data('username', username);
$('#edit-bio-popup').show();
$('#overlay').show();
}
}
function updateLeaderboard() {
let sortedUsers = [...users];
// First, calculate RAP for each user
sortedUsers.forEach(user => {
if (!user.privateInventory || currentUser.username === 'Roblox') {
user.calculatedRap = 0;
if (user.inventory) {
user.calculatedRap = user.inventory.reduce((sum, item) => {
let catalogItem = catalogItems.find(i => i.name === item.name);
let itemRap = catalogItem ? catalogItem.rap : item.rap;
return sum + itemRap * (item.quantity || 1);
}, 0);
}
}
});
// Sort users: non-private by RAP (descending), private users at the bottom
sortedUsers.sort((a, b) => {
if (a.privateInventory && !b.privateInventory && currentUser.username !== 'Roblox') return 1;
if (!a.privateInventory && b.privateInventory && currentUser.username !== 'Roblox') return -1;
if (a.privateInventory && b.privateInventory) return 0;
return b.calculatedRap - a.calculatedRap;
});
let leaderboardHtml = '<div style="padding: 20px; background-color: #393B3D; border-radius: 10px;">';
leaderboardHtml += `
<div style="display: grid; grid-template-columns: 1fr 2fr 1fr; gap: 10px; padding: 10px; margin-bottom: 10px; border-bottom: 1px solid #5F6569; font-weight: bold; font-size: 20px;">
<div>Rank</div>
<div>Username</div>
<div>RAP</div>
</div>
`;
sortedUsers.forEach((user, index) => {
const verifiedBadge = user.verified ? ' <img src="https://en.help.roblox.com/hc/article_attachments/7997146649876" alt="Verified" class="verified-badge">' : '';
const adminBadge = user.isAdmin ? ' <img src="https://i.imgur.com/WRPVUAh.png" alt="Admin" class="admin-badge">' : '';
const robuxIcon = '<span class="robux-icon" style="width:18px;height:18px;display:inline-block;background-image:url(https://devforum-uploads.s3.dualstack.us-east-2.amazonaws.com/uploads/original/4X/e/d/f/edfae9388da4cd8496b885a8a2df613372500d9c.png);background-size:contain;background-repeat:no-repeat;background-position:center;"></span>';
const rapDisplay = (user.privateInventory && currentUser.username !== 'Roblox') ? 'Private' : robuxIcon + formatNumber(user.calculatedRap);
const rowClass = (user.privateInventory && currentUser.username !== 'Roblox') ? 'leaderboard-row-private' : 'leaderboard-row-public';
leaderboardHtml += `
<div class="${rowClass} leaderboard-row" style="display: grid; grid-template-columns: 1fr 2fr 1fr; gap: 10px; padding: 10px; border-radius: 5px; margin-bottom: 5px;">
<div>${index + 1}</div>
<div class="clickable" onclick="showUserProfile('${user.username}')">
${adminBadge}${user.username}${verifiedBadge}
</div>
<div>${rapDisplay}</div>
</div>
`;
});
leaderboardHtml += '</div>';
$('#leaderboard-list').html(leaderboardHtml);
}
function setActiveNavItem(page) {
$('.nav a').removeClass('active');
if (page === 'trade') {
$('.nav a[onclick*="showTrade"]').addClass('active');
} else if (page === 'leaderboard') {
$('.nav a[onclick*="showLeaderboard"]').addClass('active');
} else {
$(`.nav a[onclick*="show${page.charAt(0).toUpperCase() + page.slice(1)}"]`).addClass('active');
}
}
function switchUser(username) {
currentUser = users.find(u => u.username === username);
updateProfile();
updateInventory();
updateRobuxDisplay();
$('#admin-link').hide();
if (username === 'Roblox' || (currentUser && currentUser.isAdmin)) {
$('#admin-link').show();
}
$('#current-user-display').text(`Current User: ${username}`);
closePopup('users-popup');
showProfile();
$('.profile-inventory').scrollTop(0);
currentCatalogPage = 1;
currentInventoryPage = 1;
updateCatalog();
$('#item-detail-page').hide();
$('#catalog').hide();
}
function showAnnouncementPopup() {
$('#announcement-popup').show();
$('#overlay').show();
}
function showHiddenItems() {
// Get all hidden items
const hiddenItems = catalogItems.filter(item => item.hidden);
if (hiddenItems.length === 0) {
$('#hidden-items-list').html('<p style="text-align: center; padding: 20px;">No hidden items found</p>');
} else {
let hiddenItemsHtml = '';
for (let item of hiddenItems) {
let limitedIcon = '';
if (item.limitedStatus === 'unlimited') {
limitedIcon = '<div class="limited-icon unlimited"></div>';
} else if (item.limitedStatus === 'limited') {
limitedIcon = '<div class="limited-icon limited"></div>';
}
const priceDisplay = item.offSale ?
'<p style="color: #ff4444;">Offsale</p>' :
`<p><span class="robux-icon"></span>${formatNumber(item.price)}</p>`;
hiddenItemsHtml += `
<div class="catalog-item" onclick="showItemDetail(&quot;${escapeHtml(item.name)}&quot;); closePopup('hidden-items-popup');" style="cursor: pointer;">
<img src="${item.image}" alt="${item.name}">
${limitedIcon}
<div class="catalog-item-details">
<h3>${item.name}</h3>
${priceDisplay}
${(item.limitedStatus !== 'none' && !item.offSale) ? `<p>RAP: ${formatNumber(item.rap)}</p>` : ''}
<p>By: Roblox <img src="https://en.help.roblox.com/hc/article_attachments/7997146649876" alt="Verified" style="width: 16px; height: 16px; vertical-align: middle;"></p>
</div>
</div>
`;
}
$('#hidden-items-list').html(hiddenItemsHtml);
}
$('#hidden-items-popup').show();
$('#overlay').show();
}
function addAnnouncement() {
const text = $('#announcement-text').val().trim();
const color = $('#announcement-color').val();
if (!text) return;
const announcement = {
id: Date.now(),
text,
color
};
let saved = JSON.parse(localStorage.getItem("announcementData") || "[]");
saved.push(announcement);
localStorage.setItem("announcementData", JSON.stringify(saved));
renderAnnouncements();
$('#announcement-popup').hide();
$('#overlay').hide();
}
function deleteAnnouncement(id) {
let saved = JSON.parse(localStorage.getItem("announcementData") || "[]");
saved = saved.filter(a => a.id !== id);
localStorage.setItem("announcementData", JSON.stringify(saved));
renderAnnouncements();
}
function renderAnnouncements() {
let saved = JSON.parse(localStorage.getItem("announcementData") || "[]");
const current = saved.map(a => `
<div draggable="true" ondragstart="handleDragStart(event, ${a.id})" ondragover="handleDragOver(event)" ondrop="handleDrop(event, ${a.id})" style="background:${a.color};color:white;padding:10px;margin-bottom:5px;border-radius:5px;cursor: move;" data-id="${a.id}">
<span>${a.text}</span>
<span style="float:right;cursor:pointer;" class="edit-announcement-icon" data-id="${a.id}">✎</span>
<span style="float:right;cursor:pointer;margin-right:10px;" onclick="deleteAnnouncement(${a.id})">✖</span>
<span style="float:right;cursor:pointer;margin-right:10px;" onclick="toggleAnnouncementVisibility(${a.id})">👁️</span>
</div>
`).join('');
document.getElementById('current-announcements').innerHTML = current;
if (saved.length) {
// Show announcement banner below header
const topBanner = saved.filter(a => !a.hidden).map(a => `<div style="background:${a.color};color:white;text-align:center;padding:6px;font-weight:bold;font-size:14px;">${a.text}</div>`).join('');
document.getElementById('announcement-banner').innerHTML = topBanner;
document.getElementById('announcement-banner').style.display = saved.filter(a => !a.hidden).length > 0 ? "block" : "none";
} else {
document.getElementById('announcement-banner').style.display = "none";
}
}
$(document).ready(() => {
renderAnnouncements();
$('#current-announcements').on('click', '.edit-announcement-icon', function(e) {
e.stopPropagation();
const id = parseInt($(this).data('id'));
showEditAnnouncementPopup(id);
});
});
function renderAnnouncements() {
let saved = JSON.parse(localStorage.getItem("announcementData") || "[]");
const current = saved.map(a => `
<div draggable="true" ondragstart="handleDragStart(event, ${a.id})" ondragover="handleDragOver(event)" ondrop="handleDrop(event, ${a.id})" style="background:${a.color};color:white;padding:10px;margin-bottom:5px;border-radius:5px;cursor: move;" data-id="${a.id}">
<span>${a.text}</span>
<span style="float:right;cursor:pointer;" class="edit-announcement-icon" data-id="${a.id}">✎</span>
<span style="float:right;cursor:pointer;margin-right:10px;" onclick="deleteAnnouncement(${a.id})">✖</span>
<span style="float:right;cursor:pointer;margin-right:10px;" onclick="toggleAnnouncementVisibility(${a.id})">👁️</span>
</div>
`).join('');
document.getElementById('current-announcements').innerHTML = current;
if (saved.length) {
// Show announcement banner below header
const topBanner = saved.filter(a => !a.hidden).map(a => `<div style="background:${a.color};color:white;text-align:center;padding:6px;font-weight:bold;font-size:14px;">${a.text}</div>`).join('');
document.getElementById('announcement-banner').innerHTML = topBanner;
document.getElementById('announcement-banner').style.display = saved.filter(a => !a.hidden).length > 0 ? "block" : "none";
} else {
document.getElementById('announcement-banner').style.display = "none";
}
}
function toggleAnnouncementVisibility(id) {
let saved = JSON.parse(localStorage.getItem("announcementData") || "[]");
const announcementIndex = saved.findIndex(a => a.id === id);
if (announcementIndex !== -1) {
saved[announcementIndex].hidden = !saved[announcementIndex].hidden;
localStorage.setItem("announcementData", JSON.stringify(saved));
renderAnnouncements();
}
}
$(document).ready(function () {
loadFromLocalStorage();
currentUser = users.find(u => u.username === 'Roblox') || users[0];
// Make Roblox admin by default
let robloxUser = users.find(u => u.username === 'Roblox');
if (robloxUser) {
robloxUser.isAdmin = true;
saveToLocalStorage();
}
updateProfile();
showProfile();
$('#current-user-display').text(`Current User: ${currentUser.username}`);
$('#admin-link').hide();
if (currentUser.username === 'Roblox') {
$('#admin-link').show();
}
// ... existing code ...
// ... existing ready code ...
// Add this section for search functionality
$('#catalog-search').on('keypress', function(e) {
if (e.which === 13) {
performSearch();
}
});
// Add listener for the sort dropdown
$('#catalog-sort').change(function() {
updateCatalog();
});
// Add listener for the trade partner dropdown
$('#trade-partner').change(function() {
selectedItems = {
yours: [],
theirs: []
};
updateTradeItems();
});
// ... rest of existing ready code ...
});
function previewUploadImage() {
const imageUrl = $('#item-image').val() || "https://i.imgur.com/uXWWQIs.png";
// If using the specific image URL, make sure it's set properly
if (imageUrl === "https://i.imgur.com/uXWWQIs.png") {
$('#upload-preview-image').attr('src', "https://i.imgur.com/uXWWQIs.png").show();
$('#upload-preview-image').data('imageUrl', "https://i.imgur.com/uXWWQIs.png");
} else {
$('#upload-preview-image').attr('src', imageUrl).show();
$('#upload-preview-image').data('imageUrl', imageUrl);
}
}
function performSearch() {
currentCatalogPage = 1;
updateCatalog();
}
function showItemDetail(itemName) {
const item = catalogItems.find(i => i.name === itemName);
if (!item) return;
// Check if profile is visible, if so, set the source to profile
if ($('#profile').is(':visible')) {
itemDetailSource = 'profile';
// Hide profile when showing item detail from profile page
$('#profile').hide();
// Show catalog when coming from profile
$('#catalog').show();
} else {
itemDetailSource = 'catalog';
}
// Update detail page elements
$('#detail-item-image').attr('src', item.image);
$('#detail-item-name').html(item.name + (item.timerStatus === 'timer' ?
' <img src="https://i.imgur.com/m4Caqes.png" style="width: 24px; height: 24px; vertical-align: middle; margin-left: 5px;">' : ''));
$('#detail-item-description').text(item.description || 'No description available.');
// Create a unique timer ID for this item
const safeItemName = itemName.replace(/[^a-zA-Z0-9]/g, "_");
const timerElementId = `detail-timer-countdown-${safeItemName}`;
// Remove any existing timer display to prevent duplicates
$('.detail-timer-countdown').remove();
// Add timer countdown display under the item name if timer is active
if (item.timerStatus === 'timer' && item.timerEndTime) {
// Create timer display
$(`<div id="${timerElementId}" class="detail-timer-countdown" style="color: #ff4444; font-weight: bold; margin-top: 5px;"></div>`).insertAfter('#detail-item-name');
// Start the timer update with the unique element ID
updateItemTimer(itemName, timerElementId);
}
// Set limited icon if needed
if (item.limitedStatus === 'unlimited') {
$('#detail-limited-icon').show().removeClass('limited').addClass('unlimited');
} else if (item.limitedStatus === 'limited') {
$('#detail-limited-icon').show().removeClass('unlimited').addClass('limited');
} else {
$('#detail-limited-icon').hide();
}
// Update price display
let priceHtml = '';
if (item.offSale) {
priceHtml = '<p style="color: #ff4444; font-weight: bold; font-size: 24px;">Offsale</p>';
if (item.rap > 0) {
priceHtml += `<p class="detail-rap-text">RAP: <strong>${formatNumber(item.rap)}</strong></p>`;
}
// Add original price if it exists for offsale items
if (item.price) {
priceHtml += `<p class="detail-rap-text">Original Price: <strong><span class="robux-icon"></span>${formatNumber(item.price)}</strong></p>`;
}
// Add purchases count for offsale items
priceHtml += `<p class="detail-rap-text" style="cursor: pointer;" onclick="showOwners('${escapeHtml(item.name)}')">Sales: <strong>${formatNumber(item.purchases || 0)}</strong></p>`;
} else if (item.limitedStatus !== 'none') {
const listings = itemListings.filter(l => l.itemName === item.name)
.sort((a, b) => a.price - b.price);
priceHtml = !listings.length ?
'<p style="color: #B8B8B8; font-weight: bold; font-size: 24px;">No Resellers</p>' :
`<p style="font-size: 24px;"><span class="robux-icon"></span>${formatNumber(listings[0].price)}</p>`;
if (item.rap > 0) {
priceHtml += `<p class="detail-rap-text">RAP: <strong>${formatNumber(item.rap)}</strong></p>`;
}
// Add original price if it exists and item is limited
if (item.price) {
priceHtml += `<p class="detail-rap-text">Original Price: <strong><span class="robux-icon"></span>${formatNumber(item.price)}</strong></p>`;
}
// Add purchases count
priceHtml += `<p class="detail-rap-text" style="cursor: pointer;" onclick="showOwners('${escapeHtml(item.name)}')">Sales: <strong>${formatNumber(item.purchases || 0)}</strong></p>`;
} else {
priceHtml = item.price === 0 ?
'<p style="font-size: 24px;"><span class="robux-icon"></span>Free</p>' :
`<p style="font-size: 24px;"><span class="robux-icon"></span>${formatNumber(item.price || 0)}</p>`;
// Only show sales count for Roblox account
if (currentUser.username === 'Roblox') {
priceHtml += `<p class="detail-rap-text" style="cursor: pointer;" onclick="showOwners('${escapeHtml(item.name)}')">Sales: <strong>${formatNumber(item.purchases || 0)}</strong></p>`;
}
}
// Add ownership count
let userQuantity = currentUser.inventory?.find(i => i.name === item.name)?.quantity || 0;
if (userQuantity > 0) {
priceHtml += `<p class="detail-rap-text" style="color: #00A2FF;">You own: <strong>${userQuantity}</strong></p>`;
}
// Add stock text if not a limited item and has no RAP
if (item.stock !== undefined && !item.rap) {
if (item.stock > 0) {
priceHtml += `<p style="color: #808080; margin-top: 10px;">Stock: ${formatNumber(item.stock)}</p>`;
}
}
$('#detail-item-price').html(priceHtml);
// Update buttons
const buttonContainer = $('#detail-item-buttons');
let buttonHtml = '';
// Buy Button
let buyButtonDisabled = item.offSale || currentUser.banned;
if (!item.limitedStatus && !item.stock && item.timerStatus !== 'timer' && item.quantity > 0) {
buyButtonDisabled = true;
}
if (item.maxPurchase !== null && userQuantity >= item.maxPurchase) {
buyButtonDisabled = true;
}
buttonHtml += `<button id="detail-buy-button" class="button" onclick="showBuyConfirmation(&quot;${escapeHtml(item.name)}&quot;)"
${buyButtonDisabled ? 'disabled' : ''} style="font-size: 18px; padding: 15px 30px;">Buy</button>`;
// Sell Button (for current user's owned items)
let canSell = currentUser && !currentUser.banned &&
currentUser.inventory && currentUser.inventory.some(i => i.name === item.name) &&
(item.limitedStatus === 'limited' || item.limitedStatus === 'unlimited');
if (canSell) {
buttonHtml += `<button id="detail-sell-button" class="button" onclick="createListing(&quot;${escapeHtml(item.name)}&quot;)"
style="font-size: 18px; padding: 15px 30px; margin-left: 10px;">Sell</button>`;
}
// Admin Buttons (for Roblox account)
if (currentUser && currentUser.username === 'Roblox') {
buttonHtml += `
<button class="button" onclick="showEditItemPopup(&quot;${escapeHtml(item.name)}&quot;)" style="margin-left: 10px;">Edit</button>
<button class="button" onclick="showDeleteConfirmation(&quot;${escapeHtml(item.name)}&quot;)" style="margin-left: 10px;">Delete</button>
<button class="button" onclick="toggleOffSale(&quot;${escapeHtml(item.name)}&quot;); backToCatalog();" style="margin-left: 10px;">
${item.offSale ? 'On Sale' : 'Off Sale'}
</button>
<button class="button" onclick="showGiveItemPopup(&quot;${escapeHtml(item.name)}&quot;)" style="margin-left: 10px;">Give</button>
`;
}
// Update button container
$('#detail-item-buttons').html(buttonHtml);
// Show detail page, hide catalog
$('#catalog').hide();
$('#item-detail-page').show();
}
function backToCatalog() {
$('#item-detail-page').hide();
// If the user came from profile, return to profile
if (itemDetailSource === 'profile') {
$('#profile').show();
} else {
// Otherwise return to catalog
$('#catalog').show();
}
}
function showListingConfirmation(listingId) {
const listing = itemListings.find(l => l.id === listingId);
if (!listing) return;
const item = catalogItems.find(i => i.name === listing.itemName);
if (!item) return;
let robuxAfterPurchase = currentUser.robux - listing.price;
$('#buy-confirmation-image').attr('src', item.image);
$('#buy-item-name').text(item.name);
$('#buy-item-price').html(`<span class="robux-icon"></span>${formatNumber(listing.price)}`);
$('#robux-after-purchase').text(formatNumber(robuxAfterPurchase));
// Update the popup content
const buyConfirmationPopup = $('#buy-confirmation-popup');
buyConfirmationPopup.find('h3').text('Confirm Purchase');
buyConfirmationPopup.find('p:first-of-type').html(
`Would you like to buy the item <span id="buy-item-name">${item.name}</span> from ${listing.seller} for <span id="buy-item-price"><span class="robux-icon"></span>${formatNumber(listing.price)}</span>?`
);
buyConfirmationPopup.find('.confirm-button').attr('onclick', `confirmBuyListing('${listing.id}')`);
buyConfirmationPopup.find('.cancel-button').attr('onclick', 'cancelPurchase()');
buyConfirmationPopup.find('p:last-of-type').html(
`Your balance after this transaction will be <span class="robux-icon"></span><span id="robux-after-purchase">${formatNumber(robuxAfterPurchase)}</span>.`
);
// Disable confirm button if balance would be negative
$('.confirm-button').prop('disabled', robuxAfterPurchase < 0);
if (robuxAfterPurchase < 0) {
$('.confirm-button').css('background-color', '#808080');
} else {
$('.confirm-button').css('background-color', '#ffffff');
}
$('#buy-confirmation-popup').show();
$('#overlay').show();
}
function confirmBuyListing(listingId) {
const listing = itemListings.find(l => l.id === listingId);
if (!listing) return;
const item = catalogItems.find(i => i.name === listing.itemName);
if (!item) return;
if (currentUser.robux < listing.price) {
alert('Not enough Robux!');
return;
}
const seller = users.find(u => u.username === listing.seller);
if (!seller) {
alert('Seller not found!');
return;
}
const sellerItem = seller.inventory.find(i => i.name === listing.itemName);
if (!sellerItem) {
alert('Item no longer available!');
return;
}
showLoadingScreen();
setTimeout(() => {
// Deduct Robux from buyer
currentUser.robux -= listing.price;
// Add 90% of price to seller (10% marketplace fee)
const sellerEarnings = Math.floor(listing.price * 0.9);
seller.robux += sellerEarnings;
// Give Roblox account the 10% marketplace fee
const robloxUser = users.find(u => u.username === 'Roblox');
if (robloxUser) {
if (!robloxUser.robux) robloxUser.robux = 0;
const marketplaceFee = listing.price - sellerEarnings; // The 10% fee
robloxUser.robux += marketplaceFee;
// Add marketplace fee transaction to Roblox's history
if (!robloxUser.transactions) robloxUser.transactions = [];
robloxUser.transactions.push({
type: 'Sale',
item: `${item.name} (10% Marketplace Fee)`,
amount: marketplaceFee,
date: new Date().toLocaleDateString('en-US')
});
}
// Add transaction record for buyer
if (!currentUser.transactions) currentUser.transactions = [];
currentUser.transactions.push({
type: 'Purchase',
item: listing.itemName,
amount: listing.price,
date: new Date().toLocaleDateString('en-US')
});
// Add transaction record for seller
if (!seller.transactions) seller.transactions = [];
seller.transactions.push({
type: 'Sale',
item: listing.itemName,
amount: sellerEarnings,
date: new Date().toLocaleDateString('en-US')
});
// Add item to buyer's inventory
if (!currentUser.inventory) currentUser.inventory = [];
let userItem = currentUser.inventory.find(i => i.name === listing.itemName);
if (userItem) {
userItem.quantity = (userItem.quantity || 1) + 1;
} else {
currentUser.inventory.push({
...catalogItems.find(i => i.name === listing.itemName),
quantity: 1
});
}
// Remove item from seller's inventory
if (sellerItem.quantity > 1) {
sellerItem.quantity--;
} else {
seller.inventory = seller.inventory.filter(i => i.name !== listing.itemName);
}
// Remove the listing
itemListings = itemListings.filter(l => l.id !== listingId);
// Update RAP - Check if this is the first purchase
const catalogItem = catalogItems.find(i => i.name === listing.itemName);
if (catalogItem) {
// If item has 0 RAP and is limited/limited u, this is the first purchase
if (catalogItem.rap === 0 && (catalogItem.limitedStatus === 'limited' || catalogItem.limitedStatus === 'unlimited')) {
catalogItem.rap = listing.price; // Set initial RAP to full price for first purchase
} else {
// For subsequent purchases, use the normal RAP calculation
catalogItem.rap = recalculateRAP(catalogItem, listing.price);
}
}
// Add to purchase history
purchaseHistory.push({
itemName: listing.itemName,
quantity: listing.quantity,
price: listing.price,
seller: listing.seller,
buyer: currentUser.username,
date: new Date().toISOString()
});
// Update item detail page if visible
if ($('#item-detail-page').is(':visible')) {
showItemDetail(listing.itemName);
}
// Immediately update Robux display
updateRobuxDisplay();
saveToLocalStorage();
hideLoadingScreen();
closePopup('buy-confirmation-popup');
closePopup('item-listings-popup');
showSuccessBanner('Purchase successful!');
}, 1200);
}
function deleteListing(listingId) {
const listing = itemListings.find(l => l.id === listingId);
if (!listing) return;
// Only allow Roblox or the listing owner to delete
if (currentUser.username !== 'Roblox' && currentUser.username !== listing.seller) return;
itemListings = itemListings.filter(l => l.id !== listingId);
saveToLocalStorage();
showSuccessBanner('Listing deleted successfully!');
// Refresh the listings popup
const itemName = listing.itemName;
closePopup('item-listings-popup');
showBuyConfirmation(itemName);
}
function showOwners(itemName) {
let ownersList = '';
users.forEach(user => {
// Skip users with private inventory unless currentUser is Roblox
if (user.privateInventory && currentUser.username !== 'Roblox') return;
const userItem = user.inventory?.find(i => i.name === itemName);
if (userItem && userItem.quantity > 0) {
const verified = user.verified ? ' <img src="https://en.help.roblox.com/hc/article_attachments/7997146649876" alt="Verified" class="verified-badge">' : '';
ownersList += `<p style="padding: 8px; margin: 5px 0; background-color: #232527; border-radius: 5px;">
<span class="clickable" onclick="showUserProfile('${user.username}'); closePopup('owners-popup')"> ${user.username}${verified}</span>: x${userItem.quantity}
</p>`;
}
});
if (!ownersList) {
ownersList = '<p>No users currently own this item</p>';
}
$('#owners-list').html(ownersList);
$('#owners-popup').show();
$('#overlay').show();
}
function updateInventory() {
if (!currentUser || !currentUser.inventory) return;
// Update the toggle state
$('#private-inventory-toggle').prop('checked', currentUser.privateInventory || false);
if (!currentUser.equippedItems) {
currentUser.equippedItems = [];
}
let sortedInventory = [...currentUser.inventory].sort((a, b) => {
// If item is equipped, put it first
const aEquipped = currentUser.equippedItems.includes(a.name);
const bEquipped = currentUser.equippedItems.includes(b.name);
if (aEquipped && !bEquipped) return -1;
if (!aEquipped && bEquipped) return 1;
let catalogItemA = catalogItems.find(i => i.name === a.name);
let catalogItemB = catalogItems.find(i => i.name === b.name);
let isLimitedA = catalogItemA && (catalogItemA.limitedStatus === 'limited' || catalogItemA.limitedStatus === 'unlimited');
let isLimitedB = catalogItemB && (catalogItemB.limitedStatus === 'limited' || catalogItemB.limitedStatus === 'unlimited');
let isOffSaleA = catalogItemA && catalogItemA.offSale;
let isOffSaleB = catalogItemB && catalogItemB.offSale;
if (isLimitedA && !isLimitedB) return -1;
if (!isLimitedA && isLimitedB) return 1;
if (isLimitedA && isLimitedB) {
return (catalogItemB.rap || 0) - (catalogItemA.rap || 0);
}
if (isOffSaleA && !isOffSaleB) return 1;
if (!isOffSaleA && isOffSaleB) return -1;
return (b.price || 0) - (a.price || 0);
});
let startIndex = (currentInventoryPage - 1) * itemsPerPage;
let endIndex = startIndex + itemsPerPage;
let pageItems = sortedInventory.slice(startIndex, endIndex);
let inventoryHtml = '';
for (let item of pageItems) {
if (!item) continue;
let limitedIcon = '';
if (item.limitedStatus === 'unlimited') {
limitedIcon = '<div class="limited-icon unlimited"></div>';
} else if (item.limitedStatus === 'limited') {
limitedIcon = '<div class="limited-icon limited"></div>';
}
let catalogItem = catalogItems.find(i => i.name === item.name) || {};
let isOffSale = catalogItem && catalogItem.offSale;
const listings = itemListings.filter(l => l.itemName === item.name)
.sort((a, b) => a.price - b.price);
const lowestListing = listings[0];
const priceDisplay = (catalogItem.limitedStatus !== 'none' && !lowestListing) ?
'<p style="color: #B8B8B8; font-weight: bold;">No Resellers</p>' :
(lowestListing ?
`<p><span class="robux-icon"></span>${formatNumber(lowestListing.price)}</p>` :
(catalogItem.price === 0 ?
'<p><span class="robux-icon"></span>Free</p>' :
`<p><span class="robux-icon"></span>${formatNumber(catalogItem.price || 0)}</p>`));
const isEquipped = currentUser.equippedItems.includes(item.name);
const equipButton = isEquipped ?
`<button class="take-off-button" onclick="event.stopPropagation(); toggleEquipItem('${escapeHtml(item.name)}')">Take Off</button>` :
`<button class="put-on-button" onclick="event.stopPropagation(); toggleEquipItem('${escapeHtml(item.name)}')">Put On</button>`;
inventoryHtml += `
<div class="catalog-item">
<img src="${item.image || ''}" alt="${item.name || 'Item'}">
${limitedIcon}
<div class="catalog-item-details">
<h3>${item.name || 'Unnamed Item'}</h3>
${isOffSale ? '<p style="color: #ff4444;">Offsale</p>' : ''}
${(item.limitedStatus !== 'none' && !item.offSale) ? `<p>RAP: ${formatNumber(catalogItem.rap || 0)}</p>` : ''}
${priceDisplay}
<p>By: Roblox <img src="https://en.help.roblox.com/hc/article_attachments/7997146649876" alt="Verified" style="width: 16px; height: 16px; vertical-align: middle;"></p>
${equipButton}
</div>
</div>
`;
}
$('#inventory-items').html(inventoryHtml || '<p>No items in inventory</p>');
updatePaginationWithArrows(Math.ceil(sortedInventory.length / itemsPerPage), currentInventoryPage, 'inventory-pagination', changeInventoryPage);
}
function updatePaginationWithArrows(totalPages, currentPage, paginationId, changePageFunction) {
let paginationHtml = `
<button onclick="${changePageFunction.name}(${Math.max(1, currentPage - 1)})" ${currentPage === 1 ? 'disabled' : ''}>&larr;</button>
<span>${currentPage} / ${totalPages}</span>
<button onclick="${changePageFunction.name}(${Math.min(totalPages, currentPage + 1)})" ${currentPage === totalPages ? 'disabled' : ''}>&rarr;</button>
`;
$(`#${paginationId}`).html(paginationHtml);
}
function changeInventoryPage(page) {
currentInventoryPage = page;
updateInventory();
}
function togglePrivateInventory() {
if (!currentUser) return;
currentUser.privateInventory = $('#private-inventory-toggle').is(':checked');
saveToLocalStorage();
updateInventory();
// If currently viewing the user's profile, refresh it
if ($('#profile').is(':visible') && $('#profile-username').text().trim() === currentUser.username) {
showUserProfile(currentUser.username);
}
}
function showRemoveItemConfirmation(username, itemName) {
$('#remove-item-confirmation-popup .remove-item-username').text(username);
$('#remove-item-confirmation-popup .remove-item-name').text(itemName);
$('#remove-item-confirmation-popup').data('username', username);
$('#remove-item-confirmation-popup').data('itemName', itemName);
$('#remove-item-confirmation-popup').show();
$('#overlay').show();
}
function confirmRemoveItem() {
const username = $('#remove-item-confirmation-popup').data('username');
const itemName = $('#remove-item-confirmation-popup').data('itemName');
const user = users.find(u => u.username === username);
if (!user) return;
showLoadingScreen();
setTimeout(() => {
// Find the item in the user's inventory
const inventoryItem = user.inventory.find(i => i.name === itemName);
if (inventoryItem) {
if (inventoryItem.quantity > 1) {
inventoryItem.quantity--;
} else {
user.inventory = user.inventory.filter(i => i.name !== itemName);
}
// Remove from equipped items if it was equipped
if (user.equippedItems && user.equippedItems.includes(itemName)) {
user.equippedItems = user.equippedItems.filter(name => name !== itemName);
}
// Update RAP
user.rap = user.inventory.reduce((sum, item) => {
const catalogItem = catalogItems.find(i => i.name === item.name);
const itemRap = catalogItem ? catalogItem.rap : item.rap;
return sum + itemRap * (item.quantity || 1);
}, 0);
saveToLocalStorage();
hideLoadingScreen();
closePopup('remove-item-confirmation-popup');
showSuccessBanner(`Item removed from ${username}'s inventory!`);
// Refresh the profile view
showUserProfile(username);
}
}, 800);
}
function escapeHtml(str) {
if (!str) return '';
const div = document.createElement('div');
div.textContent = str;
return div.innerHTML;
}
function showGiveItemPopup(itemName) {
if (currentUser.username !== 'Roblox') return;
itemToGive = catalogItems.find(i => i.name === itemName);
if (!itemToGive) return;
let usersHtml = '<option value="">Select User</option>';
users.forEach(user => {
if (user.username !== 'Roblox') {
usersHtml += `<option value="${user.username}">${user.username}</option>`;
}
});
$('#recipient-select').html(usersHtml);
$('#give-quantity').val(1);
$('#give-item-popup').show();
$('#overlay').show();
}
function confirmGiveItem() {
recipientUsername = $('#recipient-select').val();
if (!recipientUsername) {
alert('Please select a user');
return;
}
$('#recipient-name').text(recipientUsername);
$('#give-item-popup').hide();
$('#confirm-give-popup').show();
}
function giveItem() {
if (!itemToGive || !recipientUsername) return;
const quantity = parseInt($('#give-quantity').val()) || 1;
showLoadingScreen();
setTimeout(() => {
const recipient = users.find(u => u.username === recipientUsername);
if (!recipient) return;
if (!recipient.inventory) recipient.inventory = [];
let existingItem = recipient.inventory.find(i => i.name === itemToGive.name);
if (existingItem) {
existingItem.quantity = (existingItem.quantity || 1) + quantity;
} else {
recipient.inventory.push({
...itemToGive,
quantity: quantity
});
}
saveToLocalStorage();
hideLoadingScreen();
closePopup('confirm-give-popup');
showSuccessBanner(`${quantity} item(s) given to ${recipientUsername}!`);
}, 1200);
}
function saveBio() {
if (!currentUser) return;
let bioText = $('#bio-text').val().trim();
let username = $('#edit-bio-popup').data('username');
let user = users.find(u => u.username === username);
if (user) {
user.bio = bioText;
saveToLocalStorage();
closePopup('edit-bio-popup');
showUserProfile(username);
showSuccessBanner('Bio updated successfully!');
}
}
function showEditBioPopup(username) {
let user = users.find(u => u.username === username);
if (user) {
$('#bio-text').val(user.bio || '');
$('#edit-bio-popup').data('username', username);
$('#edit-bio-popup').show();
$('#overlay').show();
}
}
function updateLeaderboard() {
let sortedUsers = [...users];
// First, calculate RAP for each user
sortedUsers.forEach(user => {
if (!user.privateInventory || currentUser.username === 'Roblox') {
user.calculatedRap = 0;
if (user.inventory) {
user.calculatedRap = user.inventory.reduce((sum, item) => {
let catalogItem = catalogItems.find(i => i.name === item.name);
let itemRap = catalogItem ? catalogItem.rap : item.rap;
return sum + itemRap * (item.quantity || 1);
}, 0);
}
}
});
// Sort users: non-private by RAP (descending), private users at the bottom
sortedUsers.sort((a, b) => {
if (a.privateInventory && !b.privateInventory && currentUser.username !== 'Roblox') return 1;
if (!a.privateInventory && b.privateInventory && currentUser.username !== 'Roblox') return -1;
if (a.privateInventory && b.privateInventory) return 0;
return b.calculatedRap - a.calculatedRap;
});
let leaderboardHtml = '<div style="padding: 20px; background-color: #393B3D; border-radius: 10px;">';
leaderboardHtml += `
<div style="display: grid; grid-template-columns: 1fr 2fr 1fr; gap: 10px; padding: 10px; margin-bottom: 10px; border-bottom: 1px solid #5F6569; font-weight: bold; font-size: 20px;">
<div>Rank</div>
<div>Username</div>
<div>RAP</div>
</div>
`;
sortedUsers.forEach((user, index) => {
const verifiedBadge = user.verified ? ' <img src="https://en.help.roblox.com/hc/article_attachments/7997146649876" alt="Verified" class="verified-badge">' : '';
const adminBadge = user.isAdmin ? ' <img src="https://i.imgur.com/WRPVUAh.png" alt="Admin" class="admin-badge">' : '';
const robuxIcon = '<span class="robux-icon" style="width:18px;height:18px;display:inline-block;background-image:url(https://devforum-uploads.s3.dualstack.us-east-2.amazonaws.com/uploads/original/4X/e/d/f/edfae9388da4cd8496b885a8a2df613372500d9c.png);background-size:contain;background-repeat:no-repeat;background-position:center;"></span>';
const rapDisplay = (user.privateInventory && currentUser.username !== 'Roblox') ? 'Private' : robuxIcon + formatNumber(user.calculatedRap);
const rowClass = (user.privateInventory && currentUser.username !== 'Roblox') ? 'leaderboard-row-private' : 'leaderboard-row-public';
leaderboardHtml += `
<div class="${rowClass} leaderboard-row" style="display: grid; grid-template-columns: 1fr 2fr 1fr; gap: 10px; padding: 10px; border-radius: 5px; margin-bottom: 5px;">
<div>${index + 1}</div>
<div class="clickable" onclick="showUserProfile('${user.username}')">
${adminBadge}${user.username}${verifiedBadge}
</div>
<div>${rapDisplay}</div>
</div>
`;
});
leaderboardHtml += '</div>';
$('#leaderboard-list').html(leaderboardHtml);
}
function setActiveNavItem(page) {
$('.nav a').removeClass('active');
if (page === 'trade') {
$('.nav a[onclick*="showTrade"]').addClass('active');
} else if (page === 'leaderboard') {
$('.nav a[onclick*="showLeaderboard"]').addClass('active');
} else {
$(`.nav a[onclick*="show${page.charAt(0).toUpperCase() + page.slice(1)}"]`).addClass('active');
}
}
function switchUser(username) {
currentUser = users.find(u => u.username === username);
updateProfile();
updateInventory();
updateRobuxDisplay();
$('#admin-link').hide();
if (username === 'Roblox' || (currentUser && currentUser.isAdmin)) {
$('#admin-link').show();
}
$('#current-user-display').text(`Current User: ${username}`);
closePopup('users-popup');
showProfile();
$('.profile-inventory').scrollTop(0);
currentCatalogPage = 1;
currentInventoryPage = 1;
updateCatalog();
$('#item-detail-page').hide();
$('#catalog').hide();
}
function showAnnouncementPopup() {
$('#announcement-popup').show();
$('#overlay').show();
}
function showHiddenItems() {
// Get all hidden items
const hiddenItems = catalogItems.filter(item => item.hidden);
if (hiddenItems.length === 0) {
$('#hidden-items-list').html('<p style="text-align: center; padding: 20px;">No hidden items found</p>');
} else {
let hiddenItemsHtml = '';
for (let item of hiddenItems) {
let limitedIcon = '';
if (item.limitedStatus === 'unlimited') {
limitedIcon = '<div class="limited-icon unlimited"></div>';
} else if (item.limitedStatus === 'limited') {
limitedIcon = '<div class="limited-icon limited"></div>';
}
const priceDisplay = item.offSale ?
'<p style="color: #ff4444;">Offsale</p>' :
`<p><span class="robux-icon"></span>${formatNumber(item.price)}</p>`;
hiddenItemsHtml += `
<div class="catalog-item" onclick="showItemDetail(&quot;${escapeHtml(item.name)}&quot;); closePopup('hidden-items-popup');" style="cursor: pointer;">
<img src="${item.image}" alt="${item.name}">
${limitedIcon}
<div class="catalog-item-details">
<h3>${item.name}</h3>
${priceDisplay}
${(item.limitedStatus !== 'none' && !item.offSale) ? `<p>RAP: ${formatNumber(item.rap)}</p>` : ''}
<p>By: Roblox <img src="https://en.help.roblox.com/hc/article_attachments/7997146649876" alt="Verified" style="width: 16px; height: 16px; vertical-align: middle;"></p>
</div>
</div>
`;
}
$('#hidden-items-list').html(hiddenItemsHtml);
}
$('#hidden-items-popup').show();
$('#overlay').show();
}
function addAnnouncement() {
const text = $('#announcement-text').val().trim();
const color = $('#announcement-color').val();
if (!text) return;
const announcement = {
id: Date.now(),
text,
color
};
let saved = JSON.parse(localStorage.getItem("announcementData") || "[]");
saved.push(announcement);
localStorage.setItem("announcementData", JSON.stringify(saved));
renderAnnouncements();
$('#announcement-popup').hide();
$('#overlay').hide();
}
function deleteAnnouncement(id) {
let saved = JSON.parse(localStorage.getItem("announcementData") || "[]");
saved = saved.filter(a => a.id !== id);
localStorage.setItem("announcementData", JSON.stringify(saved));
renderAnnouncements();
}
function renderAnnouncements() {
let saved = JSON.parse(localStorage.getItem("announcementData") || "[]");
const current = saved.map(a => `
<div draggable="true" ondragstart="handleDragStart(event, ${a.id})" ondragover="handleDragOver(event)" ondrop="handleDrop(event, ${a.id})" style="background:${a.color};color:white;padding:10px;margin-bottom:5px;border-radius:5px;cursor: move;" data-id="${a.id}">
<span>${a.text}</span>
<span style="float:right;cursor:pointer;" class="edit-announcement-icon" data-id="${a.id}">✎</span>
<span style="float:right;cursor:pointer;margin-right:10px;" onclick="deleteAnnouncement(${a.id})">✖</span>
<span style="float:right;cursor:pointer;margin-right:10px;" onclick="toggleAnnouncementVisibility(${a.id})">👁️</span>
</div>
`).join('');
document.getElementById('current-announcements').innerHTML = current;
if (saved.length) {
// Show announcement banner below header
const topBanner = saved.filter(a => !a.hidden).map(a => `<div style="background:${a.color};color:white;text-align:center;padding:6px;font-weight:bold;font-size:14px;">${a.text}</div>`).join('');
document.getElementById('announcement-banner').innerHTML = topBanner;
document.getElementById('announcement-banner').style.display = saved.filter(a => !a.hidden).length > 0 ? "block" : "none";
} else {
document.getElementById('announcement-banner').style.display = "none";
}
}
$(document).ready(() => {
renderAnnouncements();
});
function toggleAnnouncementVisibility(id) {
let saved = JSON.parse(localStorage.getItem("announcementData") || "[]");
const announcementIndex = saved.findIndex(a => a.id === id);
if (announcementIndex !== -1) {
saved[announcementIndex].hidden = !saved[announcementIndex].hidden;
localStorage.setItem("announcementData", JSON.stringify(saved));
renderAnnouncements();
}
}
function updateItemTimer(itemName, timerElementId) {
const item = catalogItems.find(i => i.name === itemName);
if (!item || !item.timerEndTime) return;
// Calculate remaining time
const now = Date.now();
const timeLeft = Math.max(0, item.timerEndTime - now);
if (timeLeft <= 0) {
// Timer expired, make item offsale
console.log('Timer expired for item:', itemName);
item.offSale = true;
delete item.timerEndTime;
// Also remove timer status so badge disappears
item.timerStatus = 'none';
saveToLocalStorage();
// Update display
$(`#${timerElementId}`).text('This item is now offsale').css('color', '#ff4444');
// Update item display to show offsale status
$('#detail-item-price').html('<p style="color: #ff4444; font-weight: bold; font-size: 24px;">Offsale</p>');
// Disable buy button
$('#detail-buy-button').prop('disabled', true);
// Update catalog to reflect the change
updateCatalog();
return;
}
// Calculate hours, minutes, seconds
const hours = Math.floor(timeLeft / (1000 * 60 * 60));
const minutes = Math.floor((timeLeft % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((timeLeft % (1000 * 60)) / 1000);
// Format the countdown display
const formattedTime = `Will be offsale in ${hours}h ${minutes}m ${seconds}s`;
$(`#${timerElementId}`).text(formattedTime);
// Update every second, passing the unique timer element ID
setTimeout(() => updateItemTimer(itemName, timerElementId), 1000);
}
function checkExpiredTimers() {
const now = Date.now();
let needsSave = false;
catalogItems.forEach(item => {
if (item.timerStatus === 'timer' && item.timerEndTime) {
if (now >= item.timerEndTime) {
console.log('Expired timer found for item:', item.name);
item.offSale = true;
delete item.timerEndTime;
item.timerStatus = 'none'; // Remove timer status so badge disappears
needsSave = true;
}
}
});
if (needsSave) {
console.log('Saving changes after checking expired timers');
saveToLocalStorage();
// Update the catalog display to reflect the changes
updateCatalog();
}
}
function showEditAnnouncementPopup(id) {
const saved = JSON.parse(localStorage.getItem("announcementData") || "[]");
const announcement = saved.find(a => a.id === id);
if (announcement) {
$('#edit-announcement-text').val(announcement.text);
$('#edit-announcement-color').val(announcement.color);
$('#edit-announcement-popup').data('announcementId', id);
$('#edit-announcement-popup').show();
$('#overlay').show();
}
}
function saveEditedAnnouncement() {
const id = $('#edit-announcement-popup').data('announcementId');
const text = $('#edit-announcement-text').val().trim();
const color = $('#edit-announcement-color').val();
if (!text) return;
let saved = JSON.parse(localStorage.getItem("announcementData") || "[]");
const announcement = saved.find(a => a.id === id);
if (announcement) {
announcement.text = text;
announcement.color = color;
localStorage.setItem("announcementData", JSON.stringify(saved));
renderAnnouncements();
$('#edit-announcement-popup').hide();
$('#overlay').hide();
}
}
$(document).ready(() => {
renderAnnouncements();
$('#current-announcements').on('click', '.edit-announcement-icon', function(e) {
e.stopPropagation();
const id = parseInt($(this).data('id'));
showEditAnnouncementPopup(id);
});
});
let dragSrcId = null;
function handleDragStart(e, id) {
dragSrcId = id;
}
function handleDragOver(e) {
e.preventDefault();
}
function handleDrop(e, targetId) {
if (dragSrcId === null || dragSrcId === targetId) return;
let saved = JSON.parse(localStorage.getItem("announcementData") || "[]");
const fromIndex = saved.findIndex(a => a.id === dragSrcId);
const toIndex = saved.findIndex(a => a.id === targetId);
if (fromIndex > -1 && toIndex > -1) {
const [moved] = saved.splice(fromIndex, 1);
saved.splice(toIndex, 0, moved);
localStorage.setItem("announcementData", JSON.stringify(saved));
renderAnnouncements();
}
}
// Add JavaScript to show/hide timer fields based on timer status selection
$(document).ready(function() {
// Check if timer is selected on load for both forms
if ($('#item-timer-status').val() === 'timer') {
$('#timer-fields').show();
} else {
$('#timer-fields').hide();
}
if ($('#edit-item-timer-status').val() === 'timer') {
$('#edit-timer-fields').show();
} else {
$('#edit-timer-fields').hide();
}
// For upload form
$('#item-timer-status').change(function() {
if ($(this).val() === 'timer') {
$('#timer-fields').show();
} else {
$('#timer-fields').hide();
}
});
// For edit form
$('#edit-item-timer-status').change(function() {
if ($(this).val() === 'timer') {
$('#edit-timer-fields').show();
} else {
$('#edit-timer-fields').hide();
}
});
// Check for expired timers every second
setInterval(checkExpiredTimers, 1000);
});
function showEditUserPopup(username) {
let user = users.find(u => u.username === username);
if (user) {
$('#edit-username').val(user.username);
$('#edit-join-date').val(user.joinDate);
$('#edit-place-visits').val(user.placeVisits);
$('#edit-friends').val(user.friends);
$('#edit-followers').val(user.followers);
$('#edit-following').val(user.following);
$('#edit-rap').val(user.rap);
$('#edit-robux').val(user.robux);
$('#edit-user-popup').show();
$('#overlay').show();
}
}
function saveUserChanges() {
let username = $('#edit-username').val();
let joinDate = $('#edit-join-date').val();
let placeVisits = parseInt($('#edit-place-visits').val());
let friends = parseInt($('#edit-friends').val());
let followers = parseInt($('#edit-followers').val());
let following = parseInt($('#edit-following').val());
let rap = parseInt($('#edit-rap').val());
let robux = parseInt($('#edit-robux').val());
let user = users.find(u => u.username === username);
if (user) {
user.joinDate = joinDate;
user.placeVisits = placeVisits;
user.friends = friends;
user.followers = followers;
user.following = following;
user.rap = rap;
user.robux = robux;
saveToLocalStorage();
closePopup('edit-user-popup');
showSuccessBanner('User updated successfully!');
}
}
function previewUploadImage() {
const imageUrl = $('#item-image').val() || "https://i.imgur.com/uXWWQIs.png";
// If using the specific image URL, make sure it's set properly
if (imageUrl === "https://i.imgur.com/uXWWQIs.png") {
$('#upload-preview-image').attr('src', "https://i.imgur.com/uXWWQIs.png").show();
$('#upload-preview-image').data('imageUrl', "https://i.imgur.com/uXWWQIs.png");
} else {
$('#upload-preview-image').attr('src', imageUrl).show();
$('#upload-preview-image').data('imageUrl', imageUrl);
}
}
function showUserSortingPopup() {
// Generate the sortable user list
let sortableUsersHtml = '';
for (let user of users) {
sortableUsersHtml += `
<div class="sortable-user-item" draggable="true" data-username="${user.username}">
<span>${user.username}</span>
<span style="cursor: move; float: right;">☰</span>
</div>
`;
}
$('#sortable-users-list').html(sortableUsersHtml);
// Add drag and drop event listeners to the sortable list
const sortableList = document.getElementById('sortable-users-list');
const sortableItems = sortableList.querySelectorAll('.sortable-user-item');
sortableItems.forEach(item => {
item.addEventListener('dragstart', function() {
// Add a class to indicate the item being dragged
setTimeout(() => this.classList.add('dragging'), 0);
});
item.addEventListener('dragend', function() {
// Remove the dragging class
this.classList.remove('dragging');
});
});
sortableList.addEventListener('dragover', function(e) {
e.preventDefault();
const draggingItem = sortableList.querySelector('.dragging');
const siblings = [...sortableList.querySelectorAll('.sortable-user-item:not(.dragging)')];
// Find the sibling after which the dragging item should be placed
let nextSibling = siblings.find(sibling => {
return e.clientY <= sibling.offsetTop + sibling.offsetHeight / 2;
});
// Insert dragging item before the found sibling or at the end
sortableList.insertBefore(draggingItem, nextSibling);
});
// Show the popup
$('#user-sorting-popup').show();
$('#overlay').show();
}
function saveUserOrder() {
// Get the new order of users from the sortable list
const sortableItems = document.querySelectorAll('.sortable-user-item');
let newUserOrder = [];
// Create a new users array with the updated order
sortableItems.forEach(item => {
const username = item.dataset.username;
const user = users.find(u => u.username === username);
if (user) {
newUserOrder.push(user);
}
});
// Update the users array with the new order
if (newUserOrder.length === users.length) {
users = newUserOrder;
saveToLocalStorage();
showSuccessBanner('User order saved successfully!');
closePopup('user-sorting-popup');
} else {
alert('Error: Some users were lost during sorting. Please try again.');
}
}
// Add this CSS to the existing styles
function addSortableUserStyles() {
const styleElement = document.createElement('style');
styleElement.innerHTML = `
.sortable-list {
max-height: 400px;
overflow-y: auto;
margin-bottom: 15px;
}
.sortable-user-item {
padding: 10px 15px;
background-color: #2C2F33;
border-radius: 5px;
margin-bottom: 8px;
cursor: pointer;
user-select: none;
transition: background-color 0.2s;
}
.sortable-user-item:hover {
background-color: #393B3D;
}
.sortable-user-item.dragging {
opacity: 0.7;
background-color: #1F2022;
}
`;
document.head.appendChild(styleElement);
}
// Call this function when the document is ready
$(document).ready(function() {
// ... existing ready code ...
addSortableUserStyles();
});
// Add these functions to handle user sorting drag and drop
function handleUserDragStart(event, username) {
event.dataTransfer.setData('text/plain', username);
setTimeout(() => event.target.classList.add('dragging'), 0);
}
function handleUserDragOver(event) {
event.preventDefault();
}
function handleUserDrop(event, targetUsername) {
event.preventDefault();
const draggedUsername = event.dataTransfer.getData('text/plain');
if (draggedUsername === targetUsername) return;
// Find the indices of the dragged and target items
const draggedIndex = users.findIndex(u => u.username === draggedUsername);
const targetIndex = users.findIndex(u => u.username === targetUsername);
if (draggedIndex === -1 || targetIndex === -1) return;
// Reorder the users array
const userToMove = users.splice(draggedIndex, 1)[0];
users.splice(targetIndex, 0, userToMove);
// Update the UI
showUserSortingPopup();
}
function showUserSortingPopup() {
// Generate the sortable user list
let sortableUsersHtml = '';
for (let user of users) {
sortableUsersHtml += `
<div draggable="true"
ondragstart="handleUserDragStart(event, '${user.username}')"
ondragover="handleUserDragOver(event)"
ondrop="handleUserDrop(event, '${user.username}')"
class="sortable-user-item"
data-username="${user.username}">
<span>${user.username}</span>
<span style="float:right;cursor:pointer;margin-right:10px;">☰</span>
</div>
`;
}
$('#sortable-users-list').html(sortableUsersHtml);
// Show the popup
$('#user-sorting-popup').show();
$('#overlay').show();
}
function saveUserOrder() {
saveToLocalStorage();
showSuccessBanner('User order saved successfully!');
closePopup('user-sorting-popup');
}
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=ewr23r2r/344dddd" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>