This repository has been archived on 2026-01-04. You can view files and clone it, but cannot push or open issues or pull requests.
polyplus/js/account/avatar-sandbox.js

1061 lines
No EOL
42 KiB
JavaScript

let Utilities;
const Container = document.querySelector('.container.p-0.p-lg-5')
const ItemCache = {
24122: {
type: "hat",
accessoryType: "hat",
name: "Polytoria Cap",
price: 0,
creator: {
name: "Polytoria",
id: 1
},
thumbnail: "https://c0.ptacdn.com/thumbnails/assets/RR0VPd5hX30Fx5APwRBGObotf1xD1DRT.png",
asset: "https://c0.ptacdn.com/assets/InBsL5bpJdp84ZPZGQMeHuyCBlo-uOv7.glb"
},
24118: {
type: "shirt",
name: "Green Polytoria Flannel",
price: 0,
creator: {
name: "Polytoria",
id: 1
},
thumbnail: "https://c0.ptacdn.com/thumbnails/assets/s7l57JugjbZfWTKAQ0cqTohOBraRbX5E.png",
asset: "https://c0.ptacdn.com/assets/uWrrnFGwgNN5W171vqYTWY7E639rKiXK.png"
},
24123: {
type: "pants",
name: "Jeans",
price: 0,
creator: {
name: "Polytoria",
id: 1
},
thumbnail: "https://c0.ptacdn.com/thumbnails/assets/anebTuFMLg8NKhRL3ab7hbzCfmcsFqGO.png",
asset: "https://c0.ptacdn.com/assets/HD6TFdXD8CaflRNmd84VCNyNsmTB0SH3.png"
}
/*
37582: {
type: "torso",
name: "Slim Body",
price: 0,
creator: {
name: "Polytoria",
id: 1
},
thumbnail: "https://c0.ptacdn.com/thumbnails/assets/f_k-ZN_xmA_ALZiJQanOKT-Y4qq5kI1b.png",
asset: "https://c0.ptacdn.com/assets/qoqZ2qPyaGvB3MLGgJZ6oLWTz-xxGo-8.glb"
}
*/
}
let Avatar = {
useCharacter: true,
items: [24122],
shirt: 24118,
pants: 24123,
headColor: '#e0e0e0',
torsoColor: '#e0e0e0',
leftArmColor: '#e0e0e0',
rightArmColor: '#e0e0e0',
leftLegColor: '#e0e0e0',
rightLegColor: '#e0e0e0'
};
/* Discovery */
let Page = 1
let PageCount = 1
let Search = ""
let Sort = "createdAt"
let Order = "desc"
let ShowOffsale = true
let TabSelected = "hat"
let RetroItems = null
let Outfits = null
/* Customization */
let SelectedBodyPart
!(async () => {
Utilities = await import(chrome.runtime.getURL('resources/utils.js'));
Utilities = Utilities.default;
chrome.storage.sync.get(['PolyPlus_Settings', 'PolyPlus_AvatarSandboxOutfits'], function(result){
Settings = result.PolyPlus_Settings || Utilities.DefaultSettings;
Outfits = result.PolyPlus_AvatarSandboxOutfits || [];
if (Settings.AvatarSandboxOn || 1 === 1) {
if (new URLSearchParams(window.location.search).has('sandbox')) {
document.title = 'Poly+ Avatar Sandbox'
PageLoad()
} else {
const SandboxButton = document.createElement('a');
SandboxButton.classList = 'btn btn-outline-success w-100 mt-3';
SandboxButton.href = '?sandbox=true';
SandboxButton.innerHTML = '<i class="fas fa-shirt"></i> Avatar Sandbox';
document.getElementById('cont-move').parentElement.appendChild(SandboxButton);
}
}
})
})();
async function PageLoad() {
const PageContents = (await ((await fetch(chrome.runtime.getURL('resources/avatar-sandbox.html'))).text()))
Container.innerHTML = PageContents
Utilities.InjectResource("registerTooltips")
IFrame = document.getElementById('viewFrame')
UpdateAvatar()
LoadItems()
const Tabs = document.getElementById('tabs')
Array.from(Tabs.children).forEach((element) => {
element.addEventListener('click', function () {
let Link = element.getElementsByTagName('a')[0];
if (!Link.classList.contains('active')) {
Link.classList.add('active');
Tabs.querySelector(`[data-tab="${TabSelected}"]`).classList.remove('active');
TabSelected = Link.getAttribute('data-tab');
ItemSearch.previousElementSibling.value = ''
Page = 1;
Search = ""
LoadItems();
}
});
});
const BodyColorsModal = document.getElementById('p+body_colors')
const BodyParts = Array.from(document.getElementsByClassName('bodypart'))
BodyParts.forEach(part => {
part.addEventListener('click', function(){
SelectedBodyPart = part.id
BodyColorsModal.showModal()
})
})
const BodyColors = Array.from(document.getElementsByClassName('colorpicker-color'))
BodyColors.forEach(color => {
color.addEventListener('click', function(){
Avatar[SelectedBodyPart+'Color'] = color.style.backgroundColor
BodyColorsModal.close()
UpdateAvatar()
})
})
const ItemSearch = document.getElementById('search-btn')
ItemSearch.addEventListener('click', function(){
Search = ItemSearch.previousElementSibling.value
Page = 1
LoadItems()
})
const ItemSort = document.getElementById('item-sort')
ItemSort.addEventListener('change', function(){
Sort = ItemSort.options[ItemSort.selectedIndex].value
Page = 1
LoadItems()
})
const ItemOrder = document.getElementById('item-order')
ItemOrder.addEventListener('change', function(){
Order = ItemOrder.options[ItemOrder.selectedIndex].value
Page = 1
LoadItems()
})
/*
Public API does not have an offsale parameter
document.getElementById('show-offsale').addEventListener('change', function(){
ShowOffsale = !ShowOffsale
console.log(ShowOffsale)
Page = 1
LoadItems()
})
*/
// Pagination is annoying
const First = document.getElementById('pagination-first');
const Prev = document.getElementById('pagination-prev');
const Next = document.getElementById('pagination-next');
const Last = document.getElementById('pagination-last');
if (Page > 0) {
Prev.parentElement.classList.remove('disabled');
First.parentElement.classList.remove('disabled');
} else {
Prev.parentElement.classList.add('disabled');
First.parentElement.classList.add('disabled');
}
First.addEventListener('click', function () {
if (Page > 1) {
Page = 1;
LoadItems();
}
});
Prev.addEventListener('click', function () {
if (Page > 1) {
Page--;
LoadItems();
}
});
Next.addEventListener('click', function () {
if (Page < PageCount) {
Page++;
LoadItems();
}
});
Last.addEventListener('click', function () {
if (Page < PageCount) {
Page = PageCount;
LoadItems();
}
});
const ClearButton = document.getElementById('clear');
ClearButton.addEventListener('click', function () {
Avatar = {
useCharacter: true,
items: [24122],
shirt: 24118,
pants: 24123,
headColor: '#e0e0e0',
torsoColor: '#e0e0e0',
leftArmColor: '#e0e0e0',
rightArmColor: '#e0e0e0',
leftLegColor: '#e0e0e0',
rightLegColor: '#e0e0e0'
};
UpdateAvatar();
});
const LoadMyselfButton = document.getElementById('myself');
LoadMyselfButton.addEventListener('click', function () {
LoadUser(JSON.parse(window.localStorage.getItem('p+account_info')).ID);
});
const JSONUploadButton = document.getElementById('jsonUpload');
JSONUploadButton.addEventListener('change', function () {
const Reader = new FileReader();
Reader.addEventListener('loadend', function () {
Avatar = JSON.parse(Reader.result);
UpdateAvatar();
JSONUploadButton.value = '';
});
Reader.readAsText(JSONUploadButton.files[0]);
});
const JSONSaveButton = document.getElementById('jsonSave');
JSONSaveButton.addEventListener('click', function () {
const Download = document.createElement('a');
Download.href = URL.createObjectURL(
new Blob([JSON.stringify(Avatar)], {
type: 'application/json'
})
);
Download.setAttribute('download', 'AvatarSandbox.json');
document.body.appendChild(Download);
Download.click();
document.body.removeChild(Download);
});
const LoadAsset = document.getElementById('load-asset')
const LoadAssetType = document.getElementById('load-asset-type')
LoadAsset.addEventListener('click', function(){
const SelectedType = LoadAssetType.options[LoadAssetType.selectedIndex].value
if (LoadAsset.previousElementSibling.value === "trofie") {
LoadAsset.previousElementSibling.value = 31501
}
if (SelectedType !== 'user') {
if (SelectedType === 'hat') {
Avatar.items.push(LoadAsset.previousElementSibling.value);
} else {
if (!isNaN(LoadAsset.previousElementSibling.value)) {
Avatar[SelectedType] = parseInt(LoadAsset.previousElementSibling.value)
} else {
Avatar[SelectedType] = LoadAsset.previousElementSibling.value
}
}
UpdateAvatar();
} else {
LoadUser(LoadAsset.previousElementSibling.value)
}
LoadAsset.previousElementSibling.value = ""
})
const SaveButton = document.getElementById('saveOutfit')
const OutfitCreateModal = document.getElementById('p+outfit_create')
const OutfitCreateButton = document.getElementById('p+save_outfit_confirm')
const OutfitCreateError = document.getElementById('p+outfit_create_error')
SaveButton.addEventListener('click', function(){
console.log(Outfits)
OutfitCreateModal.showModal()
})
OutfitCreateButton.addEventListener('click', function(){
let OutfitName = OutfitCreateButton.previousElementSibling.value.trim()
OutfitCreateButton.previousElementSibling.value = ''
if (OutfitName === '') {
OutfitCreateError.classList = 'text-danger';
OutfitCreateError.innerHTML = '<i class="fa-duotone fa-circle-exclamation mr-1"></i> You cannot name an outfit nothing.';
return
} else if (OutfitName.length > 25) {
OutfitName = OutfitName.substring(0, 25)
} else if (Outfits.findIndex((x) => x.name.trim() === OutfitName) !== -1) {
OutfitCreateError.classList = 'text-danger';
OutfitCreateError.innerHTML = '<i class="fa-duotone fa-circle-exclamation mr-1"></i> You already have an outfit with the name "' + OutfitName + '".';
return
}
OutfitCreateModal.close()
Outfits.push({
name: OutfitName,
createdAt: new Date().getTime(),
data: Avatar
})
if (TabSelected === 'outfit') {
LoadItems()
}
chrome.storage.sync.set({'PolyPlus_AvatarSandboxOutfits': Outfits}, function(){
console.log('Saved outfits!')
})
})
document.getElementById('view-cache').addEventListener('click', function(){
console.log('Cache: ', ItemCache)
})
}
async function UpdateAvatar() {
// Hats, Tools: https://api.polytoria.com/v1/assets/serve-mesh/ID
// or: https://api.polytoria.com/v1/assets/serve/ID/Asset
const FormattedAvatar = structuredClone(Avatar)
if (RetroItems === null) {
const RetroItemsPromise = Avatar.items.map(async (id) => {
if (Math.abs(id) !== id) {
Items = (await (await fetch('https://poly-upd-archival.pages.dev/data.json')).json())
Object.values(Items).forEach((item, index) => {
item.id = parseInt(Object.keys(Items)[index])
item.thumbnail = 'https://poly-archive.pages.dev/assets/thumbnails/' + item.id + '.png'
item.creator = {
id: 1,
name: "Polytoria"
}
if (item.asset === undefined) {
item.asset = 'https://poly-upd-archival.pages.dev/glb/' + item.id + '.glb'
}
item.id = item.id*-1
item.ribbon = 'retro'
ItemCache[item.id] = item
})
const PaginationItems = Object.values(Items)
let Groups = []
while (PaginationItems.length > 0) {
Groups.push(PaginationItems.splice(0, 12));
}
Items = {
assets: Groups[Page - 1],
pages: Groups.length
}
RetroItems = Groups
return
}
})
await Promise.all(RetroItemsPromise)
}
const AccessoryPromise = [...Avatar.items, Avatar.tool].filter((x) => x !== undefined && !x.toString().startsWith('http') && !x.toString().startsWith('data:')).map(async (x, index) => {
if (ItemCache[x] === undefined) {
try {
const ItemDetails = (await (await fetch('https://api.polytoria.com/v1/store/' + x)).json())
ItemCache[x] = {
type: ItemDetails.type,
name: ItemDetails.name,
price: ItemDetails.price,
creator: {
name: ItemDetails.creator.name,
id: ItemDetails.creator.id
},
thumbnail: ItemDetails.thumbnail,
asset: undefined
}
if (ItemDetails.type === 'hat') {
ItemCache[x].accessoryType = ItemDetails.accessoryType
}
} catch(error) {
ItemCache[x] = {
type: "unknown",
name: "#" + x,
price: null,
creator: null,
thumbnail: "https://c0.ptacdn.com/static/images/broken.136e44ee.png",
asset: undefined,
ribbon: "unknown"
}
}
if (["mesh", "decal", "audio"].indexOf(ItemCache[x].type) !== -1) {
ItemCache[x].type = document.getElementById('load-asset-type').options[document.getElementById('load-asset-type').selectedIndex].value
ItemCache[x].ribbon = 'custom'
}
}
if (ItemCache[x].asset === undefined) {
const MeshURL = (await (await fetch('https://api.polytoria.com/v1/assets/serve-mesh/' + x)).json())
if (MeshURL.success) {
ItemCache[x].asset = MeshURL.url
if (ItemCache[x].type === 'hat') {
FormattedAvatar.items[index] = MeshURL.url
} else {
FormattedAvatar[ItemCache[x].type] = MeshURL.url
}
}
} else {
if (ItemCache[x].type === 'hat') {
FormattedAvatar.items[index] = ItemCache[x].asset
} else {
FormattedAvatar[ItemCache[x].type] = ItemCache[x].asset
}
}
})
const TexturePromise = [Avatar.shirt, Avatar.pants, Avatar.face, Avatar.torso].filter((x) => x !== undefined && !x.toString().startsWith('http') && !x.toString().startsWith('data:') && x !== undefined).map(async (x, index) => {
if (ItemCache[x] === undefined) {
try {
const ItemDetails = (await (await fetch('https://api.polytoria.com/v1/store/' + x)).json())
ItemCache[x] = {
type: ItemDetails.type,
name: ItemDetails.name,
price: ItemDetails.price,
creator: {
name: ItemDetails.creator.name,
id: ItemDetails.creator.id
},
thumbnail: ItemDetails.thumbnail,
asset: undefined
}
if (ItemDetails.price === 0) {
if (ItemDetails.sales === 0) {
ItemCache[x].price = null
} else {
ItemCache[x].price = 0
}
}
} catch(error) {
ItemCache[x] = {
type: "unknown",
name: "#" + x,
price: null,
creator: null,
thumbnail: "https://c0.ptacdn.com/static/images/broken.136e44ee.png",
asset: undefined,
ribbon: "unknown"
}
}
if (["mesh", "decal", "audio"].indexOf(ItemCache[x].type) !== -1) {
ItemCache[x].ribbon = 'custom'
}
}
if (ItemCache[x].asset === undefined) {
const TextureURL = (await (await fetch('https://api.polytoria.com/v1/assets/serve/' + x + '/Asset')).json())
if (TextureURL.success) {
ItemCache[x].asset = TextureURL.url
if (x === Avatar.shirt) {
FormattedAvatar.shirt = TextureURL.url
} else if (x === Avatar.pants) {
FormattedAvatar.pants = TextureURL.url
} else if (x === Avatar.face) {
FormattedAvatar.face = TextureURL.url
} else if (x === Avatar.torso) {
FormattedAvatar.torso = TextureURL.url
}
}
} else {
if (x === Avatar.shirt) {
FormattedAvatar.shirt = ItemCache[x].asset
} else if (x === Avatar.pants) {
FormattedAvatar.pants = ItemCache[x].asset
} else if (x === Avatar.face) {
FormattedAvatar.face = ItemCache[x].asset
} else if (x === Avatar.torso) {
FormattedAvatar.torso = ItemCache[x].asset
}
}
})
if (Avatar.face === undefined) {
FormattedAvatar.face = "https://c0.ptacdn.com/static/3dview/DefaultFace.png"
}
await Promise.all(AccessoryPromise)
await Promise.all(TexturePromise)
console.log('Real Avatar: ', Avatar)
console.log('Formatted: ', FormattedAvatar)
IFrame.addEventListener('load', function(){
IFrame.src = 'https://polytoria.com/ptstatic/itemview/#' + btoa(encodeURIComponent(JSON.stringify(FormattedAvatar)))
})
IFrame.src = 'about:blank'
UpdateBodyColors()
LoadWearing()
}
async function LoadUser(id) {
fetch('https://api.polytoria.com/v1/users/' + id + '/avatar')
.then((response) => {
if (!response.ok) {
throw new Error('Network not ok');
}
return response.json();
})
.then((data) => {
Avatar = {
useCharacter: true,
items: [],
headColor: '#' + data.colors.head || '#cdcdcd',
torsoColor: '#' + data.colors.torso || '#cdcdcd',
leftArmColor: '#' + data.colors.leftArm || '#cdcdcd',
rightArmColor: '#' + data.colors.rightArm || '#cdcdcd',
leftLegColor: '#' + data.colors.leftLeg || '#cdcdcd',
rightLegColor: '#' + data.colors.rightLeg || '#cdcdcd'
};
data.assets.forEach((item) => {
if (ItemCache[item.id] === undefined) {
ItemCache[item.id] = {
type: item.type,
name: item.name,
price: null,
creator: null,
thumbnail: item.thumbnail,
asset: item.path
}
if (item.type === 'hat' || item.type === 'tool' || item.type === 'torso') {
ItemCache[item.id].creator = {
id: 1,
name: "Polytoria"
}
}
}
if (item.type === 'hat') {
ItemCache[item.id].accessoryType = item.accessoryType
Avatar.items.push(item.id)
} else {
Avatar[item.type] = item.id
}
});
UpdateAvatar();
})
.catch((error) => {
console.log(error);
});
}
async function LoadItems() {
document.getElementById('inventory').innerHTML = ''
const SearchFilters = document.getElementsByClassName('retro-items-disable')
if (TabSelected != 'retro') {
for (let filter of SearchFilters) {
filter.disabled = false
filter.classList.remove('disabled')
}
} else {
for (let filter of SearchFilters) {
filter.disabled = true
filter.classList.add('disabled')
}
}
let Items;
if (['retro', 'outfit'].indexOf(TabSelected) === -1) {
Items = (await (await fetch('https://api.polytoria.com/v1/store?limit=12&order=' + Order + '&sort=' + Sort + '&showOffsale=' + ShowOffsale + '&types[]='+ TabSelected +'&search=' + Search + '&page=' + Page)).json())
} else if (TabSelected === 'outfit') {
const OutfitsClone = structuredClone(Outfits)
let Groups = []
while (OutfitsClone.length > 0) {
Groups.push(OutfitsClone.splice(0, 12));
}
console.log(Groups, OutfitsClone)
Items = {
assets: Groups[Page - 1],
pages: Groups.length
}
} else if (TabSelected === 'retro') {
if (RetroItems === null) {
Items = (await (await fetch('https://poly-upd-archival.pages.dev/data.json')).json())
Object.values(Items).forEach((item, index) => {
item.id = parseInt(Object.keys(Items)[index])
item.thumbnail = 'https://poly-archive.pages.dev/assets/thumbnails/' + item.id + '.png'
item.creator = {
id: 1,
name: "Polytoria"
}
if (item.asset === undefined) {
item.asset = 'https://poly-upd-archival.pages.dev/glb/' + item.id + '.glb'
}
item.id = item.id*-1
item.ribbon = 'retro'
ItemCache[item.id] = item
})
const PaginationItems = Object.values(Items)
let Groups = []
while (PaginationItems.length > 0) {
Groups.push(PaginationItems.splice(0, 12));
}
Items = {
assets: Groups[Page - 1],
pages: Groups.length
}
RetroItems = Groups
} else {
Items = {
assets: RetroItems[Page - 1],
pages: RetroItems.length
}
}
}
PageCount = Items.pages
if (Page < PageCount) {
document.getElementById('pagination-next').classList.remove('disabled');
document.getElementById('pagination-last').classList.remove('disabled');
} else {
document.getElementById('pagination-next').classList.add('disabled');
document.getElementById('pagination-last').classList.add('disabled');
}
if (Page > 1 && PageCount > 1) {
document.getElementById('pagination-prev').classList.remove('disabled');
document.getElementById('pagination-first').classList.remove('disabled');
} else {
document.getElementById('pagination-prev').classList.add('disabled');
document.getElementById('pagination-first').classList.add('disabled');
}
document.getElementById('pagination-current').innerText = Page
if (Items.assets === undefined) { Items.assets = [] }
if (Items.assets.length > 0) {
document.getElementById('inventory').classList.add('itemgrid')
if (TabSelected !== 'outfit') {
Items.assets.forEach(item => {
if (TabSelected !== "retro" && item.price === null) {
item.price = false
}
const Ribbon = ChooseRibbon(item, false)
const ItemColumn = document.createElement('div')
ItemColumn.classList = 'col-auto'
ItemColumn.innerHTML = `
<div style="max-width: 150px;">
<div class="card mb-2 avatar-item-container">
${ (Ribbon !== null) ? Ribbon : '' }
<div class="p-2">
<img src="${item.thumbnail}" class="img-fluid" style="border-radius: 10px;">
<button class="avatarAction btn btn-success btn-sm position-absolute rounded-circle text-center" style="top: -10px; right: -16px; width: 32px; height: 32px; z-index: 1;"><i class="fas fa-plus"></i></button>
</div>
</div>
<a href="${ (Math.abs(item.id) === item.id) ? '/store/' + item.id : 'https://poly-archive.vercel.app/archive/' + Math.abs(item.id) }" class="text-reset">
<h6 class="text-truncate mb-0">${item.name}</h6>
</a>
<small class="text-muted d-block text-truncate">
${FormatTypeDisplay(item)}
</small>
<small style="font-size: 0.8rem;" class="d-block text-truncate mb-2
${FormatPrice(item.price)}
</small>
</div>
`
document.getElementById('inventory').appendChild(ItemColumn)
if (ItemCache[item.id] === undefined && TabSelected !== "retro") {
ItemCache[item.id] = {
type: item.type,
name: item.name,
price: item.price,
creator: {
name: item.creator.name,
id: item.creator.id
},
thumbnail: item.thumbnail,
asset: undefined
}
if (item.type === 'hat') {
ItemCache[item.id].accessoryType = item.accessoryType
}
if (item.isLimited) {
ItemCache[item.id].ribbon = 'limited'
}
}
ItemColumn.getElementsByClassName('p-2')[0].addEventListener('click', function(){
WearAsset(item, item.id)
})
if (Ribbon !== null) {
ItemColumn.getElementsByClassName('ribbon')[0].addEventListener('click', function(){
WearAsset(item, item.id)
})
}
})
} else {
Items.assets.forEach((outfit, index) => {
const ItemColumn = document.createElement('div')
ItemColumn.classList = 'col-auto'
ItemColumn.innerHTML = `
<div style="max-width: 150px;">
<div class="card mb-2">
<div class="p-2 text-center">
<div class="mb-1">
<button style="border: 0; border-radius: 5px; cursor: default; background-color: ${outfit.data.headColor}; padding: 15px;"></button>
</div>
<div class="mb-1">
<button style="border: 0; border-radius: 5px; cursor: default; background-color: ${outfit.data.leftArmColor}; padding: 10px; padding-top: 20px; padding-bottom: 20px;"></button>
<button style="border: 0; border-radius: 5px; cursor: default; background-color: ${outfit.data.torsoColor}; padding: 20px;"></button>
<button style="border: 0; border-radius: 5px; cursor: default; background-color: ${outfit.data.rightArmColor}; padding: 20px; padding: 10px; padding-top: 20px; padding-bottom: 20px;"></button>
</div>
<button style="border: 0; border-radius: 5px; cursor: default; background-color: ${outfit.data.leftLegColor}; padding: 10px; padding-top: 20px; padding-bottom: 20px;"></button>
<button style="border: 0; border-radius: 5px; cursor: default; background-color: ${outfit.data.rightLegColor}; padding: 10px; padding-top: 20px; padding-bottom: 20px;"></button>
</div>
</div>
<h6 class="text-truncate mb-0 text-reset text-center mb-2">${outfit.name}</h6>
<div class="btn-group w-100">
<button class="btn btn-primary btn-sm p+outfit_wear_button">Wear</button>
<div class="btn-group">
<button type="button" class="btn btn-warning dropdown-toggle btn-sm" data-bs-toggle="dropdown" aria-expanded="false">
<i class="fa-duotone fa-wrench"></i>
</button>
<ul class="dropdown-menu">
<li>
<a class="dropdown-item text-primary p+outfit_rename_button" href="#">
<i class="fa-solid fa-signature"></i>
Rename
</a>
</li>
<li>
<span class="p+outfit_overwrite_button dropdown-item text-warning">
<i class="fa-solid fa-wand-magic-sparkles"></i>
<span>Overwrite</span>
</span>
</li>
<li>
<hr class="dropdown-divider">
</li>
<li>
<span class="p+outfit_delete_button dropdown-item text-danger">
<i class="fa-duotone fa-trash"></i>
<span>Delete</span>
</span>
</li>
</ul>
</div>
</div>
</div>
`
document.getElementById('inventory').appendChild(ItemColumn)
Utilities.InjectResource("registerTooltips")
ItemColumn.getElementsByClassName('p+outfit_wear_button')[0].addEventListener('click', async function(){
if (Avatar === outfit.data) {
return
}
console.log('Equipped Outfit: ', outfit)
Avatar = outfit.data
UpdateAvatar()
})
const OutfitRenameModal = document.getElementById('p+outfit_rename')
const OutfitRenameButton = document.getElementById('p+rename_outfit_confirm')
const OutfitRenameError = document.getElementById('p+outfit_rename_error')
ItemColumn.getElementsByClassName('p+outfit_rename_button')[0].addEventListener('click', function(){
OutfitRenameModal.showModal()
document.getElementById('p+outfit_rename_name').innerText = outfit.name
OutfitRenameButton.previousElementSibling.value = outfit.name
})
OutfitRenameButton.addEventListener('click', function(){
let OutfitName = OutfitRenameButton.previousElementSibling.value.trim()
OutfitRenameButton.previousElementSibling.value = ''
if (OutfitName === '') {
OutfitRenameError.classList = 'text-danger';
OutfitRenameError.innerHTML = '<i class="fa-duotone fa-circle-exclamation mr-1"></i> You cannot name an outfit nothing.';
return
} else if (OutfitName.length > 25) {
OutfitName = OutfitName.substring(0, 25)
} else if (Outfits.findIndex((x) => x.name.trim() === OutfitName) !== -1) {
OutfitRenameError.classList = 'text-danger';
OutfitRenameError.innerHTML = '<i class="fa-duotone fa-circle-exclamation mr-1"></i> You already have an outfit with the name "' + OutfitName + '".';
return
}
OutfitRenameModal.close()
Outfits[index].name = OutfitName
if (TabSelected === 'outfit') {
LoadItems()
}
chrome.storage.sync.set({'PolyPlus_AvatarSandboxOutfits': Outfits}, function(){
console.log('Saved outfits!')
})
})
let OverwritePending = false
const OutfitOverwriteButton = ItemColumn.getElementsByClassName('p+outfit_overwrite_button')[0]
OutfitOverwriteButton.addEventListener('click', function(e){
e.stopPropagation()
if (OverwritePending === false) {
OverwritePending = true
OutfitOverwriteButton.children[1].innerText = 'Are you sure?'
setTimeout(function (){
if (OverwritePending === true) {
OutfitOverwriteButton.children[1].innerText = 'Overwrite'
OverwritePending = false
}
}, 3000)
} else {
OverwritePending = false
console.log('Overwrite Outfit (outfit, avatar): ', outfit, Avatar)
Outfits[index].data = Avatar
if (TabSelected === 'outfit') {
LoadItems()
}
chrome.storage.sync.set({'PolyPlus_AvatarSandboxOutfits': Outfits}, function(){
console.log('Saved outfits!')
})
}
})
let DeletePending = false
const OutfitDeleteButton = ItemColumn.getElementsByClassName('p+outfit_delete_button')[0]
OutfitDeleteButton.addEventListener('click', function(e){
e.stopPropagation()
if (DeletePending === false) {
DeletePending = true
OutfitDeleteButton.children[1].innerText = 'Are you sure?'
setTimeout(function (){
if (DeletePending === true) {
OutfitDeleteButton.children[1].innerText = 'Delete'
DeletePending = false
}
}, 3000)
} else {
DeletePending = false
console.log('Deleted Outfit: ', outfit)
Outfits.splice(index, 1)
if (TabSelected === 'outfit') {
LoadItems()
}
chrome.storage.sync.set({'PolyPlus_AvatarSandboxOutfits': Outfits}, function(){
console.log('Saved outfits!')
})
}
})
})
}
} else {
document.getElementById('inventory').classList.remove('itemgrid')
document.getElementById('inventory').innerHTML = `
<div class="text-muted" style="padding: 37px 30px;">
<h1 class="display-3">
<i class="fas fa-box-open"></i>
</h1>
<h6 class="mb-0">
You do not have any items matching this type or search query. Find new items in the <a href="/store">store</a>!
</h6>
</div>
`
}
}
function LoadWearing() {
document.getElementById('wearing').innerHTML = '';
[...Avatar.items, Avatar.face, Avatar.shirt, Avatar.pants, Avatar.tool, Avatar.torso].filter((x) => x !== undefined).forEach(id => {
const Cached = Object.values(ItemCache)[Object.keys(ItemCache).indexOf(id.toString())]
if (Cached !== undefined) {
if (Cached.creator === undefined || Cached.creator === null) {
Cached.creator = {
id: 1,
name: "-"
}
}
if (Cached.price === undefined || Cached.price === null) { Cached.price = "???" }
const Ribbon = ChooseRibbon(Cached, true)
const ItemColumn = document.createElement('div')
ItemColumn.classList = 'col-auto'
ItemColumn.innerHTML = `
<div style="max-width: 150px;">
<div class="card mb-2 avatar-item-container">
${ (Ribbon !== null) ? Ribbon : '' }
<div class="p-2">
<img src="${Cached.thumbnail}" class="img-fluid" style="border-radius: 10px;">
<button class="avatarAction btn btn-danger btn-sm position-absolute rounded-circle text-center" style="top: -10px; right: -16px; width: 32px; height: 32px; z-index: 1;"><i class="fas fa-minus"></i></button>
</div>
</div>
<a href="${ (Math.abs(id) === id) ? '/store/' + id : 'https://poly-archive.vercel.app/archive/' + Math.abs(id) }" class="text-reset">
<h6 class="text-truncate mb-0">${Cached.name}</h6>
</a>
<small class="text-muted d-block text-truncate">
${FormatTypeDisplay(Cached)}
</small>
<small style="font-size: 0.8rem;" class="d-block text-truncate mb-2
${FormatPrice(Cached.price)}
</small>
</div>
`
document.getElementById('wearing').appendChild(ItemColumn)
ItemColumn.getElementsByClassName('p-2')[0].addEventListener('click', function(){
WearAsset(Cached, id)
})
if (Ribbon !== null) {
ItemColumn.getElementsByClassName('ribbon')[0].addEventListener('click', function(){
WearAsset(Cached, id)
})
}
}
})
}
function WearAsset(details, id) {
if (Avatar[details.type] !== id && Avatar.items.indexOf(id) === -1) {
// Equip
if (details.type === 'hat') {
Avatar.items.push(id)
} else {
Avatar[details.type] = id
}
} else {
// Unequip
if (details.type === 'hat') {
Avatar.items.splice(Avatar.items.indexOf(parseInt(id)), 1);
} else {
Avatar[details.type] = undefined
}
}
UpdateAvatar()
LoadWearing()
}
function UpdateBodyColors() {
const BodyColors = {
head: Avatar.headColor,
torso: Avatar.torsoColor,
leftArm: Avatar.leftArmColor,
rightArm: Avatar.rightArmColor,
leftLeg: Avatar.leftLegColor,
rightLeg: Avatar.rightLegColor
}
Object.keys(BodyColors).forEach((elementID, i) => {
document.getElementById(elementID).style.backgroundColor = Object.values(BodyColors)[i]
})
}
function CleanAccessoryType(type) {
const CleanAccessoryTypes = {
hat: "Hat",
backAccessory: "Back Accessory",
faceAccessory: "Face Accessory",
headAttachment: "Head Attachment",
hair: "Hair",
neckAccessory: "Neck Accessory",
headCover: "Head Cover",
headAccessory: "Head Accessory",
frontAccessory: "Front Accessory"
}
return Object.values(CleanAccessoryTypes)[Object.keys(CleanAccessoryTypes).indexOf(type)] || type
}
function FormatPrice(price) {
if (price === 0) {
return 'text-primary fw-bold">Free</small>'
} else if (price === false) {
return 'text-muted fw-bold">Offsale</small>'
} else if (price === null) {
return 'text-muted">???</small>'
} else if (price !== "???") {
return 'text-success"><i class="pi mr-1">$</i> ' + price + '</small>'
} else {
return 'text-muted">???</small>'
}
return '">how did this happen</small>'
}
function ChooseRibbon(item, wearing) {
const NewDateAgo = new Date();
NewDateAgo.setDate(NewDateAgo.getDate() - 3);
if (item.ribbon === 'custom') {
return '<div class="ribbon ribbon-polyplus-custom ribbon-top-right"><span>Custom</span></div>';
} else if (item.ribbon === 'unknown') {
return '<div class="ribbon ribbon-polyplus-unknown ribbon-top-right"><span><i>?</i></span></div>';
} else if (item.ribbon === 'retro' && wearing === true) {
return '<div class="ribbon ribbon-polyplus-retro ribbon-top-right"><span>Retro</span></div>';
} else if (item.isLimited) {
return '<div class="ribbon ribbon-limited ribbon-top-right"><span><i class="fas fa-star"></i></span></div>';
} else if (new Date(item.createdAt) > NewDateAgo) {
return '<div class="ribbon ribbon-new ribbon-top-right"><span>New</span></div>';
} else {
return null
}
}
function FormatTypeDisplay(item) {
if (["hat", "tool", "face", "torso"].indexOf(item.type) !== -1) {
if (item.type === "hat") {
return CleanAccessoryType(item.accessoryType)
} else if (item.type === "torso") {
return "Body Part"
} else {
return item.type.substring(0, 1).toUpperCase() + item.type.substring(1)
}
} else {
return 'by <a class="text-muted" href="/u/' + item.creator.name + '">' + item.creator.name + '</a>'
}
}
chrome.storage.onChanged.addListener(function (changes, namespace) {
if ('PolyPlus_AvatarSandboxOutfits' in changes) {
chrome.storage.sync.get(['PolyPlus_AvatarSandboxOutfits'], function (result) {
Outfits = result.PolyPlus_AvatarSandboxOutfits || [];
});
}
})