Compare commits

...

199 commits

Author SHA1 Message Date
a01c1be3da minor: update manifest 2024-10-29 17:22:17 -05:00
6349957fa3 minor: change to use polytoria.trade URL 2024-10-29 17:20:24 -05:00
0e3be312a8 minor: change LOVE valuation to use website API
temp disabled
2024-10-29 17:19:57 -05:00
5fc1c37383 minor: add cache to pinned games 2024-10-18 13:19:31 -05:00
bcce85e63d style: add caches to data_structure.txt 2024-10-04 22:00:19 -05:00
172d666575 minor: move "Avatar Sandbox" out of "Experimental Settings" 2024-10-01 18:47:38 -05:00
bb4d5c06e5 minor: move "Time Played" to "Experimental Settings" 2024-10-01 18:45:16 -05:00
7ae424b60b minor: disable search filters for retro items avatar sandbox 2024-10-01 18:44:31 -05:00
591dbe1f01 minor: remove update notifier code
the alarm was already disabled
2024-10-01 18:28:38 -05:00
001f92db85 minor: updated trade valuation tooltip + added shorthand 2024-09-29 18:58:10 -05:00
12e3a38e8d feat: "Trade Valuation" feature (thanks to LOVE) 2024-09-29 14:57:57 -05:00
849e2dde84 minor: quick shortcut to serve-mesh API 2024-09-29 13:55:00 -05:00
70808ced7c feat: asset designer credit setting toggle 2024-09-15 13:43:11 -05:00
4a65a22f17 style: add asset designer cache to debug page 2024-09-15 11:20:19 -05:00
9b20404929 minor: cache asset designer data
and rename "Item Modeler" to "Asset Designer"
2024-09-15 11:20:03 -05:00
d0799a127f feat: item modeler info on known items 2024-09-14 13:39:30 -05:00
11ac584246 minor: cache friend count 2024-09-13 21:00:03 -05:00
c8da41a558 style: reorganize great divide user stats caching
and add it to the debug page
2024-08-25 14:33:25 -05:00
5743cf2422 minor: "Restricted" server join button if not allowed 2024-08-24 20:02:43 -05:00
afe9eaaf6f feat: add "Archived Settings" category to settings 2024-08-19 18:25:01 -05:00
4013251a8e fix: forum mentions feature breaking emojis 2024-08-19 18:23:41 -05:00
fd55054ac6 feat: new "see player id and join date when blocked" feature 2024-08-19 18:23:23 -05:00
29539ac59e minor: add items cached count to debug page inventory cache 2024-08-19 18:21:16 -05:00
8ef22bf1cc minor: remove question mark icon next to place revenue 2024-08-17 18:47:00 -05:00
1776bc8f44 minor: search blocked users on search, not button click 2024-08-08 18:52:06 -05:00
b112777a86 minor: add spacing between blocked users 2024-08-08 18:51:47 -05:00
3dbdec5521 fix: improved query selector for blocked users search 2024-08-08 18:51:36 -05:00
083d4cc80a fix: passing decal name in formData with multi-upload 2024-08-06 22:23:12 -05:00
eaf161e9f0 minor: update how "Time Played:" looks 2024-08-06 17:17:41 -05:00
ae060b6e93 fix: fix time played not checking if place is whitelisted
+ add button listener for joining individual servers (was always going to add I just eventually forgot after awhile)
2024-08-05 14:48:32 -05:00
f6102eafcc feat: quick play audio + bring back the great divide features 2024-08-04 19:27:45 -05:00
f94f43b626 feat: (joke feature) use anniversary message templates
+ work in progress AdCache for upcoming "Ignore Specific Ads" feature
2024-08-01 15:16:33 -05:00
aa840db336 feat: re-try if ratelimited for multi-whitelist feature 2024-07-25 23:38:00 -05:00
80be8b407d style: update data_structure.txt (i forgot how to spell) 2024-07-25 23:36:11 -05:00
3419e646cd minor: delete (unused) places.js 2024-07-25 23:34:32 -05:00
b95cf5d9df style: fetch retro items in format avatar - avatar sandbox 2024-07-25 23:32:51 -05:00
fbb69d63ff fix: loading outfits with retro items avatar sandbox 2024-07-25 12:52:58 -05:00
2795cb7489 fix: update forum mentions to use new profile URLs 2024-07-25 10:16:26 -05:00
ad3fe90caf feat: (part 2) no more great divide features 🥺🥺🥺🥺 2024-07-23 19:56:13 -05:00
67b092700a fix: used wrong variable lol avatar sandbox 2024-07-23 17:47:13 -05:00
d6d9bed142 fix: change link to poly-archive if item is retro avatar sandbox 2024-07-23 14:42:35 -05:00
a650381218 feat: percentage after like rating on create page 2024-07-22 20:15:50 -05:00
96d78860f8 minor: avatar sandbox trofie shortcut 2024-07-22 20:14:24 -05:00
9350038f5c fix: ??? price for avatar sandbox retro items 2024-07-22 15:40:30 -05:00
416519dbec feat: avatar sandbox retro item ribbon 2024-07-22 15:31:42 -05:00
57e9c7a289 fix: avatar sandbox retro item tools 2024-07-22 11:18:08 -05:00
7825c45431 minor: add border radius to avatar sandbox discovery images 2024-07-22 08:51:33 -05:00
2950561606 fix: click event listener with avatar sandbox ribbons 2024-07-22 08:48:01 -05:00
d01fed21fe feat: avatar sandbox remove accessory type tags 2024-07-22 08:45:58 -05:00
492a895e13 feat: avatar sandbox type parity with avatar redesign 2024-07-22 08:44:01 -05:00
9857fc24e0 feat: avatar sandbox item ribbons 2024-07-20 17:35:14 -05:00
6e1ca47be6 feat: avatar sandbox name/accessory type parity 2024-07-20 13:14:50 -05:00
48975f06c5 feat: off-sale price items in avatar sandbox
thanks Alyx for fixing the store API
2024-07-20 13:10:34 -05:00
66451aa9cc style: update data_structure.txt 2024-07-20 00:46:50 -05:00
9eeb7761e4 feat: add proper torso loading to avatar sandbox 2024-07-19 21:35:10 -05:00
484c396233 feat: add friendly name for "frontAccessory" items avatar sandbox 2024-07-19 21:31:53 -05:00
908637e72e feat: "Multi-Whitelist" and "Clear Whitelist" features 2024-07-19 21:25:48 -05:00
c6a3ce2c69 feat: improved purchase button detection
for the hundredth time
2024-07-17 17:10:46 -05:00
9c8a08cf78 feat: no more great divide features 🥺🥺🥺🥺 2024-07-16 22:58:38 -05:00
254ee41c11 fix: i forgot to add default setting for join friend button 2024-07-16 22:51:54 -05:00
2975795971 feat: add event items json generator to dev page 2024-07-16 22:48:50 -05:00
f0ac985c13 feat: "Homepage Join Friends Tooltip Button" feature 2024-07-16 22:26:11 -05:00
ffcb35d15f fix: "re-add copyable places" setting toggle 2024-07-15 08:54:12 -05:00
0eca6b62c7 feat: "pinned games" rewrite 2024-07-15 08:53:47 -05:00
90dab88d0e feat: add "time played" feature to debug page 2024-07-14 16:36:34 -05:00
1ea8dae60c feat: add "Copy Thread ID" context menu item 2024-07-14 16:36:18 -05:00
8cd989af3d fix: fix store owned tags on event items 2024-07-14 16:35:59 -05:00
9f49fc848d feat: new "time played" feature (a bit buggy right now) 2024-07-14 16:35:29 -05:00
80d223e4d1 fix: copyable places feature always enabled 2024-07-10 17:36:33 -05:00
2e4ade1c97 minor: fill in rename outfit input avatar sandbox 2024-07-10 17:35:12 -05:00
077e49db36 fix: change error icon for "timed-item owner check" 2024-07-10 17:34:55 -05:00
83e2f19fa2 fix: add outfits avatar sandbox category icon 2024-07-10 07:43:06 -05:00
662f4733af minor: add avatar sandbox poly-archive credits 2024-07-10 07:41:46 -05:00
5eeef015e4 feat: added warning to retro items avatar sandbox category 2024-07-10 07:41:23 -05:00
0774558d7d minor: add avatar sandbox outfits to debug page 2024-07-10 07:40:57 -05:00
35fe13d6a2 minor: update registerTooltips.js to register dropdowns 2024-07-10 07:40:37 -05:00
792375158d feat: add avatar sandbox oufits feature! 2024-07-10 07:40:26 -05:00
4a3ad2eb5f fix: reset avatar before loading user avatar sandbox 2024-07-09 07:35:09 -05:00
f5f324b323 chore: remove avatar sandbox console log 2024-07-08 19:44:03 -05:00
d0a2223e76 feat: "Hoarded Items" card on collectibles inv tab 2024-07-08 19:13:10 -05:00
d0839859b5 feat: serial tags on collectibles inventory tab 2024-07-08 18:41:17 -05:00
7ba43cc07b minor: up collectibles inv tab page limit from 3 to 12 2024-07-08 18:33:00 -05:00
c7df8cbea3 fix: fix unequipping items in avatar sandbox
hopefully fixed for the last time lol keeps breaking, but i think it's finally fixed
2024-07-08 16:05:15 -05:00
7a745a6232 feat: make retro item ids negative avatar sandbox 2024-07-08 16:04:37 -05:00
254957bdc7 chore: remove sitewide.js console logs 2024-07-08 10:09:10 -05:00
a20b22d148 feat: add "Retro" items category to avatar sandbox
+ improved price formatting as a function instead of 1-line ternary
2024-07-08 10:08:53 -05:00
b631cb59e9 style: change avatar sandbox element id 2024-07-07 08:08:57 -05:00
74488c4d2b fix: fix wearing assets other than hats by id avatar sandbox 2024-07-07 08:06:28 -05:00
14d9faa484 minor: improved "no participation" great divide stats card 2024-07-07 08:04:23 -05:00
d86f928b6f fix: fix loading privated assets in avatar sandbox 2024-07-06 06:28:20 -05:00
ec9c7f8671 fix: LoadItems() overwriting item cache avatar sandbox 2024-07-06 06:27:57 -05:00
3e5ae5b782 minor: add "New in v1.3!" setting tag to copyable places feature 2024-07-06 06:03:45 -05:00
8a82f9cc95 minor: setting name text-shadow 2024-07-06 06:01:38 -05:00
406b3ff479 fix: github desktop selecting issue 2024-07-06 01:06:38 -05:00
d743aaf820 chore: remove avatar sandbox console logs 2024-07-05 05:02:39 -05:00
f6eba67d6e minor: add border radius to item image avatar sandbox 2024-07-05 04:57:14 -05:00
bc6840c3d7 minor: show accessory type instead of creator avatar sandbox
* if the creator is unknown or Polytoria
2024-07-05 04:57:01 -05:00
ba19dd2691 fix: include face in wearing avatar sandbox 2024-07-05 04:56:21 -05:00
7d0ef89748 fix: include tool in wearing avatar sandbox 2024-07-05 03:04:50 -05:00
923ce838bd minor: rename "Torso" to "Body Part" in avatar sandbox frontend 2024-07-05 02:55:10 -05:00
577b71ae7c minor: "advanced" avatar sandbox card section 2024-07-05 02:48:31 -05:00
5be5e22f40 minor: remove tooltip on debug cache avatar sandbox 2024-07-05 02:41:59 -05:00
d1f329a0e1 minor: remove open in new tab button avatar sandbox 2024-07-05 02:41:08 -05:00
84f06e3b80 chore: remove try-on item console log 2024-07-05 02:33:28 -05:00
177da6b8af fix: 2 tools showing when trying on item 2024-07-05 02:31:53 -05:00
cde27b8006 fix: fix avatar dimension toggle feature 2024-07-04 05:34:01 -05:00
870def2dc1 feat: redesigned avatar sandbox to look like avatar page 2024-07-04 05:28:02 -05:00
e8ee269945 style: remove unused data-color attributes avatar sandbox 2024-07-04 04:47:39 -05:00
2d6e5bd947 feat: avatar sandbox change body part colors feature 2024-07-04 04:45:45 -05:00
ca5eb0829d chore: delete old avatar sandbox files 2024-07-03 04:34:37 -05:00
0689cc3a9a minor: add torsos tab to avatar sandbox 2024-07-03 04:33:02 -05:00
fdbabf43b0 minor: add torso option to wear asset type dropdown 2024-07-03 04:09:15 -05:00
db00c107b8 debug: add button to log item cache for avatar sandbox 2024-07-03 04:08:29 -05:00
6d290f5648 minor: reset search after switching tab in avatar sandbox 2024-07-03 00:57:43 -05:00
099b1c2b21 fix: fix creator name and uIDs always being Polytoria in avatar sandbox 2024-07-03 00:55:23 -05:00
aed860159b fix: fix un-equipping in avatar sandbox 2024-07-03 00:50:07 -05:00
e6cffdd8e2 style: make "Free" avatar sandbox text bold like store 2024-07-03 00:49:38 -05:00
8e8cf7981a fix: fix errors wearing toolbox assets in avatar sandbox 2024-07-03 00:49:11 -05:00
3b0f079a2f fix: fix avatar sandbox load user overwriting cache 2024-07-02 14:29:19 -05:00
431ef7ee73 feat: add torso support to avatar sandbox 2024-07-02 14:28:22 -05:00
85b4683a66 feat: add redirects for old Polytoria site paths 2024-07-02 13:35:04 -05:00
3206c6bbcd feat: add "Re-add Copyable Places" feature 2024-07-02 13:34:29 -05:00
26c63d3bf6 feat: store "owned tags" in event items category 2024-07-02 03:14:32 -05:00
eb644e8f1e minor: reorder debug page 2024-07-01 21:48:23 -05:00
8228697bf9 style: update css on debug page for collapse elements 2024-07-01 20:10:25 -05:00
4fce8c95cc minor: redesign debug page to use bootstrap collapse elements 2024-07-01 20:07:05 -05:00
5294df3e1d fix: update github repo links on dev and debug pages 2024-07-01 19:49:33 -05:00
db99562391 minor: add "sync" and "local" card tags to debug page 2024-07-01 19:48:40 -05:00
339c81e7a5 fix: fix debug page not showing storage in bytes (missing functionality) 2024-07-01 19:48:20 -05:00
183c49891d minor: make dev page incorporate local storage for byte size 2024-07-01 19:46:49 -05:00
5167016103 style: add Inventory Cache to debug page 2024-07-01 19:42:26 -05:00
7e8e8d4b84 style: remove best friends from data_structure.txt 2024-07-01 19:34:59 -05:00
bebd685bcd fix: fix label text not changing when changing place activity 2024-07-01 05:10:46 -05:00
f724cd9f3c fix: fix text change when toggling place activity 2024-07-01 05:09:13 -05:00
c636ee9576 fix: fix loading when readyState is "interactive" 2024-07-01 04:07:28 -05:00
8c249ad624 fix: update sitewide.js loading order using PageLoad() 2024-07-01 04:06:46 -05:00
14250d1add feat: add "Planet X, Andromeda" great divide user stats 2024-06-30 19:49:00 -05:00
f65d95bca2 feat: add back popup, and quick links to popup
credits to svgrepo.com for the chrome and firefox icons
2024-06-30 03:59:05 -05:00
05b2e54847 chore: remove commented out height css for pop-up 2024-06-30 03:42:22 -05:00
a222585718 minor: "New in v1.3!" setting marker 2024-06-30 03:41:12 -05:00
7c586e0984 minor: ability to toggle QOL achievement features
previously, there wasn't an option to disable those small, quality-of-life achievement features
2024-06-30 03:40:40 -05:00
e439cc9702 chore: remove setting options console log 2024-06-30 03:39:32 -05:00
7f8cb33f01 fix: only the first setting checkbox registering 2024-06-30 03:38:43 -05:00
cfc25fd09b feat: "List of Values Everlast" Integration feature 2024-06-29 19:08:34 -05:00
d3a038bc42 style: capitalize "loading" lottie-player labels
@StarManTheGamer begged me
2024-06-29 17:46:40 -05:00
5c3ef39e37 chore: remove un-used function parameter 2024-06-29 05:52:37 -05:00
2cc4894d43 minor: float great divide user stats icons to sides 2024-06-29 05:51:09 -05:00
2249044de7 feat: add "Planet X, Andromeda" to great divide place allowlist 2024-06-29 05:48:43 -05:00
7207f229d2 chore: remove great divide console logs 2024-06-29 05:34:53 -05:00
2cce628807 chore: remove forgotten, old hoarders list console log 2024-06-29 00:19:54 -05:00
bcc2d4db6f chore: remove old hoarders list console log 2024-06-29 00:18:01 -05:00
661fcd8ee6 style: update data_structure.txt 2024-06-29 00:14:43 -05:00
ac4ca43be6 fix: avatar dimension toggle working with all badges 2024-06-29 00:12:21 -05:00
fe3901a5f5 minor: redesign great divide user statistics card 2024-06-28 23:57:29 -05:00
f11c21c2be minor: fix "hasn't participated in the great divide" text 2024-06-28 18:56:50 -05:00
7bc077a420 fix: fix context invalidation when using reload keybind 2024-06-28 12:42:16 -05:00
439978a5b5 chore: remove console logs 2024-06-28 11:52:51 -05:00
0e5a3d75e3 fix: store owned tag inventory caching 2024-06-28 11:52:35 -05:00
69b2a1599b minor: add support for multiple links in event items store category 2024-06-28 11:48:59 -05:00
ff3e9595f5 feat: fix event logo height on home event container 2024-06-28 11:40:32 -05:00
3158d4ef91 fix: great divide user stats cache length 2024-06-28 10:50:38 -05:00
0df034c46c fix: great divide user stats KDR tooltip randomly not registering 2024-06-28 10:49:51 -05:00
2afea6ffb6 minor: change great divide user stats KDR text color 2024-06-28 10:49:07 -05:00
57e4130ede minor: KDR great divide user stats tooltip 2024-06-28 10:41:09 -05:00
0f7aea03b2 chore: remove old great divide user stats function 2024-06-28 10:34:24 -05:00
f9e3e5b09f chore: remove commented out code 2024-06-28 10:33:53 -05:00
1fd36f5a6d feat: great divide user statistics section instead of tab 2024-06-28 10:33:24 -05:00
27a8986272 fix: use userID instead of username for great divide stats request 2024-06-28 01:31:21 -05:00
a71b0f26be chore: remove old, commented out background css 2024-06-28 01:23:02 -05:00
68f635495f compatibility: use mask instead of -webkit-mask 2024-06-28 01:22:10 -05:00
f434803f46 minor: setting notes to reduce description length 2024-06-28 01:21:14 -05:00
f7fd5bde11 style: add credit to goldenretriveryt for multi-decal uploading 2024-06-28 01:13:04 -05:00
97866f933c chore: remove console logs 2024-06-28 01:12:09 -05:00
05a3031a83 fix: multi-decal uploading submit button query 2024-06-28 01:11:15 -05:00
02fbb5c8bd fix: "options" buttons in settings 2024-06-28 01:03:06 -05:00
c821f9bc33 minor: re-order experimental settings 2024-06-28 01:01:42 -05:00
7f35484287 minor: change setting container structure 2024-06-28 01:00:43 -05:00
50b45b6614 chore: add spacing between avatar sandbox rows 2024-06-27 22:22:21 -05:00
350e3ab92f feat: avatar sandbox pagination 2024-06-27 22:19:37 -05:00
a57098b608 feat: avatar sandbox search, sorting, & ordering 2024-06-27 18:52:25 -05:00
6d09433395 feat: add price to avatar sandbox discovery 2024-06-27 18:34:45 -05:00
1e5e9fe69f chore: remove script tag in avatar sandbox 2024-06-27 18:34:11 -05:00
26dddf0019 chore: remove rewrite avatar sandbox banner 2024-06-27 18:33:54 -05:00
438717ec46 chore: use avatar sandbox rewrite in manifest 2024-06-27 18:33:26 -05:00
24cfd40504 chore: remove console log 2024-06-27 18:17:34 -05:00
ee6893f906 fix: place activity toggle button 2024-06-27 18:09:22 -05:00
ce3ebc99bb fix collectibles inventory tab page fetching 2024-06-27 16:20:17 -05:00
37ad14a9be update manifest and irl price setting description 2024-06-26 12:13:49 -05:00
373ef1b156 epic new item wishlist button 2024-06-26 12:03:47 -05:00
deeaba05b2 irl price on resellers and no more pagination on item wishlist and collectibles 2024-06-26 11:48:27 -05:00
9457437ba6 Update manifest.json 2024-06-25 09:16:21 -05:00
f3e33ea840 v1.2.2
- Avatar Sandbox rewrite

- Updated owned item tags code

- Inventory Collectibles Category

- Great Divide Features ("Potentially Unbalanced" indicator in server list and User Statistics tab on profiles)

- Refreshed Settings Page design

- Fixed library item type detection

- Breadcrumbs on the view page for models, audios, decals, and meshes now link to the Library instead of the Store.

- If the document loads prior to the DOMContentLoaded event being registered in the sitewide.js file, then it'll still run everything as normal instead of just not running

- Updated README.md

I was bored so:
- You can now make a profile act like a birthday profile by appending ?birthday=true to the URL
2024-06-25 09:14:52 -05:00
c0dbbdeb0d
Merge pull request #9 from okdargy/main
fix settings being undefined on change load
2024-06-18 00:11:57 -05:00
b3cfe05657 new great divide feature and avatar dimension toggle feature
- new great divide event feature

- event place cards now have a animated gradient instead of a still gradient

- you can now extract textures from an official polytoria item easily in the extension popup (temporarily disabled)

- new avatar dimension toggle feature

- fixed library-download.js errors due to it trying to run on official polytoria item pages
2024-06-18 00:07:21 -05:00
dargy
4b15275648 chore: remove console.log 2024-06-17 23:14:54 -05:00
dargy
456bf389f0 fix: theme settings being applied before settings
fixes `TypeError: Settings is undefined` on firefox
2024-06-17 21:58:02 -05:00
35bc026724
Merge pull request #8 from GoldenretriverYT/main
Add multiple decals upload and reload "hotkey"
2024-06-08 21:22:38 -05:00
6a42004dba disable best friends feature & improvements
- disabled best friends feature

- improved load asset by ID in avatar sandbox (originally was quickly added cause I was bored, now it actually works with different asset types and stuff)

- add work in progress "automatic ad bidding" feature modal

- fixed apply membership themes with nested setting objects

- fixed try-on items feature defaulting to giving the preview avatar shirt and pants even if the user's avatar didn't have them

- fixed "download place file" because site no longer uses csrf tokens
2024-06-08 21:21:33 -05:00
GoldenretriverYT
4bc5cf2260 Add multiple decals upload and reload "hotkey" 2024-06-09 01:53:55 +02:00
43 changed files with 5663 additions and 2461 deletions

1
.gitignore vendored
View file

@ -1 +1,2 @@
.DS_Store .DS_Store
manifest.json

View file

@ -4,43 +4,60 @@
> [!IMPORTANT] > [!IMPORTANT]
> This extension is currently in BETA. If you find any bugs, please report them in this repository's issues section as it'd be a great help towards the development of the extension. > This extension is currently in BETA. If you find any bugs, please report them in this repository's issues section as it'd be a great help towards the development of the extension.
> [!TIP]
> Would you like to learn what's in store for Poly+? Check out the roadmap [here](https://github.com/users/indexxing/projects/2)!
# Poly+ # Poly+
Poly+ is an upcoming quality-of-life browser extension for the Polytoria website! The extension provides tons of improvements to improve your experience on Polytoria! Visit the website [here](https://polyplus.vercel.app/)! Poly+ is a quality-of-life browser extension for the Polytoria website! The extension provides tons of improvements to improve your experience on Polytoria! Visit the website [here](https://polyplus.vercel.app/)!
![image](https://img.shields.io/badge/Google_chrome-4285F4?style=for-the-badge&logo=Google-chrome&logoColor=white) ![image](https://img.shields.io/badge/Google_chrome-4285F4?style=for-the-badge&logo=Google-chrome&logoColor=white)
![image](https://img.shields.io/badge/Microsoft_Edge-0078D7?style=for-the-badge&logo=Microsoft-edge&logoColor=white) ![image](https://img.shields.io/badge/Microsoft_Edge-0078D7?style=for-the-badge&logo=Microsoft-edge&logoColor=white)
![image](https://img.shields.io/badge/Opera-FF1B2D?style=for-the-badge&logo=Opera&logoColor=white) ![image](https://img.shields.io/badge/Opera-FF1B2D?style=for-the-badge&logo=Opera&logoColor=white)
![image](https://img.shields.io/badge/Brave-FF1B2D?style=for-the-badge&logo=Brave&logoColor=white) ![image](https://img.shields.io/badge/Brave-FF1B2D?style=for-the-badge&logo=Brave&logoColor=white)
![image](https://img.shields.io/badge/Vivaldi-EF3939?style=for-the-badge&logo=Vivaldi&logoColor=white) ![image](https://img.shields.io/badge/Vivaldi-EF3939?style=for-the-badge&logo=Vivaldi&logoColor=white)
![image](https://img.shields.io/badge/Firefox-000?style=for-the-badge&logo=Firefox&logoColor=white)
# Features # Features
- Pinned Games - 📍 Pinned Games
- Forum Mentions - 📣 Forum Mentions
- 🎨 Theme Creator
- 🆕 Store "owned" Tags
- 🧾 Item Wishlist
- Best Friends - Best Friends
- Improved Friend Lists - Improved Friend Lists
- IRL Price with Brick Count - IRL Price with Brick Count
- Hide Sidebar Notification Badges - Hide Sidebar Notification Badges
- Theme Creator
- Store `owned` Tags
- More Search Filters - More Search Filters
- Modify Navbar - Modify Navbar
- Free Membership Themes - Free Membership Themes
- Multi-Cancel Outbound Trades - Multi-Cancel Outbound Trades
- Item Wishlist - Quick Place Downloads
- Outfit Cost - Try-On Items
- Show Outfit Cost on Profiles
- Show Approximate Place Revenue
- Show "Owners" instead of "Sales"
- Collectibles' Hoarders List
- Quick Library Downloads
- "Event Items" Store Category
- Show Friend Count on Homepage
- Timed-Item Owner Check
- Hide User Ads
- Multi-Decal Uploading
- Avatar Dimension Toggle
And there is more to come! And there is more to come!
### Experimental Settings
> Experimental settings may not be finished and may have bugs!
- Game Profiles
- Inline Editing
- Forum Unix Timestamps
- Avatar Sandbox
# Supported Browsers # Supported Browsers
As of right now, Poly+ only works on Chromium-based browsers. Currently, there are no plans to bring it to other browsers. Poly+ is supported on Chromium-based browsers and Firefox!
> TL;DR Chromium-based browsers
## Examples of Browsers that are Chromium-based ## Examples of Browsers that are Chromium-based
@ -55,13 +72,13 @@ As of right now, Poly+ only works on Chromium-based browsers. Currently, there a
## How to Download - Stable Build? ## How to Download - Stable Build?
1. Go to the releases section of this GitHub repository 1. Go to the extensions (or add-ons) webstore for your respective browser:
2. Go to the latest release and download the .ZIP file for that release - Chromium-based browsers: [Chrome Webstore](https://chromewebstore.google.com/detail/poly+/feafepokhecfmimpepbpccmcnjbcbklg)
3. Extract the .ZIP - Firefox: [Firefox Add-ons Store](https://addons.mozilla.org/en-US/firefox/addon/polytoriaplus/)
4. Enable developer mode on your browser (developer mode allows you to load unpacked extensions)
5. Click the "Load Unpacked" button 2. Click on the Poly+ icon in your browser's tooltip (if it's pinned, if not you may have to go to a sub-menu in your browser's toolbar and find the extension there) to go to the settings page, and customize your experience. Remember to click save at the bottom of the page when you are done!
6. When the file selector comes up, open the unzipped folder from this GitHub repository
7. Go to Polytoria and you should see some changes! 3. Go to Polytoria's website and enjoy the many features and QOL improvements that come with Poly+!
## How to Download - Pre-Release Build? ## How to Download - Pre-Release Build?

221
css/settings.css Normal file
View file

@ -0,0 +1,221 @@
html,
body,
#page {
background: linear-gradient(rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7)), url(/images/client-loading.png);
background-color: #222222;
background-size: 400px;
background-position: center;
}
#page {
margin-top: 7.5rem;
width: 65%;
margin-right: auto;
margin-left: auto;
margin-bottom: 3.5rem;
}
h1 {
font-size: 4.6rem;
/*color: rgb(48, 48, 48);*/
}
h1 span.indent {
border-left: 10px solid rgb(48, 48, 48);
margin-right: 15px;
}
h1 span.highlight {
color: red;
}
h2 {
color: rgb(48, 48, 48);
}
h2 span.indent {
border-left: 7.5px solid rgb(48, 48, 48);
margin-right: 15px;
}
.setting-container:not(:last-child) {
margin-bottom: 30px;
}
.setting-container .title {
font-size: 1.4rem;
font-weight: bold;
}
.setting-container .desc, .setting-container .warning, .setting-container .note {
display: block;
width: 75%;
}
.setting-container .desc {
color: rgb(120, 120, 120);
}
.setting-buttons {
float: right;
}
.setting-buttons button {
width: 100px;
}
.setting-buttons button:first-child:not(:last-child) {
margin-right: 5px;
}
.setting-container select:first-of-type {
margin-top: 10px;
}
.goback {
color: rgb(120, 120, 120);
text-decoration: none;
}
dialog {
background-color: #080808;
color: #c4c4c4;
border: 1px solid #3bafff;
border-radius: 10px;
}
.input-group-text {
background-color: #000;
border-color: #000;
color: #fff;
}
label {
font-size: 0.8rem;
margin-bottom: 2.75px;
}
dialog::backdrop {
background-color: rgba(0, 0, 0, 0.73);
}
dialog .modal-header p {
margin-bottom: 0px;
color: #fff;
}
dialog .modal-body p:first-child {
font-size: 0.9rem;
}
.setting-container .indicator {
border-radius: 5rem;
display: inline-block;
cursor: default;
margin-right: 4px;
height: 20px;
vertical-align: text-top;
}
.setting-container.enabled .indicator { background-color: #007bff;}
.setting-container.enabled .title { text-shadow: 0px 1px 3px blue; }
.setting-container.disabled .indicator { background-color: orangered; }
.setting-container.disabled .title { text-shadow: 0px 1px 3px orangered; }
.limited-time {
background: transparent;
border-color: transparent;
/*
changed the box shadow to black cause @dargy begged me to
box-shadow: -3px -3px 15px 0 rgba(255, 116, 16, 0.25) inset, 3px 3px 15px 0 rgba(205, 96, 255, 0.25) inset;
*/
box-shadow: -3px -3px 15px 0 black inset;
border: none;
}
.limited-time * {
position: relative;
z-index: 2;
}
.limited-time .row {
--bs-gutter-x: 10px;
}
.limited-time .indicator {
height: 100%;
}
.limited-time .desc {
margin-bottom: 10px;
width: 85%;
}
.limited-time-tag {
font-size: 0.7rem;
color: rgba(255, 116, 16, 0.25);
-webkit-animation: LimitedTimeTag 5s ease infinite alternate;
-moz-animation: LimitedTimeTag 5s ease infinite alternate;
animation: LimitedTimeTag 5s ease infinite alternate;
}
.limited-time::before {
content: '';
position: absolute;
inset: 0;
padding: 3px;
background:
linear-gradient(
45deg,
/*
changed the gradient to blue and green cause @ItsLuiggiYahoo begged me to
rgba(255, 116, 16, 1),
rgba(205, 96, 255, 1)
*/
blue,
green
);
mask:
linear-gradient(#fff 0 0) content-box,
linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
display: block;
border-radius: inherit;
}
.setting-container .warning {
font-size: 0.8rem;
color: orange;
font-weight: lighter;
}
.setting-container .note {
font-size: 0.8rem;
color: #439eff;
font-weight: lighter;
}
.card {
--bs-card-border-color: #1e1e1e;
--bs-card-bg: #121212;
}
@-webkit-keyframes LimitedTimeTag {
0%{color:rgba(255, 116, 16, 1);}
50%{color:rgba(23, 107, 233, 1);}
100%{color:rgba(205, 96, 255, 1);}
}
@-moz-keyframes LimitedTimeTag {
0%{color:rgba(255, 116, 16, 1);}
50%{color:rgba(23, 107, 233, 1);}
100%{color:rgba(205, 96, 255, 1);}
}
@keyframes LimitedTimeTag {
0%{color:rgba(255, 116, 16, 1);}
50%{color:rgba(23, 107, 233, 1);}
100%{color:rgba(205, 96, 255, 1);}
}

View file

@ -14,12 +14,31 @@ body[data-URL^='/create/'] .col.d-flex.align-content-between.flex-wrap {
width: 50%; width: 50%;
} }
body[data-URL^='/create'] .me-2:has(.fa-thumbs-up)::after {
content: '%';
margin-left: -2.5px;
margin-bottom: 2px;
cursor: text;
}
.text-truncate { .text-truncate {
white-space: nowrap !important; white-space: nowrap !important;
overflow: hidden !important; overflow: hidden !important;
text-overflow: ellipsis !important; text-overflow: ellipsis !important;
} }
#servers-tabpane .card {
margin-bottom: 10px;
}
body[data-URL^='/my/settings/privacy'] .card-body:has([action^="/api/users/"]) .card {
margin-bottom: 10px;
}
.home-event-container .img-container {
height: 100% !important;
}
/* ------------------------------------------ */ /* ------------------------------------------ */
/* /*
@ -56,3 +75,66 @@ body:has(.polyplus-modal[open]) {
} }
/* ------------------------------------------ */ /* ------------------------------------------ */
/*
EVENT PLACE CARD HIGHLIGHT ANIMATION
CSS BY @Dragonism ON POLYTORIA
*/
.event-card {
background-size: 200% 200% !important;
-webkit-animation: EventPlace 5s ease infinite;
-moz-animation: EventPlace 5s ease infinite;
animation: EventPlace 5s ease infinite;
}
@-webkit-keyframes EventPlace {
0%{background-position:10% 0%}
50%{background-position:91% 100%}
100%{background-position:10% 0%}
}
@-moz-keyframes EventPlace {
0%{background-position:10% 0%}
50%{background-position:91% 100%}
100%{background-position:10% 0%}
}
@keyframes EventPlace {
0%{background-position:10% 0%}
50%{background-position:91% 100%}
100%{background-position:10% 0%}
}
/* - */
/*
AVATAR SANDBOX
*/
.ribbon-polyplus-custom span {
background-color: orange !important;
}
.ribbon-polyplus-custom::after, .ribbon-polyplus-custom::before {
border-color: #c68000 !important;
}
.ribbon-polyplus-unknown span {
background-color: #000 !important;
}
.ribbon-polyplus-unknown::after, .ribbon-polyplus-unknown::before {
border-color: #121212 !important;
}
.ribbon-polyplus-retro span {
background-color: purple !important;
font-family: 'Courier New', Courier, monospace;
}
.ribbon-polyplus-retro::after, .ribbon-polyplus-retro::before {
border-color: rgb(104, 0, 104) !important;
}

View file

@ -1,4 +1,23 @@
Sync:
------------------
PolyPlus_Settings: {} PolyPlus_Settings: {}
PolyPlus_PinnedGames: [] PolyPlus_PinnedGames: []
PolyPlus_BestFriends: []
PolyPlus_ItemWishlist: [] PolyPlus_ItemWishlist: []
PolyPlus_AvatarSandboxOutfits: []
PolyPlus_TimePlayed: {}
Local:
------------------
PolyPlus_InventoryCache: {
data: [],
requested: number (representing date)
}
PolyPlus_GreatDivideStats: {}
PolyPlus_FriendCount: {
count: number,
requested: number (representing date)
}
PolyPlus_AssetDesigners: {
data: {},
requested: number (representing date)
}

28
images/chrome-icon.svg Normal file
View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>chrome-color</title>
<desc>Created with Sketch.</desc>
<defs>
</defs>
<g id="Icons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Color-" transform="translate(-400.000000, -1043.000000)">
<g id="chrome" transform="translate(400.000000, 1043.000000)">
<path d="M5.7954035,8.36130434 C16.9522782,-4.62351526 37.639151,-2.06037988 45.3727574,13.1072081 C39.9288251,13.1091897 31.4040328,13.1055761 26.786937,13.1072081 C23.4382318,13.1083738 21.2761308,13.0322537 18.9347285,14.2648621 C16.1820632,15.7138239 14.1051274,18.3997073 13.3801164,21.5544341 L5.7954035,8.36130434 L5.7954035,8.36130434 Z" id="chrome-icon-path" fill="#EA4335">
</path>
<path d="M16.015461,23.9991346 C16.015461,28.3998753 19.5936811,31.9800817 23.9919804,31.9800817 C28.3901632,31.9800817 31.9683834,28.3998753 31.9683834,23.9991346 C31.9683834,19.5985104 28.3901632,16.0181875 23.9919804,16.0181875 C19.5936811,16.0181875 16.015461,19.5985104 16.015461,23.9991346 L16.015461,23.9991346 Z" id="chrome-icon-path" fill="#4285F4">
</path>
<path d="M27.0876366,34.4456482 C22.6105798,35.7761751 17.371347,34.3006354 14.5014777,29.3468879 C12.3108329,25.5655987 6.52286114,15.4823164 3.89206021,10.8973955 C-5.32185953,25.0194695 2.61924235,44.2642006 19.3464574,47.5489026 L27.0876366,34.4456482 L27.0876366,34.4456482 Z" id="chrome-icon-path" fill="#34A853">
</path>
<path d="M31.4014697,16.0181875 C35.1303309,19.4863704 35.9427207,25.102234 33.4168909,29.4566966 C31.5138971,32.7374352 25.4402549,42.9884614 22.4966379,47.9523505 C39.730883,49.0147671 52.2944399,32.1238121 46.6195946,16.0181875 L31.4014697,16.0181875 L31.4014697,16.0181875 Z" id="chrome-icon-path" fill="#FBBC05">
</path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
images/client-loading.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 548 KiB

107
images/firefox-icon.svg Normal file
View file

@ -0,0 +1,107 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M28.9905 10.7265C28.3816 9.2574 27.1473 7.67139 26.1784 7.17039C26.967 8.72015 27.4232 10.2746 27.5976 11.4344C27.5976 11.4344 27.5976 11.4426 27.6005 11.4578C26.0156 7.49777 23.3277 5.90065 21.1327 2.42407C21.0213 2.24869 20.9105 2.07331 20.802 1.88566C20.7407 1.77985 20.6911 1.68397 20.648 1.59336C20.557 1.41757 20.4867 1.23179 20.4386 1.03975C20.439 1.03063 20.4359 1.02169 20.4301 1.01467C20.4243 1.00765 20.4161 1.00305 20.4071 1.00175C20.3985 0.999416 20.3894 0.999416 20.3808 1.00175C20.3785 1.00281 20.3763 1.00419 20.3744 1.00584C20.3709 1.00584 20.3674 1.00994 20.3639 1.0111L20.3697 1.0035C16.8483 3.07063 15.6536 6.89446 15.544 8.80784C14.1368 8.90428 12.7913 9.42358 11.683 10.298C11.5672 10.1998 11.4461 10.1081 11.3202 10.0232C11.0008 8.9027 10.9873 7.71683 11.2811 6.58931C9.84091 7.24697 8.72095 8.28463 7.90664 9.20303H7.90023C7.34433 8.49742 7.38341 6.17015 7.41491 5.68435C7.40849 5.65395 7.00076 5.89656 6.94826 5.93339C6.45773 6.2841 5.9992 6.67771 5.57805 7.1096C5.0988 7.59655 4.66096 8.12276 4.26909 8.68274C3.36752 9.96323 2.72814 11.4101 2.3879 12.9398C2.38149 12.9702 2.37565 13.0017 2.36924 13.0327C2.34299 13.1561 2.24791 13.7751 2.23099 13.9096V13.9406C2.10704 14.5803 2.02984 15.2282 2 15.8791V15.951C2 23.7097 8.27646 30 16.0182 30C22.9521 30 28.7088 24.9549 29.8364 18.328C29.8597 18.1485 29.8789 17.9673 29.8999 17.786C30.1788 15.3763 29.869 12.8439 28.9905 10.7265ZM12.8327 21.7239C12.8981 21.7549 12.9599 21.7894 13.027 21.8197L13.0363 21.8256C12.9692 21.7929 12.901 21.759 12.8333 21.7239H12.8327ZM27.6017 11.4642V11.4508V11.466V11.4642Z" fill="url(#paint0_linear_87_7118)"/>
<path d="M28.9907 10.7265C28.3818 9.25741 27.1475 7.67141 26.1786 7.17041C26.9672 8.72017 27.4234 10.2746 27.5978 11.4344V11.4631C28.9208 15.0572 28.1998 18.7121 27.1615 20.9452C25.555 24.4002 21.6661 27.9416 15.578 27.7692C9.00581 27.5821 3.21175 22.6885 2.1297 16.2842C1.93254 15.2735 2.1297 14.7608 2.22886 13.9406C2.10812 14.5725 2.06203 14.7555 2.00195 15.8791V15.951C2.00195 23.7098 8.27842 30 16.0202 30C22.954 30 28.7108 24.9549 29.8383 18.328C29.8616 18.1485 29.8809 17.9673 29.9019 17.7861C30.179 15.3764 29.8692 12.8439 28.9907 10.7265Z" fill="url(#paint1_radial_87_7118)"/>
<path d="M28.9907 10.7265C28.3818 9.25741 27.1475 7.67141 26.1786 7.17041C26.9672 8.72017 27.4234 10.2746 27.5978 11.4344V11.4631C28.9208 15.0572 28.1998 18.7121 27.1615 20.9452C25.555 24.4002 21.6661 27.9416 15.578 27.7692C9.00581 27.5821 3.21175 22.6885 2.1297 16.2842C1.93254 15.2735 2.1297 14.7608 2.22886 13.9406C2.10812 14.5725 2.06203 14.7555 2.00195 15.8791V15.951C2.00195 23.7098 8.27842 30 16.0202 30C22.954 30 28.7108 24.9549 29.8383 18.328C29.8616 18.1485 29.8809 17.9673 29.9019 17.7861C30.179 15.3764 29.8692 12.8439 28.9907 10.7265Z" fill="url(#paint2_radial_87_7118)"/>
<path d="M22.1776 12.3773C22.2085 12.3989 22.2359 12.4205 22.2651 12.4422C21.9133 11.8161 21.4749 11.243 20.9631 10.7398C16.6058 6.37292 19.821 1.27058 20.3629 1.01102L20.3687 1.00342C16.8473 3.07054 15.6526 6.89438 15.543 8.80776C15.7063 8.79665 15.869 8.78262 16.0353 8.78262C18.6631 8.78262 20.952 10.2312 22.1776 12.3773Z" fill="url(#paint3_radial_87_7118)"/>
<path d="M16.0446 13.2499C16.0219 13.6006 14.7899 14.8049 14.3589 14.8049C10.3725 14.8049 9.72559 17.2216 9.72559 17.2216C9.90058 19.2572 11.3157 20.9332 13.0277 21.82C13.1059 21.8604 13.1846 21.8966 13.2611 21.9329C13.3981 21.9913 13.5358 22.0498 13.6729 22.1018C14.26 22.3094 14.8748 22.4276 15.4969 22.4526C22.4838 22.7811 23.8383 14.08 18.7955 11.5534C20.0864 11.3283 21.4269 11.8492 22.1753 12.3759C20.9503 10.2299 18.6608 8.78125 16.033 8.78125C15.8667 8.78125 15.704 8.79528 15.5406 8.80639C14.1345 8.90403 12.7903 9.4239 11.6832 10.2983C11.8973 10.4801 12.1388 10.7221 12.6468 11.2237C13.6 12.1661 16.0394 13.1359 16.0446 13.2499Z" fill="url(#paint4_radial_87_7118)"/>
<path d="M16.0446 13.2499C16.0219 13.6006 14.7899 14.8049 14.3589 14.8049C10.3725 14.8049 9.72559 17.2216 9.72559 17.2216C9.90058 19.2572 11.3157 20.9332 13.0277 21.82C13.1059 21.8604 13.1846 21.8966 13.2611 21.9329C13.3981 21.9913 13.5358 22.0498 13.6729 22.1018C14.26 22.3094 14.8748 22.4276 15.4969 22.4526C22.4838 22.7811 23.8383 14.08 18.7955 11.5534C20.0864 11.3283 21.4269 11.8492 22.1753 12.3759C20.9503 10.2299 18.6608 8.78125 16.033 8.78125C15.8667 8.78125 15.704 8.79528 15.5406 8.80639C14.1345 8.90403 12.7903 9.4239 11.6832 10.2983C11.8973 10.4801 12.1388 10.7221 12.6468 11.2237C13.6 12.1661 16.0394 13.1359 16.0446 13.2499Z" fill="url(#paint5_radial_87_7118)"/>
<path d="M11.0311 9.83093C11.1448 9.90459 11.2382 9.96656 11.3227 10.0233C11.0034 8.90275 10.9899 7.71688 11.2837 6.58936C9.84345 7.24702 8.72349 8.28468 7.90918 9.20308C7.97509 9.20132 10.0085 9.16449 11.0311 9.83093Z" fill="url(#paint6_radial_87_7118)"/>
<path d="M2.1297 16.284C3.21175 22.6883 9.00581 27.5819 15.5827 27.769C21.6707 27.9414 25.5574 24.4 27.1661 20.945C28.2044 18.7113 28.9254 15.057 27.6025 11.4629V11.436C27.6025 11.4395 27.6025 11.4442 27.6054 11.4594C28.1024 14.7138 26.451 17.8665 23.8692 19.9986C23.8666 20.0045 23.8641 20.0106 23.8617 20.0167C18.8306 24.1223 14.0165 22.4936 13.0418 21.8289C12.9741 21.7962 12.9059 21.7623 12.8382 21.7272C9.9047 20.3242 8.69316 17.6438 8.95273 15.3469C6.47656 15.3469 5.63192 13.2529 5.63192 13.2529C5.63192 13.2529 7.85552 11.664 10.7861 13.046C13.5003 14.3262 16.0493 13.2535 16.0493 13.2529C16.0441 13.1389 13.6047 12.1662 12.6533 11.2267C12.1452 10.7251 11.9037 10.4831 11.6896 10.3013C11.5738 10.2031 11.4527 10.1114 11.3268 10.0265C11.2434 9.96809 11.1518 9.90963 11.0352 9.83421C10.0126 9.16778 7.97918 9.20461 7.9121 9.20636H7.90568C7.34978 8.50076 7.38886 6.17348 7.42036 5.68769C7.41395 5.65729 7.00621 5.89989 6.95371 5.93672C6.46318 6.28743 6.00465 6.68104 5.58351 7.11293C5.10426 7.59988 4.66642 8.12609 4.27455 8.68607C3.37298 9.96657 2.7336 11.4134 2.39336 12.9431C2.38228 12.97 1.88354 15.1523 2.1297 16.284Z" fill="url(#paint7_radial_87_7118)"/>
<path d="M20.9634 10.7399C21.4752 11.2431 21.9135 11.8162 22.2653 12.4423C22.3383 12.4971 22.4083 12.5557 22.4753 12.6176C25.6532 15.55 23.9908 19.7012 23.8642 19.9993C26.446 17.8673 28.0973 14.7146 27.6003 11.4601C26.0155 7.49777 23.3276 5.90065 21.1325 2.42407C21.0211 2.24869 20.9103 2.07331 20.8018 1.88566C20.7406 1.77985 20.691 1.68397 20.6478 1.59336C20.5569 1.41757 20.4866 1.23179 20.4384 1.03975C20.4388 1.03063 20.4358 1.02169 20.43 1.01467C20.4241 1.00765 20.4159 1.00305 20.4069 1.00175C20.3983 0.999416 20.3893 0.999416 20.3807 1.00175C20.3783 1.00281 20.3762 1.00419 20.3742 1.00584C20.3707 1.00584 20.3672 1.00994 20.3637 1.0111C19.8213 1.27066 16.606 6.37301 20.9634 10.7399Z" fill="url(#paint8_radial_87_7118)"/>
<path d="M22.4743 12.6146C22.4073 12.5526 22.3372 12.4941 22.2643 12.4392C22.2357 12.4176 22.206 12.396 22.1768 12.3743C21.4284 11.8482 20.088 11.3267 18.7971 11.5518C23.8393 14.0784 22.4854 22.7795 15.4985 22.451C14.8764 22.426 14.2616 22.3078 13.6744 22.1002C13.5374 22.0488 13.3997 21.9921 13.2626 21.9313C13.1833 21.895 13.1045 21.8588 13.0293 21.8185L13.0386 21.8243C14.0133 22.4908 18.8274 24.1194 23.8585 20.0121C23.8585 20.0121 23.8614 20.0045 23.8661 19.9939C23.9909 19.7011 25.6534 15.5499 22.4743 12.6146Z" fill="url(#paint9_radial_87_7118)"/>
<path d="M9.72532 17.2215C9.72532 17.2215 10.3722 14.8048 14.3586 14.8048C14.7897 14.8048 16.0216 13.5994 16.0444 13.2498C16.0671 12.9002 13.4953 14.3231 10.7811 13.0428C7.85055 11.6608 5.62695 13.2498 5.62695 13.2498C5.62695 13.2498 6.47159 15.3438 8.94776 15.3438C8.68819 17.6407 9.89973 20.3187 12.8332 21.7241C12.8986 21.755 12.9604 21.7895 13.0275 21.8199C11.3154 20.9349 9.90207 19.2571 9.72532 17.2215Z" fill="url(#paint10_radial_87_7118)"/>
<path d="M28.9905 10.7265C28.3816 9.2574 27.1473 7.67139 26.1784 7.17039C26.967 8.72015 27.4232 10.2746 27.5976 11.4344C27.5976 11.4344 27.5976 11.4426 27.6005 11.4578C26.0156 7.49777 23.3277 5.90065 21.1327 2.42407C21.0213 2.24869 20.9105 2.07331 20.802 1.88566C20.7407 1.77985 20.6911 1.68397 20.648 1.59336C20.557 1.41757 20.4867 1.23179 20.4386 1.03975C20.439 1.03063 20.4359 1.02169 20.4301 1.01467C20.4243 1.00765 20.4161 1.00305 20.4071 1.00175C20.3985 0.999416 20.3894 0.999416 20.3808 1.00175C20.3785 1.00281 20.3763 1.00419 20.3744 1.00584C20.3709 1.00584 20.3674 1.00994 20.3639 1.0111L20.3697 1.0035C16.8483 3.07063 15.6536 6.89446 15.544 8.80784C15.7073 8.79673 15.8701 8.78271 16.0363 8.78271C18.6641 8.78271 20.9531 10.2313 22.1786 12.3774C21.4302 11.8512 20.0898 11.3298 18.7989 11.5549C23.841 14.0815 22.4872 22.7826 15.5002 22.454C14.8782 22.429 14.2633 22.3108 13.6762 22.1033C13.5391 22.0518 13.4015 21.9951 13.2644 21.9343C13.1851 21.8981 13.1063 21.8618 13.0311 21.8215L13.0404 21.8273C12.9727 21.7946 12.9045 21.7607 12.8368 21.7256C12.9021 21.7566 12.964 21.7911 13.0311 21.8215C11.3155 20.9347 9.90216 19.2569 9.72542 17.2213C9.72542 17.2213 10.3723 14.8046 14.3587 14.8046C14.7898 14.8046 16.0217 13.5992 16.0445 13.2496C16.0392 13.1356 13.5998 12.1628 12.6484 11.2234C12.1403 10.7218 11.8988 10.4798 11.6848 10.298C11.5689 10.1998 11.4478 10.1081 11.3219 10.0232C11.0026 8.9027 10.9891 7.71683 11.2829 6.58931C9.84266 7.24697 8.7227 8.28463 7.90839 9.20303H7.90198C7.34608 8.49742 7.38516 6.17015 7.41666 5.68435C7.41024 5.65395 7.00251 5.89656 6.95001 5.93339C6.45948 6.2841 6.00095 6.67771 5.5798 7.1096C5.10055 7.59655 4.66271 8.12276 4.27084 8.68274C3.36927 9.96323 2.72989 11.4101 2.38965 12.9398C2.38324 12.9702 2.3774 13.0017 2.37099 13.0327C2.34474 13.1561 2.22574 13.7839 2.20941 13.9184C2.20941 13.9289 2.20941 13.9084 2.20941 13.9184C2.10019 14.5671 2.03026 15.2219 2 15.8791V15.951C2 23.7097 8.27646 30 16.0182 30C22.9521 30 28.7088 24.9549 29.8364 18.328C29.8597 18.1485 29.8789 17.9673 29.8999 17.786C30.1788 15.3763 29.869 12.8439 28.9905 10.7265ZM27.5999 11.4479V11.4631V11.4479Z" fill="url(#paint11_linear_87_7118)"/>
<defs>
<linearGradient id="paint0_linear_87_7118" x1="27.135" y1="5.49261" x2="3.81392" y2="27.9437" gradientUnits="userSpaceOnUse">
<stop offset="0.05" stop-color="#FFF44F"/>
<stop offset="0.11" stop-color="#FFE847"/>
<stop offset="0.22" stop-color="#FFC830"/>
<stop offset="0.37" stop-color="#FF980E"/>
<stop offset="0.4" stop-color="#FF8B16"/>
<stop offset="0.46" stop-color="#FF672A"/>
<stop offset="0.53" stop-color="#FF3647"/>
<stop offset="0.7" stop-color="#E31587"/>
</linearGradient>
<radialGradient id="paint1_radial_87_7118" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(26.0596 4.21879) scale(29.2246 29.2888)">
<stop offset="0.13" stop-color="#FFBD4F"/>
<stop offset="0.19" stop-color="#FFAC31"/>
<stop offset="0.25" stop-color="#FF9D17"/>
<stop offset="0.28" stop-color="#FF980E"/>
<stop offset="0.4" stop-color="#FF563B"/>
<stop offset="0.47" stop-color="#FF3750"/>
<stop offset="0.71" stop-color="#F5156C"/>
<stop offset="0.78" stop-color="#EB0878"/>
<stop offset="0.86" stop-color="#E50080"/>
</radialGradient>
<radialGradient id="paint2_radial_87_7118" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(15.3809 16.1925) scale(29.2246 29.2888)">
<stop offset="0.3" stop-color="#960E18"/>
<stop offset="0.35" stop-color="#B11927" stop-opacity="0.74"/>
<stop offset="0.43" stop-color="#DB293D" stop-opacity="0.34"/>
<stop offset="0.5" stop-color="#F5334B" stop-opacity="0.09"/>
<stop offset="0.53" stop-color="#FF3750" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint3_radial_87_7118" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(18.904 -2.42815) scale(21.172 21.2184)">
<stop offset="0.13" stop-color="#FFF44F"/>
<stop offset="0.25" stop-color="#FFDC3E"/>
<stop offset="0.51" stop-color="#FF9D12"/>
<stop offset="0.53" stop-color="#FF980E"/>
</radialGradient>
<radialGradient id="paint4_radial_87_7118" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(12.1487 23.8433) scale(13.915 13.9455)">
<stop offset="0.35" stop-color="#3A8EE6"/>
<stop offset="0.47" stop-color="#5C79F0"/>
<stop offset="0.67" stop-color="#9059FF"/>
<stop offset="1" stop-color="#C139E6"/>
</radialGradient>
<radialGradient id="paint5_radial_87_7118" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(15.8005 12.7119) rotate(-13.9265) scale(7.37316 8.67852)">
<stop offset="0.21" stop-color="#9059FF" stop-opacity="0"/>
<stop offset="0.28" stop-color="#8C4FF3" stop-opacity="0.06"/>
<stop offset="0.75" stop-color="#7716A8" stop-opacity="0.45"/>
<stop offset="0.97" stop-color="#6E008B" stop-opacity="0.6"/>
</radialGradient>
<radialGradient id="paint6_radial_87_7118" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(15.011 3.02041) scale(10.0108 10.0328)">
<stop stop-color="#FFE226"/>
<stop offset="0.12" stop-color="#FFDB27"/>
<stop offset="0.3" stop-color="#FFC82A"/>
<stop offset="0.5" stop-color="#FFA930"/>
<stop offset="0.73" stop-color="#FF7E37"/>
<stop offset="0.79" stop-color="#FF7139"/>
</radialGradient>
<radialGradient id="paint7_radial_87_7118" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(22.8805 -3.34313) scale(42.7109 42.8046)">
<stop offset="0.11" stop-color="#FFF44F"/>
<stop offset="0.46" stop-color="#FF980E"/>
<stop offset="0.62" stop-color="#FF5634"/>
<stop offset="0.72" stop-color="#FF3647"/>
<stop offset="0.9" stop-color="#E31587"/>
</radialGradient>
<radialGradient id="paint8_radial_87_7118" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(18.7517 1.33374) rotate(84.2447) scale(31.1996 20.4543)">
<stop stop-color="#FFF44F"/>
<stop offset="0.06" stop-color="#FFE847"/>
<stop offset="0.17" stop-color="#FFC830"/>
<stop offset="0.3" stop-color="#FF980E"/>
<stop offset="0.36" stop-color="#FF8B16"/>
<stop offset="0.45" stop-color="#FF672A"/>
<stop offset="0.57" stop-color="#FF3647"/>
<stop offset="0.74" stop-color="#E31587"/>
</radialGradient>
<radialGradient id="paint9_radial_87_7118" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(14.7757 6.73593) scale(26.6644 26.723)">
<stop offset="0.14" stop-color="#FFF44F"/>
<stop offset="0.48" stop-color="#FF980E"/>
<stop offset="0.59" stop-color="#FF5634"/>
<stop offset="0.66" stop-color="#FF3647"/>
<stop offset="0.9" stop-color="#E31587"/>
</radialGradient>
<radialGradient id="paint10_radial_87_7118" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(21.8145 8.30059) scale(29.1844 29.2484)">
<stop offset="0.09" stop-color="#FFF44F"/>
<stop offset="0.23" stop-color="#FFE141"/>
<stop offset="0.51" stop-color="#FFAF1E"/>
<stop offset="0.63" stop-color="#FF980E"/>
</radialGradient>
<linearGradient id="paint11_linear_87_7118" x1="26.855" y1="5.37218" x2="7.01043" y2="25.1739" gradientUnits="userSpaceOnUse">
<stop offset="0.17" stop-color="#FFF44F" stop-opacity="0.8"/>
<stop offset="0.27" stop-color="#FFF44F" stop-opacity="0.63"/>
<stop offset="0.49" stop-color="#FFF44F" stop-opacity="0.22"/>
<stop offset="0.6" stop-color="#FFF44F" stop-opacity="0"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 15 KiB

BIN
images/greatdivide.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

View file

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View file

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

1368
js/account/avatar-sandbox.js Executable file → Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,498 +0,0 @@
const UserID = JSON.parse(window.localStorage.getItem('p+account_info')).ID;
const BodyColors = [
'#f8f8f8',
'#cdcdcd',
'#111111',
'#ff0000',
'#a34b4b',
'#ffc9c9',
'#957977',
'#c4281c',
'#da867a',
'#694028',
'#cc8e69',
'#a05f35',
'#7c5c46',
'#eab892',
'#da8541',
'#aa5500',
'#ffcc99',
'#e29b40',
'#ffaf00',
'#ffb000',
'#d7c59a',
'#f5cd30',
'#fdea8d',
'#e5e4df',
'#c1be42',
'#ffff00',
'#ffffcc',
'#a4bd47',
'#7f8e64',
'#a1c48c',
'#3a7d15',
'#4b974b',
'#00ff00',
'#ccffcc',
'#27462d',
'#287f47',
'#789082',
'#9ff3e9',
'#12eed4',
'#f2f3f3',
'#00ffff',
'#008f9c',
'#04afec',
'#80bbdb',
'#b4d2e4',
'#0d69ac',
'#1b2a35',
'#afddff',
'#6e99ca',
'#74869d',
'#2154b9',
'#002060',
'#0000ff',
'#b1a7ff',
'#a3a2a5',
'#6225d1',
'#b480ff',
'#8c5b9f',
'#6b327c',
'#aa00aa',
'#635f62',
'#ff00bf',
'#ff66cc',
'#e8bac8'
];
let PageContainer = document.querySelector('.container.p-0.p-lg-5');
let ItemGrid;
let Wearing;
let Tabs;
let IFrame;
let TabSelected = 'hat';
let Search;
let Page = 1;
let Avatar = {
useCharacter: true,
items: [24122],
shirt: 24118,
pants: 24123,
headColor: '#e0e0e0',
torsoColor: '#e0e0e0',
leftArmColor: '#e0e0e0',
rightArmColor: '#e0e0e0',
leftLegColor: '#e0e0e0',
rightLegColor: '#e0e0e0'
};
if (new URLSearchParams(window.location.search).has('sandbox')) {
console.log('Avatar Sandbox!');
LoadFile(chrome.runtime.getURL('resources/avatar-sandbox.html'), function (html) {
PageContainer.innerHTML = html;
ItemGrid = document.getElementById('inventory');
Wearing = document.getElementById('wearing');
Tabs = document.getElementById('tabs');
IFrame = document.getElementById('viewFrame');
Search = document.getElementById('item-search');
Search.addEventListener('onchange', function () {
RefreshItems();
});
UpdateAvatar();
RefreshItems();
LoadWearing();
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');
Page = 1;
RefreshItems();
}
});
});
let Clear = document.getElementById('clear');
Clear.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();
});
let Myself = document.getElementById('myself');
Myself.addEventListener('click', function () {
LoadMyself();
});
let JSONUpload = document.getElementById('jsonUpload');
JSONUpload.addEventListener('change', function () {
let Reader = new FileReader();
Reader.addEventListener('loadend', function () {
Avatar = JSON.parse(Reader.result);
UpdateAvatar();
JSONUpload.value = '';
});
Reader.readAsText(JSONUpload.files[0]);
});
let JSONSave = document.getElementById('jsonSave');
JSONSave.addEventListener('click', function () {
let 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);
});
let OpenInNewTab = document.getElementById('openNewTab');
OpenInNewTab.addEventListener('click', function () {
UpdateAvatar();
});
let LoadAsset = document.getElementById('load-asset');
LoadAsset.addEventListener('click', async function () {
console.log('clickk');
const MeshURL = (await (await fetch('https://api.polytoria.com/v1/assets/serve-mesh/' + LoadAsset.previousElementSibling.value)).json()).url;
Avatar.items.push(MeshURL);
UpdateAvatar();
});
});
} 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);
}
function UpdateAvatar() {
GenerateHash().then((hash) => {
IFrame.addEventListener('load', function () {
IFrame.src = 'https://polytoria.com/ptstatic/itemview/#' + hash;
});
IFrame.src = 'about:blank';
});
}
function LoadFile(path, callback) {
var xhr = new XMLHttpRequest();
xhr.onload = function () {
return callback(this.responseText);
};
xhr.open('GET', path, true);
xhr.send();
}
async function GenerateHash(data) {
if (!data) {
console.log('Data not provided');
let FormattedAvatar = await FormatAvatar();
return btoa(encodeURIComponent(JSON.stringify(FormattedAvatar)));
} else {
console.log('Data provided');
return btoa(encodeURIComponent(JSON.stringify(data)));
}
}
function RefreshItems() {
fetch(`https://api.polytoria.com/v1/store?search=${Search.value}&types%5B%5D=${TabSelected}&sort=createdAt&order=desc&page=${Page}&limit=12`)
.then((response) => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then((data) => {
Array.from(ItemGrid.children).forEach((element) => {
element.remove();
});
data = data.assets;
data.forEach((item) => {
let NewItemCard = document.createElement('div');
NewItemCard.setAttribute('data-id', item.id);
NewItemCard.classList = 'col-auto';
NewItemCard.innerHTML = `
<div style="max-width: 150px;">
<div class="card mb-2 avatar-item-container">
<div class="p-2">
<img src="${item.thumbnail}" class="img-fluid">
<span class="position-absolute" style="top: 5px; left: 5px; z-index: 1;">
<span class="badge bg-secondary">${item.type.charAt(0).toUpperCase() + item.type.substring(1)}</span>
</span>
<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="/store/${item.id}" class="text-reset">
<h6 class="text-truncate mb-0">${item.name}</h6>
</a>
<small class="text-muted d-block text-truncate">
by <a href="/users/${item.creator.id}" class="text-reset">${item.creator.name}</a>
</small>
</div>
`;
NewItemCard.getElementsByClassName('p-2')[0].addEventListener('click', function () {
WearAsset(NewItemCard, item);
});
ItemGrid.appendChild(NewItemCard);
});
})
.catch((error) => {
console.error('Fetch error:', error);
});
}
async function FormatAvatar() {
const FormattedAvatar = structuredClone(Avatar);
// Hats, Tools: https://api.polytoria.com/v1/assets/serve-mesh/:id
// or: https://api.polytoria.com/v1/assets/serve/:id/Asset
Avatar.items.forEach(async (item, index) => {
if (typeof item === 'number') {
console.log(item);
await FetchMesh(item)
.then((URL) => {
console.log('URL: ' + URL);
FormattedAvatar.items[index] = URL;
})
.catch((error) => {
throw new Error(error);
});
console.log('after url');
//Avatar.items[index] = URL
}
});
if (typeof FormattedAvatar.tool === 'number') {
console.log(FormattedAvatar.tool);
FormattedAvatar.tool = await FetchMesh(FormattedAvatar.tool);
}
if (FormattedAvatar.face === undefined) { FormattedAvatar.face = 'https://c0.ptacdn.com/static/3dview/DefaultFace.png'; }
if (!FormattedAvatar.face.startsWith('data:') && !FormattedAvatar.face.startsWith('http')) {
if (FormattedAvatar.face && typeof FormattedAvatar.face === 'number') {
FormattedAvatar.face = await FetchAsset(FormattedAvatar.face);
} else {
FormattedAvatar.face = 'https://c0.ptacdn.com/static/3dview/DefaultFace.png';
}
}
if (typeof FormattedAvatar.shirt === 'number') {
FormattedAvatar.shirt = await FetchAsset(FormattedAvatar.shirt);
}
if (typeof FormattedAvatar.pants === 'number') {
FormattedAvatar.pants = await FetchAsset(FormattedAvatar.pants);
}
console.log('Real Avatar: ', Avatar, 'Formatted: ', FormattedAvatar);
return FormattedAvatar;
}
function LoadMyself() {
fetch('https://api.polytoria.com/v1/users/:id/avatar'.replace(':id', UserID))
.then((response) => {
if (!response.ok) {
throw new Error('Network not ok');
}
return response.json();
})
.then((data) => {
Avatar.items = [];
data.assets.forEach((item) => {
switch (item.type) {
case 'hat':
Avatar.items.push(item.id);
break;
default:
Avatar[item.type] = item.id;
break;
}
});
Avatar.headColor = '#' + data.colors.head || '#cdcdcd';
Avatar.torsoColor = '#' + data.colors.torso || '#cdcdcd';
Avatar.leftArmColor = '#' + data.colors.leftArm || '#cdcdcd';
Avatar.rightArmColor = '#' + data.colors.rightArm || '#cdcdcd';
Avatar.leftLegColor = '#' + data.colors.leftLeg || '#cdcdcd';
Avatar.rightLegColor = '#' + data.colors.rightLeg || '#cdcdcd';
UpdateAvatar();
})
.catch((error) => {
console.log(error);
});
}
function WearAsset(element, info) {
if (Avatar.items.indexOf(info.id) === -1 && Avatar[info.type] !== info.id) {
console.log('Equip', info);
switch (info.type) {
case 'hat':
Avatar.items.push(info.id);
break;
default:
Avatar[info.type] = info.id;
break;
}
} else {
console.log('unequip', info);
switch (info.type) {
case 'hat':
Avatar.items.splice(Avatar.items.indexOf(info.id), 1);
break;
case 'face':
Avatar.face = 'https://c0.ptacdn.com/static/3dview/DefaultFace.png';
break;
default:
Avatar[info.type] = undefined;
break;
}
}
const ToggleButton = element.getElementsByClassName('avatarAction')[0];
ToggleButton.classList.toggle('btn-success');
ToggleButton.classList.toggle('btn-danger');
ToggleButton.children[0].classList.toggle('fa-plus');
ToggleButton.children[0].classList.toggle('fa-minus');
const Duplicate = ItemGrid.querySelector(`[data-id="${info.id}"]`);
if (Duplicate !== null && Duplicate !== element) {
const DuplicateToggleButton = Duplicate.getElementsByClassName('avatarAction')[0];
DuplicateToggleButton.classList.toggle('btn-success');
DuplicateToggleButton.classList.toggle('btn-danger');
DuplicateToggleButton.children[0].classList.toggle('fa-plus');
DuplicateToggleButton.children[0].classList.toggle('fa-minus');
}
LoadWearing();
UpdateAvatar();
}
async function FetchMesh(id) {
if (id === null) {
return null;
}
console.log('https://api.polytoria.com/v1/assets/serve-mesh/:id'.replace(':id', id));
return fetch('https://api.polytoria.com/v1/assets/serve-mesh/:id'.replace(':id', id))
.then((response) => {
if (!response.ok) {
throw new Error('Network not ok');
}
return response.json();
})
.then((data) => {
console.log(data, 'finished', data.url);
return data.url;
})
.catch((error) => {
console.log('Fetch error: ' + error);
});
}
async function FetchAsset(id) {
if (id === null) {
return null;
}
return fetch('https://api.polytoria.com/v1/assets/serve/:id/Asset'.replace(':id', id))
.then((response) => {
if (!response.ok) {
throw new Error('Network not ok');
}
return response.json();
})
.then((data) => {
return data.url;
})
.catch((error) => {
console.log('Fetch error: ' + error);
});
}
function LoadWearing() {
const WearingItems = [...Avatar.items, Avatar.shirt, Avatar.pants, Avatar.face].filter((item) => item !== null && item !== undefined);
Array.from(Wearing.children).forEach((element) => {
const ItemID = element.getElementsByTagName('a')[0].href.split('/')[2];
if (!WearingItems.includes(ItemID)) {
element.remove();
}
});
WearingItems.forEach((item) => {
const ExistingElement = Wearing.querySelector(`[data-itemid="${item}"]`);
if (!ExistingElement) {
fetch(`https://api.polytoria.com/v1/store/${item}`)
.then((response) => {
if (!response.ok) {
throw new Error('Network not ok');
}
return response.json();
})
.then((item) => {
if (Wearing.innerHTML === 'No items to show.') {
Wearing.innerHTML = '';
}
let NewItemCard = document.createElement('div');
NewItemCard.setAttribute('data-id', item.id);
NewItemCard.classList = 'col-auto';
NewItemCard.innerHTML = `
<div style="max-width: 150px;">
<div class="card mb-2 avatar-item-container">
<div class="p-2">
<img src="${item.thumbnail}" class="img-fluid">
<span class="position-absolute" style="top: 5px; left: 5px; z-index: 1;">
<span class="badge bg-secondary">${item.type.charAt(0).toUpperCase() + item.type.substring(1)}</span>
</span>
<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="/store/${item.id}" class="text-reset">
<h6 class="text-truncate mb-0">${item.name}</h6>
</a>
<small class="text-muted d-block text-truncate">
by <a href="/users/${item.creator.id}" class="text-reset">${item.creator.name}</a>
</small>
</div>
`;
Wearing.appendChild(NewItemCard);
NewItemCard.getElementsByClassName('p-2')[0].addEventListener('click', function () {
WearAsset(NewItemCard, item);
});
})
.catch((error) => {
console.log('Fetch error: ' + error);
});
}
});
if (Array.from(Wearing.children).length === 0) {
Wearing.innerHTML = 'No items to show.';
}
}

398
js/account/home.js Executable file → Normal file
View file

@ -1,223 +1,179 @@
/* chrome.storage.sync.get(['PolyPlus_Settings', 'PolyPlus_PinnedGames'], async function(result){
this file needs a rewrite by me lol Settings = result.PolyPlus_Settings || {
*/ PinnedGamesOn: true
var Settings;
var PinnedGamesData;
var BestFriendsData;
let Utilities;
(async () => {
Utilities = await import(chrome.runtime.getURL('resources/utils.js'));
Utilities = Utilities.default;
chrome.storage.sync.get(['PolyPlus_Settings'], async function (result) {
Settings = Utilities.MergeObjects(result.PolyPlus_Settings || Utilities.DefaultSettings);
if (Settings.IRLPriceWithCurrency && Settings.IRLPriceWithCurrency.Enabled === true) {
IRLPrice();
}
if (Settings.HomeFriendCountOn === true) {
ShowFriendCount();
}
if (Settings.PinnedGamesOn === true || Settings.BestFriendsOn === true) {
Update();
}
});
})();
let ContainerElement = `
<div class="card-body p-0 m-1 scrollFadeContainer d-flex"></div>`;
let GameContainerElement = `
<div class="scrollFade card me-2 place-card force-desktop text-center mb-2" style="opacity: 1;">
<div class="card-body">
<div class="ratings-header">
<img src=":Thumbnail" class="place-card-image" style="position: relative;">
<div style="position: absolute;background: linear-gradient(to bottom, black, transparent, transparent, transparent);width: 100%;height: 100%;top: 0;left: 0;border-radius: 10px;padding-top: 5px;color: gray;font-size: 0.8rem;">
<span>
<i id="thumbup-icn" class="thumb-icon far fa-thumbs-up"></i>
:Likes
</span>
|
<span>
<i id="thumbdown-icn" class="thumb-icon far fa-thumbs-down"></i>
:Dislikes
</span>
</div>
</div>
<div>
<div class="mt-2 mb-1 place-card-title">
:GameName
</div>
</div>
</div>
</div>
`;
let TitleElement = `
<div class="col">
<h6 class="dash-ctitle2">Jump right back into your favorite games</h6>
<h5 class="dash-ctitle">Pinned Games</h5>
</div>`;
var FriendContainer = document.querySelector('.card:has(.friendsPopup) .card-body');
let NewContainer = document.createElement('div');
NewContainer.style.display = 'none';
NewContainer.classList = 'card card-dash mcard';
NewContainer.style.animationDelay = '0.18s';
NewContainer.innerHTML = ContainerElement;
let NewTitle = document.createElement('div');
NewTitle.style.display = 'none';
NewTitle.classList = 'row reqFadeAnim px-2 px-lg-0';
NewTitle.innerHTML = TitleElement;
let BestFriendsContainer = document.createElement('div');
BestFriendsContainer.classList = 'd-flex';
BestFriendsContainer.style = 'display: none; border-bottom: 1px solid #000; padding-bottom: 10px; margin-bottom: 10px; width: 100%;';
let Spacer = document.createElement('div');
Spacer.innerHTML = ' ';
Spacer.style.width = '50px';
Spacer.prepend(BestFriendsContainer);
FriendContainer.prepend(BestFriendsContainer);
async function Update() {
chrome.storage.sync.get(['PolyPlus_PinnedGames'], function (result) {
PinnedGamesData = result.PolyPlus_PinnedGames || [];
if (Settings.PinnedGamesOn === true) {
PinnedGames();
} else {
NewContainer.style.display = 'none';
NewTitle.style.display = 'none';
}
});
chrome.storage.sync.get(['PolyPlus_BestFriends'], function (result) {
BestFriendsData = result.PolyPlus_BestFriends || [];
if (Settings.BestFriendsOn === true) {
BestFriends();
} else {
BestFriendsContainer.style.display = 'none';
Spacer.style.display = 'none';
}
});
}
function PinnedGames() {
var Existing = NewContainer.children[0].children;
Array.from(Existing).forEach((element) => {
element.remove();
});
if (PinnedGamesData.length === 0) {
NewContainer.style.display = 'none';
NewTitle.style.display = 'none';
} else {
NewContainer.style.display = '';
NewTitle.style.display = '';
}
PinnedGamesData.forEach((element) => {
fetch('https://api.polytoria.com/v1/places/' + element)
.then((response) => response.json())
.then((data) => {
let GameName = data.name;
let GameThumbnail = data.thumbnail;
var NewGameContainer = document.createElement('a');
NewGameContainer.innerHTML = GameContainerElement.replace(':GameName', GameName)
.replace(':Thumbnail', GameThumbnail)
.replace(':Likes', data.rating.likes)
.replace(':Dislikes', data.rating.dislikes);
NewGameContainer.href = '/places/' + element;
/*
if (new Date().getDate() >= new Date(data.updatedAt).getDate()) {
console.log('Game has updated')
}
*/
NewContainer.children[0].appendChild(NewGameContainer);
})
.catch((error) => {
console.error('Error:', error);
});
});
}
function BestFriends() {
Array.from(document.querySelectorAll('[bestFriend]')).forEach((element) => {
element.removeAttribute('bestFriend');
element.getElementsByClassName('friend-name')[0].style.color = 'initial';
FriendContainer.appendChild(element);
});
if (BestFriendsData.length === 0) {
BestFriendsContainer.style.visibility = 'hidden';
BestFriendsContainer.style.padding = '0px !important';
BestFriendsContainer.style.margin = '0px !important';
} else {
BestFriendsContainer.style.visibility = 'visible';
BestFriendsContainer.style.padding = '';
BestFriendsContainer.style.margin = '';
}
BestFriendsData.forEach((element) => {
let ExistingFriend = document.getElementById('friend-' + element);
if (ExistingFriend) {
ExistingFriend.setAttribute('bestFriend', 'true');
ExistingFriend.getElementsByClassName('friend-name')[0].style.color = 'yellow';
BestFriendsContainer.prepend(ExistingFriend);
}
});
}
var SecondaryColumn = document.getElementsByClassName('col-lg-8')[0];
SecondaryColumn.insertBefore(NewContainer, SecondaryColumn.children[0]);
SecondaryColumn.insertBefore(NewTitle, SecondaryColumn.children[0]);
async function IRLPrice() {
(async () => {
Utilities = await import(chrome.runtime.getURL('resources/utils.js'));
Utilities = Utilities.default;
const TrendingItems = document.getElementById('home-trendingItems');
for (let item of TrendingItems.children[1].getElementsByClassName('d-flex')[0].children) {
const Price = item.getElementsByClassName('text-success')[0];
if (Price !== undefined) {
const IRLResult = await Utilities.CalculateIRL(Price.innerText, Settings.IRLPriceWithCurrency.Currency);
let Span = document.createElement('span');
Span.classList = 'text-muted polyplus-price-tag';
Span.style = 'font-size: 0.7rem; font-weight: lighter;';
Span.innerText = '($' + IRLResult.result + ' ' + IRLResult.display + ')';
Price.appendChild(Span);
}
}
})();
}
async function ShowFriendCount() {
let FriendCount = (await (await fetch('https://polytoria.com/api/friends?page=1')).json()).meta.total;
/*
const FirstPage = (await (await fetch('https://polytoria.com/api/friends?page=1')).json())
if (FirstPage.meta.lastPage > 1) {
const LastPage = (await (await fetch('https://polytoria.com/api/friends?page=' + Pages)).json())
FriendCount = (12*(FirstPage.meta.pages-1)) + LastPage.data.length
} else {
FriendCount = FirstPage.data.length
} }
*/
const CountText = document.createElement('small'); if (Settings.PinnedGamesOn === true) {
CountText.classList = 'text-muted fw-lighter'; const PlaceIDs = result.PolyPlus_PinnedGames || [];
CountText.style.fontSize = '0.8rem'; chrome.storage.local.get(['PolyPlus_PinnedGamesData'], async function(result){
CountText.innerText = ' (' + FriendCount + ')'; let PinnedGamesData = result['PolyPlus_PinnedGamesData'] || {data:undefined};
document.querySelector('#home-friendsOnline h5').appendChild(CountText);
} const PinnedGamesContainer = document.createElement('div')
PinnedGamesContainer.innerHTML = `
<div class="row reqFadeAnim px-2 px-lg-0">
<div class="col">
<h6 class="dash-ctitle2">Jump right back into your favorite games</h6>
<h5 class="dash-ctitle">Pinned Games</h5>
</div>
</div>
<div class="card card-dash mcard mb-3">
<div class="card-body p-0 m-1 scrollFadeContainer" id="p+pinned_games_card">
<div class="text-center p-5">
<div class="spinner-border text-muted" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
</div>
</div>
`
const RightSideColumn = document.getElementsByClassName('col-lg-8')[0];
if (document.getElementsByClassName('home-event-container')[0] === undefined) {
RightSideColumn.insertBefore(PinnedGamesContainer, RightSideColumn.children[0]);
} else {
RightSideColumn.insertBefore(PinnedGamesContainer, RightSideColumn.children[1]);
}
// cache for 5 minutes
if (PinnedGamesData === undefined || (new Date().getTime() - PinnedGamesData.requested > 300000)) {
PinnedGamesData.data = {}
for (let i = 0; i < PlaceIDs.length; i++) {
const PlaceID = PlaceIDs.toSorted((a, b) => b - a)[i]
const PlaceDetails = (await (await fetch('https://api.polytoria.com/v1/places/' + PlaceID)).json())
PinnedGamesData.data[PlaceID] = PlaceDetails
}
chrome.storage.local.set({['PolyPlus_PinnedGamesData']: {
data: PinnedGamesData.data,
requested: new Date().getTime()
}}, function(){});
}
const PinnedGamesCard = document.getElementById('p+pinned_games_card')
for (let i = 0; i < PlaceIDs.length; i++) {
const PlaceID = PlaceIDs.toSorted((a, b) => b - a)[i]
const PlaceDetails = PinnedGamesData.data[PlaceID]
const PlaceCard = document.createElement('a')
PlaceCard.classList = 'd-none'
PlaceCard.href = '/places/' + PlaceID
PlaceCard.innerHTML = `
<div class="scrollFade card me-2 place-card force-desktop text-center mb-2" style="opacity: 1;">
<div class="card-body">
<div class="ratings-header">
<img src="${PlaceDetails.thumbnail}" class="place-card-image" style="position: relative;">
<div class="p+pinned_games_playing" style="position: absolute;background: linear-gradient(to bottom, #000000f7, transparent, transparent, transparent);width: 100%;height: 100%;top: 0;left: 0;border-radius: 15px;padding-top: 12px;color: gray;font-size: 0.8rem;">
<i class="fa-duotone fa-users"></i>
<span>
${PlaceDetails.playing}
Playing
</span>
</div>
</div>
<div>
<div class="mt-2 mb-1 place-card-title">
${PlaceDetails.name}
</div>
</div>
</div>
</div>
`
if (!PlaceDetails.isActive) {
const PlayerCountText = PlaceCard.getElementsByClassName('p+pinned_games_playing')[0]
PlayerCountText.children[0].classList = 'text-warning fa-duotone fa-lock'
PlayerCountText.children[1].remove()
}
PinnedGamesCard.appendChild(PlaceCard)
}
PinnedGamesCard.children[0].remove()
PinnedGamesCard.classList.add('d-flex')
Array.from(PinnedGamesCard.children).forEach((place) => {place.classList.remove('d-none')})
});
}
if (Settings.IRLPriceWithCurrency && Settings.IRLPriceWithCurrency.Enabled === true) {
(async () => {
Utilities = await import(chrome.runtime.getURL('resources/utils.js'));
Utilities = Utilities.default;
const TrendingItems = document.getElementById('home-trendingItems');
for (let item of TrendingItems.children[1].getElementsByClassName('d-flex')[0].children) {
const Price = item.getElementsByClassName('text-success')[0];
if (Price !== undefined) {
const IRLResult = await Utilities.CalculateIRL(Price.innerText, Settings.IRLPriceWithCurrency.Currency);
let Span = document.createElement('span');
Span.classList = 'text-muted polyplus-price-tag';
Span.style = 'font-size: 0.7rem; font-weight: lighter;';
Span.innerText = '($' + IRLResult.result + ' ' + IRLResult.display + ')';
Price.appendChild(Span);
}
}
})();
}
if (Settings.HomeFriendCountOn === true) {
chrome.storage.local.get(['PolyPlus_FriendCount'], async function(result){
let FriendCount = result['PolyPlus_FriendCount'].count;
// cache for 5 minutes
if (FriendCount === undefined || (new Date().getTime() - FriendCount.requested > 300000)) {
FriendCount = (await (await fetch('https://polytoria.com/api/friends?page=1')).json()).meta.total;
chrome.storage.local.set({['PolyPlus_FriendCount']: {
count: FriendCount,
requested: new Date().getTime()
}}, function(){});
}
const CountText = document.createElement('small');
CountText.classList = 'text-muted fw-lighter';
CountText.style.fontSize = '0.8rem';
CountText.innerText = ' (' + FriendCount + ')';
document.querySelector('#home-friendsOnline h5').appendChild(CountText);
});
}
if (Settings.HomeJoinFriendsButtonOn === true) {
const FriendsPopup = document.getElementById('friend-name')
const ChangeMutator = new MutationObserver(async function (list) {
for (let record of list) {
for (let node of record.addedNodes) {
if (node.tagName === 'A') {
const JoinButton = document.createElement('button')
JoinButton.classList = 'btn btn-success btn-sm'
JoinButton.style = 'position: absolute; top: 0; right: 0; z-index: 2000; font-size: 1.2rem;'
JoinButton.innerHTML = '<i class="fas fa-play"></i>'
node.parentElement.appendChild(JoinButton)
JoinButton.addEventListener('click', async function(){
const PlayingStatus = (await (await fetch('https://api.polytoria.com/v1/users/' + document.getElementById('friendsProfileLink').getAttribute('href').split('/')[2])).json()).playing;
const Token = (await (await fetch('https://polytoria.com/api/places/join', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
placeID: parseInt(node.getAttribute('href').split('/')[2]),
serverID: PlayingStatus.serverID
})
})).json())
if (!Token.success) {
alert(Token.message);
return
}
window.location.href = 'polytoria://client/' + Token.token
})
}
}
}
})
ChangeMutator.observe(FriendsPopup, {childList: true})
}
})

View file

@ -1,203 +1,352 @@
console.log('path', window.location.pathname.split('/')[3]); /*
Proper pagination will be added in v1.2.3
*/
Username = window.location.pathname.split('/')[2];
let UserID;
let ItemGrid;
let _Utilities;
if (window.location.pathname.split('/')[3] === 'inventory') { if (window.location.pathname.split('/')[3] === 'inventory') {
const Username = window.location.pathname.split('/')[2]; !(async () => {
console.log(JSON.parse(window.localStorage.getItem('p+account_info')).Username); UserID = (await (await fetch('https://api.polytoria.com/v1/users/find?username=' + Username )).json()).id;
if (Username === JSON.parse(window.localStorage.getItem('p+account_info')).Username) { ItemGrid = document.getElementsByClassName('itemgrid')[0];
console.log('is user');
let Nav = document.getElementsByClassName('nav-pills')[0];
let WishlistNav = document.createElement('li');
WishlistNav.classList.add('nav-item');
WishlistNav.innerHTML = `
<a href="wishlist/" class="nav-link">
<i class="fa-regular fa-sparkles me-1"></i>
<span class="pilltitle">Item Wishlist</span>
</a>
`;
Nav.appendChild(WishlistNav);
if (window.location.pathname.split('/')[4] === 'wishlist') { /*
console.log('aaa'); Rewritten item wishlist function will take in the data with a parameter instead
const ItemGrid = document.getElementsByClassName('itemgrid')[0]; */
const ItemCardContents = ` chrome.storage.sync.get(['PolyPlus_Settings'], async function(result){
<a href="/store/:ItemID" class="text-reset"> _Utilities = await import(chrome.runtime.getURL('resources/utils.js'));
<div class="card mb-2"> _Utilities = _Utilities.default
:LimitedTag
<div class="card-body">
<img src=":ItemThumbnail" class="img-fluid rounded">
</div>
</div>
<h6 class="text-truncate mb-0">
:ItemName
</h6>
</a>
<small class="text-muted d-block mb-1">
by <a href="/users/:CreatorID" class="text-muted">:CreatorName</a>
</small>
<button class="polyplus-itemwish-removebtn btn btn-danger btn-sm" style="width: 100%;">X</button>
`;
Array.from(ItemGrid.children).forEach((element) => { Settings = result.PolyPlus_Settings
element.remove();
}); const Nav = document.getElementsByClassName('nav-pills')[0];
Array.from(Nav.children).forEach((element) => {
element = element.children[0]; if (Settings.ItemWishlistOn && Username === JSON.parse(window.localStorage.getItem('p+account_info')).Username) {
if (!(element === WishlistNav)) { let WishlistNav = document.createElement('li');
if (element.classList.contains('active')) { WishlistNav.classList.add('nav-item');
element.classList.remove('active'); WishlistNav.innerHTML = `
} <a href="${ (window.location.pathname.split('/')[4] !== '') ? '../wishlist/' : 'wishlist/' }" class="nav-link">
<i class="fa-regular fa-list me-1"></i>
<span class="pilltitle">Item Wishlist</span>
</a>
`;
Nav.appendChild(WishlistNav);
if (window.location.pathname.split('/')[4] === 'wishlist') {
Array.from(Nav.children).forEach((element) => {
element = element.children[0];
if (!(element === WishlistNav)) {
if (element.classList.contains('active')) {
element.classList.remove('active');
}
}
});
WishlistNav.children[0].classList.add('active');
ItemWishlist()
} }
}); }
WishlistNav.children[0].classList.add('active');
const Search = document.createElement('div');
Search.classList = 'row';
Search.innerHTML = `
<div class="col-auto">
<select class="form-select" id="polyplus-itemwish-type" style="width: 150px;">
<option value="any">Any</option>
<option value="hat">Hat</option>
<option value="face">Faces</option>
<option value="tool">Tools</option>
<option value="shirt">Shirt</option>
<option value="pants">Pants</option>
</select>
</div>
<div class="col">
<input id="polyplus-itemwish-searchbar" type="text" class="form-control bg-dark" placeholder="Search...">
</div>
<div class="col-auto">
<div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="polyplus-itemwish-isLimited">
<label class="form-check-label" for="polyplus-itemwish-isLimited">
Is Limited?
<span class="text-muted" style="font-size: 0.65rem; display: block;">Items that are limited</span>
</label>
</div>
</div>
<div class="col-auto">
<div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="polyplus-itemwish-isAvailable">
<label class="form-check-label" for="polyplus-itemwish-isAvailable">
Is Available?
<span class="text-muted" style="font-size: 0.65rem; display: block; width: 170px;">Items that are equal to or less than the budget (excluding limiteds)</span>
</label>
</div>
</div>
`;
ItemGrid.parentElement.prepend(document.createElement('br'), ItemGrid.parentElement.children[0]);
ItemGrid.parentElement.prepend(Search, ItemGrid.parentElement.children[0]);
let Type = document.getElementById('polyplus-itemwish-type'); if (Settings.CollectibleInventoryCatOn) {
let SearchBar = document.getElementById('polyplus-itemwish-searchbar'); let CollectibleNav = document.createElement('li');
let IsLimited = document.getElementById('polyplus-itemwish-isLimited'); CollectibleNav.classList.add('nav-item');
let IsAvailable = document.getElementById('polyplus-itemwish-isAvailable'); CollectibleNav.innerHTML = `
<a href="${ (window.location.pathname.split('/')[4] !== '') ? '../collectibles/' : 'collectibles/' }" class="nav-link">
<i class="fa-regular fa-sparkles me-1"></i>
<span class="pilltitle">Collectibles</span>
</a>
`;
Nav.appendChild(CollectibleNav);
Type.addEventListener('change', function () { if (window.location.pathname.split('/')[4] === 'collectibles') {
Update(Type.options[Type.selectedIndex].value, SearchBar.value, IsLimited.checked, IsAvailable.checked); Array.from(Nav.children).forEach((element) => {
}); element = element.children[0];
if (!(element === CollectibleNav)) {
SearchBar.addEventListener('change', function () { if (element.classList.contains('active')) {
Update(Type.options[Type.selectedIndex].value, SearchBar.value, IsLimited.checked, IsAvailable.checked); element.classList.remove('active');
});
IsLimited.addEventListener('change', function () {
Update(Type.options[Type.selectedIndex].value, SearchBar.value, IsLimited.checked, IsAvailable.checked);
});
IsAvailable.addEventListener('change', function () {
Update(Type.options[Type.selectedIndex].value, SearchBar.value, IsLimited.checked, IsAvailable.checked);
});
chrome.storage.sync.get(['PolyPlus_ItemWishlist'], function (result) {
let Wishlist = result.PolyPlus_ItemWishlist || [];
console.log('wishlist: ', Wishlist);
Wishlist.forEach((element) => {
let NewItemCard = document.createElement('div');
NewItemCard.classList = 'px-0';
fetch('https://api.polytoria.com/v1/store/:id'.replace(':id', element))
.then((response) => response.json())
.then((data) => {
NewItemCard.innerHTML = ItemCardContents.replace(':ItemID', data.id)
.replace(':ItemThumbnail', data.thumbnail)
.replace(':ItemName', data.name)
.replace(':CreatorID', data.creator.id)
.replace(':CreatorName', data.creator.name);
if (data.isLimited === true) {
NewItemCard.innerHTML = NewItemCard.innerHTML.replace(':LimitedTag', '<div class="ribbon ribbon-limited ribbon-top-right"><span>Limited</span></div>');
} else {
NewItemCard.innerHTML = NewItemCard.innerHTML.replace(':LimitedTag', '');
}
NewItemCard.setAttribute('data-id', data.id);
NewItemCard.setAttribute('data-name', data.name);
NewItemCard.setAttribute('data-type', data.type);
NewItemCard.setAttribute('data-creator', data.creator.name);
NewItemCard.setAttribute('data-limited', data.isLimited);
if (data.isLimited === false) {
NewItemCard.setAttribute('data-price', data.price);
} }
}
});
CollectibleNav.children[0].classList.add('active');
ItemGrid.appendChild(NewItemCard); let HoardedCard = document.createElement('li');
HoardedCard.classList.add('nav-item');
HoardedCard.classList.add('text-center');
HoardedCard.innerHTML = `
<h6 class="section-title mt-3 px-2">
Hoarded Items
</h6>
<div class="card bg-dark mt-2">
<div class="card-body" id="p+hoarded_card"></div>
</div>
`;
Nav.appendChild(HoardedCard);
NewItemCard.getElementsByClassName('polyplus-itemwish-removebtn')[0].addEventListener('click', function () { CollectibleCategory()
let Index = Wishlist.indexOf(parseInt(NewItemCard.getAttribute('data-id'))); }
if (Index === -1) { }
NewItemCard.remove(); });
return; })();
} else { }
Wishlist.splice(Index, 1);
console.log(Wishlist); /*
NewItemCard.remove(); Item Wishlist will be rewritten in v1.2.3
} */
chrome.storage.sync.set({PolyPlus_ItemWishlist: Wishlist, arrayOrder: true}, function () { function ItemWishlist() {
console.log('ItemWishlist successfully saved: ' + ItemWishlist); const ItemGrid = document.getElementsByClassName('itemgrid')[0];
}); const ItemCardContents = `
}); <a href="/store/:ItemID" class="text-reset">
}) <div class="card mb-2">
.catch((error) => { :LimitedTag
console.error('Error:', error); <div class="card-body">
<img src=":ItemThumbnail" class="img-fluid rounded">
</div>
</div>
<h6 class="text-truncate mb-0">
:ItemName
</h6>
</a>
<small class="text-muted d-block mb-1">
by <a href="/users/:CreatorID" class="text-muted">:CreatorName</a>
</small>
<button class="polyplus-itemwish-removebtn btn btn-danger btn-sm" style="width: 100%;">X</button>
`;
ItemGrid.innerHTML = ''
document.getElementsByClassName('pagination')[0].remove()
const Search = document.createElement('div');
Search.classList = 'row';
Search.innerHTML = `
<div class="col-auto">
<select class="form-select" id="polyplus-itemwish-type" style="width: 150px;">
<option value="any">Any</option>
<option value="hat">Hat</option>
<option value="face">Faces</option>
<option value="tool">Tools</option>
<option value="shirt">Shirt</option>
<option value="pants">Pants</option>
</select>
</div>
<div class="col">
<input id="polyplus-itemwish-searchbar" type="text" class="form-control bg-dark" placeholder="Search...">
</div>
<div class="col-auto">
<div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="polyplus-itemwish-isLimited">
<label class="form-check-label" for="polyplus-itemwish-isLimited">
Is Collectible?
<span class="text-muted" style="font-size: 0.65rem; display: block;">Items that are collectible</span>
</label>
</div>
</div>
<div class="col-auto">
<div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="polyplus-itemwish-isAvailable">
<label class="form-check-label" for="polyplus-itemwish-isAvailable">
Is Available?
<span class="text-muted" style="font-size: 0.65rem; display: block; width: 170px;">Items that are equal to or less than the budget (excluding limiteds)</span>
</label>
</div>
</div>
`;
ItemGrid.parentElement.prepend(document.createElement('br'), ItemGrid.parentElement.children[0]);
ItemGrid.parentElement.prepend(Search, ItemGrid.parentElement.children[0]);
let Type = document.getElementById('polyplus-itemwish-type');
let SearchBar = document.getElementById('polyplus-itemwish-searchbar');
let IsLimited = document.getElementById('polyplus-itemwish-isLimited');
let IsAvailable = document.getElementById('polyplus-itemwish-isAvailable');
Type.addEventListener('change', function () {
Update(Type.options[Type.selectedIndex].value, SearchBar.value, IsLimited.checked, IsAvailable.checked);
});
SearchBar.addEventListener('change', function () {
Update(Type.options[Type.selectedIndex].value, SearchBar.value, IsLimited.checked, IsAvailable.checked);
});
IsLimited.addEventListener('change', function () {
Update(Type.options[Type.selectedIndex].value, SearchBar.value, IsLimited.checked, IsAvailable.checked);
});
IsAvailable.addEventListener('change', function () {
Update(Type.options[Type.selectedIndex].value, SearchBar.value, IsLimited.checked, IsAvailable.checked);
});
chrome.storage.sync.get(['PolyPlus_ItemWishlist'], function (result) {
let Wishlist = result.PolyPlus_ItemWishlist || [];
console.log('wishlist: ', Wishlist);
Wishlist.forEach((element) => {
let NewItemCard = document.createElement('div');
NewItemCard.classList = 'px-0';
fetch('https://api.polytoria.com/v1/store/' + element)
.then((response) => response.json())
.then((data) => {
NewItemCard.innerHTML = ItemCardContents.replace(':ItemID', data.id)
.replace(':ItemThumbnail', data.thumbnail)
.replace(':ItemName', data.name)
.replace(':CreatorID', data.creator.id)
.replace(':CreatorName', data.creator.name);
if (data.isLimited === true) {
NewItemCard.innerHTML = NewItemCard.innerHTML.replace(':LimitedTag', '<div class="ribbon ribbon-limited ribbon-top-right"><span>Limited</span></div>');
} else {
NewItemCard.innerHTML = NewItemCard.innerHTML.replace(':LimitedTag', '');
}
NewItemCard.setAttribute('data-id', data.id);
NewItemCard.setAttribute('data-name', data.name);
NewItemCard.setAttribute('data-type', data.type);
NewItemCard.setAttribute('data-creator', data.creator.name);
NewItemCard.setAttribute('data-limited', data.isLimited);
if (data.isLimited === false) {
NewItemCard.setAttribute('data-price', data.price);
}
ItemGrid.appendChild(NewItemCard);
NewItemCard.getElementsByClassName('polyplus-itemwish-removebtn')[0].addEventListener('click', function () {
let Index = Wishlist.indexOf(parseInt(NewItemCard.getAttribute('data-id')));
if (Index === -1) {
NewItemCard.remove();
return;
} else {
Wishlist.splice(Index, 1);
console.log(Wishlist);
NewItemCard.remove();
}
chrome.storage.sync.set({PolyPlus_ItemWishlist: Wishlist, arrayOrder: true}, function () {
console.log('ItemWishlist successfully saved: ' + ItemWishlist);
}); });
});
})
.catch((error) => {
console.error('Error:', error);
}); });
}); });
});
const Update = function(type, query, isLimited, isAvailable) {
let ItemGrid = document.getElementsByClassName('itemgrid')[0];
let BrickBalance = parseInt(JSON.parse(window.localStorage.getItem('p+account_info')).Bricks);
query = query.toLowerCase();
let Results = Array.from(ItemGrid.children);
for (let i = 0; i < Results.length; i++) {
let Show = true;
console.log('type: ', type);
if (!(type === 'any')) {
console.log("isn't any");
if (!(Results[i].getAttribute('data-type') === type)) {
Show = false;
}
}
if (!Results[i].getAttribute('data-name').toLowerCase().startsWith(query)) {
Show = false;
}
if (isLimited === true) {
if (!(Results[i].getAttribute('data-limited') === 'true')) {
Show = false;
}
}
if (isAvailable === true) {
if (!(parseInt(Results[i].getAttribute('data-price')) <= BrickBalance)) {
Show = false;
}
}
if (Show === true) {
Results[i].style.display = 'block';
} else {
Results[i].style.display = 'none';
}
} }
} }
} }
function Update(type, query, isLimited, isAvailable) { async function CollectibleCategory() {
let ItemGrid = document.getElementsByClassName('itemgrid')[0]; ItemGrid.innerHTML = ''
let BrickBalance = parseInt(JSON.parse(window.localStorage.getItem('p+account_info')).Bricks); document.getElementsByClassName('pagination')[0].remove()
query = query.toLowerCase(); const CollectibleItemTypes = [
let Results = Array.from(ItemGrid.children); "hat",
for (let i = 0; i < Results.length; i++) { "face",
let Show = true; "tool"
]
const Collectibles = []
console.log('type: ', type); for (let type of CollectibleItemTypes) {
if (!(type === 'any')) { const InitialInventory = (await (await fetch('https://api.polytoria.com/v1/users/' + UserID + '/inventory?type=' + type + '&limit=100')).json())
console.log("isn't any"); Collectibles.push(...InitialInventory.inventory.filter((x) => x.asset.isLimited === true))
if (!(Results[i].getAttribute('data-type') === type)) {
Show = false; if (InitialInventory.pages > 1) {
if (InitialInventory.pages > 12) {
InitialInventory.pages = 12
} }
}
if (!Results[i].getAttribute('data-name').toLowerCase().startsWith(query)) { for (let page = 2; page < InitialInventory.pages; page++) {
Show = false; const PageResult = (await (await fetch('https://api.polytoria.com/v1/users/' + UserID + '/inventory?type=' + type + '&limit=100&page=' + page)).json())
} Collectibles.push(...PageResult.inventory.filter((x) => x.asset.isLimited === true))
if (isLimited === true) {
if (!(Results[i].getAttribute('data-limited') === 'true')) {
Show = false;
} }
} }
if (isAvailable === true) {
if (!(parseInt(Results[i].getAttribute('data-price')) <= BrickBalance)) {
Show = false;
}
}
if (Show === true) {
Results[i].style.display = 'block';
} else {
Results[i].style.display = 'none';
}
} }
const ItemMultiples = []
Collectibles.forEach(item => {
const Multiple = ItemMultiples.findIndex((x) => x.asset.id === item.asset.id)
if (Multiple !== -1) {
ItemMultiples[Multiple].copies++
ItemMultiples[Multiple].serials.push(item.serial)
} else { ItemMultiples.push({asset: item.asset, copies: 1, serials: [item.serial]}) }
const ItemColumn = document.createElement('div')
ItemColumn.classList = 'px-0'
ItemColumn.innerHTML = `
<a href="/store/${item.asset.id}" class="text-reset">
<div class="card mb-2">
<div class="ribbon ribbon-limited ribbon-top-right"><span>Limited</span></div>
<div class="card-body">
<img src="${item.asset.thumbnail}" class="img-fluid rounded">
<span class="badge bg-dark" style="
font-weight: lighter;
position: absolute;
bottom: 0;
left: 0;
margin: 5px;
">#${item.serial}</span>
</div>
</div>
<h6 class="text-truncate mb-0">
${item.asset.name}
</h6>
</a>
<small class="text-muted d-block mb-1">
by <a href="/u/Polytoria" class="text-muted">Polytoria</a>
</small>
`
ItemGrid.appendChild(ItemColumn)
})
const Hoarded = ItemMultiples.filter((x) => x.copies > 1)
const HoardedCard = document.getElementById('p+hoarded_card')
Hoarded.forEach(item => {
const ListElement = document.createElement('div')
ListElement.classList = 'mb-1 mb-2'
ListElement.innerHTML = `
<a href="/store/${item.asset.id}" style="font-size: 0.8rem; color: #454545 !important;">
<b>${item.asset.name}</b>
</a>
<br>
<span>
${item.copies} copies <i class="fa-solid fa-circle-info" data-bs-toggle="tooltip" data-bs-title="#${item.serials.sort((a, b) => a - b).join(', #')}"></i>
</span>
`
HoardedCard.appendChild(ListElement)
})
_Utilities.InjectResource("registerTooltips")
} }

View file

@ -1,4 +1,6 @@
let UserID = window.location.pathname.split('/')[2]; let Username = window.location.pathname.split('/')[2];
let Blocked = false;
const AvatarRow = document.getElementsByClassName('d-flex flex-row flex-nowrap overflow-x-scroll px-3 px-lg-0 mb-2 mb-lg-0')[0]; const AvatarRow = document.getElementsByClassName('d-flex flex-row flex-nowrap overflow-x-scroll px-3 px-lg-0 mb-2 mb-lg-0')[0];
const AvatarHeading = document.querySelector('.section-title:has(i.fa-user-crown)'); const AvatarHeading = document.querySelector('.section-title:has(i.fa-user-crown)');
@ -6,18 +8,72 @@ var Settings;
var BestFriends; var BestFriends;
let FavoriteBtn; let FavoriteBtn;
let CalculateButton; let CalculateButton;
let AvatarIFrame;
let Utilities; let Utilities;
if (UserID) { if (Username) {
(async () => { (async () => {
UserID = (await (await fetch('https://api.polytoria.com/v1/users/')).json()).id; UserID = (await (await fetch('https://api.'+window.location.hostname+'/v1/users/find?username=' + Username )).json());
Utilities = await import(chrome.runtime.getURL('resources/utils.js')); Utilities = await import(chrome.runtime.getURL('resources/utils.js'));
Utilities = Utilities.default; Utilities = Utilities.default;
chrome.storage.sync.get(['PolyPlus_Settings'], function (result) { if (document.querySelector('.card .fa-ban')) {
Blocked = true;
}
chrome.storage.sync.get(['PolyPlus_Settings'], async function (result) {
Settings = result.PolyPlus_Settings || {}; Settings = result.PolyPlus_Settings || {};
const InfoColumns = document.getElementById('user-stats-card');
const UserIDRow = document.createElement('div')
UserIDRow.classList = 'mb-1 text-start'
UserIDRow.innerHTML = `
<b><i class="fa fa-hashtag text-center d-inline-block" style="width:1.2em"></i> Player ID</b>
<span class="float-end">
${UserID.id} <a id="copy" href="#copy"><i class="fad fa-copy" style="margin-left: 5px;"></i></a>
</span>
`
if (!Blocked) {
InfoColumns.children[0].insertBefore(UserIDRow, InfoColumns.children[0].children[1]);
} else {
if (Settings.MoreBlockedDetailsOn) {
const BlockedCard = document.querySelector('.card-body:has(.fa-ban)')
BlockedCard.appendChild(document.createElement('hr'))
const AccountInfo = (await (await fetch('https://api.'+window.location.hostname+'/v1/users/' + UserID.id )).json());
const AccountCreatedRow = document.createElement('div')
AccountCreatedRow.classList = 'mb-1 text-start'
AccountCreatedRow.innerHTML = `
<b><i class="fad fa-calendar text-center d-inline-block" style="width:1.2em"></i> Join date</b>
<span class="float-end">
${ ["January","February","March","April","May","June","July","August","September","November","December"][new Date(AccountInfo.registeredAt).getMonth() - 1] + " " + new Date(AccountInfo.registeredAt).getDate() + ", " + new Date(AccountInfo.registeredAt).getFullYear() }
</span>
`
BlockedCard.appendChild(UserIDRow)
BlockedCard.appendChild(AccountCreatedRow)
}
}
const CopyButton = UserIDRow.getElementsByTagName('a')[0]
CopyButton.addEventListener('click', function(){
navigator.clipboard
.writeText(UserID.id)
.then(() => {
CopyButton.classList.add('text-success')
CopyButton.children[0].classList = 'fa-duotone fa-circle-check'
CopyButton.children[0].style.marginLeft = '3px'
setTimeout(() => {
CopyButton.classList.remove('text-success')
CopyButton.children[0].classList = 'fad fa-copy'
CopyButton.children[0].style.marginLeft = '5px'
}, 1500);
})
})
if (Settings.IRLPriceWithCurrency && Settings.IRLPriceWithCurrency.Enabled === true) { if (Settings.IRLPriceWithCurrency && Settings.IRLPriceWithCurrency.Enabled === true) {
IRLPrice(); IRLPrice();
} }
@ -49,10 +105,85 @@ if (UserID) {
} }
}); });
} }
AvatarIFrame = document.getElementById('user-avatar-card').getElementsByTagName('iframe')[0]
if (Settings.AvatarDimensionToggleOn === true || 1 === 1) {
const AvatarCard = document.getElementById('user-avatar-card')
const ToggleButton = document.createElement('button')
ToggleButton.classList = 'btn btn-primary btn-sm 3dviewtoggler isactive'
ToggleButton.style = 'position: absolute; right: 15px; margin-top: 20px;'
ToggleButton.innerHTML = '<i class="toggleIcn fad fa-image"></i>'
AvatarCard.getElementsByClassName('position-relative')[0].insertBefore(ToggleButton, AvatarIFrame)
ToggleButton.addEventListener('click', async function(){
if (ToggleButton.children[0].classList.contains('fa-image')) {
if (document.getElementById('polyplus-2davatar')) {
const AvatarImage = document.getElementById('polyplus-2davatar')
AvatarImage.width = AvatarIFrame.offsetWidth
AvatarImage.height = AvatarIFrame.offsetHeight
AvatarImage.style.display = 'block'
AvatarIFrame.style.display = 'none'
ToggleButton.children[0].classList = 'toggleIcn fad fa-360-degrees'
} else {
const AvatarImage = document.createElement('img')
AvatarImage.id = 'polyplus-2davatar'
AvatarImage.width = AvatarIFrame.offsetWidth
AvatarImage.height = AvatarIFrame.offsetHeight
AvatarImage.style.padding = '20px'
const UserDetails = (await (await fetch('https://api.polytoria.com/v1/users/' + UserID.id)).json())
AvatarImage.src = UserDetails.thumbnail.avatar
AvatarIFrame.style.display = 'none'
const CustomBadge = document.querySelector('#user-avatar-card .badge:has(.pi)')
if (CustomBadge === null) {
AvatarCard.children[0].insertBefore(AvatarImage, AvatarCard.getElementsByClassName('user-badges')[0])
} else {
try {
AvatarCard.children[0].insertBefore(AvatarImage, CustomBadge)
} catch(error) {
AvatarCard.children[0].insertBefore(AvatarImage, CustomBadge.parentElement)
}
}
ToggleButton.children[0].classList = 'toggleIcn fad fa-360-degrees'
}
} else {
document.getElementById('polyplus-2davatar').style.display = 'none'
AvatarIFrame.style.display = 'block'
ToggleButton.children[0].classList = 'toggleIcn fad fa-image'
}
})
}
}); });
if (new URLSearchParams(window.location.search).get('anniversary') === '1') {
const JoinDateRow = document.querySelector('#user-stats-card .mb-1:has(.fa-calendar)')
const BirthdayCard = document.createElement('div')
BirthdayCard.classList = 'card card-themed card-player-birthday mb-2'
const AnniversaryNumber = (new Date().getFullYear() - new Date(JoinDateRow.children[1].innerText).getFullYear())
BirthdayCard.innerHTML = `
<div class="card-body">
<div class="fw-semibold text-birthday-gradient">
<i class="fas fa-cake me-1"></i> It's my ${AnniversaryNumber + (AnniversaryNumber % 10 === 1 && AnniversaryNumber % 100 !== 11 ? 'st' : AnniversaryNumber % 10 === 2 && AnniversaryNumber % 100 !== 12 ? 'nd' : AnniversaryNumber % 10 === 3 && AnniversaryNumber % 100 !== 13 ? 'rd' : 'th')} Polytoria anniversary!
</div>
<a href="/inbox/messages/${UserID.id}/compose?anniversaryPreset=${AnniversaryNumber}" class="btn btn-sm btn-outline-light mt-2"><i class="fas fa-hands-clapping me-1"></i> Send ${UserID.username} congrats</a>
</div>
`
document.getElementById('user-avatar-card').parentElement.insertBefore(BirthdayCard, document.getElementById('user-avatar-card'))
JoinDateRow.children[1].innerHTML = '<i class="fas fa-cake"></i> ' + JoinDateRow.children[1].innerHTML
JoinDateRow.children[1].classList.add('text-birthday-gradient')
JoinDateRow.children[1].classList.add('fw-semibold')
}
})(); })();
const AvatarIFrame = document.querySelector('[src^="/ptstatic"]'); if (AvatarIFrame === null) {
AvatarIFrame = document.getElementById('user-avatar-card').getElementsByTagName('iframe')[0]
}
const DropdownMenu = document.getElementsByClassName('dropdown-menu dropdown-menu-right')[0]; const DropdownMenu = document.getElementsByClassName('dropdown-menu dropdown-menu-right')[0];
const CopyItem = document.createElement('a'); const CopyItem = document.createElement('a');
@ -94,7 +225,7 @@ if (UserID) {
alert('Failure to copy sharable 3D avatar URL.'); alert('Failure to copy sharable 3D avatar URL.');
}); });
}); });
} else if (UserID && UserID[0] === '@') { } else if (Username && Username[0] === '@') {
const Username = window.location.pathname.split('/')[2].substring(1); const Username = window.location.pathname.split('/')[2].substring(1);
let Reference = new URLSearchParams(new URL(window.location.href).search); let Reference = new URLSearchParams(new URL(window.location.href).search);
@ -131,7 +262,7 @@ function BestFriends() {
FavoriteBtn = document.createElement('button'); FavoriteBtn = document.createElement('button');
FavoriteBtn.classList = 'btn btn-warning btn-sm ml-2'; FavoriteBtn.classList = 'btn btn-warning btn-sm ml-2';
if (!(BestFriends.length === Utilities.Limits.BestFriends)) { if (!(BestFriends.length === Utilities.Limits.BestFriends)) {
if (Array.isArray(BestFriends) && BestFriends.includes(parseInt(UserID))) { if (Array.isArray(BestFriends) && BestFriends.includes(parseInt(UserID.id))) {
FavoriteBtn.innerText = 'Remove Best Friend Status'; FavoriteBtn.innerText = 'Remove Best Friend Status';
} else { } else {
FavoriteBtn.innerText = 'Best Friend'; FavoriteBtn.innerText = 'Best Friend';
@ -139,9 +270,9 @@ function BestFriends() {
} else { } else {
FavoriteBtn.innerText = 'Remove Best Friend Status (max ' + Utilities.Limits.BestFriends + '/' + Utilities.Limits.BestFriends + ')'; FavoriteBtn.innerText = 'Remove Best Friend Status (max ' + Utilities.Limits.BestFriends + '/' + Utilities.Limits.BestFriends + ')';
} }
if (UserID !== JSON.parse(window.localStorage.getItem('p+account_info')).ID && document.getElementById('add-friend-button').classList.contains('btn-success') === false) { if (UserID.id !== JSON.parse(window.localStorage.getItem('p+account_info')).ID && document.getElementById('add-friend-button').classList.contains('btn-success') === false) {
FavoriteBtn.addEventListener('click', function () { FavoriteBtn.addEventListener('click', function () {
Fav(UserID, FavoriteBtn); Fav(UserID.id, FavoriteBtn);
}); });
} else { } else {
FavoriteBtn.style.display = 'none'; FavoriteBtn.style.display = 'none';
@ -149,25 +280,25 @@ function BestFriends() {
document.querySelectorAll('.section-title.px-3.px-lg-0.mt-3')[0].appendChild(FavoriteBtn); document.querySelectorAll('.section-title.px-3.px-lg-0.mt-3')[0].appendChild(FavoriteBtn);
function Fav(UserID, btn) { function Fav(UserID, btn) {
if (UserID === JSON.parse(window.localStorage.getItem('p+account_info')).ID) { if (UserID.id === JSON.parse(window.localStorage.getItem('p+account_info')).ID) {
return; return;
} }
btn.setAttribute('disabled', 'true'); btn.setAttribute('disabled', 'true');
chrome.storage.sync.get(['PolyPlus_BestFriends'], function (result) { chrome.storage.sync.get(['PolyPlus_BestFriends'], function (result) {
const BestFriends = result.PolyPlus_BestFriends || []; const BestFriends = result.PolyPlus_BestFriends || [];
const index = BestFriends.indexOf(parseInt(UserID)); const index = BestFriends.indexOf(parseInt(UserID.id));
if (index !== -1) { if (index !== -1) {
// Number exists, remove it // Number exists, remove it
BestFriends.splice(index, 1); BestFriends.splice(index, 1);
btn.innerText = 'Best Friend'; btn.innerText = 'Best Friend';
console.log('Number', parseInt(UserID), 'removed from BestFriends'); console.log('Number', parseInt(UserID.id), 'removed from BestFriends');
} else { } else {
// Number doesn't exist, add it // Number doesn't exist, add it
BestFriends.push(parseInt(UserID)); BestFriends.push(parseInt(UserID.id));
btn.innerText = 'Remove Best Friend Status'; btn.innerText = 'Remove Best Friend Status';
console.log('Number', parseInt(UserID), 'added to BestFriends'); console.log('Number', parseInt(UserID.id), 'added to BestFriends');
} }
chrome.storage.sync.set({PolyPlus_BestFriends: BestFriends, arrayOrder: true}, function () { chrome.storage.sync.set({PolyPlus_BestFriends: BestFriends, arrayOrder: true}, function () {
@ -186,7 +317,7 @@ function BestFriends() {
BestFriends = result.PolyPlus_BestFriends || []; BestFriends = result.PolyPlus_BestFriends || [];
if (!(BestFriends.length === Utilities.Limits.BestFriends)) { if (!(BestFriends.length === Utilities.Limits.BestFriends)) {
if (Array.isArray(BestFriends) && BestFriends.includes(parseInt(UserID))) { if (Array.isArray(BestFriends) && BestFriends.includes(parseInt(UserID.id))) {
FavoriteBtn.innerText = 'Remove Best Friend Status'; FavoriteBtn.innerText = 'Remove Best Friend Status';
} else { } else {
FavoriteBtn.innerText = 'Best Friend'; FavoriteBtn.innerText = 'Best Friend';

View file

@ -1,6 +1,6 @@
chrome.storage.sync.get(['PolyPlus_Settings'], function (result) { chrome.storage.sync.get(['PolyPlus_Settings'], function (result) {
if (result.PolyPlus_Settings.MoreSearchFiltersOn === true) { if (result.PolyPlus_Settings.MoreSearchFiltersOn === true) {
const BlockedUsersCard = document.getElementsByClassName('card-body')[1]; const BlockedUsersCard = document.querySelector('.card-body:has([action^="/api/users/"])');
const InputGroup = document.createElement('div'); const InputGroup = document.createElement('div');
InputGroup.classList = 'input-group mb-2'; InputGroup.classList = 'input-group mb-2';
InputGroup.innerHTML = ` InputGroup.innerHTML = `
@ -9,9 +9,8 @@ chrome.storage.sync.get(['PolyPlus_Settings'], function (result) {
`; `;
BlockedUsersCard.insertBefore(InputGroup, BlockedUsersCard.children[0]); BlockedUsersCard.insertBefore(InputGroup, BlockedUsersCard.children[0]);
const SearchBar = document.getElementById('blocked-users-search'); const SearchBar = document.getElementById('blocked-users-search');
const ConfirmBtn = document.getElementById('blocked-users-confirm');
ConfirmBtn.addEventListener('click', function () { SearchBar.addEventListener('input', function () {
SearchBlockedUsers(SearchBar.value); SearchBlockedUsers(SearchBar.value);
}); });

View file

@ -0,0 +1,105 @@
var Settings;
var Utilities;
(async () => {
Utilities = await import(chrome.runtime.getURL('resources/utils.js'));
Utilities = Utilities.default;
chrome.storage.sync.get(['PolyPlus_Settings'], async function(result){
Settings = result.PolyPlus_Settings
if (
Settings.ValueListInfo &&
Settings.ValueListInfo.Enabled == true &&
Settings.ValueListInfo.TradeValuation == true
) {
const ColumnLabels = [
"name",
"short",
"value",
"type",
"trend",
"demand",
"tags"
]
const TagColors = {
"Projected": "warning",
"Hoarded": "success",
"Rare": "primary",
"Freaky": "danger"
}
/*
Table to JSON function (slightly modified for my use-case)
https://stackoverflow.com/questions/9927126/how-to-convert-the-following-table-to-json-with-javascript#answer-60196347
*/
const ExtractTableJSON = function(table) {
var data = [];
for (var i = 1; i < table.rows.length; i++) {
var tableRow = table.rows[i];
var rowData = {
tags: []
};
for (var j = 0; j < tableRow.cells.length; j++) {
let Value = tableRow.cells[j].children[0].children[0].innerText;
if (ColumnLabels[j] === "name") {
const LinkValue = tableRow.cells[j].getElementsByTagName('a')[0]
if (LinkValue) {
rowData.id = LinkValue.href.split('https://www.google.com/url?q=')[1].split('&')[0].split('/')[4]
}
}
if (ColumnLabels[j] === "tags") {
Array.from(tableRow.cells[j].children).forEach(tag => {
/*
The regex for the emoji character codes replacement was made by AI, such a time saver lol
*/
rowData.tags.push(tag.children[0].innerHTML.replace(/\s/g,'').replace(/([\u2700-\u27BF]|[\uE000-\uF8FF]|\uD83C[\uDC00-\uDFFF]|\uD83D[\uDC00-\uDFFF]|[\u2011-\u26FF]|\uD83E[\uDD10-\uDDFF])/g, ''))
})
} else {
rowData[ColumnLabels[j]] = Value
}
}
data.push(rowData);
}
return data;
}
const ValueListDocument = new DOMParser().parseFromString(await (await fetch('https://docs.google.com/feeds/download/documents/Export?exportFormat=html&format=html&id=1W7JN74MU-9Dbd-9xNnjxE18hQVBPXWuwjK5DGSnuQR4')).text(), 'text/html')
const GetTagColor = function(label) {
if (TagColors[label] !== undefined) {
return TagColors[label]
} else if (TagColors[label.substring(1)] !== undefined) {
return TagColors[label.substring(1)]
} else {
return 'dark'
}
}
const ValueJSON = ExtractTableJSON(ValueListDocument.getElementsByTagName('table')[0])
for (let card of Array.from(document.querySelectorAll('.card:has(a[href^="/store"])'))) {
const ItemValuations = Array.from(card.querySelectorAll('a[href^="/store"]')).map((item) => ValueJSON.filter((x) => x.id == item.getAttribute('href').split('/')[2])[0]||null).filter((x)=>x!=null)
if (ItemValuations.length > 0) {
/* this code is so bad I never want to look at it again */
const QuestionMarkTooltip = document.createElement('i')
QuestionMarkTooltip.classList = 'fa fa-question-circle'
QuestionMarkTooltip.setAttribute('data-bs-toggle', 'tooltip')
QuestionMarkTooltip.setAttribute('data-bs-html', 'true')
QuestionMarkTooltip.setAttribute('data-bs-title', ItemValuations.map((item, i) => `
<small class="text-muted" style="font-size: 0.65rem">"${item.short}" | ${item.trend}</small><br>
<b style="text-align: left !important;">${item.name}</b><br>
${ item.tags.map((x) => `
<span class="badge bg-${ GetTagColor(x) }">${x}</span>
`).join('')}
`).join('<hr class="mt-2 mb-1">'))
card.getElementsByClassName('card-header')[0].appendChild(QuestionMarkTooltip)
}
}
Utilities.InjectResource("registerTooltips")
}
});
})();

View file

@ -77,9 +77,44 @@ const DefaultSettings = {
Enabled: false, Enabled: false,
Banners: true, Banners: true,
Rectangles: true Rectangles: true
} },
UploadMultipleDecals: true,
GD_ServerBalanceOn: true,
AvatarDimensionToggleOn: true,
TheGreatDivide: {
Enabled: true,
UnbalancedIndicatorOn: true,
MVPUserIndicatorOn: true,
UserStatsOn: true,
LeaderboardsOn: true
},
CollectibleInventoryCatOn: true,
ValueListInfo: {
Enabled: true,
ItemValuation: true,
TradeValuation: true
},
ImprovedAchievements: {
Enabled: true,
ProgressBarOn: true,
PercentageOn: true,
OpacityOn: true
},
ReaddCopyablePlacesOn: true,
TimePlayedOn: true,
HomeJoinFriendsButtonOn: true,
ImprovedPlaceManagement: {
Enabled: true,
QuickActivityToggleOn: true,
PlaceFileDownloadOn: true,
MultiWhitelistOn: true,
ClearWhitelistOn: true
},
MoreBlockedDetailsOn: true,
AssetDesignerCreditOn: true
} }
// ON EXTENSION INSTALL / RELOAD
chrome.runtime.onInstalled.addListener(() => { chrome.runtime.onInstalled.addListener(() => {
chrome.storage.sync.get(['PolyPlus_Settings'], function(result){ chrome.storage.sync.get(['PolyPlus_Settings'], function(result){
const MergedSettings = MergeObjects((result.PolyPlus_Settings || DefaultSettings), DefaultSettings) const MergedSettings = MergeObjects((result.PolyPlus_Settings || DefaultSettings), DefaultSettings)
@ -89,94 +124,383 @@ chrome.runtime.onInstalled.addListener(() => {
}) })
}); });
let RecordingTimePlayed = false
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
if (request.action === 'reload') {
chrome.runtime.reload();
} else if (request.action === 'greatdivide_stats') {
chrome.storage.local.get(['PolyPlus_GreatDivideStats'], async function(result){
const Cache = (result['PolyPlus_GreatDivideStats']||{[request.userID]:undefined})
// cache for 5 minutes
if (Cache[request.userID] === undefined || (new Date().getTime() - Cache[request.userID].requested > 300000)) {
let Statistics = (await (await fetch('https://stats.silly.mom/player_stats?id=' + request.userID)).json()).results
if (Statistics !== null) {
Statistics = Statistics[0]
}
Cache[request.userID] = {
data: Statistics,
requested: new Date().getTime()
}
chrome.storage.local.set({['PolyPlus_GreatDivideStats']: Cache}, function(){})
}
chrome.tabs.query({ active: true, currentWindow: true }, function(tabs){
chrome.scripting
.executeScript({
target: {tabId: tabs[0].id},
func: LoadStats,
args: [Cache[request.userID].data]
})
})
})
const LoadStats = function(stats){
const GreatDivideCard = document.getElementById('p+greatdivide_card')
if (stats !== null) {
let KDR = (stats.Kills / stats.Deaths)
if (isNaN(KDR)) {
KDR = "N/A"
} else {
KDR = KDR.toFixed(4)
}
if (stats.Team === 'phantoms') {
GreatDivideCard.parentElement.style.backgroundImage = 'linear-gradient(rgba(0.7, 0.7, 0.7, 0.7), rgba(0.7, 0.7, 0.7, 0.7)), url("https://c0.ptacdn.com/assets/N3DH4x5a6iW7raaQ-3lwHpRHHpWShdXc.png")';
GreatDivideCard.parentElement.style.borderColor = '';
GreatDivideCard.parentElement.style.border = '1.25px solid blue !important';
} else {
GreatDivideCard.parentElement.style.backgroundImage = 'linear-gradient(rgba(0.7, 0.7, 0.7, 0.7), rgba(0.7, 0.7, 0.7, 0.7)), url("https://c0.ptacdn.com/assets/1HXpaoDLHJo2rrvwwxqJEDWvDZ6BgvSE.png")';
GreatDivideCard.parentElement.style.borderColor = '';
GreatDivideCard.parentElement.style.border = '1.25px solid green !important';
}
GreatDivideCard.innerHTML = `
<div class="mb-1">
<b>
<i class="fa-duotone fa-eye text-center d-inline-block" style="width:1.2em"></i>
Last Round Seen
</b>
<span class="float-end">
${ (stats.LastRoundSeen === 0) ? '-' : stats.LastRoundSeen }
</span>
</div>
<hr class="mb-3 mt-2">
<div class="mb-1">
<b>
<i class="fa-duotone fa-swords text-center d-inline-block" style="width:1.2em"></i>
Kills
</b>
<span class="float-end">
${stats.Kills.toLocaleString()} <small class="text-muted" style="font-size: 0.8rem;">(${stats.UniqueKills.toLocaleString()} unique)</small>
</span>
</div>
<div class="mb-1">
<b>
<i class="fa-duotone fa-skull text-center d-inline-block" style="width:1.2em"></i>
Deaths
</b>
<span class="float-end">
${stats.Deaths.toLocaleString()}
</span>
</div>
<div class="mb-1">
<b>
<i class="fa-solid fa-percent text-center d-inline-block" style="width:1.2em"></i>
Kill Death Ratio
</b>
<span class="float-end ${ (!isNaN(KDR) && KDR > 1) ? 'text-success' : (!isNaN(KDR) && KDR !== 0) ? 'text-danger' : '' }">
${KDR} <i class="fa-solid fa-circle-info" data-bs-toggle="tooltip" data-bs-title="KDR is a user's kills divided by the amount of times they have died. If their KDR is above 1, they are making a positive contribution. If their KDR is less than 1, that means they die more than they kill."></i>
</span>
</div>
<div class="mb-1">
<b>
<i class="fa-duotone fa-hundred-points text-center d-inline-block" style="width:1.2em"></i>
Points Scored
</b>
<span class="float-end">
${stats.PointsScored.toLocaleString()}
</span>
</div>
<div class="mb-1">
<b>
<i class="fa-solid fa-money-bill-wave text-center d-inline-block" style="width:1.2em"></i>
Cash Earned
</b>
<span class="float-end">
${stats.CashEarned.toLocaleString()}
</span>
</div>
<div class="mb-1">
<b>
<i class="fa-duotone fa-flag text-center d-inline-block" style="width:1.2em"></i>
Flags Captured
</b>
<span class="float-end">
${stats.FlagsCaptured} (${stats.FlagsReturned} returned)
</span>
</div>
<div>
<b>
<i class="fa-solid fa-box-open text-center d-inline-block" style="width:1.2em"></i>
Airdrops Collected
</b>
<span class="float-end">
${stats.AirdropsCollected}
</span>
</div>
<hr class="mb-3 mt-2">
<div class="mb-1">
<b>
<i class="fa-solid fa-chart-pyramid text-center d-inline-block" style="width:1.2em"></i>
Monoliths Destroyed
</b>
<span class="float-end">
${stats.ObelisksDestroyed}
</span>
</div>
<div class="mb-1">
<b>
<i class="fa-duotone fa-block-question text-center d-inline-block" style="width:1.2em"></i>
Blocks Placed
</b>
<span class="float-end">
${stats.BlocksPlaced} (${stats.BlocksDestroyed} destroyed)
</span>
</div>
<div class="mb-1">
<b>
<i class="fa-duotone fa-head-side-brain text-center d-inline-block" style="width:1.2em"></i>
Headshots
</b>
<span class="float-end">
${stats.Headshots}
</span>
</div>
`
const Script = document.createElement('script');
Script.setAttribute('type', 'text/javascript');
Script.setAttribute('src', chrome.runtime.getURL('resources/registerTooltips.js'));
Script.addEventListener('load', function () {
Script.remove();
});
document.body.appendChild(Script);
} else {
GreatDivideCard.classList.add('text-center', 'py-5')
GreatDivideCard.innerHTML = `
<h1 class="display-3"><i class="fa-solid fa-face-thinking"></i></h1>
<h5> Not Drafted </h5>
<p class="mb-0">This user didn't participate in The Great Divide.</p>
`
}
}
return true
} else if (request.action === 'start_time_played') {
if (RecordingTimePlayed === true) {
console.log('Time Played: Already Started Interval')
return
}
RecordingTimePlayed = true
chrome.storage.sync.get(['PolyPlus_TimePlayed'], function(result){
console.log('Time Played: Start Interval')
const Playtime = result.PolyPlus_TimePlayed || {
[request.placeID]: 0
};
let LoadedIn = false
const TimePlayedInterval = setInterval(async () => {
console.log('Time Played: Run Check')
const PlaceStatus = (await (await fetch('https://api.polytoria.com/v1/users/' + request.userID)).json()).playing
if (PlaceStatus === null) {
console.log('Time Played: Not Playing Anything')
if (LoadedIn === true) {
console.log('Time Played: End Interval')
clearInterval(TimePlayedInterval)
}
} else {
LoadedIn = true
if (!Playtime[PlaceStatus.placeID]) {
Playtime[PlaceStatus.placeID] = 0
}
Playtime[PlaceStatus.placeID] += 5
console.log('Time Played: Time Increase: ', new Date(Playtime[PlaceStatus.placeID] * 1000).toISOString().slice(11, 19), PlaceStatus)
chrome.storage.sync.set({'PolyPlus_TimePlayed': Playtime}, function(){
console.log('Time Played: Saved Playtime')
})
}
}, 5000);
})
} else if (request.action == "item_valuation") {
chrome.storage.local.get(['PolyPlus_ItemValuationData'], async function(result){
const Cache = (result['PolyPlus_ItemValuationData']||{[request.itemID]:undefined})
// cache for 5 minutes
if (Cache[request.itemID] === undefined || (new Date().getTime() - Cache[request.itemID].requested > 300000)) {
let ValueDetails = (await (await fetch('https://polytoria.trade/api/trpc/getItem?batch=1&input={"0":' + request.itemID + '}',{mode:'no-cors'})).json())
if (ValueDetails.result.length > 0) {
ValueDetails = ValueDetails[0].result.data
}
Cache[request.itemID] = {
data: ValueDetails,
requested: new Date().getTime()
}
chrome.storage.local.set({['PolyPlus_GreatDivideStats']: Cache}, function(){})
}
chrome.tabs.query({ active: true, currentWindow: true }, function(tabs){
chrome.scripting
.executeScript({
target: {tabId: tabs[0].id},
func: LoadValuation,
args: [Cache[request.itemID].data]
})
})
})
const LoadValuation = async function(valuation) {
const GetTagColor = function(label) {
if (TagColors[label] !== undefined) {
return TagColors[label]
} else if (TagColors[label.substring(1)] !== undefined) {
return TagColors[label.substring(1)]
} else {
return 'dark'
}
}
const TagColors = {
"Projected": "warning",
"Hoarded": "success",
"Rare": "primary",
"Freaky": "danger"
}
//const ValueDetails = (await (await fetch('https://polytoria.trade/api/trpc/getItem?batch=1&input={"0":' + ItemID + '}')).json())
if (valuation !== undefined) {
ValueCard.innerHTML = `
<div class="mb-1">
<b class="text-success">
<i class="pi pi-brick" style="width:1.2em"></i>
Value
</b>
<span class="float-end">
${valuation.value}
</span>
</div>
<div class="mb-1">
<b class="text-primary"">
<i class="pi" style="width:1.2em">%</i>
Trend
</b>
<span class="float-end">
${valuation.trend}
</span>
</div>
<div class="mb-1">
<b>
<i class="fa-duotone fa-triangle" style="width:1.2em"></i>
Valuation Type
</b>
<span class="float-end">
${valuation.type}
</span>
</div>
<div class="mb-1">
<b>
<i class="fa-duotone fa-hand-wave" style="width:1.2em"></i>
Shorthand
</b>
<span class="float-end">
${valuation.short}
</span>
</div>
<div class="d-flex" style="gap: 5px;">
${ ValueDetails.tags.map((x) => `
<span class="badge bg-${ GetTagColor(x) }">${x}</span>
`).join('')}
</div>
`
} else {
ValueCard.innerHTML = `
There is no evaluation for this item at this time.
`
}
}
}
});
// WHEN CLICKING ON EXTENSION ICON OPEN THE SETTINGS PAGE // WHEN CLICKING ON EXTENSION ICON OPEN THE SETTINGS PAGE
chrome.action.onClicked.addListener((tab) => { chrome.action.onClicked.addListener((tab) => {
chrome.tabs.create({active: true, url: SettingsURL}); chrome.tabs.create({active: true, url: SettingsURL});
}); });
// REGISTER AN ALARM FOR DAILY UPDATE CHECK
chrome.alarms.create('PolyPlus-UpdateCheck', {
when: Date.now() + GetNext12PM()
});
function GetNext12PM() {
const Now = new Date();
const Next = new Date();
Next.setHours(12, 0, 0, 0);
if (Now.getHours() >= 12) {
Next.setDate(Next.getDate() + 1);
}
return Next - Now;
}
// HANDLE ALARMS FIRING
chrome.alarms.onAlarm.addListener(function (alarm) {
if (alarm.name === 'PolyPlus-UpdateCheck') {
RunUpdateNotifier();
}
});
function RunUpdateNotifier() {
chrome.storage.local.get(['PolyPlus_LiveVersion', 'PolyPlus_OutOfDate', 'PolyPlus_SkipUpdate'], function (result) {
const OutOfDate = result.PolyPlus_OutOfDate || false;
const SkipUpdate = result.PolyPlus_SkipUpdate || null;
const LiveVersion = result.PolyPlus_LiveVersion || Manifest.version;
if (OutOfDate !== true && SkipUpdate !== LiveVersion) {
fetch('https://polyplus.vercel.app/data/version.json')
.then((response) => {
if (!response.ok) {
throw new Error('Network not ok');
}
return response.json();
})
.then((data) => {
chrome.storage.local.set({PolyPlus_LiveVersion: data.version}, function () {
console.log('Cached live version');
});
if (data.version > Manifest.version) {
chrome.storage.local.set({PolyPlus_OutOfDate: true, PolyPlus_ReleaseNotes: data.releaseNotes}, function () {
console.log('Cached update notifier result');
});
chrome.notifications.create(
'',
{
type: 'basic',
iconUrl: chrome.runtime.getURL('icon.png'),
title: 'New Update Available',
message: 'A new update is available for Poly+! (v' + data.version + ')'
},
function (notificationID) {
chrome.notifications.onClicked.addListener(function (id) {
if (id === notificationID) {
chrome.tabs.create({url: 'https://github.com/IndexingGitHub/PolyPlus/releases', active: true});
chrome.notifications.clear(notificationID);
}
});
}
);
}
})
.catch((error) => {
console.log(error);
});
}
});
}
chrome.contextMenus.removeAll(function () { chrome.contextMenus.removeAll(function () {
chrome.contextMenus.create({
title: 'Run Update Notifier',
id: 'PolyPlus-RunUpdateNotifier',
contexts: ['all'],
documentUrlPatterns: ['https://polytoria.com/my/settings/polyplus*']
});
// COPY ASSET ID CONTEXT MENU ITEM REGISTRATION // COPY ASSET ID CONTEXT MENU ITEM REGISTRATION
/*
const AssetTypes = ["Place", "User", "Item", "Guild"]
AssetTypes.forEach(type => {
chrome.contextMenus.create({
title: 'Copy ' + type + ' ID',
id: 'PolyPlus-Copy' + type + 'ID',
contexts: ['link'],
documentUrlPatterns: ['https://polytoria.com/*', SettingsURL],
targetUrlPatterns: [
'https://polytoria.com/places/**'
]
});
})
*/
chrome.contextMenus.create({ chrome.contextMenus.create({
title: 'Copy Asset ID', title: 'Copy Place ID',
id: 'PolyPlus-CopyID', id: 'PolyPlus-CopyPlaceID',
contexts: ['link'], contexts: ['link'],
documentUrlPatterns: ['https://polytoria.com/*', SettingsURL], documentUrlPatterns: ['https://polytoria.com/*', SettingsURL],
targetUrlPatterns: ['https://polytoria.com/places/**', 'https://polytoria.com/users/**', 'https://polytoria.com/u/**', 'https://polytoria.com/store/**', 'https://polytoria.com/guilds/**'] targetUrlPatterns: [
'https://polytoria.com/places/**'
]
});
chrome.contextMenus.create({
title: 'Copy User ID',
id: 'PolyPlus-CopyUserID',
contexts: ['link'],
documentUrlPatterns: ['https://polytoria.com/*', SettingsURL],
targetUrlPatterns: [
'https://polytoria.com/users/**',
'https://polytoria.com/u/**'
]
});
chrome.contextMenus.create({
title: 'Copy Item ID',
id: 'PolyPlus-CopyItemID',
contexts: ['link'],
documentUrlPatterns: ['https://polytoria.com/*', SettingsURL],
targetUrlPatterns: [
'https://polytoria.com/store/**'
]
});
chrome.contextMenus.create({
title: 'Copy Guild ID',
id: 'PolyPlus-CopyGuildID',
contexts: ['link'],
documentUrlPatterns: ['https://polytoria.com/*', SettingsURL],
targetUrlPatterns: [
'https://polytoria.com/guilds/**'
]
});
chrome.contextMenus.create({
title: 'Copy Thread ID',
id: 'PolyPlus-CopyThreadID',
contexts: ['link'],
documentUrlPatterns: ['https://polytoria.com/*', SettingsURL],
targetUrlPatterns: [
'https://polytoria.com/forum/post/**'
]
}); });
// COPY AVATAR HASH CONTEXT MENU ITEM REGISTRATION // COPY AVATAR HASH CONTEXT MENU ITEM REGISTRATION
@ -185,19 +509,30 @@ chrome.contextMenus.removeAll(function () {
id: 'PolyPlus-CopyAvatarHash', id: 'PolyPlus-CopyAvatarHash',
contexts: ['image'], contexts: ['image'],
documentUrlPatterns: ['https://polytoria.com/*', SettingsURL], documentUrlPatterns: ['https://polytoria.com/*', SettingsURL],
targetUrlPatterns: ['https://c0.ptacdn.com/thumbnails/avatars/**', 'https://c0.ptacdn.com/thumbnails/avatars/**'] targetUrlPatterns: [
'https://c0.ptacdn.com/thumbnails/avatars/**'
]
}); });
}); });
// HANDLE CONTEXT MENU ITEMS // HANDLE CONTEXT MENU ITEMS
chrome.contextMenus.onClicked.addListener(async function (info, tab) { chrome.contextMenus.onClicked.addListener(async function (info, tab) {
if (info.menuItemId === 'PolyPlus-CopyID') { if (["CopyPlaceID", "CopyUserID", "CopyItemID", "CopyGuildID"].indexOf(info.menuItemId.split('-')[1]) !== -1) {
console.log(info.linkUrl.split('/')[3]);
let ID = info.linkUrl.split('/')[4]; let ID = info.linkUrl.split('/')[4];
if (info.linkUrl.split('/')[3] === 'u') { if (info.linkUrl.split('/')[3] === 'u') {
ID = (await (await fetch('https://api.polytoria.com/v1/users/find?username=' + info.linkUrl.split('/')[4])).json()).id; ID = (await (await fetch('https://api.polytoria.com/v1/users/find?username=' + info.linkUrl.split('/')[4])).json()).id;
} }
console.log(ID); chrome.scripting
.executeScript({
target: {tabId: tab.id},
func: CopyAssetID,
args: [ID]
})
.then(() => console.log('Copied ID!'));
}
if (info.menuItemId === 'PolyPlus-CopyThreadID') {
let ID = info.linkUrl.split('/')[5];
chrome.scripting chrome.scripting
.executeScript({ .executeScript({
target: {tabId: tab.id}, target: {tabId: tab.id},
@ -235,18 +570,18 @@ chrome.tabs.onActivated.addListener(function (info){
}); });
function CheckIfScriptApplies(url) { function CheckIfScriptApplies(url) {
return Manifest.content_scripts.forEach(script => { const matches = Manifest.content_scripts.map(script => {
COMMENT
if (matchesUrl(script.matches, url)) {
return true
}
script.matches.forEach(match => { script.matches.forEach(match => {
if (url.startsWith(match.replaceAll('*', ''))) { console.log(url, match, url.startsWith(match))
if (url.startsWith(match)) {
return true return true
} } else {
return false
}
}) })
}) })
return matches
} }
function matchesUrl(patterns, url) { function matchesUrl(patterns, url) {
@ -259,11 +594,8 @@ function matchesUrl(patterns, url) {
function CopyAssetID(id) { function CopyAssetID(id) {
navigator.clipboard navigator.clipboard
.writeText(id) .writeText(id)
.then(() => { .catch((err) => {
alert('Successfully copied ID!'); alert('Failure to copy ID.', err);
})
.catch(() => {
alert('Failure to copy ID.');
}); });
} }
@ -278,6 +610,15 @@ function CopyAvatarHash(hash) {
}); });
} }
function OpenSweetAlert2Modal(icon, title, text) {
console.log(window, window.Swal, window.bootstrap)
window.Swal.fire({
icon: icon,
title: title,
text: text
})
}
// MergeObjects function was written by ChatGPT cause I was lazy and it was awhile ago // MergeObjects function was written by ChatGPT cause I was lazy and it was awhile ago
function MergeObjects(obj1, obj2) { function MergeObjects(obj1, obj2) {
var mergedObj = {}; var mergedObj = {};

View file

@ -0,0 +1,61 @@
const AssetGrid = document.getElementById('assets')
const AssetObserver = new MutationObserver(async function (list) {
const SelectedTab = document.getElementsByClassName('nav-link active')[0].getAttribute('data-category')
if (SelectedTab === "audio") {
for (const record of list) {
for (const audio of record.addedNodes) {
if (audio.tagName === 'DIV') {
const PlayButton = document.createElement('button')
PlayButton.classList = 'btn btn-primary btn-sm'
PlayButton.style = 'position: absolute; bottom: 0; right: 0; margin: 5px; margin-bottom: 55px; z-index: 2000;'
PlayButton.innerHTML = '<i class="fa-solid fa-play"></i>'
audio.getElementsByTagName('a')[0].parentElement.insertBefore(PlayButton, audio.getElementsByTagName('a')[0])
audio.children[0].style.position = 'relative'
let AudioElement = null
let Playing = false
PlayButton.addEventListener('click', async function(){
if (!Playing) {
if (AudioElement === null) {
PlayButton.innerHTML = '<div class="spinner-border text-light" role="status" style="--bs-spinner-width: 15px; --bs-spinner-height: 15px; --bs-spinner-border-width: 2px; vertical-align: middle; text-align: center;"><span class="sr-only">Loading...</span></div>'
const AudioURL = (await (await fetch('https://api.polytoria.com/v1/assets/serve-audio/' + audio.querySelector('a[href^="/store"]').getAttribute('href').split('/')[2])).json())
if (AudioURL.success) {
AudioElement = new Audio(AudioURL.url)
} else {
PlayButton.remove();
}
AudioElement.addEventListener("canplaythrough", (event) => {
Playing = true
AudioElement.play();
PlayButton.innerHTML = '<i class="fa-duotone fa-solid fa-play-pause"></i>'
PlayButton.classList = 'btn btn-warning btn-sm'
});
} else {
Playing = true
AudioElement.play();
PlayButton.innerHTML = '<i class="fa-duotone fa-solid fa-play-pause"></i>'
PlayButton.classList = 'btn btn-warning btn-sm'
}
AudioElement.addEventListener("ended", function() {
Playing = false
PlayButton.innerHTML = '<i class="fa-solid fa-play"></i>'
PlayButton.classList = 'btn btn-primary btn-sm'
})
} else {
Playing = false
AudioElement.pause()
PlayButton.innerHTML = '<i class="fa-solid fa-play"></i>'
PlayButton.classList = 'btn btn-primary btn-sm'
}
})
}
}
AssetObserver.observe(AssetGrid, {attributes: false, childList: true, subtree: false});
}
}
});
AssetObserver.observe(AssetGrid, {attributes: false, childList: true, subtree: false});

85
js/create/image.js Normal file
View file

@ -0,0 +1,85 @@
/*
credit to goldenretriveryt on Polytoria for making the multi-decal uploading feature
*/
(async () => {
var Settings = [];
chrome.storage.sync.get(["PolyPlus_Settings"], async function (result) {
let Utilities = (await import(chrome.runtime.getURL("resources/utils.js")))
.default;
Settings = result.PolyPlus_Settings || {
UploadMultipleDecals: true,
};
if (Settings.UploadMultipleDecals === true) {
UploadMultipleDecals();
}
async function UploadMultipleDecals() {
const fileInput = document.querySelector("#file");
fileInput.setAttribute("multiple", "true");
const submitBtn = document.querySelector(
"form[action^=\"/create\"] button[type=\"submit\"]"
);
const nameInput = document.querySelector("form[action^=\"/create\"] input[name=\"name\"]")
console.log(submitBtn)
submitBtn.addEventListener("click", async function (ev) {
ev.preventDefault();
submitBtn.disabled = true;
submitBtn.textContent =
"Uploading... (0/" + fileInput.files.length + ")";
const files = fileInput.files;
// we cant submit multiple files to the API, so we create a request for each file
let i = 0;
const uploadFile = async (file, index) => {
const formData = new FormData();
formData.append("file", file);
if (nameInput.value !== "") {
formData.append("name", nameInput.value)
}
try {
const response = await Utilities.RatelimitRepeatingFetch(
"https://polytoria.com/create/upload-decal",
{
method: "POST",
body: formData,
}
);
if (response.ok) {
i++;
submitBtn.textContent =
"Uploading... (" + i + "/" + fileInput.files.length + ")";
} else {
throw new Error(response.status);
}
} catch (err) {
console.error(err);
submitBtn.textContent =
"File " + index + " failed to upload: " + err;
submitBtn.disabled = false;
throw err; // re-throw the error to stop further processing
}
};
const uploadFiles = async () => {
try {
await Promise.all(Array.from(files).map(uploadFile));
submitBtn.textContent = "Upload";
submitBtn.disabled = false;
window.location.reload();
} catch (err) {
console.error("Some files failed to upload:", err);
}
};
uploadFiles();
});
}
});
})();

264
js/create/place-access.js Executable file
View file

@ -0,0 +1,264 @@
const PlaceID = window.location.pathname.split('/')[3];
const Form = document.querySelector('form[action="/create/place/update"]');
var Settings;
var PlaceData = null;
let Utilities;
!(async () => {
Utilities = (await import(chrome.runtime.getURL('resources/utils.js')))
.default;
chrome.storage.sync.get(['PolyPlus_Settings'], function(result){
Settings = result.PolyPlus_Settings || Utilities.DefaultSettings;
if (Settings.ImprovedPlaceManagement.Enabled) {
if (Settings.ImprovedPlaceManagement.QuickActivityToggleOn && Settings.ImprovedPlaceManagement.QuickActivityToggleOn === true) {
ActivityToggle();
}
if (Settings.ImprovedPlaceManagement.PlaceFileDownloadOn && Settings.ImprovedPlaceManagement.PlaceFileDownloadOn === true) {
CopyOwnedPlace();
}
if (Settings.ImprovedPlaceManagement.MultiWhitelistOn && Settings.ImprovedPlaceManagement.MultiWhitelistOn === true) {
MultiWhitelist();
}
if (Settings.ImprovedPlaceManagement.ClearWhitelistOn && Settings.ImprovedPlaceManagement.ClearWhitelistOn === true) {
ClearWhitelist();
}
}
})
})();
async function ActivityToggle() {
if (PlaceData === null) {
PlaceData = await fetch('https://api.polytoria.com/v1/places/' + PlaceID);
PlaceData = await PlaceData.json();
}
let Status = PlaceData.isActive;
const DIV = document.createElement('div');
DIV.classList = 'form-group mt-4';
DIV.innerHTML = `
<label class="mb-2">
<h5 class="mb-0">Toggle Activity</h5>
<small class="text-muted">Make your place active or inactive (currently <span id="p+current_place_activity">${Status === true ? 'active' : 'inactive'}</span>).</small>
</label>
<br>
`;
Form.insertBefore(DIV, Form.children[Form.children.length - 1]);
const ActivityBtn = document.createElement('button');
ActivityBtn.type = 'button';
ActivityBtn.classList = 'btn ' + (Status === true ? 'btn-danger' : 'btn-success');
ActivityBtn.innerText = 'Set ' + (Status === true ? 'Private' : 'Public');
DIV.appendChild(ActivityBtn);
ActivityBtn.addEventListener('click', async function () {
const Toggle = (await (await fetch(`https://polytoria.com/api/places/${PlaceID}/toggle-active`,{ method: 'POST' })).json())
if (Toggle.success) {
Status = Toggle.isActive;
ActivityBtn.innerText = 'Set ' + (Status === true ? 'Private' : 'Public');
ActivityBtn.classList = 'btn ' + (Status === true ? 'btn-danger' : 'btn-success');
Status === true ? document.getElementById('p+current_place_activity').innerText = 'active' : document.getElementById('p+current_place_activity').innerText = 'inactive'
} else {
//chrome.runtime.sendMessage({ action: "sweetalert2", icon: "error", title: "Error", text: Toggle.message });
alert(Toggle.message)
}
});
}
function RequestGameProfile() {
const Div = document.createElement('div');
Div.classList = 'card mt-4';
Div.innerHTML = `
<div class="card-body">
<input type="text" class="form-control bg-dark mb-2" placeholder="Game Title..">
<input type="color" class="form-control bg-dark mb-2" placeholder="Background Color..">
<input type="color" class="form-control bg-dark mb-2" placeholder="Accent Color..">
<input type="color" class="form-control bg-dark mb-2" placeholder="Secondary Color..">
<input type="color" class="form-control bg-dark mb-2" placeholder="Card Background Color..">
<input type="color" class="form-control bg-dark mb-2" placeholder="Text Color..">
<button type="button" class="btn btn-primary">Submit Request</button>
</div>
`;
Form.insertBefore(Div, Form.children[Form.children.length - 1]);
const SubmitBtn = Div.getElementsByTagName('button')[0];
SubmitBtn.addEventListener('click', function () {
const CardBody = Div.children[0];
const Result = {
gameTitle: CardBody.children[0].value,
bg: CardBody.children[1].value,
accent: CardBody.children[2].value,
secondary: CardBody.children[3].value,
cardBg: CardBody.children[4].value,
text: CardBody.children[5].value
};
window.location.href = 'https://polyplus.vercel.app/app/game-profile.html?gameId=' + PlaceID + '&profile=' + encodeURIComponent(btoa(JSON.stringify(Result)));
});
}
async function CopyOwnedPlace() {
if (PlaceData === null) {
PlaceData = await fetch('https://api.polytoria.com/v1/places/' + PlaceID);
PlaceData = await PlaceData.json();
}
if (PlaceData.creator.id !== parseInt(JSON.parse(window.localStorage.getItem('p+account_info')).ID)) {
return
}
const DIV = document.createElement('div');
DIV.classList = 'form-group mt-4';
DIV.innerHTML = `
<label class="mb-2">
<h5 class="mb-0">Download <code style="color: orange;">.poly</code> File</h5>
<small class="text-muted">Quickly download your place from the site!</small>
</label>
<br>
<button type="button" class="btn btn-primary">Download</button>
`;
Form.insertBefore(DIV, Form.children[Form.children.length - 1]);
const DownloadButton = DIV.getElementsByTagName('button')[0];
DownloadButton.addEventListener('click', async function () {
let CreatorToken = await fetch('https://polytoria.com/api/places/edit', {
method: 'POST',
body: JSON.stringify({placeID: PlaceID})
});
CreatorToken = await CreatorToken.json();
CreatorToken = CreatorToken.token;
fetch(`https://api.polytoria.com/v1/places/get-place?id=${PlaceID}&tokenType=creator`, {
headers: {
Authorization: CreatorToken
}
})
.then((response) => {
if (!response.ok) {
throw new Error('Network not ok');
}
return response.blob();
})
.then((data) => {
const DownloadURL = URL.createObjectURL(data);
const Link = document.createElement('a');
Link.href = DownloadURL;
Link.download = PlaceData.name + '.poly';
document.body.appendChild(Link);
Link.click();
Link.remove();
})
.catch((error) => {
console.log(error);
});
});
}
function MultiWhitelist(){
const WhitelistCard = document.querySelector('.card:has(#whitelist-username)')
const MultiWhitelistCard = document.createElement('card')
MultiWhitelistCard.classList = 'card mt-3'
MultiWhitelistCard.innerHTML = `
<div class="card-header">
<i class="fa-duotone fa-solid fa-vial-circle-check"></i>
Multi-Whitelist
</div>
<div class="card-body">
<textarea class="form-control bg-dark mb-2" placeholder="Usernames (separated by lines).." style="min-height: 250px;"></textarea>
<button class="btn btn-primary">
<i class="fa-duotone fa-solid fa-users"></i>
Whitelist
</button>
</div>
`
WhitelistCard.parentElement.appendChild(MultiWhitelistCard)
const MultiWhitelistSubmitButton = MultiWhitelistCard.getElementsByTagName('button')[0]
MultiWhitelistSubmitButton.addEventListener('click', async function(){
const Usernames = MultiWhitelistSubmitButton.previousElementSibling.value.split('\n').filter((x) => x !== "")
MultiWhitelistSubmitButton.previousElementSibling.disabled = true
if (Usernames.length > 0) {
for (let username of Usernames) {
Utilities.RatelimitRepeatingFetch('https://polytoria.com/api/create/whitelist', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
placeID: PlaceID,
username: username
})
})
}
window.location.reload()
}
})
}
function ClearWhitelist() {
const WhitelistCard = document.querySelector('.card:has(#whitelist-username)')
const ClearWhitelistButton = document.createElement('button')
ClearWhitelistButton.classList = 'btn btn-danger btn-sm'
ClearWhitelistButton.style = 'position: absolute; top: 0; right: 0; margin: 4px;'
ClearWhitelistButton.innerHTML = '<i class="fa-duotone fa-solid fa-broom-wide"></i> Clear'
WhitelistCard.children[0].appendChild(ClearWhitelistButton)
let WhitelistData = null
let ClearPending = false
ClearWhitelistButton.addEventListener('click', async function(){
if (ClearPending === false) {
ClearPending = true
ClearWhitelistButton.innerText = 'Are you sure?'
setTimeout(() => {
if (ClearPending === true) {
ClearPending = false;
ClearWhitelistButton.innerHTML = '<i class="fa-duotone fa-solid fa-broom-wide"></i> Clear'
}
}, 3000);
} else {
ClearPending = false
ClearWhitelistButton.innerHTML = '<i class="fa-duotone fa-solid fa-broom-wide"></i> Clear'
if (confirm('Are you sure you\'d like to clear all of this place\'s whitelist')) {
if (WhitelistData === null) {
const InitialWhitelist = (await (await fetch('https://polytoria.com/api/create/whitelist?placeID=' + PlaceID + '&page=1')).json())
WhitelistData = [...InitialWhitelist.data]
if (InitialWhitelist.meta.lastPage > 1) {
for (let page = 1; page < InitialWhitelist.meta.lastPage; page++) {
const PageResult = (await (await fetch('https://polytoria.com/api/create/whitelist?placeID=' + PlaceID + '&page=' + (page+1))).json())
WhitelistData.push(...PageResult.data)
}
}
for (let id of WhitelistData.map((x) => x.user.id)) {
Utilities.RatelimitRepeatingFetch('https://polytoria.com/api/create/remove-whitelist', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
placeID: PlaceID,
userID: id
})
})
}
window.location.reload()
}
}
}
})
}

View file

@ -32,7 +32,7 @@ if (window.location.pathname.split('/')[3] === 'polyplus' && window.location.has
<p class="text-muted mb-1">Version: v${Version}</p> <p class="text-muted mb-1">Version: v${Version}</p>
<p class="text-muted mb-3">Data Size: <span id="data-size">Loading</span> byte(s)</p> <p class="text-muted mb-3">Data Size: <span id="data-size">Loading</span> byte(s)</p>
<button class="btn btn-primary btn-sm w-100" id="check-for-updates">Check for Updates</button> <button class="btn btn-primary btn-sm w-100" id="check-for-updates">Check for Updates</button>
<a href="https://github.com/IndexingGitHub/PolyPlus" class="btn btn-dark btn-sm w-100 mt-2" target="_blank">Open GitHub</a> <a href="https://github.com/indexxing/PolyPlus" class="btn btn-dark btn-sm w-100 mt-2" target="_blank">Open GitHub</a>
</div> </div>
</div> </div>
<hr> <hr>
@ -75,17 +75,21 @@ if (window.location.pathname.split('/')[3] === 'polyplus' && window.location.has
<hr> <hr>
<div class="card"> <div class="card">
<div class="card-header text-primary">RUN TESTS</div> <div class="card-header text-primary">Tools</div>
<div class="card-body"> <div class="card-body">
<label>Test "IRL Price with Brick Count"</label> <label>Generate <code style="color: orange;">JSON</code> for "Event Items" store category</label>
<p>will add later</p> <div class="input-group">
<input type="text" class="form-control bg-dark" placeholder="Event ID..">
<input type="text" class="form-control bg-dark" placeholder="Item IDs (separated by commas)..">
<button class="btn btn-primary" id="generate-event-items">Generate</button>
</div>
</div> </div>
</div> </div>
<hr> <hr>
<div class="card"> <div class="card">
<div class="card-header" style="color: red;">DANGER ZONE!</div> <div class="card-header" style="color: red;">Danger Zone!</div>
<div class="card-body"> <div class="card-body">
<label>Clear Specific Data Locations</label> <label>Clear Specific Data Locations</label>
<p>Quickly clear specific locations of the extension's local data</p> <p>Quickly clear specific locations of the extension's local data</p>
@ -234,96 +238,275 @@ if (window.location.pathname.split('/')[3] === 'polyplus' && window.location.has
}); });
}); });
chrome.storage.sync.getBytesInUse(['PolyPlus_Settings', 'PolyPlus_PinnedGames', 'PolyPlus_BestFriends', 'PolyPlus_ItemWishlist'], function (bytes) { const GenerateEventItems = document.getElementById('generate-event-items')
console.log(bytes) GenerateEventItems.addEventListener('click', async function(){
document.getElementById('data-size').innerText = bytes.toLocaleString(); const EventItemsJSON = []
const ItemIDs = GenerateEventItems.previousElementSibling.value.split(',')
for (let id of ItemIDs) {
const ItemDetails = (await (await fetch('https://api.polytoria.com/v1/store/' + id.trim())).json())
EventItemsJSON.push({
id: parseInt(id),
name: ItemDetails.name,
event: GenerateEventItems.previousElementSibling.previousElementSibling.value,
thumbnail: ItemDetails.thumbnail
})
}
console.log(EventItemsJSON)
navigator.clipboard.writeText(JSON.stringify(EventItemsJSON, null, 2))
.then(() => {
alert('Successfully copied generated event items JSON!')
})
.catch(() => {
alert('Failure when trying to copy generated event items JSON.')
})
})
chrome.storage.sync.getBytesInUse(['PolyPlus_Settings', 'PolyPlus_PinnedGames', 'PolyPlus_ItemWishlist', 'PolyPlus_TimePlayed', 'PolyPlus_AvatarSandboxOutfits'], function (sync) {
chrome.storage.local.getBytesInUse(['PolyPlus_InventoryCache', 'PolyPlus_GreatDivideStats'], function(local){
document.getElementById('data-size').innerText = (sync + local).toLocaleString();
})
}); });
}); });
} else if (window.location.pathname.split('/')[3] === 'polyplus' && window.location.hash === '#debug') { } else if (window.location.pathname.split('/')[3] === 'polyplus' && window.location.hash === '#debug') {
document.addEventListener('DOMContentLoaded', function () { document.addEventListener('DOMContentLoaded', function () {
chrome.storage.sync.get(['PolyPlus_Settings', 'PolyPlus_PinnedGames', 'PolyPlus_BestFriends', 'PolyPlus_ItemWishlist'], function(result) { chrome.storage.sync.get(['PolyPlus_Settings', 'PolyPlus_PinnedGames', 'PolyPlus_BestFriends', 'PolyPlus_ItemWishlist', 'PolyPlus_AvatarSandboxOutfits', 'PolyPlus_TimePlayed'], function(sync) {
document.querySelector('#main-content .container').innerHTML = ` chrome.storage.local.get(['PolyPlus_InventoryCache', 'PolyPlus_GreatDivideStats', 'PolyPlus_FriendCount', 'PolyPlus_AssetDesigners'], function(local){
<style> document.querySelector('#main-content .container').innerHTML = `
#main-content .container label { <style>
font-size: 0.8rem; #main-content .container label {
color: darkgray; font-size: 0.8rem;
} color: darkgray;
}
#main-content .container label + p { #main-content .container label + p {
margin-bottom: 4px; margin-bottom: 4px;
font-size: 0.9rem; font-size: 0.9rem;
margin-top: -4px; margin-top: -4px;
}
.card:has([data-bs-toggle="collapse"]):not(:has(.card-body.collapse.show)) a:has(.card-header), .card:has([data-bs-toggle="collapse"]):not(:has(.card-body.collapse.show)) .card-header, .card:has([data-bs-toggle="collapse"]):has(.card-body.collapse.collapsing) a:has(.card-header), .card:has([data-bs-toggle="collapse"]):has(.card-body.collapse.collapsing) .card-header {
border-radius: inherit;
}
</style>
<div class="text-center mb-3">
<h1 class="text-center" style="font-size: 4.6rem;">Poly+ Debug</h1>
<p class="w-75 d-block mx-auto mb-0">This page is used for accessing most data-related objects stored by the extension.</p>
<small style="font-size: 0.75rem;" class="text-muted">* note: cache objects don't get cleared, instead when they are requested, if the data stored is old, new data will replace the old data.</small>
</div>
<div class="row">
<div class="col-md-2" style="padding-left: 0px;">
<div class="card">
<div class="card-body">
<p class="text-muted mb-1">Version: v${Version}</p>
<p class="text-muted mb-3">Data Size: <span id="data-size">Loading</span> byte(s)</p>
<button class="btn btn-primary btn-sm w-100" id="check-for-updates">Check for Updates</button>
<a href="https://github.com/indexxing/PolyPlus" class="btn btn-dark btn-sm w-100 mt-2" target="_blank">Open GitHub</a>
</div>
</div>
<hr>
Created by <a href="/u/Index" target="_blank">Index</a>
<br><br>
Beta Testers:
<ul>
<li><a href="/u/datastore" target="_blank">datastore</a></li>
<li><a href="/u/Emir" target="_blank">Emir</a></li>
<li><a href="/u/InsertSoda" target="_blank">InsertSoda</a></li>
<li><a href="/u/qwp" target="_blank">qwp</a></li>
</ul>
</div>
<div class="col">
<div class="card mb-3">
<a class="text-reset" data-bs-toggle="collapse" href="#settings" role="button" aria-expanded="false" aria-controls="settings">
<div class="card-header">
<span class="badge bg-primary" style="margin-right: 5px; vertical-align: text-bottom;">Sync</span>
Settings
</div>
</a>
<div class="card-body collapse" id="settings">
<div style="padding: 10px; background: #171717; font-family: monospace; color: orange; font-size: 0.8rem; border-radius: 10px; position: relative;">
${JSON.stringify((sync.PolyPlus_Settings || {}), null, 2)
.replaceAll('\n','<br>')
.replaceAll(' ', '&nbsp;')
.replaceAll('\t', '&nbsp;&nbsp;&nbsp;&nbsp;')}
</div>
</div>
</div>
<div class="card mb-3">
<a class="text-reset" data-bs-toggle="collapse" href="#pinned-games" role="button" aria-expanded="false" aria-controls="pinned-games">
<div class="card-header">
<span class="badge bg-primary" style="margin-right: 5px; vertical-align: text-bottom;">Sync</span>
Pinned Games
<small class="text-muted" style="font-size: 0.7rem;">(${(sync.PolyPlus_PinnedGames || []).length})</small>
</div>
</a>
<div class="card-body collapse" id="pinned-games">
<div style="padding: 10px; background: #171717; font-family: monospace; color: orange; font-size: 0.8rem; border-radius: 10px; position: relative;">
${JSON.stringify((sync.PolyPlus_PinnedGames || []), null, 2)
.replaceAll('\n','<br>')
.replaceAll(' ', '&nbsp;')
.replaceAll('\t', '&nbsp;&nbsp;&nbsp;&nbsp;')}
<button class="btn btn-warning btn-sm" style="position: absolute; top: 0; right: 0; margin: 10px;" onclick="navigator.clipboard.writeText('${JSON.stringify((sync.PolyPlus_PinnedGames || []))}') .then(() => {alert('copied')}) .catch(() => {});">copy</button>
</div>
</div>
</div>
<div class="card mb-3">
<a class="text-reset" data-bs-toggle="collapse" href="#item-wishlist" role="button" aria-expanded="false" aria-controls="item-wishlist">
<div class="card-header">
<span class="badge bg-primary" style="margin-right: 5px; vertical-align: text-bottom;">Sync</span>
Item Wishlist
<small class="text-muted" style="font-size: 0.7rem;">(${(sync.PolyPlus_ItemWishlist || []).length})</small>
</div>
</a>
<div class="card-body collapse" id="item-wishlist">
<div style="padding: 10px; background: #171717; font-family: monospace; color: orange; font-size: 0.8rem; border-radius: 10px; position: relative;">
${JSON.stringify((sync.PolyPlus_ItemWishlist || []), null, 2)
.replaceAll('\n','<br>')
.replaceAll(' ', '&nbsp;')
.replaceAll('\t', '&nbsp;&nbsp;&nbsp;&nbsp;')}
<button class="btn btn-warning btn-sm" style="position: absolute; top: 0; right: 0; margin: 10px;" onclick="navigator.clipboard.writeText('${JSON.stringify((sync.PolyPlus_ItemWishlist || []))}') .then(() => {alert('copied')}) .catch(() => {});">copy</button>
</div>
</div>
</div>
<div class="card mb-3">
<a class="text-reset" data-bs-toggle="collapse" href="#avatar-sandbox-outfits" role="button" aria-expanded="false" aria-controls="item-wishlist">
<div class="card-header">
<span class="badge bg-primary" style="margin-right: 5px; vertical-align: text-bottom;">Sync</span>
Avatar Sandbox Outfits
<small class="text-muted" style="font-size: 0.7rem;">(${(sync.PolyPlus_AvatarSandboxOutfits || []).length})</small>
</div>
</a>
<div class="card-body collapse" id="avatar-sandbox-outfits">
<div style="padding: 10px; background: #171717; font-family: monospace; color: orange; font-size: 0.8rem; border-radius: 10px; position: relative;">
${JSON.stringify((sync.PolyPlus_AvatarSandboxOutfits || []), null, 2)
.replaceAll('\n','<br>')
.replaceAll(' ', '&nbsp;')
.replaceAll('\t', '&nbsp;&nbsp;&nbsp;&nbsp;')}
<button class="btn btn-warning btn-sm" style="position: absolute; top: 0; right: 0; margin: 10px;" onclick="navigator.clipboard.writeText('${JSON.stringify((sync.PolyPlus_AvatarSandboxOutfits || []))}') .then(() => {alert('copied')}) .catch(() => {});">copy</button>
</div>
</div>
</div>
<div class="card mb-3">
<a class="text-reset" data-bs-toggle="collapse" href="#time-played" role="button" aria-expanded="false" aria-controls="item-wishlist">
<div class="card-header">
<span class="badge bg-primary" style="margin-right: 5px; vertical-align: text-bottom;">Sync</span>
Time Played
<small class="text-muted" style="font-size: 0.7rem;">(${(Object.keys(sync.PolyPlus_TimePlayed) || []).length} places tracked)</small>
</div>
</a>
<div class="card-body collapse" id="time-played">
<div style="padding: 10px; background: #171717; font-family: monospace; color: orange; font-size: 0.8rem; border-radius: 10px; position: relative;">
${JSON.stringify((sync.PolyPlus_TimePlayed || []), null, 2)
.replaceAll('\n','<br>')
.replaceAll(' ', '&nbsp;')
.replaceAll('\t', '&nbsp;&nbsp;&nbsp;&nbsp;')}
<button class="btn btn-warning btn-sm" style="position: absolute; top: 0; right: 0; margin: 10px;" onclick="navigator.clipboard.writeText('${JSON.stringify((sync.PolyPlus_AvatarSandboxOutfits || []))}') .then(() => {alert('copied')}) .catch(() => {});">copy</button>
</div>
</div>
</div>
<div class="card mb-3">
<a class="text-reset" data-bs-toggle="collapse" href="#inventory-cache" role="button" aria-expanded="false" aria-controls="inventory-cache">
<div class="card-header">
<span class="badge bg-secondary" style="margin-right: 5px; vertical-align: text-bottom;">Local</span>
Inventory Cache
<small class="text-muted" style="font-size: 0.7rem;">(cached for 5 minutes, ${local.PolyPlus_InventoryCache.data.length} items cached)</small>
</div>
</a>
<div class="card-body collapse" id="inventory-cache">
<div style="padding: 10px; background: #171717; font-family: monospace; color: orange; font-size: 0.8rem; border-radius: 10px; position: relative;">
${JSON.stringify((local.PolyPlus_InventoryCache || {data: [], requested: "never"}), null, 2)
.replaceAll('\n','<br>')
.replaceAll(' ', '&nbsp;')
.replaceAll('\t', '&nbsp;&nbsp;&nbsp;&nbsp;')}
</div>
</div>
</div>
<div class="card mb-3">
<a class="text-reset" data-bs-toggle="collapse" href="#great-divide-stats" role="button" aria-expanded="false" aria-controls="inventory-cache">
<div class="card-header">
<span class="badge bg-secondary" style="margin-right: 5px; vertical-align: text-bottom;">Local</span>
Great Divide User Statistics
<small class="text-muted" style="font-size: 0.7rem;">(cached for 5 minutes, ${Object.keys(local.PolyPlus_GreatDivideStats || {}).length} users cached)</small>
</div>
</a>
<div class="card-body collapse" id="great-divide-stats">
<div style="padding: 10px; background: #171717; font-family: monospace; color: orange; font-size: 0.8rem; border-radius: 10px; position: relative;">
${JSON.stringify((local.PolyPlus_GreatDivideStats || {}), null, 2)
.replaceAll('\n','<br>')
.replaceAll(' ', '&nbsp;')
.replaceAll('\t', '&nbsp;&nbsp;&nbsp;&nbsp;')}
</div>
</div>
</div>
<div class="card mb-3">
<a class="text-reset" data-bs-toggle="collapse" href="#friend-count" role="button" aria-expanded="false" aria-controls="inventory-cache">
<div class="card-header">
<span class="badge bg-secondary" style="margin-right: 5px; vertical-align: text-bottom;">Local</span>
Friend Count
<small class="text-muted" style="font-size: 0.7rem;">(cached for 5 minutes)</small>
</div>
</a>
<div class="card-body collapse" id="friend-count">
<div style="padding: 10px; background: #171717; font-family: monospace; color: orange; font-size: 0.8rem; border-radius: 10px; position: relative;">
${JSON.stringify((local.PolyPlus_FriendCount || {data: [], requested: "never"}), null, 2)
.replaceAll('\n','<br>')
.replaceAll(' ', '&nbsp;')
.replaceAll('\t', '&nbsp;&nbsp;&nbsp;&nbsp;')}
</div>
</div>
</div>
<div class="card mb-3">
<a class="text-reset" data-bs-toggle="collapse" href="#asset-designers" role="button" aria-expanded="false" aria-controls="inventory-cache">
<div class="card-header">
<span class="badge bg-secondary" style="margin-right: 5px; vertical-align: text-bottom;">Local</span>
Asset Designers Cache
<small class="text-muted" style="font-size: 0.7rem;">(cached for 5 minutes)</small>
</div>
</a>
<div class="card-body collapse" id="asset-designers">
<div style="padding: 10px; background: #171717; font-family: monospace; color: orange; font-size: 0.8rem; border-radius: 10px; position: relative;">
${JSON.stringify((local.PolyPlus_AssetDesigners || {data: {}, requested: "never"}), null, 2)
.replaceAll('\n','<br>')
.replaceAll(' ', '&nbsp;')
.replaceAll('\t', '&nbsp;&nbsp;&nbsp;&nbsp;')}
</div>
</div>
</div>
</div>
</div>
`;
const CheckForUpdatesButton = document.getElementById('check-for-updates');
function CheckForUpdates() {
CheckForUpdatesButton.removeEventListener('click', CheckForUpdates);
CheckForUpdatesButton.disabled = true;
fetch('https://polyplus.vercel.app/data/version.json')
.then((response) => {
if (!response.ok) {
throw new Error('Network not ok');
}
return response.json();
})
.then((data) => {
if (data.version === Version || Math.floor((data.version - Version) * 10) === 0) {
CheckForUpdatesButton.innerText = 'No updates available';
} else {
CheckForUpdatesButton.innerText = Math.floor((data.version - Version) * 10) + ' updates available';
}
})
.catch((error) => {
console.log(error);
});
} }
</style> CheckForUpdatesButton.addEventListener('click', CheckForUpdates);
<div class="text-center mb-3">
<h1 class="text-center" style="font-size: 4.6rem;">Poly+ Debug</h1> chrome.storage.sync.getBytesInUse(['PolyPlus_Settings', 'PolyPlus_PinnedGames', 'PolyPlus_ItemWishlist', 'PolyPlus_TimePlayed', 'PolyPlus_AvatarSandboxOutfits'], function (sync) {
<p class="w-75 d-block mx-auto">This page is used by developers for debugging most data related things. It is unrecommended you modify any data on this page, but if you ever want to go ahead.</p> chrome.storage.local.getBytesInUse(['PolyPlus_InventoryCache'], function(local){
</div> document.getElementById('data-size').innerText = (sync + local).toLocaleString();
<div class="row"> })
<div class="col-md-5" style="padding-left: 0px;"> });
<div class="card mb-3"> })
<div class="card-body">
<h2>Settings</h2>
<div style="padding: 10px; background: #171717; font-family: monospace; color: orange; font-size: 0.8rem; border-radius: 10px; position: relative;">
${JSON.stringify((result.PolyPlus_Settings || {}), null, 2)
.replaceAll('\n','<br>')
.replaceAll(' ', '&nbsp;')
.replaceAll('\t', '&nbsp;&nbsp;&nbsp;&nbsp;')}
<!--<button class="btn btn-warning btn-sm" style="position: absolute; top: 0; right: 0; margin: 10px;" onclick="navigator.clipboard.writeText('${JSON.stringify((result.PolyPlus_Settings || [])).replaceAll('"', "\'")}') .then(() => {alert('copied')}) .catch(() => {});">copy</button>-->
</div>
</div>
</div>
<div class="card">
<div class="card-body">
<p class="text-muted mb-1">Version: v${Version}</p>
<p class="text-muted mb-3">Data Size: <span id="data-size">Loading</span> byte(s)</p>
<button class="btn btn-primary btn-sm w-100" id="check-for-updates">Check for Updates</button>
<a href="https://github.com/IndexingGitHub/PolyPlus" class="btn btn-dark btn-sm w-100 mt-2" target="_blank">Open GitHub</a>
</div>
</div>
<hr>
Created by <a href="/u/Index" target="_blank">Index</a>
</div>
<div class="col">
<div class="card mb-3">
<div class="card-body">
<h3>Pinned Games (${(result.PolyPlus_PinnedGames || []).length})</h3>
<div style="padding: 10px; background: #171717; font-family: monospace; color: orange; font-size: 0.8rem; border-radius: 10px; position: relative;">
${JSON.stringify((result.PolyPlus_PinnedGames || []), null, 2)
.replaceAll('\n','<br>')
.replaceAll(' ', '&nbsp;')
.replaceAll('\t', '&nbsp;&nbsp;&nbsp;&nbsp;')}
<button class="btn btn-warning btn-sm" style="position: absolute; top: 0; right: 0; margin: 10px;" onclick="navigator.clipboard.writeText('${JSON.stringify((result.PolyPlus_PinnedGames || []))}') .then(() => {alert('copied')}) .catch(() => {});">copy</button>
</div>
</div>
</div>
<div class="card mb-3">
<div class="card-body">
<h3>Best Friends (${(result.PolyPlus_BestFriends || []).length})</h3>
<div style="padding: 10px; background: #171717; font-family: monospace; color: orange; font-size: 0.8rem; border-radius: 10px; position: relative;">
${JSON.stringify((result.PolyPlus_BestFriends || []), null, 2)
.replaceAll('\n','<br>')
.replaceAll(' ', '&nbsp;')
.replaceAll('\t', '&nbsp;&nbsp;&nbsp;&nbsp;')}
<button class="btn btn-warning btn-sm" style="position: absolute; top: 0; right: 0; margin: 10px;" onclick="navigator.clipboard.writeText('${JSON.stringify((result.PolyPlus_BestFriends || []))}') .then(() => {alert('copied')}) .catch(() => {});">copy</button>
</div>
</div>
</div>
<div class="card">
<div class="card-body">
<h3>Item Wishlist (${(result.PolyPlus_ItemWishlist || []).length})</h3>
<div style="padding: 10px; background: #171717; font-family: monospace; color: orange; font-size: 0.8rem; border-radius: 10px; position: relative;">
${JSON.stringify((result.PolyPlus_ItemWishlist || []), null, 2)
.replaceAll('\n','<br>')
.replaceAll(' ', '&nbsp;')
.replaceAll('\t', '&nbsp;&nbsp;&nbsp;&nbsp;')}
<button class="btn btn-warning btn-sm" style="position: absolute; top: 0; right: 0; margin: 10px;" onclick="navigator.clipboard.writeText('${JSON.stringify((result.PolyPlus_ItemWishlist || []))}') .then(() => {alert('copied')}) .catch(() => {});">copy</button>
</div>
</div>
</div>
</div>
</div>
`;
}) })
}); });
} }

View file

@ -17,17 +17,25 @@ chrome.storage.sync.get(['PolyPlus_Settings'], function (result) {
}); });
function ForumMentions() { function ForumMentions() {
const Regex = /@([\w.]+)/g; const Regex = /@([\w.]+)/g;
for (let text of ForumText) { for (let text of ForumText) {
let FormattedText = text.innerHTML; const TreeWalker = document.createTreeWalker(text, NodeFilter.SHOW_TEXT, null, false);
let match; let Node;
while ((match = Regex.exec(text.innerText)) !== null) {
const Username = match[0].substring(1); while ((Node = TreeWalker.nextNode())) {
FormattedText = FormattedText.replaceAll(match[0], `<a href="/users/@${Username}?ref=${encodeURIComponent(window.location.pathname)}" class="polyplus-mention">${match[0]}</a>`); let Match;
} let Replacement = Node.nodeValue;
text.innerHTML = FormattedText;
} while ((Match = Regex.exec(Node.nodeValue)) !== null) {
const Username = Match[0].substring(1);
const Mention = `<a href="/u/${Username}" class="polyplus-mention">${Match[0]}</a>`;
Replacement = replacedText.replace(Match[0], Mention);
}
Node.nodeValue = replacedText;
}
}
} }
function ForumUnixTimestamps() { function ForumUnixTimestamps() {
@ -44,7 +52,6 @@ function ForumUnixTimestamps() {
const Distance = new Intl.RelativeTimeFormat({numeric: 'auto', style: 'short'}).format(Math.floor((Timestamp - new Date()) / (60 * 1000)), 'day'); const Distance = new Intl.RelativeTimeFormat({numeric: 'auto', style: 'short'}).format(Math.floor((Timestamp - new Date()) / (60 * 1000)), 'day');
const Result = `<code style="color: orange;">${Months[Timestamp.getMonth()]} ${Timestamp.getDate()}, ${Timestamp.getFullYear()} (${['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'][Timestamp.getDay() - 1]}) at ${Timestamp.getHours() - 12}:${String(Timestamp.getMinutes()).padStart(2, '0')} (${Distance})</code>`; const Result = `<code style="color: orange;">${Months[Timestamp.getMonth()]} ${Timestamp.getDate()}, ${Timestamp.getFullYear()} (${['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'][Timestamp.getDay() - 1]}) at ${Timestamp.getHours() - 12}:${String(Timestamp.getMinutes()).padStart(2, '0')} (${Distance})</code>`;
FormattedText = FormattedText.replaceAll(match[0], Result); FormattedText = FormattedText.replaceAll(match[0], Result);
console.log(FormattedText);
} }
text.innerHTML = FormattedText; text.innerHTML = FormattedText;
} }

View file

@ -1,11 +1,18 @@
const AssetID = window.location.pathname.split('/')[2]; const AssetID = window.location.pathname.split('/')[2];
const LibraryType = document.querySelectorAll('ol a')[1].innerText.toLowerCase(); const LibraryType = document.querySelectorAll('ol a')[1].innerText.toLowerCase();
const LibraryTypes = ['model', 'audio', 'decal', 'mesh']; const LibraryTypes = ['model', 'audio', 'decal', 'mesh', 'shirt', 'pant'];
if (LibraryTypes.filter((x) => !LibraryTypes.some(element => element.startsWith(LibraryType))).length > 0) { if (LibraryTypes.some(element => LibraryType.startsWith(element))) {
chrome.storage.sync.get(['PolyPlus_Settings'], function (result) { chrome.storage.sync.get(['PolyPlus_Settings'], async function (result) {
Settings = result.PolyPlus_Settings || {}; Settings = result.PolyPlus_Settings || {};
if (['model', 'audio', 'decal', 'mesh'].some(element => LibraryType.startsWith(element))) {
const Breadcrumbs = document.querySelectorAll('ol a')
Breadcrumbs[0].href = '/library'
Breadcrumbs[0].innerText = 'Library'
Breadcrumbs[1].href = '/library'
}
if (Settings.LibraryDownloadsOn === false) { if (Settings.LibraryDownloadsOn === false) {
return; return;
} }
@ -18,6 +25,15 @@ if (LibraryTypes.filter((x) => !LibraryTypes.some(element => element.startsWith(
DownloadLink.innerHTML = `<i class="fa-duotone fa-download"></i> Download`; DownloadLink.innerHTML = `<i class="fa-duotone fa-download"></i> Download`;
Dropdown.insertBefore(DownloadLink, Dropdown.children[Dropdown.children.length - 1]); Dropdown.insertBefore(DownloadLink, Dropdown.children[Dropdown.children.length - 1]);
const SourceLink = document.createElement('a');
SourceLink.classList = 'dropdown-item text-primary';
SourceLink.href = '#';
SourceLink.innerHTML = `<i class="fa-duotone fa-copy"></i> Copy CDN URL`;
if (LibraryType.startsWith('audio') || LibraryType.startsWith('decal') || LibraryType.startsWith('shirt') || LibraryType.startsWith('pant') || LibraryType.startsWith('mesh')) {
Dropdown.insertBefore(SourceLink, DownloadLink)
}
switch (LibraryType) { switch (LibraryType) {
case LibraryType.startsWith('model'): case LibraryType.startsWith('model'):
DownloadLink.href = 'https://api.polytoria.com/v1/models/get-model?id=' + AssetID; DownloadLink.href = 'https://api.polytoria.com/v1/models/get-model?id=' + AssetID;
@ -26,21 +42,69 @@ if (LibraryTypes.filter((x) => !LibraryTypes.some(element => element.startsWith(
const AudioBlob = new Blob([document.getElementsByTagName('audio')[0]], {type: 'octet-steam'}); const AudioBlob = new Blob([document.getElementsByTagName('audio')[0]], {type: 'octet-steam'});
DownloadLink.href = URL.createObjectURL(AudioBlob); DownloadLink.href = URL.createObjectURL(AudioBlob);
DownloadLink.download = document.getElementsByTagName('h1')[0].innerText + '.mp3'; DownloadLink.download = document.getElementsByTagName('h1')[0].innerText + '.mp3';
case LibraryType.startsWith('decal'):
console.log('aaaaa')
SourceLink.addEventListener('click', function() {
console.log('LCICKED!!')
navigator.clipboard
.writeText(document.getElementsByTagName('audio')[0].src)
.then(() => {})
.catch(() => {
alert('Failure to copy .png file CDN url to clipboard')
});
})
break;
/*
case (LibraryType.startsWith('decal')):
const DecalBlob = new Blob([document.getElementsByClassName('store-thumbnail')[0]], {type: 'image/png'}); const DecalBlob = new Blob([document.getElementsByClassName('store-thumbnail')[0]], {type: 'image/png'});
DownloadLink.href = URL.createObjectURL(DecalBlob); DownloadLink.href = URL.createObjectURL(DecalBlob);
DownloadLink.download = document.getElementsByTagName('h1')[0].innerText + '.png'; DownloadLink.download = document.getElementsByTagName('h1')[0].innerText + '.png';
break; break;
*/
} }
if (LibraryType.startsWith('mesh')) { if (LibraryType.startsWith('shirt') || LibraryType.startsWith('pant') || LibraryType.startsWith('decal')) {
let ClothingURL = null;
DownloadLink.addEventListener('click', async function () {
if (ClothingURL !== null) {
return;
}
ClothingURL = (await (await fetch('https://api.polytoria.com/v1/assets/serve/' + AssetID + '/Asset')).json());
if (ClothingURL.success === true) {
const ClothingBlob = (await (await fetch(ClothingURL.url)).blob())
DownloadLink.href = URL.createObjectURL(ClothingBlob);
DownloadLink.download = document.getElementsByTagName('h1')[0].innerText + '.png';
DownloadLink.click();
} else {
alert('Failure to fetch .png file for clothing item');
}
});
SourceLink.addEventListener('click', async function(){
if (ClothingURL !== null) {
return;
}
ClothingURL = (await (await fetch('https://api.polytoria.com/v1/assets/serve/' + AssetID + '/Asset')).json());
if (ClothingURL.success === true) {
navigator.clipboard.writeText(ClothingURL.url)
.then(() => {})
.catch(() => {
alert('Failure to copy .png file CDN url to clipboard')
});
} else {
alert('Failure to fetch .png file for clothing item');
}
})
} else if (LibraryType.startsWith('mesh')) {
let MeshURL = null; let MeshURL = null;
DownloadLink.addEventListener('click', async function () { DownloadLink.addEventListener('click', async function () {
if (MeshURL !== null) { if (MeshURL !== null) {
return; return;
} }
MeshURL = await fetch('https://api.polytoria.com/v1/assets/serve-mesh/' + AssetID); MeshURL = (await (await fetch('https://api.polytoria.com/v1/assets/serve-mesh/' + AssetID)).json());
MeshURL = await MeshURL.json();
if (MeshURL.success === true) { if (MeshURL.success === true) {
DownloadLink.href = MeshURL.url; DownloadLink.href = MeshURL.url;
@ -51,6 +115,23 @@ if (LibraryTypes.filter((x) => !LibraryTypes.some(element => element.startsWith(
alert('Failure to fetch .glb file for mesh'); alert('Failure to fetch .glb file for mesh');
} }
}); });
SourceLink.addEventListener('click', async function(){
if (MeshURL !== null) {
return;
}
MeshURL = (await (await fetch('https://api.polytoria.com/v1/assets/serve-mesh/' + AssetID)).json());
if (MeshURL.success === true) {
navigator.clipboard.writeText(MeshURL.url)
.then(() => {})
.catch(() => {
alert('Failure to copy .glb file CDN url to clipboard')
});
} else {
alert('Failure to fetch .glb file for mesh');
}
})
} }
}); });
} }

View file

@ -1,13 +1,17 @@
chrome.storage.sync.get(['PolyPlus_Settings'], function (result) { chrome.storage.sync.get(['PolyPlus_Settings'], function (result) {
Settings = result.PolyPlus_Settings || { Settings = result.PolyPlus_Settings || {
ApplyMembershipTheme: {
Enabled: false,
Theme: 0
},
ApplyMembershipThemeOn: false, ApplyMembershipThemeOn: false,
ApplyMembershipThemeTheme: 0 ApplyMembershipThemeTheme: 0
}; };
if (Settings.ApplyMembershipThemeOn !== true) { if (Settings.ApplyMembershipTheme.Enabled !== true) {
return; return;
} }
MembershipTheme = Settings.ApplyMembershipThemeTheme === 0 ? 'plus' : 'plusdx'; MembershipTheme = Settings.ApplyMembershipTheme.Theme === 0 ? 'plus' : 'plusdx';
document.addEventListener('DOMContentLoaded', function () { document.addEventListener('DOMContentLoaded', function () {
if (document.getElementsByClassName('card-header')[0] && document.getElementsByClassName('card-header')[0].innerText === ' Page not found') { if (document.getElementsByClassName('card-header')[0] && document.getElementsByClassName('card-header')[0].innerText === ' Page not found') {
@ -24,25 +28,28 @@ chrome.storage.sync.get(['PolyPlus_Settings'], function (result) {
Navbar.classList.add('navbar-' + MembershipTheme); Navbar.classList.add('navbar-' + MembershipTheme);
Sidebar.classList.add('sidebar-' + MembershipTheme); Sidebar.classList.add('sidebar-' + MembershipTheme);
if (MembershipTheme === 'plusdx') { let SidebarLogo = Sidebar.getElementsByTagName('img')[0];
let SidebarLogo = document.querySelector('.nav-sidebar img'); if (MembershipTheme === 'plus') {
SidebarLogo.setAttribute('src', 'https://c0.ptacdn.com/static/images/branding/icon-plusdx.bd9daa92.svg'); SidebarLogo.src = 'https://c0.ptacdn.com/static/images/branding/icon-plus.8f6e41f1.svg'
let SidebarLogoLabel = document.createElement('div'); } else {
SidebarLogoLabel.classList = 'nplusdx-banner'; SidebarLogo.src = 'https://c0.ptacdn.com/static/images/branding/icon-plusdx.bd9daa92.svg'
SidebarLogoLabel.innerHTML = ` }
<i class="pi pi-plusdx" style="margin-right:-0.4em"></i>
`;
SidebarLogo.parentElement.appendChild(SidebarLogoLabel);
if (window.location.pathname === '/home') { let SidebarLogoLabel = document.createElement('div');
let HomeUsernameText = document.getElementsByClassName('home-title2')[0]; SidebarLogoLabel.classList = 'n' + MembershipTheme + '-banner';
HomeUsernameText.children[0].classList.add('text-plusdx'); SidebarLogoLabel.innerHTML = `
let Label = document.createElement('div'); <i class="pi pi-${MembershipTheme}" style="margin-right:-0.4em"></i>
Label.classList = 'hplusdx-banner rounded-2'; `;
Label.setAttribute('style', 'margin-top: -8px; animation-delay: 0.09s;'); SidebarLogo.parentElement.appendChild(SidebarLogoLabel);
Label.innerText = 'Deluxe';
HomeUsernameText.appendChild(Label); if (MembershipTheme === 'plusdx' && window.location.pathname === '/home') {
} let HomeUsernameText = document.getElementsByClassName('home-title2')[0];
HomeUsernameText.classList.add('text-plusdx');
let Label = document.createElement('div');
Label.classList = 'hplusdx-banner reqFadeAnim rounded-2';
Label.setAttribute('style', 'margin-top:-8px');
Label.innerText = 'Deluxe';
HomeUsernameText.parentElement.appendChild(Label);
} }
}); });
}); });

View file

@ -1,160 +0,0 @@
const PlaceID = window.location.pathname.split('/')[3];
const Form = document.querySelector('form[action="/create/place/update"]');
var Settings;
var PlaceData = null;
!(async () => {
ActivityToggle();
//RequestGameProfile()
CopyOwnedPlace();
})();
async function ActivityToggle() {
if (PlaceData === null) {
PlaceData = await fetch('https://api.polytoria.com/v1/places/' + PlaceID);
PlaceData = await PlaceData.json();
}
let Status = PlaceData.isActive;
const DIV = document.createElement('div');
DIV.classList = 'form-group mt-4';
DIV.innerHTML = `
<label class="mb-2">
<h5 class="mb-0">Toggle Activity</h5>
<small class="text-muted">Make your place active or inactive (currently ${Status === true ? 'active' : 'inactive'}).</small>
</label>
<br>
`;
Form.insertBefore(DIV, Form.children[Form.children.length - 1]);
const ActivityBtn = document.createElement('button');
ActivityBtn.type = 'button';
ActivityBtn.classList = 'btn ' + (Status === true ? 'btn-danger' : 'btn-success');
ActivityBtn.innerText = Status === true ? 'Deactivate' : 'Activate';
DIV.appendChild(ActivityBtn);
ActivityBtn.addEventListener('click', function () {
fetch(`https://polytoria.com/api/places/${PlaceID}/toggle-active`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': document.querySelector('input[name="_csrf"]').value
}
})
.then((response) => {
if (!response.ok) {
throw new Error('Network not ok ' + response.status);
}
return response.json();
})
.then((data) => {
Status = data.isActive;
ActivityBtn.innerText = Status === true ? 'Deactivate' : 'Activate';
ActivityBtn.classList = 'btn ' + (Status === true ? 'btn-danger' : 'btn-success');
})
.catch((error) => {
console.log(error);
});
});
}
function RequestGameProfile() {
const Div = document.createElement('div');
Div.classList = 'card mt-4';
Div.innerHTML = `
<div class="card-body">
<input type="text" class="form-control bg-dark mb-2" placeholder="Game Title..">
<input type="color" class="form-control bg-dark mb-2" placeholder="Background Color..">
<input type="color" class="form-control bg-dark mb-2" placeholder="Accent Color..">
<input type="color" class="form-control bg-dark mb-2" placeholder="Secondary Color..">
<input type="color" class="form-control bg-dark mb-2" placeholder="Card Background Color..">
<input type="color" class="form-control bg-dark mb-2" placeholder="Text Color..">
<button type="button" class="btn btn-primary">Submit Request</button>
</div>
`;
Form.insertBefore(Div, Form.children[Form.children.length - 1]);
const SubmitBtn = Div.getElementsByTagName('button')[0];
SubmitBtn.addEventListener('click', function () {
const CardBody = Div.children[0];
const Result = {
gameTitle: CardBody.children[0].value,
bg: CardBody.children[1].value,
accent: CardBody.children[2].value,
secondary: CardBody.children[3].value,
cardBg: CardBody.children[4].value,
text: CardBody.children[5].value
};
window.location.href = 'https://polyplus.vercel.app/app/game-profile.html?gameId=' + PlaceID + '&profile=' + encodeURIComponent(btoa(JSON.stringify(Result)));
});
}
async function CopyOwnedPlace() {
console.log('ran function');
if (PlaceData === null) {
PlaceData = await fetch('https://api.polytoria.com/v1/places/' + 2640);
PlaceData = await PlaceData.json();
}
if (PlaceData.creator.id !== parseInt(JSON.parse(window.localStorage.getItem('p+account_info')).ID)) {
console.log('returned');
//return
}
const DIV = document.createElement('div');
DIV.classList = 'form-group mt-4';
DIV.innerHTML = `
<label class="mb-2">
<h5 class="mb-0">Download <code style="color: orange;">.poly</code> File</h5>
<small class="text-muted">Quickly download your place from the site!</small>
</label>
<br>
<button type="button" class="btn btn-primary">Download</button>
`;
Form.insertBefore(DIV, Form.children[Form.children.length - 1]);
const DownloadButton = DIV.getElementsByTagName('button')[0];
DownloadButton.addEventListener('click', async function () {
console.log('clicked download epic');
let CreatorToken = await fetch('https://polytoria.com/api/places/edit', {
method: 'POST',
headers: {
'X-CSRF-Token': document.querySelector('input[name="_csrf"]').value
},
body: JSON.stringify({placeID: PlaceID})
});
CreatorToken = await CreatorToken.json();
CreatorToken = CreatorToken.token;
fetch(`https://api.polytoria.com/v1/places/get-place?id=${PlaceID}&tokenType=creator`, {
headers: {
Authorization: CreatorToken
}
})
.then((response) => {
if (!response.ok) {
throw new Error('Network not ok');
}
return response.blob();
})
.then((data) => {
//const JSONBlob = new Blob([data], {type: "application/xml"})
const DownloadURL = URL.createObjectURL(data);
const Link = document.createElement('a');
Link.href = DownloadURL;
Link.download = PlaceData.name + '.poly';
document.body.appendChild(Link);
Link.click();
Link.remove();
})
.catch((error) => {
console.log(error);
});
});
}

View file

@ -1,10 +1,12 @@
const PlaceID = window.location.pathname.split('/')[2]; const PlaceID = parseInt(window.location.pathname.split('/')[2]);
const UserID = JSON.parse(window.localStorage.getItem('p+account_info')).ID; const UserID = JSON.parse(window.localStorage.getItem('p+account_info')).ID;
const GameCreator = document.querySelector('#main-content .card-body .col div.text-muted a[href^="/users/"]').getAttribute('href').split('/')[2]; const GameCreator = document.querySelector('#main-content .card-body .col div.text-muted a[href^="/users/"]').getAttribute('href').split('/')[2];
let Utilities; let Utilities;
let PlaceDetails = null;
var Settings; var Settings;
let TimePlayed;
var PinnedGamesData = []; var PinnedGamesData = [];
let GamePinned; let GamePinned;
@ -44,14 +46,15 @@ const Gamepasses = Array.from(GamepassesTab.getElementsByClassName('card')) || [
RatingsContainer.children[0].appendChild(PercentageLabel); RatingsContainer.children[0].appendChild(PercentageLabel);
chrome.storage.sync.get(['PolyPlus_Settings'], async function (result) { chrome.storage.sync.get(['PolyPlus_Settings', 'PolyPlus_PinnedGames', 'PolyPlus_TimePlayed'], async function (result) {
Settings = result.PolyPlus_Settings || {}; Settings = result.PolyPlus_Settings || {};
TimePlayed = result.PolyPlus_TimePlayed || {};
Utilities = await import(chrome.runtime.getURL('resources/utils.js')); Utilities = await import(chrome.runtime.getURL('resources/utils.js'));
Utilities = Utilities.default; Utilities = Utilities.default;
if (Settings.PinnedGamesOn === true) { if (Settings.PinnedGamesOn === true) {
PinnedGames(); PinnedGames(result.PolyPlus_PinnedGames);
} }
if (Settings.InlineEditingOn === true && GameCreator === UserID) { if (Settings.InlineEditingOn === true && GameCreator === UserID) {
@ -71,6 +74,33 @@ const Gamepasses = Array.from(GamepassesTab.getElementsByClassName('card')) || [
IRLPrice(); IRLPrice();
} }
if (Settings.TimePlayedOn === true) {
const TimePlayedNameRow = document.createElement('li');
TimePlayedNameRow.innerText = 'Time Played:';
const TimePlayedValueRow = document.createElement('li')
if (TimePlayed[PlaceID]) {
TimePlayedValueRow.innerHTML = '<i class="text-center text-muted fa-duotone fa-solid fa-watch me-1" style="width: 1.3em;"></i> ' + new Date(TimePlayed[PlaceID] * 1000).toISOString().slice(11, 19) + ' <small style="font-size: 0.7rem;" class="text-muted">(poly+)</small>'
} else {
TimePlayedValueRow.innerHTML = '<i class="text-center text-muted fa-duotone fa-solid fa-watch me-1" style="width: 1.3em;"></i> - <small style="font-size: 0.7rem;" class="text-muted">(poly+)</small>'
}
InfoColumns[0].appendChild(TimePlayedNameRow);
InfoColumns[1].appendChild(TimePlayedValueRow);
if (document.getElementById('btn-play')) {
document.getElementById('btn-play').addEventListener('click', function(){
chrome.runtime.sendMessage({ action: "start_time_played", placeID: PlaceID, userID: UserID });
})
Array.from(document.querySelectorAll('button[onclick^="joinPlace"]')).forEach(serverJoin => {
serverJoin.addEventListener("click", function(){
chrome.runtime.sendMessage({ action: "start_time_played", placeID: PlaceID, userID: UserID })
})
})
}
}
if (Settings.ShowPlaceRevenueOn === true) { if (Settings.ShowPlaceRevenueOn === true) {
const NameRow = document.createElement('li'); const NameRow = document.createElement('li');
NameRow.innerText = 'Revenue:'; NameRow.innerText = 'Revenue:';
@ -95,107 +125,112 @@ const Gamepasses = Array.from(GamepassesTab.getElementsByClassName('card')) || [
}); });
} }
if (AchievementsTab.getElementsByClassName('display-3')[0] === undefined) { if (Settings.ImprovedAchievements && Settings.ImprovedAchievements.Enabled === true && AchievementsTab.getElementsByClassName('display-3')[0] === undefined) {
AchievementProgressBar(); if (Settings.ImprovedAchievements.ProgressBarOn && Settings.ImprovedAchievements.ProgressBarOn === true) {
AchievementEarnedPercentage(); AchievementProgressBar();
}
for (let achievement of Achievements) { if (Settings.ImprovedAchievements.PercentageOn && Settings.ImprovedAchievements.PercentageOn === true) {
if ((achievement.getElementsByClassName('fad fa-check-circle')[0] !== undefined) === false) { AchievementEarnedPercentage();
achievement.style.opacity = '0.5'; }
if (Settings.ImprovedAchievements.OpacityOn && Settings.ImprovedAchievements.OpacityOn === true) {
for (let achievement of Achievements) {
if ((achievement.getElementsByClassName('fad fa-check-circle')[0] !== undefined) === false) {
achievement.style.opacity = '0.5';
}
} }
} }
} }
if (Settings.ReaddCopyablePlacesOn === true) {
ReaddCopyable()
}
if (document.querySelector('.text-warning .fa-lock') || new URLSearchParams(window.location.search).has('testRestrictedServers')) {
const ServerJoinButtons = Array.from(document.querySelectorAll('#servers-tabpane button'));
for (let button of ServerJoinButtons) {
button.classList.add('btn-secondary');
button.classList.remove('btn-success');
button.disabled = true;
button.innerText = 'Restricted'
}
}
}); });
})(); })();
function PinnedGames(placeIDs) {
const PinButton = document.createElement('button');
PinButton.classList = 'btn btn-primary btn-sm';
PinButton.style = 'position: absolute; top: 0; right: 0; margin: 4px; font-size: 1.3em;';
PinButton.innerHTML = `
<i class="fa-regular fa-star"></i>
`;
async function PinnedGames() { const UpdatePinButtonState = function() {
chrome.storage.sync.get(['PolyPlus_PinnedGames'], function (result) { PinButton.classList = 'btn btn-primary btn-sm';
PinnedGamesData = result.PolyPlus_PinnedGames || []; PinButton.disabled = false; // Ensure button is enabled initially
if (placeIDs.indexOf(PlaceID) === -1) {
const PinBtn = document.createElement('button'); // Not Pinned
PinBtn.classList = 'btn btn-warning btn-sm'; if (placeIDs.length >= Utilities.Limits.PinnedGames) {
PinBtn.style = 'position: absolute; right: 0; margin-right: 7px;'; PinButton.disabled = true;
if (PinnedGamesData.includes(parseInt(PlaceID))) {
PinBtn.innerHTML = '<i class="fa-duotone fa-star"></i> Un-pin';
} else {
if (PinnedGamesData.length !== Utilities.Limits.PinnedGames) {
PinBtn.innerHTML = '<i class="fa-duotone fa-star"></i> Pin';
} else {
PinBtn.setAttribute('disabled', true);
PinBtn.innerHTML = `<i class="fa-duotone fa-star"></i> Pin (max ${Utilities.Limits.PinnedGames}/${Utilities.Limits.PinnedGames})`;
} }
PinButton.children[0].classList = 'fa-regular fa-star';
} else {
// Pinned
PinButton.children[0].classList = 'fa-duotone fa-star';
}
};
PinButton.addEventListener('mouseenter', function() {
if (placeIDs.indexOf(PlaceID) !== -1) {
PinButton.classList.add('btn-danger');
PinButton.classList.remove('btn-primary');
PinButton.children[0].classList.add('fa-star-half-stroke');
PinButton.children[0].classList.remove('fa-star');
}
});
PinButton.addEventListener('mouseleave', function() {
if (placeIDs.indexOf(PlaceID) !== -1) {
PinButton.classList.add('btn-primary');
PinButton.classList.remove('btn-danger');
PinButton.children[0].classList.add('fa-star');
PinButton.children[0].classList.remove('fa-star-half-stroke');
}
});
UpdatePinButtonState();
document.querySelector('h1.my-0').parentElement.appendChild(PinButton);
PinButton.addEventListener('click', function() {
if (placeIDs.indexOf(PlaceID) === -1) {
placeIDs.push(PlaceID);
} else {
placeIDs.splice(placeIDs.indexOf(PlaceID), 1);
} }
PinBtn.addEventListener('click', function () { // clear cache
PinBtn.setAttribute('disabled', 'true'); chrome.storage.local.set({'PolyPlus_PinnedGamesData':{
data: undefined,
requested: 0
}}, function(){});
chrome.storage.sync.get(['PolyPlus_PinnedGames'], function (result) { UpdatePinButtonState();
PinnedGamesData = result.PolyPlus_PinnedGames || [];
/*
const Index = PinnedGames.indexOf(parseInt(PlaceID))
if (Index !== -1) {
//delete PinnedGames[PlaceID]
PinnedGames.splice(Index, 1)
PinBtn.innerHTML = '<i class="fa-duotone fa-star"></i> Pin'
} else {
//PinnedGames[PlaceID] = {lastVisited: new Date()}
PinnedGames.push(parseInt(PlaceID))
PinBtn.innerHTML = '<i class="fa-duotone fa-star"></i> Un-pin'
}
*/
const Index = PinnedGamesData.indexOf(parseInt(PlaceID));
if (Index !== -1) {
PinnedGamesData.splice(Index, 1);
PinBtn.innerHTML = '<i class="fa-duotone fa-star"></i> Pin';
} else {
PinnedGamesData.push(parseInt(PlaceID));
PinBtn.innerHTML = '<i class="fa-duotone fa-star"></i> Un-pin';
}
chrome.storage.sync.set({PolyPlus_PinnedGames: PinnedGamesData, arrayOrder: true}, function () { chrome.storage.sync.set({'PolyPlus_PinnedGames': placeIDs}, function() {
setTimeout(function () { PinButton.disabled = true;
PinBtn.removeAttribute('disabled'); setTimeout(() => {
console.log(PinnedGamesData); PinButton.disabled = false;
}, 1250); UpdatePinButtonState(); // Ensure state is updated after re-enabling
}); }, 750);
});
}); });
});
document.getElementsByClassName('card-header')[2].appendChild(PinBtn); chrome.storage.onChanged.addListener(function(changes) {
if ('PolyPlus_PinnedGames' in changes) {
chrome.storage.onChanged.addListener(function (changes, namespace) { placeIDs = changes.PolyPlus_PinnedGames.newValue;
if ('PolyPlus_PinnedGames' in changes) { UpdatePinButtonState();
chrome.storage.sync.get(['PolyPlus_PinnedGames'], function (result) { }
PinnedGamesData = result.PolyPlus_PinnedGames || [];
/*
if (PinnedGamesData[PlaceID]) {
PinBtn.innerHTML = '<i class="fa-duotone fa-star"></i> Un-pin'
} else {
if (PinnedGamesData.length !== 5) {
PinBtn.removeAttribute('disabled')
PinBtn.innerHTML = '<i class="fa-duotone fa-star"></i> Pin'
} else {
PinBtn.setAttribute('disabled', true)
PinBtn.innerHTML = '<i class="fa-duotone fa-star"></i> Pin (max 5/5)'
}
}
*/
if (PinnedGamesData.includes(parseInt(PlaceID))) {
PinBtn.innerHTML = '<i class="fa-duotone fa-star"></i> Un-pin';
} else {
if (PinnedGamesData.length !== Utilities.Limits.PinnedGames) {
PinBtn.removeAttribute('disabled');
PinBtn.innerHTML = '<i class="fa-duotone fa-star"></i> Pin';
} else {
PinBtn.setAttribute('disabled', true);
PinBtn.innerHTML = `<i class="fa-duotone fa-star"></i> Pin (max ${Utilities.Limits.PinnedGames}/${Utilities.Limits.PinnedGames})`;
}
}
});
}
});
}); });
} }
@ -383,8 +418,9 @@ async function IRLPrice() {
async function PlaceRevenue() { async function PlaceRevenue() {
const BricksPerView = 5; const BricksPerView = 5;
let PlaceDetails = await fetch('https://api.polytoria.com/v1/places/' + PlaceID); if (PlaceDetails === null) {
PlaceDetails = await PlaceDetails.json(); PlaceDetails = (await (await fetch('https://api.polytoria.com/v1/places/' + PlaceID)).json());
}
let CreatorDetails = await fetch('https://api.polytoria.com/v1/users/' + GameCreator); let CreatorDetails = await fetch('https://api.polytoria.com/v1/users/' + GameCreator);
CreatorDetails = await CreatorDetails.json(); CreatorDetails = await CreatorDetails.json();
@ -468,48 +504,96 @@ function AchievementProgressBar() {
AchievementsTab.prepend(ProgressBar); AchievementsTab.prepend(ProgressBar);
} }
function AchievementEarnedPercentage() { async function AchievementEarnedPercentage() {
fetch('https://api.polytoria.com/v1/places/' + PlaceID) if (PlaceDetails === null) {
.then((response) => { PlaceDetails = (await (await fetch('https://api.polytoria.com/v1/places/' + PlaceID)).json());
if (!response.ok) { }
throw new Error('Network not ok');
}
return response.json();
})
.then((data) => {
const UserCount = data.uniqueVisits;
for (let achievement of Achievements) {
const OwnerText = achievement.getElementsByClassName('text-muted small my-0')[0];
// thanks to Stackoverflow on how to remove everything except numbers from string
const OwnerCount = parseInt(OwnerText.innerText.replace(/[^0-9]/g, ''));
const PercentageOfPlayers = ((OwnerCount * 100) / UserCount).toFixed(2);
OwnerText.innerHTML += ' (' + PercentageOfPlayers + '%, ' + GetAchievementDifficulty(PercentageOfPlayers) + ')'; const GetAchievementDifficulty = function(percent) {
} if (percent >= 90 && percent <= 100) {
}) return 'Freebie';
.catch((error) => { } else if (percent >= 80 && percent <= 89.9) {
console.log(error); return 'Cake Walk';
}); } else if (percent >= 50 && percent <= 79.9) {
} return 'Easy';
} else if (percent >= 30 && percent <= 49.9) {
return 'Moderate';
} else if (percent >= 20 && percent <= 29.9) {
return 'Challenging';
} else if (percent >= 10 && percent <= 19.9) {
return 'Hard';
} else if (percent >= 5 && percent <= 9.9) {
return 'Extreme';
} else if (percent >= 1 && percent <= 4.9) {
return 'Insane';
} else if (percent >= 0 && percent <= 0.9) {
return 'Impossible';
}
}
function GetAchievementDifficulty(percent) { const UserCount = PlaceDetails.uniqueVisits;
if (percent >= 90 && percent <= 100) { for (let achievement of Achievements) {
return 'Freebie'; const OwnerText = achievement.getElementsByClassName('text-muted small my-0')[0];
} else if (percent >= 80 && percent <= 89.9) { // thanks to Stackoverflow on how to remove everything except numbers from string
return 'Cake Walk'; const OwnerCount = parseInt(OwnerText.innerText.replace(/[^0-9]/g, ''));
} else if (percent >= 50 && percent <= 79.9) { const PercentageOfPlayers = ((OwnerCount * 100) / UserCount).toFixed(2);
return 'Easy';
} else if (percent >= 30 && percent <= 49.9) { OwnerText.innerHTML += ' (' + PercentageOfPlayers + '%, ' + GetAchievementDifficulty(PercentageOfPlayers) + ')';
return 'Moderate'; }
} else if (percent >= 20 && percent <= 29.9) { }
return 'Challenging';
} else if (percent >= 10 && percent <= 19.9) { async function ReaddCopyable() {
return 'Hard'; if (PlaceDetails === null) {
} else if (percent >= 5 && percent <= 9.9) { PlaceDetails = (await (await fetch('https://api.polytoria.com/v1/places/' + PlaceID)).json());
return 'Extreme'; }
} else if (percent >= 1 && percent <= 4.9) {
return 'Insane'; if (PlaceDetails.isCopyable) {
} else if (percent >= 0 && percent <= 0.9) { const TitleCardButtons = document.querySelector('.card:has(h1.my-0) .col-auto[style^="m"]')
return 'Impossible';
const DownloadCopyButton = document.createElement('button')
DownloadCopyButton.style.padding = '9px'
DownloadCopyButton.classList = 'px-4 btn btn-success my-2'
DownloadCopyButton.setAttribute('data-bs-toggle', 'tooltip')
DownloadCopyButton.setAttribute('data-bs-title', 'Download this place')
DownloadCopyButton.innerHTML = '<i class="fas fa-download"></i>'
if (TitleCardButtons.children[0].children.length === 0) {
TitleCardButtons.children[0].innerHTML = '<div class="col-auto px-1"></div>'
TitleCardButtons.children[0].appendChild(DownloadCopyButton)
} else {
TitleCardButtons.children[0].prepend(DownloadCopyButton)
}
DownloadCopyButton.addEventListener('click', async function () {
let CreatorToken = (await (await fetch('https://polytoria.com/api/places/edit', {
method: 'POST',
body: JSON.stringify({placeID: PlaceID})
})).json()).token;
fetch(`https://api.polytoria.com/v1/places/get-place?id=${PlaceID}&tokenType=creator`, {
headers: {
Authorization: CreatorToken
}
})
.then((response) => {
if (!response.ok) {
throw new Error('Network not ok');
}
return response.blob();
})
.then((data) => {
const DownloadURL = URL.createObjectURL(data);
const Link = document.createElement('a');
Link.href = DownloadURL;
Link.download = PlaceDetails.name + '.poly';
document.body.appendChild(Link);
Link.click();
Link.remove();
})
.catch((error) => {
console.log(error);
});
});
} }
} }

View file

@ -1,33 +0,0 @@
/*
THIS FILE DOES NOT RUN
IT WAS ADDED WHILE I CONSIDER WHETHER OR NOT TO ADD PINNED GAMES TO THE MAIN PLACES PAGE
*/
let Settings;
let PinnedGamesData;
chrome.storage.sync.get(['PolyPlus_Settings', 'PolyPlus_PinnedGames'], function (result) {
Settings = result.PolyPlus_Settings || {};
PinnedGamesData = result.PolyPlus_PinnedGames || [];
if (Settings.PinnedGamesOn === true) {
}
});
function PinnedGames() {
for (let game of PinnedGamesData) {
fetch('https://api.polytoria.com/v1/places/' + game)
.then((response) => {
if (!response.ok) {
throw new Error('Network not ok');
}
return response.json();
})
.then((data) => {
// epic
})
.catch((error) => {
console.log(error);
});
}
}

View file

@ -1,5 +1,6 @@
const SettingsURL = chrome.runtime.getURL('settings.html'); const SettingsURL = chrome.runtime.getURL('settings.html');
const InExtensionSettings = window.location.pathname.split('/')[3] === 'polyplus' && window.location.hash !== '#dev' && window.location.hash !== '#debug'; const InExtensionSettings = window.location.pathname.split('/')[3] === 'polyplus' && window.location.hash !== '#dev' && window.location.hash !== '#debug';
if (InExtensionSettings === true) { if (InExtensionSettings === true) {
window.location.href = SettingsURL + window.location.hash; window.location.href = SettingsURL + window.location.hash;
} }

37
js/site-redirects.js Normal file
View file

@ -0,0 +1,37 @@
const URLs = [
{
Old: "/games/",
New: "/places/"
},
{
Old: "/shop/",
New: "/store/"
},
{
Old: "/my/referrals",
New: "/my/settings/referrals"
},
{
Old: "/create/?t=",
New: "/create/"
},
{
Old: "/user/",
New: "/users/"
},
{
Old: "/library/",
New: "/models/"
}
]
let Matches = URLs.filter((x) => window.location.pathname.startsWith(x.Old))
if (Matches.length > 0) {
let Match = Matches[0]
window.location.pathname = window.location.pathname.replace(Match.Old, Match.New)
}

View file

@ -2,208 +2,288 @@ var Settings;
let Theme = ``; let Theme = ``;
(async () => { (async () => {
let Utilities = await import(chrome.runtime.getURL('resources/utils.js')); let Utilities = await import(chrome.runtime.getURL('resources/utils.js'));
Utilities = Utilities.default; Utilities = Utilities.default;
chrome.storage.sync.get(['PolyPlus_Settings'], function (result) { chrome.storage.sync.get(['PolyPlus_Settings'], function (result) {
// Merge settings and expected settings to make sure all keys exist // Merge settings and expected settings to make sure all keys exist
const RawSettings = result.PolyPlus_Settings; const RawSettings = result.PolyPlus_Settings;
Settings = MergeObjects(RawSettings || Utilities.DefaultSettings, Utilities.DefaultSettings); Settings = Utilities.MergeObjects(RawSettings || Utilities.DefaultSettings, Utilities.DefaultSettings);
// If theme exists, create a style element to represent it const PageLoad = async function() {
if (Settings.ThemeCreator && Settings.ThemeCreator.Enabled === true) { if (document.getElementsByClassName('card-header')[0] && document.getElementsByClassName('card-header')[0].innerText === ' Page not found') {
switch (Settings.ThemeCreator.BGImageSize) { return;
case 0:
Settings.ThemeCreator.BGImageSize = 'fit';
break;
case 1:
Settings.ThemeCreator.BGImageSize = 'cover';
break;
case 2:
Settings.ThemeCreator.BGImageSize = 'contain';
break;
}
Theme += `
:root {
--polyplus-navbgcolor: ${Settings.ThemeCreator.NavBGColor};
--polyplus-navbordercolor: ${Settings.ThemeCreator.NavBorderColor};
--polyplus-navitemcolor: ${Settings.ThemeCreator.NavItemColor};
--polyplus-sidebarbgcolor: ${Settings.ThemeCreator.SideBGColor};
--polyplus-sidebarbordercolor: ${Settings.ThemeCreator.SideBorderColor};
--polyplus-sidebaritembgcolor: ${Settings.ThemeCreator.SideItemBGColor};
--polyplus-sidebaritembordercolor: ${Settings.ThemeCreator.SideItemBorderColor};
--polyplus-sidebaritemcolor: ${Settings.ThemeCreator.SideItemColor};
--polyplus-sidebaritemlabelcolor: ${Settings.ThemeCreator.SideItemLabelColor};
--polyplus-bgcolor: ${Settings.ThemeCreator.BGColor};
--polyplus-bgimage: url(${Settings.ThemeCreator.BGImage});
--polyplus-bgimagesize: ${Settings.ThemeCreator.BGImageSize};
--polyplus-primarytextcolor: ${Settings.ThemeCreator.PrimaryTextColor};
--polyplus-secondarytextcolor: ${Settings.ThemeCreator.SecondaryTextColor};
--polyplus-linktextcolor: ${Settings.ThemeCreator.LinkTextColor};
--polyplus-linkhoveredtextcolor: ${Settings.ThemeCreator.LinkHoveredTextColor};
--polyplus-linkfocusedtextcolor: ${Settings.ThemeCreator.LinkFocusedTextColor};
--polyplus-linkvisitedtextcolor: ${Settings.ThemeCreator.LinkVisitedTextColor};
--polyplus-cardheadbgcolor: ${Settings.ThemeCreator.CardHeadBGColor};
--polyplus-cardbodybgcolor: ${Settings.ThemeCreator.CardBodyBGColor};
--polyplus-cardbordercolor: ${Settings.ThemeCreator.CardBorderColor};
} }
nav { Utilities.InjectResource('getUserDetails');
background-color: var(--polyplus-navbgcolor) !important; document.body.setAttribute('data-URL', window.location.pathname);
border-bottom: 1px solid var(--polyplus-navbordercolor) !important;
if (Settings.IRLPriceWithCurrency && Settings.IRLPriceWithCurrency.Enabled === true) {
const IRLResult = await Utilities.CalculateIRL(document.querySelector('.brickBalanceCont').innerText.replace(/\s+/g, ''), Settings.IRLPriceWithCurrency.Currency);
// Desktop
document.querySelector('.text-success .brickBalanceCount').innerHTML += ` (${IRLResult.icon}${IRLResult.result} ${IRLResult.display})`;
// Mobile
document.querySelector('.text-success .brickBalanceCont').innerHTML += ` (${IRLResult.icon}${IRLResult.result} ${IRLResult.display})`;
//document.querySelector('.text-success .brickBalanceCont').innerHTML += `<div class="text-muted" style="font-size: 0.6rem;text-align: right;">(${IRLResult.icon}${IRLResult.result} ${IRLResult.display})</div>`
} }
.nav-sidebar { if (Settings.ModifyNavOn && Settings.ModifyNavOn === true) {
background-color: var(--polyplus-sidebarbgcolor) !important; let NavbarItems = document.querySelectorAll('.navbar-nav.me-auto a.nav-link[href]');
border-right: 1px solid var(--polyplus-sidebarbordercolor) !important; let Needed = [NavbarItems[11], NavbarItems[12], NavbarItems[13], NavbarItems[14], NavbarItems[15]];
for (let i = 0; i < Settings.ModifyNav.length; i++) {
if (Settings.ModifyNav[i].Label != null) {
Needed[i].children[1].innerText = Settings.ModifyNav[i].Label;
Needed[i].href = Settings.ModifyNav[i].Link;
}
}
} }
#app { if (Settings.HideUserAds && Settings.HideUserAds.Enabled === true) {
background-color: var(--polyplus-bgcolor) !important; if (Settings.HideUserAds.Banners && Settings.HideUserAds.Banners === true) {
background-image: var(--polyplus-bgimage) !important; Theme += `
background-size var(--polyplus-bgimagesize) div[style^="max-width: 728px;"]:has(a[href^="/ads"]) {
color: var(--polyplus-primarytextcolor) !important; display: none;
}
`;
}
if (Settings.HideUserAds.Rectangles && Settings.HideUserAds.Rectangles === true) {
Theme += `
div[style^="max-width: 300px;"]:has(a[href^="/ads"]) {
display: none;
}
`;
}
} }
.text-muted { if (Settings.HideNotifBadgesOn === true) {
color: var(--polyplus-secondarytextcolor) !important; document.getElementsByClassName('notifications-toggle')[0].getElementsByTagName('i')[0].classList = 'fa-bell far'
Theme += `
.notif-nav.notif-sidebar {
display: none;
}
.notifications-toggle {
opacity: 0.6;
}
.notifications-toggle .unread-indicator {
display: none;
}
`;
} }
a { // Credit to @SK-Fast (also known as DevPixels) for the improved loading code (taken from his original Poly+, and reformatted to Index Poly+)
color: var(--polyplus-linktextcolor) !important; const ThemeBlob = new Blob([Theme], { type: 'text/css' });
} const ThemeURL = URL.createObjectURL(ThemeBlob);
document.head.innerHTML += `<link href="${ThemeURL}" rel="stylesheet" type="text/css">`;
a:hover {
color: var(--polyplus-linkhoveredtextcolor) !important;
}
a:focus {
color: var(--polyplus-linkfocusedtextcolor) !important;
}
/* /*
a:visited { chrome.storage.local.get(['PolyPlus_AdCache'], async function(result){
color: var(--polyplus-linkvisitedtextcolor) !important; const AdCache = result.PolyPlus_AdCache || {};
}
*/
.card-header { const NewCacheAdditions = Array.from(document.querySelectorAll('a[href^="/ads"]')).map(async (ad) => {
background-color: var(--polyplus-cardheadbgcolor) !important; const AdID = ad.getAttribute('href').split('/')[2]
} if (Object.keys(AdCache).indexOf(AdID) === -1) {
const AssetURL = (await fetch(ad.href)).url
const AssetInfo = ((await fetch(AssetURL.replace('polytoria.com/', 'api.polytoria.com/v1/'))).json())
console.log(AssetInfo)
AdCache[AdID] = AssetInfo
}
})
.card { await Promise.all(NewCacheAdditions)
background-color: var(--polyplus-cardbodybgcolor) !important;
border-color: var(--polyplus-cardbordercolor) !important;
}
nav a.nav-link { console.log(AdCache)
color: var(--polyplus-navitemcolor) !important; })
} */
.nav-sidebar .nav-sidebar-button { if (/\/inbox\/messages\/[0-9]+\/compose/.test(window.location.pathname) && new URLSearchParams(window.location.search).has('anniversaryPreset')) {
background-color: var(--polyplus-sidebaritembgcolor) !important; const AnniversaryNumber = parseInt(new URLSearchParams(window.location.search).get('anniversaryPreset'))
border-color: var(--polyplus-sidebaritembordercolor) !important; const MessageSubject = document.querySelector('[action^="/inbox"] #subject')
color: var(--polyplus-sidebaritemcolor) !important; const MessageBody = document.querySelector('[action^="/inbox"] #body')
}
.nav-sidebar-text { const RandomAnniversaryMessage = [
color: var(--polyplus-sidebaritemlabelcolor) !important; {
} subject: "Happy :number+ Polytorian Anniversary!",
`; body: `Congratulations on your :number full year on Polytoria, :recipient! 🎉🎂🎈
}
});
if (Settings.HideUserAds && Settings.HideUserAds.Enabled === true) { Best wishes,
if (Settings.HideUserAds.Banners && Settings.HideUserAds.Banners === true) { - :username
Theme += ` `
div[style^="max-width: 728px;"]:has(a[href^="/ads"]) { },
display: none; {
} subject: `:number Year${ (AnniversaryNumber > 1) ? 's' : '' } on Polytoria!`,
`; body: `Happy :number+ Polytorian Anniversary, :recipient! 🎈🎈🎉🎉
}
if (Settings.HideUserAds.Rectangles && Settings.HideUserAds.Rectangles === true) { Yours truly,
Theme += ` - :username
div[style^="max-width: 300px;"]:has(a[href^="/ads"]) { `
display: none; },
} {
`; subject: `:number Year${ (AnniversaryNumber > 1) ? 's' : '' } of Polytorian Fun!`,
} body: `I am so proud of you for reaching :number years on Polytoria, :recipient! 🎉🎂🎈
}
if (Settings.HideNotifBadgesOn === true) { Warm regards,
Theme += ` - :username
.notif-nav .notif-sidebar { `
display: none; },
{
subject: "Congratulations on :number Years on Polytoria!",
body: `:recipient, you have been on Polytoria for :number ${ (AnniversaryNumber === 1) ? 'whole' : 'full' } years! I am writing to congratulate you on this amazing achievement! 🎉🎂🎈
With love and fun,
- :username
`
}
][Math.floor(Math.random() * 3) + 1]
MessageSubject.value = RandomAnniversaryMessage.subject.replace(':number', AnniversaryNumber).replace(':number+', AnniversaryNumber + (AnniversaryNumber % 10 === 1 && AnniversaryNumber % 100 !== 11 ? 'st' : AnniversaryNumber % 10 === 2 && AnniversaryNumber % 100 !== 12 ? 'nd' : AnniversaryNumber % 10 === 3 && AnniversaryNumber % 100 !== 13 ? 'rd' : 'th'))
setTimeout(() => {
const NewMessageBody = document.createElement('textarea')
NewMessageBody.classList = 'form-control'
NewMessageBody.id = 'body'
NewMessageBody.name = 'body'
NewMessageBody.rows = '16'
NewMessageBody.innerHTML = RandomAnniversaryMessage.body.replace(':number', AnniversaryNumber).replace(':number+', AnniversaryNumber + (AnniversaryNumber % 10 === 1 && AnniversaryNumber % 100 !== 11 ? 'st' : AnniversaryNumber % 10 === 2 && AnniversaryNumber % 100 !== 12 ? 'nd' : AnniversaryNumber % 10 === 3 && AnniversaryNumber % 100 !== 13 ? 'rd' : 'th')).replace(':recipient', document.querySelector('[action^="/inbox"] a[href^="/u"]').innerText).replace(':username', document.querySelector('a[href^="/u"]:has(.dropdown-item):first-child').innerText.replaceAll('\n', '').replaceAll('\t', '').trim())
MessageBody.parentElement.appendChild(NewMessageBody)
MessageBody.remove()
}, 100);
}
} }
`;
}
// Credit to @SK-Fast (also known as DevPixels) for the improved loading code (taken from his original Poly+, and reformatted to Index Poly+) if (document.readyState === 'complete' || document.readyState === 'interactive') {
const ThemeBlob = new Blob([Theme], {type: 'text/css'}); PageLoad()
const ThemeURL = URL.createObjectURL(ThemeBlob); } else {
document.head.innerHTML += `<link href="${ThemeURL}" rel="stylesheet" type="text/css">`; document.addEventListener('DOMContentLoaded', PageLoad);
document.addEventListener('DOMContentLoaded', async function () {
if (document.getElementsByClassName('card-header')[0] && document.getElementsByClassName('card-header')[0].innerText === ' Page not found') {
return;
}
Utilities.InjectResource('getUserDetails');
document.body.setAttribute('data-URL', window.location.pathname);
const UserData = JSON.parse(window.localStorage.getItem('p+account_info'));
if (Settings.IRLPriceWithCurrency && Settings.IRLPriceWithCurrency.Enabled === true) {
const IRLResult = await Utilities.CalculateIRL(document.querySelector('.brickBalanceCont').innerText.replace(/\s+/g, ''), Settings.IRLPriceWithCurrency.Currency);
// Desktop
document.querySelector('.text-success .brickBalanceCount').innerHTML += ` (${IRLResult.icon}${IRLResult.result} ${IRLResult.display})`;
// Mobile
document.querySelector('.text-success .brickBalanceCont').innerHTML += ` (${IRLResult.icon}${IRLResult.result} ${IRLResult.display})`;
//document.querySelector('.text-success .brickBalanceCont').innerHTML += `<div class="text-muted" style="font-size: 0.6rem;text-align: right;">(${IRLResult.icon}${IRLResult.result} ${IRLResult.display})</div>`
}
if (Settings.ModifyNavOn && Settings.ModifyNavOn === true) {
let NavbarItems = document.querySelectorAll('.navbar-nav.me-auto a.nav-link[href]');
let Needed = [NavbarItems[11], NavbarItems[12], NavbarItems[13], NavbarItems[14], NavbarItems[15]];
for (let i = 0; i < Settings.ModifyNav.length; i++) {
if (Settings.ModifyNav[i].Label != null) {
console.log(Needed[i], Needed[i].children[1]);
Needed[i].children[1].innerText = Settings.ModifyNav[i].Label;
Needed[i].href = Settings.ModifyNav[i].Link;
}
}
}
/*
if (Settings.HideUserAdsOn === true) {
Array.from(document.querySelectorAll('.text-center:has(a[href^="/ads"])')).forEach(ad => {ad.remove()})
} }
*/
if (Settings.HideNotifBadgesOn === true) { // Apply Theme
document.getElementsByClassName('notif-nav notif-sidebar').forEach((element) => { LoadTheme();
element.remove();
}); const combination = "reload";
} let currentCombination = "";
}); document.addEventListener("keypress", function (e) {
currentCombination += e.key;
if (currentCombination === combination && document.activeElement.tagName !== "INPUT" && document.activeElement.tagName !== "TEXTAREA") {
try {
console.log("Reloading Poly+...");
chrome.runtime.sendMessage({ action: "reload" });
window.location.reload();
} catch(error) {
console.log("Reloading Poly+...");
window.location.reload()
chrome.runtime.sendMessage({ action: "reload" });
window.location.reload();
}
} else if (!combination.startsWith(currentCombination)) {
currentCombination = "";
}
});
});
})(); })();
function MergeObjects(obj1, obj2) { function LoadTheme() {
var mergedObj = {}; if (!Settings.ThemeCreator || Settings.ThemeCreator.Enabled !== true) { return }
// Copy the values from obj1 to the mergedObj switch (Settings.ThemeCreator.BGImageSize) {
for (var key in obj1) { case 0:
mergedObj[key] = obj1[key]; Settings.ThemeCreator.BGImageSize = 'fit';
} break;
case 1:
Settings.ThemeCreator.BGImageSize = 'cover';
break;
case 2:
Settings.ThemeCreator.BGImageSize = 'contain';
break;
}
Theme += `
:root {
--polyplus-navbgcolor: ${Settings.ThemeCreator.NavBGColor};
--polyplus-navbordercolor: ${Settings.ThemeCreator.NavBorderColor};
--polyplus-navitemcolor: ${Settings.ThemeCreator.NavItemColor};
--polyplus-sidebarbgcolor: ${Settings.ThemeCreator.SideBGColor};
--polyplus-sidebarbordercolor: ${Settings.ThemeCreator.SideBorderColor};
--polyplus-sidebaritembgcolor: ${Settings.ThemeCreator.SideItemBGColor};
--polyplus-sidebaritembordercolor: ${Settings.ThemeCreator.SideItemBorderColor};
--polyplus-sidebaritemcolor: ${Settings.ThemeCreator.SideItemColor};
--polyplus-sidebaritemlabelcolor: ${Settings.ThemeCreator.SideItemLabelColor};
--polyplus-bgcolor: ${Settings.ThemeCreator.BGColor};
--polyplus-bgimage: url(${Settings.ThemeCreator.BGImage});
--polyplus-bgimagesize: ${Settings.ThemeCreator.BGImageSize};
--polyplus-primarytextcolor: ${Settings.ThemeCreator.PrimaryTextColor};
--polyplus-secondarytextcolor: ${Settings.ThemeCreator.SecondaryTextColor};
--polyplus-linktextcolor: ${Settings.ThemeCreator.LinkTextColor};
--polyplus-linkhoveredtextcolor: ${Settings.ThemeCreator.LinkHoveredTextColor};
--polyplus-linkfocusedtextcolor: ${Settings.ThemeCreator.LinkFocusedTextColor};
--polyplus-linkvisitedtextcolor: ${Settings.ThemeCreator.LinkVisitedTextColor};
--polyplus-cardheadbgcolor: ${Settings.ThemeCreator.CardHeadBGColor};
--polyplus-cardbodybgcolor: ${Settings.ThemeCreator.CardBodyBGColor};
--polyplus-cardbordercolor: ${Settings.ThemeCreator.CardBorderColor};
}
// Merge the values from obj2 into the mergedObj, favoring obj2 for non-existing keys in obj1 nav {
for (var key in obj2) { background-color: var(--polyplus-navbgcolor) !important;
if (!obj1.hasOwnProperty(key)) { border-bottom: 1px solid var(--polyplus-navbordercolor) !important;
mergedObj[key] = obj2[key]; }
}
}
return mergedObj; .nav-sidebar {
background-color: var(--polyplus-sidebarbgcolor) !important;
border-right: 1px solid var(--polyplus-sidebarbordercolor) !important;
}
#app {
background-color: var(--polyplus-bgcolor) !important;
background-image: var(--polyplus-bgimage) !important;
background-size var(--polyplus-bgimagesize)
color: var(--polyplus-primarytextcolor) !important;
}
.text-muted {
color: var(--polyplus-secondarytextcolor) !important;
}
a {
color: var(--polyplus-linktextcolor) !important;
}
a:hover {
color: var(--polyplus-linkhoveredtextcolor) !important;
}
a:focus {
color: var(--polyplus-linkfocusedtextcolor) !important;
}
/*
a:visited {
color: var(--polyplus-linkvisitedtextcolor) !important;
}
*/
.card-header {
background-color: var(--polyplus-cardheadbgcolor) !important;
}
.card {
background-color: var(--polyplus-cardbodybgcolor) !important;
border-color: var(--polyplus-cardbordercolor) !important;
}
nav a.nav-link {
color: var(--polyplus-navitemcolor) !important;
}
.nav-sidebar .nav-sidebar-button {
background-color: var(--polyplus-sidebaritembgcolor) !important;
border-color: var(--polyplus-sidebaritembordercolor) !important;
color: var(--polyplus-sidebaritemcolor) !important;
}
.nav-sidebar-text {
color: var(--polyplus-sidebaritemlabelcolor) !important;
}
`;
} }

View file

@ -1,6 +1,9 @@
const ItemID = window.location.pathname.split('/')[2]; const ItemID = window.location.pathname.split('/')[2];
if (window.location.pathname.slice(-6) == "/asset") {
window.location.href = "https://api.polytoria.com/v1/assets/serve-mesh/" + ItemID
}
let ItemModeler = null;
const ItemType = document.querySelector('.row .badge').innerHTML; const ItemType = document.querySelector('.row .badge').innerHTML;
console.log(ItemType);
var Settings; var Settings;
var ItemWishlist; var ItemWishlist;
@ -19,11 +22,8 @@ var Utilities;
chrome.storage.sync.get(['PolyPlus_Settings'], async function (result) { chrome.storage.sync.get(['PolyPlus_Settings'], async function (result) {
Settings = result.PolyPlus_Settings || {}; Settings = result.PolyPlus_Settings || {};
PurchaseBtn = document.querySelector('.btn[onclick^="buy"]'); PurchaseBtn = document.querySelector('.col:has(h1) .justify-content-lg-end .d-flex .flex-grow-1:nth-child(2) :nth-child(1)')
if (PurchaseBtn === null) { ItemOwned = PurchaseBtn.innerText === ' Item owned' || PurchaseBtn.innerText === ' You earned this achievement' || document.querySelector('.btn[onclick="sellItem()"]') !== null;
PurchaseBtn = document.querySelector('.btn#purchase-button');
}
ItemOwned = PurchaseBtn.innerText === ' Item owned' || document.querySelector('.btn[onclick="sellItem()"]') !== null;
if (PurchaseBtn.getAttribute('data-seller-name')) { if (PurchaseBtn.getAttribute('data-seller-name')) {
PurchaseBtn.setAttribute('data-bs-toggle', 'tooltip'); PurchaseBtn.setAttribute('data-bs-toggle', 'tooltip');
@ -31,25 +31,23 @@ var Utilities;
Utilities.InjectResource('registerTooltips'); Utilities.InjectResource('registerTooltips');
} }
if (Settings.IRLPriceWithCurrency && Settings.IRLPriceWithCurrency.Enabled === true && ItemOwned === false) { if (Settings.IRLPriceWithCurrency && Settings.IRLPriceWithCurrency.Enabled === true) {
IRLPrice(); IRLPrice();
} }
if (Settings.ItemWishlistOn === true) {
HandleItemWishlist();
}
if (Settings.TryOnItemsOn === true && (Utilities.MeshTypes.indexOf(ItemType.toLowerCase()) !== -1 || Utilities.TextureTypes.indexOf(ItemType.toLowerCase()) !== -1)) { if (Settings.TryOnItemsOn === true && (Utilities.MeshTypes.indexOf(ItemType.toLowerCase()) !== -1 || Utilities.TextureTypes.indexOf(ItemType.toLowerCase()) !== -1)) {
TryOnItems(); TryOnItems();
} }
if (Settings.ReplaceItemSalesOn === true) { if (Settings.ReplaceItemSalesOn === true) {
const Sales = document.querySelectorAll('.col:has(h6):has(h3.small)')[2]; const Sales = document.querySelectorAll('.col:has(h6):has(h3.small)')[3];
if (Sales.children[1].innerText === '0') { if (Sales.children[1].innerText === '0') {
InitialOwners = await (await fetch('https://api.polytoria.com/v1/store/' + ItemID + '/owners?limit=100')).json(); InitialOwners = await (await fetch('https://api.polytoria.com/v1/store/' + ItemID + '/owners?limit=100')).json();
Sales.children[0].innerText = 'Owners'; Sales.children[0].innerHTML = `Owners <i class="fa-solid fa-circle-info" data-bs-toggle="tooltip" data-bs-title="${ (InitialOwners.total <= 15) ? InitialOwners.inventories.map((x) => x.user.username).join(', ') : InitialOwners.inventories.slice(0, 14).map((x) => x.user.username).join(', ') + ' ...' }"></i>`;
Sales.children[1].innerText = Owners.total.toLocaleString(); Sales.children[1].innerText = InitialOwners.total.toLocaleString();
Utilities.InjectResource("registerTooltips")
} }
} }
@ -57,9 +55,45 @@ var Utilities;
if (Settings.HoardersList && Settings.HoardersList.Enabled === true) { if (Settings.HoardersList && Settings.HoardersList.Enabled === true) {
HoardersList(parseInt(Settings.HoardersList.MinCopies), Settings.HoardersList.AvatarsEnabled); HoardersList(parseInt(Settings.HoardersList.MinCopies), Settings.HoardersList.AvatarsEnabled);
} }
} else if (Settings.ItemOwnerCheckOn === true && document.getElementById('timer') && /\d/.test(document.getElementById('timer').innerText)) {
if (
Settings.ValueListInfo &&
Settings.ValueListInfo.Enabled == true &&
Settings.ValueListInfo.ItemValuation == true
) {
ValueListData()
}
}
if (Settings.ItemOwnerCheckOn === true && ((document.getElementById('timer') !== null && /\d/.test(document.getElementById('timer').innerText) || document.getElementById('resellers') !== null))) {
CheckOwner(); CheckOwner();
} }
if (Settings.ItemWishlistOn === true) {
HandleItemWishlist();
}
if (Settings.AssetDesignerCreditOn === true) {
chrome.storage.local.get(['PolyPlus_AssetDesigners'], async function(result){
let AssetDesignerData = result['PolyPlus_AssetDesigners'];
// cache for 5 minutes
if (AssetDesignerData === undefined || (new Date().getTime() - AssetDesignerData.requested > 300000)) {
AssetDesignerData = await ((await fetch('https://polyplus.vercel.app/data/itemModelers.json')).json());
chrome.storage.local.set({['PolyPlus_AssetDesigners']: {
data: AssetDesignerData,
requested: new Date().getTime()
}}, function(){});
} else {
AssetDesignerData = AssetDesignerData.data
}
if (AssetDesignerData[ItemID]) {
AssetDesignerInfo(AssetDesignerData)
}
});
}
}); });
})(); })();
@ -69,23 +103,20 @@ chrome.storage.onChanged.addListener(function (changes, namespace) {
ItemWishlist = result.PolyPlus_ItemWishlist || []; ItemWishlist = result.PolyPlus_ItemWishlist || [];
if (Array.isArray(ItemWishlist) && ItemWishlist.includes(parseInt(ItemID))) { if (Array.isArray(ItemWishlist) && ItemWishlist.includes(parseInt(ItemID))) {
WishlistBtn.classList = 'btn btn-danger btn-sm';
WishlistBtn.innerHTML = ` WishlistBtn.innerHTML = `
<i class="fa fa-star" style="margin-right: 2.5px;"></i> Un-Wishlist Item <i class="fa-solid fa-star" style="color: orange;"></i>
`; `;
} else { } else {
if (!(ItemWishlist.length === 25)) { if (!(ItemWishlist.length === 25)) {
WishlistBtn.removeAttribute('disabled'); WishlistBtn.removeAttribute('disabled');
WishlistBtn.classList = 'btn btn-warning btn-sm';
WishlistBtn.innerHTML = ` WishlistBtn.innerHTML = `
<i class="fa fa-star" style="margin-right: 2.5px;"></i> Wishlist Item <i class="fa-regular fa-star"></i>
`; `;
} else { } else {
WishlistBtn.setAttribute('disabled', true); WishlistBtn.setAttribute('disabled', true);
WishlistBtn.classList = 'btn btn-warning btn-sm';
WishlistBtn.innerHTML = ` WishlistBtn.innerHTML = `
<i class="fa fa-star" style="margin-right: 2.5px;"></i> Wishlist Item <i class="fad fa-star"></i>
`; `;
} }
} }
}); });
@ -94,21 +125,52 @@ chrome.storage.onChanged.addListener(function (changes, namespace) {
async function IRLPrice() { async function IRLPrice() {
const Price = PurchaseBtn.getAttribute('data-price'); const Price = PurchaseBtn.getAttribute('data-price');
if (Price === null || Price === '0') { if (Price !== null && Price !== '0') {
return; const Span = document.createElement('span');
Span.classList = 'text-muted polyplus-own-tag';
Span.style.fontSize = '0.7rem';
Span.style.fontWeight = 'normal';
const IRLResult = await Utilities.CalculateIRL(Price, Settings.IRLPriceWithCurrency.Currency);
Span.innerText = '($' + IRLResult.result + ' ' + IRLResult.display + ')';
PurchaseBtn.appendChild(Span);
}
const Resellers = document.getElementById('resellers-container')
if (Resellers !== null) {
const observer = new MutationObserver(async function (list) {
for (const record of list) {
for (const element of record.addedNodes) {
if (element.tagName === 'DIV') {
const ResellerSpan = document.createElement('span');
ResellerSpan.classList = 'text-muted polyplus-own-tag';
ResellerSpan.style.fontSize = '0.7rem';
ResellerSpan.style.fontWeight = 'normal';
const ResellerIRLResult = await Utilities.CalculateIRL(element.getElementsByClassName('text-success')[0].innerText, Settings.IRLPriceWithCurrency.Currency);
ResellerSpan.innerText = ' ($' + ResellerIRLResult.result + ' ' + ResellerIRLResult.display + ')';
element.getElementsByClassName('text-success')[0].appendChild(ResellerSpan);
}
}
observer.observe(Resellers, {attributes: false, childList: true, subtree: false});
}
});
observer.observe(Resellers, {attributes: false, childList: true, subtree: false});
} }
const Span = document.createElement('span');
Span.classList = 'text-muted polyplus-own-tag';
Span.style.fontSize = '0.7rem';
Span.style.fontWeight = 'normal';
const IRLResult = await Utilities.CalculateIRL(Price, Settings.IRLPriceWithCurrency.Currency);
Span.innerText = '($' + IRLResult.result + ' ' + IRLResult.display + ')';
PurchaseBtn.appendChild(Span);
} }
function HandleItemWishlist() { function HandleItemWishlist() {
const DescriptionText = document.querySelector('.mcard .card-body:has(p)'); const DescriptionText = document.querySelector('.mcard .card-body:has(p)');
WishlistBtn = document.createElement('button'); const Column = document.createElement('div')
Column.classList = 'col'
Column.style = 'text-align: right; --bs-gutter-x: 0px;'
Column.innerHTML = `
<button class="btn btn-sm btn-link"></button>
`
const ItemNameHeading = document.getElementsByTagName('h1')[0]
ItemNameHeading.parentElement.parentElement.prepend(Column)
ItemNameHeading.parentElement.classList.add('col-auto')
ItemNameHeading.parentElement.style.paddingLeft = '0px !important'
WishlistBtn = Column.children[0];
chrome.storage.sync.get(['PolyPlus_ItemWishlist'], function (result) { chrome.storage.sync.get(['PolyPlus_ItemWishlist'], function (result) {
ItemWishlist = result.PolyPlus_ItemWishlist || []; ItemWishlist = result.PolyPlus_ItemWishlist || [];
@ -127,15 +189,13 @@ function HandleItemWishlist() {
} }
if (ItemWishlist.includes(parseInt(ItemID))) { if (ItemWishlist.includes(parseInt(ItemID))) {
WishlistBtn.classList = 'btn btn-danger btn-sm';
WishlistBtn.innerHTML = ` WishlistBtn.innerHTML = `
<i class="fa fa-star" style="margin-right: 2.5px;"></i> Un-Wishlist Item <i class="fa-solid fa-star" style="color: orange;"></i>
`; `;
} else { } else {
WishlistBtn.classList = 'btn btn-warning btn-sm';
WishlistBtn.innerHTML = ` WishlistBtn.innerHTML = `
<i class="fa fa-star" style="margin-right: 2.5px;"></i> Wishlist Item <i class="fa-regular fa-star"></i>
`; `;
} }
WishlistBtn.addEventListener('click', function () { WishlistBtn.addEventListener('click', function () {
@ -146,16 +206,14 @@ function HandleItemWishlist() {
let i = ItemWishlist.indexOf(parseInt(ItemID)); let i = ItemWishlist.indexOf(parseInt(ItemID));
if (i !== -1) { if (i !== -1) {
ItemWishlist.splice(i, 1); ItemWishlist.splice(i, 1);
WishlistBtn.classList = 'btn btn-warning btn-sm';
WishlistBtn.innerHTML = ` WishlistBtn.innerHTML = `
<i class="fa fa-star" style="margin-right: 2.5px;"></i> Wishlist Item <i class="fa-solid fa-star" style="color: orange;"></i>
`; `;
} else { } else {
ItemWishlist.push(parseInt(ItemID)); ItemWishlist.push(parseInt(ItemID));
WishlistBtn.classList = 'btn btn-danger btn-sm';
WishlistBtn.innerHTML = ` WishlistBtn.innerHTML = `
<i class="fa fa-star" style="margin-right: 2.5px;"></i> Un-Wishlist Item <i class="fa-regular fa-star"></i>
`; `;
} }
chrome.storage.sync.set({PolyPlus_ItemWishlist: ItemWishlist, arrayOrder: true}, function () { chrome.storage.sync.set({PolyPlus_ItemWishlist: ItemWishlist, arrayOrder: true}, function () {
@ -166,8 +224,8 @@ function HandleItemWishlist() {
}); });
}); });
DescriptionText.appendChild(document.createElement('br')); //DescriptionText.appendChild(document.createElement('br'));
DescriptionText.appendChild(WishlistBtn); //DescriptionText.appendChild(WishlistBtn);
}); });
} }
@ -175,8 +233,6 @@ function TryOnItems() {
const Avatar = { const Avatar = {
useCharacter: true, useCharacter: true,
items: [], items: [],
shirt: 'https://c0.ptacdn.com/assets/uWrrnFGwgNN5W171vqYTWY7E639rKiXK.png',
pants: 'https://c0.ptacdn.com/assets/HD6TFdXD8CaflRNmd84VCNyNsmTB0SH3.png',
headColor: '#e0e0e0', headColor: '#e0e0e0',
torsoColor: '#e0e0e0', torsoColor: '#e0e0e0',
leftArmColor: '#e0e0e0', leftArmColor: '#e0e0e0',
@ -190,7 +246,7 @@ function TryOnItems() {
const ItemThumbnail = document.getElementsByClassName('store-thumbnail')[0]; const ItemThumbnail = document.getElementsByClassName('store-thumbnail')[0];
//const IFrame = document.getElementsByClassName('store-thumbnail-3d')[0] //const IFrame = document.getElementsByClassName('store-thumbnail-3d')[0]
const TryIFrame = document.createElement('iframe'); const TryIFrame = document.createElement('iframe');
TryIFrame.style = 'width: 100%; height: auto; aspect-ratio: 1; border-radius: 20px;'; TryIFrame.style = 'width: 100%; height: auto; aspect-ratio: 1; border-radius: 20px; background: #1e1e1e;';
const TryOnBtn = document.createElement('button'); const TryOnBtn = document.createElement('button');
TryOnBtn.classList = 'btn btn-warning'; TryOnBtn.classList = 'btn btn-warning';
@ -211,22 +267,18 @@ function TryOnItems() {
}) })
.then((data) => { .then((data) => {
data.assets.forEach((item) => { data.assets.forEach((item) => {
switch (item.type) { if (item.type !== AssetType.toLowerCase()) {
case 'hat': if (item.type === 'hat') {
Avatar.items[Avatar.items.length] = item.path || ''; Avatar.items[Avatar.items.length] = item.path || '';
break; } else if (item.type === 'face') {
case 'face':
Avatar.face = item.path || ''; Avatar.face = item.path || '';
break; } else if (item.type === 'tool') {
case 'tool':
Avatar.tool = item.path || ''; Avatar.tool = item.path || '';
break; } else if (item.type === 'shirt') {
case 'shirt':
Avatar.shirt = item.path || ''; Avatar.shirt = item.path || '';
break; } else if (item.type === 'pants') {
case 'pants':
Avatar.pants = item.path || ''; Avatar.pants = item.path || '';
break; }
} }
}); });
@ -246,13 +298,12 @@ function TryOnItems() {
return response.json(); return response.json();
}) })
.then((data) => { .then((data) => {
if (ItemType === 'tool') { if (AssetType === 'Tool') {
Avatar.tool = data.url; Avatar.tool = data.url;
} else { } else {
Avatar.items.push(data.url); Avatar.items.push(data.url);
} }
console.log(Avatar);
TryIFrame.src = 'https://polytoria.com/ptstatic/itemview/#' + btoa(encodeURIComponent(JSON.stringify(Avatar))); TryIFrame.src = 'https://polytoria.com/ptstatic/itemview/#' + btoa(encodeURIComponent(JSON.stringify(Avatar)));
}) })
.catch((error) => { .catch((error) => {
@ -269,15 +320,12 @@ function TryOnItems() {
.then((data) => { .then((data) => {
switch (AssetType) { switch (AssetType) {
case 'Shirt': case 'Shirt':
console.log('IS SHIRT');
Avatar.shirt = data.url; Avatar.shirt = data.url;
break; break;
case 'Pants': case 'Pants':
console.log('IS PANTS');
Avatar.pants = data.url; Avatar.pants = data.url;
break; break;
case 'Face': case 'Face':
console.log('IS FACE');
Avatar.face = data.url; Avatar.face = data.url;
break; break;
} }
@ -303,7 +351,7 @@ function TryOnItems() {
<div class="row text-muted mb-2" style="font-size: 0.8rem;"> <div class="row text-muted mb-2" style="font-size: 0.8rem;">
<div class="col"> <div class="col">
<h5 class="mb-0" style="color: #fff;">Preview</h5> <h5 class="mb-0" style="color: #fff;">Preview</h5>
Try this avatar on your avatar before purchasing it! Try this item on your avatar before purchasing it!
</div> </div>
<div class="col-md-2"> <div class="col-md-2">
<button class="btn btn-info w-100 mx-auto" onclick="this.parentElement.parentElement.parentElement.close();">X</button> <button class="btn btn-info w-100 mx-auto" onclick="this.parentElement.parentElement.parentElement.close();">X</button>
@ -311,13 +359,11 @@ function TryOnItems() {
</div> </div>
</div> </div>
<div class="modal-body"></div> <div class="modal-body"></div>
`; `;
document.body.prepend(TryOnModal); document.body.prepend(TryOnModal);
ItemThumbnail.parentElement.appendChild(TryOnBtn); ItemThumbnail.parentElement.appendChild(TryOnBtn);
TryOnModal.children[1].prepend(TryIFrame); TryOnModal.children[1].prepend(TryIFrame);
//Utilities.InjectResource('registerTooltips')
} }
async function HoardersList(min, avatars) { async function HoardersList(min, avatars) {
@ -327,21 +373,21 @@ async function HoardersList(min, avatars) {
const Tab = document.createElement('li'); const Tab = document.createElement('li');
Tab.classList = 'nav-item'; Tab.classList = 'nav-item';
Tab.innerHTML = ` Tab.innerHTML = `
<a class="nav-link"> <a class="nav-link">
<i class="fas fa-calculator me-1"></i> <i class="fas fa-calculator me-1"></i>
<span class="d-none d-sm-inline"><span class="pilltitle">Hoarders</span> <span class="d-none d-sm-inline"><span class="pilltitle">Hoarders</span>
</a> </a>
`; `;
Tabs.appendChild(Tab); Tabs.appendChild(Tab);
const TabContent = document.createElement('div'); const TabContent = document.createElement('div');
TabContent.classList = 'd-none'; TabContent.classList = 'd-none';
TabContent.innerHTML = ` TabContent.innerHTML = `
<small class="d-block text-center text-muted" style="font-size: 0.8rem;"> <small class="d-block text-center text-muted" style="font-size: 0.8rem;">
loading... (this may take a few seconds) Loading... (this may take a few seconds)
</small> </small>
<lottie-player id="avatar-loading" src="https://c0.ptacdn.com/static/images/lottie/poly-brick-loading.2b51aa85.json" background="transparent" speed="1" style="width: 20%;height: auto;margin: -16px auto 50px;margin-top: 0px;" loop="" autoplay=""></lottie-player> <lottie-player id="avatar-loading" src="https://c0.ptacdn.com/static/images/lottie/poly-brick-loading.2b51aa85.json" background="transparent" speed="1" style="width: 20%;height: auto;margin: -16px auto 50px;margin-top: 0px;" loop="" autoplay=""></lottie-player>
`; `;
document.getElementById('owners').parentElement.appendChild(TabContent); document.getElementById('owners').parentElement.appendChild(TabContent);
// Add tab logic // Add tab logic
@ -409,11 +455,9 @@ async function HoardersList(min, avatars) {
try { try {
AvatarsFetched++ AvatarsFetched++
const Avatar = (await (await fetch('https://api.polytoria.com/v1/users/' + hoarder.user.id)).json()); const Avatar = (await (await fetch('https://api.polytoria.com/v1/users/' + hoarder.user.id)).json());
console.log(hoarder.user.username, Avatar)
hoarder.user.avatar = Avatar.thumbnail.icon; hoarder.user.avatar = Avatar.thumbnail.icon;
} catch(error) { } catch(error) {
hoarder.user.avatar = '' hoarder.user.avatar = ''
console.log(hoarder.user.username + ` (${hoarder.user.id}) - avatar failed`)
} }
} }
} }
@ -577,7 +621,6 @@ async function HoardersList(min, avatars) {
}); });
const UpdateHoardersList = function () { const UpdateHoardersList = function () {
console.log(Hoarders, AmountOfHoarders, Groups);
Current.innerText = Page + 1; Current.innerText = Page + 1;
if (Groups[Page] !== undefined) { if (Groups[Page] !== undefined) {
@ -648,13 +691,13 @@ function CheckOwner() {
const CheckOwnerDiv = document.createElement('div'); const CheckOwnerDiv = document.createElement('div');
CheckOwnerDiv.classList = 'mt-3 d-none'; CheckOwnerDiv.classList = 'mt-3 d-none';
CheckOwnerDiv.innerHTML = ` CheckOwnerDiv.innerHTML = `
<div class="input-group mb-2"> <div class="input-group mb-2">
<input type="text" class="form-control bg-dark" placeholder="Username.."> <input type="text" class="form-control bg-dark" placeholder="Username..">
<button class="btn btn-success">Check</button> <button class="btn btn-success">Check</button>
</div> </div>
<b class="text-muted" style="font-size: 0.85rem;"><i class="fa-duotone fa-square-question mr-1"></i> ...</b> <b class="text-muted" style="font-size: 0.85rem;"><i class="fa-duotone fa-square-question mr-1"></i> ...</b>
`; `;
ImageCard.appendChild(CheckOwnerDiv); ImageCard.appendChild(CheckOwnerDiv);
@ -701,10 +744,10 @@ function CheckOwner() {
if (Owns.owned === true) { if (Owns.owned === true) {
ResultText.classList = 'text-success'; ResultText.classList = 'text-success';
ResultText.innerHTML = '<i class="fa-duotone fa-circle-check mr-1"></i> ' + Username + ' owns #' + Owns.inventory.serial + ' of ' + document.getElementsByTagName('h1')[0].innerText + '".'; ResultText.innerHTML = '<i class="fa-duotone fa-circle-check mr-1"></i> ' + Username + ' owns #' + Owns.inventory.serial + ' of "' + document.getElementsByTagName('h1')[0].innerText + '".';
} else { } else {
ResultText.classList = 'text-danger'; ResultText.classList = 'text-danger';
ResultText.innerHTML = '<i class="fa-duotone fa-circle-check mr-1"></i> ' + Username + ' does not own "' + document.getElementsByTagName('h1')[0].innerText + '".'; ResultText.innerHTML = '<i class="fa-duotone fa-circle-exclamation mr-1"></i> ' + Username + ' does not own "' + document.getElementsByTagName('h1')[0].innerText + '".';
} }
} else { } else {
ResultText.classList = 'text-warning'; ResultText.classList = 'text-warning';
@ -712,3 +755,202 @@ function CheckOwner() {
} }
}); });
} }
async function ValueListDataNew() {
let Tabs = document.getElementById('store-tabs');
const ValueSection = document.createElement('div')
ValueSection.classList = 'mb-3'
ValueSection.innerHTML = `
<h6 class="section-title mt-3 mt-lg-0 mb-3 px-2">
Valuation <a href="https://polytoria.trade/" target="_blank">(based off LOVE)</a>
</h6>
<div class="card" id="p+valuation_card">
<div class="card-body">
<small class="d-block text-center text-muted" style="font-size: 0.8rem;">
Loading...
</small>
<lottie-player id="avatar-loading" src="https://c0.ptacdn.com/static/images/lottie/poly-brick-loading.2b51aa85.json" background="transparent" speed="1" style="width: 20%;height: auto;margin: -16px auto 50px;margin-top: 0px;" loop="" autoplay=""></lottie-player>
</div>
</div>
`
Tabs.parentElement.insertBefore(ValueSection, Tabs)
const ValueCard = document.getElementById('p+valuation_card').children[0]
const TagColors = {
"Projected": "warning",
"Hoarded": "success",
"Rare": "primary",
"Freaky": "danger"
}
const GetTagColor = function(label) {
if (TagColors[label] !== undefined) {
return TagColors[label]
} else if (TagColors[label.substring(1)] !== undefined) {
return TagColors[label.substring(1)]
} else {
return 'dark'
}
}
await chrome.runtime.sendMessage({
action: "item_valuation",
itemID: ItemID
});
}
async function ValueListData() {
let Tabs = document.getElementById('store-tabs');
const ValueSection = document.createElement('div')
ValueSection.classList = 'mb-3'
ValueSection.innerHTML = `
<h6 class="section-title mt-3 mt-lg-0 mb-3 px-2">
Valuation <a href="https://docs.google.com/document/d/1W7JN74MU-9Dbd-9xNnjxE18hQVBPXWuwjK5DGSnuQR4/" target="_blank">(based off LOVE)</a>
</h6>
<div class="card" id="p+valuation_card">
<div class="card-body">
<small class="d-block text-center text-muted" style="font-size: 0.8rem;">
Loading...
</small>
<lottie-player id="avatar-loading" src="https://c0.ptacdn.com/static/images/lottie/poly-brick-loading.2b51aa85.json" background="transparent" speed="1" style="width: 20%;height: auto;margin: -16px auto 50px;margin-top: 0px;" loop="" autoplay=""></lottie-player>
</div>
</div>
`
Tabs.parentElement.insertBefore(ValueSection, Tabs)
const ValueCard = document.getElementById('p+valuation_card').children[0]
const ColumnLabels = [
"name",
"short",
"value",
"type",
"trend",
"demand",
"tags"
]
const TagColors = {
"Projected": "warning",
"Hoarded": "success",
"Rare": "primary",
"Freaky": "danger"
}
const ExtractTableJSON = function(table) {
var data = [];
for (var i = 1; i < table.rows.length; i++) {
var tableRow = table.rows[i];
var rowData = {
tags: []
};
for (var j = 0; j < tableRow.cells.length; j++) {
let Value = tableRow.cells[j].children[0].children[0].innerText;
if (ColumnLabels[j] === "name") {
const LinkValue = tableRow.cells[j].getElementsByTagName('a')[0]
if (LinkValue) {
rowData.id = LinkValue.href.split('https://www.google.com/url?q=')[1].split('&')[0].split('/')[4]
}
}
if (ColumnLabels[j] === "tags") {
Array.from(tableRow.cells[j].children).forEach(tag => {
/*
The regex for the emoji character codes replacement was made by AI, such a time saver lol
*/
rowData.tags.push(tag.children[0].innerHTML.replace(/\s/g,'').replace(/([\u2700-\u27BF]|[\uE000-\uF8FF]|\uD83C[\uDC00-\uDFFF]|\uD83D[\uDC00-\uDFFF]|[\u2011-\u26FF]|\uD83E[\uDD10-\uDDFF])/g, ''))
})
} else {
rowData[ColumnLabels[j]] = Value
}
}
data.push(rowData);
}
return data;
}
const ValueListDocument = new DOMParser().parseFromString(await (await fetch('https://docs.google.com/feeds/download/documents/Export?exportFormat=html&format=html&id=1W7JN74MU-9Dbd-9xNnjxE18hQVBPXWuwjK5DGSnuQR4')).text(), 'text/html')
/*
Table to JSON function (slightly modified for my use-case)
https://stackoverflow.com/questions/9927126/how-to-convert-the-following-table-to-json-with-javascript#answer-60196347
*/
const GetTagColor = function(label) {
if (TagColors[label] !== undefined) {
return TagColors[label]
} else if (TagColors[label.substring(1)] !== undefined) {
return TagColors[label.substring(1)]
} else {
return 'dark'
}
}
const ValueDetails = ExtractTableJSON(ValueListDocument.getElementsByTagName('table')[0]).filter((x) => x.id === ItemID)[0]
if (ValueDetails !== undefined) {
ValueCard.innerHTML = `
<div class="mb-1">
<b class="text-success">
<i class="pi pi-brick" style="width:1.2em"></i>
Value
</b>
<span class="float-end">
${ValueDetails.value}
</span>
</div>
<div class="mb-1">
<b class="text-primary"">
<i class="pi" style="width:1.2em">%</i>
Trend
</b>
<span class="float-end">
${ValueDetails.trend}
</span>
</div>
<div class="mb-1">
<b>
<i class="fa-duotone fa-triangle" style="width:1.2em"></i>
Valuation Type
</b>
<span class="float-end">
${ValueDetails.type}
</span>
</div>
<div class="mb-1">
<b>
<i class="fa-duotone fa-hand-wave" style="width:1.2em"></i>
Shorthand
</b>
<span class="float-end">
${ValueDetails.short}
</span>
</div>
<div class="d-flex" style="gap: 5px;">
${ ValueDetails.tags.map((x) => `
<span class="badge bg-${ GetTagColor(x) }">${x}</span>
`).join('')}
</div>
`
} else {
ValueCard.innerHTML = `
There is no evaluation for this item at this time.
`
}
}
async function AssetDesignerInfo(data) {
let QuickStats = document.querySelectorAll('.row:has(h3, h6, [data-bs-tooltip])')[2];
const CreatorStat = document.createElement('div')
CreatorStat.classList = 'col text-center'
CreatorStat.innerHTML = `
<h6>Asset Designer</h6>
<h3 class="small">
<a href="/u/${data[ItemID]}" style="background-clip:text;-webkit-background-clip:text;color:transparent;background-image: linear-gradient(90deg, #1ad05b, #68f);-webkit-text-fill-color: transparent;">${data[ItemID]}</a>
</h3>
`
QuickStats.appendChild(CreatorStat)
}

View file

@ -21,24 +21,34 @@ chrome.storage.sync.get(['PolyPlus_Settings'], async function (result) {
}); });
} }
if (Settings.StoreOwnTagOn === true) {
Inventory = (await (await fetch('https://api.polytoria.com/v1/users/' + UserID + '/inventory?type=hat&limit=100')).json());
if (Inventory.errors === undefined) {
Inventory = Inventory.inventory
Inventory.concat(await (await fetch('https://api.polytoria.com/v1/users/' + UserID + '/inventory?type=face&limit=100')).json()).inventory;
Inventory.concat(await (await fetch('https://api.polytoria.com/v1/users/' + UserID + '/inventory?type=tool&limit=100')).json()).inventory;
console.log(Inventory);
Array.from(ItemGrid.children).forEach((element) => {
LoadOwnedTags(element);
});
} else {
console.log(Inventory)
}
}
if (Settings.EventItemsCatOn === true) { if (Settings.EventItemsCatOn === true) {
EventItems(); EventItems();
} }
if (Settings.StoreOwnTagOn === true) {
chrome.storage.local.get('PolyPlus_InventoryCache', async function(result){
if (result.PolyPlus_InventoryCache !== undefined && (new Date().getTime() - result.PolyPlus_InventoryCache.requested < 30000)) {
Inventory = result.PolyPlus_InventoryCache.data
Array.from(ItemGrid.children).forEach((element) => {
LoadOwnedTags(element);
});
} else {
Inventory = (await (await fetch('https://api.polytoria.com/v1/users/' + UserID + '/inventory?type=hat&limit=100')).json());
if (Inventory.errors === undefined) {
Inventory = Inventory.inventory
Inventory = [...Inventory, ...(await (await fetch('https://api.polytoria.com/v1/users/' + UserID + '/inventory?type=face&limit=100')).json()).inventory];
Inventory = [...Inventory, ...(await (await fetch('https://api.polytoria.com/v1/users/' + UserID + '/inventory?type=tool&limit=100')).json()).inventory];
Inventory = [...Inventory, ...(await (await fetch('https://api.polytoria.com/v1/users/' + UserID + '/inventory?type=profileTheme&limit=100')).json()).inventory];
chrome.storage.local.set({'PolyPlus_InventoryCache': {data: Inventory, requested: new Date().getTime()}}, function(){})
Array.from(ItemGrid.children).forEach((element) => {
LoadOwnedTags(element);
});
} else {
console.log(Inventory)
}
}
})
}
}); });
const observer = new MutationObserver(async function (list) { const observer = new MutationObserver(async function (list) {
@ -90,8 +100,13 @@ async function LoadIRLPrices(element) {
} }
} }
function LoadOwnedTags(element) { function LoadOwnedTags(element, id) {
let Item = CheckInventory(parseInt(element.querySelector('[href^="/store/"]').getAttribute('href').split('/')[2])); let Item;
if (id !== undefined) {
Item = CheckInventory(parseInt(id))
} else {
Item = CheckInventory(parseInt(element.querySelector('[href^="/store/"]').getAttribute('href').split('/')[2]));
}
if (Item !== null) { if (Item !== null) {
const Tag = document.createElement('span'); const Tag = document.createElement('span');
Tag.classList = `badge ${Item.asset.isLimited === false ? 'bg-primary' : 'bg-warning'} polyplus-own-tag`; Tag.classList = `badge ${Item.asset.isLimited === false ? 'bg-primary' : 'bg-warning'} polyplus-own-tag`;
@ -164,16 +179,21 @@ function EventItems() {
<h6 class="dash-ctitle2">${x.date}</h6> <h6 class="dash-ctitle2">${x.date}</h6>
<h5 class="dash-ctitle">${x.name}</h5> <h5 class="dash-ctitle">${x.name}</h5>
</div> </div>
${x.link !== undefined ? ` ${x.link !== undefined ? typeof(x.link) !== "object" ? `
<div class="col-auto d-flex align-items-center"> <div class="col-auto d-flex align-items-center">
<a class="text-muted" href="${x.link}"> <a class="text-muted" href="${x.link}">
<span class="d-none d-lg-inline">${x.link.startsWith('https://polytoria.com/places/') ? 'Event Place' : 'Blog Post'}</span> <span class="d-none d-lg-inline">${x.link.startsWith('https://polytoria.com/places/') ? 'Event Place' : 'Blog Post'}</span>
<i class="fas fa-angle-right ms-2"></i> <i class="fas fa-angle-right ms-2"></i>
</a> </a>
</div> </div>
` ` : x.link.map(l => `
: '' <div class="col-auto d-flex align-items-center">
} <a class="text-muted" href="${l.link}">
<span class="d-none d-lg-inline">${l.label}</span>
<i class="fas fa-angle-right ms-2"></i>
</a>
</div>
`).join('') : ''}
</div> </div>
<div class="card card-dash mcard mb-3" style="animation-delay: 0.27s;"> <div class="card card-dash mcard mb-3" style="animation-delay: 0.27s;">
<div class="card-body p-0 m-1 scrollFadeContainer"> <div class="card-body p-0 m-1 scrollFadeContainer">
@ -232,6 +252,12 @@ function EventItems() {
const Next = document.getElementById('p+ei-pagination-next'); const Next = document.getElementById('p+ei-pagination-next');
const Last = document.getElementById('p+ei-pagination-last'); const Last = document.getElementById('p+ei-pagination-last');
if (Settings.StoreOwnTagOn === true) {
Array.from(Container.querySelectorAll('a[href^="/store"]')).forEach((element) => {
LoadOwnedTags(element, element.getAttribute('href').split('/')[2]);
});
}
if (Page > 0) { if (Page > 0) {
Prev.parentElement.classList.remove('disabled'); Prev.parentElement.classList.remove('disabled');
First.parentElement.classList.remove('disabled'); First.parentElement.classList.remove('disabled');
@ -332,6 +358,12 @@ function EventItems() {
Next.parentElement.classList.add('disabled'); Next.parentElement.classList.add('disabled');
Last.parentElement.classList.add('disabled'); Last.parentElement.classList.add('disabled');
} }
if (Settings.StoreOwnTagOn === true) {
Array.from(Container.querySelectorAll('a[href^="/store"]')).forEach((element) => {
LoadOwnedTags(element, element.getAttribute('href').split('/')[2]);
});
}
}; };
}); });
} }

96
js/the-great-divide.js Normal file
View file

@ -0,0 +1,96 @@
let EventOngoing = true;
let Team;
let HasTeam = true;
const PlaceAllowlist = [
'9656',
'9757'
];
(async () => {
Utilities = await import(chrome.runtime.getURL('resources/utils.js'))
.default
chrome.storage.sync.get(['PolyPlus_Settings'], function(result) {
Settings = result.PolyPlus_Settings || {};
if (Settings.TheGreatDivide.Enabled !== true) {
return
}
if (Settings.TheGreatDivide.UserStatsOn === true && window.location.pathname.split('/')[1] === 'u') {
if (HasTeam === true) {
UserStatsTab()
} else {
if (EventOngoing === true) {
UserStatsTab()
}
}
}
})
async function UnbalancedServerMarkers() {
const Team = (await (await fetch('https://api.polytoria.com/v1/users/' + JSON.parse(window.localStorage.getItem('p+account_info')).ID + '/greatdivide')).json()).team
if (Team !== undefined) {
const Servers = Array.from(document.getElementById('servers-tabpane').children)
Servers.forEach(server => {
const TeamCounts = {
phantoms: server.getElementsByClassName('border-phantoms').length,
cobras: server.getElementsByClassName('border-cobras').length
}
let Enemy = "cobras"
if (Team === "cobras") { Enemy = "phantoms" }
if (new URLSearchParams(window.location.search).has('forceServerUnbalance')) {
TeamCounts[Enemy] = 1000
}
if (TeamCounts[Team] < TeamCounts[Enemy]) {
const UnbalancedText = document.createElement('p')
UnbalancedText.classList = 'mb-2'
UnbalancedText.style.fontSize = '0.7rem'
UnbalancedText.style.color = 'orange'
UnbalancedText.innerHTML = `*Potentially Unbalanced <i class="fa-solid fa-circle-info" data-bs-toggle="tooltip" data-bs-title="${TeamCounts.cobras} Cobras and ${TeamCounts.phantoms} Phantoms" data-bs-placement="right"></i>`
const ServerInfoColumn = server.getElementsByClassName('col-3')[0]
ServerInfoColumn.children[0].style.marginBottom = '0px'
ServerInfoColumn.insertBefore(UnbalancedText, ServerInfoColumn.children[1])
Utilities.InjectResource("registerTooltips")
}
})
}
}
async function UserStatsTab() {
const EventSection = document.createElement('div')
EventSection.innerHTML = `
<div class="d-grid mt-2 mb-4"></div>
<h6 class="text-center section-title px-3 px-lg-0 fw-bold" style="background-clip:text;-webkit-background-clip:text;color:transparent;background-image: linear-gradient(90deg, #1ad05b, #68f);-webkit-text-fill-color: transparent;">
<i class="fas fa-swords me-1 float-start"></i>
GREATEST DIVISION
<i class="fas fa-swords me-1 float-end"></i>
</h6>
<div class="card mcard mb-4" style="min-height: 226px; background-image: linear-gradient(rgba(0.7, 0.7, 0.7, 0.7), rgba(0.7, 0.7, 0.7, 0.7)), url('https://blog.polytoria.com/content/images/2024/06/TheGreatDivide.png'); background-size: cover; background-position: center; border-color: #000 !important;">
<div class="card-body" id="p+greatdivide_card">
<button class="btn btn-primary btn-sm w-100">Load Statistics</button>
</div>
</div>
`
document.getElementsByClassName('user-right')[0].appendChild(EventSection)
const EventCard = document.getElementById('p+greatdivide_card')
EventCard.innerHTML = `
<small class="d-block text-center text-muted" style="font-size: 0.8rem;">
Loading...
</small>
<lottie-player id="avatar-loading" src="https://c0.ptacdn.com/static/images/lottie/poly-brick-loading.2b51aa85.json" background="transparent" speed="1" style="width: 20%;height: auto;margin: -16px auto 50px;margin-top: 0px;" loop="" autoplay=""></lottie-player>
`
await chrome.runtime.sendMessage({
action: "greatdivide_stats",
userID: document.querySelector('.dropdown-item.text-danger[href^="/report"]').getAttribute('href').split('?')[0].split('/')[3]
});
}
})();

56
manifest.json Executable file → Normal file
View file

@ -2,8 +2,8 @@
"manifest_version": 3, "manifest_version": 3,
"author": "Index", "author": "Index",
"name": "Poly+", "name": "Poly+",
"version": "1.11", "version": "1.3",
"version_name": "Pre-Release Build (v1.1.1)", "version_name": "Release Build (v1.3)",
"description": "Power-up your Polytoria experience with Poly+! Created by Index.", "description": "Power-up your Polytoria experience with Poly+! Created by Index.",
"homepage_url": "https://polyplus.vercel.app/", "homepage_url": "https://polyplus.vercel.app/",
"permissions": ["storage", "contextMenus", "scripting", "alarms", "notifications"], "permissions": ["storage", "contextMenus", "scripting", "alarms", "notifications"],
@ -33,8 +33,13 @@
}, },
{ {
"matches": ["https://polytoria.com/create/place/**"], "matches": ["https://polytoria.com/create/place/**/access"],
"js": ["/js/places/place-edit.js"] "js": ["/js/create/place-access.js"]
},
{
"matches": ["https://polytoria.com/create/image", "https://polytoria.com/create/image/"],
"js": ["/js/create/image.js"]
}, },
{ {
@ -58,7 +63,7 @@
}, },
{ {
"matches": ["https://polytoria.com/store/**"], "matches": ["https://polytoria.com/store/**", "https://polytoria.com/store/*/asset"],
"js": ["/js/store/item-view.js"] "js": ["/js/store/item-view.js"]
}, },
@ -95,7 +100,7 @@
{ {
"matches": ["https://polytoria.com/my/avatar*"], "matches": ["https://polytoria.com/my/avatar*"],
"js": ["/js/account/avatar-sandbox2.js"] "js": ["/js/account/avatar-sandbox.js"]
}, },
{ {
@ -111,6 +116,26 @@
{ {
"matches": ["https://polytoria.com/store/**", "https://polytoria.com/models/**"], "matches": ["https://polytoria.com/store/**", "https://polytoria.com/models/**"],
"js": ["/js/library-download.js"] "js": ["/js/library-download.js"]
},
{
"matches": ["https://polytoria.com/games/*", "https://polytoria.com/shop/*", "https://polytoria.com/my/referrals", "https://polytoria.com/create/?t=*", "https://polytoria.com/user/*", "https://polytoria.com/library/*"],
"js": ["/js/site-redirects.js"]
},
{
"matches": ["https://polytoria.com/library"],
"js": ["/js/create/audio-library.js"]
},
{
"matches": ["https://polytoria.com/u/**"],
"js": ["/js/the-great-divide.js"]
},
{
"matches": ["https://polytoria.com/trade/view/**"],
"js": ["/js/account/trade-valuation.js"]
} }
], ],
"background": { "background": {
@ -133,16 +158,17 @@
"action": { "action": {
"default_title": "Poly+", "default_title": "Poly+",
"default_icon": { "default_icon": {
"16": "/icons/icon.png", "16": "/images/icon.png",
"32": "/icons/icon.png", "32": "/images/icon.png",
"48": "/icons/icon.png", "48": "/images/icon.png",
"128": "/icons/icon.png" "128": "/images/icon.png"
} },
"default_popup": "/popup.html"
}, },
"icons": { "icons": {
"16": "/icons/icon.png", "16": "/images/icon.png",
"32": "/icons/icon.png", "32": "/images/icon.png",
"48": "/icons/icon.png", "48": "/images/icon.png",
"128": "/icons/icon.png" "128": "/images/icon.png"
} }
} }

36
popup.html Normal file
View file

@ -0,0 +1,36 @@
<!doctype html>
<html>
<head>
<!-- META TAGS -->
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- PUBLIC -->
<title>Poly+ Settings</title>
<!-- RESOURCES -->
<link rel="stylesheet" href="css/polytoria.css" />
</head>
<body style="width: 245px;">
<div class="p-2 pt-0">
<small class="text-muted" style="font-size: 0.7rem;">quick links</small>
<br>
<a href="#" class="btn btn-primary btn-sm w-100 mb-1" id="settings-btn" target="_window">Settings</a>
<a href="https://polytoria.com/my/settings/polyplus#debug" class="btn btn-warning btn-sm w-100 mb-1" target="_window">Debug</a>
<div class="d-flex" style="gap: 5px;">
<a href="https://chromewebstore.google.com/detail/poly+/feafepokhecfmimpepbpccmcnjbcbklg" class="btn btn-dark btn-sm w-100" target="_window"><img src="/images/chrome-icon.svg" alt="Chrome Webstore" width="25" height="25"></a>
<a href="https://addons.mozilla.org/en-US/firefox/addon/polytoriaplus/" class="btn btn-dark btn-sm w-100" target="_window"><img src="/images/firefox-icon.svg" alt="Chrome Webstore" width="25" height="25"></a>
</div>
<hr class="mt-2 mb-3" style="color: #2b2b2b;">
<small class="text-muted" style="font-size: 0.7rem;">extract texture from item</small>
<br>
<div class="input-group">
<input type="text" class="form-control form-control-sm" placeholder="Item ID..">
<button class="btn btn-success btn-sm" id="extract-texture">Extract</button>
</div>
</div>
<script src="popup.js"></script>
</body>
</html>

89
popup.js Normal file
View file

@ -0,0 +1,89 @@
/*
ParseGLB & GetTexture function code made by ChatGPT (I didn't want to use external libraries because of storage space)
*/
document.getElementById('settings-btn').href = chrome.runtime.getURL('settings.html')
const ExtractButton = document.getElementById('extract-texture')
ExtractButton.addEventListener('click', async function(){
const MeshURL = (await (await fetch('https://api.polytoria.com/v1/assets/serve-mesh/' + ExtractButton.previousElementSibling.value)).json()).url
if (MeshURL !== undefined) {
const Mesh = (await (await fetch(MeshURL)).arrayBuffer())
const ParsedGTLF = ParseGLB(Mesh)
const Texture = GetTexture(ParsedGTLF);
if (Texture) {
const DownloadLink = document.createElement('a');
DownloadLink.href = Texture;
DownloadLink.download = ExtractButton.previousElementSibling.value + '.png';
document.body.appendChild(DownloadLink)
DownloadLink.click()
DownloadLink.remove()
}
}
})
function ParseGLB(arrayBuffer) {
const MAGIC_glTF = 0x46546C67;
const dataView = new DataView(arrayBuffer);
const magic = dataView.getUint32(0, true);
if (magic !== MAGIC_glTF) {
throw new Error('Invalid GLB file.');
}
const version = dataView.getUint32(4, true);
if (version !== 2) {
throw new Error('Unsupported GLB version.');
}
const length = dataView.getUint32(8, true);
const chunkLength = dataView.getUint32(12, true);
const chunkType = dataView.getUint32(16, true);
if (chunkType !== 0x4E4F534A) {
throw new Error('Invalid GLB JSON chunk.');
}
const jsonChunk = new TextDecoder().decode(
new Uint8Array(arrayBuffer, 20, chunkLength)
);
const json = JSON.parse(jsonChunk);
const binChunkHeader = 20 + chunkLength;
const binChunkLength = dataView.getUint32(binChunkHeader, true);
const binChunkType = dataView.getUint32(binChunkHeader + 4, true);
if (binChunkType !== 0x004E4942) {
throw new Error('Invalid GLB BIN chunk.');
}
const binChunk = arrayBuffer.slice(binChunkHeader + 8, binChunkHeader + 8 + binChunkLength);
return {
json: json,
bin: binChunk
};
}
function GetTexture(gltf) {
let Texture = null
const images = gltf.json.images;
if (images.length === 0) {
return null;
}
const image = images[0];
const bufferView = gltf.json.bufferViews[image.bufferView];
const byteOffset = bufferView.byteOffset || 0;
const byteLength = bufferView.byteLength;
const buffer = new Uint8Array(gltf.bin, byteOffset, byteLength);
const blob = new Blob([buffer], { type: image.mimeType });
return URL.createObjectURL(blob);
}

View file

@ -2,9 +2,26 @@
#options { #options {
position: absolute; position: absolute;
bottom: 0; bottom: 0;
margin: 20px; margin-bottom: 15px;
margin-bottom: 40px;
font-size: 1.25rem; font-size: 1.25rem;
background: #1a1a1a;
border-radius: 10px;
padding: 2.5px;
font-size: 1.25rem;
/*
button iteration #2
font-size: 1.25rem;
background: #333333;
border-radius: var(--bs-card-border-radius);
padding: 2.5px;
font-size: 1.25rem;
left: -1px;
bottom: -1px;
border-top-left-radius: 0px;
border-bottom-right-radius: 0px;
*/
} }
#options *:not(input) { #options *:not(input) {
@ -17,59 +34,214 @@
#options *:not(input):not(:nth-child(2)) { #options *:not(input):not(:nth-child(2)) {
margin-bottom: 3.5px; margin-bottom: 3.5px;
} }
[class^="p+outfit_overwrite_button"]:hover, [class^="p+outfit_delete_button"]:hover {
cursor: pointer;
}
[id="p+retro_items_warning"] {
display: none;
color: #000;
font-weight: bold;
}
body:has([data-tab="retro"].active) [id="p+retro_items_warning"] {
display: block;
}
</style> </style>
<dialog id="p+body_colors" class="polyplus-modal" style="width: 375px; border: 1px solid #484848; background-color: #181818; border-radius: 20px; overflow: hidden;">
<div class="row text-muted mb-4" style="font-size: 0.8rem;">
<div class="col">
<h5 class="mb-0" style="color: #fff;">Modify Body Colors</h5>
Selected Body Part: <i id="p+selected_bodypart">none</i>
</div>
<div class="col-md-3">
<button class="btn btn-info w-100 mx-auto" onclick="this.parentElement.parentElement.parentElement.close();">X</button>
</div>
</div>
<div class="modal-body">
<div class="wrapper">
<div class="colorpicker-color" style="background-color: #f8f8f8"></div>
<div class="colorpicker-color" style="background-color: #cdcdcd"></div>
<div class="colorpicker-color" style="background-color: #111111"></div>
<div class="colorpicker-color" style="background-color: #ff0000"></div>
<div class="colorpicker-color" style="background-color: #a34b4b"></div>
<div class="colorpicker-color" style="background-color: #ffc9c9"></div>
<div class="colorpicker-color" style="background-color: #957977"></div>
<div class="colorpicker-color" style="background-color: #c4281c"></div>
<div class="colorpicker-color" style="background-color: #da867a"></div>
<div class="colorpicker-color" style="background-color: #694028"></div>
<div class="colorpicker-color" style="background-color: #cc8e69"></div>
<div class="colorpicker-color" style="background-color: #a05f35"></div>
<div class="colorpicker-color" style="background-color: #7c5c46"></div>
<div class="colorpicker-color" style="background-color: #eab892"></div>
<div class="colorpicker-color" style="background-color: #da8541"></div>
<div class="colorpicker-color" style="background-color: #aa5500"></div>
<div class="colorpicker-color" style="background-color: #ffcc99"></div>
<div class="colorpicker-color" style="background-color: #e29b40"></div>
<div class="colorpicker-color" style="background-color: #ffaf00"></div>
<div class="colorpicker-color" style="background-color: #ffb000"></div>
<div class="colorpicker-color" style="background-color: #d7c59a"></div>
<div class="colorpicker-color" style="background-color: #f5cd30"></div>
<div class="colorpicker-color" style="background-color: #fdea8d"></div>
<div class="colorpicker-color" style="background-color: #e5e4df"></div>
<div class="colorpicker-color" style="background-color: #c1be42"></div>
<div class="colorpicker-color" style="background-color: #ffff00"></div>
<div class="colorpicker-color" style="background-color: #ffffcc"></div>
<div class="colorpicker-color" style="background-color: #a4bd47"></div>
<div class="colorpicker-color" style="background-color: #7f8e64"></div>
<div class="colorpicker-color" style="background-color: #a1c48c"></div>
<div class="colorpicker-color" style="background-color: #3a7d15"></div>
<div class="colorpicker-color" style="background-color: #4b974b"></div>
<div class="colorpicker-color" style="background-color: #00ff00"></div>
<div class="colorpicker-color" style="background-color: #ccffcc"></div>
<div class="colorpicker-color" style="background-color: #27462d"></div>
<div class="colorpicker-color" style="background-color: #287f47"></div>
<div class="colorpicker-color" style="background-color: #789082"></div>
<div class="colorpicker-color" style="background-color: #9ff3e9"></div>
<div class="colorpicker-color" style="background-color: #12eed4"></div>
<div class="colorpicker-color" style="background-color: #f2f3f3"></div>
<div class="colorpicker-color" style="background-color: #00ffff"></div>
<div class="colorpicker-color" style="background-color: #008f9c"></div>
<div class="colorpicker-color" style="background-color: #04afec"></div>
<div class="colorpicker-color" style="background-color: #80bbdb"></div>
<div class="colorpicker-color" style="background-color: #b4d2e4"></div>
<div class="colorpicker-color" style="background-color: #0d69ac"></div>
<div class="colorpicker-color" style="background-color: #1b2a35"></div>
<div class="colorpicker-color" style="background-color: #afddff"></div>
<div class="colorpicker-color" style="background-color: #6e99ca"></div>
<div class="colorpicker-color" style="background-color: #74869d"></div>
<div class="colorpicker-color" style="background-color: #2154b9"></div>
<div class="colorpicker-color" style="background-color: #002060"></div>
<div class="colorpicker-color" style="background-color: #0000ff"></div>
<div class="colorpicker-color" style="background-color: #b1a7ff"></div>
<div class="colorpicker-color" style="background-color: #a3a2a5"></div>
<div class="colorpicker-color" style="background-color: #6225d1"></div>
<div class="colorpicker-color" style="background-color: #b480ff"></div>
<div class="colorpicker-color" style="background-color: #8c5b9f"></div>
<div class="colorpicker-color" style="background-color: #6b327c"></div>
<div class="colorpicker-color" style="background-color: #aa00aa"></div>
<div class="colorpicker-color" style="background-color: #635f62"></div>
<div class="colorpicker-color" style="background-color: #ff00bf"></div>
<div class="colorpicker-color" style="background-color: #ff66cc"></div>
<div class="colorpicker-color" style="background-color: #e8bac8"></div>
</div>
<div class="input-group mt-2">
<input type="text" class="form-control bg-dark" placeholder="HEX Code..">
<button id="p+bodypart_customhex" class="btn btn-primary">Set</button>
</div>
</div>
</dialog>
<dialog id="p+outfit_create" class="polyplus-modal" style="width: 375px; border: 1px solid #484848; background-color: #181818; border-radius: 20px; overflow: hidden;">
<div class="row text-muted mb-4" style="font-size: 0.8rem;">
<div class="col">
<h5 class="mb-0" style="color: #fff;">Create Outfit</h5>
Save this avatar for later!
</div>
<div class="col-md-2">
<button class="btn btn-info w-100 mx-auto" onclick="this.parentElement.parentElement.parentElement.close();">X</button>
</div>
</div>
<div class="modal-body">
<div class="input-group mb-2">
<input type="text" class="form-control" placeholder="Outfit Name...">
<button id="p+save_outfit_confirm" class="btn btn-success">Save</button>
</div>
<b id="p+outfit_create_error" class="text-muted" style="font-size: 0.85rem;"><i class="fa-duotone fa-square-question mr-1"></i> ...</b>
</div>
</dialog>
<dialog id="p+outfit_rename" class="polyplus-modal" style="width: 375px; border: 1px solid #484848; background-color: #181818; border-radius: 20px; overflow: hidden;">
<div class="row text-muted mb-4" style="font-size: 0.8rem;">
<div class="col">
<h5 class="mb-0" style="color: #fff;">Rename Outfit</h5>
Renaming Outfit "<i id="p+outfit_rename_name">none</i>"
</div>
<div class="col-md-2">
<button class="btn btn-info w-100 mx-auto" onclick="this.parentElement.parentElement.parentElement.close();">X</button>
</div>
</div>
<div class="modal-body">
<div class="input-group mb-2">
<input type="text" class="form-control" placeholder="New Outfit Name...">
<button id="p+rename_outfit_confirm" class="btn btn-success">Save</button>
</div>
<b id="p+outfit_rename_error" class="text-muted" style="font-size: 0.85rem;"><i class="fa-duotone fa-square-question mr-1"></i> ...</b>
</div>
</dialog>
<span class="badge bg-warning mb-2" id="view-cache">Poly+</span>
<h4 class="d-none d-lg-block">
Avatar Sandbox
</h4>
<div class="row"> <div class="row">
<div class="col-12 col-lg-3"> <div class="col-12 col-lg-3">
<div class="card mcard mb-3"> <div class="card mcard mb-3">
<h6 class="card-header">
<i class="fad fa-user-crown"></i>
Avatar Sandbox
</h6>
<div class="card-body"> <div class="card-body">
<iframe id="viewFrame" style="width: 100%; height: 314px; border-radius: 0.65rem;"></iframe> <iframe id="viewFrame" style="width: 100%; height: 314px; border-radius: 0.65rem;"></iframe>
<div id="options"> <div id="options">
<input name="JSONUpload" type="file" accept="application/json" multiple="false" id="jsonUpload" aria-label="Upload JSON!" style="display: none;" /> <input name="JSONUpload" type="file" accept="application/json" multiple="false" id="jsonUpload" aria-label="Upload JSON!" style="display: none;" />
<label for="JSONUpload" style="display: block;"> <label for="JSONUpload" style="display: block;">
<button aria-label="Upload JSON!" onclick="this.parentElement.previousElementSibling.click()"> <button aria-label="Upload JSON!" onclick="this.parentElement.previousElementSibling.click()" data-bs-toggle="tooltip" data-bs-title="Upload" data-bs-placement="right">
<i class="fa-duotone fa-download"></i> <i class="fa-duotone fa-upload"></i>
</button> </button>
</label> </label>
<button id="jsonSave" aria-label="Save as JSON!" style="display: block;"> <button id="jsonSave" aria-label="Save as JSON!" style="display: block;" data-bs-toggle="tooltip" data-bs-title="Download" data-bs-placement="right">
<i class="fa-duotone fa-upload"></i> <i class="fa-duotone fa-download"></i>
</button> </button>
<button id="openNewTab" aria-label="View in a new tab!" style="display: block;"> <button aria-label="View in full screen!" style="display: block;" onclick="document.getElementById('viewFrame').requestFullscreen()" data-bs-toggle="tooltip" data-bs-title="Full Screen" data-bs-placement="right">
<i class="fa-duotone fa-up-right-from-square"></i>
</button>
<button aria-label="View in full screen!" style="display: block;" onclick="document.getElementById('viewFrame').requestFullscreen()">
<i class="fa-duotone fa-minimize"></i> <i class="fa-duotone fa-minimize"></i>
</button> </button>
</div> </div>
</div> </div>
</div> </div>
<button id="myself" class="btn btn-outline-primary w-100 mb-2">
<i class="fa-duotone fa-shirt"></i>
Load Myself
</button>
<div class="row mb-3"> <div class="row mb-3">
<div class="col">
<button id="myself" class="btn btn-outline-primary w-100">
<i class="fa-duotone fa-shirt"></i>
Myself
</button>
</div>
<div class="col"> <div class="col">
<button id="clear" class="btn btn-outline-warning w-100"> <button id="clear" class="btn btn-outline-warning w-100">
<i class="fa-duotone fa-trash"></i> <i class="fa-duotone fa-trash"></i>
Clear Clear
</button> </button>
</div> </div>
<div class="col">
<button id="saveOutfit" class="btn btn-outline-success w-100">
<i class="fa-duotone fa-save"></i>
Save
</button>
</div>
</div> </div>
<div class="input-group mb-3"> <hr class="mt-2 mb-3">
<input type="text" class="form-control bg-dark" placeholder="Asset ID.." />
<button class="btn btn-primary" id="load-asset">Load Asset</button>
</div>
<div class="card mcard mb-3"> <div class="card mcard mb-3">
<h6 class="card-header"> <h6 class="card-header text-center">
<i class="fad fa-wrench"></i>
Advanced
</h6>
<div class="card-body">
<small class="text-muted mb-1" style="font-size: 0.7rem;">Wear asset by ID</small>
<br>
<select class="form-select bg-dark mb-1" id="load-asset-type">
<option value="hat" selected>Hat</option>
<option value="tool">Tool</option>
<option value="face">Face</option>
<option value="torso">Body Part</option>
<hr>
<option value="shirt">Shirt</option>
<option value="pants">Pants</option>
<hr>
<option value="user">User Avatar</option>
</select>
<div class="input-group">
<input type="text" class="form-control bg-dark" placeholder="Asset ID.." />
<button class="btn btn-primary" id="load-asset">Wear</button>
</div>
</div>
</div>
<div class="card mcard mb-1">
<h6 class="card-header text-center">
<i class="fad fa-palette"></i> <i class="fad fa-palette"></i>
Body Colors Body Colors
</h6> </h6>
@ -79,58 +251,132 @@
<button id="head" class="avatarAction bodypart bp1x1" style="background-color: #e0e0e0;"></button> <button id="head" class="avatarAction bodypart bp1x1" style="background-color: #e0e0e0;"></button>
</div> </div>
<div style="margin-bottom: 5px"> <div style="margin-bottom: 5px">
<button id="rightArm" class="avatarAction bodypart bp1x2" style="background-color: #e0e0e0; margin-right: 5px;"></button <button id="rightArm" class="avatarAction bodypart bp1x2" style="background-color: #e0e0e0; margin-right: 5px;"></button>
><button id="torso" class="avatarAction bodypart bp2x2" style="background-color: #e0e0e0;"></button <button id="torso" class="avatarAction bodypart bp2x2" style="background-color: #e0e0e0;"></button>
><button class="avatarAction bodypart bp1x2" id="leftArm" style="background-color: #e0e0e0; margin-left: 5px;"></button> <button class="avatarAction bodypart bp1x2" id="leftArm" style="background-color: #e0e0e0; margin-left: 5px;"></button>
</div> </div>
<div> <div>
<button class="avatarAction bodypart bp1x2" id="rightLeg" style="background-color: #e0e0e0; margin-right: 5px; padding-right: 18px;"></button <button class="avatarAction bodypart bp1x2" id="rightLeg" style="background-color: #e0e0e0; margin-right: 5px; padding-right: 18px;"></button>
><button id="leftLeg" class="avatarAction bodypart bp1x2" style="background-color: #e0e0e0; padding-right: 18px;"></button> <button id="leftLeg" class="avatarAction bodypart bp1x2" style="background-color: #e0e0e0; padding-right: 18px;"></button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<small class="text-muted text-center" style="font-size: 0.7rem;">thanks to <a href="https://polytoria.com/u/Hawli">@Hawli</a> and <a href="https://polytoria.com/u/Emir">@Emir</a> for letting me use their<br><a href="https://poly-archive.vercel.app/" target="_blank">Poly-Archive</a> website's assets!</small>
<hr class="mt-2 mb-1">
<small class="text-muted text-center" style="font-size: 0.7rem;">feature of Poly+</small> <small class="text-muted text-center" style="font-size: 0.7rem;">feature of Poly+</small>
<!--
Public API does not have an offsale parameter
<span class="form-check form-switch" style="font-size: 0.7rem;">
<input class="form-check-input" type="checkbox" role="switch" id="show-offsale" checked />
<label class="form-check-label" for="hide-user-ads-banner">Show Offsale Items</label>
</span>
-->
</div> </div>
<div class="col-12 col-lg-9"> <div class="col-12 col-lg-9">
<ul class="nav nav-pills nav-justified mb-3" id="tabs"> <ul class="nav nav-pills no-shrink avatar-nav-pills nav-justified row" id="tabs">
<li class="nav-item"> <li class="nav-item col-6 col-md-4 col-xl-3" style="flex: unset">
<a class="nav-link active" data-tab="hat"> <a class="nav-link active" data-tab="hat">
<i class="fas fa-hat-cowboy me-1"></i> <i class="fas fa-hat-wizard me-1"></i>
Hats <span class="pilltitle">Accessories</span>
</a> </a>
</li> </li>
<li class="nav-item"> <li class="nav-item col-6 col-md-4 col-xl-3" style="flex: unset">
<a class="nav-link" data-tab="tool">
<i class="fas fa-hammer me-1"></i>
Tools
</a>
</li>
<li class="nav-item">
<a class="nav-link" data-tab="face">
<i class="fas fa-face-smile me-1"></i>
Faces
</a>
</li>
<li class="nav-item">
<a class="nav-link" data-tab="shirt"> <a class="nav-link" data-tab="shirt">
<i class="fas fa-tshirt me-1"></i> <i class="fas fa-tshirt me-1"></i>
Shirts <span class="pilltitle">Shirts</span>
</a> </a>
</li> </li>
<li class="nav-item"> <li class="nav-item col-6 col-md-4 col-xl-3" style="flex: unset">
<a class="nav-link" data-tab="pants"> <a class="nav-link" data-tab="pants">
<i class="fas fa-socks me-1"></i> <i class="fas fa-socks me-1"></i>
Pants <span class="pilltitle">Pants</span>
</a>
</li>
<li class="nav-item col-6 col-md-4 col-xl-3" style="flex: unset">
<a class="nav-link" data-tab="tool">
<i class="fas fa-hammer me-1"></i>
<span class="pilltitle">Tools</span>
</a>
</li>
<li class="nav-item col-6 col-md-4 col-xl-3" style="flex: unset">
<a class="nav-link" data-tab="face">
<i class="fas fa-face-smile me-1"></i>
<span class="pilltitle">Faces</span>
</a>
</li>
<li class="nav-item col-6 col-md-4 col-xl-3" style="flex: unset">
<a class="nav-link" data-tab="torso">
<i class="fas fa-cube me-1"></i>
<span class="pilltitle">Body Parts</span>
</a>
</li>
<li class="nav-item col-6 col-md-4 col-xl-3" style="flex: unset">
<a class="nav-link" data-tab="retro" data-bs-toggle="tooltip" data-bs-title="Items from Bloxtopia, Superium, and Polytoria v1!">
<i class="fa-solid fa-tv-retro me-1"></i>
<span class="pilltitle">Retro</span>
</a>
</li>
<li class="nav-item col-6 col-md-4 col-xl-3" style="flex: unset">
<a class="nav-link" data-tab="outfit">
<i class="fas fa-person me-1"></i>
<span class="pilltitle">Outfits</span>
</a> </a>
</li> </li>
</ul> </ul>
<div class="card px-2 pt-2 pb-2 mb-4" style="background: rgba(0, 0, 0, 0.23); border-color: rgba(0, 0, 0, 0.23); border-top-left-radius: 20px; border-top-right-radius: 20px;"> <div role="alert" class="alert bg-warning text-center mb-1 mt-2" id="p+retro_items_warning">
<input id="item-search" type="text" class="form-control mb-2" placeholder="Search for an item..." /> Items in this category are from past iterations of Polytoria. They can only be worn in the Avatar Sandbox.
<div class="row alignleft itemgrid" id="inventory"></div> </div>
<div class="card px-2 pt-2 pb-2 mb-2" style="background: transparent; border-color: transparent; border-top-left-radius: 20px; border-top-right-radius: 20px;">
<div class="row mb-1">
<div class="col-md-4">
<div class="input-group">
<select id="item-sort" class="form-select retro-items-disable">
<option value="name">Name</option>
<option value="price">Price</option>
<option value="createdAt" selected>Creation Date</option>
<option value="updatedAt">Updated Date</option>
</select>
<select id="item-order" class="form-select retro-items-disable">
<option value="asc">Ascending</option>
<option value="desc">Descending</option>
</select>
</div>
</div>
<div class="col">
<div class="input-group">
<input id="item-search" type="text" class="form-control mb-2 retro-items-disable" placeholder="Search for an item..." />
<button id="search-btn" class="btn btn-primary h-100 retro-items-disable">Search</button>
</div>
</div>
</div>
<div class="row alignleft itemgrid mb-4" id="inventory"></div>
<nav id="pagination" style="margin: auto;">
<ul class="pagination">
<li class="page-item disabled" id="pagination-first">
<a class="page-link" href="#!">«</a>
</li>
<li class="page-item disabled" id="pagination-prev">
<a class="page-link" href="#!"></a>
</li>
<li class="page-item active">
<a class="page-link">
<span class="visually-hidden">Page</span>
<span id="pagination-current">1</span>
</a>
</li>
<li class="page-item" id="pagination-next">
<a class="page-link" href="#!"></a>
</li>
<li class="page-item" id="pagination-last">
<a class="page-link" href="#!">»</a>
</li>
</ul>
</nav>
</div> </div>
<h6 class="card-header mb-2"><i class="fad fa-hat-wizard me-1"></i> Wearing</h6> <h6 class="card-header mb-2"><i class="fad fa-hat-wizard me-1"></i> Wearing</h6>
<div class="card px-2 pt-2 pb-2 mb-4" style="background: rgba(0, 0, 0, 0.23); border-color: rgba(0, 0, 0, 0.23); border-top-left-radius: 20px; border-top-right-radius: 20px;"> <div class="card px-2 pt-2 pb-2 mb-4" style="background: transparent; border-color: transparent; border-top-left-radius: 20px; border-top-right-radius: 20px;">
<div class="row alignleft itemgrid" id="wearing"></div> <div class="row alignleft itemgrid" id="wearing"></div>
</div> </div>
</div> </div>

View file

@ -1,2 +1,5 @@
var tooltips = document.querySelectorAll('[data-bs-toggle="tooltip"]'); var tooltips = document.querySelectorAll('[data-bs-toggle="tooltip"]');
var list = [...tooltips].map((tool) => new bootstrap.Tooltip(tool)); var tooltip_list = [...tooltips].map((tool) => new bootstrap.Tooltip(tool));
var dropdowns = document.querySelectorAll('[data-bs-toggle="dropdown"]');
var dropdown_list = [...dropdowns].map((dropdown) => new bootstrap.Dropdown(dropdown));

View file

@ -97,11 +97,45 @@ export default {
Enabled: false, Enabled: false,
Banners: true, Banners: true,
Rectangles: true Rectangles: true
} },
UploadMultipleDecals: true,
GD_ServerBalanceOn: true,
AvatarDimensionToggleOn: true,
TheGreatDivide: {
Enabled: true,
UnbalancedIndicatorOn: true,
MVPUserIndicatorOn: true,
UserStatsOn: true,
LeaderboardsOn: true
},
CollectibleInventoryCatOn: true,
ValueListInfo: {
Enabled: true,
ItemValuation: true,
TradeValuation: true
},
ImprovedAchievements: {
Enabled: true,
ProgressBarOn: true,
PercentageOn: true,
OpacityOn: true
},
TimePlayedOn: true,
HomeJoinFriendsButtonOn: true,
ImprovedPlaceManagement: {
Enabled: true,
QuickActivityToggleOn: true,
PlaceFileDownloadOn: true,
MultiWhitelistOn: true,
ClearWhitelistOn: true
},
MoreBlockedDetailsOn: true,
AssetDesignerCreditOn: true
}, },
Limits: { Limits: {
PinnedGames: 10, PinnedGames: 10,
BestFriends: 15, BestFriends: 15,
// Item Wishlist and ImprovedFrLists limit here is not implemented in the code
ImprovedFrLists: 20, ImprovedFrLists: 20,
ItemWishlist: 20, ItemWishlist: 20,
HoardersListPages: 4 HoardersListPages: 4
@ -198,6 +232,16 @@ export default {
Result = (bricks * 0.49).toFixed(2); Result = (bricks * 0.49).toFixed(2);
Display = 'BRL'; Display = 'BRL';
break;
// Zimbabwean Dollar
case 8:
Icon = 'Z$'
IsIconAbbr = true
Result = (bricks * 0.13739106).toFixed(2)
Display = 'ZWL'
break; break;
} }
@ -241,7 +285,7 @@ export default {
}); });
Node.appendChild(Script); Node.appendChild(Script);
}, },
// MergeObjects function was written by ChatGPT cause I was lazy and it was awhile ago // MergeObjects function was written by ChatGPT cause I was lazy and it was a while ago
MergeObjects: function(obj1, obj2) { MergeObjects: function(obj1, obj2) {
var mergedObj = {}; var mergedObj = {};
@ -258,5 +302,16 @@ export default {
} }
return mergedObj; return mergedObj;
},
RatelimitRepeatingFetch: async function (...args) {
const req = await fetch(...args);
if (req.status === 429) {
const retryAfter = req.headers.get('Retry-After') || 1; // Retry after 1 second if no header is present, else use the header value
await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000));
return RatelimitRepeatingFetch(...args);
}
return req;
} }
}; };

View file

@ -1,3 +1,4 @@
<!doctype html>
<html> <html>
<head> <head>
<!-- META TAGS --> <!-- META TAGS -->
@ -9,111 +10,9 @@
<!-- RESOURCES --> <!-- RESOURCES -->
<link rel="stylesheet" href="css/polytoria.css" /> <link rel="stylesheet" href="css/polytoria.css" />
<link rel="stylesheet" href="css/settings.css" />
</head> </head>
<body data-bs-theme="dark"> <body data-bs-theme="dark">
<style>
html,
body,
#page {
background: #202020;
color: #fff;
}
#page {
margin-top: 7.5rem;
width: 65%;
margin-right: auto;
margin-left: auto;
margin-bottom: 3.5rem;
}
h1 {
font-size: 4.6rem;
/*color: rgb(48, 48, 48);*/
}
h1 span.indent {
border-left: 10px solid rgb(48, 48, 48);
margin-right: 15px;
}
h1 span.highlight {
color: red;
}
h2 {
color: rgb(48, 48, 48);
}
h2 span.indent {
border-left: 7.5px solid rgb(48, 48, 48);
margin-right: 15px;
}
p span.title {
font-size: 1.4rem;
font-weight: lighter;
}
span.desc {
color: rgb(120, 120, 120);
}
.goback {
color: rgb(120, 120, 120);
text-decoration: none;
}
dialog {
background-color: #080808;
color: #c4c4c4;
border: 1px solid #3bafff;
border-radius: 10px;
}
.input-group-text {
background-color: #000;
border-color: #000;
color: #fff;
}
label {
font-size: 0.8rem;
margin-bottom: 2.75px;
}
dialog::backdrop {
background-color: rgba(0, 0, 0, 0.73);
}
dialog .modal-header p {
margin-bottom: 0px;
color: #fff;
}
dialog .modal-body p:first-child {
font-size: 0.9rem;
}
.setting-container .indicator {
border-radius: 5rem;
display: inline-block;
cursor: default;
}
.setting-container.enabled .indicator {
background-color: #007bff;
}
.setting-container.disabled .indicator {
background-color: orangered;
}
.toggle-btn {
float: right;
width: 100px;
}
</style>
<dialog class="w-50" id="ResetDefaults-Modal"> <dialog class="w-50" id="ResetDefaults-Modal">
<div class="modal-header"> <div class="modal-header">
<p>Are you sure?</p> <p>Are you sure?</p>
@ -321,63 +220,134 @@
</div> </div>
</div> </div>
</dialog> </dialog>
<dialog class="w-75" id="AutoAdBidding-Modal" data-setting="AutoAdBidding">
<div class="modal-header">
<p>Automatic Ad Bidding</p>
</div>
<div class="modal-body">
<p>description (max 10 ads)</p>
<hr class="mt-2 mb-3" />
<div class="input-group mb-2">
<input type="text" class="form-control ignore" placeholder="Ad ID..">
<button class="btn btn-primary" id="auto-ad-bidding-add">Add</button>
</div>
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Ad Name</th>
<th scope="col">Bid</th>
<th scope="col">Automation</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">1</th>
<td><a href="https://polytoria.com/create/ad/632">"everdayi m shuffling"</a></td>
<td class="text-success"><span class="pi">$</span> 150</td>
<td>
<select class="form-select ignore">
<option value="daily" selected>Daily</option>
<option value="weekly">Weekly</option>
<option value="monthly">Monthly</option>
</select>
</td>
<td>
<div role="group" class="btn-group w-100">
<button class="btn btn-success w-25">
BID
</button>
<button class="btn btn-orange w-25">
EDIT
</button>
<button class="btn btn-danger w-25">
DELETE
</button>
</div>
</td>
</tr>
</tbody>
</table>
<div role="group" class="btn-group w-100">
<button class="btn btn-success w-25" data-setting="[save]">Save</button>
<button class="btn btn-warning w-25" data-setting="[reset-default]">Reset to Defaults</button>
<button class="btn btn-secondary w-25" data-setting="[cancel]">Cancel</button>
</div>
</div>
</dialog>
<div id="page"> <div id="page">
<h1 class="text-center" style="text-shadow: 0px 0px 5px orange; padding-bottom: 5px; margin-bottom: 10px;">Poly+ Settings</h1> <h1
<p class="setting-container" id="pinned-games"> class="text-center"
style="
text-shadow: 0px 0px 5px orange;
padding-bottom: 5px;
margin-bottom: 10px;
"
>
Poly+ Settings
</h1>
<div class="setting-container" id="pinned-games">
<span class="indicator">&nbsp;</span> <span class="indicator">&nbsp;</span>
<span class="title"> <span class="title">
Pinned Games Pinned Games
<button class="btn btn-sm toggle-btn" data-setting="PinnedGamesOn">Toggle</button>
</span> </span>
<div class="setting-buttons">
<button class="btn btn-sm toggle-btn" data-setting="PinnedGamesOn">Toggle</button>
</div>
<br /> <br />
<span class="desc">Pin your favorite places to the top of the homepage! (limit: <span id="PinnedGames-limit"></span> places)</span> <span class="desc">Pin your favorite places to the top of the homepage! (limit: <span id="PinnedGames-limit"></span> places)</span>
</p> </div>
<p class="setting-container" id="forum-mentions"> <div class="setting-container" id="forum-mentions">
<span class="indicator">&nbsp;</span> <span class="indicator">&nbsp;</span>
<span class="title"> <span class="title">
Forum Mentions Forum Mentions
</span>
<div class="setting-buttons">
<button class="btn btn-sm toggle-btn" data-setting="ForumMentsOn">Toggle</button> <button class="btn btn-sm toggle-btn" data-setting="ForumMentsOn">Toggle</button>
</div>
<br />
<span class="desc">
Get a quick link to the popular person everyone is talking about's profile!
</span> </span>
<br /> <span class="warning">
<span class="desc">Get a quick link to the popular person everyone is talking about's profile!</span> * Forum Mentions do not notify the user or show up as a notification on their account.
<br />
<span style="font-size: 0.8rem; color: orange;">* Forum Mentions do not notify the user or show up as a notification on their account.</span>
</p>
<p class="setting-container" id="best-friends">
<span class="indicator">&nbsp;</span>
<span class="title">
Best Friends
<button class="btn btn-sm toggle-btn" data-setting="BestFriendsOn">Toggle</button>
</span> </span>
<br /> </div>
<span class="desc">Prioritize the bestest of friends on applicable friend lists! (limit: <span id="BestFriends-limit"></span> best friends)</span> <div class="setting-container" id="improved-friend-lists">
</p>
<p class="setting-container" id="improved-friend-lists">
<span class="indicator">&nbsp;</span> <span class="indicator">&nbsp;</span>
<span class="title"> <span class="title">
Improved Friend Lists Improved Friend Lists
<button class="btn btn-sm toggle-btn" data-setting="ImprovedFrListsOn">Toggle</button>
</span> </span>
<div class="setting-buttons">
<button class="btn btn-sm toggle-btn" data-setting="ImprovedFrListsOn">Toggle</button>
</div>
<br /> <br />
<span class="desc"> <span class="desc">
Accept or decline all friend requests with the click of a button or multi-remove existing friends! Accept or decline all friend requests with the click of a button or multi-remove existing friends!
<br />
<span style="font-size: 0.8rem; color: orange;">* You can only remove up to <span id="ImprovedFrLists-limit"></span> friends at once.</span>
</span> </span>
</p> <span class="warning">
<p class="setting-container" id="irl-price-with-brick-count"> * You can only remove up to <span id="ImprovedFrLists-limit"></span> friends at once.
</span>
</div>
<div class="setting-container" id="irl-price-with-brick-count">
<span class="indicator">&nbsp;</span> <span class="indicator">&nbsp;</span>
<span class="title"> <span class="title">
Show IRL price with Brick Count Show IRL price with Brick Count
<button class="btn btn-sm toggle-btn" data-setting="Enabled" data-parent="IRLPriceWithCurrency">Toggle</button>
</span> </span>
<div class="setting-buttons">
<button class="btn btn-sm toggle-btn" data-setting="Enabled" data-parent="IRLPriceWithCurrency">Toggle</button>
</div>
<br /> <br />
<span class="desc mb-4"> <span class="desc">
See the real life currency value along with Bricks across the site! See the real life currency value along with Bricks across the site!
<br /> </span>
<span style="font-size: 0.8rem; color: orange;">* Currencies were calculated on <span id="IRLPriceWithCurrency-Date">[DATE]</span>.</span> <span class="warning">
<br /> * Currencies were calculated on <span id="IRLPriceWithCurrency-Date">[DATE]</span>.
<span style="font-size: 0.8rem; color: orange;">* Currencies other than USD are purely approximations.</span> </span>
<span class="warning">
* Currencies other than USD are purely approximations.
</span> </span>
<select id="IRLPriceWithCurrencyCurrency" class="form-select form-select-sm mb-2" style="width:350px;" data-setting="Currency" data-parent="IRLPriceWithCurrency"> <select id="IRLPriceWithCurrencyCurrency" class="form-select form-select-sm mb-2" style="width:350px;" data-setting="Currency" data-parent="IRLPriceWithCurrency">
<option value="USD" selected>United States Dollar (USD)</option> <option value="USD" selected>United States Dollar (USD)</option>
@ -388,6 +358,7 @@
<option value="AUD">Australian Dollar (AUD)</option> <option value="AUD">Australian Dollar (AUD)</option>
<option value="TRY">Turkish Lira (TRY)</option> <option value="TRY">Turkish Lira (TRY)</option>
<option value="BRL">Brazilian Real (BRL)</option> <option value="BRL">Brazilian Real (BRL)</option>
<option value="ZWL">Zimbabwean Dollar (ZWL)</option>
</select> </select>
<!-- <!--
<select id="IRLPriceWithCurrencyPackage" class="form-select form-select-sm mb-2" style="width:350px;" data-setting="Package" data-parent="IRLPriceWithCurrency"> <select id="IRLPriceWithCurrencyPackage" class="form-select form-select-sm mb-2" style="width:350px;" data-setting="Package" data-parent="IRLPriceWithCurrency">
@ -399,145 +370,185 @@
<option value="5">$99.99 Brick Package</option> <option value="5">$99.99 Brick Package</option>
</select> </select>
--> -->
</p> </div>
<p class="setting-container" id="hide-notification-badges"> <div class="setting-container" id="hide-notification-badges">
<span class="indicator">&nbsp;</span> <span class="indicator">&nbsp;</span>
<span class="title"> <span class="title">
Hide Notification Badges Hide Notification Badges
<button class="btn btn-sm toggle-btn" data-setting="HideNotifBadgesOn">Toggle</button>
</span> </span>
<div class="setting-buttons">
<button class="btn btn-sm toggle-btn" data-setting="HideNotifBadgesOn">Toggle</button>
</div>
<br /> <br />
<span class="desc">Hide the annoying red circles on the sidebar!</span> <span class="desc">Hide the annoying red circles on the sidebar!</span>
</p> </div>
<p class="setting-container" id="store-own-tag"> <div class="setting-container" id="store-own-tag">
<span class="indicator">&nbsp;</span> <span class="indicator">&nbsp;</span>
<span class="title"> <span class="title">
Show "OWNED" Tag on Store Main Page Show "OWNED" Tag on Store Main Page
<button class="btn btn-sm toggle-btn" data-setting="StoreOwnTagOn">Toggle</button>
</span> </span>
<div class="setting-buttons">
<button class="btn btn-sm toggle-btn" data-setting="StoreOwnTagOn">Toggle</button>
</div>
<br /> <br />
<span class="desc">Quickly see if you own the item at a glance with a little tag in the top left corner of item cards on the main store page!</span> <span class="desc">
</p> Quickly see if you own the item at a glance with a little tag in the top left corner of item cards on the main store page!
<p class="setting-container" id="theme-creator"> </span>
<span class="note">
* If the star is yellow/orange, that means the item is a collectible and hovering on the star will show your serial.
</span>
</div>
<div class="setting-container" id="theme-creator">
<span class="indicator">&nbsp;</span> <span class="indicator">&nbsp;</span>
<span class="title"> <span class="title">
Theme Creator Theme Creator
<button class="btn btn-sm toggle-btn" data-setting="Enabled" data-parent="ThemeCreator">Toggle</button>
<button id="ThemeCreator-Options" class="btn btn-primary btn-sm" data-modal="ThemeCreator">Options</button>
</span> </span>
<div class="setting-buttons">
<button id="ThemeCreator-Options" class="btn btn-primary btn-sm options-btn" data-modal="ThemeCreator">Options</button>
<button class="btn btn-sm toggle-btn" data-setting="Enabled" data-parent="ThemeCreator">Toggle</button>
</div>
<br /> <br />
<span class="desc">Unleash your creativity and customize the Polytoria website to your liking! (this feature is still in development)</span> <span class="desc">Unleash your creativity and customize the Polytoria website to your liking! (this feature is still in development)</span>
</p> </div>
<p class="setting-container" id="more-search-filters"> <div class="setting-container" id="more-search-filters">
<span class="indicator">&nbsp;</span> <span class="indicator">&nbsp;</span>
<span class="title"> <span class="title">
More Search Filters More Search Filters
<button class="btn btn-sm toggle-btn" data-setting="MoreSearchFiltersOn">Toggle</button>
</span> </span>
<div class="setting-buttons">
<button class="btn btn-sm toggle-btn" data-setting="MoreSearchFiltersOn">Toggle</button>
</div>
<br /> <br />
<span class="desc">Easily find what you're looking for with more search filters side-wide! (this does not affect the main site search on the navbar)</span> <span class="desc">
<br /> Easily find what you're looking for with more search filters side-wide! (this does not affect the main site search on the navbar)
<span style="font-size: 0.8rem; color: orange;">* This currently only has a "Creator" username filter on the forum search page, more search filters are yet to come.</span> </span>
</p> <span class="warning">
<p class="setting-container" id="apply-membership-theme"> * This currently only has a "Creator" username filter on the forum search page, more search filters are yet to come.
</span>
</div>
<div class="setting-container" id="apply-membership-theme">
<span class="indicator">&nbsp;</span> <span class="indicator">&nbsp;</span>
<span class="title"> <span class="title">
Apply Membership Theme for <b>Free</b> Apply Membership Theme for <b>Free</b>
<button class="btn btn-sm toggle-btn" data-setting="Enabled" data-parent="ApplyMembershipTheme">Toggle</button>
</span> </span>
<div class="setting-buttons">
<button class="btn btn-sm toggle-btn" data-setting="Enabled" data-parent="ApplyMembershipTheme">Toggle</button>
</div>
<br /> <br />
<span class="desc">Ever want the fancy membership themes for completely <b>free</b>? Well now you can get apply them site-wide!</span> <span class="desc">Ever want the fancy membership themes for completely <b>free</b>? Well now you can get apply them site-wide!</span>
<select id="ApplyMembershipThemeTheme" class="form-select form-select-sm mb-2" style="width:350px;" data-setting="Theme" data-parent="ApplyMembershipTheme"> <select id="ApplyMembershipThemeTheme" class="form-select form-select-sm mb-2" style="width:350px;" data-setting="Theme" data-parent="ApplyMembershipTheme">
<option value="Plus" selected>Plus</option> <option value="Plus" selected>Plus</option>
<option value="PlusDX">Plus Deluxe</option> <option value="PlusDX">Plus Deluxe</option>
</select> </select>
</p> </div>
<p class="setting-container" id="multi-cancel-outbound-trades"> <div class="setting-container" id="multi-cancel-outbound-trades">
<span class="indicator">&nbsp;</span> <span class="indicator">&nbsp;</span>
<span class="title"> <span class="title">
Multi-Cancel Outbound Trades Multi-Cancel Outbound Trades
<button class="btn btn-sm toggle-btn" data-setting="MultiCancelOutTradesOns">Toggle</button>
</span> </span>
<div class="setting-buttons">
<button class="btn btn-sm toggle-btn" data-setting="MultiCancelOutTradesOns">Toggle</button>
</div>
<br /> <br />
<span class="desc"> <span class="desc">
Quickly cancel several out-bound trades (trades that you have sent) all at once Quickly cancel several out-bound trades (trades that you have sent) all at once
<br /> <br />
<span style="font-size: 0.8rem; color: orange;">* You can only cancel up to 10 trades at once.</span> <span class="warning">* You can only cancel up to 10 trades at once.</span>
</span> </span>
</p> </div>
<p class="setting-container" id="modify-navbar"> <div class="setting-container" id="modify-navbar">
<span class="indicator">&nbsp;</span> <span class="indicator">&nbsp;</span>
<span class="title"> <span class="title">
Modify Navbar Modify Navbar
<button class="btn btn-sm toggle-btn" data-setting="ModifyNavOn">Toggle</button>
<button id="ModifyNav-Options" class="btn btn-primary btn-sm" data-modal="ModifyNav">Options</button>
</span> </span>
<div class="setting-buttons">
<button id="ModifyNav-Options" class="btn btn-primary btn-sm options-btn" data-modal="ModifyNav">Options</button>
<button class="btn btn-sm toggle-btn" data-setting="ModifyNavOn">Toggle</button>
</div>
<br /> <br />
<span class="desc"> Customize the navbar to your liking! </span> <span class="desc"> Customize the navbar to your liking! </span>
</p> </div>
<p class="setting-container" id="item-wishlist"> <div class="setting-container" id="item-wishlist">
<span class="indicator">&nbsp;</span> <span class="indicator">&nbsp;</span>
<span class="title"> <span class="title">
Item Wishlist Item Wishlist
<button class="btn btn-sm toggle-btn" data-setting="ItemWishlistOn">Toggle</button>
</span> </span>
<div class="setting-buttons">
<button class="btn btn-sm toggle-btn" data-setting="ItemWishlistOn">Toggle</button>
</div>
<br /> <br />
<span class="desc"> Wishlist that item that you REALLY want! </span> <span class="desc"> Wishlist that item that you REALLY want! </span>
</p> </div>
<p class="setting-container" id="try-on-items"> <div class="setting-container" id="try-on-items">
<span class="indicator">&nbsp;</span> <span class="indicator">&nbsp;</span>
<span class="title"> <span class="title">
Try-On Items Try-On Items
<button class="btn btn-sm toggle-btn" data-setting="TryOnItemsOn">Toggle</button>
</span> </span>
<div class="setting-buttons">
<button class="btn btn-sm toggle-btn" data-setting="TryOnItemsOn">Toggle</button>
</div>
<br /> <br />
<span class="desc">See how that new item looks on your avatar before spending your bricks!</span> <span class="desc">See how that new item looks on your avatar before spending your bricks!</span>
</p> </div>
<p class="setting-container" id="outfit-cost"> <div class="setting-container" id="outfit-cost">
<span class="indicator">&nbsp;</span> <span class="indicator">&nbsp;</span>
<span class="title"> <span class="title">
Show Outfit Cost on Profiles Show Outfit Cost on Profiles
<button class="btn btn-sm toggle-btn" data-setting="OutfitCostOn">Toggle</button>
</span> </span>
<div class="setting-buttons">
<button class="btn btn-sm toggle-btn" data-setting="OutfitCostOn">Toggle</button>
</div>
<br /> <br />
<span class="desc">Quickly see how many bricks a user spent on their avatar!</span> <span class="desc">Quickly see how many bricks a user spent on their avatar!</span>
</p> </div>
<p class="setting-container" id="outfit-cost"> <div class="setting-container" id="outfit-cost">
<span class="indicator">&nbsp;</span> <span class="indicator">&nbsp;</span>
<span class="title"> <span class="title">
Show Approximate Place Revenue Show Approximate Place Revenue
<button class="btn btn-sm toggle-btn" data-setting="ShowPlaceRevenueOn">Toggle</button>
</span> </span>
<div class="setting-buttons">
<button class="btn btn-sm toggle-btn" data-setting="ShowPlaceRevenueOn">Toggle</button>
</div>
<br /> <br />
<span class="desc">Quickly see how many bricks a user has gained from one of their places!</span> <span class="desc">
<br /> Quickly see how many bricks a user has gained from one of their places!
<span style="font-size: 0.8rem; color: orange;" </span>
>* Gamepass revenue is calculated assuming the price hasn't changed and all users that bought the gamepass, bought it at the same price that it is at the time of calculating.</span <span class="warning">
> * Gamepass revenue is calculated assuming the price hasn't changed and all users that bought the gamepass, bought it at the same price that it is at the time of calculating.
</p> </span>
<p class="setting-container" id="item-replace-sales"> </div>
<div class="setting-container" id="item-replace-sales">
<span class="indicator">&nbsp;</span> <span class="indicator">&nbsp;</span>
<span class="title"> <span class="title">
Show "Owners" instead of "Sales" Show "Owners" instead of "Sales"
<button class="btn btn-sm toggle-btn" data-setting="ReplaceItemSalesOn">Toggle</button>
</span> </span>
<div class="setting-buttons">
<button class="btn btn-sm toggle-btn" data-setting="ReplaceItemSalesOn">Toggle</button>
</div>
<br /> <br />
<span class="desc">Replace the "Sales" statistic with the "Owners" statistic if the item has 0 sales (most likely meaning it is an item awarded by staff).</span> <span class="desc">Replace the "Sales" statistic with the "Owners" statistic if the item has 0 sales (most likely meaning it is an item awarded by staff).</span>
</p> </div>
<p class="setting-container" id="hoarders-list"> <div class="setting-container" id="hoarders-list">
<span class="indicator">&nbsp;</span> <span class="indicator">&nbsp;</span>
<span class="title"> <span class="title">
Collectibles' Hoarders List Collectibles' Hoarders List
<button class="btn btn-sm toggle-btn" data-setting="Enabled" data-parent="HoardersList">Toggle</button>
</span> </span>
<div class="setting-buttons">
<button class="btn btn-sm toggle-btn" data-setting="Enabled" data-parent="HoardersList">Toggle</button>
</div>
<br /> <br />
<span class="desc">List all the users that have more than 1 copy of a collectible!</span> <span class="desc">List all the users that have more than 1 copy of a collectible!</span>
<span class="note mb-3">
* The hoarders list is limited to <span id="HoardersList-pageLimit"></span> pages to reduce chance of ratelimiting and improve how fast the list loads
</span>
<span class="form-check form-switch"> <span class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="hoarders-list-avatars" data-setting="AvatarsEnabled" data-parent="HoardersList" /> <input class="form-check-input" type="checkbox" role="switch" id="hoarders-list-avatars" data-setting="AvatarsEnabled" data-parent="HoardersList" />
<label class="form-check-label" for="hoarders-list-avatars"> <label class="form-check-label" for="hoarders-list-avatars">
Show Avatar Icons Show Avatar Icons
<br /> <br />
<span style="font-size: 0.6rem; color: orange;">* Avatar icons will result in more requests, potentially leading to more ratelimiting and slower hoarder list loading.</span> <span class="warning">
* Avatar icons will result in more requests, potentially leading to more ratelimiting and slower hoarder list loading.
</span>
</label> </label>
</span> </span>
<select class="form-select form-select-sm mb-2" style="width:350px;" data-setting="MinCopies" data-parent="HoardersList" data-useValue="true"> <select class="form-select form-select-sm mb-2" style="width:350px;" data-setting="MinCopies" data-parent="HoardersList" data-useValue="true">
@ -548,56 +559,96 @@
<option value="15">Min. 15+ Copies</option> <option value="15">Min. 15+ Copies</option>
<option value="35">Min. 35+ Copies</option> <option value="35">Min. 35+ Copies</option>
</select> </select>
</p> </div>
<p class="setting-container" id="hoarders-list"> <div class="setting-container" id="improved-achievements">
<span class="badge bg-primary mb-1">Now a Setting!</span>
<br>
<span class="indicator">&nbsp;</span>
<span class="title">
Improved Achievements
</span>
<div class="setting-buttons">
<button class="btn btn-sm toggle-btn" data-setting="Enabled" data-parent="ImprovedAchievements">Toggle</button>
</div>
<br />
<span class="desc">
Quality-of-life improvements to achievements!
</span>
<span class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="progress-improved-achievements" data-setting="ProgressBarOn" data-parent="ImprovedAchievements" />
<label class="form-check-label" for="progress-improved-achievements"> Progress Bar on All Achievements in Place </label>
</span>
<span class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="percentage-improved-achievements" data-setting="PercentageOn" data-parent="ImprovedAchievements" />
<label class="form-check-label" for="percentage-improved-achievements"> Percentage of Players (& difficulty) </label>
</span>
<span class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="opacity-improved-achievements" data-setting="OpacityOn" data-parent="ImprovedAchievements" />
<label class="form-check-label" for="opacity-improved-achievements"> Fade out unowned achievements </label>
</span>
</div>
<div class="setting-container" id="hoarders-list">
<span class="indicator">&nbsp;</span> <span class="indicator">&nbsp;</span>
<span class="title"> <span class="title">
Quick Library Downloads Quick Library Downloads
<button class="btn btn-sm toggle-btn" data-setting="LibraryDownloadsOn">Toggle</button>
</span> </span>
<div class="setting-buttons">
<button class="btn btn-sm toggle-btn" data-setting="LibraryDownloadsOn">Toggle</button>
</div>
<br /> <br />
<span class="desc">Quickly download a model (as a <code>.ptmd</code> file), mesh, decal, or sound right from the page showing you details about the asset!</span> <span class="desc">Quickly download a model (as a <code>.ptmd</code> file), mesh, decal, or sound right from the page showing you details about the asset!</span>
</p> </div>
<p class="setting-container" id="event-items-store"> <div class="setting-container" id="event-items-store">
<span class="indicator">&nbsp;</span> <span class="indicator">&nbsp;</span>
<span class="title"> <span class="title">
"Event Items" Store Category "Event Items" Store Category
<button class="btn btn-sm toggle-btn" data-setting="EventItemsCatOn">Toggle</button>
</span> </span>
<div class="setting-buttons">
<button class="btn btn-sm toggle-btn" data-setting="EventItemsCatOn">Toggle</button>
</div>
<br /> <br />
<span class="desc">List all the on-going and past event items separated by their event with a store category!</span> <span class="desc">List all the on-going and past event items separated by their event with a store category!</span>
</p> </div>
<p class="setting-container" id="event-items-store"> <div class="setting-container" id="event-items-store">
<span class="indicator">&nbsp;</span> <span class="indicator">&nbsp;</span>
<span class="title"> <span class="title">
Show Friend Count on Homepage Show Friend Count on Homepage
<button class="btn btn-sm toggle-btn" data-setting="HomeFriendCountOn">Toggle</button>
</span> </span>
<div class="setting-buttons">
<button class="btn btn-sm toggle-btn" data-setting="HomeFriendCountOn">Toggle</button>
</div>
<br /> <br />
<span class="desc">See how many friends you have on your homepage/dashboard, just scroll to the "friends" section and you'll see it next to the heading!</span> <span class="desc">See how many friends you have on your homepage/dashboard, just scroll to the "friends" section and you'll see it next to the heading!</span>
</p> </div>
<p class="setting-container" id="timed-item-owner-check"> <div class="setting-container" id="timed-item-owner-check">
<span class="indicator">&nbsp;</span> <span class="indicator">&nbsp;</span>
<span class="title"> <span class="title">
Timed-Item Owner Check Timed-Item Owner Check
<button class="btn btn-sm toggle-btn" data-setting="ItemOwnerCheckOn">Toggle</button>
</span> </span>
<div class="setting-buttons">
<button class="btn btn-sm toggle-btn" data-setting="ItemOwnerCheckOn">Toggle</button>
</div>
<br /> <br />
<span class="desc" <span class="desc">
>Click the shopping bags icon in the bottom left-hand corner of the timed item's item thumbnail to toggle an input and a button. Then just enter someone's username and it'll tell you whether Click the shopping bags icon in the bottom left-hand corner of the timed item's item thumbnail to toggle an input and a button. Then just enter someone's username and it'll tell you whether
they own it or not and if so what serial!</span they own it or not and if so what serial!
> </span>
</p> </div>
<p class="setting-container" id="hide-user-ads"> <div class="setting-container" id="hide-user-ads">
<span class="indicator">&nbsp;</span> <span class="indicator">&nbsp;</span>
<span class="title"> <span class="title">
Hide User Ads Hide User Ads
<button class="btn btn-sm toggle-btn" data-setting="Enabled" data-parent="HideUserAds">Toggle</button>
</span> </span>
<div class="setting-buttons">
<button class="btn btn-sm toggle-btn" data-setting="Enabled" data-parent="HideUserAds">Toggle</button>
</div>
<br /> <br />
<span class="desc">Hide those annoying <s>out of context Discord message</s> user ads!</span> <span class="desc">
<br /> Hide those annoying <s>out of context Discord message</s> user ads!
<span style="font-size: 0.8rem; color: orange;">* Ads that are shown to help support Polytoria are not hidden.</span> </span>
<span class="warning">
* Ads that are shown to help support Polytoria are not hidden.
</span>
<span class="form-check form-switch"> <span class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="hide-user-ads-banner" data-setting="Banners" data-parent="HideUserAds" /> <input class="form-check-input" type="checkbox" role="switch" id="hide-user-ads-banner" data-setting="Banners" data-parent="HideUserAds" />
<label class="form-check-label" for="hide-user-ads-banner"> Hide Banner User Ads </label> <label class="form-check-label" for="hide-user-ads-banner"> Hide Banner User Ads </label>
@ -606,60 +657,313 @@
<input class="form-check-input" type="checkbox" role="switch" id="hide-user-ads-rectangle" data-setting="Rectangles" data-parent="HideUserAds" /> <input class="form-check-input" type="checkbox" role="switch" id="hide-user-ads-rectangle" data-setting="Rectangles" data-parent="HideUserAds" />
<label class="form-check-label" for="hide-user-ads-rectangle"> Hide Rectangle User Ads </label> <label class="form-check-label" for="hide-user-ads-rectangle"> Hide Rectangle User Ads </label>
</span> </span>
</p> </div>
<div class="setting-container" id="upload-multiple-decals">
<span class="indicator">&nbsp;</span>
<span class="title">
Multi-Decal Uploading
</span>
<div class="setting-buttons">
<button class="btn btn-sm toggle-btn" data-setting="UploadMultipleDecals">Toggle</button>
</div>
<br />
<span class="desc">
Quickly upload several decals all at once!
</span>
</div>
<div class="setting-container" id="avatar-dimension-toggle">
<span class="indicator">&nbsp;</span>
<span class="title">
Avatar Dimension Toggle
</span>
<div class="setting-buttons">
<button class="btn btn-sm toggle-btn" data-setting="AvatarDimensionToggleOn">Toggle</button>
</div>
<br />
<span class="desc">
Quickly switch between a 3D or 2D rendition of somebody's avatar on their profile page!
</span>
</div>
<div class="setting-container" id="collectibles-inventory-category">
<span class="indicator">&nbsp;</span>
<span class="title">
"Collectibles" Inventory Category
</span>
<div class="setting-buttons">
<button class="btn btn-sm toggle-btn" data-setting="CollectibleInventoryCatOn">Toggle</button>
</div>
<br />
<span class="desc">
Sort a player's inventory by all of their limited-edition collectibles!
</span>
</div>
<div class="setting-container" id="value-list-info">
<span class="badge bg-primary mb-1">New in v1.3!</span>
<br>
<span class="indicator">&nbsp;</span>
<span class="title">
"List of Values Everlast" Integration
</span>
<div class="setting-buttons">
<button class="btn btn-sm toggle-btn" data-setting="Enabled" data-parent="ValueListInfo">Toggle</button>
</div>
<br />
<span class="desc">
Quickly see a collectible's evaluation by the community-trusted "List-of-Values Everlast" guild! Credits to them for all value information provided by the extension.
</span>
<span class="note">
* This feature will be expanded to allow you to pick from a list of value lists (list-inception), if any prominent value lists pop-up in the future!
</span>
<span class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="item-view-valuation" data-setting="ItemValuation" data-parent="ValueListInfo" />
<label class="form-check-label" for="item-view-valuation"> Show Value Info on Item View Page </label>
</span>
<span class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="trade-valuation" data-setting="TradeValuation" data-parent="ValueListInfo" />
<label class="form-check-label" for="trade-valuation"> Show Value Info for all items on Trade View Page </label>
</span>
</div>
<div class="setting-container" id="readd-copyable-places">
<span class="badge bg-primary mb-1">New in v1.3!</span>
<br>
<span class="indicator">&nbsp;</span>
<span class="title">
Re-add Copyable Places
</span>
<div class="setting-buttons">
<button class="btn btn-sm toggle-btn" data-setting="ReaddCopyablePlacesOn">Toggle</button>
</div>
<br />
<span class="desc">
Download "copyable" places on Polytoria again!
</span>
<span class="warning">
* This feature does not make it possible to set your place(s) as "copyable"
</span>
</div>
<div class="setting-container" id="home-join-friends">
<span class="badge bg-primary mb-1">New in v1.3!</span>
<br>
<span class="indicator">&nbsp;</span>
<span class="title">
Homepage Join Friends Tooltip Button
</span>
<div class="setting-buttons">
<button class="btn btn-sm toggle-btn" data-setting="HomeJoinFriendsButtonOn">Toggle</button>
</div>
<br />
<span class="desc">
Quickly join your friends' servers by clicking the play button inside the tooltip in the "Friends" section of the homepage.
</span>
</div>
<div class="setting-container" id="improved-place-management">
<span class="badge bg-primary mb-1">Now a Setting!</span>
<br>
<span class="indicator">&nbsp;</span>
<span class="title">
Improved Place Management
</span>
<div class="setting-buttons">
<button class="btn btn-sm toggle-btn" data-setting="Enabled" data-parent="ImprovedPlaceManagement">Toggle</button>
</div>
<br />
<span class="desc">
Develop the next hit game easier with a few management tools!
</span>
<span class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="quick-activity-toggle" data-setting="QuickActivityToggleOn" data-parent="ImprovedPlaceManagement" />
<label class="form-check-label" for="quick-activity-toggle"> Quick Place Activity Toggle </label>
</span>
<span class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="download-place-file-button" data-setting="PlaceFileDownloadOn" data-parent="ImprovedPlaceManagement" />
<label class="form-check-label" for="download-place-file-button"> Download Place File Button </label>
</span>
<span class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="multi-whitelist" data-setting="MultiWhitelistOn" data-parent="ImprovedPlaceManagement" />
<label class="form-check-label" for="multi-whitelist"> <span class="badge bg-primary mb-1">New in v1.3!</span> Multi-Whitelist Several Users </label>
</span>
<span class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="clear-whitelist-button" data-setting="ClearWhitelistOn" data-parent="ImprovedPlaceManagement" />
<label class="form-check-label" for="clear-whitelist-button"> <span class="badge bg-primary mb-1">New in v1.3!</span> Quick Clear Whitelist Button </label>
</span>
</div>
<div class="setting-container" id="more-blocked-details">
<span class="badge bg-primary mb-1">New in v1.3!</span>
<br>
<span class="indicator">&nbsp;</span>
<span class="title">
See Player ID and Join Date when Blocked
</span>
<div class="setting-buttons">
<button class="btn btn-sm toggle-btn" data-setting="MoreBlockedDetailsOn">Toggle</button>
</div>
<br />
<span class="desc">
See someone's player ID and join date even if they have blocked you on their profile.
</span>
</div>
<div class="setting-container" id="asset-designer-credit">
<span class="badge bg-primary mb-1">New in v1.3!</span>
<br>
<span class="indicator">&nbsp;</span>
<span class="title">
Asset Designer Credit
</span>
<div class="setting-buttons">
<button class="btn btn-sm toggle-btn" data-setting="AssetDesignerCreditOn">Toggle</button>
</div>
<br />
<span class="desc">
Check who modeled the item you're looking at on the item view page! If the text doesn't show up next to the sales counter, make sure to DM me so I can add whoever modeled that item.
</span>
</div>
<div class="setting-container" id="avatar-sandbox">
<span class="badge bg-primary mb-1">Finished & No longer Experimental in v1.3!</span>
<br>
<span class="indicator">&nbsp;</span>
<span class="title">
Avatar Sandbox
</span>
<div class="setting-buttons">
<button class="btn btn-sm toggle-btn" data-setting="AvatarSandboxOn">Toggle</button>
</div>
<br />
<span class="desc">
Create an avatar with all the item possibilities available to your heart's content!
</span>
<span class="warning">
* This feature is not polished - things like modifying avatar "Body Colors", pagination, outfits, etc haven't been added.
</span>
</div>
<!---
coming soon maybe I don't know, I'm lazy
<div class="setting-container" id="auto-ad-bidding">
<span class="indicator">&nbsp;</span>
<span class="title">
Automatic Ad Bidding
</span>
<div class="setting-buttons">
<button class="btn btn-primary btn-sm" data-modal="AutoAdBidding">Options</button>
<button class="btn btn-sm toggle-btn" data-setting="AutoAdBiddingOn">Toggle</button>
</div>
<br />
<span class="desc">description</span>
</div>
-->
<div class="card mb-3">
<div class="card-body">
<h3>ARCHIVED SETTINGS</h3>
<p>Features here still work, but are separated from other features as they will no longer receive any improvements, or changes (bugs however may be fixed eventually if any are found).</p>
<hr />
<div class="card card-body limited-time setting-container mb-3 enabled" id="greatdivide-server-unbalance" style="background-image: linear-gradient(rgba(0.8, 0.8, 0.8, 0.8), rgba(0.8, 0.8, 0.8, 0.8)), url(/images/greatdivide.png); background-size: cover; background-position: center;">
<div class="row">
<div class="col-auto">
<span class="indicator">&nbsp;</span>
</div>
<div class="col">
<span class="title">
The Great Divide
</span>
<div class="setting-buttons">
<button class="btn btn-sm toggle-btn btn-danger" data-setting="Enabled" data-parent="TheGreatDivide">Disable</button>
</div>
</div>
</div>
<span class="desc">Reinforcements to aid in your quest to bring home the victory to your team, and bring honor to your peers.</span>
<span class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="server-unbalanced-greatdivide" data-setting="UnbalancedIndicatorOn" data-parent="TheGreatDivide">
<label class="form-check-label" for="server-unbalanced-greatdivide">"Potentially Unbalanced" indicator in server list </label>
</span>
<span class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="user-statistics-greatdivide" data-setting="UserStatsOn" data-parent="TheGreatDivide">
<label class="form-check-label" for="user-statistics-greatdivide">User Statistics tab on profiles</label>
</span>
<small class="text-muted mt-1" style="font-size: 0.7rem;">
* features use <a href="https://polytoria.com/u/dargy" target="_blank">@dargy</a>'s public API! thank you!
</small>
<small class="limited-time-tag mt-1">
* was available until July 14th
</small>
</div>
</div>
</div>
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h3>EXPERIMENTAL SETTINGS</h3> <h3>EXPERIMENTAL SETTINGS</h3>
<p>These features are a work in progress.</p> <p>These features are a work in progress.</p>
<hr /> <hr />
<p class="setting-container" id="game-profiles"> <div class="setting-container" id="time-played">
<span class="badge bg-primary mb-1">New in v1.3!</span>
<br>
<span class="indicator">&nbsp;</span> <span class="indicator">&nbsp;</span>
<span class="title"> <span class="title">
Game Profiles Time Played
<button class="btn btn-sm toggle-btn" data-setting="GameProfilesOn">Toggle</button>
</span> </span>
<div class="setting-buttons">
<button class="btn btn-sm toggle-btn" data-setting="TimePlayedOn">Toggle</button>
</div>
<br /> <br />
<span class="desc">Traverse a place view page that matches a place's color palette!</span> <span class="desc">
<br /> Track your playtime in places around Polytoria!
<span style="font-size: 0.8rem; color: orange;">* Place creators must request a game profile for one to be made.</span> </span>
<br /> <span class="warning">
<span style="font-size: 0.8rem; color: orange;">* This feature will be expanded upon in the future.</span> * Past playtime data (prior to extension install/prior to setting enable) is, understandably, not able to be tracked
</p> </span>
<p class="setting-container" id="inline-editing"> </div>
<div class="setting-container" id="inline-editing">
<span class="indicator">&nbsp;</span> <span class="indicator">&nbsp;</span>
<span class="title"> <span class="title">
Inline Editing Inline Editing
<button class="btn btn-sm toggle-btn" data-setting="InlineEditingOn">Toggle</button>
</span> </span>
<div class="setting-buttons">
<button class="btn btn-sm toggle-btn" data-setting="InlineEditingOn">Toggle</button>
</div>
<br /> <br />
<span class="desc">Quickly edit your asset's details such as name and description on the same page as viewing the asset!</span> <span class="desc">Quickly edit your asset's details such as name and description on the same page as viewing the asset!</span>
<span class="warning">
* This feature only supports places at this time.
</span>
<span class="warning">
* This feature is not finished or polished.
</span>
</div>
<div class="setting-container" id="game-profiles">
<span class="indicator">&nbsp;</span>
<span class="title">
Game Profiles
</span>
<div class="setting-buttons">
<button class="btn btn-sm toggle-btn" data-setting="GameProfilesOn">Toggle</button>
</div>
<br /> <br />
<span style="font-size: 0.8rem; color: orange;">* This feature currently only supports places.</span> <span class="desc">
<br /> Traverse a place view page that matches a place's color palette!
<span style="font-size: 0.8rem; color: orange;">* This feature is not finished or polished.</span> </span>
</p> <span class="warning">
<p class="setting-container" id="forum-unix-timestamps"> * Place creators must request a game profile for one to be made.
</span>
<span class="warning">
* This feature will be expanded upon in the future.
</span>
</div>
<div class="setting-container" id="forum-unix-timestamps">
<span class="indicator">&nbsp;</span> <span class="indicator">&nbsp;</span>
<span class="title"> <span class="title">
Forum Unix Timestamps Forum Unix Timestamps
</span>
<div class="setting-buttons">
<button class="btn btn-sm toggle-btn" data-setting="ForumUnixStampsOn">Toggle</button> <button class="btn btn-sm toggle-btn" data-setting="ForumUnixStampsOn">Toggle</button>
</div>
<br />
<span class="desc">
See a date and time that is adjusted to everyone (who is using Poly+)'s local time.
</span> </span>
<br /> <span class="warning">
<span class="desc">See a date and time that is adjusted to everyone (who is using Poly+)'s local time</span> * The styling for this feature is not yet done.
<br />
<span style="font-size: 0.8rem; color: orange;">* The styling for this feature is not yet done.</span>
</p>
<p class="setting-container" id="avatar-sandbox">
<span class="indicator">&nbsp;</span>
<span class="title">
Avatar Sandbox
<button class="btn btn-sm toggle-btn" data-setting="AvatarSandboxOn">Toggle</button>
</span> </span>
<br /> </div>
<span class="desc">Create an avatar with all the item possibilities available to your heart's content!</span>
<br />
<span style="font-size: 0.8rem; color: orange;">* This feature is not polished - things like modifying avatar "Body Colors", pagination, outfits, etc haven't been added.</span>
</p>
</div> </div>
</div> </div>
<hr /> <hr />

View file

@ -11,9 +11,9 @@ var Utilities;
LoadCurrent(); LoadCurrent();
document.getElementById('PinnedGames-limit').innerText = Utilities.Limits.PinnedGames; document.getElementById('PinnedGames-limit').innerText = Utilities.Limits.PinnedGames;
document.getElementById('BestFriends-limit').innerText = Utilities.Limits.BestFriends; //document.getElementById('ImprovedFrLists-limit').innerText = Utilities.Limits.ImprovedFrLists;
document.getElementById('ImprovedFrLists-limit').innerText = Utilities.Limits.ImprovedFrLists;
//document.getElementById('ItemWishlist-limit').innerText = Utilities.Limits.ItemWishlist; //document.getElementById('ItemWishlist-limit').innerText = Utilities.Limits.ItemWishlist;
document.getElementById('HoardersList-pageLimit').innerText = Utilities.Limits.HoardersListPages;
})(); })();
// Handle buttons at the bottom of the page // Handle buttons at the bottom of the page
@ -37,10 +37,10 @@ document.getElementById('ResetDefaults-Modal-No').addEventListener('click', func
// Loop thru each setting container and handle toggling, selecting, opening modal, etc // Loop thru each setting container and handle toggling, selecting, opening modal, etc
Elements.forEach((element) => { Elements.forEach((element) => {
let Button = element.getElementsByTagName('button')[0]; let Button = element.getElementsByClassName('toggle-btn')[0];
let Options = element.getElementsByTagName('button')[1]; let Options = element.getElementsByClassName('options-btn')[0];
let Select = element.getElementsByTagName('select')[0]; let Select = element.getElementsByTagName('select')[0];
let Checkbox = element.getElementsByTagName('input')[0]; let Checkbox = element.getElementsByTagName('input');
if (Button) { if (Button) {
Button.addEventListener('click', function () { Button.addEventListener('click', function () {
@ -63,9 +63,11 @@ Elements.forEach((element) => {
} }
if (Checkbox) { if (Checkbox) {
Checkbox.addEventListener('change', function () { Array.from(Checkbox).forEach(check => {
SetSetting(Checkbox, Checkbox.checked, false); check.addEventListener('change', function () {
}); SetSetting(check, check.checked, false);
});
})
} }
if (Options) { if (Options) {
@ -194,10 +196,12 @@ function SetSetting(element, value, update, modalParent) {
return "Are you sure you'd like to leave? Your Poly+ settings haven't been saved." return "Are you sure you'd like to leave? Your Poly+ settings haven't been saved."
}; };
} }
/*
if (AreIdentical(Settings, RecentSave) === true) { if (AreIdentical(Settings, RecentSave) === true) {
document.title = 'Poly+ Settings' document.title = 'Poly+ Settings'
SaveBtn.disabled = true SaveBtn.disabled = true
} }
*/
} }
function GetSettingValue(element, modalParent) { function GetSettingValue(element, modalParent) {
@ -326,6 +330,48 @@ function LoadThemeJSON(string) {
} }
} }
chrome.storage.sync.get(['PolyPlus_AutoAds'], function(result){
let AutoAds = result.PolyPlus_AutoAds || [];
const Modal = document.getElementById("AutoAdBidding-Modal")
const AddButton = document.getElementById('auto-ad-bidding-add')
AddButton.addEventListener('click', async function() {
const Page = new DOMParser().parseFromString((await (await fetch('https://polytoria.com/create/ad/' + AddButton.previousElementSibling.value)).text()), 'text/html')
})
const AddRow = function(index, info) {
const Row = document.createElement('tr')
Row.innerHTML = `
<th scope="row">${index+1}</th>
<td><a href="https://polytoria.com/create/ad/${info.id}">"${info.name}"</a></td>
<td class="text-success"><span class="pi">$</span> 150</td>
<td>
<select class="form-select ignore">
<option value="daily" selected>Daily</option>
<option value="weekly">Weekly</option>
<option value="monthly">Monthly</option>
</select>
</td>
<td>
<div role="group" class="btn-group w-100">
<button class="btn btn-success w-25">
BID
</button>
<button class="btn btn-orange w-25">
EDIT
</button>
<button class="btn btn-danger w-25">
DELETE
</button>
</div>
</td>
`
}
})
function AreIdentical(obj1, obj2) { function AreIdentical(obj1, obj2) {
if (obj1.length !== obj2.length) { return false } if (obj1.length !== obj2.length) { return false }
return JSON.stringify(obj1) === JSON.stringify(obj2) return JSON.stringify(obj1) === JSON.stringify(obj2)